The the fix wasn't quite that simple. The CallSystemFunction() has to be changed as well. The engine was right in releasing the object twice, but CallSystemFunction() wasn't adding a reference as it should when storing the object handle in objectRegister. Also CallSystemFunction() was erronously allocating memory for a new object, thinking it would return the object by value to the VM.
as_callfunc_x86.cpp
int CallSystemFunction(int id, asCContext *context, void *objectPointer){ id = -id - 1; asQWORD retTemp = 0; asDWORD retDW = 0; asQWORD retQW = 0; asCScriptEngine *engine = context->engine; asSSystemFunctionInterface *sysFunc = engine->systemFunctionInterfaces[id]; asCScriptFunction *descr = engine->systemFunctions[id]; int callConv = sysFunc->callConv; void *func = (void*)sysFunc->func; int paramSize = sysFunc->paramSize; asDWORD *args = context->stackPointer; void *retPointer = 0; void *obj = 0; asDWORD *vftable; int popSize = paramSize; context->objectType = descr->returnType.objectType; if( descr->returnType.IsObject() && !descr->returnType.isReference && !descr->returnType.isExplicitHandle ) { // Allocate the memory for the object retPointer = malloc(descr->returnType.GetSizeInMemoryBytes()); if( sysFunc->hostReturnInMemory ) { // The return is made in memory callConv++; } } if( callConv >= ICC_THISCALL ) { if( objectPointer ) obj = objectPointer; else { // The object pointer should be popped from the context stack popSize++; // Check for null pointer obj = (void*)*(args + paramSize); if( obj == 0 ) { context->SetInternalException(TXT_NULL_POINTER_ACCESS); free(retPointer); return 0; } // Add the base offset for multiple inheritance obj = (void*)(int(obj) + sysFunc->baseOffset); } } asDWORD paramBuffer[64]; if( sysFunc->takesObjByVal ) { paramSize = 0; int spos = 0; int dpos = 1; for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) { if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].isReference ) {#ifdef COMPLEX_OBJS_PASSED_BY_REF if( descr->parameterTypes[n].objectType->flags & COMPLEX_MASK ) { paramBuffer[dpos++] = args[spos++]; paramSize++; } else#endif { // Copy the object's memory to the buffer memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); // Delete the original memory free(*(char**)(args+spos)); spos++; dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); } } else { // Copy the value directly paramBuffer[dpos++] = args[spos++]; if( descr->parameterTypes[n].GetSizeInMemoryDWords() > 1 ) paramBuffer[dpos++] = args[spos++]; paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); } } // Keep a free location at the beginning args = ¶mBuffer[1]; } context->isCallingSystemFunction = true; switch( callConv ) { case ICC_CDECL: retQW = CallCDeclFunctionQWord(args, paramSize<<2, (asDWORD)func); break; case ICC_CDECL_RETURNINMEM: retQW = CallCDeclFunctionRetByRef(args, paramSize<<2, (asDWORD)func, retPointer); break; case ICC_STDCALL: retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (asDWORD)func); break; case ICC_STDCALL_RETURNINMEM: // Push the return pointer on the stack paramSize++; args--; *args = (asDWORD)retPointer; retQW = CallSTDCallFunctionQWord(args, paramSize<<2, (asDWORD)func); break; case ICC_THISCALL: retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, (asDWORD)func); break; case ICC_THISCALL_RETURNINMEM: retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, (asDWORD)func, retPointer); break; case ICC_VIRTUAL_THISCALL: // Get virtual function table from the object pointer vftable = *(asDWORD**)obj; retQW = CallThisCallFunctionQWord(obj, args, paramSize<<2, vftable[asDWORD(func)>>2]); break; case ICC_VIRTUAL_THISCALL_RETURNINMEM: // Get virtual function table from the object pointer vftable = *(asDWORD**)obj; retQW = CallThisCallFunctionRetByRef(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], retPointer); break; case ICC_CDECL_OBJLAST: retQW = CallCDeclFunctionQWordObjLast(obj, args, paramSize<<2, (asDWORD)func); break; case ICC_CDECL_OBJLAST_RETURNINMEM: // Call the system object method as a cdecl with the obj ref as the last parameter retQW = CallCDeclFunctionRetByRefObjLast(obj, args, paramSize<<2, (asDWORD)func, retPointer); break; case ICC_CDECL_OBJFIRST: // Call the system object method as a cdecl with the obj ref as the first parameter retQW = CallCDeclFunctionQWordObjFirst(obj, args, paramSize<<2, (asDWORD)func); break; case ICC_CDECL_OBJFIRST_RETURNINMEM: // Call the system object method as a cdecl with the obj ref as the first parameter retQW = CallCDeclFunctionRetByRefObjFirst(obj, args, paramSize<<2, (asDWORD)func, retPointer); break; default: context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); } context->isCallingSystemFunction = false;#ifdef COMPLEX_OBJS_PASSED_BY_REF if( sysFunc->takesObjByVal ) { // Need to free the complex objects passed by value args = context->stackPointer; int spos = 0; for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) { if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].isReference && (descr->parameterTypes[n].objectType->flags & COMPLEX_MASK) ) { void *obj = (void*)args[spos++]; asSTypeBehaviour *beh = &descr->parameterTypes[n].objectType->beh; if( beh->destruct ) engine->CallObjectMethod(obj, beh->destruct); free(obj); } else spos += descr->parameterTypes[n].GetSizeInMemoryDWords(); } }#endif // Store the returned value in our stack if( descr->returnType.IsObject() && !descr->returnType.isReference ) { if( descr->returnType.isExplicitHandle ) { context->objectRegister = (void*)retQW; // It's the responsibility of the application function to increase the reference // engine->CallObjectMethod(context->objectRegister, descr->returnType.objectType->beh.addref); } else { if( !sysFunc->hostReturnInMemory ) { // Copy the returned value to the pointer sent by the script engine if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)retPointer = (asDWORD)retQW; else *(asQWORD*)retPointer = retQW; } // Store the object in the register context->objectRegister = retPointer; } } else { // Store value in returnVal register if( sysFunc->hostReturnFloat ) { if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)&context->returnVal = GetReturnedFloat(); else context->returnVal = GetReturnedDouble(); } else if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)&context->returnVal = (asDWORD)retQW; else context->returnVal = retQW; } return popSize;}
as_callfunc_sh4.cpp
int CallSystemFunction(int id, asCContext *context, void *objectPointer) { id = -id - 1; asQWORD retQW = 0; asCScriptEngine *engine = context->engine; asSSystemFunctionInterface *sysFunc = engine->systemFunctionInterfaces[id]; asCScriptFunction *descr = engine->systemFunctions[id]; int callConv = sysFunc->callConv; void *func = (void*)sysFunc->func; int paramSize = sysFunc->paramSize; asDWORD *args = context->stackPointer; void *retPointer = 0; void *obj = 0; asDWORD *vftable; int popSize = paramSize; context->objectType = descr->returnType.objectType; if( descr->returnType.IsObject() && !descr->returnType.isReference && !descr->returnType.isExplicitHandle ) { // Allocate the memory for the object retPointer = malloc(descr->returnType.GetSizeInMemoryBytes()); sh4Args[AS_SH4_MAX_ARGS+1] = (asDWORD) retPointer; if( sysFunc->hostReturnInMemory ) { // The return is made in memory callConv++; } } if( callConv >= ICC_THISCALL ) { if( objectPointer ) obj = objectPointer; else { // The object pointer should be popped from the context stack popSize++; // Check for null pointer obj = (void*)*(args + paramSize); if( obj == 0 ) { context->SetInternalException(TXT_NULL_POINTER_ACCESS); free(retPointer); return 0; } // Add the base offset for multiple inheritance obj = (void*)(int(obj) + sysFunc->baseOffset); } } assert(descr->parameterTypes.GetLength() <= 32); // mark all float arguments int argBit = 1; int hostFlags = 0; int intArgs = 0; for( int a = 0; a < descr->parameterTypes.GetLength(); a++ ) { if (descr->parameterTypes[a].IsFloatType()) { hostFlags |= argBit; } else intArgs++; argBit <<= 1; } asDWORD paramBuffer[64]; if( sysFunc->takesObjByVal ) { paramSize = 0; int spos = 0; int dpos = 1; for( int n = 0; n < descr->parameterTypes.GetLength(); n++ ) { if( descr->parameterTypes[n].IsObject() && !descr->parameterTypes[n].isReference ) {#ifdef COMPLEX_OBJS_PASSED_BY_REF if( descr->parameterTypes[n].objectType->flags & COMPLEX_MASK ) { paramBuffer[dpos++] = args[spos++]; paramSize++; } else#endif { // Copy the object's memory to the buffer memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes()); // Delete the original memory free(*(char**)(args+spos)); spos++; dpos += descr->parameterTypes[n].GetSizeInMemoryDWords(); paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); } } else { // Copy the value directly paramBuffer[dpos++] = args[spos++]; if( descr->parameterTypes[n].GetSizeInMemoryDWords() > 1 ) paramBuffer[dpos++] = args[spos++]; paramSize += descr->parameterTypes[n].GetSizeInMemoryDWords(); } } // Keep a free location at the beginning args = ¶mBuffer[1]; } context->isCallingSystemFunction = true; switch( callConv ) { case ICC_CDECL: case ICC_CDECL_RETURNINMEM: case ICC_STDCALL: case ICC_STDCALL_RETURNINMEM: retQW = CallCDeclFunction(args, paramSize<<2, (asDWORD)func, hostFlags); break; case ICC_THISCALL: case ICC_THISCALL_RETURNINMEM: retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); break; case ICC_VIRTUAL_THISCALL: case ICC_VIRTUAL_THISCALL_RETURNINMEM: // Get virtual function table from the object pointer vftable = *(asDWORD**)obj; retQW = CallThisCallFunction(obj, args, paramSize<<2, vftable[asDWORD(func)>>2], hostFlags); break; case ICC_CDECL_OBJLAST: case ICC_CDECL_OBJLAST_RETURNINMEM: retQW = CallThisCallFunction_objLast(obj, args, paramSize<<2, (asDWORD)func, hostFlags); break; case ICC_CDECL_OBJFIRST: case ICC_CDECL_OBJFIRST_RETURNINMEM: retQW = CallThisCallFunction(obj, args, paramSize<<2, (asDWORD)func, hostFlags); break; default: context->SetInternalException(TXT_INVALID_CALLING_CONVENTION); } context->isCallingSystemFunction = false; // Store the returned value in our stack if( descr->returnType.IsObject() && !descr->returnType.isReference ) { if( descr->returnType.isExplicitHandle ) { context->objectRegister = (void*)retQW; // It's the responsibility of the application function to increase the reference // engine->CallObjectMethod(context->objectRegister, descr->returnType.objectType->beh.addref); } else { if( !sysFunc->hostReturnInMemory ) { // Copy the returned value to the pointer sent by the script engine if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)retPointer = (asDWORD)retQW; else *(asQWORD*)retPointer = retQW; } // Store the object in the register context->objectRegister = retPointer; } } else { // Store value in returnVal register if( sysFunc->hostReturnFloat ) { if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)&context->returnVal = GetReturnedFloat(); else context->returnVal = GetReturnedDouble(); } else if( sysFunc->hostReturnSize == 1 ) *(asDWORD*)&context->returnVal = (asDWORD)retQW; else context->returnVal = retQW; } return popSize;}
[edit]Changed the way object handles were treated upon return from the function.[/edit]
I applied the same changes to CallSystemFunction() in the SH4 file, but I haven't tested them.
I also noticed a possible memory leak in the SH4 file. When the arguments are prepared you have the #ifdef COMPLEX_OBJS_PASSED_BY_REF, but these objects are never deleted after the function returns. Please, take a look at how it works in the X86 file, and verify if the objects has to be deleted or not upon return from the function.
[Edited by - WitchLord on January 13, 2005 4:46:25 PM]