001    /*
002     * The JastAdd Extensible Java Compiler (http://jastadd.org) is covered
003     * by the modified BSD License. You should have received a copy of the
004     * modified BSD license with this compiler.
005     * 
006     * Copyright (c) 2005-2008, Torbjorn Ekman
007     * All rights reserved.
008     */
009    
010    aspect InnerClasses {
011      // no attribute since needed in phases when the AST has been modified
012      public boolean TypeDecl.hasField(String name) {
013        if(!memberFields(name).isEmpty())
014          return true;
015        for(int i = 0; i < getNumBodyDecl(); i++) {
016          if(getBodyDecl(i) instanceof FieldDeclaration) {
017            FieldDeclaration decl = (FieldDeclaration)getBodyDecl(i);
018            if(decl.name().equals(name))
019              return true;
020          }
021        }
022        return false;
023      }
024    
025      private TypeDecl VarAccess.fieldQualifierType() {
026        if(hasPrevExpr())
027          return prevExpr().type();
028        TypeDecl typeDecl = hostType();
029        while(typeDecl != null && !typeDecl.hasField(name()))
030          typeDecl = typeDecl.enclosingType();
031        if(typeDecl != null)
032          return typeDecl;
033        return decl().hostType();
034      }
035    
036      public boolean TypeDecl.hasMethod(String id) {
037        if(!memberMethods(id).isEmpty()) return true;
038        for(int i = 0; i < getNumBodyDecl(); i++) {
039          if(getBodyDecl(i) instanceof MethodDecl) {
040            MethodDecl decl = (MethodDecl)getBodyDecl(i);
041            if(decl.name().equals(id))
042              return true;
043          }
044        }
045        return false;
046      }
047    
048      private TypeDecl MethodAccess.methodQualifierType() {
049        if(hasPrevExpr())
050          return prevExpr().type();
051        TypeDecl typeDecl = hostType();
052        while(typeDecl != null && !typeDecl.hasMethod(name()))
053          typeDecl = typeDecl.enclosingType();
054        if(typeDecl != null)
055          return typeDecl;
056        return decl().hostType();
057      }
058    
059    
060      // Helpers for arrays
061      inh TypeDecl ArrayInit.expectedType();
062    //  eq Program.getCompilationUnit(int i).expectedType() = null;
063        // ES: switching to getChild to include compilation units moved to nta
064      eq Program.getChild(int i).expectedType() = null;
065    
066      eq ArrayCreationExpr.getArrayInit().expectedType() = type().componentType();
067      eq FieldDeclaration.getInit().expectedType() = type().componentType();
068      eq VariableDeclaration.getInit().expectedType() = type().componentType();
069      eq VariableDecl.getInit().expectedType() = null;
070      eq ArrayInit.getInit().expectedType() = expectedType().componentType();
071      
072      syn lazy int ArrayCreationExpr.numArrays() {
073        int i = type().dimension();
074        Access a = getTypeAccess();
075        while(a instanceof ArrayTypeAccess && !(a instanceof ArrayTypeWithSizeAccess)) {
076          i--;
077          a = ((ArrayTypeAccess)a).getAccess();
078        }
079        return i;
080      }
081    
082      syn TypeDecl TypeDecl.stringPromotion() = this;
083      eq ReferenceType.stringPromotion() = typeObject();
084      eq NullType.stringPromotion() = typeObject();
085      eq ByteType.stringPromotion() = typeInt();
086      eq ShortType.stringPromotion() = typeInt();
087    
088      syn boolean ASTNode.isStringAdd() = false;
089      eq AddExpr.isStringAdd() = type().isString() && !isConstant();
090      
091      syn boolean AddExpr.firstStringAddPart() = type().isString() && !getLeftOperand().isStringAdd();
092      syn boolean AddExpr.lastStringAddPart() = !getParent().isStringAdd();
093    
094      syn MethodDecl TypeDecl.methodWithArgs(String name, TypeDecl[] args) {
095        for(Iterator iter = memberMethods(name).iterator(); iter.hasNext(); ) {
096          MethodDecl m = (MethodDecl)iter.next();
097          if(m.getNumParameter() == args.length) {
098            for(int i = 0; i < args.length; i++)
099              if(m.getParameter(i).type() == args[i])
100                return m;
101          }
102        }
103        return null;
104      }
105    
106      protected TypeDecl Access.superConstructorQualifier(TypeDecl targetEnclosingType) {
107        TypeDecl enclosing = hostType();
108        while(!enclosing.instanceOf(targetEnclosingType))
109          enclosing = enclosing.enclosingType();
110        return enclosing;
111      }
112    
113      public TypeDecl MethodAccess.superAccessorTarget() {
114        TypeDecl targetDecl = prevExpr().type();
115        TypeDecl enclosing = hostType();
116        do {
117          enclosing = enclosing.enclosingType();
118        } while (!enclosing.instanceOf(targetDecl));
119        return enclosing;
120      }
121    
122      // The set of TypeDecls that has this TypeDecl as their directly enclosing TypeDecl.
123      // I.e., NestedTypes, InnerTypes, AnonymousClasses, LocalClasses.
124      private Collection TypeDecl.nestedTypes;
125      public Collection TypeDecl.nestedTypes() {
126        return nestedTypes != null ? nestedTypes : new HashSet();
127      }
128      public void TypeDecl.addNestedType(TypeDecl typeDecl) {
129        if(nestedTypes == null) nestedTypes = new HashSet();
130        if(typeDecl != this)
131          nestedTypes.add(typeDecl);
132      }
133    
134      // The set of nested TypeDecls that are accessed in this TypeDecl
135      private Collection TypeDecl.usedNestedTypes;
136      public Collection TypeDecl.usedNestedTypes() {
137        return usedNestedTypes != null ? usedNestedTypes : new HashSet();
138      }
139      public void TypeDecl.addUsedNestedType(TypeDecl typeDecl) {
140        if(usedNestedTypes == null) usedNestedTypes = new HashSet();
141        usedNestedTypes.add(typeDecl);
142      }
143    
144      // collect the set of variables used in the enclosing class(es)
145      syn lazy Collection TypeDecl.enclosingVariables() {
146        HashSet set = new HashSet();
147        for(TypeDecl e = this; e != null; e = e.enclosingType())
148          if(e.isLocalClass() || e.isAnonymous())
149            collectEnclosingVariables(set, e.enclosingType());
150        if(isClassDecl()) {
151          ClassDecl classDecl = (ClassDecl)this;
152          if(classDecl.isNestedType() && classDecl.hasSuperclass())
153            set.addAll(classDecl.superclass().enclosingVariables());
154        }
155        return set;
156      }
157    
158      public void ASTNode.collectEnclosingVariables(HashSet set, TypeDecl typeDecl) {
159        for(int i = 0; i < getNumChild(); i++)
160          getChild(i).collectEnclosingVariables(set, typeDecl);
161      }
162      public void VarAccess.collectEnclosingVariables(HashSet set, TypeDecl typeDecl) {
163        Variable v = decl();
164        if(!v.isInstanceVariable() && !v.isClassVariable() && v.hostType() == typeDecl)
165          set.add(v);
166        super.collectEnclosingVariables(set, typeDecl);
167      }
168    
169    
170      public int TypeDecl.accessorCounter = 0;
171    
172      private HashMap TypeDecl.accessorMap = null;
173      public ASTNode TypeDecl.getAccessor(ASTNode source, String name) {
174        ArrayList key = new ArrayList(2);
175        key.add(source);
176        key.add(name);
177        if(accessorMap == null || !accessorMap.containsKey(key)) return null;
178        return (ASTNode)accessorMap.get(key);
179      }
180    
181      public void TypeDecl.addAccessor(ASTNode source, String name, ASTNode accessor) {
182        ArrayList key = new ArrayList(2);
183        key.add(source);
184        key.add(name);
185        if(accessorMap == null) accessorMap = new HashMap();
186        accessorMap.put(key, accessor);
187      }
188    
189      public ASTNode TypeDecl.getAccessorSource(ASTNode accessor) {
190        Iterator i = accessorMap.entrySet().iterator();
191        while (i.hasNext()) {
192          Map.Entry entry = (Map.Entry) i.next();
193          if (entry.getValue() == accessor)
194            return (ASTNode) ((ArrayList) entry.getKey()).get(0);
195        }
196        return null;
197      }
198    
199      public MethodDecl MethodDecl.createAccessor(TypeDecl methodQualifier) {
200        MethodDecl m = (MethodDecl)methodQualifier.getAccessor(this, "method");
201        if(m != null) return m;
202    
203        int accessorIndex = methodQualifier.accessorCounter++;
204        
205        List parameterList = new List();
206        for(int i = 0; i < getNumParameter(); i++)
207          parameterList.add(new ParameterDeclaration(
208                // We don't need to create a qualified access to the type here
209                // since there can be no ambiguity concerning unqualified
210                // type names in an inner/enclosing class
211                // Jesper 2012-05-04
212                // FALSE! We need to create a qualified access in case the
213                // method we are generating an access for is not declared
214                // in the methodQualifier type
215                getParameter(i).type().createQualifiedAccess(),
216                getParameter(i).name()));
217        List exceptionList = new List();
218        for(int i = 0; i < getNumException(); i++)
219          exceptionList.add((Access) getException(i).fullCopy());
220    
221        // add synthetic flag to modifiers
222        Modifiers modifiers = new Modifiers(new List());
223        if(getModifiers().isStatic())
224          modifiers.addModifier(new Modifier("static"));
225        modifiers.addModifier(new Modifier("synthetic"));
226        modifiers.addModifier(new Modifier("public"));
227        // build accessor declaration
228        m = new MethodDecl(
229          modifiers,
230          getTypeAccess().type().createQualifiedAccess(),
231          name() + "$access$" + accessorIndex,
232          parameterList,
233          exceptionList,
234          new Opt(
235            new Block(
236              new List().add(
237                createAccessorStmt()
238              )
239            )
240          )
241        );
242        m = methodQualifier.addMemberMethod(m);
243        methodQualifier.addAccessor(this, "method", m);
244        return m;
245      }
246      
247      private Stmt MethodDecl.createAccessorStmt() {
248        List argumentList = new List();
249        for(int i = 0; i < getNumParameter(); i++)
250          argumentList.add(new VarAccess(getParameter(i).name()));
251        Access access = new BoundMethodAccess(name(), argumentList, this);
252        if(!isStatic())
253          access = new ThisAccess("this").qualifiesAccess(access);
254        return isVoid() ? (Stmt) new ExprStmt(access) : new ReturnStmt(new Opt(access));
255      }
256    
257      public MethodDecl MethodDecl.createSuperAccessor(TypeDecl methodQualifier) {
258        MethodDecl m = (MethodDecl)methodQualifier.getAccessor(this, "method_super");
259        if(m != null) return m;
260    
261        int accessorIndex = methodQualifier.accessorCounter++;
262        List parameters = new List();
263        List args = new List();
264        for(int i = 0; i < getNumParameter(); i++) {
265          parameters.add(new ParameterDeclaration(getParameter(i).type(), getParameter(i).name()));
266          args.add(new VarAccess(getParameter(i).name()));
267        }
268        Stmt stmt;
269        if(type().isVoid())
270          stmt = new ExprStmt(new SuperAccess("super").qualifiesAccess(new MethodAccess(name(), args)));
271        else 
272          stmt = new ReturnStmt(new Opt(new SuperAccess("super").qualifiesAccess(new MethodAccess(name(), args))));
273        m = new MethodDecl(
274          new Modifiers(new List().add(new Modifier("synthetic"))),
275          type().createQualifiedAccess(),
276          name() + "$access$" + accessorIndex,
277          parameters,
278          new List(),
279          new Opt(
280            new Block(
281              new List().add(stmt)
282            )
283          )
284        );
285        m = methodQualifier.addMemberMethod(m);
286        methodQualifier.addAccessor(this, "method_super", m);
287        return m;
288      }
289    
290      public MethodDecl FieldDeclaration.createAccessor(TypeDecl fieldQualifier) {
291        MethodDecl m = (MethodDecl)fieldQualifier.getAccessor(this, "field_read");
292        if(m != null) return m;
293        
294        int accessorIndex = fieldQualifier.accessorCounter++;
295        Modifiers modifiers = new Modifiers(new List());
296        modifiers.addModifier(new Modifier("static"));
297        modifiers.addModifier(new Modifier("synthetic"));
298        modifiers.addModifier(new Modifier("public"));
299    
300        List parameters = new List();
301        if(!isStatic())
302          parameters.add(new ParameterDeclaration(fieldQualifier.createQualifiedAccess(), "that"));
303    
304        m = new MethodDecl(
305          modifiers,
306          type().createQualifiedAccess(),
307          "get$" + name() + "$access$" + accessorIndex,
308          parameters,
309          new List(),
310          new Opt(
311            new Block(
312              new List().add(
313                new ReturnStmt(createAccess())
314              )
315            )
316          )
317        );
318        m = fieldQualifier.addMemberMethod(m);
319        fieldQualifier.addAccessor(this, "field_read", m);
320        return m;
321      }
322    
323      public MethodDecl FieldDeclaration.createAccessorWrite(TypeDecl fieldQualifier) {
324        MethodDecl m = (MethodDecl)fieldQualifier.getAccessor(this, "field_write");
325        if(m != null) return m;
326    
327        int accessorIndex = fieldQualifier.accessorCounter++;
328        Modifiers modifiers = new Modifiers(new List());
329        modifiers.addModifier(new Modifier("static"));
330        modifiers.addModifier(new Modifier("synthetic"));
331        modifiers.addModifier(new Modifier("public"));
332    
333        List parameters = new List();
334        if(!isStatic())
335          parameters.add(new ParameterDeclaration(fieldQualifier.createQualifiedAccess(), "that"));
336        parameters.add(new ParameterDeclaration(type().createQualifiedAccess(), "value"));
337    
338        m = new MethodDecl(
339          modifiers,
340          type().createQualifiedAccess(),
341          "set$" + name() + "$access$" + accessorIndex,
342          parameters,
343          new List(),
344          new Opt(
345            new Block(
346              new List().add(
347                new ExprStmt(
348                  new AssignSimpleExpr(
349                    createAccess(),
350                    new VarAccess("value")
351                  )
352                )
353              ).add(
354                new ReturnStmt(
355                  new Opt(
356                    new VarAccess("value")
357                  )
358                )
359              )
360            )
361          )
362        );
363        m = fieldQualifier.addMemberMethod(m);
364        fieldQualifier.addAccessor(this, "field_write", m);
365        return m;
366      }
367    
368      private Access FieldDeclaration.createAccess() {
369        Access fieldAccess = new BoundFieldAccess(this);
370        return isStatic() ? fieldAccess : new VarAccess("that").qualifiesAccess(fieldAccess);
371      }
372    
373      syn boolean VarAccess.requiresAccessor() {
374        Variable v = decl();
375        if(!(v instanceof FieldDeclaration))
376          return false;
377        FieldDeclaration f = (FieldDeclaration)v;
378        if(f.isPrivate() && !hostType().hasField(v.name()))
379          return true;
380        if(f.isProtected() && !f.hostPackage().equals(hostPackage()) && !hostType().hasField(v.name()))
381          return true;
382        return false;
383      }
384    
385      syn boolean MethodAccess.requiresAccessor() {
386        MethodDecl m = decl();
387        if(m.isPrivate() && m.hostType() != hostType())
388          return true;
389        if(m.isProtected() && !m.hostPackage().equals(hostPackage()) && !hostType().hasMethod(m.name()))
390          return true;
391        return false;
392      }
393    
394      syn boolean TypeDecl.isAnonymousInNonStaticContext() {
395        return isAnonymous() && 
396               !((ClassInstanceExpr)getParent().getParent()).unqualifiedScope().inStaticContext()
397               && (!inExplicitConstructorInvocation() || enclosingBodyDecl().hostType().isInnerType());
398      }
399      
400      syn boolean TypeDecl.needsEnclosing() {
401        if(isAnonymous())
402          return isAnonymousInNonStaticContext();
403        else if(isLocalClass())
404          return !inStaticContext();
405        else if(isInnerType())
406          return true;
407        return false;
408      }
409      
410      syn boolean TypeDecl.needsSuperEnclosing() {
411        if(!isAnonymous())
412          return false;
413        TypeDecl superClass = ((ClassDecl)this).superclass();
414        if(superClass.isLocalClass())
415          return !superClass.inStaticContext();
416        else if(superClass.isInnerType())
417          return true;
418        if(needsEnclosing() && enclosing() == superEnclosing())
419          return false;
420        return false;
421      }
422      syn TypeDecl TypeDecl.enclosing() {
423        if(!needsEnclosing())
424          return null;
425        TypeDecl typeDecl = enclosingType();
426        if(isAnonymous() && inExplicitConstructorInvocation())
427          typeDecl = typeDecl.enclosingType();
428        return typeDecl;
429      }
430      syn TypeDecl TypeDecl.superEnclosing() = null;
431      eq ClassDecl.superEnclosing() = superclass().enclosing();
432    
433      syn boolean ConstructorDecl.needsEnclosing() = hostType().needsEnclosing();
434      syn boolean ConstructorDecl.needsSuperEnclosing() = hostType().needsSuperEnclosing();
435    
436      syn TypeDecl ConstructorDecl.enclosing() = hostType().enclosing();
437      syn TypeDecl ConstructorDecl.superEnclosing() = hostType().superEnclosing();
438    
439    
440    
441      // add val$name as fields to the class
442      private boolean TypeDecl.addEnclosingVariables = true;
443      public void TypeDecl.addEnclosingVariables() {
444        if(!addEnclosingVariables) return;
445        addEnclosingVariables = false;
446        for(Iterator iter = enclosingVariables().iterator(); iter.hasNext(); ) {
447          Variable v = (Variable)iter.next();
448          Modifiers m = new Modifiers();
449          m.addModifier(new Modifier("public"));
450          m.addModifier(new Modifier("synthetic"));
451          m.addModifier(new Modifier("final"));
452          addMemberField(new FieldDeclaration(m, v.type().createQualifiedAccess(), "val$" + v.name(), new Opt()));
453        }
454      }
455    
456      // add val$name as parameters to the constructor
457      protected boolean ConstructorDecl.addEnclosingVariables = true;
458      public void ConstructorDecl.addEnclosingVariables() {
459        if(!addEnclosingVariables) return;
460        addEnclosingVariables = false;
461        hostType().addEnclosingVariables();
462        for(Iterator iter = hostType().enclosingVariables().iterator(); iter.hasNext(); ) {
463          Variable v = (Variable)iter.next();
464          getParameterList().add(new ParameterDeclaration(v.type(), "val$" + v.name()));
465        }
466      }
467    
468      // add val$name as arguments to the constructor
469      protected boolean ConstructorAccess.addEnclosingVariables = true;
470      public void ConstructorAccess.addEnclosingVariables() {
471        if(!addEnclosingVariables) return;
472        addEnclosingVariables = false;
473        decl().addEnclosingVariables();
474        for(Iterator iter = decl().hostType().enclosingVariables().iterator(); iter.hasNext(); ) {
475          Variable v = (Variable)iter.next();
476          getArgList().add(new VarAccess("val$" + v.name()));
477        }
478      }
479    
480      // add val$name as arguments to the constructor
481      protected boolean ClassInstanceExpr.addEnclosingVariables = true;
482      public void ClassInstanceExpr.addEnclosingVariables() {
483        if(!addEnclosingVariables) return;
484        addEnclosingVariables = false;
485        decl().addEnclosingVariables();
486        for(Iterator iter = decl().hostType().enclosingVariables().iterator(); iter.hasNext(); ) {
487          Variable v = (Variable)iter.next();
488          getArgList().add(new VarAccess(v.name()));
489        }
490      }
491    
492      public ConstructorDecl ConstructorDecl.createAccessor() {
493        ConstructorDecl c = (ConstructorDecl)hostType().getAccessor(this, "constructor");
494        if(c != null) return c;
495    
496        // make sure enclosing varibles are added as parameters prior to building accessor
497        addEnclosingVariables();
498    
499        Modifiers modifiers = new Modifiers(new List());
500        modifiers.addModifier(new Modifier("synthetic"));
501        modifiers.addModifier(new Modifier("public"));
502    
503        List parameters = createAccessorParameters();
504    
505        List exceptionList = new List(); 
506        for(int i = 0; i < getNumException(); i++)
507          exceptionList.add(getException(i).type().createQualifiedAccess());
508        
509        // add all parameters as arguments except for the dummy parameter
510        List args = new List();
511        for(int i = 0; i < parameters.getNumChildNoTransform() - 1; i++)
512          args.add(new VarAccess(((ParameterDeclaration)parameters.getChildNoTransform(i)).name()));
513        ConstructorAccess access = new ConstructorAccess("this", args);
514        access.addEnclosingVariables = false;
515    
516        c = new ConstructorDecl(
517          modifiers,
518          name(),
519          parameters,
520          exceptionList,
521          new Opt(
522            new ExprStmt(
523              access
524            )
525          ),
526          new Block(
527            new List().add(new ReturnStmt(new Opt()))
528          )
529        );
530        c = hostType().addConstructor(c);
531        c.addEnclosingVariables = false;
532        hostType().addAccessor(this, "constructor", c);
533        return c;
534      }
535    
536      protected List ConstructorDecl.createAccessorParameters() {
537        List parameters = new List();
538        for (int i=0; i<getNumParameter(); i++)
539          parameters.add(new ParameterDeclaration(getParameter(i).type(), getParameter(i).name()));
540        parameters.add(new ParameterDeclaration(createAnonymousJavaTypeDecl().createBoundAccess(), ("p" + getNumParameter())));
541        return parameters;
542      }
543    
544      protected TypeDecl ConstructorDecl.createAnonymousJavaTypeDecl() {
545        ClassDecl classDecl =
546          new ClassDecl(
547              new Modifiers(new List().add(new Modifier("synthetic"))),
548              "" + hostType().nextAnonymousIndex(),
549              new Opt(),
550              new List(),
551              new List()
552          );
553        classDecl = hostType().addMemberClass(classDecl);
554        hostType().addNestedType(classDecl);
555        return classDecl;
556      }
557    
558    }