Define include completion as void and reject file-open failures
Build / AerScript (push) Successful in 37s

This commit is contained in:
2026-10-29 22:44:18 +01:00
parent 54fd0cf7a6
commit 3c194e3fa6
2 changed files with 83 additions and 39 deletions
+58 -39
View File
@@ -2928,44 +2928,9 @@ static void GenStateFixIncludeLabels(ph7_gen_state *pGen) {
}
}
}
/*
* Compile the 'include' and 'require' statements
*/
static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) {
char *zFile;
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
sxu32 nLine = pGen->pIn->nLine;
sxi32 iP1 = (nKey == PH7_KEYWORD_REQUIRE) ? 1 : 0;
sxi32 bParen = FALSE;
/* Jump the 'include' or 'require' keyword */
pGen->pIn++;
if(!iP1 && pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_LPAREN)) {
bParen = TRUE;
pGen->pIn++;
}
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR)) == 0) {
if(pGen->pIn >= pGen->pEnd) {
pGen->pIn--;
}
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z'", &pGen->pIn->sData);
}
zFile = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte);
pGen->pIn++;
if(bParen) {
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_RPAREN) == 0) {
PH7_GenCompileError(pGen, E_ERROR, nLine, "Include: expecting ')'");
}
pGen->pIn++;
}
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z', expecting ';'",
&pGen->pIn->sData);
}
if(iP1) {
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0);
} else {
/* Both spellings borrow the same lexical owner and transfer bridges.
* A NULL path denotes an expression operand on the VM stack. */
static void GenStateEmitInclude(ph7_gen_state *pGen, char *zFile, sxu32 nLine) {
ph7_include *pInclude = (ph7_include *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(ph7_include));
GenBlock *pBlock = pGen->pCurrent;
GenBlock *pLoop = PH7_GenStateFetchBlock(pBlock, GEN_BLOCK_LOOP, 0);
@@ -2982,9 +2947,48 @@ static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) {
}
pBlock = pBlock->pParent;
}
if(pBlock == 0) {
PH7_GenCompileError(pGen, E_ERROR, nLine, "include is only allowed inside a closure/method");
}
pInclude->pFunc = pBlock ? (ph7_vm_func *)pBlock->pUserData : 0;
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, 0, 1, pInclude, 0);
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, 0, zFile ? 1 : 2, pInclude, 0);
GenStateCompileLoopBridge(pGen, pInclude, pLoop, nLine);
}
/*
* Compile the 'include' and 'require' statements
*/
static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) {
char *zFile;
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
sxu32 nLine = pGen->pIn->nLine;
sxi32 iP1 = (nKey == PH7_KEYWORD_REQUIRE) ? 1 : 0;
if(!iP1 && &pGen->pIn[1] < pGen->pEnd && (pGen->pIn[1].nType & PH7_TK_LPAREN)) {
sxi32 rc = PH7_CompileExpr(pGen, 0, 0);
if(rc == SXRET_OK) {
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_POP, 1, 0, 0, 0);
}
return rc;
}
/* Jump the 'include' or 'require' keyword */
pGen->pIn++;
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_SSTR | PH7_TK_DSTR)) == 0) {
if(pGen->pIn >= pGen->pEnd) {
pGen->pIn--;
}
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z'", &pGen->pIn->sData);
}
zFile = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn->sData.zString, pGen->pIn->sData.nByte);
pGen->pIn++;
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI/*';'*/) == 0) {
/* Unexpected token */
PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Include: Unexpected token '%z', expecting ';'",
&pGen->pIn->sData);
}
if(iP1) {
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0);
} else {
GenStateEmitInclude(pGen, zFile, nLine);
}
return SXRET_OK;
}
@@ -4935,6 +4939,21 @@ static sxi32 PH7_GenStateEmitExprCode(
"Invalid expression node,PH7 is aborting compilation");
}
iVmOp = pNode->pOp->iVmOp;
if(iVmOp == PH7_OP_CALL && pNode->pLeft && pNode->pLeft->xCode == PH7_CompileLiteral &&
pNode->pLeft->pEnd == pNode->pLeft->pStart + 1 &&
(pNode->pLeft->pStart->nType & PH7_TK_KEYWORD) &&
SX_PTR_TO_INT(pNode->pLeft->pStart->pUserData) == PH7_KEYWORD_INCLUDE) {
sxu32 nLine = pNode->pLeft->pStart->nLine;
if(SySetUsed(&pNode->aNodeArgs) != 1) {
PH7_GenCompileError(pGen, E_ERROR, nLine, "include expects one string argument");
}
rc = PH7_GenStateEmitExprCode(pGen, *(ph7_expr_node **)SySetAt(&pNode->aNodeArgs, 0),
(iFlags | EXPR_FLAG_RDONLY_LOAD) & ~EXPR_FLAG_LOAD_IDX_STORE);
if(rc == SXRET_OK) {
GenStateEmitInclude(pGen, 0, nLine);
}
return rc;
}
if(pNode->pOp->iOp == EXPR_OP_QUESTY) {
sxu32 nJz, nJmp;
/* Ternary operator require special handling */
+34 -9
View File
@@ -5093,8 +5093,9 @@ Resume:
}
/*
* OP_INCLUDE P1 P2 P3
* Include another source file. If P1 is zero, 'include' statement was used, otherwise it was 'require'.
* Include another source file. If P1 is zero, 'include' was used, otherwise it was 'require'.
* P3 contains a path, or lexical include metadata when P2 is set.
* P2 == 2 consumes a string path operand and produces VOID on completion.
*/
case PH7_OP_INCLUDE:
{
@@ -5103,15 +5104,41 @@ Resume:
ph7_value sIncludeResult;
int iFlags = pInstr->iP1 ? PH7_AERSCRIPT_CODE : PH7_AERSCRIPT_CHNK;
SyString sFile;
if(SyStrlen(zFile) < 1) {
if(pInstr->iP2 == 2) {
if((pTos->nType & (MEMOBJ_STRING | MEMOBJ_HASHMAP)) != MEMOBJ_STRING) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "include expects one string argument");
}
/* Stream devices take a terminated path; retain its operand until
* execution and diagnostics no longer borrow these bytes. */
zFile = (char *)ph7_value_to_string(pTos, 0);
SyStringInitFromBuf(&sFile, zFile, SyBlobLength(&pTos->sBlob));
} else {
SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
}
if(pInstr->iP1 && sFile.nByte < 1) {
break;
}
SyStringInitFromBuf(&sFile, zFile, SyStrlen(zFile));
PH7_MemObjInit(pVm, &sIncludeResult);
rc = VmExecIncludedFile(&(*pVm), &sFile, iFlags | PH7_AERSCRIPT_FILE, &sIncludeResult, pInclude);
if(rc != SXRET_OK && rc != SXERR_EXISTS && rc != VM_INCLUDE_JUMP &&
(pInstr->iP1 || (rc != VM_INCLUDE_RETURN && rc != VM_RETURN_VOID)) &&
rc != VM_INCLUDE_EXCEPTION && rc != PH7_EXCEPTION &&
(pInstr->iP1 || rc != PH7_ABORT)) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile);
}
if(pInstr->iP2 == 2) {
PH7_MemObjRelease(pTos);
pTos--;
}
if(rc == VM_INCLUDE_JUMP) {
nTarget = (sxi32)sIncludeResult.x.iVal;
PH7_MemObjRelease(&sIncludeResult);
/* The transfer abandons this chunk's expression even when its
* target stays inside the same try. Restore its entry checkpoint;
* enclosing evaluations retain their own operand stacks. */
if(pTos - pStack > nTos) {
VmPopOperand(&pTos, (sxi32)(pTos - pStack) - nTos);
}
iTransfer = VM_TRANSFER_JUMP;
bExternalJump = FALSE;
goto Unwind;
@@ -5131,15 +5158,13 @@ Resume:
goto Unwind;
}
PH7_MemObjRelease(&sIncludeResult);
if(!pInstr->iP1 && rc == SXERR_NOTFOUND) {
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "IO error while including file: '%z'", &sFile);
break;
}
if(!pInstr->iP1 && rc == PH7_ABORT) {
goto Abort;
}
if(rc != SXRET_OK && rc != SXERR_EXISTS) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "IO error while including file: '%z'", &sFile);
if(pInstr->iP2 == 2) {
++pTos;
MemObjSetType(pTos, MEMOBJ_VOID);
pTos->nIdx = SXU32_HIGH;
}
break;
}