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:
@@ -3437,6 +3437,12 @@ static sxi32 PH7_GenStateCompileClassMethod(
|
|||||||
/* Peek method name */
|
/* Peek method name */
|
||||||
pName = &pGen->pIn->sData;
|
pName = &pGen->pIn->sData;
|
||||||
nLine = pGen->pIn->nLine;
|
nLine = pGen->pIn->nLine;
|
||||||
|
if(((pName->nByte == sizeof("__construct") - 1 &&
|
||||||
|
SyMemcmp(pName->zString, "__construct", sizeof("__construct") - 1) == 0) ||
|
||||||
|
(pName->nByte == sizeof("__destruct") - 1 &&
|
||||||
|
SyMemcmp(pName->zString, "__destruct", sizeof("__destruct") - 1) == 0)) && nType != MEMOBJ_VOID) {
|
||||||
|
PH7_GenCompileError(pGen, E_ERROR, nLine, "Method '%z::%z()' must return void", &pClass->sName, pName);
|
||||||
|
}
|
||||||
/* Jump the method name */
|
/* Jump the method name */
|
||||||
pGen->pIn++;
|
pGen->pIn++;
|
||||||
if(iFlags & PH7_CLASS_ATTR_VIRTUAL) {
|
if(iFlags & PH7_CLASS_ATTR_VIRTUAL) {
|
||||||
|
|||||||
+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 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
|
||||||
static sxi32 VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
|
static sxi32 VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
|
||||||
static ph7_class *VmMethodClass(ph7_vm *pVm);
|
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.
|
/* Find a mounted name to transport a method call, not an eligible overload.
|
||||||
* The caller must retain its lookup root and receiver for VmOverload(). */
|
* The caller must retain its lookup root and receiver for VmOverload(). */
|
||||||
static ph7_class_method *VmMethodSeed(ph7_vm *pVm, ph7_class *pLookup, SyString *pName,
|
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)) {
|
if(pPreferred && SyHashGet(&pVm->hFunction, pPreferred->sVmName.zString, pPreferred->sVmName.nByte)) {
|
||||||
return pPreferred;
|
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 *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
|
||||||
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pName->zString, pName->nByte);
|
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pName->zString, pName->nByte);
|
||||||
if(pMeth && pMeth->sFunc.pClass == pClass &&
|
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
|
/* Walk C3 without modifying any declaration's overload chain. Only
|
||||||
* accessible declarations participate in identical-signature hiding. */
|
* 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 *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
|
||||||
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pList->sName.zString, pList->sName.nByte);
|
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pList->sName.zString, pList->sName.nByte);
|
||||||
SyHashEntry *pEntry;
|
SyHashEntry *pEntry;
|
||||||
@@ -2461,13 +2478,14 @@ static sxi32 VmByteCodeExec(
|
|||||||
if(pLastRef) {
|
if(pLastRef) {
|
||||||
*pLastRef = pTos->nIdx;
|
*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 */
|
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));
|
VmLeaveFrame(&(*pVm));
|
||||||
}
|
}
|
||||||
} else if(pVm->pFrame->iFlags & VM_FRAME_LOOP) {
|
} 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));
|
VmLeaveFrame(&(*pVm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4696,8 +4714,8 @@ static sxi32 VmByteCodeExec(
|
|||||||
/* Point to the instantiated class */
|
/* Point to the instantiated class */
|
||||||
pClass = pThis->pClass;
|
pClass = pThis->pClass;
|
||||||
pScope = VmMethodClass(pVm);
|
pScope = VmMethodClass(pVm);
|
||||||
if(pNos->iFlags == MEMOBJ_PARENTOBJ) {
|
if(pNos->iFlags == MEMOBJ_PARENTOBJ && !pInstr->iP2) {
|
||||||
/* Parent lookup starts at the declaring class, not the receiver. */
|
/* Preserve lexical parent lookup for fields. */
|
||||||
if(pScope) {
|
if(pScope) {
|
||||||
pClass = pScope;
|
pClass = pScope;
|
||||||
}
|
}
|
||||||
@@ -4987,7 +5005,7 @@ static sxi32 VmByteCodeExec(
|
|||||||
SySetPut(&aArg, (const void *)&pArg);
|
SySetPut(&aArg, (const void *)&pArg);
|
||||||
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 */
|
/* TICKET 1433-52: Unsetting $this in the constructor body */
|
||||||
if(pNew->iRef < 1) {
|
if(pNew->iRef < 1) {
|
||||||
pNew->iRef = 1;
|
pNew->iRef = 1;
|
||||||
@@ -5156,8 +5174,7 @@ static sxi32 VmByteCodeExec(
|
|||||||
/* Instance already loaded */
|
/* Instance already loaded */
|
||||||
pThis = (ph7_class_instance *)pTarget->x.pOther;
|
pThis = (ph7_class_instance *)pTarget->x.pOther;
|
||||||
pLookup = pThis->pClass;
|
pLookup = pThis->pClass;
|
||||||
if(pTarget->iFlags == MEMOBJ_PARENTOBJ && VmMethodClass(pVm)) {
|
if(pTarget->iFlags == MEMOBJ_PARENTOBJ) {
|
||||||
pLookup = VmMethodClass(pVm);
|
|
||||||
bParent = 1;
|
bParent = 1;
|
||||||
}
|
}
|
||||||
pThis->iRef += 2;
|
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 SXRET_OK if the method was successfully called.Any other
|
||||||
* return value indicates failure.
|
* return value indicates failure.
|
||||||
*/
|
*/
|
||||||
PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
static sxi32 VmCallClassMethod(
|
||||||
ph7_vm *pVm, /* Target VM */
|
ph7_vm *pVm, /* Target VM */
|
||||||
ph7_class_instance *pThis, /* Target class instance [i.e: Object in the PHP jargon]*/
|
ph7_class_instance *pThis, /* Target class instance [i.e: Object in the PHP jargon]*/
|
||||||
ph7_class_method *pMethod, /* Method name */
|
ph7_class_method *pMethod, /* Method name */
|
||||||
ph7_value *pResult, /* Store method return value here. NULL otherwise */
|
ph7_value *pResult, /* Store method return value here. NULL otherwise */
|
||||||
int nArg, /* Total number of given arguments */
|
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;
|
ph7_value *aStack;
|
||||||
VmInstr aInstr[2];
|
VmInstr aInstr[2];
|
||||||
@@ -6804,7 +6822,7 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
|||||||
aStack[i].nIdx = apArg[i]->nIdx;
|
aStack[i].nIdx = apArg[i]->nIdx;
|
||||||
}
|
}
|
||||||
iCursor = nArg + 1;
|
iCursor = nArg + 1;
|
||||||
iEntry = 2; /* VM-owned method call: preserve access to lifecycle methods. */
|
iEntry = bInternal ? 2 : 0;
|
||||||
if(pThis) {
|
if(pThis) {
|
||||||
/*
|
/*
|
||||||
* Push the class instance so that the '$this' variable will be available.
|
* 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 */
|
pThis->iRef++; /* Increment reference count */
|
||||||
aStack[i].x.pOther = pThis;
|
aStack[i].x.pOther = pThis;
|
||||||
aStack[i].nType = MEMOBJ_OBJ;
|
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)) {
|
if((SyStrncmp(pMethod->sFunc.sName.zString, "main", 4) == 0) || (SyStrncmp(pMethod->sFunc.sName.zString, "__construct", 11) == 0)) {
|
||||||
/* Do not overload entry point */
|
/* Do not overload entry point */
|
||||||
iEntry |= 1;
|
iEntry |= 1;
|
||||||
@@ -6860,6 +6878,10 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
|
|||||||
SyMemBackendFree(&pVm->sAllocator, aStack);
|
SyMemBackendFree(&pVm->sAllocator, aStack);
|
||||||
return PH7_OK;
|
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
|
* 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
|
* 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;
|
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(
|
static sxi32 VmExecFinallyBlock(
|
||||||
ph7_vm *pVm, /* Target VM */
|
ph7_vm *pVm, /* Target VM */
|
||||||
ph7_exception *pException /* Exception thrown */
|
ph7_exception *pException /* Exception thrown */
|
||||||
) {
|
) {
|
||||||
sxi32 rc;
|
return VmExecExceptionBlock(pVm, pException, 0, 0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The following routine is invoked by the engine when an uncaught
|
* 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_block *pCatch; /* Catch block to execute */
|
||||||
ph7_exception **apException;
|
ph7_exception **apException;
|
||||||
ph7_exception *pException;
|
ph7_exception *pException;
|
||||||
sxi32 rc, rcc, rcf;
|
sxi32 rcc, rcf;
|
||||||
/* Point to the stack of loaded exceptions */
|
/* Point to the stack of loaded exceptions */
|
||||||
apException = (ph7_exception **)SySetBasePtr(&pVm->aException);
|
apException = (ph7_exception **)SySetBasePtr(&pVm->aException);
|
||||||
pException = 0;
|
pException = 0;
|
||||||
@@ -8532,23 +8588,7 @@ static sxi32 VmThrowException(
|
|||||||
/* Tell the upper layer that the exception was caught */
|
/* Tell the upper layer that the exception was caught */
|
||||||
pFrame->iFlags &= ~VM_FRAME_THROW;
|
pFrame->iFlags &= ~VM_FRAME_THROW;
|
||||||
}
|
}
|
||||||
/* Create a private frame first */
|
rcc = VmExecExceptionBlock(pVm, pException, pCatch, pThis);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* Execute the 'finally' block if available */
|
/* Execute the 'finally' block if available */
|
||||||
if(pException && SySetUsed(&pException->sFinally)) {
|
if(pException && SySetUsed(&pException->sFinally)) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
class Beaufort {
|
class Beaufort {
|
||||||
private string $cipher;
|
private string $cipher;
|
||||||
|
|
||||||
public string __construct(string $text, string $key) {
|
public void __construct(string $text, string $key) {
|
||||||
string $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
string $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
for(int $i = 0; $i < strlen($text); $i++) {
|
for(int $i = 0; $i < strlen($text); $i++) {
|
||||||
int $j;
|
int $j;
|
||||||
|
|||||||
Reference in New Issue
Block a user