aspect InnerClasses { syn boolean BodyDecl.generate() = true; // The set of TypeDecls that has this TypeDecl as their directly enclosing TypeDecl. // I.e., NestedTypes, InnerTypes, AnonymousClasses, LocalClasses. private Collection TypeDecl.nestedTypes; public Collection TypeDecl.nestedTypes() { return nestedTypes != null ? nestedTypes : new HashSet(); } public void TypeDecl.addNestedType(TypeDecl typeDecl) { if(nestedTypes == null) nestedTypes = new ArrayList(); if(typeDecl != this) nestedTypes.add(typeDecl); } // The set of nested TypeDecls that are accessed in this TypeDecl private Collection TypeDecl.usedNestedTypes; public Collection TypeDecl.usedNestedTypes() { return usedNestedTypes != null ? usedNestedTypes : new HashSet(); } public void TypeDecl.addUsedNestedType(TypeDecl typeDecl) { if(usedNestedTypes == null) usedNestedTypes = new HashSet(); usedNestedTypes.add(typeDecl); } // no attribute since needed in phases when the AST has been modified public boolean TypeDecl.hasField(String name) { if(!memberFields(name).isEmpty()) return true; for(int i = 0; i < getNumBodyDecl(); i++) { if(getBodyDecl(i) instanceof FieldDeclaration) { FieldDeclaration decl = (FieldDeclaration)getBodyDecl(i); if(decl.name().equals(name)) return true; } } return false; } private TypeDecl VarAccess.fieldQualifierType() { if(hasPrevExpr()) return prevExpr().type(); TypeDecl typeDecl = hostType(); while(typeDecl != null && !typeDecl.hasField(name())) typeDecl = typeDecl.enclosingType(); if(typeDecl != null) return typeDecl; return decl().hostType(); } public boolean TypeDecl.hasMethod(String id) { if(!memberMethods(id).isEmpty()) return true; for(int i = 0; i < getNumBodyDecl(); i++) { if(getBodyDecl(i) instanceof MethodDecl) { MethodDecl decl = (MethodDecl)getBodyDecl(i); if(decl.name().equals(id)) return true; } } return false; } private TypeDecl MethodAccess.methodQualifierType() { if(hasPrevExpr()) return prevExpr().type(); TypeDecl typeDecl = hostType(); while(typeDecl != null && !typeDecl.hasMethod(name())) typeDecl = typeDecl.enclosingType(); if(typeDecl != null) return typeDecl; return decl().hostType(); } // Helpers for arrays inh TypeDecl ArrayInit.expectedType(); eq Program.getCompilationUnit(int i).expectedType() = null; eq ArrayCreationExpr.getArrayInit().expectedType() = type().componentType(); eq FieldDeclaration.getInit().expectedType() = type().componentType(); eq VariableDeclaration.getInit().expectedType() = type().componentType(); eq VariableDecl.getInit().expectedType() = null; eq ArrayInit.getInit().expectedType() = expectedType().componentType(); syn lazy int ArrayCreationExpr.numArrays() { int i = type().dimension(); Access a = getTypeAccess(); while(a instanceof ArrayTypeAccess && !(a instanceof ArrayTypeWithSizeAccess)) { i--; a = ((ArrayTypeAccess)a).getAccess(); } return i; } // Helpers for string promotion syn String TypeDecl.stringPromotion() = "java.lang.Object"; eq ReferenceType.stringPromotion() = isString() ? "java.lang.String" : super.stringPromotion(); eq PrimitiveType.stringPromotion() = name(); eq ByteType.stringPromotion() = "int"; eq ShortType.stringPromotion() = "int"; syn boolean ASTNode.isStringAdd() = false; eq AddExpr.isStringAdd() = type().isString(); syn boolean AddExpr.firstStringAddPart() = type().isString() && (!getLeftOperand().isStringAdd() || getLeftOperand().isConstant()); syn boolean AddExpr.lastStringAddPart() = !getParent().isStringAdd(); protected TypeDecl Access.superConstructorQualifier(TypeDecl targetEnclosingType) { TypeDecl enclosing = hostType(); while(!enclosing.instanceOf(targetEnclosingType)) enclosing = enclosing.enclosingType(); return enclosing; } public TypeDecl MethodAccess.superAccessorTarget() { TypeDecl targetDecl = prevExpr().type(); TypeDecl enclosing = hostType(); do { enclosing = enclosing.enclosingType(); } while (!enclosing.instanceOf(targetDecl)); return enclosing; } public int TypeDecl.accessorCounter = 0; private HashMap TypeDecl.accessorMap = null; public ASTNode TypeDecl.getAccessor(ASTNode source, String name) { ArrayList key = new ArrayList(2); key.add(source); key.add(name); if(accessorMap == null || !accessorMap.containsKey(key)) return null; return (ASTNode)accessorMap.get(key); } public void TypeDecl.addAccessor(ASTNode source, String name, ASTNode accessor) { ArrayList key = new ArrayList(2); key.add(source); key.add(name); if(accessorMap == null) accessorMap = new HashMap(); accessorMap.put(key, accessor); } public ASTNode TypeDecl.getAccessorSource(ASTNode accessor) { Iterator i = accessorMap.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); if (entry.getValue() == accessor) return (ASTNode) ((ArrayList) entry.getKey()).get(0); } return null; } public MethodDecl MethodDecl.createAccessor(TypeDecl methodQualifier) { MethodDecl m = (MethodDecl)methodQualifier.getAccessor(this, "method"); if(m != null) return m; int accessorIndex = methodQualifier.accessorCounter++; // add synthetic flag to modifiers Modifiers modifiers = new Modifiers(new List()); if(getModifiers().isStatic()) modifiers.addModifier(new Modifier("static")); modifiers.addModifier(new Modifier("synthetic")); modifiers.addModifier(new Modifier("public")); // build accessor declaration m = new MethodDecl( modifiers, type().createQualifiedAccess(), name() + "$access$" + accessorIndex, (List)getParameterList().fullCopy(), (List)getExceptionList().fullCopy(), new Opt( new Block( new List().add( createAccessorStmt() ) ) ) ); m = methodQualifier.addMemberMethod(m); methodQualifier.addAccessor(this, "method", m); return m; } private Stmt MethodDecl.createAccessorStmt() { List argumentList = new List(); for(int i = 0; i < getNumParameter(); i++) argumentList.add(new VarAccess(getParameter(i).name())); Access access = new MethodAccess(name(), argumentList); if(!isStatic()) access = new ThisAccess("this").qualifiesAccess(access); return isVoid() ? (Stmt) new ExprStmt(access) : new ReturnStmt(new Opt(access)); } public MethodDecl MethodDecl.createSuperAccessor(TypeDecl methodQualifier) { MethodDecl m = (MethodDecl)methodQualifier.getAccessor(this, "method_super"); if(m != null) return m; int accessorIndex = methodQualifier.accessorCounter++; List parameters = new List(); List args = new List(); for(int i = 0; i < getNumParameter(); i++) { parameters.add(getParameter(i).fullCopy()); args.add(new VarAccess(getParameter(i).name())); } Stmt stmt; if(type().isVoid()) stmt = new ExprStmt(new SuperAccess("super").qualifiesAccess(new MethodAccess(name(), args))); else stmt = new ReturnStmt(new Opt(new SuperAccess("super").qualifiesAccess(new MethodAccess(name(), args)))); m = new MethodDecl( new Modifiers(new List().add(new Modifier("synthetic"))), type().createQualifiedAccess(), name() + "$access$" + accessorIndex, parameters, new List(), new Opt( new Block( new List().add(stmt) ) ) ); m = methodQualifier.addMemberMethod(m); methodQualifier.addAccessor(this, "method_super", m); return m; } public MethodDecl FieldDeclaration.createAccessor(TypeDecl fieldQualifier) { MethodDecl m = (MethodDecl)fieldQualifier.getAccessor(this, "field_read"); if(m != null) return m; int accessorIndex = fieldQualifier.accessorCounter++; Modifiers modifiers = new Modifiers(new List()); modifiers.addModifier(new Modifier("static")); modifiers.addModifier(new Modifier("synthetic")); modifiers.addModifier(new Modifier("public")); List parameters = new List(); if(!isStatic()) parameters.add(new ParameterDeclaration(fieldQualifier.createQualifiedAccess(), "that")); m = new MethodDecl( modifiers, type().createQualifiedAccess(), "get$" + name() + "$access$" + accessorIndex, parameters, new List(), new Opt( new Block( new List().add( new ReturnStmt(createAccess()) ) ) ) ); m = hostType().addMemberMethod(m); fieldQualifier.addAccessor(this, "field_read", m); return m; } public MethodDecl FieldDeclaration.createAccessorWrite(TypeDecl fieldQualifier) { MethodDecl m = (MethodDecl)fieldQualifier.getAccessor(this, "field_write"); if(m != null) return m; int accessorIndex = fieldQualifier.accessorCounter++; Modifiers modifiers = new Modifiers(new List()); modifiers.addModifier(new Modifier("static")); modifiers.addModifier(new Modifier("synthetic")); modifiers.addModifier(new Modifier("public")); List parameters = new List(); if(!isStatic()) parameters.add(new ParameterDeclaration(fieldQualifier.createQualifiedAccess(), "that")); parameters.add(new ParameterDeclaration(type().createQualifiedAccess(), "value")); m = new MethodDecl( modifiers, type().createQualifiedAccess(), "set$" + name() + "$access$" + accessorIndex, parameters, new List(), new Opt( new Block( new List().add( new ExprStmt( new AssignSimpleExpr( createAccess(), new VarAccess("value") ) ) ).add( new ReturnStmt( new Opt( new VarAccess("value") ) ) ) ) ) ); m = hostType().addMemberMethod(m); fieldQualifier.addAccessor(this, "field_write", m); return m; } private Access FieldDeclaration.createAccess() { Access fieldAccess = new VarAccess(name()); return isStatic() ? fieldAccess : new VarAccess("that").qualifiesAccess(fieldAccess); } syn boolean VarAccess.requiresAccessor() { Variable v = decl(); if(!(v instanceof FieldDeclaration)) return false; FieldDeclaration f = (FieldDeclaration)v; if(f.isPrivate() && !hostType().hasField(v.name())) return true; if(f.isProtected() && !f.hostPackage().equals(hostPackage()) && !hostType().hasField(v.name())) return true; return false; } syn boolean ConstructorDecl.needsEnclosing() = hostType().needsEnclosing(); syn boolean ConstructorDecl.needsSuperEnclosing() = hostType().needsSuperEnclosing(); syn boolean TypeDecl.isAnonymousInNonStaticContext() { return isAnonymous() && !((ClassInstanceExpr)getParent().getParent()).unqualifiedScope().inStaticContext() && !inExplicitConstructorInvocation(); } syn boolean TypeDecl.needsEnclosing() { if(isAnonymous()) return isAnonymousInNonStaticContext(); else if(isLocalClass()) return !inStaticContext(); else if(isInnerType()) return true; return false; } syn boolean TypeDecl.needsSuperEnclosing() { if(!isAnonymous()) return false; TypeDecl superClass = ((ClassDecl)this).superclass(); if(superClass.isLocalClass()) return !superClass.inStaticContext(); else if(superClass.isInnerType()) return true; return false; } // collect the set of variables used in the enclosing class(es) syn lazy Collection TypeDecl.enclosingVariables() { HashSet set = new HashSet(); for(TypeDecl e = this; e != null; e = e.enclosingType()) if(e.isLocalClass() || e.isAnonymous()) collectEnclosingVariables(set, e.enclosingType()); if(isClassDecl()) { ClassDecl classDecl = (ClassDecl)this; if(classDecl.isNestedType() && classDecl.hasSuperclass()) set.addAll(classDecl.superclass().enclosingVariables()); } return set; } public void ASTNode.collectEnclosingVariables(HashSet set, TypeDecl typeDecl) { for(int i = 0; i < getNumChild(); i++) getChild(i).collectEnclosingVariables(set, typeDecl); } public void VarAccess.collectEnclosingVariables(HashSet set, TypeDecl typeDecl) { Variable v = decl(); if(!v.isInstanceVariable() && !v.isClassVariable() && v.hostType() == typeDecl) set.add(v); super.collectEnclosingVariables(set, typeDecl); } // add val$name as fields to the class private boolean TypeDecl.addEnclosingVariables = true; public void TypeDecl.addEnclosingVariables() { if(!addEnclosingVariables) return; addEnclosingVariables = false; for(Iterator iter = enclosingVariables().iterator(); iter.hasNext(); ) { Variable v = (Variable)iter.next(); Modifiers m = new Modifiers(); m.addModifier(new Modifier("public")); m.addModifier(new Modifier("synthetic")); addMemberField(new FieldDeclaration(m, v.type().createQualifiedAccess(), v.name(), new Opt())); } } // add val$name as parameters to the constructor protected boolean ConstructorDecl.addEnclosingVariables = true; public void ConstructorDecl.addEnclosingVariables() { if(!addEnclosingVariables) return; addEnclosingVariables = false; hostType().addEnclosingVariables(); for(Iterator iter = hostType().enclosingVariables().iterator(); iter.hasNext(); ) { Variable v = (Variable)iter.next(); getParameterList().add(new ParameterDeclaration(v.type(), "val$" + v.name())); } } // add val$name as arguments to the constructor protected boolean ConstructorAccess.addEnclosingVariables = true; public void ConstructorAccess.addEnclosingVariables() { if(!addEnclosingVariables) return; addEnclosingVariables = false; decl().addEnclosingVariables(); for(Iterator iter = decl().hostType().enclosingVariables().iterator(); iter.hasNext(); ) { Variable v = (Variable)iter.next(); getArgList().add(new VarAccess("val$" + v.name())); } } // add val$name as arguments to the constructor protected boolean ClassInstanceExpr.addEnclosingVariables = true; public void ClassInstanceExpr.addEnclosingVariables() { if(!addEnclosingVariables) return; addEnclosingVariables = false; decl().addEnclosingVariables(); for(Iterator iter = decl().hostType().enclosingVariables().iterator(); iter.hasNext(); ) { Variable v = (Variable)iter.next(); getArgList().add(new VarAccess(v.name())); } } public ConstructorDecl ConstructorDecl.createAccessor() { ConstructorDecl c = (ConstructorDecl)hostType().getAccessor(this, "constructor"); if(c != null) return c; // make sure enclosing varibles are added as parameters prior to building accessor addEnclosingVariables(); Modifiers modifiers = new Modifiers(new List()); modifiers.addModifier(new Modifier("synthetic")); modifiers.addModifier(new Modifier("public")); List parameters = createAccessorParameters(); // add all parameters as arguments except for the dummy parameter List args = new List(); for(int i = 0; i < parameters.getNumChild() - 1; i++) args.add(new VarAccess("p" + i)); c = new ConstructorDecl( modifiers, name(), parameters, (List)getExceptionList().fullCopy(), new Opt( new ExprStmt( new ConstructorAccess("this", args) ) ), new Block( new List().add(new ReturnStmt(new Opt())) ) ); c = hostType().addConstructor(c); c.addEnclosingVariables = false; hostType().addAccessor(this, "constructor", c); return c; } protected List ConstructorDecl.createAccessorParameters() { List parameters = new List(); for (int i=0; i