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     @SuppressWarnings("fallthrough")
 491     private void codeCommon(Environment env, Context ctx, Assembler asm,
 492                             boolean forValue) {
 493         asm.add(where, opc_new, field.getClassDeclaration());
 494         if (forValue) {
 495             asm.add(where, opc_dup);
 496         }
 497 
 498         ClassDefinition refc = field.getClassDefinition();
 499         UplevelReference r = refc.getReferencesFrozen();
 500 
 501         if (r != null) {
 502             r.codeArguments(env, ctx, asm, where, field);
 503         }
 504 
 505         if (outerArg != null) {
 506             outerArg.codeValue(env, ctx, asm);
 507             switch (outerArg.op) {
 508             case THIS:
 509             case SUPER:
 510             case NEW:
 511                 // guaranteed non-null
 512                 break;
 513             case FIELD: {
 514                 MemberDefinition f = ((FieldExpression)outerArg).field;
 515                 if (f != null && f.isNeverNull()) {
 516                     break;
 517                 }
 518                 // else fall through:
 519             }
 520             default:
 521                 // Test for nullity by invoking some trivial operation
 522                 // that can throw a NullPointerException.
 523                 try {
 524                     ClassDefinition c = env.getClassDefinition(idJavaLangObject);
 525                     MemberDefinition getc = c.getFirstMatch(idGetClass);
 526                     asm.add(where, opc_dup);
 527                     asm.add(where, opc_invokevirtual, getc);
 528                     asm.add(where, opc_pop);
 529                 } catch (ClassNotFound e) {
 530                 }
 531             }
 532         }
 533 
 534         if (implMethod != null) {
 535             // Constructor call will be via an access method.
 536             // Pass 'null' as the value of the dummy argument.
 537             asm.add(where, opc_aconst_null);
 538         }
 539 
 540         for (int i = 0 ; i < args.length ; i++) {
 541             args[i].codeValue(env, ctx, asm);
 542         }
 543         asm.add(where, opc_invokespecial,
 544                 ((implMethod != null) ? implMethod : field));
 545     }
 546 }