Implement cooperative parent calls across runtime C3 MRO and enforce lifecycle method rules
Build / AerScript (push) Successful in 41s
Build / AerScript (push) Successful in 41s
This commit is contained in:
+89
-49
@@ -811,6 +811,23 @@ static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags);
|
||||
static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
|
||||
static sxi32 VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
|
||||
static ph7_class *VmMethodClass(ph7_vm *pVm);
|
||||
static sxi32 VmCallClassMethod(ph7_vm *pVm, ph7_class_instance *pThis, ph7_class_method *pMethod,
|
||||
ph7_value *pResult, int nArg, ph7_value **apArg, sxi32 bInternal);
|
||||
/* Cooperative calls continue after the lexical owner in the receiver's C3 order. */
|
||||
static sxu32 VmMethodStart(ph7_vm *pVm, ph7_class *pLookup, sxi32 bParent) {
|
||||
sxu32 i;
|
||||
ph7_class *pScope;
|
||||
if(!bParent) {
|
||||
return 0;
|
||||
}
|
||||
pScope = VmMethodClass(pVm);
|
||||
for(i = 0; i < SySetUsed(&pLookup->aMro); i++) {
|
||||
if(*(ph7_class **)SySetAt(&pLookup->aMro, i) == pScope) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return SySetUsed(&pLookup->aMro);
|
||||
}
|
||||
/* Find a mounted name to transport a method call, not an eligible overload.
|
||||
* The caller must retain its lookup root and receiver for VmOverload(). */
|
||||
static ph7_class_method *VmMethodSeed(ph7_vm *pVm, ph7_class *pLookup, SyString *pName,
|
||||
@@ -819,7 +836,7 @@ static ph7_class_method *VmMethodSeed(ph7_vm *pVm, ph7_class *pLookup, SyString
|
||||
if(pPreferred && SyHashGet(&pVm->hFunction, pPreferred->sVmName.zString, pPreferred->sVmName.nByte)) {
|
||||
return pPreferred;
|
||||
}
|
||||
for(i = bParent ? 1 : 0; i < SySetUsed(&pLookup->aMro); i++) {
|
||||
for(i = VmMethodStart(pVm, pLookup, bParent); i < SySetUsed(&pLookup->aMro); i++) {
|
||||
ph7_class *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
|
||||
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pName->zString, pName->nByte);
|
||||
if(pMeth && pMeth->sFunc.pClass == pClass &&
|
||||
@@ -994,7 +1011,7 @@ static ph7_vm_func *VmOverload(
|
||||
}
|
||||
/* Walk C3 without modifying any declaration's overload chain. Only
|
||||
* accessible declarations participate in identical-signature hiding. */
|
||||
for(i = bParent ? 1 : 0; i < SySetUsed(&pLookup->aMro); i++) {
|
||||
for(i = VmMethodStart(pVm, pLookup, bParent); i < SySetUsed(&pLookup->aMro); i++) {
|
||||
ph7_class *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
|
||||
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pList->sName.zString, pList->sName.nByte);
|
||||
SyHashEntry *pEntry;
|
||||
@@ -2461,13 +2478,14 @@ static sxi32 VmByteCodeExec(
|
||||
if(pLastRef) {
|
||||
*pLastRef = pTos->nIdx;
|
||||
}
|
||||
/* Ensure we are in active frame. Force abort all loops */
|
||||
/* Unwind local loops, but let VmExecExceptionBlock() release
|
||||
* the handler without freeing its borrowed owner frames. */
|
||||
if(pInstr->p3) { /* Called from a return statement */
|
||||
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
|
||||
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & (VM_FRAME_ACTIVE | VM_FRAME_CATCH | VM_FRAME_FINALLY)) == 0) {
|
||||
VmLeaveFrame(&(*pVm));
|
||||
}
|
||||
} else if(pVm->pFrame->iFlags & VM_FRAME_LOOP) {
|
||||
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
|
||||
while(pVm->pFrame->pParent && (pVm->pFrame->iFlags & (VM_FRAME_ACTIVE | VM_FRAME_CATCH | VM_FRAME_FINALLY)) == 0) {
|
||||
VmLeaveFrame(&(*pVm));
|
||||
}
|
||||
}
|
||||
@@ -4696,8 +4714,8 @@ static sxi32 VmByteCodeExec(
|
||||
/* 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(pNos->iFlags == MEMOBJ_PARENTOBJ && !pInstr->iP2) {
|
||||
/* Preserve lexical parent lookup for fields. */
|
||||
if(pScope) {
|
||||
pClass = pScope;
|
||||
}
|
||||
@@ -4987,7 +5005,7 @@ static sxi32 VmByteCodeExec(
|
||||
SySetPut(&aArg, (const void *)&pArg);
|
||||
pArg++;
|
||||
}
|
||||
PH7_VmCallClassMethod(&(*pVm), pNew, pCons, 0, (int)SySetUsed(&aArg), (ph7_value **)SySetBasePtr(&aArg));
|
||||
VmCallClassMethod(&(*pVm), pNew, pCons, 0, (int)SySetUsed(&aArg), (ph7_value **)SySetBasePtr(&aArg), FALSE);
|
||||
/* TICKET 1433-52: Unsetting $this in the constructor body */
|
||||
if(pNew->iRef < 1) {
|
||||
pNew->iRef = 1;
|
||||
@@ -5156,8 +5174,7 @@ static sxi32 VmByteCodeExec(
|
||||
/* Instance already loaded */
|
||||
pThis = (ph7_class_instance *)pTarget->x.pOther;
|
||||
pLookup = pThis->pClass;
|
||||
if(pTarget->iFlags == MEMOBJ_PARENTOBJ && VmMethodClass(pVm)) {
|
||||
pLookup = VmMethodClass(pVm);
|
||||
if(pTarget->iFlags == MEMOBJ_PARENTOBJ) {
|
||||
bParent = 1;
|
||||
}
|
||||
pThis->iRef += 2;
|
||||
@@ -6775,13 +6792,14 @@ static int vm_builtin_is_subclass_of(ph7_context *pCtx, int nArg, ph7_value **ap
|
||||
* Return SXRET_OK if the method was successfully called.Any other
|
||||
* return value indicates failure.
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
||||
static sxi32 VmCallClassMethod(
|
||||
ph7_vm *pVm, /* Target VM */
|
||||
ph7_class_instance *pThis, /* Target class instance [i.e: Object in the PHP jargon]*/
|
||||
ph7_class_method *pMethod, /* Method name */
|
||||
ph7_value *pResult, /* Store method return value here. NULL otherwise */
|
||||
int nArg, /* Total number of given arguments */
|
||||
ph7_value **apArg /* Method arguments */
|
||||
ph7_value **apArg, /* Method arguments */
|
||||
sxi32 bInternal /* VM-owned lifecycle call, not source new */
|
||||
) {
|
||||
ph7_value *aStack;
|
||||
VmInstr aInstr[2];
|
||||
@@ -6804,7 +6822,7 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
||||
aStack[i].nIdx = apArg[i]->nIdx;
|
||||
}
|
||||
iCursor = nArg + 1;
|
||||
iEntry = 2; /* VM-owned method call: preserve access to lifecycle methods. */
|
||||
iEntry = bInternal ? 2 : 0;
|
||||
if(pThis) {
|
||||
/*
|
||||
* Push the class instance so that the '$this' variable will be available.
|
||||
@@ -6812,7 +6830,7 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
||||
pThis->iRef++; /* Increment reference count */
|
||||
aStack[i].x.pOther = pThis;
|
||||
aStack[i].nType = MEMOBJ_OBJ;
|
||||
if(SyStrncmp(pThis->pClass->sName.zString, "Program", 7) == 0) {
|
||||
if(bInternal && SyStrncmp(pThis->pClass->sName.zString, "Program", 7) == 0) {
|
||||
if((SyStrncmp(pMethod->sFunc.sName.zString, "main", 4) == 0) || (SyStrncmp(pMethod->sFunc.sName.zString, "__construct", 11) == 0)) {
|
||||
/* Do not overload entry point */
|
||||
iEntry |= 1;
|
||||
@@ -6860,6 +6878,10 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
||||
SyMemBackendFree(&pVm->sAllocator, aStack);
|
||||
return PH7_OK;
|
||||
}
|
||||
PH7_PRIVATE sxi32 PH7_VmCallClassMethod(ph7_vm *pVm, ph7_class_instance *pThis, ph7_class_method *pMethod,
|
||||
ph7_value *pResult, int nArg, ph7_value **apArg) {
|
||||
return VmCallClassMethod(pVm, pThis, pMethod, pResult, nArg, apArg, TRUE);
|
||||
}
|
||||
/*
|
||||
* Call a user defined or foreign function where the name of the function
|
||||
* is stored in the pFunc parameter and the given arguments are stored
|
||||
@@ -8400,27 +8422,61 @@ static int vm_builtin_debug_backtrace(ph7_context *pCtx, int nArg, ph7_value **a
|
||||
*/
|
||||
return PH7_OK;
|
||||
}
|
||||
/* Handlers run synchronously before the throwing calls unwind. Borrow their
|
||||
* owner's context without releasing the suspended calls or overwriting their
|
||||
* self stack slots when a handler invokes another method. */
|
||||
static sxi32 VmExecExceptionBlock(ph7_vm *pVm, ph7_exception *pException,
|
||||
ph7_exception_block *pCatch, ph7_class_instance *pThis) {
|
||||
VmFrame *pSavedFrame = pVm->pFrame;
|
||||
VmFrame *pOwner = pException->pFrame;
|
||||
VmFrame *pFrame;
|
||||
SySet sSelf, sSavedSelf;
|
||||
sxu32 nSelf = SySetUsed(&pVm->aSelf), i;
|
||||
sxi32 rc;
|
||||
for(pFrame = pSavedFrame; pFrame != pOwner; pFrame = pFrame->pParent) {
|
||||
ph7_vm_func *pFunc = (ph7_vm_func *)pFrame->pUserData;
|
||||
if((pFrame->iFlags & VM_FRAME_ACTIVE) && pFunc && (pFunc->iFlags & VM_FUNC_CLASS_METHOD)) {
|
||||
nSelf--;
|
||||
}
|
||||
}
|
||||
SySetInit(&sSelf, &pVm->sAllocator, sizeof(ph7_class *));
|
||||
for(i = 0; i < nSelf; i++) {
|
||||
if(SySetPut(&sSelf, SySetAt(&pVm->aSelf, i)) != SXRET_OK) {
|
||||
SySetRelease(&sSelf);
|
||||
return SXERR_MEM;
|
||||
}
|
||||
}
|
||||
sSavedSelf = pVm->aSelf;
|
||||
pVm->aSelf = sSelf;
|
||||
pVm->pFrame = pOwner;
|
||||
rc = VmEnterFrame(pVm, 0, 0, &pFrame);
|
||||
if(rc == SXRET_OK) {
|
||||
if(pCatch) {
|
||||
ph7_value *pObj = VmCreateMemObj(pVm, &pCatch->sThis, FALSE);
|
||||
pFrame->iFlags |= VM_FRAME_CATCH;
|
||||
if(pObj) {
|
||||
pThis->iRef++;
|
||||
pObj->x.pOther = pThis;
|
||||
MemObjSetType(pObj, MEMOBJ_OBJ);
|
||||
}
|
||||
} else {
|
||||
pFrame->iFlags |= VM_FRAME_FINALLY;
|
||||
}
|
||||
rc = VmLocalExec(pVm, pCatch ? &pCatch->sByteCode : &pException->sFinally, 0);
|
||||
while(pVm->pFrame != pOwner) {
|
||||
VmLeaveFrame(pVm);
|
||||
}
|
||||
}
|
||||
pVm->pFrame = pSavedFrame;
|
||||
SySetRelease(&pVm->aSelf);
|
||||
pVm->aSelf = sSavedSelf;
|
||||
return rc;
|
||||
}
|
||||
static sxi32 VmExecFinallyBlock(
|
||||
ph7_vm *pVm, /* Target VM */
|
||||
ph7_exception *pException /* Exception thrown */
|
||||
) {
|
||||
sxi32 rc;
|
||||
VmFrame *pFrame = pVm->pFrame;
|
||||
while(pFrame->pParent && (pFrame->iFlags & VM_FRAME_EXCEPTION)) {
|
||||
/* Safely ignore the exception frame */
|
||||
pFrame = pFrame->pParent;
|
||||
}
|
||||
/* Create a private frame first */
|
||||
rc = VmEnterFrame(&(*pVm), 0, 0, &pFrame);
|
||||
if(rc == SXRET_OK) {
|
||||
/* Mark as 'finally' frame */
|
||||
pFrame->iFlags |= VM_FRAME_FINALLY;
|
||||
/* Execute the block */
|
||||
rc = VmLocalExec(&(*pVm), &pException->sFinally, 0);
|
||||
/* Leave the frame */
|
||||
VmLeaveFrame(&(*pVm));
|
||||
}
|
||||
return rc;
|
||||
return VmExecExceptionBlock(pVm, pException, 0, 0);
|
||||
}
|
||||
/*
|
||||
* The following routine is invoked by the engine when an uncaught
|
||||
@@ -8491,7 +8547,7 @@ static sxi32 VmThrowException(
|
||||
ph7_exception_block *pCatch; /* Catch block to execute */
|
||||
ph7_exception **apException;
|
||||
ph7_exception *pException;
|
||||
sxi32 rc, rcc, rcf;
|
||||
sxi32 rcc, rcf;
|
||||
/* Point to the stack of loaded exceptions */
|
||||
apException = (ph7_exception **)SySetBasePtr(&pVm->aException);
|
||||
pException = 0;
|
||||
@@ -8532,23 +8588,7 @@ static sxi32 VmThrowException(
|
||||
/* Tell the upper layer that the exception was caught */
|
||||
pFrame->iFlags &= ~VM_FRAME_THROW;
|
||||
}
|
||||
/* Create a private frame first */
|
||||
rc = VmEnterFrame(&(*pVm), 0, 0, &pFrame);
|
||||
if(rc == SXRET_OK) {
|
||||
/* Mark as catch frame */
|
||||
ph7_value *pObj = VmCreateMemObj(&(*pVm), &pCatch->sThis, FALSE);
|
||||
pFrame->iFlags |= VM_FRAME_CATCH;
|
||||
if(pObj) {
|
||||
/* Install the exception instance */
|
||||
pThis->iRef++; /* Increment reference count */
|
||||
pObj->x.pOther = pThis;
|
||||
MemObjSetType(pObj, MEMOBJ_OBJ);
|
||||
}
|
||||
/* Execute the block */
|
||||
rcc = VmLocalExec(&(*pVm), &pCatch->sByteCode, 0);
|
||||
/* Leave the frame */
|
||||
VmLeaveFrame(&(*pVm));
|
||||
}
|
||||
rcc = VmExecExceptionBlock(pVm, pException, pCatch, pThis);
|
||||
}
|
||||
/* Execute the 'finally' block if available */
|
||||
if(pException && SySetUsed(&pException->sFinally)) {
|
||||
|
||||
Reference in New Issue
Block a user