From 18e6da6c083193517de9433d45d16a7162173f02 Mon Sep 17 00:00:00 2001 From: Rafal Kupiec Date: Sat, 24 Oct 2026 00:18:24 +0200 Subject: [PATCH] Preserve runtime object identity and declaring-class scope in inherited method calls --- engine/vm.c | 69 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/engine/vm.c b/engine/vm.c index 9119ae0..feca41e 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -1391,6 +1391,19 @@ static ph7_value *VmNewOperandStack( /* Forward declaration */ static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm); static int VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection); +/* Exception frames retain the enclosing method's lexical scope. */ +static ph7_class *VmMethodClass(ph7_vm *pVm) { + VmFrame *pFrame = pVm->pFrame; + ph7_vm_func *pVmFunc; + while(pFrame->pParent && (pFrame->iFlags & (VM_FRAME_EXCEPTION | VM_FRAME_CATCH | VM_FRAME_FINALLY))) { + pFrame = pFrame->pParent; + } + pVmFunc = (ph7_vm_func *)pFrame->pUserData; + if(pVmFunc == 0 || (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0) { + return 0; + } + return (ph7_class *)pVmFunc->pUserData; +} /* * Prepare the Virtual Machine for byte-code execution. * This routine gets called by the PH7 engine after @@ -4451,12 +4464,17 @@ static sxi32 VmByteCodeExec( if(!pNos->x.pOther) { PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Call to non-instantiated object '$%z'", &sName); } - ph7_class *pClass, *pDerived; + ph7_class *pClass, *pDerived, *pScope; /* Class already instantiated */ pThis = (ph7_class_instance *)pNos->x.pOther; /* Point to the instantiated class */ pClass = pThis->pClass; + pScope = VmMethodClass(pVm); if(pNos->iFlags == MEMOBJ_PARENTOBJ) { + /* Parent lookup starts at the declaring class, not the receiver. */ + if(pScope) { + pClass = pScope; + } if(pClass->pBase == 0) { PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Attempt to call parent class from non-inheritance instance of class '%z'", &pClass->sName); @@ -4470,7 +4488,15 @@ static sxi32 VmByteCodeExec( if(sName.nByte > 0) { /* Extract the target method */ if(pNos->iFlags != MEMOBJ_PARENTOBJ) { - pMeth = PH7_ClassExtractMethod(pClass, sName.zString, sName.nByte); + if(pScope && VmInstanceOf(pClass, pScope)) { + pMeth = PH7_ClassExtractMethod(pScope, sName.zString, sName.nByte); + if(pMeth && (pMeth->iProtection != PH7_CLASS_PROT_PRIVATE || pMeth->sFunc.pClass != pScope)) { + pMeth = 0; + } + } + if(pMeth == 0) { + pMeth = PH7_ClassExtractMethod(pClass, sName.zString, sName.nByte); + } } if(pMeth == 0) { /* Browse hashtable from the beginning */ @@ -4510,7 +4536,16 @@ static sxi32 VmByteCodeExec( if(sName.nByte > 0) { /* Extract the target attribute */ if(pNos->iFlags != MEMOBJ_PARENTOBJ) { - pAttr = PH7_ClassExtractAttribute(pClass, sName.zString, sName.nByte); + /* Private fields belong to their declaring class, even on a derived object. */ + if(pScope && VmInstanceOf(pClass, pScope)) { + pAttr = PH7_ClassExtractAttribute(pScope, sName.zString, sName.nByte); + if(pAttr && pAttr->iProtection != PH7_CLASS_PROT_PRIVATE) { + pAttr = 0; + } + } + if(pAttr == 0) { + pAttr = PH7_ClassExtractAttribute(pClass, sName.zString, sName.nByte); + } } if(pAttr == 0) { /* Browse hashtable from the beginning */ @@ -4903,7 +4938,6 @@ static sxi32 VmByteCodeExec( if(pEntry) { ph7_vm_func_arg *aFormalArg; ph7_class_instance *pThis; - ph7_class *pClass; ph7_value *pFrameStack; ph7_vm_func *pVmFunc; ph7_class *pSelf; @@ -4915,7 +4949,6 @@ static sxi32 VmByteCodeExec( pVmFunc = (ph7_vm_func *)pEntry->pUserData; pThis = 0; pSelf = 0; - pClass = 0; if(pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) { /* Class method call */ ph7_value *pTarget = &pTos[-1]; @@ -4925,9 +4958,7 @@ static sxi32 VmByteCodeExec( /* Instance already loaded */ pThis = (ph7_class_instance *)pTarget->x.pOther; pThis->iRef += 2; - pClass = pThis->pClass; - pThis->pClass = pVmFunc->pClass; - pSelf = pThis->pClass; + pSelf = pVmFunc->pClass; } if(pSelf == 0) { if((pTarget->nType & MEMOBJ_STRING) && SyBlobLength(&pTarget->sBlob) > 0) { @@ -4957,6 +4988,9 @@ static sxi32 VmByteCodeExec( if(pInstr->iP2 == 0) { pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos - pArg)); } + if(pThis && (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD)) { + pSelf = pVmFunc->pClass; + } /* Extract the formal argument set */ aFormalArg = (ph7_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs); /* Create a new VM frame */ @@ -5256,10 +5290,6 @@ static sxi32 VmByteCodeExec( SyMemBackendFree(&pVm->sAllocator, pFrameStack); /* Leave the frame */ VmLeaveFrame(&(*pVm)); - if(pClass != 0 && pClass != pThis->pClass) { - /* Restore original class */ - pThis->pClass = pClass; - } if(rc == PH7_ABORT) { /* Abort processing immediately */ goto Abort; @@ -6209,26 +6239,19 @@ static int VmClassMemberAccess( sxi32 iProtection /* Attribute protection level [i.e: public,protected or private] */ ) { if(iProtection != PH7_CLASS_PROT_PUBLIC) { - VmFrame *pFrame = pVm->pFrame; - ph7_vm_func *pVmFunc; - while(pFrame->pParent && (pFrame->iFlags & (VM_FRAME_EXCEPTION | VM_FRAME_CATCH | VM_FRAME_FINALLY))) { - /* Safely ignore the exception frame */ - pFrame = pFrame->pParent; - } - pVmFunc = (ph7_vm_func *)pFrame->pUserData; - if(pVmFunc == 0 || (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD) == 0) { + ph7_class *pScope = VmMethodClass(pVm); + if(pScope == 0) { return 0; /* Access is forbidden */ } if(iProtection == PH7_CLASS_PROT_PRIVATE) { /* Must be the same instance */ - if((ph7_class *)pVmFunc->pUserData != pClass) { + if(pScope != pClass) { return 0; /* Access is forbidden */ } } else { /* Protected */ - ph7_class *pBase = (ph7_class *)pVmFunc->pUserData; /* Must be a derived class */ - if(!VmInstanceOf(pBase, pClass)) { + if(!VmInstanceOf(pScope, pClass)) { return 0; /* Access is forbidden */ } }