Preserve class attribute identity and validate inheritance conflicts across C3 MRO
Build / AerScript (push) Successful in 42s
Build / AerScript (push) Successful in 42s
This commit is contained in:
+2
-2
@@ -3275,7 +3275,7 @@ loop:
|
||||
return SXERR_ABORT;
|
||||
}
|
||||
/* All done,install the constant */
|
||||
rc = PH7_ClassInstallAttr(pClass, pCons);
|
||||
rc = PH7_ClassInstallAttr(pGen, pClass, pCons);
|
||||
if(rc != SXRET_OK) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 is running out-of-memory");
|
||||
}
|
||||
@@ -3367,7 +3367,7 @@ loop:
|
||||
PH7_VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
|
||||
}
|
||||
/* All done,install the attribute */
|
||||
rc = PH7_ClassInstallAttr(pClass, pAttr);
|
||||
rc = PH7_ClassInstallAttr(pGen, pClass, pAttr);
|
||||
if(rc != SXRET_OK) {
|
||||
PH7_GenCompileError(pGen, E_ERROR, nLine, "PH7 is running out-of-memory");
|
||||
}
|
||||
|
||||
+128
-26
@@ -163,32 +163,67 @@ PH7_PRIVATE ph7_class_method *PH7_ClassExtractMethod(ph7_class *pClass, const ch
|
||||
return (ph7_class_method *)pEntry->pUserData;
|
||||
}
|
||||
/*
|
||||
* Check if the given name is a class attribute.
|
||||
* Check if the given name is an attribute declared directly by the given class.
|
||||
* Names are compared exactly (length and bytes), inherited copies are ignored.
|
||||
* Return the desired attribute [i.e: ph7_class_attr instance] on success.NULL otherwise.
|
||||
*/
|
||||
PH7_PRIVATE ph7_class_attr *PH7_ClassExtractAttribute(ph7_class *pClass, const char *zName, sxu32 nByte) {
|
||||
SyHashEntry *pEntry;
|
||||
ph7_class_attr *pAttr;
|
||||
SyString sName;
|
||||
/* Attribute names match exactly, including their length */
|
||||
SyStringInitFromBuf(&sName, zName, nByte);
|
||||
/* Perform a hash lookup */
|
||||
SyHashResetLoopCursor(&pClass->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0) {
|
||||
/* Point to the desired method */
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(pAttr->pClass == pClass && SyStrncmp(pAttr->sName.zString, zName, nByte) == 0) {
|
||||
if(pAttr->pClass == pClass && SyStringCmp(&pAttr->sName, &sName, SyMemcmp) == 0) {
|
||||
return pAttr;
|
||||
}
|
||||
}
|
||||
/* No such entry */
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Format an attribute reference for diagnostics. Instance fields use
|
||||
* 'Class->name', static fields 'Class::$name' and constants 'Class::NAME'.
|
||||
*/
|
||||
PH7_PRIVATE void PH7_ClassAttrDiagnosticName(ph7_class *pClass, sxi32 iFlags, const SyString *pName,
|
||||
char *zBuf, sxu32 nBuf, SyString *pOut) {
|
||||
const char *zSeparator;
|
||||
if(iFlags & PH7_CLASS_ATTR_CONSTANT) {
|
||||
zSeparator = "::";
|
||||
} else if(iFlags & PH7_CLASS_ATTR_STATIC) {
|
||||
zSeparator = "::$";
|
||||
} else {
|
||||
zSeparator = "->";
|
||||
}
|
||||
nBuf = SyBufferFormat(zBuf, nBuf, "%z%s%z", &pClass->sName, zSeparator, pName);
|
||||
SyStringInitFromBuf(pOut, zBuf, nBuf);
|
||||
}
|
||||
/*
|
||||
* Install a class attribute in the corresponding container.
|
||||
* Return SXRET_OK on success. Any other return value indicates failure.
|
||||
* All class attributes (instance fields, static fields and constants) share
|
||||
* a single name space. Duplicates inside one class are rejected at compile
|
||||
* time; conflicts across the inheritance chain are checked at class init.
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass, ph7_class_attr *pAttr) {
|
||||
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_gen_state *pGen, ph7_class *pClass, ph7_class_attr *pAttr) {
|
||||
SyHashEntry *pEntry;
|
||||
SyString *pName = &pAttr->sName;
|
||||
sxi32 rc;
|
||||
pAttr->pClass = pClass;
|
||||
SyHashResetLoopCursor(&pClass->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0) {
|
||||
ph7_class_attr *pOther = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(pOther->pClass == pClass && SyStringCmp(&pOther->sName, pName, SyMemcmp) == 0) {
|
||||
char zAttr[256];
|
||||
SyString sAttr;
|
||||
PH7_ClassAttrDiagnosticName(pClass, pAttr->iFlags, &pAttr->sName, zAttr, sizeof(zAttr), &sAttr);
|
||||
PH7_GenCompileError(pGen, E_ERROR, pAttr->nLine, "Duplicate class attribute '%z' in class '%z'", &sAttr, &pClass->sName);
|
||||
}
|
||||
}
|
||||
rc = SyHashInsert(&pClass->hAttr, (const void *)pName->zString, pName->nByte, pAttr);
|
||||
return rc;
|
||||
}
|
||||
@@ -203,6 +238,71 @@ PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pM
|
||||
rc = SyHashInsert(&pClass->hMethod, (const void *)pName->zString, pName->nByte, pMeth);
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
* Return TRUE if the first class is a descendant of the second one according
|
||||
* to its already built C3 linearization.
|
||||
*/
|
||||
static sxi32 ClassIsDescendant(ph7_class *pClass, ph7_class *pAncestor) {
|
||||
sxu32 i;
|
||||
for(i = 1; i < SySetUsed(&pClass->aMro); i++) {
|
||||
if(*(ph7_class **)SySetAt(&pClass->aMro, i) == pAncestor) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
/*
|
||||
* Validate attribute declarations across the C3 linearization. Two visible
|
||||
* declarations with the same name conflict even when they are identical.
|
||||
* Private declarations of different classes are independent, even along an
|
||||
* inheritance chain, but a private declaration cannot narrow a visible member
|
||||
* inherited from a base.
|
||||
*/
|
||||
static sxi32 ClassValidateAttributes(ph7_vm *pVm, ph7_class *pClass) {
|
||||
SyHashEntry *pEntry, *pOtherEntry;
|
||||
sxu32 i, j;
|
||||
for(i = 0; i < SySetUsed(&pClass->aMro); i++) {
|
||||
ph7_class *pOwner = *(ph7_class **)SySetAt(&pClass->aMro, i);
|
||||
SyHashResetLoopCursor(&pOwner->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pOwner->hAttr)) != 0) {
|
||||
ph7_class_attr *pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(pAttr->pClass != pOwner) {
|
||||
/* Inherited copy, not a declaration of this class */
|
||||
continue;
|
||||
}
|
||||
for(j = i + 1; j < SySetUsed(&pClass->aMro); j++) {
|
||||
ph7_class *pOtherOwner = *(ph7_class **)SySetAt(&pClass->aMro, j);
|
||||
SyHashResetLoopCursor(&pOtherOwner->hAttr);
|
||||
while((pOtherEntry = SyHashGetNextEntry(&pOtherOwner->hAttr)) != 0) {
|
||||
ph7_class_attr *pOther = (ph7_class_attr *)pOtherEntry->pUserData;
|
||||
if(pOther->pClass != pOtherOwner || SyStringCmp(&pAttr->sName, &pOther->sName, SyMemcmp) != 0) {
|
||||
continue;
|
||||
}
|
||||
if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE && pOther->iProtection == PH7_CLASS_PROT_PRIVATE) {
|
||||
/* Private declarations of different classes are independent */
|
||||
continue;
|
||||
}
|
||||
if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE) {
|
||||
if(!ClassIsDescendant(pOwner, pOtherOwner)) {
|
||||
/* A private member of an unrelated base cannot hide a visible one */
|
||||
continue;
|
||||
}
|
||||
} else if(pOther->iProtection == PH7_CLASS_PROT_PRIVATE && !ClassIsDescendant(pOtherOwner, pOwner)) {
|
||||
/* An inherited private member does not hide a new visible declaration */
|
||||
continue;
|
||||
}
|
||||
char zAttr[256], zOther[256];
|
||||
SyString sAttr, sOther;
|
||||
PH7_ClassAttrDiagnosticName(pAttr->pClass, pAttr->iFlags, &pAttr->sName, zAttr, sizeof(zAttr), &sAttr);
|
||||
PH7_ClassAttrDiagnosticName(pOther->pClass, pOther->iFlags, &pOther->sName, zOther, sizeof(zOther), &sOther);
|
||||
PH7_VmThrowError(pVm, PH7_CTX_ERR, "Class attribute '%z' conflicts with '%z' in class '%z'",
|
||||
&sAttr, &sOther, &pClass->sName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return SXRET_OK;
|
||||
}
|
||||
/*
|
||||
* Validate actual declarations, not inherited hash entries. Forward-declared
|
||||
* bases need not have executed CLASS_INIT, and virtual methods are not mounted
|
||||
@@ -423,7 +523,13 @@ done:
|
||||
SySetRelease(&sMro);
|
||||
pClass->iMroState = 0;
|
||||
}
|
||||
return rc == SXRET_OK ? ClassValidateMethods(pVm, pClass) : rc;
|
||||
if(rc == SXRET_OK) {
|
||||
rc = ClassValidateAttributes(pVm, pClass);
|
||||
}
|
||||
if(rc == SXRET_OK) {
|
||||
rc = ClassValidateMethods(pVm, pClass);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
* Perform an inheritance operation.
|
||||
@@ -472,20 +578,31 @@ PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBas
|
||||
SyHashEntry *pEntry;
|
||||
SyString *pName;
|
||||
sxi32 rc;
|
||||
SXUNUSED(pVm);
|
||||
/* Install in the derived hashtable */
|
||||
rc = SyHashInsert(&pSub->hDerived, (const void *)SyStringData(&pBase->sName), SyStringLength(&pBase->sName), pBase);
|
||||
if(rc != SXRET_OK) {
|
||||
return rc;
|
||||
}
|
||||
/* Copy all attributes from the base class */
|
||||
/* Copy all attributes from the base class. A diamond shares the same
|
||||
* declaration pointer, which must yield a single inherited entry. */
|
||||
SyHashResetLoopCursor(&pBase->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pBase->hAttr)) != 0) {
|
||||
/* Check if attributes are not being redeclared in the subclass and emit WARNING */
|
||||
sxi32 bInstalled;
|
||||
SyHashEntry *pSubEntry;
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
pName = &pAttr->sName;
|
||||
if((pEntry = SyHashGet(&pSub->hAttr, (const void *)pName->zString, pName->nByte)) != 0) {
|
||||
ph7_class_attr *pSubAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
PH7_VmThrowError(pVm, PH7_CTX_WARNING, "Attribute '%z::$%z' hides inherited member '%z::$%z'", &pSubAttr->pClass->sName, pName, &pBase->sName, pName);
|
||||
bInstalled = FALSE;
|
||||
SyHashResetLoopCursor(&pSub->hAttr);
|
||||
while((pSubEntry = SyHashGetNextEntry(&pSub->hAttr)) != 0) {
|
||||
if(pSubEntry->pUserData == pAttr) {
|
||||
bInstalled = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(bInstalled) {
|
||||
/* Already inherited through another base */
|
||||
continue;
|
||||
}
|
||||
/* Install the attribute */
|
||||
rc = SyHashInsert(&pSub->hAttr, (const void *)pName->zString, pName->nByte, pAttr);
|
||||
@@ -586,25 +703,10 @@ PH7_PRIVATE sxi32 PH7_ClassInterfaceInherit(ph7_class *pSub, ph7_class *pBase) {
|
||||
*/
|
||||
PH7_PRIVATE sxi32 PH7_ClassImplement(ph7_vm *pVm, ph7_class *pMain, ph7_class *pInterface) {
|
||||
ph7_class_method *pMeth;
|
||||
ph7_class_attr *pAttr;
|
||||
SyHashEntry *pEntry;
|
||||
SyString *pName;
|
||||
sxi32 rc;
|
||||
/* First off,copy all constants declared inside the interface */
|
||||
SyHashResetLoopCursor(&pInterface->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pInterface->hAttr)) != 0) {
|
||||
/* Point to the constant declaration */
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
pName = &pAttr->sName;
|
||||
/* Make sure the attribute is not redeclared in the main class */
|
||||
if(SyHashGet(&pMain->hAttr, pName->zString, pName->nByte) == 0) {
|
||||
/* Install the attribute */
|
||||
rc = SyHashInsert(&pMain->hAttr, pName->zString, pName->nByte, pAttr);
|
||||
if(rc != SXRET_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Interface constants are not inherited by the implementing class.
|
||||
* They remain reachable through the interface name only. */
|
||||
SyHashResetLoopCursor(&pInterface->hMethod);
|
||||
while((pEntry = SyHashGetNextEntry(&pInterface->hMethod)) != 0) {
|
||||
pMeth = (ph7_class_method *)pEntry->pUserData;
|
||||
|
||||
+145
-44
@@ -829,6 +829,116 @@ static ph7_class_method *VmMethodSeed(ph7_vm *pVm, ph7_class *pLookup, SyString
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Resolve a class attribute by its exact name. Classes walk their C3
|
||||
* linearization, interfaces follow their pBase chain. A private declaration of
|
||||
* the current lexical scope wins, while any other inaccessible candidate is
|
||||
* remembered but never hides a later visible member. When nothing is
|
||||
* accessible, pDenied tells the caller to report an access error rather than
|
||||
* an undefined attribute; ppDeniedAttr receives the first rejected declaration
|
||||
* and pAmbiguous tells whether more than one declaration was rejected.
|
||||
*/
|
||||
static ph7_class_attr *VmResolveClassAttr(ph7_vm *pVm, ph7_class *pClass, const char *zName, sxu32 nByte,
|
||||
ph7_class *pScope, sxi32 bParent, sxi32 *pDenied, ph7_class_attr **ppDeniedAttr, sxi32 *pAmbiguous) {
|
||||
ph7_class_attr *pAttr, *pDeniedAttr = 0;
|
||||
sxi32 bAmbiguous = FALSE;
|
||||
SyString sName;
|
||||
sxu32 i;
|
||||
*pDenied = FALSE;
|
||||
*ppDeniedAttr = 0;
|
||||
*pAmbiguous = FALSE;
|
||||
SyStringInitFromBuf(&sName, zName, nByte);
|
||||
if(pClass->iFlags & PH7_CLASS_INTERFACE) {
|
||||
ph7_class *pIt = pClass;
|
||||
ph7_class_attr *pResult = 0;
|
||||
SySet aVisited;
|
||||
SySetInit(&aVisited, &pVm->sAllocator, sizeof(ph7_class *));
|
||||
while(pIt) {
|
||||
SyHashEntry *pEntry;
|
||||
sxu32 n;
|
||||
for(n = 0; n < SySetUsed(&aVisited); n++) {
|
||||
if(*(ph7_class **)SySetAt(&aVisited, n) == pIt) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(n < SySetUsed(&aVisited)) {
|
||||
/* Interface inheritance is cyclic; all reachable members were scanned */
|
||||
break;
|
||||
}
|
||||
if(SySetPut(&aVisited, &pIt) != SXRET_OK) {
|
||||
SySetRelease(&aVisited);
|
||||
PH7_VmMemoryError(pVm);
|
||||
return 0;
|
||||
}
|
||||
SyHashResetLoopCursor(&pIt->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pIt->hAttr)) != 0) {
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(SyStringCmp(&pAttr->sName, &sName, SyMemcmp) == 0) {
|
||||
pResult = pAttr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(pResult) {
|
||||
break;
|
||||
}
|
||||
pIt = pIt->pBase;
|
||||
}
|
||||
SySetRelease(&aVisited);
|
||||
return pResult;
|
||||
}
|
||||
if(!bParent && SySetUsed(&pClass->aMro) == 0) {
|
||||
/* Static initializers run before CLASS_INIT; only direct declarations
|
||||
* are available at that point. */
|
||||
pAttr = PH7_ClassExtractAttribute(pClass, zName, nByte);
|
||||
if(pAttr) {
|
||||
if(VmClassMemberAccess(pVm, pAttr->pClass, pAttr->iProtection)) {
|
||||
return pAttr;
|
||||
}
|
||||
*pDenied = TRUE;
|
||||
*ppDeniedAttr = pAttr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if(pScope && !bParent && VmInstanceOf(pVm, pClass, pScope)) {
|
||||
pAttr = PH7_ClassExtractAttribute(pScope, zName, nByte);
|
||||
if(pAttr && pAttr->iProtection == PH7_CLASS_PROT_PRIVATE) {
|
||||
/* Methods use their own class declaration */
|
||||
return pAttr;
|
||||
}
|
||||
}
|
||||
for(i = bParent ? 1 : 0; i < SySetUsed(&pClass->aMro); i++) {
|
||||
ph7_class *pOwner = *(ph7_class **)SySetAt(&pClass->aMro, i);
|
||||
pAttr = PH7_ClassExtractAttribute(pOwner, zName, nByte);
|
||||
if(pAttr == 0) {
|
||||
continue;
|
||||
}
|
||||
if(pAttr->iProtection == PH7_CLASS_PROT_PRIVATE) {
|
||||
if(pScope == pOwner) {
|
||||
return pAttr;
|
||||
}
|
||||
/* Inaccessible private candidate, keep looking */
|
||||
*pDenied = TRUE;
|
||||
if(pDeniedAttr == 0) {
|
||||
pDeniedAttr = pAttr;
|
||||
} else if(pDeniedAttr != pAttr) {
|
||||
bAmbiguous = TRUE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(VmClassMemberAccess(pVm, pAttr->pClass, pAttr->iProtection)) {
|
||||
return pAttr;
|
||||
}
|
||||
*pDenied = TRUE;
|
||||
if(pDeniedAttr == 0) {
|
||||
pDeniedAttr = pAttr;
|
||||
} else if(pDeniedAttr != pAttr) {
|
||||
bAmbiguous = TRUE;
|
||||
}
|
||||
}
|
||||
*ppDeniedAttr = pDeniedAttr;
|
||||
*pAmbiguous = bAmbiguous;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Select the appropriate VM function for the current call context.
|
||||
* This is the implementation of the powerful 'function overloading' feature
|
||||
@@ -1027,7 +1137,9 @@ static sxi32 VmMountUserClass(
|
||||
while((pEntry = SyHashGetNextEntry(&pClass->hAttr)) != 0) {
|
||||
/* Extract the current attribute */
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(pAttr->iFlags & (PH7_CLASS_ATTR_CONSTANT | PH7_CLASS_ATTR_STATIC)) {
|
||||
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;
|
||||
/* Reserve a memory object for this constant/static attribute */
|
||||
pMemObj = PH7_ReserveMemObj(&(*pVm));
|
||||
@@ -4578,7 +4690,7 @@ static sxi32 VmByteCodeExec(
|
||||
if(!pNos->x.pOther) {
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Call to non-instantiated object '$%z'", &sName);
|
||||
}
|
||||
ph7_class *pClass, *pDerived, *pScope;
|
||||
ph7_class *pClass, *pScope;
|
||||
/* Class already instantiated */
|
||||
pThis = (ph7_class_instance *)pNos->x.pOther;
|
||||
/* Point to the instantiated class */
|
||||
@@ -4628,56 +4740,34 @@ static sxi32 VmByteCodeExec(
|
||||
} else {
|
||||
/* Attribute access */
|
||||
VmClassAttr *pObjAttr = 0;
|
||||
ph7_class_attr *pAttr = 0;
|
||||
ph7_class_attr *pAttr = 0, *pDeniedAttr = 0;
|
||||
SyHashEntry *pEntry;
|
||||
sxi32 bDenied = FALSE, bAmbiguous = FALSE;
|
||||
if(sName.nByte > 0) {
|
||||
/* Extract the target attribute */
|
||||
if(pNos->iFlags != MEMOBJ_PARENTOBJ) {
|
||||
/* Private fields belong to their declaring class, even on a derived object. */
|
||||
if(pScope && VmInstanceOf(pVm, pClass, pScope)) {
|
||||
pAttr = PH7_ClassExtractAttribute(pScope, sName.zString, sName.nByte);
|
||||
if(pAttr && pAttr->iProtection != PH7_CLASS_PROT_PRIVATE) {
|
||||
pAttr = 0;
|
||||
}
|
||||
}
|
||||
if(pAttr == 0) {
|
||||
pAttr = PH7_ClassExtractAttribute(pClass, sName.zString, sName.nByte);
|
||||
}
|
||||
}
|
||||
if(pAttr == 0) {
|
||||
/* Browse hashtable from the beginning */
|
||||
SyHashResetLoopCursor(&pClass->hDerived);
|
||||
/* Search for appropriate class member */
|
||||
SyHashEntry *pEntry;
|
||||
while((pEntry = SyHashGetNextEntry(&pClass->hDerived)) != 0) {
|
||||
pDerived = (ph7_class *) pEntry->pUserData;
|
||||
SyHashResetLoopCursor(&pDerived->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pDerived->hAttr)) != 0) {
|
||||
pAttr = (ph7_class_attr *)pEntry->pUserData;
|
||||
if(SyStrncmp(pAttr->sName.zString, sName.zString, sName.nByte) == 0) {
|
||||
break;
|
||||
}
|
||||
pAttr = 0;
|
||||
}
|
||||
if(pAttr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Resolve the declaration first, then match the object slot by identity */
|
||||
pAttr = VmResolveClassAttr(pVm, pClass, sName.zString, sName.nByte, pScope,
|
||||
pNos->iFlags == MEMOBJ_PARENTOBJ, &bDenied, &pDeniedAttr, &bAmbiguous);
|
||||
if(pAttr) {
|
||||
SyHashResetLoopCursor(&pThis->hAttr);
|
||||
while((pEntry = SyHashGetNextEntry(&pThis->hAttr)) != 0) {
|
||||
pObjAttr = (VmClassAttr *)pEntry->pUserData;
|
||||
if(pObjAttr->pAttr->pClass == pAttr->pClass) {
|
||||
if(SyStrncmp(pObjAttr->pAttr->sName.zString, sName.zString, sName.nByte) == 0) {
|
||||
break;
|
||||
}
|
||||
if(pObjAttr->pAttr == pAttr) {
|
||||
break;
|
||||
}
|
||||
pObjAttr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(pObjAttr == 0) {
|
||||
if(bDenied) {
|
||||
char zAttr[256];
|
||||
SyString sAttr;
|
||||
ph7_class *pOwner = (pDeniedAttr && !bAmbiguous) ? pDeniedAttr->pClass : pClass;
|
||||
PH7_ClassAttrDiagnosticName(pOwner, pDeniedAttr ? pDeniedAttr->iFlags : 0,
|
||||
&sName, zAttr, sizeof(zAttr), &sAttr);
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR,
|
||||
"Class attribute '%z' is inaccessible due to its protection level", &sAttr);
|
||||
}
|
||||
/* No such attribute,load null */
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Undefined class attribute '%z->%z'",
|
||||
&pClass->sName, &sName);
|
||||
@@ -4791,12 +4881,23 @@ static sxi32 VmByteCodeExec(
|
||||
pTos->nIdx = SXU32_HIGH;
|
||||
} else {
|
||||
/* Attribute access */
|
||||
ph7_class_attr *pAttr = 0;
|
||||
/* Extract the target attribute */
|
||||
ph7_class_attr *pAttr = 0, *pDeniedAttr = 0;
|
||||
ph7_class *pScope = VmMethodClass(pVm);
|
||||
sxi32 bDenied = FALSE, bAmbiguous = FALSE;
|
||||
/* Resolve inherited declarations through the C3 MRO */
|
||||
if(sName.nByte > 0) {
|
||||
pAttr = PH7_ClassExtractAttribute(pClass, sName.zString, sName.nByte);
|
||||
pAttr = VmResolveClassAttr(pVm, pClass, sName.zString, sName.nByte, pScope, FALSE,
|
||||
&bDenied, &pDeniedAttr, &bAmbiguous);
|
||||
}
|
||||
if(pAttr == 0) {
|
||||
if(bDenied) {
|
||||
char zAttr[256];
|
||||
SyString sAttr;
|
||||
ph7_class *pOwner = (pDeniedAttr && !bAmbiguous) ? pDeniedAttr->pClass : pClass;
|
||||
PH7_ClassAttrDiagnosticName(pOwner, pDeniedAttr ? pDeniedAttr->iFlags : 0,
|
||||
&sName, zAttr, sizeof(zAttr), &sAttr);
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Class attribute '%z' is inaccessible due to its protection level", &sAttr);
|
||||
}
|
||||
/* No such attribute,load null */
|
||||
PH7_VmThrowError(&(*pVm), PH7_CTX_ERR, "Undefined class attribute '%z::$%z'",
|
||||
&pClass->sName, &sName);
|
||||
@@ -4816,7 +4917,7 @@ static sxi32 VmByteCodeExec(
|
||||
} else {
|
||||
ph7_value *pValue;
|
||||
/* Check if the access to the attribute is allowed */
|
||||
if(VmClassMemberAccess(&(*pVm), pClass, pAttr->iProtection)) {
|
||||
if(VmClassMemberAccess(&(*pVm), pAttr->pClass, pAttr->iProtection)) {
|
||||
/* Load the desired attribute */
|
||||
pValue = (ph7_value *)SySetAt(&pVm->aMemObj, pAttr->nIdx);
|
||||
if(pValue) {
|
||||
|
||||
+3
-1
@@ -1768,7 +1768,9 @@ PH7_PRIVATE ph7_class_method *PH7_NewClassMethod(ph7_vm *pVm, ph7_class *pClass,
|
||||
sxi32 iProtection, sxi32 iFlags, sxi32 iFuncFlags);
|
||||
PH7_PRIVATE ph7_class_method *PH7_ClassExtractMethod(ph7_class *pClass, const char *zName, sxu32 nByte);
|
||||
PH7_PRIVATE ph7_class_attr *PH7_ClassExtractAttribute(ph7_class *pClass, const char *zName, sxu32 nByte);
|
||||
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_class *pClass, ph7_class_attr *pAttr);
|
||||
PH7_PRIVATE void PH7_ClassAttrDiagnosticName(ph7_class *pClass, sxi32 iFlags, const SyString *pName,
|
||||
char *zBuf, sxu32 nBuf, SyString *pOut);
|
||||
PH7_PRIVATE sxi32 PH7_ClassInstallAttr(ph7_gen_state *pGen, ph7_class *pClass, ph7_class_attr *pAttr);
|
||||
PH7_PRIVATE sxi32 PH7_ClassInstallMethod(ph7_class *pClass, ph7_class_method *pMeth);
|
||||
PH7_PRIVATE sxi32 PH7_ClassInherit(ph7_vm *pVm, ph7_class *pSub, ph7_class *pBase);
|
||||
PH7_PRIVATE sxi32 PH7_ClassBuildMro(ph7_vm *pVm, ph7_class *pClass);
|
||||
|
||||
Reference in New Issue
Block a user