Implement nominal class and interface types across declarations and calls
Build / AerScript (push) Successful in 37s
Build / AerScript (push) Successful in 37s
This commit is contained in:
+72
-25
@@ -981,6 +981,36 @@ PH7_PRIVATE sxi32 PH7_CompileArray(ph7_gen_state *pGen, sxi32 iCompileFlag) {
|
||||
/* Node successfully compiled */
|
||||
return SXRET_OK;
|
||||
}
|
||||
/* Names, not class pointers, survive forward declarations and arena growth. */
|
||||
static sxu32 GenStateNamedType(ph7_gen_state *pGen, SyString *pName) {
|
||||
SyString sName;
|
||||
sxu32 i;
|
||||
char *zName;
|
||||
for(i = 0; i < SySetUsed(&pGen->pVm->aTypeNames); i++) {
|
||||
if(SyStringCmp(pName, (SyString *)SySetAt(&pGen->pVm->aTypeNames, i), SyMemcmp) == 0) {
|
||||
return ((i + 1) << 12) | MEMOBJ_OBJ;
|
||||
}
|
||||
}
|
||||
zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
|
||||
if(zName == 0 || i >= (MEMOBJ_NAMED >> 12)) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "PH7 engine is running out-of-memory");
|
||||
}
|
||||
SyStringInitFromBuf(&sName, zName, pName->nByte);
|
||||
if(SySetPut(&pGen->pVm->aTypeNames, &sName) != SXRET_OK) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "PH7 engine is running out-of-memory");
|
||||
}
|
||||
return ((i + 1) << 12) | MEMOBJ_OBJ;
|
||||
}
|
||||
static sxi32 GenStateIsNamedDeclaration(SyToken *pIn, SyToken *pEnd) {
|
||||
if(pIn >= pEnd || (pIn->nType & PH7_TK_ID) == 0 || (pIn->nType & PH7_TK_OP)) {
|
||||
return FALSE;
|
||||
}
|
||||
pIn++;
|
||||
if(pIn < pEnd && &pIn[1] < pEnd && (pIn->nType & PH7_TK_OSB) && (pIn[1].nType & PH7_TK_CSB)) {
|
||||
pIn += 2;
|
||||
}
|
||||
return pIn < pEnd && (pIn->nType & PH7_TK_DOLLAR);
|
||||
}
|
||||
/*
|
||||
* Compile a closure (anonymous function).
|
||||
* Closures (also known as anonymous functions), allow the creation of functions
|
||||
@@ -1010,7 +1040,9 @@ PH7_PRIVATE sxi32 PH7_CompileClosure(ph7_gen_state *pGen, sxi32 iCompileFlag) {
|
||||
sxu32 nType = 0;
|
||||
SXUNUSED(iCompileFlag);
|
||||
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & PH7_KEYWORD_BOOL) {
|
||||
if(pGen->pIn->nType & PH7_TK_ID) {
|
||||
nType = GenStateNamedType(pGen, &pGen->pIn->sData);
|
||||
} else if(nKey & PH7_KEYWORD_BOOL) {
|
||||
nType = MEMOBJ_BOOL;
|
||||
} else if(nKey & PH7_KEYWORD_CALLBACK) {
|
||||
nType = MEMOBJ_CALL;
|
||||
@@ -2057,7 +2089,7 @@ static sxi32 PH7_CompileFor(ph7_gen_state *pGen) {
|
||||
pTmp = pGen->pEnd;
|
||||
pGen->pEnd = pEnd;
|
||||
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) {
|
||||
if((nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) || GenStateIsNamedDeclaration(pGen->pIn, pGen->pEnd)) {
|
||||
/* Compile variable */
|
||||
PH7_CompileVar(&(*pGen));
|
||||
}
|
||||
@@ -2243,14 +2275,18 @@ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) {
|
||||
pTmp = pGen->pEnd;
|
||||
pGen->pEnd = pCur;
|
||||
nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) {
|
||||
if((nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) || GenStateIsNamedDeclaration(pGen->pIn, pGen->pEnd)) {
|
||||
SyToken *pDeclName = &pGen->pIn[2];
|
||||
if(pGen->pIn[1].nType & PH7_TK_OSB) {
|
||||
pDeclName += 2;
|
||||
}
|
||||
/* Hack to compile variable */
|
||||
pGen->pEnd->nType = PH7_TK_SEMI;
|
||||
if(pGen->pIn[3].nType != PH7_TK_SEMI) {
|
||||
if(pDeclName + 1 != pGen->pEnd) {
|
||||
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Improper key declaration");
|
||||
}
|
||||
/* Extract variable name */
|
||||
zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn[2].sData.zString, pGen->pIn[2].sData.nByte);
|
||||
zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pDeclName->sData.zString, pDeclName->sData.nByte);
|
||||
/* Compile variable declaration */
|
||||
PH7_CompileVar(&(*pGen));
|
||||
/* Record key name */
|
||||
@@ -2292,14 +2328,18 @@ static sxi32 PH7_CompileForeach(ph7_gen_state *pGen) {
|
||||
pTmp = pGen->pEnd;
|
||||
pGen->pEnd = pCur;
|
||||
nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) {
|
||||
if((nKey & (PH7_KEYWORD_AUTO | PH7_KEYWORD_TYPEDEF)) || GenStateIsNamedDeclaration(pGen->pIn, pGen->pEnd)) {
|
||||
SyToken *pDeclName = &pGen->pIn[2];
|
||||
if(pGen->pIn[1].nType & PH7_TK_OSB) {
|
||||
pDeclName += 2;
|
||||
}
|
||||
/* Hack to compile variable */
|
||||
pGen->pEnd->nType = PH7_TK_SEMI;
|
||||
if(pGen->pIn[3].nType != PH7_TK_SEMI) {
|
||||
if(pDeclName + 1 != pGen->pEnd) {
|
||||
PH7_GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Improper value declaration");
|
||||
}
|
||||
/* Extract variable name */
|
||||
zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pGen->pIn[2].sData.zString, pGen->pIn[2].sData.nByte);
|
||||
zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pDeclName->sData.zString, pDeclName->sData.nByte);
|
||||
/* Compile variable declaration */
|
||||
PH7_CompileVar(&(*pGen));
|
||||
/* Record value name */
|
||||
@@ -2564,7 +2604,9 @@ static sxi32 PH7_CompileVar(ph7_gen_state *pGen) {
|
||||
pGen->pIn++;
|
||||
nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
}
|
||||
if(nKey & PH7_KEYWORD_BOOL) {
|
||||
if(pGen->pIn->nType & PH7_TK_ID) {
|
||||
nType = GenStateNamedType(pGen, &pGen->pIn->sData);
|
||||
} else if(nKey & PH7_KEYWORD_BOOL) {
|
||||
nType = MEMOBJ_BOOL;
|
||||
} else if(nKey & PH7_KEYWORD_CALLBACK) {
|
||||
nType = MEMOBJ_CALL;
|
||||
@@ -3088,11 +3130,10 @@ static sxi32 PH7_GenStateCollectFuncArgs(ph7_vm_func *pFunc, ph7_gen_state *pGen
|
||||
/* Argument must be a class instance, record that*/
|
||||
zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
|
||||
if(zDup) {
|
||||
sArg.nType = SXU32_HIGH; /* 0xFFFFFFFF as sentinel */
|
||||
sArg.nType = GenStateNamedType(pGen, pName);
|
||||
SyStringInitFromBuf(&sArg.sClass, zDup, pName->nByte);
|
||||
} else {
|
||||
/* This should not happen, but fallback to object anyway */
|
||||
sArg.nType = MEMOBJ_OBJ;
|
||||
PH7_GenCompileError(pGen, E_ERROR, pIn->nLine, "PH7 engine is running out-of-memory");
|
||||
}
|
||||
}
|
||||
pIn++;
|
||||
@@ -3855,7 +3896,7 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) {
|
||||
/* End of interface body */
|
||||
break;
|
||||
}
|
||||
if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
|
||||
if((pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z'. Expecting method signature or constant declaration inside interface '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
@@ -3870,7 +3911,7 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) {
|
||||
if(nKwrd == PH7_KEYWORD_PUBLIC) {
|
||||
/* Advance the stream cursor */
|
||||
pGen->pIn++;
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Expecting method signature inside interface '%z'", pName);
|
||||
}
|
||||
@@ -3892,14 +3933,16 @@ static sxi32 PH7_CompileClassInterface(ph7_gen_state *pGen) {
|
||||
"Cannot declare a static method in an interface '%z'",
|
||||
pName);
|
||||
}
|
||||
if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0) {
|
||||
if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && (pGen->pIn->nType & PH7_TK_ID) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z', expecting data type for method signature inside interface '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
}
|
||||
sxu32 nType = 0;
|
||||
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & PH7_KEYWORD_BOOL) {
|
||||
if(pGen->pIn->nType & PH7_TK_ID) {
|
||||
nType = GenStateNamedType(pGen, &pGen->pIn->sData);
|
||||
} else if(nKey & PH7_KEYWORD_BOOL) {
|
||||
nType = MEMOBJ_BOOL;
|
||||
} else if(nKey & PH7_KEYWORD_CALLBACK) {
|
||||
nType = MEMOBJ_CALL;
|
||||
@@ -4089,7 +4132,7 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
|
||||
/* End of class body */
|
||||
break;
|
||||
}
|
||||
if((pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
|
||||
if((pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z'. Expecting attribute or method declaration inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
@@ -4102,7 +4145,7 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
|
||||
if(nKwrd == PH7_KEYWORD_PUBLIC || nKwrd == PH7_KEYWORD_PRIVATE || nKwrd == PH7_KEYWORD_PROTECTED) {
|
||||
iProtection = nKwrd;
|
||||
pGen->pIn++; /* Jump the visibility token */
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z'. Expecting attribute or method declaration inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
@@ -4124,7 +4167,7 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
|
||||
/* Static method or attribute,record that */
|
||||
iAttrflags |= PH7_CLASS_ATTR_STATIC;
|
||||
pGen->pIn++; /* Jump the static keyword */
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0) {
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z',Expecting attribute or method declaration inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
@@ -4156,8 +4199,8 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
|
||||
pGen->pIn++; /* Jump the static keyword */
|
||||
}
|
||||
nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ||
|
||||
((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) {
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0 ||
|
||||
((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && (pGen->pIn->nType & PH7_TK_ID) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z', expecting method declaration after 'virtual' keyword inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
@@ -4181,21 +4224,23 @@ static sxi32 PH7_GenStateCompileClass(ph7_gen_state *pGen, sxi32 iFlags) {
|
||||
pGen->pIn++; /* Jump the static keyword */
|
||||
}
|
||||
nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & PH7_TK_KEYWORD) == 0 ||
|
||||
((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) {
|
||||
if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) == 0 ||
|
||||
((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && (pGen->pIn->nType & PH7_TK_ID) == 0 && pGen->pIn[2].nType != PH7_TK_LPAREN)) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z', expecting method declaration after 'final' keyword inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
}
|
||||
}
|
||||
if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0) {
|
||||
if((nKwrd & PH7_KEYWORD_TYPEDEF) == 0 && (pGen->pIn->nType & PH7_TK_ID) == 0) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
|
||||
"Unexpected token '%z', expecting data type for attribute or method declaration inside class '%z'",
|
||||
&pGen->pIn->sData, pName);
|
||||
}
|
||||
sxu32 nType = 0;
|
||||
sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pGen->pIn->pUserData));
|
||||
if(nKey & PH7_KEYWORD_BOOL) {
|
||||
if(pGen->pIn->nType & PH7_TK_ID) {
|
||||
nType = GenStateNamedType(pGen, &pGen->pIn->sData);
|
||||
} else if(nKey & PH7_KEYWORD_BOOL) {
|
||||
nType = MEMOBJ_BOOL;
|
||||
} else if(nKey & PH7_KEYWORD_CALLBACK) {
|
||||
nType = MEMOBJ_CALL;
|
||||
@@ -5384,6 +5429,8 @@ static sxi32 PH7_GenStateCompileChunk(
|
||||
"Syntax error: Unexpected keyword '%z'",
|
||||
&pGen->pIn->sData);
|
||||
}
|
||||
} else if(GenStateIsNamedDeclaration(pGen->pIn, pGen->pEnd)) {
|
||||
xCons = PH7_CompileVar;
|
||||
} else if((pGen->pIn->nType & PH7_TK_ID) && (&pGen->pIn[1] < pGen->pEnd) && (pGen->pIn[1].nType & PH7_TK_COLON /*':'*/)) {
|
||||
/* Label found [i.e: Out: ],point to the routine responsible of compiling it */
|
||||
xCons = PH7_CompileLabel;
|
||||
|
||||
+43
-15
@@ -255,6 +255,34 @@ static sxi32 HashmapGrowBucket(ph7_hashmap *pMap) {
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
/* Both insertion and overwrite must enforce the owning map's leaf contract. */
|
||||
static sxi32 HashmapStoreNodeValue(ph7_hashmap *pMap, ph7_value *pValue, ph7_value *pDest) {
|
||||
if(pMap->nDeclaredType) {
|
||||
sxu32 nType = pMap->nDeclaredType | (pValue ? pValue->nType & MEMOBJ_HASHMAP : 0);
|
||||
ph7_value sNull;
|
||||
if((!pValue || MemObjIsNullReference(pValue)) && pDest->nDeclaredType) {
|
||||
nType = pDest->nDeclaredType;
|
||||
}
|
||||
if(!PH7_VmRefTypeCompatible(pDest, nType)) {
|
||||
PH7_VmThrowError(pMap->pVm, PH7_CTX_ERR, "Cannot insert a value of incompatible type to array");
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
if(!pValue) {
|
||||
PH7_MemObjInit(pMap->pVm, &sNull);
|
||||
pValue = &sNull;
|
||||
}
|
||||
pDest->nDeclaredType = nType;
|
||||
if(PH7_MemObjSafeStore(pValue, pDest) != SXRET_OK) {
|
||||
PH7_VmThrowError(pMap->pVm, PH7_CTX_ERR, "Cannot insert a value of incompatible type to array");
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
if(pValue) {
|
||||
return PH7_MemObjStore(pValue, pDest);
|
||||
}
|
||||
return PH7_MemObjRelease(pDest);
|
||||
}
|
||||
/*
|
||||
* Insert a 64-bit integer key and it's associated value (if any) in the given
|
||||
* hashmap.
|
||||
@@ -280,7 +308,12 @@ static sxi32 HashmapInsertIntKey(ph7_hashmap *pMap, sxi64 iKey, ph7_value *pValu
|
||||
pValue = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, nValue);
|
||||
}
|
||||
/* Duplicate the value */
|
||||
PH7_MemObjStore(pValue, pObj);
|
||||
rc = HashmapStoreNodeValue(pMap, pValue, pObj);
|
||||
if(rc != SXRET_OK) {
|
||||
return rc;
|
||||
}
|
||||
} else if(pMap->nDeclaredType) {
|
||||
HashmapStoreNodeValue(pMap, 0, pObj);
|
||||
}
|
||||
/* Hash the key */
|
||||
nHash = pMap->xIntHash(iKey);
|
||||
@@ -327,7 +360,12 @@ static sxi32 HashmapInsertBlobKey(ph7_hashmap *pMap, const void *pKey, sxu32 nKe
|
||||
pValue = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, nValue);
|
||||
}
|
||||
/* Duplicate the value */
|
||||
PH7_MemObjStore(pValue, pObj);
|
||||
rc = HashmapStoreNodeValue(pMap, pValue, pObj);
|
||||
if(rc != SXRET_OK) {
|
||||
return rc;
|
||||
}
|
||||
} else if(pMap->nDeclaredType) {
|
||||
HashmapStoreNodeValue(pMap, 0, pObj);
|
||||
}
|
||||
/* Hash the key */
|
||||
nHash = pMap->xBlobHash(pKey, nKeyLen);
|
||||
@@ -529,12 +567,7 @@ static sxi32 HashmapInsert(
|
||||
ph7_value *pElem;
|
||||
pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
|
||||
if(pElem) {
|
||||
if(pVal) {
|
||||
PH7_MemObjStore(pVal, pElem);
|
||||
} else {
|
||||
/* Release the entry */
|
||||
PH7_MemObjRelease(pElem);
|
||||
}
|
||||
return HashmapStoreNodeValue(pMap, pVal, pElem);
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
@@ -553,12 +586,7 @@ IntKey:
|
||||
ph7_value *pElem;
|
||||
pElem = (ph7_value *)SySetAt(&pMap->pVm->aMemObj, pNode->nValIdx);
|
||||
if(pElem) {
|
||||
if(pVal) {
|
||||
PH7_MemObjStore(pVal, pElem);
|
||||
} else {
|
||||
/* Release the entry */
|
||||
PH7_MemObjRelease(pElem);
|
||||
}
|
||||
return HashmapStoreNodeValue(pMap, pVal, pElem);
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
@@ -3155,7 +3183,7 @@ static int ph7_hashmap_splice(ph7_context *pCtx, int nArg, ph7_value **apArg) {
|
||||
/* Replace the old node */
|
||||
pOld = HashmapExtractNodeValue(pCur);
|
||||
if(pRvalue && pOld) {
|
||||
PH7_MemObjStore(pRvalue, pOld);
|
||||
HashmapStoreNodeValue(pSrc, pRvalue, pOld);
|
||||
}
|
||||
} else {
|
||||
/* Unlink the node from the source hashmap */
|
||||
|
||||
+80
-3
@@ -394,6 +394,40 @@ PH7_PRIVATE sxi32 PH7_CheckVarCompat(ph7_value *pObj, int nType) {
|
||||
}
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
/* Apply only to owned storage. Array copies are made before this walk, so a
|
||||
* wider destination never weakens the source array's leaf contracts. */
|
||||
PH7_PRIVATE void PH7_MemObjSetDeclaredType(ph7_value *pValue, sxu32 nType) {
|
||||
ph7_hashmap_node *pNode;
|
||||
ph7_hashmap *pMap;
|
||||
if(nType & MEMOBJ_NAMED) {
|
||||
PH7_VmTypeClass(pValue->pVm, nType, TRUE);
|
||||
} else {
|
||||
nType = 0;
|
||||
}
|
||||
pValue->nDeclaredType = nType;
|
||||
if(nType) {
|
||||
pValue->iFlags = MEMOBJ_VARIABLE;
|
||||
}
|
||||
if(nType && MemObjIsNullReference(pValue)) {
|
||||
pValue->nType = nType & MEMOBJ_TYPEMASK;
|
||||
pValue->x.pOther = 0;
|
||||
if(nType & MEMOBJ_HASHMAP) {
|
||||
pValue->x.pOther = PH7_NewHashmap(pValue->pVm, 0, 0);
|
||||
if(pValue->x.pOther == 0) {
|
||||
PH7_VmMemoryError(pValue->pVm);
|
||||
}
|
||||
}
|
||||
}
|
||||
if((pValue->nType & MEMOBJ_HASHMAP) == 0 || pValue->x.pOther == 0) {
|
||||
return;
|
||||
}
|
||||
pMap = (ph7_hashmap *)pValue->x.pOther;
|
||||
pMap->nDeclaredType = nType & ~MEMOBJ_HASHMAP;
|
||||
for(pNode = pMap->pFirst; pNode; pNode = pNode->pPrev) {
|
||||
ph7_value *pLeaf = (ph7_value *)SySetAt(&pValue->pVm->aMemObj, pNode->nValIdx);
|
||||
PH7_MemObjSetDeclaredType(pLeaf, nType ? pMap->nDeclaredType | (pLeaf->nType & MEMOBJ_HASHMAP) : 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Duplicate safely the contents of a ph7_value if source and
|
||||
* destination are of the compatible data types.
|
||||
@@ -403,6 +437,38 @@ PH7_PRIVATE sxi32 PH7_MemObjSafeStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||
ph7_vm *pVm = (pSrc->nType & MEMOBJ_HASHMAP) ? pDest->pVm : 0;
|
||||
sxu32 nDest = pVm ? pDest->nIdx : SXU32_HIGH;
|
||||
sxi32 bArena = pVm && SySetAt(&pVm->aMemObj, nDest) == pDest;
|
||||
if(pDest->nDeclaredType & MEMOBJ_NAMED) {
|
||||
sxu32 nDeclared = pDest->nDeclaredType;
|
||||
sxi32 iFlags = pDest->iFlags;
|
||||
ph7_value sValue, sPrevious;
|
||||
sxi32 rc;
|
||||
if(PH7_VmTypeCheck(pSrc, nDeclared) != SXRET_OK) {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
/* Retain both payloads until storage has its contract again. Releasing
|
||||
* the old object/map may invoke a destructor which writes through an
|
||||
* alias of this binding. The source can also be the destination itself. */
|
||||
PH7_MemObjInit(pDest->pVm, &sValue);
|
||||
PH7_MemObjInit(pDest->pVm, &sPrevious);
|
||||
PH7_MemObjLoad(pSrc, &sValue);
|
||||
PH7_MemObjLoad(pDest, &sPrevious);
|
||||
PH7_MemObjRelease(pDest);
|
||||
pDest->x.pOther = 0;
|
||||
if(MemObjIsNullReference(&sValue)) {
|
||||
rc = SXRET_OK;
|
||||
} else {
|
||||
rc = PH7_MemObjStore(&sValue, pDest);
|
||||
if(bArena) {
|
||||
pDest = (ph7_value *)SySetAt(&pVm->aMemObj, nDest);
|
||||
}
|
||||
pDest->nType = nDeclared & MEMOBJ_TYPEMASK;
|
||||
}
|
||||
PH7_MemObjSetDeclaredType(pDest, nDeclared);
|
||||
pDest->iFlags = iFlags;
|
||||
PH7_MemObjRelease(&sValue);
|
||||
PH7_MemObjRelease(&sPrevious);
|
||||
return rc;
|
||||
}
|
||||
if(pDest->nType == 0 || pDest->nType == MEMOBJ_NULL || pDest->nType == pSrc->nType) {
|
||||
sxu32 nType = pSrc->nType;
|
||||
PH7_MemObjStore(pSrc, pDest);
|
||||
@@ -465,6 +531,10 @@ PH7_PRIVATE sxi32 PH7_MemObjSafeStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||
} else {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
if(bArena) {
|
||||
pDest = (ph7_value *)SySetAt(&pVm->aMemObj, nDest);
|
||||
}
|
||||
PH7_MemObjSetDeclaredType(pDest, 0);
|
||||
return SXRET_OK;
|
||||
}
|
||||
/*
|
||||
@@ -957,7 +1027,10 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||
if((pDest->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) == MEMOBJ_OBJ) {
|
||||
pObj = (ph7_class_instance *)pDest->x.pOther;
|
||||
}
|
||||
SyMemcpy((const void *) & (*pSrc), &(*pDest), sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32)));
|
||||
pDest->x = pSrc->x;
|
||||
pDest->iFlags = pSrc->iFlags;
|
||||
pDest->nType = pSrc->nType;
|
||||
pDest->nDeclaredType = pSrc->nDeclaredType;
|
||||
rc = SXRET_OK;
|
||||
if(SyBlobLength(&pSrc->sBlob) > 0) {
|
||||
SyBlobReset(&pDest->sBlob);
|
||||
@@ -976,6 +1049,7 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||
* Stack destinations have no arena slot and must not be rebound. */
|
||||
pMap = PH7_NewHashmap(pVm, 0, 0);
|
||||
PH7_HashmapDup(pSrcMap, pMap);
|
||||
pMap->nDeclaredType = pSrcMap->nDeclaredType;
|
||||
if(bArena) {
|
||||
pDest = (ph7_value *)SySetAt(&pVm->aMemObj, nDest);
|
||||
}
|
||||
@@ -991,8 +1065,10 @@ PH7_PRIVATE sxi32 PH7_MemObjStore(ph7_value *pSrc, ph7_value *pDest) {
|
||||
* buffer contents,simply point to it.
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc, ph7_value *pDest) {
|
||||
SyMemcpy((const void *) & (*pSrc), &(*pDest),
|
||||
sizeof(ph7_value) - (sizeof(ph7_vm *) + sizeof(SyBlob) + sizeof(sxu32)));
|
||||
pDest->x = pSrc->x;
|
||||
pDest->iFlags = pSrc->iFlags;
|
||||
pDest->nType = pSrc->nType;
|
||||
pDest->nDeclaredType = pSrc->nDeclaredType;
|
||||
if(pSrc->x.pOther) {
|
||||
if(pSrc->nType & MEMOBJ_HASHMAP) {
|
||||
/* Increment reference count */
|
||||
@@ -1014,6 +1090,7 @@ PH7_PRIVATE sxi32 PH7_MemObjLoad(ph7_value *pSrc, ph7_value *pDest) {
|
||||
* Invalidate any prior representation of a given ph7_value.
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_MemObjRelease(ph7_value *pObj) {
|
||||
pObj->nDeclaredType = 0;
|
||||
if((pObj->nType & MEMOBJ_NULL) == 0) {
|
||||
if(pObj->x.pOther) {
|
||||
if(pObj->nType & MEMOBJ_HASHMAP) {
|
||||
|
||||
+17
-2
@@ -376,6 +376,9 @@ static sxi32 ExprAssembleClosure(ph7_gen_state *pGen, SyToken **ppCur, SyToken *
|
||||
/* Jump the data type keyword */
|
||||
nLine = pIn->nLine;
|
||||
pIn++;
|
||||
if(pIn < pEnd && &pIn[1] < pEnd && (pIn->nType & PH7_TK_OSB) && (pIn[1].nType & PH7_TK_CSB)) {
|
||||
pIn += 2;
|
||||
}
|
||||
if(pIn < pEnd && (pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD))) {
|
||||
pIn++;
|
||||
}
|
||||
@@ -479,9 +482,21 @@ static sxi32 ExprExtractNode(ph7_gen_state *pGen, ph7_expr_node **ppNode) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Missing closing braces '}'");
|
||||
}
|
||||
pNode->xCode = PH7_CompileArray;
|
||||
} else if(pCur->nType & PH7_TK_KEYWORD) {
|
||||
} else if(pCur->nType & (PH7_TK_KEYWORD | PH7_TK_ID)) {
|
||||
sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pCur->pUserData);
|
||||
if(pCur[1].nType & PH7_TK_LPAREN && (nKeyword & PH7_KEYWORD_TYPEDEF)) {
|
||||
SyToken *pArgs = &pCur[1], *pAfter = 0;
|
||||
sxi32 bClosure = FALSE;
|
||||
if(pArgs < pGen->pEnd && &pArgs[1] < pGen->pEnd && (pArgs->nType & PH7_TK_OSB) && (pArgs[1].nType & PH7_TK_CSB)) {
|
||||
pArgs += 2;
|
||||
}
|
||||
if(pArgs < pGen->pEnd && (pArgs->nType & PH7_TK_LPAREN)) {
|
||||
PH7_DelimitNestedTokens(&pArgs[1], pGen->pEnd, PH7_TK_LPAREN, PH7_TK_RPAREN, &pAfter);
|
||||
if(pAfter < pGen->pEnd && &pAfter[1] < pGen->pEnd) {
|
||||
bClosure = (pAfter[1].nType & PH7_TK_OCB) ||
|
||||
((pAfter[1].nType & PH7_TK_KEYWORD) && SX_PTR_TO_INT(pAfter[1].pUserData) == PH7_KEYWORD_USING);
|
||||
}
|
||||
}
|
||||
if(bClosure || ((pCur->nType & PH7_TK_KEYWORD) && pArgs < pGen->pEnd && (pArgs->nType & PH7_TK_LPAREN) && (nKeyword & PH7_KEYWORD_TYPEDEF))) {
|
||||
/* Anonymous function */
|
||||
if(&pCur[1] >= pGen->pEnd) {
|
||||
/* Assume a literal */
|
||||
|
||||
+305
-143
@@ -240,7 +240,7 @@ PH7_PRIVATE sxi32 PH7_VmSameSignature(ph7_vm_func *pFirst, ph7_vm_func *pSecond)
|
||||
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)) {
|
||||
SyStringCmp(&aFirst[i].sClass, &aSecond[i].sClass, SyMemcmp) != 0) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -602,89 +602,71 @@ static void VmLeaveFrame(ph7_vm *pVm) {
|
||||
}
|
||||
/* Forward declaration */
|
||||
static int VmInstanceOf(ph7_vm *pVm, ph7_class *pThis, ph7_class *pClass);
|
||||
PH7_PRIVATE ph7_class *PH7_VmTypeClass(ph7_vm *pVm, sxu32 nType, sxi32 bResolve) {
|
||||
SyString *pName;
|
||||
SyHashEntry *pEntry;
|
||||
ph7_class *pClass;
|
||||
if((nType & MEMOBJ_NAMED) == 0) {
|
||||
return 0;
|
||||
}
|
||||
pName = (SyString *)SySetAt(&pVm->aTypeNames, ((nType & MEMOBJ_NAMED) >> 12) - 1);
|
||||
if(pName == 0) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Invalid nominal type");
|
||||
return 0;
|
||||
}
|
||||
if(!bResolve) {
|
||||
pEntry = SyHashGet(&pVm->hClass, pName->zString, pName->nByte);
|
||||
return pEntry ? (ph7_class *)pEntry->pUserData : 0;
|
||||
}
|
||||
/* Copy the name descriptor: an autoloader may grow aTypeNames. */
|
||||
{
|
||||
SyString sName = *pName;
|
||||
pClass = PH7_VmExtractClass(pVm, sName.zString, sName.nByte, FALSE);
|
||||
if(pClass == 0) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Unknown data type '%z'", &sName);
|
||||
}
|
||||
}
|
||||
return pClass;
|
||||
}
|
||||
/* Read-only validation of all leaves; never cast or manufacture an instance. */
|
||||
PH7_PRIVATE sxi32 PH7_VmTypeCheck(ph7_value *pValue, sxu32 nType) {
|
||||
ph7_class *pClass = PH7_VmTypeClass(pValue->pVm, nType, FALSE);
|
||||
ph7_class_instance *pInstance;
|
||||
if(pClass == 0) {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
if(MemObjIsNullReference(pValue)) {
|
||||
return SXRET_OK;
|
||||
}
|
||||
if(nType & MEMOBJ_HASHMAP) {
|
||||
ph7_hashmap_node *pNode;
|
||||
ph7_hashmap *pMap;
|
||||
if((pValue->nType & MEMOBJ_HASHMAP) == 0) {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
pMap = (ph7_hashmap *)pValue->x.pOther;
|
||||
for(pNode = pMap ? pMap->pFirst : 0; pNode; pNode = pNode->pPrev) {
|
||||
ph7_value *pLeaf = (ph7_value *)SySetAt(&pValue->pVm->aMemObj, pNode->nValIdx);
|
||||
sxu32 nLeafType = (nType & ~MEMOBJ_HASHMAP) | (pLeaf->nType & MEMOBJ_HASHMAP);
|
||||
if(PH7_VmTypeCheck(pLeaf, nLeafType) != SXRET_OK) {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
if((pValue->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) != MEMOBJ_OBJ) {
|
||||
return SXERR_NOMATCH;
|
||||
}
|
||||
pInstance = (ph7_class_instance *)pValue->x.pOther;
|
||||
return !pInstance || VmInstanceOf(pValue->pVm, pInstance->pClass, pClass) ? SXRET_OK : SXERR_NOMATCH;
|
||||
}
|
||||
/*
|
||||
* Ranks for general parameter types. Class and interface specificity uses
|
||||
* the shortest inheritance distance instead, which is always lower than
|
||||
* these values.
|
||||
* Ranks for general parameter types. Nominal specificity is compared by
|
||||
* the subtype relation, never by inheritance distance or MRO position.
|
||||
*/
|
||||
#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 */
|
||||
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, sxi32 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(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 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;
|
||||
sxi32 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
|
||||
@@ -693,7 +675,7 @@ static sxi32 VmOverloadClassDistance(ph7_vm *pVm, ph7_class *pClass, ph7_class *
|
||||
static sxi32 VmOverloadNullRank(ph7_vm *pVm, ph7_vm_func_arg *pFormal) {
|
||||
sxu32 nFormalType = pFormal->nType;
|
||||
SyHashEntry *pEntry;
|
||||
if(nFormalType == SXU32_HIGH) {
|
||||
if(nFormalType & MEMOBJ_NAMED) {
|
||||
/* Class or interface type hint */
|
||||
pEntry = SyHashGet(&pVm->hClass, (const void *)pFormal->sClass.zString, pFormal->sClass.nByte);
|
||||
return pEntry != 0 ? 1 : 4;
|
||||
@@ -826,19 +808,29 @@ static sxi32 VmOverloadArrayRank(ph7_vm *pVm, ph7_value *pArg, sxu32 nFormalElem
|
||||
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;
|
||||
sxi32 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) {
|
||||
if(nFormalType & MEMOBJ_NAMED) {
|
||||
/* Class or interface type hint */
|
||||
pHint = PH7_VmTypeClass(pVm, nFormalType, FALSE);
|
||||
if(pHint == 0) {
|
||||
return VM_OVERLOAD_RANK_UNKNOWN;
|
||||
}
|
||||
if(MemObjIsNullReference(pArg)) {
|
||||
return VmOverloadNullRank(pVm, pFormal);
|
||||
}
|
||||
if(nFormalType & MEMOBJ_HASHMAP) {
|
||||
if((nArgType & MEMOBJ_HASHMAP) == 0 || PH7_VmTypeCheck(pArg, nFormalType) != SXRET_OK) {
|
||||
return -1;
|
||||
}
|
||||
return pArg->nDeclaredType == nFormalType ? 0 : 1;
|
||||
}
|
||||
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;
|
||||
@@ -846,18 +838,10 @@ static sxi32 VmOverloadArgRank(ph7_vm *pVm, ph7_value *pArg, ph7_vm_func_arg *pF
|
||||
/* 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) {
|
||||
if(!VmInstanceOf(pVm, pInstance->pClass, pHint)) {
|
||||
return -1;
|
||||
}
|
||||
return nDistance;
|
||||
return pInstance->pClass == pHint ? 0 : 1;
|
||||
}
|
||||
if(nFormalType & MEMOBJ_MIXED) {
|
||||
if(((nFormalType & MEMOBJ_HASHMAP) != 0) != ((nArgType & MEMOBJ_HASHMAP) != 0)) {
|
||||
@@ -909,6 +893,13 @@ static sxi32 VmOverloadRankArgs(ph7_vm *pVm, ph7_vm_func *pFunc, ph7_value *aArg
|
||||
if((aFormal[i].iFlags & VM_FUNC_ARG_BY_REF) && aArg[i].nIdx == SXU32_HIGH) {
|
||||
return FALSE;
|
||||
}
|
||||
if(aFormal[i].iFlags & VM_FUNC_ARG_BY_REF) {
|
||||
ph7_value *pStorage = (ph7_value *)SySetAt(&pVm->aMemObj, aArg[i].nIdx);
|
||||
sxu32 nDeclared = pStorage->nDeclaredType ? pStorage->nDeclaredType : pStorage->nType;
|
||||
if(((nDeclared | aFormal[i].nType) & MEMOBJ_NAMED) && nDeclared != aFormal[i].nType) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
nRank = VmOverloadArgRank(&(*pVm), &aArg[i], &aFormal[i]);
|
||||
if(nRank < 0) {
|
||||
return FALSE;
|
||||
@@ -917,18 +908,79 @@ static sxi32 VmOverloadRankArgs(ph7_vm *pVm, ph7_vm_func *pFunc, ph7_value *aArg
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
/* A partial order, deliberately not graph distance or C3 precedence. */
|
||||
static sxi32 VmOverloadArgCompare(ph7_vm *pVm, ph7_value *pArg, ph7_vm_func_arg *pFirst, ph7_vm_func_arg *pSecond) {
|
||||
sxi32 nFirst, nSecond;
|
||||
ph7_class *pA, *pB;
|
||||
if((pArg->nType & MEMOBJ_HASHMAP) && (pFirst->nType & MEMOBJ_HASHMAP) && (pSecond->nType & MEMOBJ_HASHMAP)) {
|
||||
ph7_hashmap *pMap = (ph7_hashmap *)pArg->x.pOther;
|
||||
ph7_hashmap_node *pNode;
|
||||
sxi32 nOrder = 0;
|
||||
sxu32 nContainer = pArg->nDeclaredType ? pArg->nDeclaredType : pArg->nType;
|
||||
if(nContainer == pFirst->nType && (pFirst->nType & MEMOBJ_MIXED) == 0 && pFirst->nType != pSecond->nType) {
|
||||
return -1;
|
||||
}
|
||||
if(nContainer == pSecond->nType && (pSecond->nType & MEMOBJ_MIXED) == 0 && pFirst->nType != pSecond->nType) {
|
||||
return 1;
|
||||
}
|
||||
if(!pMap || pMap->nEntry == 0) {
|
||||
nFirst = VmOverloadArgRank(pVm, pArg, pFirst);
|
||||
nSecond = VmOverloadArgRank(pVm, pArg, pSecond);
|
||||
return nFirst < nSecond ? -1 : nFirst > nSecond ? 1 : 0;
|
||||
}
|
||||
for(pNode = pMap ? pMap->pFirst : 0; pNode; pNode = pNode->pPrev) {
|
||||
ph7_value *pLeaf = (ph7_value *)SySetAt(&pVm->aMemObj, pNode->nValIdx);
|
||||
ph7_vm_func_arg sFirst = *pFirst, sSecond = *pSecond;
|
||||
sxi32 nCur;
|
||||
sFirst.nType = (sFirst.nType & ~MEMOBJ_HASHMAP) | (pLeaf->nType & MEMOBJ_HASHMAP);
|
||||
sSecond.nType = (sSecond.nType & ~MEMOBJ_HASHMAP) | (pLeaf->nType & MEMOBJ_HASHMAP);
|
||||
nCur = VmOverloadArgCompare(pVm, pLeaf, &sFirst, &sSecond);
|
||||
if(nCur == 2 || (nCur && nOrder && nCur != nOrder)) {
|
||||
return 2;
|
||||
}
|
||||
if(nCur) {
|
||||
nOrder = nCur;
|
||||
}
|
||||
}
|
||||
return nOrder;
|
||||
}
|
||||
if((pFirst->nType & MEMOBJ_NAMED) && (pSecond->nType & MEMOBJ_NAMED) &&
|
||||
(pArg->nType & MEMOBJ_OBJ) && pArg->x.pOther) {
|
||||
pA = PH7_VmTypeClass(pVm, pFirst->nType, FALSE);
|
||||
pB = PH7_VmTypeClass(pVm, pSecond->nType, FALSE);
|
||||
if(pA == pB) {
|
||||
return 0;
|
||||
}
|
||||
if(VmInstanceOf(pVm, pA, pB)) {
|
||||
return -1;
|
||||
}
|
||||
if(VmInstanceOf(pVm, pB, pA)) {
|
||||
return 1;
|
||||
}
|
||||
/* Incomparability is not equality: treating it as a tie can create
|
||||
* dominance cycles across arguments. */
|
||||
return 2;
|
||||
}
|
||||
nFirst = VmOverloadArgRank(pVm, pArg, pFirst);
|
||||
nSecond = VmOverloadArgRank(pVm, pArg, pSecond);
|
||||
return nFirst < nSecond ? -1 : nFirst > nSecond ? 1 : 0;
|
||||
}
|
||||
/*
|
||||
* Return TRUE if the first candidate is a strictly better overload than the
|
||||
* second one. When all argument ranks are equal, prefer fewer omitted defaults.
|
||||
*/
|
||||
static sxi32 VmOverloadDominates(const sxi32 *aFirst, const sxi32 *aSecond, sxu32 nArg, sxu32 nFirstDefaults, sxu32 nSecondDefaults) {
|
||||
static sxi32 VmOverloadDominates(ph7_vm *pVm, ph7_value *aArg, ph7_vm_func *pFirst, ph7_vm_func *pSecond, sxu32 nArg, sxu32 nFirstDefaults, sxu32 nSecondDefaults) {
|
||||
sxu32 i;
|
||||
sxi32 bBetter = 0;
|
||||
ph7_vm_func_arg *aFirst = (ph7_vm_func_arg *)SySetBasePtr(&pFirst->aArgs);
|
||||
ph7_vm_func_arg *aSecond = (ph7_vm_func_arg *)SySetBasePtr(&pSecond->aArgs);
|
||||
for(i = 0 ; i < nArg ; i++) {
|
||||
if(aFirst[i] > aSecond[i]) {
|
||||
sxi32 nOrder = VmOverloadArgCompare(pVm, &aArg[i], &aFirst[i], &aSecond[i]);
|
||||
if(nOrder > 0) {
|
||||
/* Worse or incomparable on any argument prevents dominance. */
|
||||
return FALSE;
|
||||
}
|
||||
if(aFirst[i] < aSecond[i]) {
|
||||
if(nOrder < 0) {
|
||||
bBetter = 1;
|
||||
}
|
||||
}
|
||||
@@ -1222,6 +1274,34 @@ static ph7_vm_func *VmOverload(
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Invalid number of arguments passed to function/method '%z()'", &pList->sName);
|
||||
}
|
||||
apSet = (ph7_vm_func **)SySetBasePtr(&sSet);
|
||||
/* Resolve each distinct annotation before the read-only ranking pass.
|
||||
* Autoloading is name resolution, not candidate evaluation. */
|
||||
{
|
||||
SySet sTypes;
|
||||
sxu32 i, n, k, nType;
|
||||
SySetInit(&sTypes, &pVm->sAllocator, sizeof(sxu32));
|
||||
for(i = 0; i < nCandidates; i++) {
|
||||
ph7_vm_func_arg *aFormal = (ph7_vm_func_arg *)SySetBasePtr(&apSet[i]->aArgs);
|
||||
for(n = 0; n < SySetUsed(&apSet[i]->aArgs); n++) {
|
||||
nType = aFormal[n].nType & MEMOBJ_NAMED;
|
||||
if(!nType) {
|
||||
continue;
|
||||
}
|
||||
for(k = 0; k < SySetUsed(&sTypes); k++) {
|
||||
if(*(sxu32 *)SySetAt(&sTypes, k) == nType) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(k == SySetUsed(&sTypes) && SySetPut(&sTypes, &nType) != SXRET_OK) {
|
||||
PH7_VmMemoryError(pVm);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i = 0; i < SySetUsed(&sTypes); i++) {
|
||||
PH7_VmTypeClass(pVm, *(sxu32 *)SySetAt(&sTypes, i), TRUE);
|
||||
}
|
||||
SySetRelease(&sTypes);
|
||||
}
|
||||
/* Rank every argument against every candidate. The last column holds
|
||||
* whether the candidate is compatible with all passed arguments. */
|
||||
nCols = nPassed + 1;
|
||||
@@ -1259,7 +1339,7 @@ static ph7_vm_func *VmOverload(
|
||||
if(k == j || aRank[k * nCols + nPassed] == 0) {
|
||||
continue;
|
||||
}
|
||||
if(VmOverloadDominates(&aRank[k * nCols], &aRank[j * nCols], nPassed,
|
||||
if(VmOverloadDominates(pVm, aArg, apSet[k], apSet[j], nPassed,
|
||||
SySetUsed(&apSet[k]->aArgs) - nPassed,
|
||||
SySetUsed(&apSet[j]->aArgs) - nPassed)) {
|
||||
bDominated = 1;
|
||||
@@ -1276,6 +1356,14 @@ static ph7_vm_func *VmOverload(
|
||||
iTarget = j;
|
||||
}
|
||||
}
|
||||
if(iTarget == SXU32_HIGH) {
|
||||
/* Never index the sentinel, even if a comparison defect leaves every
|
||||
* compatible candidate dominated. */
|
||||
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||
SySetRelease(&sSet);
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Ambiguous call to overloaded function/method '%z()'", &pList->sName);
|
||||
return 0;
|
||||
}
|
||||
pLink = apSet[iTarget];
|
||||
SyMemBackendFree(&pVm->sAllocator, aRank);
|
||||
SySetRelease(&sSet);
|
||||
@@ -1303,18 +1391,24 @@ static sxi32 VmMountUserClass(
|
||||
if((pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) && pAttr->nIdx == SXU32_HIGH) {
|
||||
/* Static/constant storage belongs to the declaring declaration and
|
||||
* is mounted only once, even when the class is mounted again. */
|
||||
ph7_value *pMemObj, *pResult;
|
||||
ph7_value *pMemObj, sResult;
|
||||
sxu32 nObjIdx;
|
||||
/* Reserve a memory object for this constant/static attribute */
|
||||
PH7_VmTypeClass(pVm, pAttr->nType, TRUE);
|
||||
pMemObj = PH7_ReserveMemObj(&(*pVm));
|
||||
pResult = PH7_ReserveMemObj(&(*pVm));
|
||||
if(pMemObj == 0 || pResult == 0) {
|
||||
PH7_MemObjInit(pVm, &sResult);
|
||||
if(pMemObj == 0) {
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
MemObjSetType(pMemObj, pAttr->nType);
|
||||
nObjIdx = pMemObj->nIdx;
|
||||
MemObjSetType(pMemObj, (pAttr->nType & MEMOBJ_TYPEMASK));
|
||||
PH7_MemObjSetDeclaredType(pMemObj, pAttr->nType);
|
||||
if(SySetUsed(&pAttr->aByteCode) > 0) {
|
||||
/* Initialize attribute default value (any complex expression) */
|
||||
VmLocalExec(&(*pVm), &pAttr->aByteCode, pResult);
|
||||
rc = PH7_MemObjSafeStore(pResult, pMemObj);
|
||||
VmLocalExec(&(*pVm), &pAttr->aByteCode, &sResult);
|
||||
pMemObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
rc = PH7_MemObjSafeStore(&sResult, pMemObj);
|
||||
pMemObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
if(rc != SXRET_OK) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName);
|
||||
}
|
||||
@@ -1325,9 +1419,10 @@ static sxi32 VmMountUserClass(
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
pMemObj->x.pOther = pMap;
|
||||
PH7_MemObjSetDeclaredType(pMemObj, pAttr->nType);
|
||||
}
|
||||
/* Free up memory */
|
||||
PH7_MemObjRelease(pResult);
|
||||
PH7_MemObjRelease(&sResult);
|
||||
/* Record attribute index */
|
||||
pAttr->nIdx = pMemObj->nIdx;
|
||||
/* Install static attribute in the reference table */
|
||||
@@ -1379,17 +1474,22 @@ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(
|
||||
pVmAttr->pAttr = pAttr;
|
||||
if((pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) == 0) {
|
||||
ph7_value *pMemObj, sValue;
|
||||
sxu32 nObjIdx;
|
||||
/* Reserve a memory object for this attribute */
|
||||
PH7_VmTypeClass(pVm, pAttr->nType, TRUE);
|
||||
pMemObj = PH7_ReserveMemObj(&(*pVm));
|
||||
if(pMemObj == 0) {
|
||||
SyMemBackendPoolFree(&pVm->sAllocator, pVmAttr);
|
||||
return SXERR_MEM;
|
||||
}
|
||||
MemObjSetType(pMemObj, pAttr->nType);
|
||||
nObjIdx = pMemObj->nIdx;
|
||||
MemObjSetType(pMemObj, (pAttr->nType & MEMOBJ_TYPEMASK));
|
||||
PH7_MemObjSetDeclaredType(pMemObj, pAttr->nType);
|
||||
PH7_MemObjInit(pVm, &sValue);
|
||||
if(SySetUsed(&pAttr->aByteCode) > 0) {
|
||||
/* Initialize attribute default value (any complex expression) */
|
||||
rc = VmLocalExec(&(*pVm), &pAttr->aByteCode, &sValue);
|
||||
pMemObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
if(rc != SXRET_OK) {
|
||||
if(rc == PH7_EXCEPTION && pResult) {
|
||||
PH7_MemObjStore(&sValue, pResult);
|
||||
@@ -1400,6 +1500,7 @@ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(
|
||||
return rc;
|
||||
}
|
||||
rc = PH7_MemObjSafeStore(&sValue, pMemObj);
|
||||
pMemObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
if(rc != SXRET_OK) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Cannot assign a value of incompatible type to variable '%z::$%z'", &pClass->sName, &pAttr->sName);
|
||||
}
|
||||
@@ -1410,6 +1511,7 @@ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(
|
||||
PH7_VmMemoryError(&(*pVm));
|
||||
}
|
||||
pMemObj->x.pOther = pMap;
|
||||
PH7_MemObjSetDeclaredType(pMemObj, pAttr->nType);
|
||||
}
|
||||
/* Free up memory */
|
||||
PH7_MemObjRelease(&sValue);
|
||||
@@ -1441,6 +1543,39 @@ PH7_PRIVATE sxi32 PH7_VmCreateClassInstanceFrame(
|
||||
}
|
||||
/* Forward declaration */
|
||||
static VmRefObj *VmRefObjExtract(ph7_vm *pVm, sxu32 nObjIdx);
|
||||
/* A recursive array may change node shape, but not through another alias
|
||||
* while that node is bound to a typed reference or retained by a capture. */
|
||||
PH7_PRIVATE sxi32 PH7_VmRefTypeCompatible(ph7_value *pStorage, sxu32 nType) {
|
||||
ph7_vm *pVm = pStorage->pVm;
|
||||
VmRefObj *pRef;
|
||||
VmFrame *pFrame;
|
||||
if(!pStorage->nDeclaredType || pStorage->nDeclaredType == nType) {
|
||||
return TRUE;
|
||||
}
|
||||
pRef = VmRefObjExtract(pVm, pStorage->nIdx);
|
||||
if(pRef && pRef->nCapture) {
|
||||
return FALSE;
|
||||
}
|
||||
for(pFrame = pVm->pFrame; pFrame; pFrame = pFrame->pParent) {
|
||||
ph7_vm_func *pFunc;
|
||||
ph7_vm_func_arg *aFormal;
|
||||
sxu32 i;
|
||||
if((pFrame->iFlags & VM_FRAME_ACTIVE) == 0) {
|
||||
continue;
|
||||
}
|
||||
pFunc = (ph7_vm_func *)pFrame->pUserData;
|
||||
aFormal = (ph7_vm_func_arg *)SySetBasePtr(&pFunc->aArgs);
|
||||
for(i = 0; i < SySetUsed(&pFunc->aArgs); i++) {
|
||||
if((aFormal[i].iFlags & VM_FUNC_ARG_BY_REF) && (aFormal[i].nType & MEMOBJ_NAMED) && aFormal[i].nType != nType) {
|
||||
SyHashEntry *pBinding = SyHashGet(&pFrame->hVar, aFormal[i].sName.zString, aFormal[i].sName.nByte);
|
||||
if(pBinding && (sxu32)SX_PTR_TO_INT(pBinding->pUserData) == pStorage->nIdx) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
static sxi32 VmRefObjUnlink(ph7_vm *pVm, VmRefObj *pRef);
|
||||
/* Runtime closures own snapshots or pin indexed bindings, never pointers into
|
||||
* aMemObj (which may move). Templates do not own captured storage. */
|
||||
@@ -1673,6 +1808,7 @@ PH7_PRIVATE sxi32 PH7_VmInit(
|
||||
SySetInit(&pVm->aFreeObj, &pVm->sAllocator, sizeof(VmSlot));
|
||||
SySetInit(&pVm->aSelf, &pVm->sAllocator, sizeof(ph7_class *));
|
||||
SySetInit(&pVm->aAutoLoad, &pVm->sAllocator, sizeof(VmAutoLoadCB));
|
||||
SySetInit(&pVm->aTypeNames, &pVm->sAllocator, sizeof(SyString));
|
||||
SySetInit(&pVm->aShutdown, &pVm->sAllocator, sizeof(VmShutdownCB));
|
||||
SySetInit(&pVm->aException, &pVm->sAllocator, sizeof(ph7_exception *));
|
||||
/* Configuration containers */
|
||||
@@ -2722,6 +2858,9 @@ static sxi32 VmByteCodeExec(
|
||||
) {
|
||||
VmInstr *pInstr;
|
||||
ph7_value *pTos;
|
||||
/* The member load immediately before STORE supplies declaration metadata,
|
||||
* which survives operand release and memory object relocation. */
|
||||
ph7_class_attr *pStoreAttr = 0;
|
||||
SySet aArg;
|
||||
sxi32 pc;
|
||||
sxi32 rc;
|
||||
@@ -3106,11 +3245,11 @@ Resume:
|
||||
iRes = 1;
|
||||
}
|
||||
} else {
|
||||
if(pNos->nType & MEMOBJ_OBJ) {
|
||||
if((pNos->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) == MEMOBJ_OBJ) {
|
||||
ph7_class_instance *pThis = (ph7_class_instance *)pNos->x.pOther;
|
||||
ph7_class *pClass = 0;
|
||||
/* Extract the target class */
|
||||
if(pTos->nType & MEMOBJ_OBJ && pTos->x.pOther) {
|
||||
if((pTos->nType & (MEMOBJ_OBJ | MEMOBJ_HASHMAP)) == MEMOBJ_OBJ && pTos->x.pOther) {
|
||||
/* Instance already loaded */
|
||||
pClass = ((ph7_class_instance *)pTos->x.pOther)->pClass;
|
||||
} else if(pTos->nType & MEMOBJ_STRING && SyBlobLength(&pTos->sBlob) > 0) {
|
||||
@@ -3157,12 +3296,17 @@ Resume:
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Redeclaration of ‘$%z’ variable", &pStatic->sName);
|
||||
}
|
||||
if(pStatic->nIdx == SXU32_HIGH) {
|
||||
ph7_value *pObj = VmReserveMemObj(pVm, &pStatic->nIdx);
|
||||
ph7_value *pObj;
|
||||
ph7_value sValue;
|
||||
PH7_VmTypeClass(pVm, pStatic->iFlags, TRUE);
|
||||
pObj = VmReserveMemObj(pVm, &pStatic->nIdx);
|
||||
if(pObj == 0) {
|
||||
PH7_VmMemoryError(pVm);
|
||||
}
|
||||
MemObjSetType(pObj, pStatic->iFlags);
|
||||
PH7_MemObjInit(pVm, pObj);
|
||||
pObj->nIdx = pStatic->nIdx;
|
||||
MemObjSetType(pObj, (pStatic->iFlags & MEMOBJ_TYPEMASK));
|
||||
PH7_MemObjSetDeclaredType(pObj, pStatic->iFlags);
|
||||
if(SySetUsed(&pStatic->aByteCode) > 0) {
|
||||
PH7_MemObjInit(pVm, &sValue);
|
||||
rc = VmLocalExec(pVm, &pStatic->aByteCode, &sValue);
|
||||
@@ -3189,6 +3333,7 @@ Resume:
|
||||
if(pObj->x.pOther == 0) {
|
||||
PH7_VmMemoryError(pVm);
|
||||
}
|
||||
PH7_MemObjSetDeclaredType(pObj, pStatic->iFlags);
|
||||
}
|
||||
pObj->nIdx = pStatic->nIdx;
|
||||
}
|
||||
@@ -3245,6 +3390,7 @@ Resume:
|
||||
/* Reserve a room for the target object */
|
||||
pTos++;
|
||||
/* Create a new variable */
|
||||
PH7_VmTypeClass(pVm, pInstr->iP2, TRUE);
|
||||
pObj = VmCreateMemObj(&(*pVm), &sName, FALSE);
|
||||
if(!pObj) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
@@ -3262,7 +3408,8 @@ Resume:
|
||||
}
|
||||
pObj->x.pOther = pMap;
|
||||
}
|
||||
MemObjSetType(pObj, pInstr->iP2);
|
||||
MemObjSetType(pObj, (pInstr->iP2 & MEMOBJ_TYPEMASK));
|
||||
PH7_MemObjSetDeclaredType(pObj, pInstr->iP2);
|
||||
}
|
||||
pTos->nIdx = SXU32_HIGH; /* Mark as constant */
|
||||
}
|
||||
@@ -3608,8 +3755,12 @@ Resume:
|
||||
/* Perform the store operation */
|
||||
rc = PH7_MemObjSafeStore(pTos, pObj);
|
||||
if(rc != SXRET_OK) {
|
||||
char zAttr[256];
|
||||
SyString sAttr;
|
||||
PH7_ClassAttrDiagnosticName(pStoreAttr->pClass, pStoreAttr->iFlags,
|
||||
&pStoreAttr->sName, zAttr, sizeof(zAttr), &sAttr);
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
"Cannot assign a value of incompatible type to variable '$%z'", &sName);
|
||||
"Cannot assign a value of incompatible type to class attribute '%z'", &sAttr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3667,6 +3818,9 @@ Resume:
|
||||
break;
|
||||
}
|
||||
/* Phase#1: Load the array */
|
||||
if((pObj->nDeclaredType & MEMOBJ_NAMED) && (pObj->nDeclaredType & MEMOBJ_HASHMAP) == 0) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Subscripted value is neither array nor string");
|
||||
}
|
||||
if(pObj->nType & MEMOBJ_STRING) {
|
||||
VmPopOperand(&pTos, 1);
|
||||
if((pTos->nType & MEMOBJ_STRING) == 0) {
|
||||
@@ -5266,6 +5420,7 @@ Resume:
|
||||
if(pThis->iRef > 1) {
|
||||
/* Load attribute index */
|
||||
pTos->nIdx = pObjAttr->nIdx;
|
||||
pStoreAttr = pObjAttr->pAttr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5391,6 +5546,7 @@ Resume:
|
||||
if(pAttr->iFlags & PH7_CLASS_ATTR_STATIC) {
|
||||
/* Load index number */
|
||||
pTos->nIdx = pAttr->nIdx;
|
||||
pStoreAttr = pAttr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -5711,6 +5867,8 @@ Resume:
|
||||
if(pVmFunc->iFlags & VM_FUNC_CLOSURE) {
|
||||
pSelf = (ph7_class *)pVmFunc->pUserData;
|
||||
}
|
||||
/* Result annotations do not participate in overload selection. */
|
||||
PH7_VmTypeClass(pVm, pVmFunc->nType, TRUE);
|
||||
/* Extract the formal argument set */
|
||||
aFormalArg = (ph7_vm_func_arg *)SySetBasePtr(&pVmFunc->aArgs);
|
||||
/* Create a new VM frame */
|
||||
@@ -5745,28 +5903,18 @@ Resume:
|
||||
if(n < SySetUsed(&pVmFunc->aArgs)) {
|
||||
/* Make sure the given arguments are of the correct type */
|
||||
if(aFormalArg[n].nType > 0) {
|
||||
if(aFormalArg[n].nType == SXU32_HIGH) {
|
||||
if(aFormalArg[n].nType & MEMOBJ_NAMED) {
|
||||
/* Argument must be a class instance [i.e: object] */
|
||||
SyString *pName = &aFormalArg[n].sClass;
|
||||
ph7_class *pClass;
|
||||
/* Try to extract the desired class */
|
||||
pClass = PH7_VmExtractClass(&(*pVm), pName->zString, pName->nByte, TRUE);
|
||||
pClass = PH7_VmTypeClass(pVm, aFormalArg[n].nType, TRUE);
|
||||
if(pClass) {
|
||||
if((pArg->nType & MEMOBJ_OBJ) == 0) {
|
||||
if((pArg->nType & MEMOBJ_NULL) == 0) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
if(PH7_VmTypeCheck(pArg, aFormalArg[n].nType) != SXRET_OK) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR,
|
||||
"Argument %u passed to function '%z()' must be an object of type '%z'",
|
||||
n + 1, &pVmFunc->sName, pName);
|
||||
}
|
||||
} else {
|
||||
ph7_class_instance *pThis = (ph7_class_instance *)pArg->x.pOther;
|
||||
/* Make sure the object is an instance of the given class */
|
||||
if(pThis == 0 || !VmInstanceOf(pVm, pThis->pClass, pClass)) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
"Argument %u passed to function '%z()' must be an object of type '%z'",
|
||||
n+1, &pVmFunc->sName, pName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ph7_value *pTmp = PH7_ReserveMemObj(&(*pVm));
|
||||
@@ -5818,9 +5966,19 @@ Resume:
|
||||
pObj = VmExtractMemObj(&(*pVm), &sName, TRUE);
|
||||
}
|
||||
if(pObj) {
|
||||
sxu32 nObjIdx = pObj->nIdx;
|
||||
if(n < SySetUsed(&pVmFunc->aArgs) && (aFormalArg[n].nType & MEMOBJ_NAMED)) {
|
||||
pObj->nDeclaredType = aFormalArg[n].nType;
|
||||
if(PH7_MemObjSafeStore(pArg, pObj) != SXRET_OK) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Argument %u of '%z()' does not match the data type", n + 1, &pVmFunc->sName);
|
||||
}
|
||||
} else {
|
||||
PH7_MemObjStore(pArg, pObj);
|
||||
pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
PH7_MemObjSetDeclaredType(pObj, 0);
|
||||
}
|
||||
/* Insert argument index */
|
||||
sArg.nIdx = pObj->nIdx;
|
||||
sArg.nIdx = nObjIdx;
|
||||
sArg.pUserData = 0;
|
||||
SySetPut(&pFrame->sArg, (const void *)&sArg);
|
||||
}
|
||||
@@ -5892,8 +6050,15 @@ Resume:
|
||||
if(SySetUsed(&aFormalArg[n].aByteCode) > 0) {
|
||||
pObj = VmCreateMemObj(&(*pVm), &aFormalArg[n].sName, FALSE);
|
||||
if(pObj) {
|
||||
ph7_value sDefault;
|
||||
sxu32 nObjIdx = pObj->nIdx;
|
||||
PH7_MemObjInit(pVm, &sDefault);
|
||||
/* Evaluate the default value and extract it's result */
|
||||
rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, pObj);
|
||||
rc = VmLocalExec(&(*pVm), &aFormalArg[n].aByteCode, &sDefault);
|
||||
pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
PH7_MemObjStore(&sDefault, pObj);
|
||||
PH7_MemObjRelease(&sDefault);
|
||||
pObj = (ph7_value *)SySetAt(&pVm->aMemObj, nObjIdx);
|
||||
if(rc == PH7_ABORT) {
|
||||
goto Abort;
|
||||
}
|
||||
@@ -5907,28 +6072,19 @@ Resume:
|
||||
nTarget = pc + 1;
|
||||
goto Unwind;
|
||||
}
|
||||
if(aFormalArg[n].nType == SXU32_HIGH) {
|
||||
if(aFormalArg[n].nType & MEMOBJ_NAMED) {
|
||||
/* Argument must be a class instance [i.e: object] */
|
||||
SyString *pName = &aFormalArg[n].sClass;
|
||||
ph7_class *pClass;
|
||||
/* Try to extract the desired class */
|
||||
pClass = PH7_VmExtractClass(&(*pVm), pName->zString, pName->nByte, TRUE);
|
||||
pClass = PH7_VmTypeClass(pVm, aFormalArg[n].nType, TRUE);
|
||||
if(pClass) {
|
||||
if((pObj->nType & MEMOBJ_OBJ) == 0) {
|
||||
if((pObj->nType & MEMOBJ_NULL) == 0) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
if(PH7_VmTypeCheck(pObj, aFormalArg[n].nType) != SXRET_OK) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR,
|
||||
"Default value for argument %u of '%z()' must be an object of type '%z'",
|
||||
n + 1, &pVmFunc->sName, pName);
|
||||
}
|
||||
} else {
|
||||
ph7_class_instance *pThis = (ph7_class_instance *)pObj->x.pOther;
|
||||
/* Make sure the object is an instance of the given class */
|
||||
if(pThis == 0 || !VmInstanceOf(pVm, pThis->pClass, pClass)) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
"Default value for argument %u of '%z()' must be an object of type '%z'",
|
||||
n+1, &pVmFunc->sName, pName);
|
||||
}
|
||||
}
|
||||
PH7_MemObjSetDeclaredType(pObj, aFormalArg[n].nType);
|
||||
}
|
||||
} else {
|
||||
ph7_value *pTmp = PH7_ReserveMemObj(&(*pVm));
|
||||
@@ -6173,7 +6329,13 @@ Unwind:
|
||||
if(bReturnValue && (pEntryFrame->iFlags & VM_FRAME_ACTIVE) &&
|
||||
(iExecFlags & VM_EXEC_HANDLER) == 0) {
|
||||
ph7_vm_func *pFunc = (ph7_vm_func *)pEntryFrame->pUserData;
|
||||
if(pFunc->nType && (pFunc->nType & MEMOBJ_MIXED) == 0) {
|
||||
if(pFunc->nType & MEMOBJ_NAMED) {
|
||||
PH7_VmTypeClass(pVm, pFunc->nType, TRUE);
|
||||
if(PH7_VmTypeCheck(&sTransfer, pFunc->nType) != SXRET_OK) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Incompatible type when returning data by closure/method");
|
||||
}
|
||||
PH7_MemObjSetDeclaredType(&sTransfer, pFunc->nType);
|
||||
} else if(pFunc->nType && (pFunc->nType & MEMOBJ_MIXED) == 0) {
|
||||
if(pFunc->nType & MEMOBJ_VOID) {
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Return with a value in closure/method returning void");
|
||||
} else if(pFunc->nType != sTransfer.nType) {
|
||||
|
||||
@@ -618,6 +618,7 @@ struct ph7_value {
|
||||
} x;
|
||||
sxi32 iFlags; /* Control flags (see below) */
|
||||
sxu32 nType; /* Variable data type */
|
||||
sxu32 nDeclaredType; /* Immutable nominal storage contract, independent of value */
|
||||
ph7_vm *pVm; /* Virtual machine that own this instance */
|
||||
SyBlob sBlob; /* String values */
|
||||
sxu32 nIdx; /* Index number of this entry in the global object allocator */
|
||||
@@ -640,6 +641,11 @@ struct ph7_value {
|
||||
#define MEMOBJ_MIXED 0x0200 /* Memory value is mixed */
|
||||
#define MEMOBJ_HASHMAP 0x0400 /* Memory value is a hashmap aka 'array' in the PHP jargon */
|
||||
#define MEMOBJ_NULL 0x0800 /* Memory value is NULL */
|
||||
/* VM-local interned nominal names occupy bits above the value type flags. */
|
||||
#define MEMOBJ_NAMED 0x7ffff000
|
||||
#define MEMOBJ_TYPEMASK 0x00000fff
|
||||
/* NULL and an empty object reference carry no dynamic nominal identity. */
|
||||
#define MemObjIsNullReference(OBJ) (((OBJ)->nType & MEMOBJ_HASHMAP) == 0 && (((OBJ)->nType & MEMOBJ_NULL) || (((OBJ)->nType & MEMOBJ_OBJ) && (OBJ)->x.pOther == 0)))
|
||||
/* Mask of all known types */
|
||||
#define MEMOBJ_ALL (MEMOBJ_STRING|MEMOBJ_INT|MEMOBJ_REAL|MEMOBJ_BOOL|MEMOBJ_NULL|MEMOBJ_HASHMAP|MEMOBJ_OBJ|MEMOBJ_RES|MEMOBJ_CALL|MEMOBJ_CHAR|MEMOBJ_VOID)
|
||||
/* Scalar variables
|
||||
@@ -773,6 +779,7 @@ struct ph7_hashmap_node {
|
||||
* by an instance of the following structure.
|
||||
*/
|
||||
struct ph7_hashmap {
|
||||
sxu32 nDeclaredType; /* Nominal leaf contract, or zero */
|
||||
ph7_vm *pVm; /* VM that own this instance */
|
||||
ph7_hashmap_node **apBucket; /* Hash bucket */
|
||||
ph7_hashmap_node *pFirst; /* First inserted entry */
|
||||
@@ -1213,6 +1220,7 @@ struct ph7_vm {
|
||||
ph7_value *aOps; /* Operand stack */
|
||||
SySet aFreeObj; /* Stack of free memory objects */
|
||||
SyHash hClass; /* Compiled classes container */
|
||||
SySet aTypeNames; /* VM-owned interned nominal annotation names */
|
||||
SyHash hConstant; /* Host-application and user defined constants container */
|
||||
SyHash hHostFunction; /* Host-application installable functions */
|
||||
SyHash hFunction; /* Compiled functions */
|
||||
@@ -1798,6 +1806,10 @@ PH7_PRIVATE void PH7_RegisterHashmapFunctions(ph7_vm *pVm);
|
||||
PH7_PRIVATE sxi32 PH7_HashmapDump(SyBlob *pOut, ph7_hashmap *pMap, int ShowType, int nTab, int nDepth);
|
||||
PH7_PRIVATE sxi32 PH7_HashmapWalk(ph7_hashmap *pMap, int (*xWalk)(ph7_value *, ph7_value *, void *), void *pUserData);
|
||||
PH7_PRIVATE sxi32 PH7_HashmapCast(ph7_value *pObj, sxu32 nType);
|
||||
PH7_PRIVATE ph7_class *PH7_VmTypeClass(ph7_vm *pVm, sxu32 nType, sxi32 bResolve);
|
||||
PH7_PRIVATE sxi32 PH7_VmTypeCheck(ph7_value *pValue, sxu32 nType);
|
||||
PH7_PRIVATE sxi32 PH7_VmRefTypeCompatible(ph7_value *pStorage, sxu32 nType);
|
||||
PH7_PRIVATE void PH7_MemObjSetDeclaredType(ph7_value *pValue, sxu32 nType);
|
||||
PH7_PRIVATE int PH7_HashmapValuesToSet(ph7_hashmap *pMap, SySet *pOut);
|
||||
/* builtin.c function prototypes */
|
||||
PH7_PRIVATE sxi32 PH7_InputFormat(int (*xConsumer)(ph7_context *, const char *, int, void *),
|
||||
|
||||
Reference in New Issue
Block a user