Fix overload resolution using argument compatibility and recursive array matching
Build / AerScript (push) Successful in 1m1s
Build / AerScript (push) Successful in 1m1s
This commit is contained in:
+414
-93
@@ -450,27 +450,338 @@ static void VmLeaveFrame(ph7_vm *pVm) {
|
||||
SyMemBackendPoolFree(&pVm->sAllocator, pFrame);
|
||||
}
|
||||
}
|
||||
/* Forward declaration */
|
||||
static int VmInstanceOf(ph7_class *pThis, ph7_class *pClass);
|
||||
/*
|
||||
* Compare two functions signature and return the comparison result.
|
||||
* Ranks for general parameter types. Class and interface specificity uses
|
||||
* the shortest inheritance distance instead, which is always lower than
|
||||
* these values.
|
||||
*/
|
||||
static int VmOverloadCompare(SyString *pFirst, SyString *pSecond) {
|
||||
const char *zSend = &pSecond->zString[pSecond->nByte];
|
||||
const char *zFend = &pFirst->zString[pFirst->nByte];
|
||||
const char *zSin = pSecond->zString;
|
||||
const char *zFin = pFirst->zString;
|
||||
const char *zPtr = zFin;
|
||||
for(;;) {
|
||||
if(zFin >= zFend || zSin >= zSend) {
|
||||
break;
|
||||
#define VM_OVERLOAD_RANK_OBJECT (SXI32_HIGH - 2)
|
||||
#define VM_OVERLOAD_RANK_MIXED (SXI32_HIGH - 1)
|
||||
#define VM_OVERLOAD_RANK_UNKNOWN (SXI32_HIGH)
|
||||
/*
|
||||
* A single class reached while walking the inheritance/interface graph.
|
||||
*/
|
||||
typedef struct VmOverloadClassWalk VmOverloadClassWalk;
|
||||
struct VmOverloadClassWalk {
|
||||
ph7_class *pClass; /* Class or interface */
|
||||
int 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) {
|
||||
ph7_class **apVisited;
|
||||
VmOverloadClassWalk sEntry;
|
||||
sxu32 i;
|
||||
apVisited = (ph7_class **)SySetBasePtr(pVisited);
|
||||
for(i = 0 ; i < SySetUsed(pVisited) ; ++i) {
|
||||
if(apVisited[i] == pClass) {
|
||||
return;
|
||||
}
|
||||
if(zFin[0] != zSin[0]) {
|
||||
/* mismatch */
|
||||
break;
|
||||
}
|
||||
zFin++;
|
||||
zSin++;
|
||||
}
|
||||
return (int)(zFin - zPtr);
|
||||
if(SySetPut(pVisited, &pClass) != SXRET_OK) {
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
sEntry.pClass = pClass;
|
||||
sEntry.nDepth = nDepth;
|
||||
if(SySetPut(pQueue, &sEntry) != SXRET_OK) {
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Compute the shortest inheritance/interface distance between pClass and
|
||||
* pHint. Return 0 for an exact class match, a positive distance for a base
|
||||
* 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) {
|
||||
SySet sQueue, sVisited;
|
||||
VmOverloadClassWalk *pEntry;
|
||||
ph7_class **apInterface, *pCur;
|
||||
SyHashEntry *pHash;
|
||||
sxu32 i, n;
|
||||
int nDepth, nResult = -1;
|
||||
if(pClass == pHint) {
|
||||
return 0;
|
||||
}
|
||||
SySetInit(&sQueue, &pVm->sAllocator, sizeof(VmOverloadClassWalk));
|
||||
SySetInit(&sVisited, &pVm->sAllocator, sizeof(ph7_class *));
|
||||
VmOverloadEnqueueClass(&(*pVm), &sQueue, &sVisited, pClass, 0);
|
||||
for(i = 0 ; i < SySetUsed(&sQueue) ; ++i) {
|
||||
pEntry = (VmOverloadClassWalk *)SySetAt(&sQueue, i);
|
||||
pCur = pEntry->pClass;
|
||||
nDepth = pEntry->nDepth;
|
||||
if(pCur == pHint) {
|
||||
nResult = nDepth;
|
||||
break;
|
||||
}
|
||||
/* Direct base classes and base interfaces */
|
||||
SyHashResetLoopCursor(&pCur->hDerived);
|
||||
while((pHash = SyHashGetNextEntry(&pCur->hDerived)) != 0) {
|
||||
VmOverloadEnqueueClass(&(*pVm), &sQueue, &sVisited, (ph7_class *)pHash->pUserData, nDepth + 1);
|
||||
}
|
||||
/* Implemented interfaces */
|
||||
apInterface = (ph7_class **)SySetBasePtr(&pCur->aInterface);
|
||||
n = SySetUsed(&pCur->aInterface);
|
||||
while(n > 0) {
|
||||
--n;
|
||||
VmOverloadEnqueueClass(&(*pVm), &sQueue, &sVisited, apInterface[n], nDepth + 1);
|
||||
}
|
||||
}
|
||||
SySetRelease(&sQueue);
|
||||
SySetRelease(&sVisited);
|
||||
return nResult;
|
||||
}
|
||||
/*
|
||||
* Rank a NULL value, or an empty object reference, against a formal
|
||||
* 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) {
|
||||
sxu32 nFormalType = pFormal->nType;
|
||||
SyHashEntry *pEntry;
|
||||
if(nFormalType == SXU32_HIGH) {
|
||||
/* Class or interface type hint */
|
||||
pEntry = SyHashGet(&pVm->hClass, (const void *)pFormal->sClass.zString, pFormal->sClass.nByte);
|
||||
return pEntry != 0 ? 1 : 4;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_MIXED) {
|
||||
/* 'mixed' and 'mixed[]' are the least specific parameters */
|
||||
return 3;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_HASHMAP) {
|
||||
/* Typed array, including 'object[]' */
|
||||
return 1;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_OBJ) {
|
||||
/* The general 'object' keyword */
|
||||
return 2;
|
||||
}
|
||||
if(nFormalType == 0) {
|
||||
/* No type hint at all */
|
||||
return 3;
|
||||
}
|
||||
/* Concrete scalar type */
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* Rank a scalar value against a formal parameter using the conversions
|
||||
* allowed by PH7_CheckVarCompat(). The lower the returned rank, the more
|
||||
* 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) {
|
||||
if(nArgType == nFormalType) {
|
||||
/* Exact type match */
|
||||
return 0;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_REAL) {
|
||||
if(nArgType & MEMOBJ_INT) {
|
||||
/* INT to FLOAT */
|
||||
return 1;
|
||||
}
|
||||
} else if(nFormalType & MEMOBJ_CHAR) {
|
||||
if(nArgType & MEMOBJ_INT) {
|
||||
/* INT to CHAR */
|
||||
return 1;
|
||||
}
|
||||
if(nArgType & MEMOBJ_STRING) {
|
||||
/* STRING to CHAR (only if not too long) */
|
||||
sxu32 nLen = SyBlobLength(&pArg->sBlob);
|
||||
if(nLen == 0 || nLen == 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else if(nFormalType & MEMOBJ_STRING) {
|
||||
if(nArgType & MEMOBJ_CHAR) {
|
||||
/* CHAR to STRING */
|
||||
return 1;
|
||||
}
|
||||
} else if(nFormalType & MEMOBJ_CALL) {
|
||||
if(nArgType & MEMOBJ_STRING) {
|
||||
/* STRING to CALLBACK */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Rank an array argument against a typed array parameter. Entries are
|
||||
* traversed through their insertion-order links (pFirst/pPrev), so the
|
||||
* shared foreach loop cursor (pMap->pCur) is left untouched. An exact
|
||||
* container type match is ranked before the walk while every other element is
|
||||
* ranked against the formal element type, recursing into nested arrays just
|
||||
* like the binding code does. Values are never cast or modified. NULL elements
|
||||
* 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) {
|
||||
ph7_hashmap *pMap;
|
||||
ph7_hashmap_node *pNode;
|
||||
ph7_value sValue;
|
||||
sxu32 nArgElem, nCount;
|
||||
int nRank, nCur;
|
||||
if(nFormalElem == 0) {
|
||||
/* Untyped array parameter accepts any value */
|
||||
return 1;
|
||||
}
|
||||
nArgElem = pArg->nType & ~MEMOBJ_HASHMAP;
|
||||
if(nArgElem == nFormalElem) {
|
||||
/* Container types match exactly, the binding code plain-stores it */
|
||||
return 0;
|
||||
}
|
||||
pMap = (ph7_hashmap *)pArg->x.pOther;
|
||||
if(pMap == 0 || pMap->nEntry < 1) {
|
||||
/* An empty array is compatible with any typed array */
|
||||
return 1;
|
||||
}
|
||||
nCount = 0;
|
||||
nRank = 0;
|
||||
for(pNode = pMap->pFirst ; pNode ; pNode = pNode->pPrev) {
|
||||
PH7_MemObjInit(&(*pVm), &sValue);
|
||||
PH7_HashmapExtractNodeValue(pNode, &sValue, FALSE);
|
||||
if(sValue.nType & MEMOBJ_NULL) {
|
||||
/* NULL matches any element type without raising the rank */
|
||||
PH7_MemObjRelease(&sValue);
|
||||
continue;
|
||||
}
|
||||
if(sValue.nType & MEMOBJ_HASHMAP) {
|
||||
/* Nested array, rank it recursively like the binding code does */
|
||||
nCur = VmOverloadArrayRank(&(*pVm), &sValue, nFormalElem);
|
||||
} else {
|
||||
nCur = VmOverloadScalarRank(&sValue, sValue.nType, nFormalElem);
|
||||
}
|
||||
if(nCur < 0) {
|
||||
PH7_MemObjRelease(&sValue);
|
||||
return -1;
|
||||
}
|
||||
if(nCur > nRank) {
|
||||
nRank = nCur;
|
||||
}
|
||||
++nCount;
|
||||
PH7_MemObjRelease(&sValue);
|
||||
}
|
||||
return nCount > 0 ? nRank : 1;
|
||||
}
|
||||
/*
|
||||
* Rank a single passed argument against a formal parameter for the purpose
|
||||
* of overload resolution. Lower ranks denote a more specific match while a
|
||||
* negative value means that the argument is not compatible with the given
|
||||
* 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) {
|
||||
ph7_class_instance *pInstance;
|
||||
ph7_class *pHint;
|
||||
SyHashEntry *pEntry;
|
||||
sxu32 nArgType, nFormalType, nFormalElem;
|
||||
int nDistance;
|
||||
nArgType = pArg->nType;
|
||||
nFormalType = pFormal->nType;
|
||||
if((nArgType & MEMOBJ_HASHMAP) == 0 && (nArgType & MEMOBJ_NULL)) {
|
||||
/* Scalar NULL is compatible with every type */
|
||||
return VmOverloadNullRank(pVm, pFormal);
|
||||
}
|
||||
if(nFormalType == SXU32_HIGH) {
|
||||
/* Class or interface type hint */
|
||||
if((nArgType & MEMOBJ_OBJ) == 0 || (nArgType & MEMOBJ_HASHMAP) != 0) {
|
||||
/* Arrays use the same type encoding for 'Class[]' hints */
|
||||
return -1;
|
||||
}
|
||||
pInstance = (ph7_class_instance *)pArg->x.pOther;
|
||||
if(pInstance == 0 || pInstance->pClass == 0) {
|
||||
/* Empty object reference, NULL-compatible with any type */
|
||||
return VmOverloadNullRank(pVm, pFormal);
|
||||
}
|
||||
pEntry = SyHashGet(&pVm->hClass, (const void *)pFormal->sClass.zString, pFormal->sClass.nByte);
|
||||
if(pEntry == 0) {
|
||||
/* Unknown class hint. Binding would silently accept any object,
|
||||
* so never let it outrank a general parameter type. */
|
||||
return VM_OVERLOAD_RANK_UNKNOWN;
|
||||
}
|
||||
pHint = (ph7_class *)pEntry->pUserData;
|
||||
nDistance = VmOverloadClassDistance(&(*pVm), pInstance->pClass, pHint);
|
||||
if(nDistance < 0) {
|
||||
return -1;
|
||||
}
|
||||
return nDistance;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_MIXED) {
|
||||
if(((nFormalType & MEMOBJ_HASHMAP) != 0) != ((nArgType & MEMOBJ_HASHMAP) != 0)) {
|
||||
/* 'mixed' does not accept arrays while 'mixed[]' expects one */
|
||||
return -1;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_HASHMAP) {
|
||||
/* 'mixed[]' is the least specific array parameter */
|
||||
return VM_OVERLOAD_RANK_MIXED;
|
||||
}
|
||||
/* 'mixed' is less specific than every other type */
|
||||
return (nArgType == nFormalType) ? 0 : VM_OVERLOAD_RANK_MIXED;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_HASHMAP) {
|
||||
if((nArgType & MEMOBJ_HASHMAP) == 0) {
|
||||
return -1;
|
||||
}
|
||||
nFormalElem = nFormalType & ~MEMOBJ_HASHMAP;
|
||||
return VmOverloadArrayRank(&(*pVm), pArg, nFormalElem);
|
||||
}
|
||||
if(nArgType & MEMOBJ_HASHMAP) {
|
||||
/* Array cannot be passed to a scalar or object parameter */
|
||||
return -1;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_OBJ) {
|
||||
if((nArgType & MEMOBJ_OBJ) == 0) {
|
||||
return -1;
|
||||
}
|
||||
pInstance = (ph7_class_instance *)pArg->x.pOther;
|
||||
if(pInstance == 0 || pInstance->pClass == 0) {
|
||||
/* Empty object reference, NULL-compatible with any type */
|
||||
return VmOverloadNullRank(pVm, pFormal);
|
||||
}
|
||||
/* 'object' is less specific than a concrete class or interface */
|
||||
return VM_OVERLOAD_RANK_OBJECT;
|
||||
}
|
||||
return VmOverloadScalarRank(pArg, nArgType, nFormalType);
|
||||
}
|
||||
/*
|
||||
* 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) {
|
||||
ph7_vm_func_arg *aFormal;
|
||||
int i, nRank;
|
||||
aFormal = (ph7_vm_func_arg *)SySetBasePtr(&pFunc->aArgs);
|
||||
for(i = 0 ; i < nArg ; i++) {
|
||||
nRank = VmOverloadArgRank(&(*pVm), &aArg[i], &aFormal[i]);
|
||||
if(nRank < 0) {
|
||||
return FALSE;
|
||||
}
|
||||
aRank[i] = nRank;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int VmOverloadDominates(const int *aFirst, const int *aSecond, int nArg, int bFirstExact, int bSecondExact) {
|
||||
int i, bBetter = 0;
|
||||
for(i = 0 ; i < nArg ; i++) {
|
||||
if(aFirst[i] > aSecond[i]) {
|
||||
return FALSE;
|
||||
}
|
||||
if(aFirst[i] < aSecond[i]) {
|
||||
bBetter = 1;
|
||||
}
|
||||
}
|
||||
if(bBetter) {
|
||||
return TRUE;
|
||||
}
|
||||
return bFirstExact && !bSecondExact;
|
||||
}
|
||||
/* Forward declaration */
|
||||
static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags);
|
||||
@@ -487,104 +798,115 @@ static ph7_vm_func *VmOverload(
|
||||
ph7_value *aArg, /* Array of passed arguments */
|
||||
int nArg /* Total number of passed arguments */
|
||||
) {
|
||||
int iTarget, i, j, iArgs, iCur, iMax;
|
||||
ph7_vm_func *apSet[10]; /* Maximum number of candidates */
|
||||
int iTarget, j, k, iArgs, nCandidates, nCompatible, nCols, bDominated;
|
||||
ph7_vm_func **apSet;
|
||||
ph7_vm_func *pLink;
|
||||
ph7_vm_func_arg *pFuncArg;
|
||||
SyString sArgSig;
|
||||
SyBlob sSig;
|
||||
SySet sSet;
|
||||
int *aRank, *pRank;
|
||||
sxu32 nRankBytes;
|
||||
SySetInit(&sSet, &pVm->sAllocator, sizeof(ph7_vm_func *));
|
||||
pLink = pList;
|
||||
i = 0;
|
||||
/* Put functions expecting the same number of passed arguments */
|
||||
while(i < (int)SX_ARRAYSIZE(apSet)) {
|
||||
if(pLink == 0) {
|
||||
break;
|
||||
}
|
||||
iArgs = (int) SySetUsed(&pLink->aArgs);
|
||||
/* Put functions whose arguments are compatible with the current call */
|
||||
while(pLink) {
|
||||
iArgs = (int)SySetUsed(&pLink->aArgs);
|
||||
if(nArg == iArgs) {
|
||||
/* Exact amount of parameters, a candidate to call */
|
||||
apSet[i++] = pLink;
|
||||
} else if(nArg < iArgs) {
|
||||
/* Fewer parameters passed, check if all are required */
|
||||
pFuncArg = (ph7_vm_func_arg *) SySetAt(&pLink->aArgs, nArg);
|
||||
if(pFuncArg) {
|
||||
if(SySetUsed(&pFuncArg->aByteCode) >= 1) {
|
||||
/* First missing parameter has a compiled default value associated, a candidate to call */
|
||||
apSet[i++] = pLink;
|
||||
/* 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) {
|
||||
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;
|
||||
}
|
||||
if(i < 1) {
|
||||
nCandidates = (int)SySetUsed(&sSet);
|
||||
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);
|
||||
}
|
||||
if(nArg < 1 || i < 2) {
|
||||
apSet = (ph7_vm_func **)SySetBasePtr(&sSet);
|
||||
if(nCandidates < 2) {
|
||||
/* Return the only candidate */
|
||||
return apSet[0];
|
||||
pLink = apSet[0];
|
||||
SySetRelease(&sSet);
|
||||
return pLink;
|
||||
}
|
||||
/* Calculate function signature */
|
||||
SyBlobInit(&sSig, &pVm->sAllocator);
|
||||
for(j = 0 ; j < nArg ; j++) {
|
||||
int c = 'n'; /* null */
|
||||
if(aArg[j].nType & MEMOBJ_BOOL) {
|
||||
/* Bool */
|
||||
c = 'b';
|
||||
} else if(aArg[j].nType & MEMOBJ_CALL) {
|
||||
/* Callback */
|
||||
c = 'a';
|
||||
} else if(aArg[j].nType & MEMOBJ_CHAR) {
|
||||
/* Char */
|
||||
c = 'c';
|
||||
} else if(aArg[j].nType & MEMOBJ_INT) {
|
||||
/* Integer */
|
||||
c = 'i';
|
||||
} else if(aArg[j].nType & MEMOBJ_MIXED) {
|
||||
/* Mixed */
|
||||
c = 'm';
|
||||
} else if(aArg[j].nType & MEMOBJ_OBJ) {
|
||||
/* Class instance */
|
||||
ph7_class *pClass = ((ph7_class_instance *)aArg[j].x.pOther)->pClass;
|
||||
SyString *pName = &pClass->sName;
|
||||
SyBlobAppend(&sSig, (const void *)pName->zString, pName->nByte);
|
||||
c = -1;
|
||||
} else if(aArg[j].nType & MEMOBJ_REAL) {
|
||||
/* Float */
|
||||
c = 'f';
|
||||
} else if(aArg[j].nType & MEMOBJ_RES) {
|
||||
/* Resource */
|
||||
c = 'r';
|
||||
} else if(aArg[j].nType & MEMOBJ_STRING) {
|
||||
/* String */
|
||||
c = 's';
|
||||
} else if(aArg[j].nType & MEMOBJ_VOID) {
|
||||
/* Void */
|
||||
c = 'v';
|
||||
}
|
||||
if(aArg[j].nType & MEMOBJ_HASHMAP && (aArg[j].nType & MEMOBJ_OBJ) == 0) {
|
||||
c = SyToUpper(c);
|
||||
}
|
||||
if(c > 0) {
|
||||
SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
|
||||
/* 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)) {
|
||||
SySetRelease(&sSet);
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
nRankBytes = (sxu32)nCandidates * (sxu32)nCols * (sxu32)sizeof(int);
|
||||
aRank = (int *)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]) {
|
||||
++nCompatible;
|
||||
}
|
||||
}
|
||||
SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
|
||||
iTarget = 0;
|
||||
iMax = -1;
|
||||
/* Select the appropriate function */
|
||||
for(j = 0 ; j < i ; j++) {
|
||||
/* Compare the two signatures */
|
||||
iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
|
||||
if(iCur > iMax) {
|
||||
iMax = iCur;
|
||||
if(nCompatible < 1) {
|
||||
/* Candidates exist but none accepts the passed arguments */
|
||||
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||
SySetRelease(&sSet);
|
||||
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;
|
||||
for(j = 0 ; j < nCandidates ; j++) {
|
||||
if(aRank[j * nCols + nArg] == 0) {
|
||||
continue;
|
||||
}
|
||||
bDominated = 0;
|
||||
for(k = 0 ; k < nCandidates ; k++) {
|
||||
if(k == j || aRank[k * nCols + nArg] == 0) {
|
||||
continue;
|
||||
}
|
||||
if(VmOverloadDominates(&aRank[k * nCols], &aRank[j * nCols], nArg,
|
||||
SySetUsed(&apSet[k]->aArgs) == (sxu32)nArg,
|
||||
SySetUsed(&apSet[j]->aArgs) == (sxu32)nArg)) {
|
||||
bDominated = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!bDominated) {
|
||||
if(iTarget >= 0) {
|
||||
/* More than one candidate is equally good */
|
||||
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||
SySetRelease(&sSet);
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Ambiguous call to overloaded function/method '%z()'", &pList->sName);
|
||||
}
|
||||
iTarget = j;
|
||||
}
|
||||
}
|
||||
SyBlobRelease(&sSig);
|
||||
pLink = apSet[iTarget];
|
||||
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||
SySetRelease(&sSet);
|
||||
/* Appropriate function for the current call context */
|
||||
return apSet[iTarget];
|
||||
return pLink;
|
||||
}
|
||||
/*
|
||||
* Mount a compiled class into the freshly created virtual machine so that
|
||||
@@ -1068,7 +1390,6 @@ static ph7_value *VmNewOperandStack(
|
||||
}
|
||||
/* Forward declaration */
|
||||
static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm);
|
||||
static int VmInstanceOf(ph7_class *pThis, ph7_class *pClass);
|
||||
static int VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
|
||||
/*
|
||||
* Prepare the Virtual Machine for byte-code execution.
|
||||
|
||||
Reference in New Issue
Block a user