mirror of https://git.wuffs.org/MWCC
2313 lines
68 KiB
C
2313 lines
68 KiB
C
#include "compiler/CClass.h"
|
|
#include "compiler/CABI.h"
|
|
#include "compiler/CDecl.h"
|
|
#include "compiler/CError.h"
|
|
#include "compiler/CExpr.h"
|
|
#include "compiler/CInit.h"
|
|
#include "compiler/CInline.h"
|
|
#include "compiler/CMachine.h"
|
|
#include "compiler/CMangler.h"
|
|
#include "compiler/CObjC.h"
|
|
#include "compiler/CParser.h"
|
|
#include "compiler/CRTTI.h"
|
|
#include "compiler/CSOM.h"
|
|
#include "compiler/CompilerTools.h"
|
|
#include "compiler/CodeGen.h"
|
|
#include "compiler/objects.h"
|
|
#include "compiler/scopes.h"
|
|
|
|
#ifdef __MWERKS__
|
|
#pragma options align=mac68k
|
|
#endif
|
|
typedef struct OVClassBase {
|
|
struct OVClassBase *next;
|
|
struct OVClass *ovclass;
|
|
Boolean is_virtual;
|
|
} OVClassBase;
|
|
|
|
typedef struct OVFunc {
|
|
struct OVFunc *next;
|
|
Object *obj;
|
|
struct OVClass *ovc8;
|
|
struct OVFunc *ovfC;
|
|
struct OVFunc *ovf10;
|
|
} OVFunc;
|
|
|
|
typedef struct OVClass {
|
|
TypeClass *tclass;
|
|
OVFunc *vfuncs;
|
|
OVClassBase *bases;
|
|
SInt32 offset;
|
|
SInt32 voffset;
|
|
Boolean alloced_vtable;
|
|
} OVClass;
|
|
|
|
typedef struct ThunkList {
|
|
struct ThunkList *next;
|
|
Object *thunkobj;
|
|
Object *obj;
|
|
SInt32 a;
|
|
SInt32 b;
|
|
SInt32 c;
|
|
} ThunkList;
|
|
#ifdef __MWERKS__
|
|
#pragma options align=reset
|
|
#endif
|
|
|
|
static TypeClass *main_class;
|
|
static ThunkList *cclass_thunklist;
|
|
static TypeClass *cclass_isbase_mostderived;
|
|
static SInt32 cclass_isbase_foundoffset;
|
|
static Boolean cclass_isambigbase;
|
|
static short cclass_founddepth;
|
|
static char *vtable_object_data;
|
|
static SInt32 vtable_data_size;
|
|
static OLinkList *vtable_object_links;
|
|
static TypeClass *cclass_vbase;
|
|
static OVClass *cclass_ovbase;
|
|
static OVClass *cclass_root;
|
|
static Object *found_pure;
|
|
static Boolean check_pures;
|
|
static Object *cclass_dominator_vobject;
|
|
static SInt32 cclass_dominator_voffset;
|
|
static Object *cclass_dominator_oobject;
|
|
static TypeClass *cclass_dominator_oclass;
|
|
static SInt32 cclass_dominator_ooffset;
|
|
static Object *cclass_dominator_eobject;
|
|
|
|
void CClass_Init(void) {
|
|
cclass_thunklist = NULL;
|
|
}
|
|
|
|
void CClass_GenThunks(void) {
|
|
ThunkList *list;
|
|
|
|
for (list = cclass_thunklist; list; list = list->next) {
|
|
list->thunkobj->flags |= OBJECT_FLAGS_4;
|
|
CodeGen_GenVDispatchThunk(list->thunkobj, list->obj, list->a, list->b, list->c);
|
|
}
|
|
}
|
|
|
|
static Object *CClass_ThunkObject(Object *obj, SInt32 a, SInt32 b, SInt32 c) {
|
|
Object *thunkobj;
|
|
ThunkList *list;
|
|
|
|
CInline_ObjectAddrRef(obj);
|
|
for (list = cclass_thunklist; list; list = list->next) {
|
|
if (obj == list->obj && a == list->a && b == list->b && c == list->c)
|
|
return list->thunkobj;
|
|
}
|
|
|
|
thunkobj = CParser_NewCompilerDefFunctionObject();
|
|
thunkobj->name = CMangler_ThunkName(obj, a, b, c);
|
|
thunkobj->type = TYPE(&rt_func);
|
|
thunkobj->sclass = TK_EXTERN;
|
|
thunkobj->qual = Q_20000;
|
|
thunkobj->u.func.linkname = thunkobj->name;
|
|
|
|
list = galloc(sizeof(ThunkList));
|
|
list->thunkobj = thunkobj;
|
|
list->obj = obj;
|
|
list->a = a;
|
|
list->b = b;
|
|
list->c = c;
|
|
list->next = cclass_thunklist;
|
|
cclass_thunklist = list;
|
|
|
|
return thunkobj;
|
|
}
|
|
|
|
static Boolean CClass_IsZeroOffsetClass(TypeClass *a, TypeClass *b) {
|
|
while (1) {
|
|
if (a == b)
|
|
return 1;
|
|
|
|
if (!a->bases || a->bases->is_virtual)
|
|
return 0;
|
|
|
|
a = a->bases->base;
|
|
}
|
|
}
|
|
|
|
static UInt8 CClass_IsCovariantResult(Type *a, UInt32 qualA, Type *b, UInt32 qualB, Boolean errorflag) {
|
|
TypeClass *tclassA;
|
|
TypeClass *tclassB;
|
|
|
|
if (
|
|
IS_TYPE_POINTER_ONLY(a) &&
|
|
IS_TYPE_POINTER_ONLY(b) &&
|
|
TPTR_QUAL(a) == TPTR_QUAL(b) &&
|
|
!CParser_IsMoreCVQualified(qualB, qualA) &&
|
|
IS_TYPE_CLASS(TPTR_TARGET(a)) &&
|
|
IS_TYPE_CLASS(TPTR_TARGET(b))
|
|
)
|
|
{
|
|
tclassA = TYPE_CLASS(TPTR_TARGET(a));
|
|
tclassB = TYPE_CLASS(TPTR_TARGET(b));
|
|
if (tclassA == tclassB || CClass_IsBaseClass(tclassB, tclassA, NULL, errorflag, errorflag)) {
|
|
if (!CClass_IsZeroOffsetClass(tclassB, tclassA))
|
|
return 2;
|
|
else
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
UInt8 CClass_GetOverrideKind(TypeFunc *a, TypeFunc *b, Boolean errorflag) {
|
|
if (!a->args || !b->args)
|
|
return 0;
|
|
|
|
if (
|
|
(a->flags & (FUNC_FLAGS_PASCAL | FUNC_FLAGS_F0000000)) != (b->flags & (FUNC_FLAGS_PASCAL | FUNC_FLAGS_F0000000)) ||
|
|
!is_arglistsame(a->args->next, b->args->next) ||
|
|
a->args->qual != b->args->qual
|
|
)
|
|
return 0;
|
|
|
|
if (!is_typesame(a->functype, b->functype)) {
|
|
switch (CClass_IsCovariantResult(a->functype, a->qual, b->functype, b->qual, errorflag)) {
|
|
case 0:
|
|
if (errorflag)
|
|
CError_Error(CErrorStr227);
|
|
return 0;
|
|
case 1:
|
|
return 1;
|
|
case 2:
|
|
return 2;
|
|
default:
|
|
CError_FATAL(225);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Boolean CClass_IsEmpty(TypeClass *tclass) {
|
|
ClassList *base;
|
|
|
|
if (tclass->ivars)
|
|
return 0;
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (!CClass_IsEmpty(base->base))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Boolean CClass_IsNonStaticMemberFunc(TypeFunc *tfunc) {
|
|
return IS_TYPEFUNC_NONSTATIC_METHOD(tfunc) || (tfunc->flags & FUNC_FLAGS_80);
|
|
}
|
|
|
|
Object *CClass_DefaultConstructor(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
Object *object;
|
|
|
|
for (nsol = CScope_FindName(tclass->nspace, constructor_name_node); nsol; nsol = nsol->next) {
|
|
object = OBJECT(nsol->object);
|
|
if (object->otype == OT_OBJECT && IS_TYPE_FUNC(object->type)) {
|
|
if ((tclass->flags & CLASS_FLAGS_20) && !tclass->sominfo) {
|
|
if (TYPE_FUNC(object->type)->args->next && !TYPE_FUNC(object->type)->args->next->next)
|
|
return object;
|
|
} else {
|
|
if (!TYPE_FUNC(object->type)->args->next)
|
|
return object;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Object *CClass_DummyDefaultConstructor(TypeClass *tclass) {
|
|
Object *ctor;
|
|
FuncArg *args;
|
|
NameSpaceObjectList *nsol;
|
|
HashNameNode *name;
|
|
TypeMethod *tmethod;
|
|
Object *object;
|
|
ObjectList list;
|
|
|
|
ctor = NULL;
|
|
|
|
for (nsol = CScope_FindName(tclass->nspace, constructor_name_node); nsol; nsol = nsol->next) {
|
|
object = OBJECT(nsol->object);
|
|
if (object->otype == OT_OBJECT && IS_TYPE_FUNC(object->type)) {
|
|
CError_ASSERT(305, args = TYPE_FUNC(object->type)->args);
|
|
args = args->next;
|
|
if ((tclass->flags & CLASS_FLAGS_20) && !tclass->sominfo) {
|
|
CError_ASSERT(309, args);
|
|
args = args->next;
|
|
}
|
|
CError_ASSERT(312, args);
|
|
|
|
if (args->dexpr) {
|
|
if (ctor) {
|
|
list.next = NULL;
|
|
list.object = object;
|
|
CError_OverloadedFunctionError(ctor, &list);
|
|
break;
|
|
}
|
|
ctor = object;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ctor) {
|
|
CError_Error(CErrorStr203);
|
|
return NULL;
|
|
}
|
|
|
|
name = GetHashNameNodeExport("__defctor");
|
|
if ((nsol = CScope_FindName(tclass->nspace, name))) {
|
|
CError_ASSERT(339, nsol->object->otype == OT_OBJECT && IS_TYPE_FUNC(OBJECT(nsol->object)->type));
|
|
return OBJECT(nsol->object);
|
|
}
|
|
|
|
tmethod = galloc(sizeof(TypeMethod));
|
|
memclrw(tmethod, sizeof(TypeMethod));
|
|
|
|
tmethod->type = TYPEFUNC;
|
|
tmethod->functype = TYPE(&void_ptr);
|
|
tmethod->flags = FUNC_FLAGS_METHOD;
|
|
tmethod->theclass = tclass;
|
|
CDecl_SetFuncFlags(TYPE_FUNC(tmethod), 0);
|
|
|
|
if ((tclass->flags & CLASS_FLAGS_20) && !tclass->sominfo)
|
|
CDecl_AddArgument(TYPE_FUNC(tmethod), TYPE(&stsignedshort));
|
|
|
|
CDecl_AddThisPointerArgument(TYPE_FUNC(tmethod), tclass);
|
|
|
|
object = CParser_NewCompilerDefFunctionObject();
|
|
object->type = TYPE(tmethod);
|
|
object->qual = Q_INLINE | Q_80000;
|
|
object->nspace = tclass->nspace;
|
|
object->name = name;
|
|
|
|
CScope_AddObject(tclass->nspace, name, OBJ_BASE(object));
|
|
CParser_RegisterDummyCtorFunction(object, ctor);
|
|
return object;
|
|
}
|
|
|
|
ENode *CClass_DefaultConstructorCall(TypeClass *tclass, TypeClass *b, ENode *objexpr, SInt32 varg, Boolean flag1, Boolean flag2, Boolean *errorflag) {
|
|
NameSpaceObjectList *nsol;
|
|
Object *ctor;
|
|
Object *object;
|
|
FuncArg *args;
|
|
ENodeList *argexprs;
|
|
ENode *expr;
|
|
BClassList *path;
|
|
BClassList list1;
|
|
ObjectList objlist;
|
|
BClassList list2;
|
|
short founddepth;
|
|
Boolean isambigbase;
|
|
|
|
*errorflag = 0;
|
|
|
|
if (!(nsol = CScope_FindName(tclass->nspace, constructor_name_node)))
|
|
return NULL;
|
|
|
|
ctor = NULL;
|
|
do {
|
|
object = OBJECT(nsol->object);
|
|
if (object->otype == OT_OBJECT && IS_TYPE_FUNC(object->type)) {
|
|
CError_ASSERT(397, args = TYPE_FUNC(object->type)->args);
|
|
|
|
args = args->next;
|
|
|
|
if ((tclass->flags & CLASS_FLAGS_20) && !tclass->sominfo) {
|
|
CError_ASSERT(401, args);
|
|
args = args->next;
|
|
}
|
|
|
|
if (!args || args->dexpr) {
|
|
if (ctor) {
|
|
objlist.next = NULL;
|
|
objlist.object = object;
|
|
CError_OverloadedFunctionError(ctor, &objlist);
|
|
break;
|
|
}
|
|
ctor = object;
|
|
}
|
|
}
|
|
} while ((nsol = nsol->next));
|
|
|
|
if (!ctor) {
|
|
*errorflag = 1;
|
|
return NULL;
|
|
}
|
|
|
|
if (flag1) {
|
|
if (b) {
|
|
if (flag2) {
|
|
if ((path = CClass_GetBasePath(b, tclass, &founddepth, &isambigbase)))
|
|
list1 = *path;
|
|
else
|
|
goto skipCheck;
|
|
} else {
|
|
list1.next = &list2;
|
|
list1.type = TYPE(b);
|
|
list2.next = NULL;
|
|
list2.type = TYPE(tclass);
|
|
}
|
|
} else {
|
|
list1.next = NULL;
|
|
list1.type = TYPE(tclass);
|
|
}
|
|
CClass_CheckPathAccess(&list1, ctor, ctor->access);
|
|
}
|
|
|
|
skipCheck:
|
|
if ((tclass->flags & CLASS_FLAGS_20) && !tclass->sominfo) {
|
|
argexprs = lalloc(sizeof(ENodeList));
|
|
argexprs->next = NULL;
|
|
argexprs->node = intconstnode(TYPE(&stsignedshort), varg);
|
|
} else {
|
|
argexprs = NULL;
|
|
}
|
|
|
|
CError_ASSERT(471, IS_TYPE_POINTER_ONLY(objexpr->rtype));
|
|
|
|
objexpr = makemonadicnode(objexpr, EINDIRECT);
|
|
objexpr->rtype = TYPE(tclass);
|
|
|
|
list1.next = NULL;
|
|
list1.type = TYPE(tclass);
|
|
|
|
expr = CExpr_GenericFuncCall(
|
|
&list1,
|
|
objexpr,
|
|
0,
|
|
ctor,
|
|
NULL,
|
|
NULL,
|
|
argexprs,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (ENODE_IS2(expr, EFUNCCALL, EFUNCCALLP))
|
|
expr->rtype = CDecl_NewPointerType(TYPE(tclass));
|
|
|
|
return expr;
|
|
}
|
|
|
|
Object *CClass_AssignmentOperator(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
Object *object;
|
|
|
|
for (nsol = CScope_FindName(tclass->nspace, asop_name_node); nsol; nsol = nsol->next) {
|
|
object = OBJECT(nsol->object);
|
|
if (
|
|
object->otype == OT_OBJECT &&
|
|
IS_TYPE_FUNC(object->type) &&
|
|
TYPE_FUNC(object->type)->args &&
|
|
TYPE_FUNC(object->type)->args->next &&
|
|
!TYPE_FUNC(object->type)->args->next->next
|
|
)
|
|
{
|
|
if (
|
|
IS_TYPE_CLASS(TYPE_FUNC(object->type)->args->next->type) &&
|
|
TYPE_CLASS(TYPE_FUNC(object->type)->args->next->type) == tclass
|
|
)
|
|
return object;
|
|
|
|
if (
|
|
IS_TYPE_REFERENCE(TYPE_FUNC(object->type)->args->next->type) &&
|
|
TPTR_TARGET(TYPE_FUNC(object->type)->args->next->type) == TYPE(tclass)
|
|
)
|
|
return object;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Object *CClass_CopyConstructor(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
Object *object;
|
|
FuncArg *args;
|
|
|
|
if (tclass->sominfo)
|
|
return NULL;
|
|
|
|
for (nsol = CScope_FindName(tclass->nspace, constructor_name_node); nsol; nsol = nsol->next) {
|
|
object = OBJECT(nsol->object);
|
|
if (object->otype == OT_OBJECT && IS_TYPE_FUNC(object->type)) {
|
|
CError_ASSERT(532, args = TYPE_FUNC(object->type)->args);
|
|
|
|
args = args->next;
|
|
|
|
if (tclass->flags & CLASS_FLAGS_20) {
|
|
CError_ASSERT(536, args);
|
|
args = args->next;
|
|
}
|
|
|
|
if (
|
|
args &&
|
|
args != &elipsis &&
|
|
(!args->next || args->next->dexpr) &&
|
|
IS_TYPE_REFERENCE(args->type) &&
|
|
TPTR_TARGET(args->type) == TYPE(tclass)
|
|
)
|
|
return object;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NameSpaceObjectList *CClass_MemberObject(TypeClass *tclass, HashNameNode *name) {
|
|
NameSpaceObjectList *nsol;
|
|
|
|
if ((nsol = CScope_FindName(tclass->nspace, name)) && nsol->object->otype == OT_OBJECT)
|
|
return nsol;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
NameSpaceObjectList *CClass_Constructor(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
|
|
if (
|
|
(nsol = CScope_FindName(tclass->nspace, constructor_name_node)) &&
|
|
nsol->object->otype == OT_OBJECT &&
|
|
IS_TYPE_FUNC(OBJECT(nsol->object)->type)
|
|
)
|
|
return nsol;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Object *CClass_Destructor(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
|
|
for (nsol = CScope_FindName(tclass->nspace, destructor_name_node); nsol; nsol = nsol->next) {
|
|
if (
|
|
nsol->object->otype == OT_OBJECT &&
|
|
IS_TYPE_FUNC(OBJECT(nsol->object)->type)
|
|
)
|
|
return OBJECT(nsol->object);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Boolean CClass_IsConstructor(Object *obj) {
|
|
return obj && IS_TYPE_FUNC(obj->type) && (TYPE_FUNC(obj->type)->flags & FUNC_FLAGS_1000);
|
|
}
|
|
|
|
Boolean CClass_IsDestructor(Object *obj) {
|
|
return obj && IS_TYPE_FUNC(obj->type) && (TYPE_FUNC(obj->type)->flags & FUNC_FLAGS_2000);
|
|
}
|
|
|
|
Boolean CClass_IsPODClass(TypeClass *tclass) {
|
|
NameSpaceObjectList *nsol;
|
|
Object *object;
|
|
ObjMemberVar *ivar;
|
|
Type *type;
|
|
|
|
if (tclass->vtable || tclass->bases || CClass_Destructor(tclass))
|
|
return 0;
|
|
|
|
for (nsol = CClass_Constructor(tclass); nsol; nsol = nsol->next) {
|
|
if (
|
|
nsol->object->otype == OT_OBJECT &&
|
|
IS_TYPE_FUNC(OBJECT(nsol->object)->type) &&
|
|
!(TYPE_FUNC(OBJECT(nsol->object)->type)->flags & FUNC_FLAGS_100)
|
|
)
|
|
return 0;
|
|
}
|
|
|
|
object = CClass_AssignmentOperator(tclass);
|
|
if (object && !(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_100))
|
|
return 0;
|
|
|
|
for (ivar = tclass->ivars; ivar; ivar = ivar->next) {
|
|
if (ivar->access != ACCESSPUBLIC)
|
|
return 0;
|
|
|
|
type = ivar->type;
|
|
while (IS_TYPE_ARRAY(type))
|
|
type = TPTR_TARGET(type);
|
|
|
|
switch (type->type) {
|
|
case TYPECLASS:
|
|
if (!CClass_IsPODClass(TYPE_CLASS(type)))
|
|
return 0;
|
|
break;
|
|
case TYPEMEMBERPOINTER:
|
|
return 0;
|
|
case TYPEPOINTER:
|
|
if (TPTR_QUAL(type) & Q_REFERENCE)
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Boolean CClass_IsTrivialCopyClass(TypeClass *tclass) {
|
|
Object *object;
|
|
ClassList *base;
|
|
ObjMemberVar *ivar;
|
|
Type *type;
|
|
|
|
if (tclass->vtable || tclass->vbases || CClass_Destructor(tclass))
|
|
return 0;
|
|
|
|
object = CClass_CopyConstructor(tclass);
|
|
if (object && IS_TYPE_FUNC(object->type) && !(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_100))
|
|
return 0;
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (!CClass_IsTrivialCopyClass(base->base))
|
|
return 0;
|
|
}
|
|
|
|
for (ivar = tclass->ivars; ivar; ivar = ivar->next) {
|
|
type = ivar->type;
|
|
while (IS_TYPE_ARRAY(type))
|
|
type = TPTR_TARGET(type);
|
|
|
|
if (IS_TYPE_CLASS(type) && !CClass_IsTrivialCopyClass(TYPE_CLASS(type)))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Boolean CClass_IsTrivialCopyAssignClass(TypeClass *tclass) {
|
|
Object *object;
|
|
ObjMemberVar *ivar;
|
|
Type *type;
|
|
|
|
if (tclass->vtable || tclass->bases || CClass_Destructor(tclass))
|
|
return 0;
|
|
|
|
object = CClass_AssignmentOperator(tclass);
|
|
if (object && !(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_100))
|
|
return 0;
|
|
|
|
for (ivar = tclass->ivars; ivar; ivar = ivar->next) {
|
|
type = ivar->type;
|
|
while (IS_TYPE_ARRAY(type))
|
|
type = TPTR_TARGET(type);
|
|
|
|
if (IS_TYPE_REFERENCE(type))
|
|
return 0;
|
|
if (CParser_IsConst(type, ivar->qual))
|
|
return 0;
|
|
if (IS_TYPE_CLASS(type) && !CClass_IsTrivialCopyAssignClass(TYPE_CLASS(type)))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
Boolean CClass_ReferenceArgument(TypeClass *tclass) {
|
|
if ((tclass->flags & (CLASS_FLAGS_2 | CLASS_FLAGS_800)) == CLASS_FLAGS_800)
|
|
CDecl_CompleteType(TYPE(tclass));
|
|
|
|
if (copts.simple_class_byval)
|
|
return !CClass_IsTrivialCopyClass(tclass);
|
|
|
|
return CClass_Destructor(tclass) || CClass_CopyConstructor(tclass);
|
|
}
|
|
|
|
BClassList *CClass_GetPathCopy(BClassList *path, Boolean is_global) {
|
|
BClassList *last;
|
|
BClassList *copy;
|
|
|
|
if (!path)
|
|
return NULL;
|
|
|
|
copy = last = is_global ? galloc(sizeof(BClassList)) : lalloc(sizeof(BClassList));
|
|
last->next = path->next;
|
|
last->type = path->type;
|
|
|
|
while ((path = path->next)) {
|
|
last->next = is_global ? galloc(sizeof(BClassList)) : lalloc(sizeof(BClassList));
|
|
last = last->next;
|
|
last->next = path->next;
|
|
last->type = path->type;
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
|
|
BClassList *CClass_AppendPath(BClassList *dest, BClassList *src) {
|
|
BClassList *last;
|
|
|
|
if (!(last = dest))
|
|
return src;
|
|
|
|
while (last->next)
|
|
last = last->next;
|
|
|
|
while (src && src->type == last->type)
|
|
src = src->next;
|
|
|
|
last->next = src;
|
|
return dest;
|
|
}
|
|
|
|
static AccessType CClass_GetPathAccess(BClassList *path) {
|
|
AccessType result;
|
|
ClassList *base;
|
|
TypeClass *tclass;
|
|
|
|
CError_ASSERT(930, path);
|
|
|
|
result = ACCESSPUBLIC;
|
|
|
|
while (1) {
|
|
tclass = TYPE_CLASS(path->type);
|
|
if (!(path = path->next))
|
|
break;
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base == TYPE_CLASS(path->type))
|
|
break;
|
|
}
|
|
|
|
CError_ASSERT(939, base);
|
|
|
|
switch (base->access) {
|
|
case ACCESSPUBLIC:
|
|
break;
|
|
case ACCESSPROTECTED:
|
|
if (result == ACCESSPUBLIC)
|
|
result = ACCESSPROTECTED;
|
|
break;
|
|
case ACCESSPRIVATE:
|
|
if (result == ACCESSPUBLIC || result == ACCESSPROTECTED)
|
|
result = ACCESSPRIVATE;
|
|
else
|
|
return ACCESSNONE;
|
|
break;
|
|
case ACCESSNONE:
|
|
return ACCESSNONE;
|
|
default:
|
|
CError_FATAL(960);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Boolean CClass_IsMoreAccessiblePath(BClassList *path1, BClassList *path2) {
|
|
switch (CClass_GetPathAccess(path2)) {
|
|
case ACCESSPUBLIC:
|
|
return 0;
|
|
case ACCESSNONE:
|
|
return 1;
|
|
default:
|
|
CError_FATAL(978);
|
|
case ACCESSPROTECTED:
|
|
switch (CClass_GetPathAccess(path1)) {
|
|
case ACCESSPUBLIC:
|
|
return 1;
|
|
case ACCESSPRIVATE:
|
|
case ACCESSPROTECTED:
|
|
case ACCESSNONE:
|
|
return 0;
|
|
default:
|
|
CError_FATAL(987);
|
|
}
|
|
case ACCESSPRIVATE:
|
|
switch (CClass_GetPathAccess(path1)) {
|
|
case ACCESSPUBLIC:
|
|
case ACCESSPROTECTED:
|
|
return 1;
|
|
case ACCESSPRIVATE:
|
|
case ACCESSNONE:
|
|
return 0;
|
|
default:
|
|
CError_FATAL(997);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static BClassList *CClass_GetBasePathRec(TypeClass *a, TypeClass *b, SInt32 offset, short depth) {
|
|
ClassList *base;
|
|
BClassList *checkpath;
|
|
BClassList *bestpath;
|
|
BClassList *newpath;
|
|
SInt32 newOffset;
|
|
|
|
for (base = a->bases, bestpath = NULL; base; base = base->next) {
|
|
if (base->is_virtual)
|
|
newOffset = CClass_VirtualBaseOffset(cclass_isbase_mostderived, base->base);
|
|
else
|
|
newOffset = offset + base->offset;
|
|
|
|
if (base->base == b) {
|
|
if (cclass_founddepth && newOffset != cclass_isbase_foundoffset) {
|
|
cclass_isambigbase = 1;
|
|
return NULL;
|
|
}
|
|
|
|
cclass_isbase_foundoffset = newOffset;
|
|
cclass_founddepth = depth;
|
|
|
|
bestpath = lalloc(sizeof(BClassList));
|
|
bestpath->next = NULL;
|
|
bestpath->type = TYPE(b);
|
|
} else if ((checkpath = CClass_GetBasePathRec(base->base, b, newOffset, depth + 1))) {
|
|
newpath = lalloc(sizeof(BClassList));
|
|
newpath->next = checkpath;
|
|
newpath->type = TYPE(base->base);
|
|
if (!bestpath || CClass_IsMoreAccessiblePath(newpath, bestpath))
|
|
bestpath = newpath;
|
|
}
|
|
}
|
|
|
|
return bestpath;
|
|
}
|
|
|
|
BClassList *CClass_GetBasePath(TypeClass *a, TypeClass *b, short *founddepth, Boolean *isambigbase) {
|
|
BClassList *path;
|
|
BClassList *result;
|
|
|
|
if ((a->flags & (CLASS_FLAGS_2 | CLASS_FLAGS_800)) == CLASS_FLAGS_800)
|
|
CDecl_CompleteType(TYPE(a));
|
|
if ((b->flags & (CLASS_FLAGS_2 | CLASS_FLAGS_800)) == CLASS_FLAGS_800)
|
|
CDecl_CompleteType(TYPE(b));
|
|
|
|
cclass_founddepth = 0;
|
|
cclass_isbase_mostderived = a;
|
|
cclass_isbase_foundoffset = -1;
|
|
cclass_isambigbase = 0;
|
|
|
|
if ((path = CClass_GetBasePathRec(a, b, 0, 1))) {
|
|
*founddepth = cclass_founddepth;
|
|
*isambigbase = cclass_isambigbase;
|
|
|
|
result = lalloc(sizeof(BClassList));
|
|
result->next = path;
|
|
result->type = TYPE(a);
|
|
return result;
|
|
} else {
|
|
*isambigbase = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Boolean CClass_IsBaseClass(TypeClass *a, TypeClass *b, short *founddepth, Boolean pathcheckflag, Boolean ambigerrorflag) {
|
|
BClassList *path;
|
|
short depth;
|
|
Boolean isambigbase;
|
|
|
|
if ((path = CClass_GetBasePath(a, b, &depth, &isambigbase))) {
|
|
if (isambigbase && ambigerrorflag)
|
|
CError_Error(CErrorStr188);
|
|
|
|
if (pathcheckflag)
|
|
CClass_CheckPathAccess(path, NULL, ACCESSPUBLIC);
|
|
|
|
if (founddepth)
|
|
*founddepth = depth;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
TypeClass *CClass_GetQualifiedClass(void) {
|
|
DeclInfo di;
|
|
|
|
memclrw(&di, sizeof(di));
|
|
CParser_GetDeclSpecs(&di, 0);
|
|
|
|
if (IS_TYPE_CLASS(di.thetype))
|
|
return TYPE_CLASS(di.thetype);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ENode *CClass_AccessPathCast(BClassList *path, ENode *expr, Boolean reverse) {
|
|
ClassList *base;
|
|
SInt32 offset;
|
|
|
|
while (path && path->next) {
|
|
base = TYPE_CLASS(path->type)->bases;
|
|
while (1) {
|
|
if (base->base == TYPE_CLASS(path->next->type))
|
|
break;
|
|
CError_ASSERT(1157, base = base->next);
|
|
}
|
|
|
|
if (base->is_virtual) {
|
|
if (reverse) {
|
|
CError_Error(CErrorStr164);
|
|
break;
|
|
}
|
|
|
|
if (!base->base->sominfo) {
|
|
offset = base->offset;
|
|
if (offset && !canadd(expr, offset)) {
|
|
expr = makediadicnode(expr, intconstnode(TYPE(&stunsignedlong), offset), EADD);
|
|
optimizecomm(expr);
|
|
}
|
|
|
|
expr->rtype = CDecl_NewPointerType(CDecl_NewPointerType(TYPE(base->base)));
|
|
expr = makemonadicnode(expr, EINDIRECT);
|
|
expr->rtype = expr->data.monadic->rtype;
|
|
}
|
|
} else {
|
|
offset = base->offset;
|
|
if (offset) {
|
|
if (reverse)
|
|
offset = -offset;
|
|
if (!canadd(expr, offset)) {
|
|
expr = makediadicnode(expr, intconstnode(TYPE(&stunsignedlong), offset), EADD);
|
|
optimizecomm(expr);
|
|
}
|
|
}
|
|
}
|
|
|
|
path = path->next;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
ENode *CClass_ClassPointerCast(ENode *expr, TypeClass *a, TypeClass *b, Boolean typconflag, Boolean ambigerrorflag, Boolean pathcheckflag) {
|
|
BClassList *path;
|
|
Boolean reverse;
|
|
short depth;
|
|
Boolean isambigbase;
|
|
|
|
reverse = 0;
|
|
|
|
if (a != b) {
|
|
if (!(path = CClass_GetBasePath(a, b, &depth, &isambigbase))) {
|
|
CError_ASSERT(1216, typconflag);
|
|
if ((path = CClass_GetBasePath(b, a, &depth, &isambigbase))) {
|
|
reverse = 1;
|
|
goto doCast;
|
|
}
|
|
} else {
|
|
doCast:
|
|
if (isambigbase && ambigerrorflag)
|
|
CError_Error(CErrorStr188);
|
|
if (pathcheckflag)
|
|
CClass_CheckPathAccess(path, NULL, ACCESSPUBLIC);
|
|
if (!(a->flags & CLASS_FLAGS_10) && !b->sominfo)
|
|
expr = CClass_AccessPathCast(path, expr, reverse);
|
|
}
|
|
}
|
|
|
|
if (typconflag && ENODE_IS(expr, EINDIRECT) && IS_TYPE_POINTER_ONLY(expr->rtype))
|
|
expr = makemonadicnode(expr, ETYPCON);
|
|
|
|
return expr;
|
|
}
|
|
|
|
ENode *CClass_DirectBasePointerCast(ENode *expr, TypeClass *a, TypeClass *b) {
|
|
ClassList *base;
|
|
VClassList *vbase;
|
|
BClassList *path;
|
|
BClassList list1;
|
|
BClassList list2;
|
|
short depth;
|
|
Boolean isambigbase;
|
|
|
|
for (base = a->bases; base; base = base->next) {
|
|
if (base->base == b) {
|
|
list1.next = &list2;
|
|
list1.type = TYPE(a);
|
|
list2.next = NULL;
|
|
list2.type = TYPE(b);
|
|
return CClass_AccessPathCast(&list1, expr, 0);
|
|
}
|
|
}
|
|
|
|
for (vbase = a->vbases; vbase; vbase = vbase->next) {
|
|
if (vbase->base == b) {
|
|
CError_ASSERT(1273, path = CClass_GetBasePath(a, b, &depth, &isambigbase));
|
|
return CClass_AccessPathCast(path, expr, 0);
|
|
}
|
|
}
|
|
|
|
CError_FATAL(1277);
|
|
return expr;
|
|
}
|
|
|
|
SInt32 CClass_GetPathOffset(BClassList *path) {
|
|
SInt32 offset;
|
|
ClassList *base;
|
|
|
|
offset = 0;
|
|
|
|
while (path->next) {
|
|
if (path->type != path->next->type) {
|
|
for (base = TYPE_CLASS(path->type)->bases; base; base = base->next) {
|
|
if (base->base == TYPE_CLASS(path->next->type))
|
|
break;
|
|
}
|
|
|
|
if (!base) {
|
|
CError_Error(CErrorStr221);
|
|
return offset;
|
|
}
|
|
}
|
|
|
|
if (base->is_virtual)
|
|
return -1;
|
|
offset += base->offset;
|
|
|
|
path = path->next;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
Boolean CClass_ClassDominates(TypeClass *tclass, TypeClass *baseclass) {
|
|
ClassList *base;
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base == baseclass || CClass_ClassDominates(base->base, baseclass))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SInt32 CClass_VirtualBaseOffset(TypeClass *tclass, TypeClass *baseclass) {
|
|
VClassList *vbase;
|
|
|
|
for (vbase = tclass->vbases; vbase; vbase = vbase->next) {
|
|
if (vbase->base == baseclass)
|
|
return vbase->offset;
|
|
}
|
|
|
|
CError_FATAL(1342);
|
|
return 0;
|
|
}
|
|
|
|
SInt32 CClass_VirtualBaseVTableOffset(TypeClass *tclass, TypeClass *baseclass) {
|
|
VClassList *vbase;
|
|
|
|
for (vbase = tclass->vbases; vbase; vbase = vbase->next) {
|
|
if (vbase->base == baseclass)
|
|
return vbase->voffset;
|
|
}
|
|
|
|
CError_FATAL(1359);
|
|
return 0;
|
|
}
|
|
|
|
SInt32 CClass_GetMemberOffset(TypeClass *tclass, HashNameNode *name, ObjMemberVar **resultIvar) {
|
|
ObjMemberVar *ivar;
|
|
ClassList *base;
|
|
SInt32 offset;
|
|
SInt32 tmp;
|
|
Boolean foundflag;
|
|
|
|
for (ivar = tclass->ivars; ivar; ivar = ivar->next) {
|
|
if (ivar->name == name) {
|
|
if (resultIvar)
|
|
*resultIvar = ivar;
|
|
return ivar->offset;
|
|
}
|
|
}
|
|
|
|
for (base = tclass->bases, foundflag = 0; base; base = base->next) {
|
|
switch ((tmp = CClass_GetMemberOffset(base->base, name, resultIvar))) {
|
|
case -3:
|
|
case -2:
|
|
return tmp;
|
|
case -1:
|
|
break;
|
|
default:
|
|
if (base->is_virtual)
|
|
return -3;
|
|
if (foundflag)
|
|
return -2;
|
|
foundflag = 1;
|
|
offset = base->offset + tmp;
|
|
}
|
|
}
|
|
|
|
return foundflag ? offset : -1;
|
|
}
|
|
|
|
Boolean CClass_OverridesBaseMember(TypeClass *tclass, HashNameNode *name, Object *obj) {
|
|
NameSpaceObjectList *nsol;
|
|
ClassList *base;
|
|
Boolean result;
|
|
|
|
result = 0;
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base->vtable) {
|
|
for (nsol = CScope_FindName(base->base->nspace, name); nsol; nsol = nsol->next) {
|
|
if (
|
|
nsol->object->otype == OT_OBJECT &&
|
|
OBJECT(nsol->object)->datatype == DVFUNC &&
|
|
CClass_GetOverrideKind(TYPE_FUNC(OBJECT(nsol->object)->type), TYPE_FUNC(obj->type), 1)
|
|
)
|
|
result = 1;
|
|
}
|
|
|
|
if (CClass_OverridesBaseMember(base->base, name, obj))
|
|
result = 1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static OVClass *CClass_FindOVClass(OVClass *ovclass, TypeClass *tclass, SInt32 offset) {
|
|
OVClassBase *ovbase;
|
|
|
|
if (ovclass->tclass == tclass && ovclass->offset == offset)
|
|
return ovclass;
|
|
|
|
for (ovbase = ovclass->bases; ovbase; ovbase = ovbase->next) {
|
|
if ((ovclass = CClass_FindOVClass(ovbase->ovclass, tclass, offset)))
|
|
return ovclass;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static OVClass *CClass_BuildOVClassTree(OVClass *root, TypeClass *tclass, SInt32 offset, SInt32 voffset) {
|
|
ClassList *base;
|
|
Object *object;
|
|
OVClass *tree;
|
|
OVFunc *ovfunc;
|
|
OVClassBase *ovbase;
|
|
SInt32 vboffset;
|
|
SInt32 vbvoffset;
|
|
CScopeObjectIterator iter;
|
|
|
|
tree = lalloc(sizeof(OVClass));
|
|
memclrw(tree, sizeof(OVClass));
|
|
|
|
tree->tclass = tclass;
|
|
tree->offset = offset;
|
|
tree->voffset = voffset;
|
|
if (!root)
|
|
root = tree;
|
|
|
|
CScope_InitObjectIterator(&iter, tclass->nspace);
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (object->datatype == DVFUNC) {
|
|
ovfunc = lalloc(sizeof(OVFunc));
|
|
memclrw(ovfunc, sizeof(OVFunc));
|
|
|
|
ovfunc->next = tree->vfuncs;
|
|
ovfunc->obj = object;
|
|
tree->vfuncs = ovfunc;
|
|
}
|
|
}
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base->vtable || base->base->sominfo) {
|
|
ovbase = lalloc(sizeof(OVClassBase));
|
|
memclrw(ovbase, sizeof(OVClassBase));
|
|
|
|
if (base->is_virtual) {
|
|
vboffset = CClass_VirtualBaseOffset(main_class, base->base);
|
|
if (!(ovbase->ovclass = CClass_FindOVClass(root, base->base, vboffset))) {
|
|
vbvoffset = CClass_VirtualBaseVTableOffset(main_class, base->base);
|
|
ovbase->ovclass = CClass_BuildOVClassTree(root, base->base, vboffset, vbvoffset);
|
|
}
|
|
ovbase->is_virtual = 1;
|
|
} else {
|
|
ovbase->ovclass = CClass_BuildOVClassTree(
|
|
root,
|
|
base->base,
|
|
offset + base->offset,
|
|
voffset + base->voffset);
|
|
ovbase->is_virtual = 0;
|
|
}
|
|
|
|
ovbase->next = tree->bases;
|
|
tree->bases = ovbase;
|
|
}
|
|
}
|
|
|
|
return tree;
|
|
}
|
|
|
|
static Boolean CClass_IsBaseOf(OVClass *a, OVClass *b) {
|
|
OVClassBase *ovbase;
|
|
|
|
for (ovbase = b->bases; ovbase; ovbase = ovbase->next) {
|
|
if (
|
|
(ovbase->ovclass->tclass == a->tclass && ovbase->ovclass->offset == a->offset) ||
|
|
CClass_IsBaseOf(a, ovbase->ovclass)
|
|
)
|
|
{
|
|
if (!cclass_ovbase && ovbase->is_virtual)
|
|
cclass_ovbase = ovbase->ovclass;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void CClass_FindOVFunc(OVClass *a, OVClass *b, OVFunc *func) {
|
|
OVFunc *scan;
|
|
OVClassBase *ovbase;
|
|
UInt8 overrideKind;
|
|
|
|
if (CClass_IsBaseOf(b, a)) {
|
|
for (scan = a->vfuncs; scan; scan = scan->next) {
|
|
if (
|
|
(func->obj->name == scan->obj->name) &&
|
|
(overrideKind = CClass_GetOverrideKind(TYPE_FUNC(func->obj->type), TYPE_FUNC(scan->obj->type), 0))
|
|
)
|
|
{
|
|
if (func->ovc8) {
|
|
if (func->ovc8->tclass == a->tclass || CClass_ClassDominates(func->ovc8->tclass, a->tclass))
|
|
return;
|
|
if (!CClass_ClassDominates(a->tclass, func->ovc8->tclass)) {
|
|
func->ovf10 = scan;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (a == cclass_root) {
|
|
TYPE_FUNC(scan->obj->type)->flags |= FUNC_FLAGS_20;
|
|
if (overrideKind == 2)
|
|
TYPE_FUNC(scan->obj->type)->flags |= FUNC_FLAGS_400000;
|
|
}
|
|
|
|
func->ovc8 = a;
|
|
func->ovfC = scan;
|
|
func->ovf10 = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (ovbase = a->bases; ovbase; ovbase = ovbase->next)
|
|
CClass_FindOVFunc(ovbase->ovclass, b, func);
|
|
}
|
|
}
|
|
|
|
static TypeList *CClass_GetCoVariantClassList(TypeList *list, TypeClass *tclass, Object *func, Boolean flag) {
|
|
Object *object;
|
|
TypeList *scan;
|
|
Type *type;
|
|
ClassList *base;
|
|
CScopeObjectIterator iter;
|
|
|
|
if (!flag) {
|
|
CScope_InitObjectIterator(&iter, tclass->nspace);
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (
|
|
object->name == func->name &&
|
|
object->datatype == DVFUNC &&
|
|
CClass_GetOverrideKind(TYPE_FUNC(object->type), TYPE_FUNC(func->type), 0) == 2
|
|
)
|
|
{
|
|
CError_ASSERT(1686,
|
|
IS_TYPE_POINTER_ONLY(TYPE_FUNC(object->type)->functype) &&
|
|
IS_TYPE_CLASS(TPTR_TARGET(TYPE_FUNC(object->type)->functype)));
|
|
|
|
type = TPTR_TARGET(TYPE_FUNC(object->type)->functype);
|
|
for (scan = list; scan; scan = scan->next) {
|
|
if (scan->type == type)
|
|
break;
|
|
}
|
|
|
|
if (!scan) {
|
|
scan = lalloc(sizeof(TypeList));
|
|
scan->type = type;
|
|
scan->next = list;
|
|
list = scan;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base->vtable)
|
|
list = CClass_GetCoVariantClassList(list, base->base, func, 0);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
static TypeMethod *CClass_GetCovariantType(TypeMethod *tmethod, Type *type) {
|
|
TypePointer *tptr;
|
|
TypeMethod *result;
|
|
|
|
CError_ASSERT(1724,
|
|
IS_TYPE_METHOD(tmethod) &&
|
|
IS_TYPE_POINTER_ONLY(tmethod->functype));
|
|
|
|
tptr = galloc(sizeof(TypePointer));
|
|
*tptr = *TYPE_POINTER(tmethod->functype);
|
|
tptr->target = type;
|
|
|
|
result = galloc(sizeof(TypeMethod));
|
|
*result = *tmethod;
|
|
result->flags &= ~(FUNC_FLAGS_20 | FUNC_FLAGS_400000);
|
|
result->functype = TYPE(tptr);
|
|
return result;
|
|
}
|
|
|
|
static Object *CClass_FindCovariantFunction(Object *func, Type *type) {
|
|
NameSpaceObjectList *nsol;
|
|
|
|
nsol = CScope_FindName(
|
|
TYPE_METHOD(func->type)->theclass->nspace,
|
|
CMangler_GetCovariantFunctionName(func, type));
|
|
|
|
CError_ASSERT(1754, nsol && !nsol->next && nsol->object->otype == OT_OBJECT);
|
|
|
|
return OBJECT(nsol->object);
|
|
}
|
|
|
|
static ObjectList *CClass_DeclareCovariantFuncs(ObjectList *list, Object *func, TypeClass *tclass) {
|
|
Object *newfunc;
|
|
HashNameNode *name;
|
|
TypeList *types;
|
|
ObjectList *newlist;
|
|
|
|
for (types = CClass_GetCoVariantClassList(NULL, tclass, func, 1); types; types = types->next) {
|
|
name = CMangler_GetCovariantFunctionName(func, types->type);
|
|
|
|
newfunc = galloc(sizeof(Object));
|
|
memclrw(newfunc, sizeof(Object));
|
|
|
|
newfunc->otype = OT_OBJECT;
|
|
newfunc->datatype = DFUNC;
|
|
newfunc->section = func->section;
|
|
newfunc->nspace = func->nspace;
|
|
newfunc->name = name;
|
|
newfunc->type = TYPE(CClass_GetCovariantType(TYPE_METHOD(func->type), types->type));
|
|
newfunc->qual = func->qual & ~Q_INLINE;
|
|
newfunc->sclass = func->sclass;
|
|
newfunc->u.func.linkname = name;
|
|
|
|
newlist = lalloc(sizeof(ObjectList));
|
|
newlist->object = newfunc;
|
|
newlist->next = list;
|
|
list = newlist;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
void CClass_DefineCovariantFuncs(Object *method, CI_FuncData *ifuncdata) {
|
|
NameSpace *nspace;
|
|
TypeList *types;
|
|
Object *covariantFunc;
|
|
Statement *stmt;
|
|
Type *tptr;
|
|
Statement firstStmt;
|
|
|
|
for (types = CClass_GetCoVariantClassList(NULL, TYPE_METHOD(method->type)->theclass, method, 1); types; types = types->next) {
|
|
covariantFunc = CClass_FindCovariantFunction(method, types->type);
|
|
tptr = CDecl_NewPointerType(types->type);
|
|
|
|
nspace = CFunc_FuncGenSetup(&firstStmt, covariantFunc);
|
|
CInline_UnpackIFunctionData(covariantFunc, ifuncdata, &firstStmt);
|
|
|
|
for (stmt = &firstStmt; stmt; stmt = stmt->next) {
|
|
if (stmt->type == ST_RETURN && stmt->expr)
|
|
stmt->expr = CExpr_AssignmentPromotion(stmt->expr, tptr, ENODE_QUALS(stmt->expr), 0);
|
|
}
|
|
|
|
CFunc_Gen(&firstStmt, covariantFunc, 0);
|
|
cscope_current = nspace->parent;
|
|
}
|
|
}
|
|
|
|
static void CClass_OverrideOVClassTree(OVClass *ovclass) {
|
|
OVClassBase *ovbase;
|
|
OVFunc *ovfunc;
|
|
|
|
if (cclass_root != ovclass) {
|
|
for (ovfunc = ovclass->vfuncs; ovfunc; ovfunc = ovfunc->next)
|
|
CClass_FindOVFunc(cclass_root, ovclass, ovfunc);
|
|
}
|
|
|
|
for (ovbase = ovclass->bases; ovbase; ovbase = ovbase->next)
|
|
CClass_OverrideOVClassTree(ovbase->ovclass);
|
|
}
|
|
|
|
static void CClass_AllocVTableRec(OVClass *ovclass) {
|
|
OVFunc *ovfunc;
|
|
Object *object;
|
|
SInt32 offset27;
|
|
SInt32 offset26;
|
|
SInt32 offset23;
|
|
OVClassBase *ovbase;
|
|
OLinkList *link;
|
|
|
|
if (ovclass->alloced_vtable)
|
|
return;
|
|
|
|
for (ovfunc = ovclass->vfuncs; ovfunc; ovfunc = ovfunc->next) {
|
|
offset27 = ovclass->voffset + TYPE_METHOD(ovfunc->obj->type)->x1E + CABI_GetVTableOffset(ovclass->tclass);
|
|
CError_ASSERT(1867, offset27 < vtable_data_size);
|
|
|
|
if (!(vtable_object_data[offset27])) {
|
|
if (ovfunc->ovfC) {
|
|
object = ovfunc->ovfC->obj;
|
|
if (
|
|
(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_400000) &&
|
|
CClass_GetOverrideKind(TYPE_FUNC(ovfunc->obj->type), TYPE_FUNC(object->type), 0) == 2
|
|
)
|
|
{
|
|
CError_ASSERT(1887,
|
|
IS_TYPE_POINTER_ONLY(TYPE_FUNC(ovfunc->obj->type)->functype) &&
|
|
IS_TYPE_CLASS(TPTR_TARGET(TYPE_FUNC(ovfunc->obj->type)->functype)));
|
|
object = CClass_FindCovariantFunction(
|
|
object,
|
|
TPTR_TARGET(TYPE_FUNC(ovfunc->obj->type)->functype));
|
|
}
|
|
|
|
if ((offset26 = ovfunc->ovc8->offset - ovclass->offset)) {
|
|
if (!(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_8)) {
|
|
cclass_ovbase = NULL;
|
|
CError_ASSERT(1899, CClass_IsBaseOf(ovclass, ovfunc->ovc8));
|
|
|
|
if (cclass_ovbase && (main_class->flags & CLASS_FLAGS_8000)) {
|
|
offset23 = ovclass->offset - cclass_ovbase->offset;
|
|
offset23 = CABI_GetCtorOffsetOffset(cclass_ovbase->tclass, NULL) - offset23;
|
|
CError_ASSERT(1906, offset23 > 0);
|
|
object = CClass_ThunkObject(object, offset26, 0, offset23);
|
|
} else {
|
|
object = CClass_ThunkObject(object, offset26, 0, -1);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
object = ovfunc->obj;
|
|
}
|
|
|
|
if (!(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_8)) {
|
|
link = lalloc(sizeof(OLinkList));
|
|
link->next = vtable_object_links;
|
|
link->obj = object;
|
|
link->offset = offset27;
|
|
link->somevalue = 0;
|
|
vtable_object_links = link;
|
|
vtable_object_data[offset27] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ovclass->alloced_vtable = 1;
|
|
|
|
for (ovbase = ovclass->bases; ovbase; ovbase = ovbase->next)
|
|
CClass_AllocVTableRec(ovbase->ovclass);
|
|
}
|
|
|
|
static Object *CClass_CheckClass(OVClass *ovclass, Boolean errorflag) {
|
|
Object *object;
|
|
Object *check;
|
|
OVFunc *ovfunc;
|
|
OVClassBase *ovbase;
|
|
|
|
object = NULL;
|
|
|
|
for (ovfunc = ovclass->vfuncs; ovfunc; ovfunc = ovfunc->next) {
|
|
if (ovfunc->ovf10 && errorflag)
|
|
CError_Error(CErrorStr251, main_class, 0, ovfunc->obj, ovfunc->ovfC->obj, ovfunc->ovf10->obj);
|
|
|
|
if (!object) {
|
|
check = ovfunc->ovfC ? ovfunc->ovfC->obj : ovfunc->obj;
|
|
if (TYPE_FUNC(check->type)->flags & FUNC_FLAGS_8)
|
|
object = check;
|
|
}
|
|
}
|
|
|
|
for (ovbase = ovclass->bases; ovbase; ovbase = ovbase->next) {
|
|
if (object)
|
|
CClass_CheckClass(ovbase->ovclass, errorflag);
|
|
else
|
|
object = CClass_CheckClass(ovbase->ovclass, errorflag);
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
static void CClass_AllocVTable(TypeClass *tclass) {
|
|
OVClass *ovclass;
|
|
|
|
main_class = tclass;
|
|
ovclass = CClass_BuildOVClassTree(NULL, tclass, 0, 0);
|
|
cclass_root = ovclass;
|
|
CClass_OverrideOVClassTree(ovclass);
|
|
CClass_CheckClass(ovclass, 1);
|
|
CClass_AllocVTableRec(ovclass);
|
|
}
|
|
|
|
static Object *CClass_CheckVirtuals(TypeClass *tclass) {
|
|
OVClass *ovclass;
|
|
|
|
main_class = tclass;
|
|
ovclass = CClass_BuildOVClassTree(NULL, tclass, 0, 0);
|
|
cclass_root = ovclass;
|
|
CClass_OverrideOVClassTree(ovclass);
|
|
return CClass_CheckClass(ovclass, 0);
|
|
}
|
|
|
|
static void CClass_CheckVirtualBaseOverrides(OVClass *a, OVClass *b, Boolean flag) {
|
|
VClassList *vbase;
|
|
OVFunc *ovfunc;
|
|
OVClassBase *ovbase;
|
|
|
|
if (flag) {
|
|
for (ovfunc = b->vfuncs; ovfunc; ovfunc = ovfunc->next) {
|
|
if (ovfunc->ovfC) {
|
|
cclass_ovbase = NULL;
|
|
CError_ASSERT(2040, CClass_IsBaseOf(b, ovfunc->ovc8));
|
|
|
|
if (cclass_ovbase) {
|
|
for (vbase = a->tclass->vbases; vbase; vbase = vbase->next) {
|
|
if (vbase->base == cclass_ovbase->tclass)
|
|
break;
|
|
}
|
|
|
|
CError_ASSERT(2047, vbase);
|
|
vbase->has_override = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (ovbase = b->bases; ovbase; ovbase = ovbase->next) {
|
|
CClass_CheckVirtualBaseOverrides(a, ovbase->ovclass, flag || ovbase->is_virtual);
|
|
}
|
|
}
|
|
|
|
static void CClass_CheckHideVirtual(OVClass *a, OVClass *b) {
|
|
OVClassBase *ovbase;
|
|
OVFunc *ovfunc;
|
|
Object *foundObject;
|
|
Boolean isAlias;
|
|
Object *object;
|
|
CScopeObjectIterator iter;
|
|
|
|
if (a != b) {
|
|
for (ovfunc = b->vfuncs; ovfunc; ovfunc = ovfunc->next) {
|
|
if (ovfunc->ovc8 != a) {
|
|
foundObject = NULL;
|
|
isAlias = 0;
|
|
CScope_InitObjectIterator(&iter, a->tclass->nspace);
|
|
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (object->name == ovfunc->obj->name && IS_TYPE_FUNC(object->type)) {
|
|
if (object->datatype != DALIAS) {
|
|
if (!(TYPE_FUNC(object->type)->flags & FUNC_FLAGS_20))
|
|
foundObject = object;
|
|
} else {
|
|
if (object->u.alias.object == ovfunc->obj) {
|
|
// don't show a warning if this is just an alias to the base function
|
|
isAlias = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (foundObject && !isAlias)
|
|
CError_Warning(CErrorStr225, foundObject, ovfunc->obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (ovbase = b->bases; ovbase; ovbase = ovbase->next)
|
|
CClass_CheckHideVirtual(a, ovbase->ovclass);
|
|
}
|
|
|
|
void CClass_CheckOverrides(TypeClass *tclass) {
|
|
OVClass *tree;
|
|
Object *object;
|
|
ObjectList *objlist;
|
|
ClassList *base;
|
|
VClassList *vbase;
|
|
int i;
|
|
CScopeObjectIterator iter;
|
|
|
|
i = 0;
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
base->offset = i;
|
|
base->voffset = i;
|
|
i++;
|
|
}
|
|
for (vbase = tclass->vbases; vbase; vbase = vbase->next) {
|
|
vbase->offset = i;
|
|
vbase->voffset = i;
|
|
i++;
|
|
}
|
|
|
|
main_class = tclass;
|
|
tree = CClass_BuildOVClassTree(NULL, tclass, 0, 0);
|
|
cclass_root = tree;
|
|
|
|
CClass_OverrideOVClassTree(tree);
|
|
if (CClass_CheckClass(tree, 0))
|
|
tclass->flags |= CLASS_FLAGS_ABSTRACT;
|
|
|
|
if (copts.warn_hidevirtual)
|
|
CClass_CheckHideVirtual(tree, tree);
|
|
|
|
if (tclass->flags & CLASS_FLAGS_8000)
|
|
CClass_CheckVirtualBaseOverrides(tree, tree, 0);
|
|
|
|
objlist = NULL;
|
|
CScope_InitObjectIterator(&iter, tclass->nspace);
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (object->datatype == DVFUNC) {
|
|
CError_ASSERT(2175, IS_TYPE_FUNC(object->type));
|
|
if (TYPE_FUNC(object->type)->flags & FUNC_FLAGS_400000)
|
|
objlist = CClass_DeclareCovariantFuncs(objlist, object, tclass);
|
|
}
|
|
}
|
|
|
|
while (objlist) {
|
|
CScope_AddObject(tclass->nspace, objlist->object->name, OBJ_BASE(objlist->object));
|
|
objlist = objlist->next;
|
|
}
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
base->offset = 0;
|
|
base->voffset = 0;
|
|
}
|
|
for (vbase = tclass->vbases; vbase; vbase = vbase->next) {
|
|
vbase->offset = 0;
|
|
vbase->voffset = 0;
|
|
}
|
|
}
|
|
|
|
static void CClass_FindDominator(TypeClass *tclass1, SInt32 offset1, Object *object1, TypeClass *tclass2, SInt32 offset2, TypeClass *tclass3) {
|
|
Object *object;
|
|
ClassList *base;
|
|
CScopeObjectIterator iter;
|
|
|
|
CScope_InitObjectIterator(&iter, tclass1->nspace);
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (
|
|
object->name == cclass_dominator_vobject->name &&
|
|
object->datatype == DVFUNC &&
|
|
CClass_GetOverrideKind(TYPE_FUNC(cclass_dominator_vobject->type), TYPE_FUNC(object->type), 0)
|
|
)
|
|
{
|
|
if (object == cclass_dominator_vobject && offset1 == cclass_dominator_voffset) {
|
|
if (object1) {
|
|
if (cclass_dominator_oobject && cclass_dominator_ooffset != offset2) {
|
|
if (CClass_ClassDominates(cclass_dominator_oclass, tclass2))
|
|
return;
|
|
if (!CClass_ClassDominates(tclass2, cclass_dominator_oclass)) {
|
|
cclass_dominator_eobject = object1;
|
|
cclass_vbase = tclass3;
|
|
return;
|
|
}
|
|
}
|
|
|
|
cclass_dominator_oobject = object1;
|
|
cclass_dominator_ooffset = offset2;
|
|
cclass_dominator_oclass = tclass2;
|
|
cclass_dominator_eobject = NULL;
|
|
cclass_vbase = tclass3;
|
|
}
|
|
return;
|
|
} else {
|
|
if (!object1) {
|
|
object1 = object;
|
|
tclass2 = tclass1;
|
|
offset2 = offset1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (base = tclass1->bases; base; base = base->next) {
|
|
if (base->base->vtable) {
|
|
if (!base->is_virtual) {
|
|
CClass_FindDominator(base->base, offset1 + base->offset, object1, tclass2, offset2, tclass3);
|
|
} else {
|
|
SInt32 vboffset = CClass_VirtualBaseOffset(main_class, base->base);
|
|
CClass_FindDominator(base->base, vboffset, object1, tclass2, offset2, base->base);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void CClass_ConstructVTable(TypeClass *tclass, SInt32 voffset, SInt32 offset, TypeClass *baseclass) {
|
|
Object *object;
|
|
Object *thunkobject;
|
|
SInt32 newoffset;
|
|
SInt32 newvoffset;
|
|
ClassList *base;
|
|
SInt32 thunkoffset;
|
|
OLinkList *olink;
|
|
CScopeObjectIterator iter;
|
|
|
|
CScope_InitObjectIterator(&iter, tclass->nspace);
|
|
while (1) {
|
|
if (!(object = OBJECT(CScope_NextObjectIteratorObject(&iter))))
|
|
break;
|
|
|
|
if (object->datatype == DVFUNC) {
|
|
newoffset = voffset + TYPE_METHOD(object->type)->x1E + CABI_GetVTableOffset(tclass);
|
|
CError_ASSERT(2288, newoffset < vtable_data_size);
|
|
|
|
if (!vtable_object_data[newoffset]) {
|
|
cclass_dominator_vobject = object;
|
|
cclass_dominator_voffset = offset;
|
|
cclass_dominator_oobject = NULL;
|
|
cclass_dominator_ooffset = offset;
|
|
CClass_FindDominator(main_class, 0, NULL, NULL, 0, NULL);
|
|
|
|
if (cclass_dominator_oobject) {
|
|
if (cclass_dominator_eobject)
|
|
CError_Error(CErrorStr251, main_class, 0, cclass_dominator_vobject, cclass_dominator_oobject, cclass_dominator_eobject);
|
|
} else {
|
|
cclass_dominator_oobject = object;
|
|
cclass_vbase = NULL;
|
|
}
|
|
|
|
vtable_object_data[newoffset] = 1;
|
|
|
|
if (!(TYPE_FUNC(cclass_dominator_oobject->type)->flags & FUNC_FLAGS_8)) {
|
|
if (!check_pures) {
|
|
thunkobject = cclass_dominator_oobject;
|
|
if ((thunkoffset = cclass_dominator_ooffset - offset)) {
|
|
if (cclass_vbase && (main_class->flags & CLASS_FLAGS_8000)) {
|
|
thunkobject = CClass_ThunkObject(cclass_dominator_oobject, thunkoffset, 0,
|
|
CABI_GetCtorOffsetOffset(cclass_vbase, tclass));
|
|
} else {
|
|
thunkobject = CClass_ThunkObject(cclass_dominator_oobject, thunkoffset, 0, -1);
|
|
}
|
|
}
|
|
|
|
olink = lalloc(sizeof(OLinkList));
|
|
olink->next = vtable_object_links;
|
|
olink->obj = thunkobject;
|
|
olink->offset = newoffset;
|
|
olink->somevalue = 0;
|
|
vtable_object_links = olink;
|
|
}
|
|
} else {
|
|
found_pure = cclass_dominator_oobject;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (base = tclass->bases; base; base = base->next) {
|
|
if (base->base->vtable) {
|
|
if (base->is_virtual) {
|
|
newoffset = CClass_VirtualBaseOffset(main_class, base->base);
|
|
newvoffset = CClass_VirtualBaseVTableOffset(main_class, base->base);
|
|
CClass_ConstructVTable(
|
|
base->base,
|
|
newvoffset,
|
|
newoffset,
|
|
base->base
|
|
);
|
|
} else {
|
|
CClass_ConstructVTable(
|
|
base->base,
|
|
voffset + base->voffset,
|
|
offset + base->offset,
|
|
baseclass
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CClass_ClassDefaultFuncAction(TypeClass *tclass) {
|
|
}
|
|
|
|
void CClass_ClassAction(TypeClass *tclass) {
|
|
if (tclass->sominfo) {
|
|
CSOM_GenerateClassStructures(tclass);
|
|
} else if (tclass->vtable) {
|
|
vtable_data_size = tclass->vtable->size;
|
|
vtable_object_data = lalloc(vtable_data_size);
|
|
memclrw(vtable_object_data, vtable_data_size);
|
|
|
|
main_class = tclass;
|
|
vtable_object_links = NULL;
|
|
found_pure = NULL;
|
|
check_pures = 0;
|
|
|
|
CClass_AllocVTable(tclass);
|
|
|
|
memclrw(vtable_object_data, vtable_data_size);
|
|
if (copts.useRTTI && !(tclass->flags & (CLASS_FLAGS_10 | CLASS_FLAGS_2000)))
|
|
vtable_object_links = CRTTI_ConstructVTableHeaders(tclass, vtable_object_data, vtable_object_links);
|
|
|
|
CError_ASSERT(2492, tclass->vtable->object->type->size == tclass->vtable->size);
|
|
CInit_DeclareData(tclass->vtable->object, vtable_object_data, vtable_object_links, tclass->vtable->size);
|
|
}
|
|
}
|
|
|
|
void CClass_MakeStaticActionClass(TypeClass *tclass) {
|
|
if (tclass->vtable) {
|
|
tclass->vtable->object->sclass = TK_STATIC;
|
|
tclass->vtable->object->qual |= Q_20000;
|
|
if (!(tclass->vtable->object->flags & OBJECT_FLAGS_2)) {
|
|
CParser_NewCallBackAction(tclass->vtable->object, tclass);
|
|
} else if (cparamblkptr->isPrecompiling != 1) {
|
|
CParser_NewClassAction(tclass);
|
|
}
|
|
}
|
|
}
|
|
|
|
Object *CClass_CheckPures(TypeClass *tclass) {
|
|
return CClass_CheckVirtuals(tclass);
|
|
}
|
|
|
|
void CClass_MemberDef(Object *obj, TypeClass *tclass) {
|
|
switch (tclass->action) {
|
|
case CLASS_ACTION_0:
|
|
break;
|
|
case CLASS_ACTION_1:
|
|
if (IS_TYPE_FUNC(obj->type) && (TYPE_FUNC(obj->type)->flags & FUNC_FLAGS_4)) {
|
|
if (obj->qual & Q_INLINE) {
|
|
if (tclass->sominfo)
|
|
CError_Error(CErrorStr280);
|
|
tclass->action = CLASS_ACTION_0;
|
|
CClass_MakeStaticActionClass(tclass);
|
|
} else if (cparamblkptr->isPrecompiling != 1) {
|
|
CParser_NewClassAction(tclass);
|
|
}
|
|
}
|
|
break;
|
|
case CLASS_ACTION_2:
|
|
CError_FATAL(2682);
|
|
break;
|
|
case CLASS_ACTION_3:
|
|
break;
|
|
default:
|
|
CError_FATAL(2701);
|
|
}
|
|
}
|
|
|
|
Object *CClass_ThisSelfObject(void) {
|
|
ObjectList *list;
|
|
|
|
if (cscope_currentfunc && cscope_currentclass) {
|
|
if (cscope_currentclass->objcinfo) {
|
|
for (list = arguments; list; list = list->next) {
|
|
if (list->object->name == self_name_node)
|
|
return list->object;
|
|
}
|
|
CError_Error(CErrorStr301);
|
|
} else {
|
|
if (cscope_is_member_func) {
|
|
for (list = arguments; list; list = list->next) {
|
|
if (list->object->name == this_name_node)
|
|
return list->object;
|
|
}
|
|
}
|
|
CError_Error(CErrorStr189);
|
|
}
|
|
}
|
|
|
|
CError_Error(copts.cplusplus ? CErrorStr189 : CErrorStr301);
|
|
return NULL;
|
|
}
|
|
|
|
ENode *CClass_CreateThisSelfExpr(void) {
|
|
Object *object;
|
|
ENode *expr;
|
|
|
|
if (!(object = CClass_ThisSelfObject()))
|
|
return NULL;
|
|
|
|
expr = create_objectrefnode(object);
|
|
expr->rtype = CDecl_NewPointerType(TYPE(cscope_currentclass));
|
|
expr = makemonadicnode(expr, EINDIRECT);
|
|
expr->data.monadic->rtype = CDecl_NewPointerType(expr->rtype);
|
|
|
|
return expr;
|
|
}
|
|
|
|
static AccessType CClass_BaseMemberAccess(BClassList *path, AccessType access) {
|
|
ClassList *base;
|
|
|
|
if (path->next) {
|
|
access = CClass_BaseMemberAccess(path->next, access);
|
|
switch (access) {
|
|
case ACCESSPRIVATE:
|
|
case ACCESSNONE:
|
|
return ACCESSNONE;
|
|
}
|
|
|
|
for (base = TYPE_CLASS(path->type)->bases; ; base = base->next) {
|
|
if (!base)
|
|
return ACCESSNONE;
|
|
|
|
if (base->base == TYPE_CLASS(path->next->type)) {
|
|
switch (base->access) {
|
|
case ACCESSNONE:
|
|
access = ACCESSNONE;
|
|
break;
|
|
case ACCESSPROTECTED:
|
|
if (access == ACCESSPUBLIC)
|
|
access = ACCESSPROTECTED;
|
|
break;
|
|
case ACCESSPRIVATE:
|
|
if (access == ACCESSPRIVATE)
|
|
access = ACCESSNONE;
|
|
else
|
|
access = ACCESSPRIVATE;
|
|
break;
|
|
case ACCESSPUBLIC:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return access;
|
|
}
|
|
|
|
static Boolean CClass_CanAccess(BClassList *path, AccessType access) {
|
|
AccessType access2;
|
|
BClassList *scan;
|
|
BClassList *next;
|
|
TypeClass *prevclass;
|
|
TypeClass *tclass;
|
|
ClassList *base;
|
|
ClassFriend *cfriend;
|
|
|
|
tclass = TYPE_CLASS(path->type);
|
|
access2 = access;
|
|
if ((scan = path->next)) {
|
|
if (access2 != ACCESSPRIVATE) {
|
|
prevclass = tclass;
|
|
while (scan) {
|
|
for (base = prevclass->bases; base; base = base->next) {
|
|
if (base->base == TYPE_CLASS(scan->type))
|
|
break;
|
|
}
|
|
|
|
if (!base)
|
|
return 0;
|
|
|
|
switch (base->access) {
|
|
case ACCESSNONE:
|
|
access2 = ACCESSNONE;
|
|
break;
|
|
case ACCESSPROTECTED:
|
|
if (access2 == ACCESSPUBLIC)
|
|
access2 = ACCESSPROTECTED;
|
|
break;
|
|
case ACCESSPRIVATE:
|
|
if (access2 == ACCESSPRIVATE)
|
|
access2 = ACCESSNONE;
|
|
else
|
|
access2 = ACCESSPRIVATE;
|
|
break;
|
|
case ACCESSPUBLIC:
|
|
break;
|
|
}
|
|
|
|
prevclass = TYPE_CLASS(scan->type);
|
|
scan = scan->next;
|
|
}
|
|
} else {
|
|
access2 = ACCESSNONE;
|
|
}
|
|
}
|
|
|
|
if (access2 == ACCESSPUBLIC)
|
|
return 1;
|
|
|
|
if (access2 != ACCESSNONE) {
|
|
if (cscope_currentclass == tclass)
|
|
return 1;
|
|
if (cobjc_currentclass == tclass)
|
|
return 1;
|
|
|
|
for (cfriend = tclass->friends; cfriend; cfriend = cfriend->next) {
|
|
if (cfriend->isclass) {
|
|
if (cfriend->u.theclass == cscope_currentclass)
|
|
return 1;
|
|
} else {
|
|
if (cfriend->u.obj == cscope_currentfunc)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (scan = path; scan->next; scan = scan->next) {
|
|
if (CClass_CanAccess(scan->next, access)) {
|
|
if ((next = scan->next->next) || access != ACCESSPUBLIC) {
|
|
scan->next->next = NULL;
|
|
if (CClass_CanAccess(path, ACCESSPUBLIC)) {
|
|
scan->next->next = next;
|
|
return 1;
|
|
}
|
|
scan->next->next = next;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CClass_CheckPathAccess(BClassList *path, Object *obj, AccessType access) {
|
|
if (!CClass_CanAccess(path, access)) {
|
|
if (path && obj)
|
|
CError_Error(CErrorStr381, path->type, 0, obj);
|
|
else
|
|
CError_Error(CErrorStr187);
|
|
}
|
|
}
|
|
|
|
static BClassList *CClass_PathCleanup(BClassList *path, TypeClass *tclass) {
|
|
BClassList *first;
|
|
ClassList *base;
|
|
|
|
first = path;
|
|
|
|
while (1) {
|
|
if (!path->next) {
|
|
if (!tclass)
|
|
return first;
|
|
if (path->type == TYPE(tclass))
|
|
return first;
|
|
return NULL;
|
|
}
|
|
|
|
if (path->type != path->next->type) {
|
|
for (base = TYPE_CLASS(path->type)->bases; base; base = base->next) {
|
|
if (base->base == TYPE_CLASS(path->next->type))
|
|
break;
|
|
}
|
|
|
|
if (base) {
|
|
path = path->next;
|
|
} else {
|
|
first = path = path->next;
|
|
}
|
|
} else {
|
|
path->next = path->next->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CClass_CheckStaticAccess(BClassList *path, TypeClass *tclass, AccessType access) {
|
|
ClassFriend *cfriend;
|
|
|
|
if (path) {
|
|
path = CClass_PathCleanup(path, tclass);
|
|
if (path && path->next) {
|
|
if (!CClass_CanAccess(path, access))
|
|
CError_Error(CErrorStr187);
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (access) {
|
|
case ACCESSPUBLIC:
|
|
return;
|
|
case ACCESSPRIVATE:
|
|
case ACCESSPROTECTED:
|
|
if (tclass == cscope_currentclass)
|
|
return;
|
|
|
|
for (cfriend = tclass->friends; cfriend; cfriend = cfriend->next) {
|
|
if (cfriend->isclass) {
|
|
if (cfriend->u.theclass == cscope_currentclass)
|
|
return;
|
|
} else {
|
|
if (cfriend->u.obj == cscope_currentfunc)
|
|
return;
|
|
}
|
|
}
|
|
case ACCESSNONE:
|
|
CError_Warning(CErrorStr187);
|
|
return;
|
|
default:
|
|
CError_FATAL(3013);
|
|
}
|
|
}
|
|
|
|
void CClass_CheckObjectAccess(BClassList *path, Object *obj) {
|
|
short depth;
|
|
Boolean isambigbase;
|
|
|
|
if (obj->nspace && obj->nspace->theclass) {
|
|
if (!path && cscope_currentclass)
|
|
path = CClass_GetBasePath(cscope_currentclass, obj->nspace->theclass, &depth, &isambigbase);
|
|
|
|
CClass_CheckStaticAccess(path, obj->nspace->theclass, obj->access);
|
|
}
|
|
}
|
|
|
|
void CClass_CheckEnumAccess(BClassList *path, ObjEnumConst *objec) {
|
|
if (path) {
|
|
if ((path = CClass_PathCleanup(path, NULL))) {
|
|
if (!CClass_CanAccess(path, objec->access))
|
|
CError_Error(CErrorStr187);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (
|
|
objec->access != ACCESSPUBLIC &&
|
|
IS_TYPE_ENUM(objec->type) &&
|
|
TYPE_ENUM(objec->type)->nspace &&
|
|
TYPE_ENUM(objec->type)->nspace->theclass
|
|
)
|
|
CClass_CheckStaticAccess(NULL, TYPE_ENUM(objec->type)->nspace->theclass, objec->access);
|
|
}
|
|
|
|
static Type *CClass_PointerTypeCopy(Type *type) {
|
|
Type *copy;
|
|
|
|
switch (type->type) {
|
|
case TYPEPOINTER:
|
|
case TYPEARRAY:
|
|
copy = galloc(sizeof(TypePointer));
|
|
*TYPE_POINTER(copy) = *TYPE_POINTER(type);
|
|
TPTR_TARGET(copy) = CClass_PointerTypeCopy(TPTR_TARGET(copy));
|
|
return copy;
|
|
case TYPEMEMBERPOINTER:
|
|
copy = galloc(sizeof(TypeMemberPointer));
|
|
*TYPE_MEMBER_POINTER(copy) = *TYPE_MEMBER_POINTER(type);
|
|
return copy;
|
|
default:
|
|
return type;
|
|
}
|
|
}
|
|
|
|
Type *CClass_CombineClassAccessQualifiers(Type *type, UInt32 qual1, UInt32 qual2, UInt32 *outflags) {
|
|
Type *inner;
|
|
|
|
qual2 = qual2 & (Q_CONST | Q_VOLATILE);
|
|
if (qual1 & Q_MUTABLE)
|
|
qual2 &= ~Q_CONST;
|
|
qual1 = qual1 & (Q_CONST | Q_VOLATILE);
|
|
|
|
inner = type;
|
|
while (IS_TYPE_ARRAY(inner))
|
|
inner = TPTR_TARGET(inner);
|
|
|
|
switch (inner->type) {
|
|
case TYPEMEMBERPOINTER:
|
|
case TYPEPOINTER:
|
|
if (qual2) {
|
|
type = CClass_PointerTypeCopy(type);
|
|
inner = type;
|
|
while (IS_TYPE_ARRAY(inner))
|
|
inner = TPTR_TARGET(inner);
|
|
|
|
switch (inner->type) {
|
|
case TYPEPOINTER:
|
|
TPTR_QUAL(inner) |= qual2;
|
|
break;
|
|
case TYPEMEMBERPOINTER:
|
|
TYPE_MEMBER_POINTER(inner)->qual |= qual2;
|
|
break;
|
|
default:
|
|
CError_FATAL(3125);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
qual1 |= qual2;
|
|
}
|
|
|
|
*outflags = qual1;
|
|
return type;
|
|
}
|
|
|
|
static void CClass_OptimizeBitFieldAccess(Type **ptype, SInt32 *poffset) {
|
|
Type *innertype;
|
|
TypeBitfield *newtype;
|
|
short i;
|
|
|
|
innertype = TYPE_BITFIELD(*ptype)->bitfieldtype;
|
|
if (TYPE_BITFIELD(*ptype)->unkB == 8) {
|
|
switch (TYPE_BITFIELD(*ptype)->unkA) {
|
|
case 0:
|
|
i = 0;
|
|
break;
|
|
case 8:
|
|
i = 1;
|
|
break;
|
|
case 16:
|
|
i = 2;
|
|
break;
|
|
case 24:
|
|
i = 3;
|
|
break;
|
|
default:
|
|
i = -1;
|
|
break;
|
|
}
|
|
|
|
if (i >= 0) {
|
|
if (innertype->size != 1) {
|
|
if (is_unsigned(TYPE_BITFIELD(*ptype)->bitfieldtype))
|
|
*ptype = TYPE(&stunsignedchar);
|
|
else
|
|
*ptype = TYPE(&stsignedchar);
|
|
} else {
|
|
*ptype = innertype;
|
|
}
|
|
*poffset += i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (TYPE_BITFIELD(*ptype)->unkB == 16) {
|
|
switch (TYPE_BITFIELD(*ptype)->unkA) {
|
|
case 0:
|
|
i = 0;
|
|
break;
|
|
case 16:
|
|
i = 2;
|
|
break;
|
|
default:
|
|
i = -1;
|
|
break;
|
|
}
|
|
|
|
if (i >= 0) {
|
|
if (innertype->size != stsignedshort.size) {
|
|
if (is_unsigned(innertype))
|
|
*ptype = TYPE(&stunsignedshort);
|
|
else
|
|
*ptype = TYPE(&stsignedshort);
|
|
} else {
|
|
*ptype = innertype;
|
|
}
|
|
*poffset += i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (TYPE_BITFIELD(*ptype)->unkB == 32 && TYPE_BITFIELD(*ptype)->unkA == 0) {
|
|
if (innertype->size != stsignedlong.size) {
|
|
if (is_unsigned(innertype))
|
|
*ptype = TYPE(&stunsignedlong);
|
|
else
|
|
*ptype = TYPE(&stsignedlong);
|
|
} else {
|
|
*ptype = innertype;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((*ptype)->size != stsignedchar.size) {
|
|
i = TYPE_BITFIELD(*ptype)->unkA + TYPE_BITFIELD(*ptype)->unkB - 1;
|
|
|
|
if (TYPE_BITFIELD(*ptype)->unkB < 8 && (TYPE_BITFIELD(*ptype)->unkA & 0xFFF8) == (i & 0xFFF8)) {
|
|
newtype = galloc(sizeof(TypeBitfield));
|
|
*newtype = *TYPE_BITFIELD(*ptype);
|
|
*ptype = TYPE(newtype);
|
|
|
|
i = 0;
|
|
if (newtype->unkA >= 8)
|
|
i = 1;
|
|
if (newtype->unkA >= 16)
|
|
i = 2;
|
|
if (newtype->unkA >= 24)
|
|
i = 3;
|
|
*poffset += i;
|
|
newtype->unkA -= 8 * i;
|
|
|
|
newtype->bitfieldtype = is_unsigned(innertype) ? TYPE(&stunsignedchar) : TYPE(&stsignedchar);
|
|
newtype->size = newtype->bitfieldtype->size;
|
|
return;
|
|
}
|
|
|
|
if ((*ptype)->size != stsignedshort.size) {
|
|
if (TYPE_BITFIELD(*ptype)->unkB < 16 && (TYPE_BITFIELD(*ptype)->unkA & 0xFFF0) == (i & 0xFFF0)) {
|
|
newtype = galloc(sizeof(TypeBitfield));
|
|
*newtype = *TYPE_BITFIELD(*ptype);
|
|
*ptype = TYPE(newtype);
|
|
|
|
i = 0;
|
|
if (newtype->unkA >= 16)
|
|
i = stsignedshort.size;
|
|
*poffset += i;
|
|
newtype->unkA -= 8 * i;
|
|
|
|
newtype->bitfieldtype = is_unsigned(innertype) ? TYPE(&stunsignedshort) : TYPE(&stsignedshort);
|
|
newtype->size = newtype->bitfieldtype->size;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ENode *CClass_AccessMember(ENode *classexpr, Type *type, UInt32 qual, SInt32 offset) {
|
|
Type *innertype;
|
|
UInt32 flags;
|
|
|
|
innertype = NULL;
|
|
|
|
if (IS_TYPE_CLASS(classexpr->rtype) && (TYPE_CLASS(classexpr->rtype)->flags & CLASS_FLAGS_1)) {
|
|
classexpr = makemonadicnode(classexpr, EINDIRECT);
|
|
classexpr->data.monadic->rtype = CDecl_NewPointerType(classexpr->rtype);
|
|
}
|
|
|
|
if (IS_TYPE_BITFIELD(type)) {
|
|
innertype = TYPE_BITFIELD(type)->bitfieldtype;
|
|
CClass_OptimizeBitFieldAccess(&type, &offset);
|
|
}
|
|
|
|
if (offset && !canadd(classexpr->data.monadic, offset)) {
|
|
classexpr->data.monadic = makediadicnode(
|
|
classexpr->data.monadic,
|
|
intconstnode(TYPE(&stunsignedlong), offset),
|
|
EADD);
|
|
optimizecomm(classexpr->data.monadic);
|
|
}
|
|
|
|
if (innertype) {
|
|
if (IS_TYPE_BITFIELD(type)) {
|
|
classexpr->data.monadic = makemonadicnode(classexpr->data.monadic, EBITFIELD);
|
|
classexpr->data.monadic->rtype = type;
|
|
classexpr->rtype = TYPE_BITFIELD(type)->bitfieldtype;
|
|
} else {
|
|
classexpr->rtype = type;
|
|
}
|
|
} else {
|
|
classexpr->rtype = type;
|
|
}
|
|
|
|
classexpr->rtype = CClass_CombineClassAccessQualifiers(classexpr->rtype, qual, ENODE_QUALS(classexpr), &flags);
|
|
classexpr->flags = flags;
|
|
return classexpr;
|
|
}
|