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);
|
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) {
|
#define VM_OVERLOAD_RANK_OBJECT (SXI32_HIGH - 2)
|
||||||
const char *zSend = &pSecond->zString[pSecond->nByte];
|
#define VM_OVERLOAD_RANK_MIXED (SXI32_HIGH - 1)
|
||||||
const char *zFend = &pFirst->zString[pFirst->nByte];
|
#define VM_OVERLOAD_RANK_UNKNOWN (SXI32_HIGH)
|
||||||
const char *zSin = pSecond->zString;
|
/*
|
||||||
const char *zFin = pFirst->zString;
|
* A single class reached while walking the inheritance/interface graph.
|
||||||
const char *zPtr = zFin;
|
*/
|
||||||
for(;;) {
|
typedef struct VmOverloadClassWalk VmOverloadClassWalk;
|
||||||
if(zFin >= zFend || zSin >= zSend) {
|
struct VmOverloadClassWalk {
|
||||||
break;
|
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 */
|
/* Forward declaration */
|
||||||
static sxi32 VmExecIncludedFile(ph7_vm *pVm, SyString *pPath, int iFlags);
|
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 */
|
ph7_value *aArg, /* Array of passed arguments */
|
||||||
int nArg /* Total number of passed arguments */
|
int nArg /* Total number of passed arguments */
|
||||||
) {
|
) {
|
||||||
int iTarget, i, j, iArgs, iCur, iMax;
|
int iTarget, j, k, iArgs, nCandidates, nCompatible, nCols, bDominated;
|
||||||
ph7_vm_func *apSet[10]; /* Maximum number of candidates */
|
ph7_vm_func **apSet;
|
||||||
ph7_vm_func *pLink;
|
ph7_vm_func *pLink;
|
||||||
ph7_vm_func_arg *pFuncArg;
|
ph7_vm_func_arg *pFuncArg;
|
||||||
SyString sArgSig;
|
SySet sSet;
|
||||||
SyBlob sSig;
|
int *aRank, *pRank;
|
||||||
|
sxu32 nRankBytes;
|
||||||
|
SySetInit(&sSet, &pVm->sAllocator, sizeof(ph7_vm_func *));
|
||||||
pLink = pList;
|
pLink = pList;
|
||||||
i = 0;
|
/* Put functions whose arguments are compatible with the current call */
|
||||||
/* Put functions expecting the same number of passed arguments */
|
while(pLink) {
|
||||||
while(i < (int)SX_ARRAYSIZE(apSet)) {
|
iArgs = (int)SySetUsed(&pLink->aArgs);
|
||||||
if(pLink == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
iArgs = (int) SySetUsed(&pLink->aArgs);
|
|
||||||
if(nArg == iArgs) {
|
if(nArg == iArgs) {
|
||||||
/* Exact amount of parameters, a candidate to call */
|
/* Exact amount of parameters, a candidate to call */
|
||||||
apSet[i++] = pLink;
|
|
||||||
} else if(nArg < iArgs) {
|
} else if(nArg < iArgs) {
|
||||||
/* Fewer parameters passed, check if all are required */
|
/* Fewer parameters passed, check that every missing one has a default */
|
||||||
pFuncArg = (ph7_vm_func_arg *) SySetAt(&pLink->aArgs, nArg);
|
pFuncArg = (ph7_vm_func_arg *)SySetBasePtr(&pLink->aArgs);
|
||||||
if(pFuncArg) {
|
for(j = nArg ; j < iArgs ; ++j) {
|
||||||
if(SySetUsed(&pFuncArg->aByteCode) >= 1) {
|
if(SySetUsed(&pFuncArg[j].aByteCode) < 1) {
|
||||||
/* First missing parameter has a compiled default value associated, a candidate to call */
|
break;
|
||||||
apSet[i++] = pLink;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 */
|
/* Point to the next entry */
|
||||||
pLink = pLink->pNextName;
|
pLink = pLink->pNextName;
|
||||||
}
|
}
|
||||||
if(i < 1) {
|
nCandidates = (int)SySetUsed(&sSet);
|
||||||
|
if(nCandidates < 1) {
|
||||||
/* No candidates, throw an error */
|
/* No candidates, throw an error */
|
||||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Invalid number of arguments passed to function/method '%z()'", &pList->sName);
|
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 the only candidate */
|
||||||
return apSet[0];
|
pLink = apSet[0];
|
||||||
|
SySetRelease(&sSet);
|
||||||
|
return pLink;
|
||||||
}
|
}
|
||||||
/* Calculate function signature */
|
/* Rank every argument against every candidate. The last column holds
|
||||||
SyBlobInit(&sSig, &pVm->sAllocator);
|
* whether the candidate is compatible with all passed arguments. */
|
||||||
for(j = 0 ; j < nArg ; j++) {
|
nCols = nArg + 1;
|
||||||
int c = 'n'; /* null */
|
if((sxu32)nCandidates > SXU32_HIGH / (sxu32)nCols / (sxu32)sizeof(int)) {
|
||||||
if(aArg[j].nType & MEMOBJ_BOOL) {
|
SySetRelease(&sSet);
|
||||||
/* Bool */
|
PH7_VmMemoryError(&(*pVm));
|
||||||
c = 'b';
|
}
|
||||||
} else if(aArg[j].nType & MEMOBJ_CALL) {
|
nRankBytes = (sxu32)nCandidates * (sxu32)nCols * (sxu32)sizeof(int);
|
||||||
/* Callback */
|
aRank = (int *)SyMemBackendAlloc(&pVm->sAllocator, nRankBytes);
|
||||||
c = 'a';
|
if(aRank == 0) {
|
||||||
} else if(aArg[j].nType & MEMOBJ_CHAR) {
|
PH7_VmMemoryError(&(*pVm));
|
||||||
/* Char */
|
}
|
||||||
c = 'c';
|
nCompatible = 0;
|
||||||
} else if(aArg[j].nType & MEMOBJ_INT) {
|
for(j = 0 ; j < nCandidates ; j++) {
|
||||||
/* Integer */
|
pRank = &aRank[j * nCols];
|
||||||
c = 'i';
|
pRank[nArg] = VmOverloadRankArgs(&(*pVm), apSet[j], aArg, nArg, pRank);
|
||||||
} else if(aArg[j].nType & MEMOBJ_MIXED) {
|
if(pRank[nArg]) {
|
||||||
/* Mixed */
|
++nCompatible;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SyStringInitFromBuf(&sArgSig, SyBlobData(&sSig), SyBlobLength(&sSig));
|
if(nCompatible < 1) {
|
||||||
iTarget = 0;
|
/* Candidates exist but none accepts the passed arguments */
|
||||||
iMax = -1;
|
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||||
/* Select the appropriate function */
|
SySetRelease(&sSet);
|
||||||
for(j = 0 ; j < i ; j++) {
|
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "No matching overload found for function/method '%z()'", &pList->sName);
|
||||||
/* Compare the two signatures */
|
}
|
||||||
iCur = VmOverloadCompare(&sArgSig, &apSet[j]->sSignature);
|
/* Select the unique candidate that is not dominated by another one */
|
||||||
if(iCur > iMax) {
|
iTarget = -1;
|
||||||
iMax = iCur;
|
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;
|
iTarget = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SyBlobRelease(&sSig);
|
pLink = apSet[iTarget];
|
||||||
|
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||||
|
SySetRelease(&sSet);
|
||||||
/* Appropriate function for the current call context */
|
/* Appropriate function for the current call context */
|
||||||
return apSet[iTarget];
|
return pLink;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Mount a compiled class into the freshly created virtual machine so that
|
* Mount a compiled class into the freshly created virtual machine so that
|
||||||
@@ -1068,7 +1390,6 @@ static ph7_value *VmNewOperandStack(
|
|||||||
}
|
}
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
static sxi32 VmRegisterSpecialFunction(ph7_vm *pVm);
|
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);
|
static int VmClassMemberAccess(ph7_vm *pVm, ph7_class *pClass, sxi32 iProtection);
|
||||||
/*
|
/*
|
||||||
* Prepare the Virtual Machine for byte-code execution.
|
* Prepare the Virtual Machine for byte-code execution.
|
||||||
|
|||||||
Reference in New Issue
Block a user