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