#include #include "AST.h" #include "FieldDecl.h" #include "FlattenContext.h" #include "MethodDecl.h" #include "compiler.h" #include "decls.h" #include "errors.h" #include "code-util.h" /* Utilities */ llist *addToFront(TreeNode *, TreeListNode *); TreeListNode *remove(TreeListNode *, TreeNode *); extern NameNode *buildName(const char *fullname, SourcePosn pos); int TreeNode::incLocalCount() { undefined("incLocalCount"); return -1; } int ClassDeclNode::incLocalCount() { return ++_localCount; } int InterfaceDeclNode::incLocalCount() { return ++_localCount; } int TreeNode::incAnonCount() { undefined("incAnonCount"); return -1; } int ClassDeclNode::incAnonCount() { return ++_anonCount; } int InterfaceDeclNode::incAnonCount() { return ++_anonCount; } // Use local/anon count of outermost enclosing class to avoid name conflicts with // intermediate enclosing classes. static int getLocalAnonCount(TypeDeclNode *cclass, bool anon) { if (cclass->enclosingType()) return getLocalAnonCount(cclass->enclosingType(), anon); else return anon ? cclass->incAnonCount() : cclass->incLocalCount(); } static string *getNameString(TypeDeclNode *cclass) { if (!cclass->enclosingType() && !isTemplateInstanceDeclNode(cclass->parent())) return new string("nc_" + int2string(cclass->simpName()->ident()->length()) + *cclass->simpName()->ident()); else if (!isTemplateInstanceDeclNode(cclass->parent())) { string name = ""; if (cclass->flags() & Common::LocalClass) name = ((cclass->flags() & Common::AnonymousClass) ? "ac_" : "lc_" + int2string(cclass->classNum())); name = name + *cclass->declaredName()->ident(); return new string(*getNameString(cclass->enclosingType()) + int2string(name.length()) + name); } else { // Template has already been instantiated and has package resolved, so OK // to ask for decl. string name = cclass->decl()->mangledName(); return new string("nc_" + name); } } static NameNode *renameNestedClass(TypeDeclNode *cclass) { const string *s1 = getNameString(cclass->enclosingType()); const string *s2 = cclass->simpName()->ident(); return buildName((*s1 + int2string(s2->length()) + *s2).c_str(), cclass->simpName()->position()); } // Sort of follow Java convention here by just giving the anon class a number. // Update: prepend with "ac_" to make demangling easier. static const string *nameAnonClass(int num) { string s = "ac_" + int2string(num); return intern(s); } static const string *renameLocalClass(const string *name, int num) { string s = "lc_" + int2string(num) + *name; return intern(s); } static bool containsField(TreeNode *members, TreeNode *var) { for (int i = 0; i < members->arity(); i++) { if (isFieldDeclNode(members->child(i)) && *members->child(i)->simpName()->ident() == *var->simpName()->ident()) return true; } return false; } static llist *buildFinalVarFields(llist *&finals, TreeNode *members) { llist *tmp = NULL; foriter (p, elements(finals), ListIterator) { if (!containsField(members, *p)) // fields override final vars tmp = cons(static_cast(new FieldDeclNode((*p)->dtype()->deepClone(), (*p)->simpName()->deepClone(), (Common::Modifiers) (TreeNode::Final | //TreeNode::Static | TreeNode::Private | TreeNode::CompilerGenerated), TreeNode::omitted, members->position())), tmp); // Semantics of such a field are mostly like an instance final field. // The only exception is that it can be used in the initializer for a // static compile-time constant. } return dreverse(tmp); } // An unknown type. TypeNode *UnknownType; // Produces a parameter node using the given number, with undefined type. static TreeNode *paramNode(int n, SourcePosn posn) { if (UnknownType == NULL) { UnknownType = new TypeNameNode(new NameNode(TreeNode::omitted, intern("tiUnknown"), UnknownClassDecl, NoSourcePosition)); } string pn = "nc_" + int2string(int2string(n).length() + 1) + "_" + int2string(n); NameNode *name = buildName(pn.c_str(), posn); return new ParameterNode(true, UnknownType, name, posn); } // Produces an object node using the given number. static TreeNode *objectNode(int n, SourcePosn posn) { string pn = "nc_" + int2string(int2string(n).length() + 1) + "_" + int2string(n); NameNode *name = buildName(pn.c_str(), posn); return new ObjectNode(name, posn); } // Rewrite the constructor for a qualified anonymous class. void rewriteQualifiedAnonymousConstructor(TreeNode *cn, TypeNode *qn, bool rewriteSuper) { llist *lst1 = appendTreeList(cn->params(), NULL); TreeNode *param = paramNode(cn->params()->arity(), cn->position()); param->dtype(qn->deepClone()); lst1 = cons(param, lst1); if (rewriteSuper) { // prevents spurious error messages TreeNode *obj = objectNode(cn->params()->arity(), cn->position()); cn->constructorCall()->qualifier(obj); } cn->params(new TreeListNode(lst1, cn->params()->position())); } /* A default constructor for the given anonymous class, */ /* as it would be produced by the parser, had it been written */ /* explicitly: public polyshared Foo() { super(); } */ static TreeNode* defaultAnonConstructor (TreeNode* cl, TreeListNode *exprs) { unsigned flags = cl->flags() & Common::Public | Common::CompilerGenerated; /* PR512: default constructors are shared if (!cl->decl()->asType()->isImmutable()) flags |= Common::PolysharedQ; */ llist *lst1 = NULL; llist *lst2 = NULL; for (int i = exprs->arity() - 1; i >= 0; i--) { lst1 = cons(paramNode(i, cl->position()), lst1); lst2 = cons(objectNode(i, cl->position()), lst2); } // The throws list gets rewritten in field resolution. The Java spec states // that it should inherit the list from its super constructor. return new ConstructorDeclNode ((Common::Modifiers) flags, lst1, new NameNode(TreeNode::omitted, cl->simpName()->ident(), NULL, cl->position()), NULL, new SuperConstructorCallNode(Common::CompilerGenerated, TreeNode::omitted, lst2, NULL, cl->position()), new BlockNode(NULL, NULL, cl->position()), TreeNode::omitted, cl->position()); } // Pulls all nested classes to the top level. void TreeNode::flattenClasses(FlattenContext *ctx) { foriter (p, allChildren(), ChildIter) (*p)->flattenClasses(ctx); } void CompileUnitNode::flattenClasses(FlattenContext *ctx) { if (ctx == NULL) { llist *finals = NULL; ctx = new FlattenContext(finals); ctx->toplevel = this; } types()->flattenClasses(ctx); } void TemplateDeclNode::flattenClasses(FlattenContext *ctx) { if (ctx->outer != NULL) { error() << "nested templates not supported" << endl; } // Nested classes in templates get flattened when the template is // instantiated. } void ClassDeclNode::flattenClasses(FlattenContext *ctx) { if (ctx->outer != NULL) { enclosingType(ctx->outer); declaredName((NameNode *) simpName()); if (ctx->crntBlock && !(flags() & LocalClass)) { // This is non-anonymous local class. Need to rename. flags((Common::Modifiers) (flags() | LocalClass | Private)); if (ctx->inStatic) flags((Common::Modifiers) (flags() | Static)); classNum(getLocalAnonCount(this, false)); NameNode *name = buildName(renameLocalClass(simpName()->ident(), classNum())->c_str(), simpName()->position()); simpName(name); } else if (flags() & AnonymousClass) { NameNode *name = buildName(nameAnonClass(classNum())->c_str(), simpName()->position()); simpName(name); } // Rename this class. simpName(renameNestedClass(this)); // Add reference to enclosing block if this is local class. if (flags() & LocalClass) { // Current block can be NULL if this is anonymous class declared in // a field initializer. // assert(ctx->crntBlock != NULL); enclosingBlock(ctx->crntBlock ? ctx->crntBlock : TreeNode::omitted); } if (!(flags() & LocalClass)) flags((Common::Modifiers) (flags() | MemberType)); // Remove outer class's member or enclosing block's child corresponding to this class. if (!(flags() & LocalClass)) enclosingType()->members(remove(enclosingType()->members(), this)); else if ((flags() & LocalClass) && !(flags() & AnonymousClass)) ctx->crntBlock->stmts(remove(ctx->crntBlock->stmts(), this)); // Check if this class has enclosing instance. if (!((ctx->outer->flags() & Interface) || (flags() & Static) || ctx->inStatic)) { hasEnclosingInstance(true); } // Add final vars if this is a local class. if (flags() & LocalClass) { llist *lst = buildFinalVarFields(ctx->finals, members()); finalVars(new TreeListNode(lst, members()->position())); // Evil pass by reference clobbered lst. lst = appendTreeList(static_cast(finalVars()), NULL); lst = appendTreeList(members(), lst); members(new TreeListNode(lst, members()->position())); } if ((flags() & AnonymousClass) && superClass()->absent()) { // This is qualified anonymous class. Superclass currently not known and // will be filled in when the enclosing class undergoes field resolution. // Defer all resolution until then. return; } else { // Add this class to the CompileUnitNode. llist *lst = cons(static_cast(this)); lst = appendTreeList((TreeListNode *) ctx->toplevel->types(), lst); ctx->toplevel->types(new TreeListNode(lst, ctx->toplevel->types()->position())); } } FlattenContext subCtx(*ctx); subCtx.outer = this; subCtx.inStatic = false; subCtx.finals = NULL; subCtx.crntBlock = NULL; members()->flattenClasses(&subCtx); } void InterfaceDeclNode::flattenClasses(FlattenContext *ctx) { flags((Common::Modifiers) (flags() | Interface)); if (ctx->outer) { enclosingType(ctx->outer); //if (!(flags() & LocalClass)) // Interfaces can't be local. flags((Common::Modifiers) (flags() | MemberType)); // Add this interface to the CompileUnitNode. llist *lst = cons(static_cast(this)); lst = appendTreeList((TreeListNode *) ctx->toplevel->types(), lst); ctx->toplevel->types(new TreeListNode(lst, ctx->toplevel->types()->position())); // Remove outer class's member corresponding to this class. enclosingType()->members(remove(enclosingType()->members(), this)); // Rename this interface. declaredName((NameNode *) simpName()); simpName(renameNestedClass(this)); } FlattenContext subCtx(*ctx); subCtx.outer = this; subCtx.inStatic = true; subCtx.crntBlock = NULL; members()->flattenClasses(&subCtx); } void AllocateNode::flattenClasses(FlattenContext *ctx) { if (!cbody()->absent()) { // This assumes that the given name is a class and not an interface. // However, class resolution detects and corrects the interface case. // Rewrite anon class as normal class. Common::Modifiers mods = (Common::Modifiers) (AnonymousClass | Private | CompilerGenerated | Final | LocalClass); if (ctx->inStatic) mods = (Common::Modifiers) (mods | Static); int classnum = getLocalAnonCount(ctx->outer, true); NameNode *name = buildName(int2string(classnum).c_str(), cbody()->position()); // Since attributes are not cloned, need to clone body if part of template // instantiation to allow old one to be used in other instantiations. TreeListNode *body = ctx->inTemplate ? static_cast(cbody()->deepClone()) : static_cast(cbody()); TreeListNode *tln = new TreeListNode(ctx->finals); // Evil pass by reference clobbered lst. ctx->finals = appendTreeList(tln, NULL); // hack // Leave super class omitted if this is qualified anonymous class. TreeNode *sclass = qualifier()->absent() ? dtype() : TreeNode::omitted; ClassDeclNode *cdn = new ClassDeclNode(mods, name, sclass, NULL, appendTreeList(body, NULL), NULL, TreeNode::omitted, name, false, tln, NULL, classnum, cbody()->position()); // Only replace type if this is not qualified anonymous class. If it is, // we need to keep the old type around until we can resolve it in field // resolution. if (qualifier()->absent()) dtype(new TypeNameNode(buildName(name->ident()->c_str(), dtype()->position()), dtype()->position())); // The allocate node needs access to the class declaration in order // to rewrite the types of the constructor arguments. cbody(cdn); cdn->flattenClasses(ctx); // Add in anonymous constructor here, taking into account args. cdn->members()->child(2, defaultAnonConstructor(cdn, args())); } region()->flattenClasses(ctx); qualifier()->flattenClasses(ctx); dtype()->flattenClasses(ctx); args()->flattenClasses(ctx); } void FieldDeclNode::flattenClasses(FlattenContext *ctx) { FlattenContext subCtx(*ctx); subCtx.inStatic = (flags() & Static); TreeNode::flattenClasses(&subCtx); } void MethodDeclNode::flattenClasses(FlattenContext *ctx) { FlattenContext subCtx(*ctx); subCtx.inStatic = (flags() & Static); TreeNode::flattenClasses(&subCtx); } void ConstructorDeclNode::flattenClasses(FlattenContext *ctx) { // Second check is so as not to rename illegally named constructors. if (ctx->outer->enclosingType() && *simpName()->ident() == *ctx->outer->declaredName()->ident()) { // Rename constructor. simpName(new NameNode(simpName()->qualifier(), ctx->outer->simpName()->ident(), simpName()->decl(), simpName()->position())); } FlattenContext subCtx(*ctx); subCtx.inStatic = false; TreeNode::flattenClasses(&subCtx); } void StaticInitNode::flattenClasses(FlattenContext *ctx) { FlattenContext subCtx(*ctx); subCtx.inStatic = true; TreeNode::flattenClasses(&subCtx); } void InstanceInitNode::flattenClasses(FlattenContext *ctx) { FlattenContext subCtx(*ctx); subCtx.inStatic = false; TreeNode::flattenClasses(&subCtx); } void ParameterNode::flattenClasses(FlattenContext *ctx) { if (isfinal()) ctx->finals = extend(ctx->finals, static_cast(this)); TreeNode::flattenClasses(ctx); } void VarDeclNode::flattenClasses(FlattenContext *ctx) { // Variable is not in scope in its initializer. TreeNode::flattenClasses(ctx); if (isfinal()) ctx->finals = extend(ctx->finals, static_cast(this)); } void BlockNode::flattenClasses(FlattenContext *ctx) { FlattenContext subCtx(*ctx); subCtx.crntBlock = this; TreeNode::flattenClasses(&subCtx); }