Resolve inherited method overloads across C3 MRO using full input signatures and own import path storage for the lifetime of the VM
Build / AerScript (push) Successful in 39s

This commit is contained in:
2026-10-24 12:27:01 +02:00
parent bc5e5f6a8e
commit 5f02a34b10
4 changed files with 230 additions and 88 deletions
+8 -2
View File
@@ -464,7 +464,10 @@ static sxi32 ProcessSourceFile(
char *pFileDir, fFilePath[PATH_MAX + 1];
char pFilePath[PATH_MAX + 1];
/* Install local import path which is the current directory */
ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, "./");
rc = ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, "./");
if(rc != PH7_OK) {
goto Release;
}
if(zFilePath && SyRealPath(zFilePath, fFilePath) == PH7_OK) {
rc = snprintf(pFilePath, sizeof(pFilePath) - 1, "%s", fFilePath);
if(rc < 0) {
@@ -474,7 +477,10 @@ static sxi32 ProcessSourceFile(
pFileDir = (char *) PH7_ExtractDirName(fFilePath, SyStrlen(fFilePath), &iFileDir);
pFileDir[iFileDir + 1] = '\0';
/* Install local import path which is directory containing entry script */
ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, pFileDir);
rc = ph7_vm_config(pVm, PH7_VM_CONFIG_IMPORT_PATH, pFileDir);
if(rc != PH7_OK) {
goto Release;
}
/* Push processed file path */
PH7_VmPushFilePath(pVm, pFilePath, -1, TRUE, 0);
} else {
+18
View File
@@ -3172,6 +3172,13 @@ static sxi32 PH7_GenStateCompileFunc(
}
rc = SXRET_OK;
if((pFunc->iFlags & VM_FUNC_CLOSURE) == 0) {
SyHashEntry *pEntry = SyHashGet(&pGen->pVm->hFunction, pName->zString, pName->nByte);
ph7_vm_func *pOther = pEntry ? (ph7_vm_func *)pEntry->pUserData : 0;
for(; pOther; pOther = pOther->pNextName) {
if(PH7_VmSameSignature(pFunc, pOther)) {
PH7_GenCompileError(pGen, E_ERROR, nLine, "Duplicate function signature '%z()'", pName);
}
}
/* Finally register the function */
rc = PH7_VmInstallUserFunction(pGen->pVm, pFunc, 0);
}
@@ -3474,6 +3481,17 @@ static sxi32 PH7_GenStateCompileClassMethod(
}
/* Point beyond method signature */
pGen->pIn = &pEnd[1];
{
SyHashEntry *pEntry;
SyHashResetLoopCursor(&pClass->hMethod);
while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0) {
ph7_class_method *pOther = (ph7_class_method *)pEntry->pUserData;
if(pOther->sFunc.pClass == pClass &&
PH7_VmSameSignature(&pMeth->sFunc, &pOther->sFunc)) {
PH7_GenCompileError(pGen, E_ERROR, nLine, "Duplicate method signature '%z::%z()'", &pClass->sName, pName);
}
}
}
if(doBody) {
if(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & PH7_TK_OCB /* '{'*/) == 0) {
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
+203 -86
View File
@@ -219,6 +219,25 @@ PH7_PRIVATE sxi32 PH7_VmInitFuncState(
SyStringInitFromBuf(&pFunc->sName, zName, nByte);
return SXRET_OK;
}
/* Input identity deliberately excludes return types, defaults and modifiers. */
PH7_PRIVATE sxi32 PH7_VmSameSignature(ph7_vm_func *pFirst, ph7_vm_func *pSecond) {
ph7_vm_func_arg *aFirst, *aSecond;
sxu32 i;
if(SyStringCmp(&pFirst->sName, &pSecond->sName, SyMemcmp) != 0 ||
SySetUsed(&pFirst->aArgs) != SySetUsed(&pSecond->aArgs)) {
return FALSE;
}
aFirst = (ph7_vm_func_arg *)SySetBasePtr(&pFirst->aArgs);
aSecond = (ph7_vm_func_arg *)SySetBasePtr(&pSecond->aArgs);
for(i = 0; i < SySetUsed(&pFirst->aArgs); i++) {
if(aFirst[i].nType != aSecond[i].nType ||
((aFirst[i].iFlags ^ aSecond[i].iFlags) & VM_FUNC_ARG_BY_REF) ||
(aFirst[i].nType == SXU32_HIGH && SyStringCmp(&aFirst[i].sClass, &aSecond[i].sClass, SyMemcmp) != 0)) {
return FALSE;
}
}
return TRUE;
}
/*
* Install a user defined function in the corresponding VM container.
*/
@@ -466,13 +485,13 @@ static int VmInstanceOf(ph7_vm *pVm, ph7_class *pThis, ph7_class *pClass);
typedef struct VmOverloadClassWalk VmOverloadClassWalk;
struct VmOverloadClassWalk {
ph7_class *pClass; /* Class or interface */
int nDepth; /* Inheritance distance from the starting class */
sxi32 nDepth; /* Inheritance distance from the starting class */
};
/*
* Enqueue a class or interface for the overload specificity walk unless it
* has already been visited.
*/
static void VmOverloadEnqueueClass(ph7_vm *pVm, SySet *pQueue, SySet *pVisited, ph7_class *pClass, int nDepth) {
static void VmOverloadEnqueueClass(ph7_vm *pVm, SySet *pQueue, SySet *pVisited, ph7_class *pClass, sxi32 nDepth) {
ph7_class **apVisited;
VmOverloadClassWalk sEntry;
sxu32 i;
@@ -497,13 +516,13 @@ static void VmOverloadEnqueueClass(ph7_vm *pVm, SySet *pQueue, SySet *pVisited,
* class or implemented interface and -1 if no relation exists. The walk is
* read-only.
*/
static int VmOverloadClassDistance(ph7_vm *pVm, ph7_class *pClass, ph7_class *pHint) {
static sxi32 VmOverloadClassDistance(ph7_vm *pVm, ph7_class *pClass, ph7_class *pHint) {
SySet sQueue, sVisited;
VmOverloadClassWalk *pEntry;
ph7_class **apInterface, *pCur;
SyHashEntry *pHash;
sxu32 i, n;
int nDepth, nResult = -1;
sxi32 nDepth, nResult = -1;
if(pClass == pHint) {
return 0;
}
@@ -540,7 +559,7 @@ static int VmOverloadClassDistance(ph7_vm *pVm, ph7_class *pClass, ph7_class *pH
* parameter. NULL is compatible with every type, so the rank reflects how
* specific the formal parameter itself is.
*/
static int VmOverloadNullRank(ph7_vm *pVm, ph7_vm_func_arg *pFormal) {
static sxi32 VmOverloadNullRank(ph7_vm *pVm, ph7_vm_func_arg *pFormal) {
sxu32 nFormalType = pFormal->nType;
SyHashEntry *pEntry;
if(nFormalType == SXU32_HIGH) {
@@ -573,7 +592,7 @@ static int VmOverloadNullRank(ph7_vm *pVm, ph7_vm_func_arg *pFormal) {
* specific the match. A negative value means that the value is not
* compatible with the parameter.
*/
static int VmOverloadScalarRank(ph7_value *pArg, sxu32 nArgType, sxu32 nFormalType) {
static sxi32 VmOverloadScalarRank(ph7_value *pArg, sxu32 nArgType, sxu32 nFormalType) {
if(nArgType == nFormalType) {
/* Exact type match */
return 0;
@@ -618,12 +637,12 @@ static int VmOverloadScalarRank(ph7_value *pArg, sxu32 nArgType, sxu32 nFormalTy
* are compatible and do not affect the rank, so an empty array or an array
* made only of NULLs stays rank 1 unless the container type matches exactly.
*/
static int VmOverloadArrayRank(ph7_vm *pVm, ph7_value *pArg, sxu32 nFormalElem) {
static sxi32 VmOverloadArrayRank(ph7_vm *pVm, ph7_value *pArg, sxu32 nFormalElem) {
ph7_hashmap *pMap;
ph7_hashmap_node *pNode;
ph7_value sValue;
sxu32 nArgElem, nCount;
int nRank, nCur;
sxi32 nRank, nCur;
if(nFormalElem == 0) {
/* Untyped array parameter accepts any value */
return 1;
@@ -673,12 +692,12 @@ static int VmOverloadArrayRank(ph7_vm *pVm, ph7_value *pArg, sxu32 nFormalElem)
* parameter. This routine is strictly read-only: it never casts, mutates or
* evaluates anything.
*/
static int VmOverloadArgRank(ph7_vm *pVm, ph7_value *pArg, ph7_vm_func_arg *pFormal) {
static sxi32 VmOverloadArgRank(ph7_vm *pVm, ph7_value *pArg, ph7_vm_func_arg *pFormal) {
ph7_class_instance *pInstance;
ph7_class *pHint;
SyHashEntry *pEntry;
sxu32 nArgType, nFormalType, nFormalElem;
int nDistance;
sxi32 nDistance;
nArgType = pArg->nType;
nFormalType = pFormal->nType;
if((nArgType & MEMOBJ_HASHMAP) == 0 && (nArgType & MEMOBJ_NULL)) {
@@ -750,11 +769,15 @@ static int VmOverloadArgRank(ph7_vm *pVm, ph7_value *pArg, ph7_vm_func_arg *pFor
* Compute the per-argument ranks of a candidate function. Return TRUE if
* every passed argument is compatible with the corresponding parameter.
*/
static int VmOverloadRankArgs(ph7_vm *pVm, ph7_vm_func *pFunc, ph7_value *aArg, int nArg, int *aRank) {
static sxi32 VmOverloadRankArgs(ph7_vm *pVm, ph7_vm_func *pFunc, ph7_value *aArg, sxu32 nArg, sxi32 *aRank) {
ph7_vm_func_arg *aFormal;
int i, nRank;
sxu32 i;
sxi32 nRank;
aFormal = (ph7_vm_func_arg *)SySetBasePtr(&pFunc->aArgs);
for(i = 0 ; i < nArg ; i++) {
if((aFormal[i].iFlags & VM_FUNC_ARG_BY_REF) && aArg[i].nIdx == SXU32_HIGH) {
return FALSE;
}
nRank = VmOverloadArgRank(&(*pVm), &aArg[i], &aFormal[i]);
if(nRank < 0) {
return FALSE;
@@ -765,11 +788,11 @@ static int VmOverloadRankArgs(ph7_vm *pVm, ph7_vm_func *pFunc, ph7_value *aArg,
}
/*
* Return TRUE if the first candidate is a strictly better overload than the
* second one. When all argument ranks are equal, a candidate matching the
* exact number of passed arguments is preferred over one using defaults.
* second one. When all argument ranks are equal, prefer fewer omitted defaults.
*/
static int VmOverloadDominates(const int *aFirst, const int *aSecond, int nArg, int bFirstExact, int bSecondExact) {
int i, bBetter = 0;
static sxi32 VmOverloadDominates(const sxi32 *aFirst, const sxi32 *aSecond, sxu32 nArg, sxu32 nFirstDefaults, sxu32 nSecondDefaults) {
sxu32 i;
sxi32 bBetter = 0;
for(i = 0 ; i < nArg ; i++) {
if(aFirst[i] > aSecond[i]) {
return FALSE;
@@ -781,11 +804,31 @@ static int VmOverloadDominates(const int *aFirst, const int *aSecond, int nArg,
if(bBetter) {
return TRUE;
}
return bFirstExact && !bSecondExact;
return nFirstDefaults < nSecondDefaults;
}
/* Forward declaration */
static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags);
static sxi32 VmLocalExec(ph7_vm *pVm, SySet *pByteCode, ph7_value *pResult);
static sxi32 VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
static ph7_class *VmMethodClass(ph7_vm *pVm);
/* Find a mounted name to transport a method call, not an eligible overload.
* The caller must retain its lookup root and receiver for VmOverload(). */
static ph7_class_method *VmMethodSeed(ph7_vm *pVm, ph7_class *pLookup, SyString *pName,
ph7_class_method *pPreferred, sxi32 bParent) {
sxu32 i;
if(pPreferred && SyHashGet(&pVm->hFunction, pPreferred->sVmName.zString, pPreferred->sVmName.nByte)) {
return pPreferred;
}
for(i = bParent ? 1 : 0; i < SySetUsed(&pLookup->aMro); i++) {
ph7_class *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pName->zString, pName->nByte);
if(pMeth && pMeth->sFunc.pClass == pClass &&
SyHashGet(&pVm->hFunction, pMeth->sVmName.zString, pMeth->sVmName.nByte)) {
return pMeth;
}
}
return 0;
}
/*
* Select the appropriate VM function for the current call context.
* This is the implementation of the powerful 'function overloading' feature
@@ -796,75 +839,134 @@ static ph7_vm_func *VmOverload(
ph7_vm *pVm, /* Target VM */
ph7_vm_func *pList, /* Linked list of candidates for overloading */
ph7_value *aArg, /* Array of passed arguments */
int nArg /* Total number of passed arguments */
sxi32 nArg, /* Total number of passed arguments */
ph7_class *pLookup, /* Method lookup root, or NULL for a function */
sxi32 bStatic, /* Call without a runtime receiver */
sxi32 bParent, /* Skip the declaring class for parent lookup */
sxi32 bInternal /* VM-invoked methods already have an access context */
) {
int iTarget, j, k, iArgs, nCandidates, nCompatible, nCols, bDominated;
sxu32 iTarget, j, k, iArgs, nCandidates, nCompatible, nCols, nPassed;
sxi32 bDominated;
ph7_vm_func **apSet;
ph7_vm_func *pLink;
ph7_vm_func_arg *pFuncArg;
SySet sSet;
int *aRank, *pRank;
SySet sMethods;
sxu32 iMethod;
sxi32 *aRank, *pRank;
sxu32 nRankBytes;
if(nArg < 0) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Invalid number of arguments passed to function/method '%z()'", &pList->sName);
}
nPassed = (sxu32)nArg;
SySetInit(&sSet, &pVm->sAllocator, sizeof(ph7_vm_func *));
pLink = pList;
SySetInit(&sMethods, &pVm->sAllocator, sizeof(ph7_vm_func *));
if(pLookup) {
sxu32 i, k;
ph7_class *pScope = VmMethodClass(pVm);
/* Lexical private declarations hide only identical input signatures,
* not the receiver's other overloads. */
if(!bParent && pScope && VmInstanceOf(pVm, pLookup, pScope)) {
ph7_class_method *pPrivate = PH7_ClassExtractMethod(pScope, pList->sName.zString, pList->sName.nByte);
SyHashEntry *pEntry = pPrivate && pPrivate->sFunc.pClass == pScope ?
SyHashGet(&pVm->hFunction, pPrivate->sVmName.zString, pPrivate->sVmName.nByte) : 0;
for(pLink = pEntry ? (ph7_vm_func *)pEntry->pUserData : 0; pLink; pLink = pLink->pNextName) {
ph7_class_method *pMeth = (ph7_class_method *)pLink;
if(pMeth->iProtection == PH7_CLASS_PROT_PRIVATE &&
VmClassMemberAccess(pVm, pLink->pClass, pMeth->iProtection) &&
!(pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) &&
(!bStatic || (pMeth->iFlags & PH7_CLASS_ATTR_STATIC))) {
if(SySetPut(&sMethods, &pLink) != SXRET_OK) {
PH7_VmMemoryError(pVm);
}
}
}
}
/* Walk C3 without modifying any declaration's overload chain. Only
* accessible declarations participate in identical-signature hiding. */
for(i = bParent ? 1 : 0; i < SySetUsed(&pLookup->aMro); i++) {
ph7_class *pClass = *(ph7_class **)SySetAt(&pLookup->aMro, i);
ph7_class_method *pMeth = PH7_ClassExtractMethod(pClass, pList->sName.zString, pList->sName.nByte);
SyHashEntry *pEntry;
if(pMeth == 0 || pMeth->sFunc.pClass != pClass) {
continue;
}
pEntry = SyHashGet(&pVm->hFunction, pMeth->sVmName.zString, pMeth->sVmName.nByte);
for(pLink = pEntry ? (ph7_vm_func *)pEntry->pUserData : 0; pLink; pLink = pLink->pNextName) {
pMeth = (ph7_class_method *)pLink;
if((pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) ||
(bStatic && !(pMeth->iFlags & PH7_CLASS_ATTR_STATIC) &&
(pLookup->iFlags & (PH7_CLASS_FINAL | PH7_CLASS_VIRTUAL)) != (PH7_CLASS_FINAL | PH7_CLASS_VIRTUAL)) ||
(!bInternal && !VmClassMemberAccess(pVm, pLink->pClass, pMeth->iProtection))) {
continue;
}
for(k = 0; k < SySetUsed(&sMethods); k++) {
if(PH7_VmSameSignature(pLink, *(ph7_vm_func **)SySetAt(&sMethods, k))) {
break;
}
}
if(k == SySetUsed(&sMethods) && SySetPut(&sMethods, &pLink) != SXRET_OK) {
PH7_VmMemoryError(pVm);
}
}
}
} else {
for(pLink = pList; pLink; pLink = pLink->pNextName) {
if(SySetPut(&sMethods, &pLink) != SXRET_OK) {
PH7_VmMemoryError(pVm);
}
}
}
/* Put functions whose arguments are compatible with the current call */
while(pLink) {
iArgs = (int)SySetUsed(&pLink->aArgs);
if(nArg == iArgs) {
for(iMethod = 0; iMethod < SySetUsed(&sMethods); iMethod++) {
pLink = *(ph7_vm_func **)SySetAt(&sMethods, iMethod);
iArgs = SySetUsed(&pLink->aArgs);
if(nPassed == iArgs) {
/* Exact amount of parameters, a candidate to call */
} else if(nArg < iArgs) {
} else if(nPassed < iArgs) {
/* Fewer parameters passed, check that every missing one has a default */
pFuncArg = (ph7_vm_func_arg *)SySetBasePtr(&pLink->aArgs);
for(j = nArg ; j < iArgs ; ++j) {
for(j = nPassed ; j < iArgs ; ++j) {
if(SySetUsed(&pFuncArg[j].aByteCode) < 1) {
break;
}
}
if(j < iArgs) {
/* A missing parameter is required */
pLink = pLink->pNextName;
continue;
}
} else {
/* Too many parameters passed */
pLink = pLink->pNextName;
continue;
}
if(SySetPut(&sSet, &pLink) != SXRET_OK) {
PH7_VmMemoryError(&(*pVm));
}
/* Point to the next entry */
pLink = pLink->pNextName;
}
nCandidates = (int)SySetUsed(&sSet);
nCandidates = SySetUsed(&sSet);
SySetRelease(&sMethods);
if(nCandidates < 1) {
/* No candidates, throw an error */
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Invalid number of arguments passed to function/method '%z()'", &pList->sName);
}
apSet = (ph7_vm_func **)SySetBasePtr(&sSet);
if(nCandidates < 2) {
/* Return the only candidate */
pLink = apSet[0];
SySetRelease(&sSet);
return pLink;
}
/* Rank every argument against every candidate. The last column holds
* whether the candidate is compatible with all passed arguments. */
nCols = nArg + 1;
if((sxu32)nCandidates > SXU32_HIGH / (sxu32)nCols / (sxu32)sizeof(int)) {
nCols = nPassed + 1;
if(nCandidates > SXU32_HIGH / nCols / (sxu32)sizeof(sxi32)) {
SySetRelease(&sSet);
PH7_VmMemoryError(&(*pVm));
}
nRankBytes = (sxu32)nCandidates * (sxu32)nCols * (sxu32)sizeof(int);
aRank = (int *)SyMemBackendAlloc(&pVm->sAllocator, nRankBytes);
nRankBytes = nCandidates * nCols * (sxu32)sizeof(sxi32);
aRank = (sxi32 *)SyMemBackendAlloc(&pVm->sAllocator, nRankBytes);
if(aRank == 0) {
PH7_VmMemoryError(&(*pVm));
}
nCompatible = 0;
for(j = 0 ; j < nCandidates ; j++) {
pRank = &aRank[j * nCols];
pRank[nArg] = VmOverloadRankArgs(&(*pVm), apSet[j], aArg, nArg, pRank);
if(pRank[nArg]) {
pRank[nPassed] = VmOverloadRankArgs(&(*pVm), apSet[j], aArg, nPassed, pRank);
if(pRank[nPassed]) {
++nCompatible;
}
}
@@ -875,25 +977,25 @@ static ph7_vm_func *VmOverload(
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "No matching overload found for function/method '%z()'", &pList->sName);
}
/* Select the unique candidate that is not dominated by another one */
iTarget = -1;
iTarget = SXU32_HIGH;
for(j = 0 ; j < nCandidates ; j++) {
if(aRank[j * nCols + nArg] == 0) {
if(aRank[j * nCols + nPassed] == 0) {
continue;
}
bDominated = 0;
for(k = 0 ; k < nCandidates ; k++) {
if(k == j || aRank[k * nCols + nArg] == 0) {
if(k == j || aRank[k * nCols + nPassed] == 0) {
continue;
}
if(VmOverloadDominates(&aRank[k * nCols], &aRank[j * nCols], nArg,
SySetUsed(&apSet[k]->aArgs) == (sxu32)nArg,
SySetUsed(&apSet[j]->aArgs) == (sxu32)nArg)) {
if(VmOverloadDominates(&aRank[k * nCols], &aRank[j * nCols], nPassed,
SySetUsed(&apSet[k]->aArgs) - nPassed,
SySetUsed(&apSet[j]->aArgs) - nPassed)) {
bDominated = 1;
break;
}
}
if(!bDominated) {
if(iTarget >= 0) {
if(iTarget != SXU32_HIGH) {
/* More than one candidate is equally good */
SyMemBackendFree(&pVm->sAllocator, aRank);
SySetRelease(&sSet);
@@ -968,7 +1070,7 @@ static sxi32 VmMountUserClass(
SyHashResetLoopCursor(&pClass->hMethod);
while((pEntry = SyHashGetNextEntry(&pClass->hMethod)) != 0) {
pMeth = (ph7_class_method *)pEntry->pUserData;
if((pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) == 0) {
if(pMeth->sFunc.pClass == pClass && (pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL) == 0) {
rc = PH7_VmInstallUserFunction(&(*pVm), &pMeth->sFunc, &pMeth->sVmName);
if(rc != SXRET_OK) {
return rc;
@@ -1390,7 +1492,6 @@ static ph7_value *VmNewOperandStack(
}
/* Forward declaration */
static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm);
static int VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
/* Exception frames retain the enclosing method's lexical scope. */
static ph7_class *VmMethodClass(ph7_vm *pVm) {
VmFrame *pFrame = pVm->pFrame;
@@ -1856,8 +1957,21 @@ PH7_PRIVATE sxi32 PH7_VmConfigure(
/* Remove leading and trailing white spaces */
SyStringFullTrim(&sPath);
if(sPath.nByte > 0) {
/* Store the path in the corresponding container */
sxu32 nSize = pVm->aPaths.nSize;
/* The caller's buffer may be temporary. Keep a VM-owned copy,
* freed with the private allocator in PH7_VmRelease().
*/
sPath.zString = SyMemBackendStrDup(&pVm->sAllocator, sPath.zString, sPath.nByte);
if(sPath.zString == 0) {
rc = SXERR_MEM;
break;
}
rc = SySetPut(&pVm->aPaths, (const void *)&sPath);
if(rc != SXRET_OK) {
/* SySetPut may change capacity before allocation fails. */
pVm->aPaths.nSize = nSize;
SyMemBackendFree(&pVm->sAllocator, (void *)sPath.zString);
}
}
break;
}
@@ -4499,30 +4613,13 @@ static sxi32 VmByteCodeExec(
pMeth = PH7_ClassExtractMethod(pClass, sName.zString, sName.nByte);
}
}
if(pMeth == 0) {
/* Browse hashtable from the beginning */
SyHashResetLoopCursor(&pClass->hDerived);
/* Search for appropriate class member */
SyHashEntry *pEntry;
while((pEntry = SyHashGetNextEntry(&pClass->hDerived)) != 0) {
pDerived = (ph7_class *) pEntry->pUserData;
pMeth = PH7_ClassExtractMethod(pDerived, sName.zString, sName.nByte);
if(pMeth) {
pClass = pDerived;
break;
}
}
}
pMeth = VmMethodSeed(pVm, pClass, &sName, pMeth, pNos->iFlags == MEMOBJ_PARENTOBJ);
}
if(pMeth == 0) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Call to undefined method '%z->%z()'",
&pClass->sName, &sName
);
} else {
if(!VmClassMemberAccess(&(*pVm), pMeth->sFunc.pClass, pMeth->iProtection)) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
"Method '%z->%z()' is inaccessible due to its protection level", &pMeth->sFunc.pClass->sName, &sName);
}
/* Push method name on the stack */
PH7_MemObjRelease(pTos);
SyBlobAppend(&pTos->sBlob, SyStringData(&pMeth->sVmName), SyStringLength(&pMeth->sVmName));
@@ -4665,11 +4762,13 @@ static sxi32 VmByteCodeExec(
if(pInstr->iP2) {
/* Method call */
ph7_class_method *pMeth = 0;
ph7_class_method *pSeed = 0;
if(sName.nByte > 0 && (pClass->iFlags & PH7_CLASS_INTERFACE) == 0) {
/* Extract the target method */
pMeth = PH7_ClassExtractMethod(pClass, sName.zString, sName.nByte);
pSeed = VmMethodSeed(pVm, pClass, &sName, pMeth, FALSE);
}
if(pMeth == 0 || (pMeth->iFlags & PH7_CLASS_ATTR_VIRTUAL)) {
if(pSeed == 0) {
if(pMeth) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot call virtual method '%z::%z()'",
&pClass->sName, &sName
@@ -4684,16 +4783,10 @@ static sxi32 VmByteCodeExec(
VmPopOperand(&pTos, 1);
}
PH7_MemObjRelease(pTos);
} else if((pMeth->iFlags & PH7_CLASS_ATTR_STATIC) == 0 && ((pClass->iFlags & PH7_CLASS_FINAL) == 0 || (pClass->iFlags & PH7_CLASS_VIRTUAL) == 0)) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "An object reference is required for the non-static method '%z::%z()'",
&pClass->sName, &sName);
} else if(!VmClassMemberAccess(&(*pVm), pClass, pMeth->iProtection)) {
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Method '%z::%z()' is inaccessible due to its protection level",
&pClass->sName, &sName);
} else {
/* Push method name on the stack */
PH7_MemObjRelease(pTos);
SyBlobAppend(&pTos->sBlob, SyStringData(&pMeth->sVmName), SyStringLength(&pMeth->sVmName));
SyBlobAppend(&pTos->sBlob, SyStringData(&pSeed->sVmName), SyStringLength(&pSeed->sVmName));
MemObjSetType(pTos, MEMOBJ_STRING);
}
pTos->nIdx = SXU32_HIGH;
@@ -4887,6 +4980,8 @@ static sxi32 VmByteCodeExec(
* OP_CALL P1 P2 *
* Call a PHP or a foreign function and push the return value of the called
* function on the stack.
* P2 bit 0 skips entry-point overload selection; bit 1 marks a VM-owned
* method call whose lifecycle access has already been established.
*/
case PH7_OP_CALL: {
ph7_value *pArg = &pTos[-pInstr->iP1];
@@ -4942,6 +5037,8 @@ static sxi32 VmByteCodeExec(
ph7_value *pFrameStack;
ph7_vm_func *pVmFunc;
ph7_class *pSelf;
ph7_class *pLookup = 0;
sxi32 bParent = 0;
VmFrame *pFrame = 0;
ph7_value *pObj;
VmSlot sArg;
@@ -4958,6 +5055,11 @@ static sxi32 VmByteCodeExec(
if(pTarget->nType & MEMOBJ_OBJ) {
/* Instance already loaded */
pThis = (ph7_class_instance *)pTarget->x.pOther;
pLookup = pThis->pClass;
if(pTarget->iFlags == MEMOBJ_PARENTOBJ && VmMethodClass(pVm)) {
pLookup = VmMethodClass(pVm);
bParent = 1;
}
pThis->iRef += 2;
pSelf = pVmFunc->pClass;
}
@@ -4972,6 +5074,9 @@ static sxi32 VmByteCodeExec(
}
}
VmPopOperand(&pTos, 1);
if(pLookup == 0) {
pLookup = pSelf;
}
PH7_MemObjRelease(pTos);
/* Synchronize pointers */
pArg = &pTos[-pInstr->iP1];
@@ -4986,8 +5091,8 @@ static sxi32 VmByteCodeExec(
}
}
/* Select an appropriate function to call, if not entry point */
if(pInstr->iP2 == 0) {
pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (int)(pTos - pArg));
if((pInstr->iP2 & 1) == 0) {
pVmFunc = VmOverload(&(*pVm), pVmFunc, pArg, (sxi32)(pTos - pArg), pLookup, pThis == 0, bParent, pInstr->iP2 & 2);
}
if(pThis && (pVmFunc->iFlags & VM_FUNC_CLASS_METHOD)) {
pSelf = pVmFunc->pClass;
@@ -6234,7 +6339,7 @@ static int vm_builtin_get_class_methods(ph7_context *pCtx, int nArg, ph7_value *
* in the pAttrName parameter is visible and thus can be extracted
* from the current scope.Otherwise FALSE is returned.
*/
static int VmClassMemberAccess(
static sxi32 VmClassMemberAccess(
ph7_vm *pVm, /* Target VM */
ph7_class *pClass, /* Target Class */
sxi32 iProtection /* Attribute protection level [i.e: public,protected or private] */
@@ -6580,7 +6685,8 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
) {
ph7_value *aStack;
VmInstr aInstr[2];
int iEntry;
ph7_class_method *pSeed;
sxi32 iEntry;
int iCursor;
int i;
/* Create a new operand stack */
@@ -6598,7 +6704,7 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
aStack[i].nIdx = apArg[i]->nIdx;
}
iCursor = nArg + 1;
iEntry = 0;
iEntry = 2; /* VM-owned method call: preserve access to lifecycle methods. */
if(pThis) {
/*
* Push the class instance so that the '$this' variable will be available.
@@ -6609,15 +6715,26 @@ PH7_PRIVATE sxi32 PH7_VmCallClassMethod(
if(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;
iEntry |= 1;
}
}
}
pSeed = VmMethodSeed(pVm, pThis ? pThis->pClass : pMethod->sFunc.pClass,
&pMethod->sFunc.sName, pMethod, FALSE);
if(pSeed == 0 || ((iEntry & 1) && pSeed != pMethod)) {
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Cannot call virtual method '%z::%z()'",
&pMethod->sFunc.pClass->sName, &pMethod->sFunc.sName);
}
if(pThis == 0 && pSeed != pMethod) {
/* A transport seed must not replace the internal call's lookup root. */
SyBlobAppend(&aStack[i].sBlob, pMethod->sFunc.pClass->sName.zString, pMethod->sFunc.pClass->sName.nByte);
MemObjSetType(&aStack[i], MEMOBJ_STRING);
}
aStack[i].nIdx = SXU32_HIGH; /* Mark as constant */
i++;
/* Push method name */
SyBlobReset(&aStack[i].sBlob);
SyBlobAppend(&aStack[i].sBlob, (const void *)SyStringData(&pMethod->sVmName), SyStringLength(&pMethod->sVmName));
SyBlobAppend(&aStack[i].sBlob, (const void *)SyStringData(&pSeed->sVmName), SyStringLength(&pSeed->sVmName));
aStack[i].nType = MEMOBJ_STRING;
aStack[i].nIdx = SXU32_HIGH;
static const SyString sFileName = { "[MEMORY]", sizeof("[MEMORY]") - 1};