Validate Program entry points and resolve accessible zero-argument constructors
Build / AerScript (push) Successful in 38s

This commit is contained in:
2026-10-27 19:06:54 +01:00
parent 9d2578674c
commit fa32262998
4 changed files with 57 additions and 23 deletions
+54 -20
View File
@@ -944,6 +944,7 @@ static sxi32 VmOverloadDominates(const sxi32 *aFirst, const sxi32 *aSecond, sxu3
#define VM_INCLUDE_JUMP 4
#define VM_INCLUDE_EXCEPTION 5
#define VM_RETURN_VOID 6
#define VM_CALL_ENTRY 2
static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags, ph7_value *pResult, ph7_include *pInclude);
static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
static sxi32 VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
@@ -5721,9 +5722,11 @@ Resume:
}
}
}
/* Select an appropriate function to call, if not entry point */
/* Only the explicitly validated entry point bypasses overload resolution. */
if((pInstr->iP2 & 1) == 0) {
pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (sxi32)(pTos - pArg), pLookup, pThis == 0, bParent, pInstr->iP2 & 2);
} else {
pVmFunc = (ph7_vm_func *)pInstr->p3;
}
if(pThis && (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD)) {
pSelf = pVmFunc->pClass;
@@ -6329,6 +6332,46 @@ static void VmInvokeShutdownCallbacks(ph7_vm *pVm) {
}
SySetReset(&pVm->aShutdown);
}
/* Apply the usual C3 identical-signature hiding, but never choose between
* different public main signatures based on the CLI arguments. */
static ph7_class_method *VmEntryPoint(ph7_vm *pVm, ph7_class *pClass) {
ph7_class_method *pMain = 0;
sxu32 i;
for(i = 0; i < SySetUsed(&pClass->aMro); i++) {
ph7_class *pOwner = *(ph7_class **)SySetAt(&pClass->aMro, i);
ph7_class_method *pMethod = PH7_ClassExtractMethod(pOwner, "main", sizeof("main") - 1);
SyHashEntry *pEntry;
ph7_vm_func *pFunc;
if(!pMethod || pMethod->sFunc.pClass != pOwner) {
continue;
}
pEntry = SyHashGet(&pVm->hFunction, pMethod->sVmName.zString, pMethod->sVmName.nByte);
for(pFunc = pEntry ? (ph7_vm_func *)pEntry->pUserData : 0; pFunc; pFunc = pFunc->pNextName) {
pMethod = (ph7_class_method *)pFunc;
if(pMethod->iProtection != PH7_CLASS_PROT_PUBLIC || (pMethod->iFlags & PH7_CLASS_ATTR_VIRTUAL)) {
continue;
}
if(pMain && !PH7_VmSameSignature(&pMain->sFunc, pFunc)) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Ambiguous program entry point 'Program::main()'");
}
if(!pMain) {
pMain = pMethod;
}
}
}
if(!pMain) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Cannot find a public program entry point 'Program::main()'");
}
if(pMain->sFunc.nType != MEMOBJ_INT && pMain->sFunc.nType != MEMOBJ_VOID) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "The 'Program::main()' return value can be either Integer or Void");
}
if(SySetUsed(&pMain->sFunc.aArgs) > 1 ||
(SySetUsed(&pMain->sFunc.aArgs) == 1 &&
((ph7_vm_func_arg *)SySetBasePtr(&pMain->sFunc.aArgs))->nType != (MEMOBJ_STRING | MEMOBJ_HASHMAP))) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "The 'Program::main()' parameters must be () or (string[])");
}
return pMain;
}
/*
* Execute as much of a PH7 bytecode program as we can then return.
* This function is a wrapper around [VmByteCodeExec()].
@@ -6338,6 +6381,7 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
ph7_class *pClass;
ph7_class_instance *pInstance;
ph7_class_method *pMethod;
ph7_class_method *pMain;
ph7_value *pArgs, *sArgv;
ph7_value pResult;
char *zDup, *zParam;
@@ -6355,6 +6399,8 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
if(!pClass) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot find an entry 'Program' class");
}
/* Reject invalid entry points before running the Program constructor. */
pMain = VmEntryPoint(pVm, pClass);
pInstance = PH7_NewClassInstance(&(*pVm), pClass);
if(pInstance == 0) {
PH7_VmMemoryError(&(*pVm));
@@ -6363,7 +6409,7 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
pMethod = PH7_ClassExtractMethod(pClass, "__construct", sizeof("__construct") - 1);
if(pMethod) {
/* Call the class constructor */
PH7_VmCallClassMethod(&(*pVm), pInstance, pMethod, 0, 0, 0);
VmCallClassMethod(pVm, pInstance, pMethod, 0, 0, 0, FALSE);
}
/* Enable garbage collector */
pInstance->iRef--;
@@ -6383,20 +6429,14 @@ PH7_PRIVATE sxi32 PH7_VmByteCodeExec(ph7_vm *pVm) {
zParam = SyStrtok(NULL, " ");
}
}
/* Extract script entry point */
pMethod = PH7_ClassExtractMethod(pClass, "main", sizeof("main") - 1);
if(!pMethod) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot find a program entry point 'Program::main()'");
}
if(pMethod->sFunc.nType != MEMOBJ_INT && pMethod->sFunc.nType != MEMOBJ_VOID) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "The 'Program::main()' return value can be either Integer or Void");
}
pMethod = pMain;
/* A set of arguments is stored in array of strings */
pArgs->nType |= MEMOBJ_STRING;
/* Initialize variable for return value */
PH7_MemObjInit(pVm, &pResult);
/* Call entry point */
PH7_VmCallClassMethod(&(*pVm), pInstance, pMethod, &pResult, 1, &pArgs);
VmCallClassMethod(pVm, pInstance, pMethod, &pResult,
(int)SySetUsed(&pMethod->sFunc.aArgs), &pArgs, VM_CALL_ENTRY);
if(!pVm->iExitStatus) {
if(pMethod->sFunc.nType == MEMOBJ_INT) {
pVm->iExitStatus = ph7_value_to_int(&pResult);
@@ -7442,7 +7482,7 @@ static int vm_builtin_is_subclass_of(ph7_context *pCtx, int nArg, ph7_value **ap
ph7_value *pResult, /* Store method return value here. NULL otherwise */
int nArg, /* Total number of given arguments */
ph7_value **apArg, /* Method arguments */
sxi32 bInternal /* VM-owned lifecycle call, not source new */
sxi32 bInternal /* VM-owned lifecycle call, or VM_CALL_ENTRY */
) {
ph7_value *aStack;
VmInstr aInstr[2];
@@ -7466,7 +7506,7 @@ static int vm_builtin_is_subclass_of(ph7_context *pCtx, int nArg, ph7_value **ap
aStack[i].nIdx = apArg[i]->nIdx;
}
iCursor = nArg + 1;
iEntry = bInternal ? 2 : 0;
iEntry = bInternal == VM_CALL_ENTRY ? 3 : (bInternal ? 2 : 0);
if(pThis) {
/*
* Push the class instance so that the '$this' variable will be available.
@@ -7474,12 +7514,6 @@ static int vm_builtin_is_subclass_of(ph7_context *pCtx, int nArg, ph7_value **ap
pThis->iRef++; /* Increment reference count */
aStack[i].x.pOther = pThis;
aStack[i].nType = MEMOBJ_OBJ;
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)) {
/* Do not overload entry point */
iEntry |= 1;
}
}
}
pSeed = VmMethodSeed(pVm, pThis ? pThis->pClass : pMethod->sFunc.pClass,
&pMethod->sFunc.sName, pMethod, FALSE);
@@ -7505,7 +7539,7 @@ static int vm_builtin_is_subclass_of(ph7_context *pCtx, int nArg, ph7_value **ap
aInstr[0].pScope = 0;
aInstr[0].iP1 = nArg; /* Total number of given arguments */
aInstr[0].iP2 = iEntry;
aInstr[0].p3 = 0;
aInstr[0].p3 = (iEntry & 1) ? &pMethod->sFunc : 0;
aInstr[0].bExec = FALSE;
aInstr[0].iLine = 1;
aInstr[0].pFile = (SyString *)&sFileName;