Give eval an independent return boundary and result
Build / AerScript (push) Successful in 41s

This commit is contained in:
2026-10-29 18:53:40 +01:00
parent cf5058f478
commit b4b0d94a65
+31 -12
View File
@@ -992,6 +992,7 @@ static sxi32 VmOverloadDominates(ph7_vm *pVm, ph7_value *aArg, ph7_vm_func *pFir
/* Forward declaration */
#define VM_EXEC_INCLUDE 2
#define VM_EXEC_HANDLER 4
#define VM_EXEC_EVAL 8
#define VM_INCLUDE_RETURN 3
#define VM_INCLUDE_JUMP 4
#define VM_INCLUDE_EXCEPTION 5
@@ -2854,7 +2855,7 @@ static sxi32 VmByteCodeExec(
ph7_value *pStack, /* Operand stack */
int nTos, /* Top entry in the operand stack (usually -1) */
ph7_value *pResult, /* Store program return value here. NULL otherwise */
int iExecFlags /* Callback (1), included statements or exception handler */
int iExecFlags /* Callback (1), included statements, handler or eval */
) {
VmInstr *pInstr;
ph7_value *pTos;
@@ -6170,6 +6171,7 @@ Resume:
/* Release the call context */
VmReleaseCallContext(&sCtx);
if(rc == PH7_ABORT) {
PH7_MemObjRelease(&sRet);
goto Abort;
}
if(pInstr->iP1 > 0) {
@@ -6179,6 +6181,15 @@ Resume:
/* Save foreign function return value */
PH7_MemObjStore(&sRet, pTos);
PH7_MemObjRelease(&sRet);
if(rc == PH7_EXCEPTION) {
PH7_MemObjStore(pTos, &sTransfer);
/* Keep a NULL result if an uncaught handler resumes this
* expression. Region unwind still discards interrupted operands. */
PH7_MemObjRelease(pTos);
iTransfer = VM_TRANSFER_THROW;
nTarget = pc + 1;
goto Unwind;
}
}
break;
}
@@ -6306,7 +6317,7 @@ Unwind:
goto Resume;
}
if(iTransfer == VM_TRANSFER_THROW) {
if(SySetUsed(&pVm->aException) == 0 && (iExecFlags & (VM_EXEC_HANDLER | VM_EXEC_INCLUDE)) == 0) {
if(SySetUsed(&pVm->aException) == 0 && (iExecFlags & (VM_EXEC_HANDLER | VM_EXEC_INCLUDE | VM_EXEC_EVAL)) == 0) {
/* A nonfatal handler resumes this iteration with its bindings intact. */
rc = VmUncaughtException(pVm, (sTransfer.nType & MEMOBJ_OBJ) ? (ph7_class_instance *)sTransfer.x.pOther : 0);
if(rc == PH7_ABORT) {
@@ -6325,9 +6336,10 @@ Unwind:
/* Only the selected explicit value is checked against the return contract.
* A bare return keeps NULL. An include directly borrowing the active method
* frame has no enclosing region left to override its result; otherwise it
* defers this check to the owner, just like a handler. */
* defers this check to the owner, just like a handler. Eval has its own
* dynamic return contract, even when borrowing an active owner frame. */
if(bReturnValue && (pEntryFrame->iFlags & VM_FRAME_ACTIVE) &&
(iExecFlags & VM_EXEC_HANDLER) == 0) {
(iExecFlags & (VM_EXEC_HANDLER | VM_EXEC_EVAL)) == 0) {
ph7_vm_func *pFunc = (ph7_vm_func *)pEntryFrame->pUserData;
if(pFunc->nType & MEMOBJ_NAMED) {
PH7_VmTypeClass(pVm, pFunc->nType, TRUE);
@@ -10162,13 +10174,22 @@ static sxi32 VmEvalChunk(
/* Assume a boolean true return value */
PH7_MemObjInitFromBool(pVm, &sResult, 1);
/* Execute the compiled chunk */
if(bInclude) {
if(bInclude || iFlags == PH7_AERSCRIPT_CHNK) {
ph7_value *pStack = VmNewOperandStack(pVm, SySetUsed(&aByteCode));
if(pStack == 0) {
rc = SXERR_MEM;
} else {
/* A nested call must return its exception here before the caller's
* regions (or uncaught handler) consume it. */
ph7_exception *pBoundary = 0;
if(!bInclude && SySetPut(&pVm->aException, &pBoundary) != SXRET_OK) {
PH7_VmMemoryError(pVm);
}
rc = VmByteCodeExec(pVm, (VmInstr *)SySetBasePtr(&aByteCode), pStack, -1,
&sResult, VM_EXEC_INCLUDE);
&sResult, bInclude ? VM_EXEC_INCLUDE : VM_EXEC_EVAL);
if(!bInclude) {
SySetPop(&pVm->aException);
}
SyMemBackendFree(&pVm->sAllocator, pStack);
if((rc == VM_INCLUDE_RETURN || rc == VM_RETURN_VOID || rc == VM_INCLUDE_JUMP || rc == VM_INCLUDE_EXCEPTION) && pResult) {
PH7_MemObjStore(&sResult, pResult);
@@ -10189,7 +10210,7 @@ Cleanup:
/* Cleanup the mess left behind */
pVm->pByteContainer = pByteCode;
SySetRelease(&aByteCode);
return bRequire || bInclude ? rc : SXRET_OK;
return bRequire || bInclude || rc == PH7_EXCEPTION || rc == PH7_ABORT ? rc : SXRET_OK;
}
/*
* value eval(string $code)
@@ -10197,9 +10218,8 @@ Cleanup:
* Parameter
* code: PHP code to evaluate.
* Return
* eval() returns NULL unless return is called in the evaluated code, in which case
* the value passed to return is returned. If there is a parse error in the evaluated
* code, eval() returns FALSE and execution of the following code continues normally.
* eval() returns TRUE unless return is called in the evaluated code, in which case
* the value passed to return is returned. Missing or empty input returns NULL.
*/
static int vm_builtin_eval(ph7_context *pCtx, int nArg, ph7_value **apArg) {
SyString sChunk; /* Chunk to evaluate */
@@ -10216,8 +10236,7 @@ static int vm_builtin_eval(ph7_context *pCtx, int nArg, ph7_value **apArg) {
return SXRET_OK;
}
/* Eval the chunk */
VmEvalChunk(pCtx->pVm, &(*pCtx), &sChunk, PH7_AERSCRIPT_CHNK, 0, 0);
return SXRET_OK;
return VmEvalChunk(pCtx->pVm, &(*pCtx), &sChunk, PH7_AERSCRIPT_CHNK, 0, 0);
}
/*
* Check if a file path is already included.