diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index addc07f4b..c0a69eb6e 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -8792,20 +8792,27 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) ExpEmit selfemit; if (Function->Variants[0].Flags & VARF_Method) { -#if 0 - // [ZZ] - if (Function->Variants[0].Implementation && Function->Variants[0].Implementation->BarrierSide == FScopeBarrier::Side_Virtual) - { - // pass this even before Self, because otherwise we can't silently advance the arguments. - // this is not even implicit arguments. - build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(Function, ATAG_OBJECT)); - build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(CallingFunction, ATAG_OBJECT)); - count += 2; - } -#endif assert(Self != nullptr); selfemit = Self->Emit(build); assert((selfemit.RegType == REGT_POINTER) || (selfemit.Fixed && selfemit.Target)); + + int innerside = FScopeBarrier::SideFromFlags(Function->Variants[0].Flags); + + if (innerside == FScopeBarrier::Side_Virtual) + { + auto selfside = FScopeBarrier::SideFromObjectFlags(static_cast(Self->ValueType)->PointedType->ObjectFlags); + + int outerside = FScopeBarrier::SideFromFlags(CallingFunction->Variants[0].Flags); + if (outerside == FScopeBarrier::Side_Virtual) + outerside = FScopeBarrier::SideFromObjectFlags(CallingFunction->OwningClass->ObjectFlags); + + if (selfside != outerside && (selfside == FScopeBarrier::Side_Play || selfside == FScopeBarrier::Side_UI)) // if the self pointer and the calling functions have the same scope the check here is not needed. + { + // Check the self object against the calling function's flags at run time + build->Emit(OP_SCOPE, selfemit.RegNum, outerside + 1, build->GetConstantAddress(vmfunc, ATAG_OBJECT)); + } + } + if (selfemit.Fixed && selfemit.Target) { // Address of a local variable. @@ -8870,6 +8877,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { selfemit.Free(build); ExpEmit funcreg(build, REGT_POINTER); + build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex); if (EmitTail) { // Tail call diff --git a/src/scripting/backend/scopebarrier.cpp b/src/scripting/backend/scopebarrier.cpp index 3ba207fba..29caa2457 100644 --- a/src/scripting/backend/scopebarrier.cpp +++ b/src/scripting/backend/scopebarrier.cpp @@ -179,16 +179,10 @@ void FScopeBarrier::ValidateNew(PClass* cls, int outerside) if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) // "cannot construct ui class ... from data context" ThrowAbortException(X_OTHER, "Cannot construct %s class %s from %s context", FScopeBarrier::StringFromSide(innerside), cls->TypeName.GetChars(), FScopeBarrier::StringFromSide(outerside)); } -// this can be imported in vmexec.h -void FScopeBarrier::ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype) + +void FScopeBarrier::ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside) { - // [ZZ] anonymous blocks have 0 variants, so give them Side_Virtual. - int outerside = callingfunc->Variants.Size() ? FScopeBarrier::SideFromFlags(callingfunc->Variants[0].Flags) : FScopeBarrier::Side_Virtual; - if (outerside == FScopeBarrier::Side_Virtual) - outerside = FScopeBarrier::SideFromObjectFlags(callingfunc->OwningClass->ObjectFlags); - int innerside = FScopeBarrier::SideFromFlags(calledfunc->Variants[0].Flags); - if (innerside == FScopeBarrier::Side_Virtual) - innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); + int innerside = FScopeBarrier::SideFromObjectFlags(selftype->ObjectFlags); if ((outerside != innerside) && (innerside != FScopeBarrier::Side_PlainData)) - ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->SymbolName.GetChars(), FScopeBarrier::StringFromSide(outerside)); + ThrowAbortException(X_OTHER, "Cannot call %s function %s from %s context", FScopeBarrier::StringFromSide(innerside), calledfunc->PrintableName.GetChars(), FScopeBarrier::StringFromSide(outerside)); } \ No newline at end of file diff --git a/src/scripting/backend/scopebarrier.h b/src/scripting/backend/scopebarrier.h index af3951a8d..da0bc6d1d 100644 --- a/src/scripting/backend/scopebarrier.h +++ b/src/scripting/backend/scopebarrier.h @@ -54,6 +54,7 @@ struct FScopeBarrier // this is called from vmexec.h static void ValidateNew(PClass* cls, int scope); - static void ValidateCall(PFunction* calledfunc, PFunction* callingfunc, PClass* selftype); + static void ValidateCall(PClass* selftype, VMFunction *calledfunc, int outerside); + }; diff --git a/src/scripting/vm/vmexec.h b/src/scripting/vm/vmexec.h index a822aef53..aec532c37 100644 --- a/src/scripting/vm/vmexec.h +++ b/src/scripting/vm/vmexec.h @@ -648,6 +648,13 @@ begin: reg.a[a] = p->Virtuals[C]; } NEXTOP; + OP(SCOPE) : + { + ASSERTA(a); ASSERTA(C); + FScopeBarrier::ValidateCall(((DObject*)a)->GetClass(), (VMFunction*)C, B); + } + NEXTOP; + OP(CALL_K): ASSERTKA(a); assert(konstatag[a] == ATAG_OBJECT); @@ -665,19 +672,6 @@ begin: int numret; b = B; -#if 0 - // [ZZ] hax! - if (call->BarrierSide == 3) // :( - this is Side_Virtual. Side_Virtual should receive special arguments. - { - PFunction* calledfunc = (PFunction*)(reg.param + f->NumParam - b)[0].a; - PFunction* callingfunc = (PFunction*)(reg.param + f->NumParam - b)[1].a; - DObject* dobj = (DObject*)(reg.param + f->NumParam - b)[2].a; // this is the self pointer. it should be in, since Side_Virtual functions are always non-static methods. - PClass* selftype = dobj->GetClass(); - FScopeBarrier::ValidateCall(calledfunc, callingfunc, selftype); - b -= 2; - } -#endif - FillReturns(reg, f, returns, pc+1, C); if (call->VarFlags & VARF_Native) { diff --git a/src/scripting/vm/vmops.h b/src/scripting/vm/vmops.h index b9cba87d2..18a444677 100644 --- a/src/scripting/vm/vmops.h +++ b/src/scripting/vm/vmops.h @@ -106,6 +106,7 @@ xx(PARAMI, parami, I24, NOP, 0, 0), // push immediate, signed integer for func xx(CALL, call, RPI8I8, NOP, 0, 0), // Call function pkA with parameter count B and expected result count C xx(CALL_K, call, KPI8I8, CALL, 1, REGT_POINTER), xx(VTBL, vtbl, RPRPI8, NOP, 0, 0), // dereferences a virtual method table. +xx(SCOPE, scope, RPI8, NOP, 0, 0), // Scope check at runtime. xx(TAIL, tail, RPI8, NOP, 0, 0), // Call+Ret in a single instruction xx(TAIL_K, tail, KPI8, TAIL, 1, REGT_POINTER), xx(RESULT, result, __BCP, NOP, 0, 0), // Result should go in register encoded in BC (in caller, after CALL)