1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_bridges.hxx"
26 
27 #include <stdio.h>
28 
29 // #include <malloc.h>
30 
31 #include <com/sun/star/uno/genfunc.hxx>
32 #include "com/sun/star/uno/RuntimeException.hpp"
33 #include <uno/data.h>
34 
35 #include "bridges/cpp_uno/shared/bridge.hxx"
36 #include "bridges/cpp_uno/shared/types.hxx"
37 #include "bridges/cpp_uno/shared/unointerfaceproxy.hxx"
38 #include "bridges/cpp_uno/shared/vtables.hxx"
39 
40 #include "share.hxx"
41 
42 using namespace ::rtl;
43 using namespace ::com::sun::star::uno;
44 
45 namespace
46 {
47 
48 //==================================================================================================
49 // The call instruction within the asm section of callVirtualMethod may throw
50 // exceptions.  So that the compiler handles this correctly, it is important
51 // that (a) callVirtualMethod might call dummy_can_throw_anything (although this
52 // never happens at runtime), which in turn can throw exceptions, and (b)
53 // callVirtualMethod is not inlined at its call site (so that any exceptions are
54 // caught which are thrown from the instruction calling callVirtualMethod):
55 void callVirtualMethod(
56     void * pAdjustedThisPtr,
57     sal_Int32 nVtableIndex,
58     void * pRegisterReturn,
59     typelib_TypeDescription * pReturnTypeDescr, bool bSimpleReturn,
60     sal_Int32 * pStackLongs,
61     sal_Int32 nStackLongs ) __attribute__((noinline));
62 
63 void callVirtualMethod(
64     void * pAdjustedThisPtr,
65     sal_Int32 nVtableIndex,
66     void * pRegisterReturn,
67     typelib_TypeDescription * pReturnTypeDescr, bool bSimpleReturn,
68     sal_Int32 * pStackLongs,
69     sal_Int32 nStackLongs )
70 {
71 	// parameter list is mixed list of * and values
72 	// reference parameters are pointers
73 
74 	OSL_ENSURE( pStackLongs && pAdjustedThisPtr, "### null ptr!" );
75 	OSL_ENSURE( (sizeof(void *) == 4) && (sizeof(sal_Int32) == 4), "### unexpected size of int!" );
76 	OSL_ENSURE( nStackLongs && pStackLongs, "### no stack in callVirtualMethod !" );
77 
78     // never called
79     if (! pAdjustedThisPtr) CPPU_CURRENT_NAMESPACE::dummy_can_throw_anything("xxx"); // address something
80 
81 	volatile long edx = 0, eax = 0; // for register returns
82     void * stackptr;
83 	asm volatile (
84         "mov   %%esp, %6\n\t"
85 		"mov   %0, %%eax\n\t"
86 		"mov   %%eax, %%edx\n\t"
87                 // stack padding to keep stack aligned:
88 		"shl   $2, %%eax\n\t"
89 		"neg   %%eax\n\t"
90 		"add   %%esp, %%eax\n\t"
91 		"and   $0xf, %%eax\n\t"
92 		"sub   %%eax, %%esp\n\t"
93                 // copy:
94 		"mov   %%edx, %%eax\n\t"
95 		"dec   %%edx\n\t"
96 		"shl   $2, %%edx\n\t"
97 		"add   %1, %%edx\n"
98 		"Lcopy:\n\t"
99 		"pushl 0(%%edx)\n\t"
100 		"sub   $4, %%edx\n\t"
101 		"dec   %%eax\n\t"
102 		"jne   Lcopy\n\t"
103 		// do the actual call
104 		"mov   %2, %%edx\n\t"
105 		"mov   0(%%edx), %%edx\n\t"
106 		"mov   %3, %%eax\n\t"
107 		"shl   $2, %%eax\n\t"
108 		"add   %%eax, %%edx\n\t"
109 		"mov   0(%%edx), %%edx\n\t"
110 		"call  *%%edx\n\t"
111 		// save return registers
112  		"mov   %%eax, %4\n\t"
113  		"mov   %%edx, %5\n\t"
114 		// cleanup stack
115         "mov   %6, %%esp\n\t"
116 		:
117         : "m"(nStackLongs), "m"(pStackLongs), "m"(pAdjustedThisPtr),
118           "m"(nVtableIndex), "m"(eax), "m"(edx), "m"(stackptr)
119         : "eax", "edx" );
120 	switch( pReturnTypeDescr->eTypeClass )
121 	{
122     case typelib_TypeClass_VOID:
123         break;
124     case typelib_TypeClass_HYPER:
125     case typelib_TypeClass_UNSIGNED_HYPER:
126         ((long*)pRegisterReturn)[1] = edx;
127     case typelib_TypeClass_LONG:
128     case typelib_TypeClass_UNSIGNED_LONG:
129     case typelib_TypeClass_CHAR:
130     case typelib_TypeClass_ENUM:
131         ((long*)pRegisterReturn)[0] = eax;
132         break;
133     case typelib_TypeClass_SHORT:
134     case typelib_TypeClass_UNSIGNED_SHORT:
135         *(unsigned short*)pRegisterReturn = eax;
136         break;
137     case typelib_TypeClass_BOOLEAN:
138     case typelib_TypeClass_BYTE:
139         *(unsigned char*)pRegisterReturn = eax;
140         break;
141     case typelib_TypeClass_FLOAT:
142         asm ( "fstps %0" : : "m"(*(char *)pRegisterReturn) );
143         break;
144     case typelib_TypeClass_DOUBLE:
145         asm ( "fstpl %0\n\t" : : "m"(*(char *)pRegisterReturn) );
146         break;
147     default: {
148         sal_Int32 const nRetSize = pReturnTypeDescr->nSize;
149         if (bSimpleReturn && nRetSize <= 8 && nRetSize > 0) {
150             if (nRetSize > 4)
151                 static_cast<long *>(pRegisterReturn)[1] = edx;
152             static_cast<long *>(pRegisterReturn)[0] = eax;
153         }
154         break;
155     }
156 	}
157 }
158 
159 //==================================================================================================
160 static void cpp_call(
161 	bridges::cpp_uno::shared::UnoInterfaceProxy * pThis,
162     bridges::cpp_uno::shared::VtableSlot aVtableSlot,
163 	typelib_TypeDescriptionReference * pReturnTypeRef,
164 	sal_Int32 nParams, typelib_MethodParameter * pParams,
165 	void * pUnoReturn, void * pUnoArgs[], uno_Any ** ppUnoExc )
166 {
167   	// max space for: [complex ret ptr], values|ptr ...
168   	char * pCppStack		=
169   		(char *)alloca( sizeof(sal_Int32) + ((nParams+2) * sizeof(sal_Int64)) );
170   	char * pCppStackStart	= pCppStack;
171 
172 	// return
173 	typelib_TypeDescription * pReturnTypeDescr = 0;
174 	TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef );
175 	OSL_ENSURE( pReturnTypeDescr, "### expected return type description!" );
176 
177 	void * pCppReturn = 0; // if != 0 && != pUnoReturn, needs reconversion
178 	bool bSimpleReturn = true;
179 
180 	if (pReturnTypeDescr)
181 	{
182         bSimpleReturn = CPPU_CURRENT_NAMESPACE::isSimpleReturnType(
183             pReturnTypeDescr);
184 		if (bSimpleReturn)
185 		{
186 			pCppReturn = pUnoReturn; // direct way for simple types
187 		}
188 		else
189 		{
190             pCppReturn = (bridges::cpp_uno::shared::relatesToInterfaceType(
191                               pReturnTypeDescr )
192                           ? alloca( pReturnTypeDescr->nSize )
193                           : pUnoReturn); // direct way
194             // complex return via ptr
195             *(void **)pCppStack = pCppReturn;
196             pCppStack += sizeof(void *);
197 		}
198 	}
199 	// push this
200     void * pAdjustedThisPtr = reinterpret_cast< void ** >(pThis->getCppI())
201         + aVtableSlot.offset;
202 	*(void**)pCppStack = pAdjustedThisPtr;
203 	pCppStack += sizeof( void* );
204 
205 	// stack space
206 	OSL_ENSURE( sizeof(void *) == sizeof(sal_Int32), "### unexpected size!" );
207 	// args
208 	void ** pCppArgs  = (void **)alloca( 3 * sizeof(void *) * nParams );
209 	// indizes of values this have to be converted (interface conversion cpp<=>uno)
210 	sal_Int32 * pTempIndizes = (sal_Int32 *)(pCppArgs + nParams);
211 	// type descriptions for reconversions
212 	typelib_TypeDescription ** ppTempParamTypeDescr = (typelib_TypeDescription **)(pCppArgs + (2 * nParams));
213 
214 	sal_Int32 nTempIndizes   = 0;
215 
216 	for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos )
217 	{
218 		const typelib_MethodParameter & rParam = pParams[nPos];
219 		typelib_TypeDescription * pParamTypeDescr = 0;
220 		TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef );
221 
222 		if (!rParam.bOut
223             && bridges::cpp_uno::shared::isSimpleType( pParamTypeDescr ))
224 		{
225 			uno_copyAndConvertData( pCppArgs[nPos] = pCppStack, pUnoArgs[nPos], pParamTypeDescr,
226 									pThis->getBridge()->getUno2Cpp() );
227 
228 			switch (pParamTypeDescr->eTypeClass)
229 			{
230 			case typelib_TypeClass_HYPER:
231 			case typelib_TypeClass_UNSIGNED_HYPER:
232 			case typelib_TypeClass_DOUBLE:
233 				pCppStack += sizeof(sal_Int32); // extra long
234             default:
235                 break;
236 			}
237 			// no longer needed
238 			TYPELIB_DANGER_RELEASE( pParamTypeDescr );
239 		}
240 		else // ptr to complex value | ref
241 		{
242 			if (! rParam.bIn) // is pure out
243 			{
244 				// cpp out is constructed mem, uno out is not!
245 				uno_constructData(
246 					*(void **)pCppStack = pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ),
247 					pParamTypeDescr );
248 				pTempIndizes[nTempIndizes] = nPos; // default constructed for cpp call
249 				// will be released at reconversion
250 				ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr;
251 			}
252 			// is in/inout
253 			else if (bridges::cpp_uno::shared::relatesToInterfaceType(
254                          pParamTypeDescr ))
255 			{
256 				uno_copyAndConvertData(
257 					*(void **)pCppStack = pCppArgs[nPos] = alloca( pParamTypeDescr->nSize ),
258 					pUnoArgs[nPos], pParamTypeDescr,
259                     pThis->getBridge()->getUno2Cpp() );
260 
261 				pTempIndizes[nTempIndizes] = nPos; // has to be reconverted
262 				// will be released at reconversion
263 				ppTempParamTypeDescr[nTempIndizes++] = pParamTypeDescr;
264 			}
265 			else // direct way
266 			{
267 				*(void **)pCppStack = pCppArgs[nPos] = pUnoArgs[nPos];
268 				// no longer needed
269 				TYPELIB_DANGER_RELEASE( pParamTypeDescr );
270 			}
271 		}
272 		pCppStack += sizeof(sal_Int32); // standard parameter length
273 	}
274 
275 	try
276 	{
277 		OSL_ENSURE( !( (pCppStack - pCppStackStart ) & 3), "UNALIGNED STACK !!! (Please DO panic)" );
278 		callVirtualMethod(
279 			pAdjustedThisPtr, aVtableSlot.index,
280 			pCppReturn, pReturnTypeDescr, bSimpleReturn,
281 			(sal_Int32 *)pCppStackStart, (pCppStack - pCppStackStart) / sizeof(sal_Int32) );
282 		// NO exception occured...
283 		*ppUnoExc = 0;
284 
285 		// reconvert temporary params
286 		for ( ; nTempIndizes--; )
287 		{
288 			sal_Int32 nIndex = pTempIndizes[nTempIndizes];
289 			typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndizes];
290 
291 			if (pParams[nIndex].bIn)
292 			{
293 				if (pParams[nIndex].bOut) // inout
294 				{
295 					uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 ); // destroy uno value
296 					uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr,
297 											pThis->getBridge()->getCpp2Uno() );
298 				}
299 			}
300 			else // pure out
301 			{
302 				uno_copyAndConvertData( pUnoArgs[nIndex], pCppArgs[nIndex], pParamTypeDescr,
303 										pThis->getBridge()->getCpp2Uno() );
304 			}
305 			// destroy temp cpp param => cpp: every param was constructed
306 			uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release );
307 
308 			TYPELIB_DANGER_RELEASE( pParamTypeDescr );
309 		}
310 		// return value
311 		if (pCppReturn && pUnoReturn != pCppReturn)
312 		{
313 			uno_copyAndConvertData( pUnoReturn, pCppReturn, pReturnTypeDescr,
314 									pThis->getBridge()->getCpp2Uno() );
315 			uno_destructData( pCppReturn, pReturnTypeDescr, cpp_release );
316 		}
317 	}
318  	catch (...)
319  	{
320 #if OSL_DEBUG_LEVEL > 1
321 	fprintf( stderr, "caught C++ exception\n" );
322 #endif
323   		// fill uno exception
324 		fillUnoException( CPPU_CURRENT_NAMESPACE::__cxa_get_globals()->caughtExceptions, *ppUnoExc, pThis->getBridge()->getCpp2Uno() );
325 
326 		// temporary params
327 		for ( ; nTempIndizes--; )
328 		{
329 			sal_Int32 nIndex = pTempIndizes[nTempIndizes];
330 			// destroy temp cpp param => cpp: every param was constructed
331 			uno_destructData( pCppArgs[nIndex], ppTempParamTypeDescr[nTempIndizes], cpp_release );
332 			TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndizes] );
333 		}
334 		// return type
335 		if (pReturnTypeDescr)
336 			TYPELIB_DANGER_RELEASE( pReturnTypeDescr );
337 	}
338 }
339 
340 }
341 
342 namespace CPPU_CURRENT_NAMESPACE {
343 bool isSimpleReturnType(typelib_TypeDescription * pTD, bool recursive)
344 {
345     if (bridges::cpp_uno::shared::isSimpleType( pTD ))
346         return true;
347     // Only structs of exactly 1, 2, 4, or 8 bytes are returned through
348     // registers, see <http://developer.apple.com/documentation/DeveloperTools/
349     // Conceptual/LowLevelABI/Articles/IA32.html>:
350     if (pTD->eTypeClass == typelib_TypeClass_STRUCT &&
351         (recursive || pTD->nSize <= 2 || pTD->nSize == 4 || pTD->nSize == 8))
352     {
353         typelib_CompoundTypeDescription *const pCompTD =
354             (typelib_CompoundTypeDescription *) pTD;
355         for ( sal_Int32 pos = pCompTD->nMembers; pos--; ) {
356             typelib_TypeDescription * pMemberTD = 0;
357             TYPELIB_DANGER_GET( &pMemberTD, pCompTD->ppTypeRefs[pos] );
358             bool const b = isSimpleReturnType(pMemberTD, true);
359             TYPELIB_DANGER_RELEASE( pMemberTD );
360             if (! b)
361                 return false;
362         }
363         return true;
364     }
365     return false;
366 }
367 }
368 
369 //==================================================================================================
370 
371 namespace bridges { namespace cpp_uno { namespace shared {
372 void unoInterfaceProxyDispatch(
373 	uno_Interface * pUnoI, const typelib_TypeDescription * pMemberDescr,
374 	void * pReturn, void * pArgs[], uno_Any ** ppException )
375 {
376 	// is my surrogate
377 	bridges::cpp_uno::shared::UnoInterfaceProxy * pThis
378         = static_cast< bridges::cpp_uno::shared::UnoInterfaceProxy * >(pUnoI);
379 
380 	switch (pMemberDescr->eTypeClass)
381 	{
382 	case typelib_TypeClass_INTERFACE_ATTRIBUTE:
383 	{
384         VtableSlot aVtableSlot(
385             getVtableSlot(
386                 reinterpret_cast<
387                     typelib_InterfaceAttributeTypeDescription const * >(
388                         pMemberDescr)));
389 		if (pReturn)
390 		{
391 			// dependent dispatch
392 			cpp_call(
393 				pThis, aVtableSlot,
394 				((typelib_InterfaceAttributeTypeDescription *)pMemberDescr)->pAttributeTypeRef,
395 				0, 0, // no params
396 				pReturn, pArgs, ppException );
397 		}
398 		else
399 		{
400 			// is SET
401 			typelib_MethodParameter aParam;
402 			aParam.pTypeRef =
403 				((typelib_InterfaceAttributeTypeDescription *)pMemberDescr)->pAttributeTypeRef;
404 			aParam.bIn		= sal_True;
405 			aParam.bOut		= sal_False;
406 
407 			typelib_TypeDescriptionReference * pReturnTypeRef = 0;
408 			OUString aVoidName( RTL_CONSTASCII_USTRINGPARAM("void") );
409 			typelib_typedescriptionreference_new(
410 				&pReturnTypeRef, typelib_TypeClass_VOID, aVoidName.pData );
411 
412 			// dependent dispatch
413             aVtableSlot.index += 1; // get, then set method
414 			cpp_call(
415 				pThis, aVtableSlot,
416 				pReturnTypeRef,
417 				1, &aParam,
418 				pReturn, pArgs, ppException );
419 
420 			typelib_typedescriptionreference_release( pReturnTypeRef );
421 		}
422 
423 		break;
424 	}
425 	case typelib_TypeClass_INTERFACE_METHOD:
426 	{
427         VtableSlot aVtableSlot(
428             getVtableSlot(
429                 reinterpret_cast<
430                     typelib_InterfaceMethodTypeDescription const * >(
431                         pMemberDescr)));
432 		switch (aVtableSlot.index)
433 		{
434 			// standard calls
435 		case 1: // acquire uno interface
436 			(*pUnoI->acquire)( pUnoI );
437 			*ppException = 0;
438 			break;
439 		case 2: // release uno interface
440 			(*pUnoI->release)( pUnoI );
441 			*ppException = 0;
442 			break;
443 		case 0: // queryInterface() opt
444 		{
445 			typelib_TypeDescription * pTD = 0;
446 			TYPELIB_DANGER_GET( &pTD, reinterpret_cast< Type * >( pArgs[0] )->getTypeLibType() );
447 			if (pTD)
448 			{
449                 uno_Interface * pInterface = 0;
450                 (*pThis->pBridge->getUnoEnv()->getRegisteredInterface)(
451                     pThis->pBridge->getUnoEnv(),
452                     (void **)&pInterface, pThis->oid.pData, (typelib_InterfaceTypeDescription *)pTD );
453 
454                 if (pInterface)
455                 {
456                     ::uno_any_construct(
457                         reinterpret_cast< uno_Any * >( pReturn ),
458                         &pInterface, pTD, 0 );
459                     (*pInterface->release)( pInterface );
460                     TYPELIB_DANGER_RELEASE( pTD );
461                     *ppException = 0;
462                     break;
463                 }
464                 TYPELIB_DANGER_RELEASE( pTD );
465             }
466 		} // else perform queryInterface()
467 		default:
468 			// dependent dispatch
469 			cpp_call(
470 				pThis, aVtableSlot,
471 				((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->pReturnTypeRef,
472 				((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->nParams,
473 				((typelib_InterfaceMethodTypeDescription *)pMemberDescr)->pParams,
474 				pReturn, pArgs, ppException );
475 		}
476 		break;
477 	}
478 	default:
479 	{
480 		::com::sun::star::uno::RuntimeException aExc(
481 			OUString( RTL_CONSTASCII_USTRINGPARAM("illegal member type description!") ),
482 			::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >() );
483 
484 		Type const & rExcType = ::getCppuType( &aExc );
485 		// binary identical null reference
486 		::uno_type_any_construct( *ppException, &aExc, rExcType.getTypeLibType(), 0 );
487 	}
488 	}
489 }
490 
491 } } }
492