001    /* Copyright (c) 2005-2008, Torbjorn Ekman
002     * All rights reserved.
003     *
004     * Redistribution and use in source and binary forms, with or without
005     * modification, are permitted provided that the following conditions are met:
006     *
007     * 1. Redistributions of source code must retain the above copyright notice,
008     * this list of conditions and the following disclaimer.
009     *
010     * 2. Redistributions in binary form must reproduce the above copyright notice,
011     * this list of conditions and the following disclaimer in the documentation
012     * and/or other materials provided with the distribution.
013     *
014     * 3. Neither the name of the copyright holder nor the names of its
015     * contributors may be used to endorse or promote products derived from this
016     * software without specific prior written permission.
017     *
018     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
022     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028     * POSSIBILITY OF SUCH DAMAGE.
029     */
030    
031    import java.util.*;
032    import java.util.ArrayList;
033    
034    aspect ConstructScope {
035      inh Collection ConstructorAccess.lookupConstructor();
036      eq Program.getChild().lookupConstructor() = Collections.EMPTY_LIST;
037      eq TypeDecl.getChild().lookupConstructor() = constructors();
038      eq AbstractDot.getRight().lookupConstructor() = getLeft().type().constructors();
039    
040      inh Collection SuperConstructorAccess.lookupSuperConstructor();
041      eq TypeDecl.getChild().lookupSuperConstructor() = lookupSuperConstructor();
042      syn Collection TypeDecl.lookupSuperConstructor() = Collections.EMPTY_LIST;
043      eq ClassDecl.lookupSuperConstructor() = hasSuperclass() ? superclass().constructors() : Collections.EMPTY_LIST;
044      eq InterfaceDecl.lookupSuperConstructor() = typeObject().constructors();
045      eq Program.getChild().lookupSuperConstructor() = Collections.EMPTY_LIST;
046      eq AbstractDot.getRight().lookupSuperConstructor() = getLeft().type().lookupSuperConstructor();
047    
048      inh TypeDecl ClassInstanceExpr.typeObject();
049    
050      /**
051       * Compute the most specific constructor in a collection.
052       * The constructor is invoked with the arguments specified in argList.
053       * The curent context (this) is used to evaluate the hostType for accessibility.
054       */
055      syn SimpleSet Expr.mostSpecificConstructor(Collection<ConstructorDecl> constructors) {
056        SimpleSet maxSpecific = SimpleSet.emptySet;
057        for (Iterator iter = constructors.iterator(); iter.hasNext(); ) {
058          ConstructorDecl decl = (ConstructorDecl) iter.next();
059          if (applicableAndAccessible(decl)) {
060            if (maxSpecific.isEmpty()) {
061              maxSpecific = maxSpecific.add(decl);
062            } else {
063              ConstructorDecl other = (ConstructorDecl) maxSpecific.iterator().next();
064              if (decl.moreSpecificThan(other)) {
065                maxSpecific = SimpleSet.emptySet.add(decl);
066              } else if (!other.moreSpecificThan(decl)) {
067                maxSpecific = maxSpecific.add(decl);
068              }
069            }
070          }
071        }
072        return maxSpecific;
073      }
074    
075      syn boolean Expr.applicableAndAccessible(ConstructorDecl decl) = false;
076      eq ConstructorAccess.applicableAndAccessible(ConstructorDecl decl) =
077          decl.applicable(getArgList()) && decl.accessibleFrom(hostType());
078      eq ClassInstanceExpr.applicableAndAccessible(ConstructorDecl decl) =
079          decl.applicable(getArgList()) && decl.accessibleFrom(hostType())
080          && (!decl.isProtected() || hasTypeDecl() || decl.hostPackage().equals(hostPackage()));
081    
082      syn lazy SimpleSet ConstructorAccess.decls() = mostSpecificConstructor(lookupConstructor());
083    
084      syn lazy SimpleSet SuperConstructorAccess.decls() =
085          mostSpecificConstructor(hasPrevExpr() && !prevExpr().isTypeAccess()
086              ? hostType().lookupSuperConstructor()
087              : lookupSuperConstructor());
088    
089      syn lazy ConstructorDecl ConstructorAccess.decl() {
090        SimpleSet decls = decls();
091        if (decls.size() == 1) {
092          return (ConstructorDecl) decls.iterator().next();
093        }
094        return unknownConstructor();
095      }
096    
097      inh ConstructorDecl ConstructorAccess.unknownConstructor();
098    
099      syn lazy SimpleSet ClassInstanceExpr.decls() {
100        TypeDecl typeDecl = hasTypeDecl() ? getTypeDecl() : getAccess().type();
101        return mostSpecificConstructor(typeDecl.constructors());
102      }
103    
104      syn lazy ConstructorDecl ClassInstanceExpr.decl() {
105        SimpleSet decls = decls();
106        if (decls.size() == 1) {
107          return (ConstructorDecl) decls.iterator().next();
108        } else {
109          return unknownConstructor();
110        }
111      }
112    
113      inh ConstructorDecl ClassInstanceExpr.unknownConstructor();
114    }
115    
116    aspect ConstructorLookup {
117      public ConstructorDecl TypeDecl.lookupConstructor(ConstructorDecl signature) {
118        for (Iterator iter = constructors().iterator(); iter.hasNext(); ) {
119          ConstructorDecl decl = (ConstructorDecl) iter.next();
120          if (decl.sameSignature(signature)) {
121            return decl;
122          }
123        }
124        return null;
125      }
126    
127      syn lazy Collection<ConstructorDecl> TypeDecl.constructors() {
128        Collection<ConstructorDecl> c = new ArrayList();
129        for (int i = 0; i < getNumBodyDecl(); i++) {
130          if (getBodyDecl(i) instanceof ConstructorDecl) {
131            c.add((ConstructorDecl) getBodyDecl(i));
132          }
133        }
134        return c;
135      }
136    
137      eq ClassDecl.constructors() {
138        Collection<ConstructorDecl> c = super.constructors();
139        if (hasImplicitConstructor()) {
140          c.add(getImplicitConstructor());
141        }
142        return c;
143      }
144    }
145    
146    aspect ConstructorDecl {
147      syn lazy String ConstructorDecl.name() = getID();
148      // 8.8.2
149      syn lazy String ConstructorDecl.signature() {
150        StringBuffer s = new StringBuffer();
151        s.append(name() + "(");
152        for (int i = 0; i < getNumParameter(); i++) {
153          s.append(getParameter(i).type().typeName());
154          if (i != getNumParameter() - 1) {
155            s.append(", ");
156          }
157        }
158        s.append(")");
159        return s.toString();
160      }
161    
162      // 8.8.2
163      syn lazy boolean ConstructorDecl.sameSignature(ConstructorDecl c) {
164        if (!name().equals(c.name())) {
165          return false;
166        }
167        if (c.getNumParameter() != getNumParameter()) {
168          return false;
169        }
170        for (int i = 0; i < getNumParameter(); i++) {
171          if (!c.getParameter(i).type().equals(getParameter(i).type())) {
172            return false;
173          }
174        }
175        return true;
176      }
177    
178      syn boolean ConstructorDecl.moreSpecificThan(ConstructorDecl m) =
179          m.lessSpecificThan(this) && !this.lessSpecificThan(m);
180    
181      syn lazy boolean ConstructorDecl.lessSpecificThan(ConstructorDecl m) {
182        for (int i = 0; i < getNumParameter(); i++) {
183          if (!getParameter(i).type().instanceOf(m.getParameter(i).type())) {
184            return true;
185          }
186        }
187        return false;
188      }
189    
190      public boolean ConstructorDecl.applicable(List<Expr> argList) {
191        if (getNumParameter() != argList.getNumChild()) {
192          return false;
193        }
194        for (int i = 0; i < getNumParameter(); i++) {
195          TypeDecl arg = argList.getChild(i).type();
196          TypeDecl parameter = getParameter(i).type();
197          if (!arg.instanceOf(parameter)) {
198            return false;
199          }
200        }
201        return true;
202      }
203    }
204    
205    aspect ImplicitConstructor {
206    
207      /**
208       * A class declaration requires an implicit constructor if it has no
209       * explicit constructor.
210       * @return <code>true</code> if this class requires an implicit default
211       * contstructor.
212       */
213      syn boolean ClassDecl.needsImplicitConstructor() =
214          compilationUnit().fromSource() && !hasExplicitConstructor();
215    
216      /**
217       * Flag to indicate if this constructor is an auto-generated
218       * default constructor. Implicit constructors are not pretty
219       * printed.
220       */
221      private boolean ConstructorDecl.isImplicitConstructor = false;
222    
223      /**
224       * Set the default constructor flag. Causes this constructor
225       * to not be pretty printed.
226       */
227      public void ConstructorDecl.setImplicitConstructor() {
228        isImplicitConstructor = true;
229      }
230    
231      /**
232       * @return true if this is an auto-generated default constructor
233       */
234      syn boolean ConstructorDecl.isImplicitConstructor() = isImplicitConstructor;
235    
236      syn lazy Opt<ConstructorDecl> ClassDecl.getImplicitConstructorOpt() {
237        if (needsImplicitConstructor()) {
238          Modifiers m = new Modifiers();
239          if (isPublic()) {
240            m.addModifier(new Modifier("public"));
241          } else if (isProtected()) {
242            m.addModifier(new Modifier("protected"));
243          } else if (isPrivate()) {
244            m.addModifier(new Modifier("private"));
245          }
246          ConstructorDecl constructor = new ConstructorDecl(
247              m,
248              name(),
249              new List(),
250              new List(),
251              new Opt(),
252              new Block()
253          );
254          constructor.setParsedConstructorInvocation(
255            new ExprStmt(
256              new SuperConstructorAccess("super", new List())
257            )
258          );
259          constructor.setImplicitConstructor();
260          return new Opt<ConstructorDecl>(constructor);
261        } else {
262          return new Opt<ConstructorDecl>();
263        }
264      }
265    
266      syn lazy Opt<ConstructorDecl> AnonymousDecl.getImplicitConstructorOpt() {
267        if (needsImplicitConstructor()) {
268          ConstructorDecl decl = constructorDecl();
269          Modifiers modifiers = (Modifiers) decl.getModifiers().treeCopyNoTransform();
270          String anonName = "Anonymous" + nextAnonymousIndex();
271    
272          ConstructorDecl constructor = new ConstructorDecl(modifiers, anonName,
273              constructorParameterList(decl), new List(), new Opt(), new Block());
274    
275          setID(anonName);
276    
277          List argList = new List();
278          for (int i = 0; i < constructor.getNumParameter(); i++) {
279            argList.add(new VarAccess(constructor.getParameter(i).name()));
280          }
281    
282          constructor.setParsedConstructorInvocation(
283            new ExprStmt(
284              new SuperConstructorAccess("super", argList)
285            )
286          );
287    
288          HashSet set = new HashSet();
289    
290          // add initializer and field declaration exceptions
291          for (int i = 0; i < getNumBodyDecl(); i++) {
292            if (getBodyDecl(i) instanceof InstanceInitializer) {
293              InstanceInitializer init = (InstanceInitializer) getBodyDecl(i);
294              set.addAll(init.exceptions());
295            } else if (getBodyDecl(i) instanceof FieldDeclaration) {
296              FieldDeclaration f = (FieldDeclaration) getBodyDecl(i);
297              if (f.isInstanceVariable()) {
298                set.addAll(f.exceptions());
299              }
300            }
301          }
302    
303          // add superconstructor exceptions
304          for (int i = 0; i < decl.getNumException(); ++i) {
305            set.add(decl.getException(i).type());
306          }
307    
308          List exceptionList = new List();
309          for (Iterator iter = set.iterator(); iter.hasNext(); ) {
310            TypeDecl exceptionType = (TypeDecl) iter.next();
311            if (exceptionType.isNull()) {
312              exceptionType = typeNullPointerException();
313            }
314            exceptionList.add(exceptionType.createQualifiedAccess());
315          }
316          constructor.setExceptionList(exceptionList);
317          return new Opt<ConstructorDecl>(constructor);
318        } else {
319          return new Opt<ConstructorDecl>();
320        }
321      }
322    
323      syn boolean ClassDecl.hasExplicitConstructor() {
324        for (int i = 0; i < getNumBodyDecl(); i++) {
325          if (getBodyDecl(i) instanceof ConstructorDecl) {
326            return true;
327          }
328        }
329        return false;
330      }
331    
332      // 8.8.5
333    
334      /**
335       * Nonterminal attribute for implicit constructor invocation.
336       * This is used when an explicit constructor invocation is missing
337       * in a constructor declaration.
338       *
339       * The implicit constructor invocation used to be inserted in the
340       * same node where the parsed constructor declaration was stored.
341       * This meant that it was impossible to distinguish a parsed constructor
342       * from an implicit one.
343       */
344      syn Stmt ConstructorDecl.getImplicitConstructorInvocation() =
345          new ExprStmt(new SuperConstructorAccess("super", new List()));
346    
347      /**
348       * Test if there is an explicit or implicit constructor invocation available.
349       * This should be false only if the host type is java.lang.Object.
350       * @return {@code true} if there is a constructor invocation.
351       */
352      syn boolean ConstructorDecl.hasConstructorInvocation() =
353          hasParsedConstructorInvocation() || !hostType().isObject();
354    
355      syn Stmt ConstructorDecl.getConstructorInvocation() {
356        if (hasParsedConstructorInvocation()) {
357          return getParsedConstructorInvocation();
358        } else {
359          return getImplicitConstructorInvocation();
360        }
361      }
362    }
363    
364