View Javadoc
1   /*
2    * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.tools.tree;
27  
28  import sun.tools.java.*;
29  import sun.tools.asm.Assembler;
30  import java.util.Hashtable;
31  
32  /**
33   * WARNING: The contents of this source file are not part of any
34   * supported API.  Code that depends on them does so at its own risk:
35   * they are subject to change or removal without notice.
36   */
37  public
38  class NewInstanceExpression extends NaryExpression {
39      MemberDefinition field;
40      Expression outerArg;
41      ClassDefinition body;
42  
43      // Access method for constructor, if needed.
44      MemberDefinition implMethod = null;
45  
46      /**
47       * Constructor
48       */
49      public NewInstanceExpression(long where, Expression right, Expression args[]) {
50          super(NEWINSTANCE, where, Type.tError, right, args);
51      }
52      public NewInstanceExpression(long where, Expression right,
53                                   Expression args[],
54                                   Expression outerArg, ClassDefinition body) {
55          this(where, right, args);
56          this.outerArg = outerArg;
57          this.body = body;
58      }
59  
60      /**
61       * From the "new" in an expression of the form outer.new InnerCls(...),
62       * return the "outer" expression, or null if there is none.
63       */
64      public Expression getOuterArg() {
65          return outerArg;
66      }
67  
68      int precedence() {
69          return 100;
70      }
71  
72      public Expression order() {
73          // act like a method or field reference expression:
74          if (outerArg != null && opPrecedence[FIELD] > outerArg.precedence()) {
75              UnaryExpression e = (UnaryExpression)outerArg;
76              outerArg = e.right;
77              e.right = order();
78              return e;
79          }
80          return this;
81      }
82  
83      /**
84       * Check expression type
85       */
86      public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) {
87          // What type?
88          ClassDefinition def = null;
89  
90          Expression alreadyChecked = null;
91  
92          try {
93              if (outerArg != null) {
94                  vset = outerArg.checkValue(env, ctx, vset, exp);
95  
96                  // Remember the expression that we already checked
97                  // so that we don't attempt to check it again when
98                  // it appears as an argument to the constructor.
99                  // Fix for 4030426.
100                 alreadyChecked = outerArg;
101 
102                 // Check outerArg and the type name together.
103                 Identifier typeName = FieldExpression.toIdentifier(right);
104 
105                 // According to the inner classes spec, the type name in a
106                 // qualified 'new' expression must be a single identifier.
107                 if (typeName != null && typeName.isQualified()) {
108                     env.error(where, "unqualified.name.required", typeName);
109                 }
110 
111                 if (typeName == null || !outerArg.type.isType(TC_CLASS)) {
112                     if (!outerArg.type.isType(TC_ERROR)) {
113                         env.error(where, "invalid.field.reference",
114                                   idNew, outerArg.type);
115                     }
116                     outerArg = null;
117                 } else {
118                     // Don't perform checks on components of qualified name
119                     // ('getQualifiedClassDefinition'), because a qualified
120                     // name is illegal in this context, and will have previously
121                     // been reported as an error.
122                     ClassDefinition oc = env.getClassDefinition(outerArg.type);
123                     Identifier nm = oc.resolveInnerClass(env, typeName);
124                     right = new TypeExpression(right.where, Type.tClass(nm));
125                     // Check access directly, since we're not calling toType().
126                     env.resolve(right.where, ctx.field.getClassDefinition(),
127                                 right.type);
128                     // and fall through to env.getClassDefinition() below
129                 }
130             }
131 
132             if (!(right instanceof TypeExpression)) {
133                 // The call to 'toType' should perform component access checks.
134                 right = new TypeExpression(right.where, right.toType(env, ctx));
135             }
136 
137             if (right.type.isType(TC_CLASS))
138                 def = env.getClassDefinition(right.type);
139         } catch (AmbiguousClass ee) {
140             env.error(where, "ambig.class", ee.name1, ee.name2);
141         } catch (ClassNotFound ee) {
142             env.error(where, "class.not.found", ee.name, ctx.field);
143         }
144 
145         Type t = right.type;
146         boolean hasErrors = t.isType(TC_ERROR);
147 
148         if (!t.isType(TC_CLASS)) {
149             if (!hasErrors) {
150                 env.error(where, "invalid.arg.type", t, opNames[op]);
151                 hasErrors = true;
152             }
153         }
154 
155         // If we failed to find a class or a class was ambiguous, def
156         // may be null.  Bail out.  This allows us to report multiple
157         // unfound or ambiguous classes rather than tripping over an
158         // internal compiler error.
159         if (def == null) {
160             type = Type.tError;
161             return vset;
162         }
163 
164         // Add an extra argument, maybe.
165         Expression args[] = this.args;
166         args = NewInstanceExpression.
167                 insertOuterLink(env, ctx, where, def, outerArg, args);
168         if (args.length > this.args.length)
169             outerArg = args[0]; // recopy the checked arg
170         else if (outerArg != null)
171             // else set it to void (maybe it has a side-effect)
172             outerArg = new CommaExpression(outerArg.where, outerArg, null);
173 
174         // Compose a list of argument types
175         Type argTypes[] = new Type[args.length];
176 
177         for (int i = 0 ; i < args.length ; i++) {
178             // Don't check 'outerArg' again. Fix for 4030426.
179             if (args[i] != alreadyChecked) {
180                 vset = args[i].checkValue(env, ctx, vset, exp);
181             }
182             argTypes[i] = args[i].type;
183             hasErrors = hasErrors || argTypes[i].isType(TC_ERROR);
184         }
185 
186         try {
187             // Check if there are any type errors in the arguments
188             if (hasErrors) {
189                 type = Type.tError;
190                 return vset;
191             }
192 
193 
194             // Get the source class that this declaration appears in.
195             ClassDefinition sourceClass = ctx.field.getClassDefinition();
196 
197             ClassDeclaration c = env.getClassDeclaration(t);
198 
199             // If this is an anonymous class, handle it specially now.
200             if (body != null) {
201                 // The current package.
202                 Identifier packageName = sourceClass.getName().getQualifier();
203 
204                 // This is an anonymous class.
205                 ClassDefinition superDef = null;
206                 if (def.isInterface()) {
207                     // For interfaces, our superclass is java.lang.Object.
208                     // We could just assume that java.lang.Object has
209                     // one constructor with no arguments in the code
210                     // that follows, but we don't.  This way, if Object
211                     // grows a new constructor (unlikely) then the
212                     // compiler should handle it.
213                     superDef = env.getClassDefinition(idJavaLangObject);
214                 } else {
215                     // Otherwise, def is actually our superclass.
216                     superDef = def;
217                 }
218                 // Try to find a matching constructor in our superclass.
219                 MemberDefinition constructor =
220                     superDef.matchAnonConstructor(env, packageName, argTypes);
221                 if (constructor != null) {
222                     // We've found one.  Process the body.
223                     //
224                     // Note that we are passing in the constructors' argument
225                     // types, rather than the argument types of the actual
226                     // expressions, to checkLocalClass().  Previously,
227                     // the expression types were passed in.  This could
228                     // lead to trouble when one of the argument types was
229                     // the special internal type tNull.  (bug 4054689).
230                     if (tracing)
231                         env.dtEvent(
232                               "NewInstanceExpression.checkValue: ANON CLASS " +
233                               body + " SUPER " + def);
234                     vset = body.checkLocalClass(env, ctx, vset,
235                                                 def, args,
236                                                 constructor.getType()
237                                                 .getArgumentTypes());
238 
239                     // Set t to be the true type of this expression.
240                     // (bug 4102056).
241                     t = body.getClassDeclaration().getType();
242 
243                     def = body;
244                 }
245             } else {
246                 // Check if it is an interface
247                 if (def.isInterface()) {
248                     env.error(where, "new.intf", c);
249                     return vset;
250                 }
251 
252                 // Check for abstract class
253                 if (def.mustBeAbstract(env)) {
254                     env.error(where, "new.abstract", c);
255                     return vset;
256                 }
257             }
258 
259             // Get the constructor that the "new" expression should call.
260             field = def.matchMethod(env, sourceClass, idInit, argTypes);
261 
262             // Report an error if there is no matching constructor.
263             if (field == null) {
264                 MemberDefinition anyInit = def.findAnyMethod(env, idInit);
265                 if (anyInit != null &&
266                     new MethodExpression(where, right, anyInit, args)
267                         .diagnoseMismatch(env, args, argTypes))
268                     return vset;
269                 String sig = c.getName().getName().toString();
270                 sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false);
271                 env.error(where, "unmatched.constr", sig, c);
272                 return vset;
273             }
274 
275             if (field.isPrivate()) {
276                 ClassDefinition cdef = field.getClassDefinition();
277                 if (cdef != sourceClass) {
278                     // Use access method.
279                     implMethod = cdef.getAccessMember(env, ctx, field, false);
280                 }
281             }
282 
283             // Check for abstract anonymous class
284             if (def.mustBeAbstract(env)) {
285                 env.error(where, "new.abstract", c);
286                 return vset;
287             }
288 
289             if (field.reportDeprecated(env)) {
290                 env.error(where, "warn.constr.is.deprecated",
291                           field, field.getClassDefinition());
292             }
293 
294             // According to JLS 6.6.2, a protected constructor may be accessed
295             // by a class instance creation expression only from within the
296             // package in which it is defined.
297             if (field.isProtected() &&
298                 !(sourceClass.getName().getQualifier().equals(
299                    field.getClassDeclaration().getName().getQualifier()))) {
300                 env.error(where, "invalid.protected.constructor.use",
301                           sourceClass);
302             }
303 
304         } catch (ClassNotFound ee) {
305             env.error(where, "class.not.found", ee.name, opNames[op]);
306             return vset;
307 
308         } catch (AmbiguousMember ee) {
309             env.error(where, "ambig.constr", ee.field1, ee.field2);
310             return vset;
311         }
312 
313         // Cast arguments
314         argTypes = field.getType().getArgumentTypes();
315         for (int i = 0 ; i < args.length ; i++) {
316             args[i] = convert(env, ctx, argTypes[i], args[i]);
317         }
318         if (args.length > this.args.length) {
319             outerArg = args[0]; // recopy the checked arg
320             // maintain an accurate tree
321             for (int i = 1 ; i < args.length ; i++) {
322                 this.args[i-1] = args[i];
323             }
324         }
325 
326         // Throw the declared exceptions.
327         ClassDeclaration exceptions[] = field.getExceptions(env);
328         for (int i = 0 ; i < exceptions.length ; i++) {
329             if (exp.get(exceptions[i]) == null) {
330                 exp.put(exceptions[i], this);
331             }
332         }
333 
334         type = t;
335 
336         return vset;
337     }
338 
339     /**
340      * Given a list of arguments for a constructor,
341      * return a possibly modified list which includes the hidden
342      * argument which initializes the uplevel self pointer.
343      * @arg def the class which perhaps contains an outer link.
344      * @arg outerArg if non-null, an explicit location in which to construct.
345      */
346     public static Expression[] insertOuterLink(Environment env, Context ctx,
347                                                long where, ClassDefinition def,
348                                                Expression outerArg,
349                                                Expression args[]) {
350         if (!def.isTopLevel() && !def.isLocal()) {
351             Expression args2[] = new Expression[1+args.length];
352             System.arraycopy(args, 0, args2, 1, args.length);
353             try {
354                 if (outerArg == null)
355                     outerArg = ctx.findOuterLink(env, where,
356                                                  def.findAnyMethod(env, idInit));
357             } catch (ClassNotFound e) {
358                 // die somewhere else
359             }
360             args2[0] = outerArg;
361             args = args2;
362         }
363         return args;
364     }
365 
366     /**
367      * Check void expression
368      */
369     public Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) {
370         return checkValue(env, ctx, vset, exp);
371     }
372 
373     /**
374      * Inline
375      */
376     final int MAXINLINECOST = Statement.MAXINLINECOST;
377 
378     public Expression copyInline(Context ctx) {
379         NewInstanceExpression e = (NewInstanceExpression)super.copyInline(ctx);
380         if (outerArg != null) {
381             e.outerArg = outerArg.copyInline(ctx);
382         }
383         return e;
384     }
385 
386     Expression inlineNewInstance(Environment env, Context ctx, Statement s) {
387         if (env.dump()) {
388             System.out.println("INLINE NEW INSTANCE " + field + " in " + ctx.field);
389         }
390         LocalMember v[] = LocalMember.copyArguments(ctx, field);
391         Statement body[] = new Statement[v.length + 2];
392 
393         int o = 1;
394         if (outerArg != null && !outerArg.type.isType(TC_VOID)) {
395             o = 2;
396             body[1] = new VarDeclarationStatement(where, v[1], outerArg);
397         } else if (outerArg != null) {
398             body[0] = new ExpressionStatement(where, outerArg);
399         }
400         for (int i = 0 ; i < args.length ; i++) {
401             body[i+o] = new VarDeclarationStatement(where, v[i+o], args[i]);
402         }
403         //System.out.print("BEFORE:"); s.print(System.out); System.out.println();
404         body[body.length - 1] = (s != null) ? s.copyInline(ctx, false) : null;
405         //System.out.print("COPY:"); body[body.length - 1].print(System.out); System.out.println();
406         //System.out.print("AFTER:"); s.print(System.out); System.out.println();
407         LocalMember.doneWithArguments(ctx, v);
408 
409         return new InlineNewInstanceExpression(where, type, field, new CompoundStatement(where, body)).inline(env, ctx);
410     }
411 
412     public Expression inline(Environment env, Context ctx) {
413         return inlineValue(env, ctx);
414     }
415     public Expression inlineValue(Environment env, Context ctx) {
416         if (body != null) {
417             body.inlineLocalClass(env);
418         }
419         ClassDefinition refc = field.getClassDefinition();
420         UplevelReference r = refc.getReferencesFrozen();
421         if (r != null) {
422             r.willCodeArguments(env, ctx);
423         }
424         //right = right.inlineValue(env, ctx);
425 
426         try {
427             if (outerArg != null) {
428                 if (outerArg.type.isType(TC_VOID))
429                     outerArg = outerArg.inline(env, ctx);
430                 else
431                     outerArg = outerArg.inlineValue(env, ctx);
432             }
433             for (int i = 0 ; i < args.length ; i++) {
434                 args[i] = args[i].inlineValue(env, ctx);
435             }
436             // This 'false' that fy put in is inexplicable to me
437             // the decision to not inline new instance expressions
438             // should be revisited.  - dps
439             if (false && env.opt() && field.isInlineable(env, false) &&
440                 (!ctx.field.isInitializer()) && ctx.field.isMethod() &&
441                 (ctx.getInlineMemberContext(field) == null)) {
442                 Statement s = (Statement)field.getValue(env);
443                 if ((s == null)
444                     || (s.costInline(MAXINLINECOST, env, ctx) < MAXINLINECOST))  {
445                     return inlineNewInstance(env, ctx, s);
446                 }
447             }
448         } catch (ClassNotFound e) {
449             throw new CompilerError(e);
450         }
451         if (outerArg != null && outerArg.type.isType(TC_VOID)) {
452             Expression e = outerArg;
453             outerArg = null;
454             return new CommaExpression(where, e, this);
455         }
456         return this;
457     }
458 
459     public int costInline(int thresh, Environment env, Context ctx) {
460         if (body != null) {
461             return thresh;      // don't copy classes...
462         }
463         if (ctx == null) {
464             return 2 + super.costInline(thresh, env, ctx);
465         }
466         // sourceClass is the current class trying to inline this method
467         ClassDefinition sourceClass = ctx.field.getClassDefinition();
468         try {
469             // We only allow the inlining if the current class can access
470             // the field and the field's class;
471             if (    sourceClass.permitInlinedAccess(env, field.getClassDeclaration())
472                  && sourceClass.permitInlinedAccess(env, field)) {
473                 return 2 + super.costInline(thresh, env, ctx);
474             }
475         } catch (ClassNotFound e) {
476         }
477         return thresh;
478     }
479 
480 
481     /**
482      * Code
483      */
484     public void code(Environment env, Context ctx, Assembler asm) {
485         codeCommon(env, ctx, asm, false);
486     }
487     public void codeValue(Environment env, Context ctx, Assembler asm) {
488         codeCommon(env, ctx, asm, true);
489     }
490     private void codeCommon(Environment env, Context ctx, Assembler asm,
491                             boolean forValue) {
492         asm.add(where, opc_new, field.getClassDeclaration());
493         if (forValue) {
494             asm.add(where, opc_dup);
495         }
496 
497         ClassDefinition refc = field.getClassDefinition();
498         UplevelReference r = refc.getReferencesFrozen();
499 
500         if (r != null) {
501             r.codeArguments(env, ctx, asm, where, field);
502         }
503 
504         if (outerArg != null) {
505             outerArg.codeValue(env, ctx, asm);
506             switch (outerArg.op) {
507             case THIS:
508             case SUPER:
509             case NEW:
510                 // guaranteed non-null
511                 break;
512             case FIELD: {
513                 MemberDefinition f = ((FieldExpression)outerArg).field;
514                 if (f != null && f.isNeverNull()) {
515                     break;
516                 }
517                 // else fall through:
518             }
519             default:
520                 // Test for nullity by invoking some trivial operation
521                 // that can throw a NullPointerException.
522                 try {
523                     ClassDefinition c = env.getClassDefinition(idJavaLangObject);
524                     MemberDefinition getc = c.getFirstMatch(idGetClass);
525                     asm.add(where, opc_dup);
526                     asm.add(where, opc_invokevirtual, getc);
527                     asm.add(where, opc_pop);
528                 } catch (ClassNotFound e) {
529                 }
530             }
531         }
532 
533         if (implMethod != null) {
534             // Constructor call will be via an access method.
535             // Pass 'null' as the value of the dummy argument.
536             asm.add(where, opc_aconst_null);
537         }
538 
539         for (int i = 0 ; i < args.length ; i++) {
540             args[i].codeValue(env, ctx, asm);
541         }
542         asm.add(where, opc_invokespecial,
543                 ((implMethod != null) ? implMethod : field));
544     }
545 }