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
Build / AerScript (push) Successful in 39s
This commit is contained in:
+8
-2
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user