diff --git a/engine/compiler.c b/engine/compiler.c index 9538e61..cccf5d3 100644 --- a/engine/compiler.c +++ b/engine/compiler.c @@ -2928,6 +2928,32 @@ static void GenStateFixIncludeLabels(ph7_gen_state *pGen) { } } } +/* 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); + if(pInclude == 0) { + PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 is running out-of-memory"); + } + SyZero(pInclude, sizeof(*pInclude)); + pInclude->zPath = zFile; + pInclude->nBreak = pInclude->nContinue = SXU32_HIGH; + SySetInit(&pInclude->aLabel, &pGen->pVm->sAllocator, sizeof(Label)); + while(pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0) { + if(pBlock->iFlags & GEN_BLOCK_EXCEPTION) { + pInclude->bException = TRUE; + } + 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, zFile ? 1 : 2, pInclude, 0); + GenStateCompileLoopBridge(pGen, pInclude, pLoop, nLine); +} /* * Compile the 'include' and 'require' statements */ @@ -2936,13 +2962,15 @@ static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) { 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; + 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(!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--; @@ -2952,12 +2980,6 @@ static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) { } 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 ';'", @@ -2966,25 +2988,7 @@ static sxi32 PH7_CompileInclude(ph7_gen_state *pGen) { if(iP1) { PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0); } else { - 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); - if(pInclude == 0) { - PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 is running out-of-memory"); - } - SyZero(pInclude, sizeof(*pInclude)); - pInclude->zPath = zFile; - pInclude->nBreak = pInclude->nContinue = SXU32_HIGH; - SySetInit(&pInclude->aLabel, &pGen->pVm->sAllocator, sizeof(Label)); - while(pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0) { - if(pBlock->iFlags & GEN_BLOCK_EXCEPTION) { - pInclude->bException = TRUE; - } - pBlock = pBlock->pParent; - } - pInclude->pFunc = pBlock ? (ph7_vm_func *)pBlock->pUserData : 0; - PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, 0, 1, pInclude, 0); - GenStateCompileLoopBridge(pGen, pInclude, pLoop, nLine); + 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 */ diff --git a/engine/vm.c b/engine/vm.c index 9f6286b..595ac06 100644 --- a/engine/vm.c +++ b/engine/vm.c @@ -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; }