Keep source file descriptors stable for the lifetime of the VM and execute included fragments in the enclosing method and closure context
Build / AerScript (push) Successful in 40s
Build / AerScript (push) Successful in 40s
This commit is contained in:
+161
-26
@@ -237,6 +237,7 @@ static sxi32 GenStateFixGoto(ph7_gen_state *pGen, sxu32 nOfft)
|
||||
pInstr = PH7_VmGetInstr(pGen->pVm, pJump->nInstrIdx);
|
||||
if(pInstr) {
|
||||
pInstr->iP2 = pLabel->nJumpDest;
|
||||
pInstr->iP1 = pLabel->bExternal;
|
||||
}
|
||||
}
|
||||
return SXRET_OK;
|
||||
@@ -1127,7 +1128,8 @@ static sxi32 PH7_GenStateLoadLiteral(ph7_gen_state *pGen) {
|
||||
if(pObj == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pToken->nLine, "PH7 engine is running out-of-memory");
|
||||
}
|
||||
SyString *pFile = (SyString *)SySetPeek(&pGen->pVm->aFiles);
|
||||
SyString **ppFile = (SyString **)SySetPeek(&pGen->pVm->aFiles);
|
||||
SyString *pFile = ppFile ? *ppFile : 0;
|
||||
if(pFile == 0) {
|
||||
SyString pMemory;
|
||||
SyStringInitFromBuf(&pMemory, "[MEMORY]", (int)sizeof("[MEMORY]") - 1);
|
||||
@@ -1380,17 +1382,22 @@ static sxi32 PH7_CompileContinue(ph7_gen_state *pGen) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop");
|
||||
} else {
|
||||
sxu32 nInstrIdx = 0;
|
||||
if(!pLoop->bPostContinue) {
|
||||
/* Emit the OP_LF_STOP instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
|
||||
}
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
|
||||
if(pLoop->bPostContinue) {
|
||||
JumpFixup sJumpFix;
|
||||
/* Post-continue */
|
||||
sJumpFix.nJumpType = PH7_OP_JMP;
|
||||
sJumpFix.nInstrIdx = nInstrIdx;
|
||||
SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
|
||||
if(pLoop->iFlags & GEN_BLOCK_INCLUDE) {
|
||||
ph7_include *pInclude = (ph7_include *)pLoop->pUserData;
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 1, pInclude->nContinue, 0, 0);
|
||||
} else {
|
||||
if(!pLoop->bPostContinue) {
|
||||
/* Emit the OP_LF_STOP instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
|
||||
}
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
|
||||
if(pLoop->bPostContinue) {
|
||||
JumpFixup sJumpFix;
|
||||
/* Post-continue */
|
||||
sJumpFix.nJumpType = PH7_OP_JMP;
|
||||
sJumpFix.nInstrIdx = nInstrIdx;
|
||||
SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
|
||||
@@ -1421,14 +1428,19 @@ static sxi32 PH7_CompileBreak(ph7_gen_state *pGen) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
|
||||
} else {
|
||||
sxu32 nInstrIdx;
|
||||
if((pLoop->iFlags & GEN_BLOCK_SWITCH) == 0) {
|
||||
/* Emit the OP_LF_STOP instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
|
||||
}
|
||||
rc = PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, 0, 0, &nInstrIdx);
|
||||
if(rc == SXRET_OK) {
|
||||
/* Fix the jump later when the jump destination is resolved */
|
||||
PH7_GenStateNewJumpFixup(pLoop, PH7_OP_JMP, nInstrIdx);
|
||||
if(pLoop->iFlags & GEN_BLOCK_INCLUDE) {
|
||||
ph7_include *pInclude = (ph7_include *)pLoop->pUserData;
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 1, pInclude->nBreak, 0, 0);
|
||||
} else {
|
||||
if((pLoop->iFlags & GEN_BLOCK_SWITCH) == 0) {
|
||||
/* Emit the OP_LF_STOP instruction to leave the loop frame */
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_LF_STOP, 0, 0, 0, 0);
|
||||
}
|
||||
rc = PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_JMP, 0, 0, 0, &nInstrIdx);
|
||||
if(rc == SXRET_OK) {
|
||||
/* Fix the jump later when the jump destination is resolved */
|
||||
PH7_GenStateNewJumpFixup(pLoop, PH7_OP_JMP, nInstrIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_SEMI) == 0) {
|
||||
@@ -1469,6 +1481,7 @@ static sxi32 PH7_CompileLabel(ph7_gen_state *pGen)
|
||||
}
|
||||
SyStringInitFromBuf(&sLabel.sName, zDup, pTarget->nByte);
|
||||
sLabel.bRef = FALSE;
|
||||
sLabel.bExternal = FALSE;
|
||||
sLabel.nLine = pGen->pIn->nLine;
|
||||
pBlock = pGen->pCurrent;
|
||||
while(pBlock) {
|
||||
@@ -2315,10 +2328,9 @@ static sxi32 PH7_CompileIf(ph7_gen_state *pGen) {
|
||||
* of the current function, and returns its argument as the value of the function call.
|
||||
* return() will also end the execution of an eval() statement or script file.
|
||||
* If called from the global scope, then execution of the current script file is ended.
|
||||
* If the current script file was include()ed or require()ed, then control is passed back
|
||||
* to the calling file. Furthermore, if the current script file was include()ed, then the value
|
||||
* given to return() will be returned as the value of the include() call. If return() is called
|
||||
* from within the main script file, then script execution end.
|
||||
* An included method/closure chunk shares its caller's return boundary. A required
|
||||
* global script retains its own execution boundary. If return() is called from
|
||||
* within the main script file, then script execution ends.
|
||||
* Note that since return() is a language construct and not a function, the parentheses
|
||||
* surrounding its arguments are not required. It is common to leave them out, and you actually
|
||||
* should do so as Aer has less work to do in this case.
|
||||
@@ -2670,6 +2682,30 @@ static sxi32 PH7_CompileImport(ph7_gen_state *pGen) {
|
||||
return SXRET_OK;
|
||||
}
|
||||
|
||||
/* Resolve include-to-owner gotos only after the owner's labels are known.
|
||||
* Bridges also let nested includes forward a transfer one bytecode at a time. */
|
||||
static void GenStateFixIncludeLabels(ph7_gen_state *pGen) {
|
||||
sxu32 n, j, nCount = PH7_VmInstrLength(pGen->pVm);
|
||||
for(n = 0; n < nCount; n++) {
|
||||
VmInstr *pInstr = PH7_VmGetInstr(pGen->pVm, n);
|
||||
if(pInstr->iOp == PH7_OP_INCLUDE && !pInstr->iP1 && pInstr->iP2) {
|
||||
ph7_include *pInclude = (ph7_include *)pInstr->p3;
|
||||
sxu32 nSkip;
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, 0, 0, &nSkip);
|
||||
for(j = 0; j < SySetUsed(&pGen->aLabel); j++) {
|
||||
Label sLabel = *(Label *)SySetAt(&pGen->aLabel, j);
|
||||
if(sLabel.pFunc == pInclude->pFunc) {
|
||||
sxu32 nDest = PH7_VmInstrLength(pGen->pVm);
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, sLabel.bExternal, sLabel.nJumpDest, 0, 0);
|
||||
sLabel.nJumpDest = nDest;
|
||||
sLabel.bExternal = TRUE;
|
||||
SySetPut(&pInclude->aLabel, &sLabel);
|
||||
}
|
||||
}
|
||||
PH7_VmGetInstr(pGen->pVm, nSkip)->iP2 = PH7_VmInstrLength(pGen->pVm);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Compile the 'include' and 'require' statements
|
||||
*/
|
||||
@@ -2678,8 +2714,13 @@ 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;
|
||||
/* 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--;
|
||||
@@ -2689,12 +2730,60 @@ 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 ';'",
|
||||
&pGen->pIn->sData);
|
||||
}
|
||||
PH7_VmEmitInstr(pGen->pVm, nLine, PH7_OP_INCLUDE, iP1, 0, zFile, 0);
|
||||
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);
|
||||
sxu32 nSkip;
|
||||
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);
|
||||
if(pLoop) {
|
||||
PH7_VmEmitInstr(pGen->pVm, 0, PH7_OP_JMP, 0, 0, 0, &nSkip);
|
||||
pInclude->nBreak = PH7_VmInstrLength(pGen->pVm);
|
||||
/* Reuse the ordinary compiler's loop cleanup and forward fixups. */
|
||||
SyToken sToken;
|
||||
SyToken *pSavedIn = pGen->pIn, *pSavedEnd = pGen->pEnd;
|
||||
SyZero(&sToken, sizeof(sToken));
|
||||
sToken.nLine = nLine;
|
||||
pGen->pIn = &sToken;
|
||||
pGen->pEnd = &sToken + 1;
|
||||
PH7_CompileBreak(pGen);
|
||||
if((pLoop->iFlags & GEN_BLOCK_SWITCH) == 0) {
|
||||
pInclude->nContinue = PH7_VmInstrLength(pGen->pVm);
|
||||
pGen->pIn = &sToken;
|
||||
PH7_CompileContinue(pGen);
|
||||
}
|
||||
pGen->pIn = pSavedIn;
|
||||
pGen->pEnd = pSavedEnd;
|
||||
PH7_VmGetInstr(pGen->pVm, nSkip)->iP2 = PH7_VmInstrLength(pGen->pVm);
|
||||
}
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
/*
|
||||
@@ -3013,6 +3102,7 @@ static sxi32 PH7_GenStateCompileFuncBody(
|
||||
if(SXERR_ABORT == GenStateFixGoto(&(*pGen), nGotoOfft)) {
|
||||
rc = SXERR_ABORT;
|
||||
}
|
||||
GenStateFixIncludeLabels(pGen);
|
||||
SySetTruncate(&pGen->aGoto,nGotoOfft);
|
||||
/* Restore the default container */
|
||||
PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
|
||||
@@ -5224,6 +5314,7 @@ static sxi32 PH7_CompileScript(
|
||||
if(SXERR_ABORT == GenStateFixGoto(&(*pGen), 0)) {
|
||||
rc = SXERR_ABORT;
|
||||
}
|
||||
GenStateFixIncludeLabels(pGen);
|
||||
/* Reset container */
|
||||
SySetReset(&pGen->aGoto);
|
||||
SySetReset(&pGen->aLabel);
|
||||
@@ -5282,6 +5373,48 @@ PH7_PRIVATE sxi32 PH7_CompileAerScript(
|
||||
SySetRelease(&aAerToken);
|
||||
return rc;
|
||||
}
|
||||
/* Compile an include without resetting a suspended run-time compilation. */
|
||||
PH7_PRIVATE sxi32 PH7_CompileIncludedChunk(ph7_vm *pVm, SyString *pScript, ph7_vm_func *pFunc, ph7_include *pInclude) {
|
||||
ph7_gen_state sSaved = pVm->sCodeGen;
|
||||
ph7_gen_state *pGen = &pVm->sCodeGen;
|
||||
ph7_class_info sClass;
|
||||
sxi32 rc;
|
||||
PH7_InitCodeGenerator(pVm, pVm->pEngine->xConf.xErr, pVm->pEngine->xConf.pErrData);
|
||||
if(pFunc) {
|
||||
if(pFunc->pClass) {
|
||||
SyZero(&sClass, sizeof(sClass));
|
||||
sClass.sName = pFunc->pClass->sName;
|
||||
PH7_GenStateEnterBlock(pGen, GEN_BLOCK_CLASS, 0, &sClass, 0);
|
||||
}
|
||||
PH7_GenStateEnterBlock(pGen, GEN_BLOCK_PROTECTED | GEN_BLOCK_FUNC, 0, pFunc, 0);
|
||||
}
|
||||
if(pInclude) {
|
||||
sxu32 n;
|
||||
for(n = 0; n < SySetUsed(&pInclude->aLabel); n++) {
|
||||
Label sLabel = *(Label *)SySetAt(&pInclude->aLabel, n);
|
||||
/* A using closure executes a clone, but its labels belong to the
|
||||
* compiled template. Rebind only this compilation's borrowed copy. */
|
||||
sLabel.pFunc = pFunc;
|
||||
SySetPut(&pGen->aLabel, &sLabel);
|
||||
}
|
||||
if(pInclude->bException) {
|
||||
PH7_GenStateEnterBlock(pGen, GEN_BLOCK_EXCEPTION, 0, 0, 0);
|
||||
}
|
||||
if(pInclude->nBreak != SXU32_HIGH) {
|
||||
PH7_GenStateEnterBlock(pGen, GEN_BLOCK_LOOP | GEN_BLOCK_INCLUDE |
|
||||
(pInclude->nContinue == SXU32_HIGH ? GEN_BLOCK_SWITCH : 0), 0, pInclude, 0);
|
||||
}
|
||||
}
|
||||
rc = PH7_CompileAerScript(pVm, pScript, PH7_AERSCRIPT_CHNK | PH7_AERSCRIPT_FILE);
|
||||
PH7_ResetCodeGenerator(pVm, sSaved.xErr, sSaved.pErrData);
|
||||
PH7_GenStateReleaseBlock(&pGen->sGlobal);
|
||||
SySetRelease(&pGen->aLabel);
|
||||
SySetRelease(&pGen->aGoto);
|
||||
SyHashRelease(&pGen->hLiteral);
|
||||
SyHashRelease(&pGen->hVar);
|
||||
pVm->sCodeGen = sSaved;
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
* Utility routines.Initialize the code generator.
|
||||
*/
|
||||
@@ -5350,12 +5483,14 @@ PH7_PRIVATE sxi32 PH7_GenCompileError(ph7_gen_state *pGen, sxi32 nErrType, sxu32
|
||||
SyBlob *pWorker = &pGen->sErrBuf;
|
||||
const char *zErr;
|
||||
SyString *pFile;
|
||||
SyString **ppFile;
|
||||
va_list ap;
|
||||
sxi32 rc;
|
||||
/* Reset the working buffer */
|
||||
SyBlobReset(pWorker);
|
||||
/* Peek the processed file path if available */
|
||||
pFile = (SyString *)SySetPeek(&pGen->pVm->aFiles);
|
||||
ppFile = (SyString **)SySetPeek(&pGen->pVm->aFiles);
|
||||
pFile = ppFile ? *ppFile : 0;
|
||||
if(pGen->xErr == 0) {
|
||||
/* No available error consumer, return immediately */
|
||||
return SXRET_OK;
|
||||
|
||||
Reference in New Issue
Block a user