/* * Copyright (c) 1994, 2003, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.tools.tree; import sun.tools.java.*; import sun.tools.asm.Assembler; import java.util.Hashtable; /** * WARNING: The contents of this source file are not part of any * supported API. Code that depends on them does so at its own risk: * they are subject to change or removal without notice. */ public class NewInstanceExpression extends NaryExpression { MemberDefinition field; Expression outerArg; ClassDefinition body; // Access method for constructor, if needed. MemberDefinition implMethod = null; /** * Constructor */ public NewInstanceExpression(long where, Expression right, Expression args[]) { super(NEWINSTANCE, where, Type.tError, right, args); } public NewInstanceExpression(long where, Expression right, Expression args[], Expression outerArg, ClassDefinition body) { this(where, right, args); this.outerArg = outerArg; this.body = body; } /** * From the "new" in an expression of the form outer.new InnerCls(...), * return the "outer" expression, or null if there is none. */ public Expression getOuterArg() { return outerArg; } int precedence() { return 100; } public Expression order() { // act like a method or field reference expression: if (outerArg != null && opPrecedence[FIELD] > outerArg.precedence()) { UnaryExpression e = (UnaryExpression)outerArg; outerArg = e.right; e.right = order(); return e; } return this; } /** * Check expression type */ public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) { // What type? ClassDefinition def = null; Expression alreadyChecked = null; try { if (outerArg != null) { vset = outerArg.checkValue(env, ctx, vset, exp); // Remember the expression that we already checked // so that we don't attempt to check it again when // it appears as an argument to the constructor. // Fix for 4030426. alreadyChecked = outerArg; // Check outerArg and the type name together. Identifier typeName = FieldExpression.toIdentifier(right); // According to the inner classes spec, the type name in a // qualified 'new' expression must be a single identifier. if (typeName != null && typeName.isQualified()) { env.error(where, "unqualified.name.required", typeName); } if (typeName == null || !outerArg.type.isType(TC_CLASS)) { if (!outerArg.type.isType(TC_ERROR)) { env.error(where, "invalid.field.reference", idNew, outerArg.type); } outerArg = null; } else { // Don't perform checks on components of qualified name // ('getQualifiedClassDefinition'), because a qualified // name is illegal in this context, and will have previously // been reported as an error. ClassDefinition oc = env.getClassDefinition(outerArg.type); Identifier nm = oc.resolveInnerClass(env, typeName); right = new TypeExpression(right.where, Type.tClass(nm)); // Check access directly, since we're not calling toType(). env.resolve(right.where, ctx.field.getClassDefinition(), right.type); // and fall through to env.getClassDefinition() below } } if (!(right instanceof TypeExpression)) { // The call to 'toType' should perform component access checks. right = new TypeExpression(right.where, right.toType(env, ctx)); } if (right.type.isType(TC_CLASS)) def = env.getClassDefinition(right.type); } catch (AmbiguousClass ee) { env.error(where, "ambig.class", ee.name1, ee.name2); } catch (ClassNotFound ee) { env.error(where, "class.not.found", ee.name, ctx.field); } Type t = right.type; boolean hasErrors = t.isType(TC_ERROR); if (!t.isType(TC_CLASS)) { if (!hasErrors) { env.error(where, "invalid.arg.type", t, opNames[op]); hasErrors = true; } } // If we failed to find a class or a class was ambiguous, def // may be null. Bail out. This allows us to report multiple // unfound or ambiguous classes rather than tripping over an // internal compiler error. if (def == null) { type = Type.tError; return vset; } // Add an extra argument, maybe. Expression args[] = this.args; args = NewInstanceExpression. insertOuterLink(env, ctx, where, def, outerArg, args); if (args.length > this.args.length) outerArg = args[0]; // recopy the checked arg else if (outerArg != null) // else set it to void (maybe it has a side-effect) outerArg = new CommaExpression(outerArg.where, outerArg, null); // Compose a list of argument types Type argTypes[] = new Type[args.length]; for (int i = 0 ; i < args.length ; i++) { // Don't check 'outerArg' again. Fix for 4030426. if (args[i] != alreadyChecked) { vset = args[i].checkValue(env, ctx, vset, exp); } argTypes[i] = args[i].type; hasErrors = hasErrors || argTypes[i].isType(TC_ERROR); } try { // Check if there are any type errors in the arguments if (hasErrors) { type = Type.tError; return vset; } // Get the source class that this declaration appears in. ClassDefinition sourceClass = ctx.field.getClassDefinition(); ClassDeclaration c = env.getClassDeclaration(t); // If this is an anonymous class, handle it specially now. if (body != null) { // The current package. Identifier packageName = sourceClass.getName().getQualifier(); // This is an anonymous class. ClassDefinition superDef = null; if (def.isInterface()) { // For interfaces, our superclass is java.lang.Object. // We could just assume that java.lang.Object has // one constructor with no arguments in the code // that follows, but we don't. This way, if Object // grows a new constructor (unlikely) then the // compiler should handle it. superDef = env.getClassDefinition(idJavaLangObject); } else { // Otherwise, def is actually our superclass. superDef = def; } // Try to find a matching constructor in our superclass. MemberDefinition constructor = superDef.matchAnonConstructor(env, packageName, argTypes); if (constructor != null) { // We've found one. Process the body. // // Note that we are passing in the constructors' argument // types, rather than the argument types of the actual // expressions, to checkLocalClass(). Previously, // the expression types were passed in. This could // lead to trouble when one of the argument types was // the special internal type tNull. (bug 4054689). if (tracing) env.dtEvent( "NewInstanceExpression.checkValue: ANON CLASS " + body + " SUPER " + def); vset = body.checkLocalClass(env, ctx, vset, def, args, constructor.getType() .getArgumentTypes()); // Set t to be the true type of this expression. // (bug 4102056). t = body.getClassDeclaration().getType(); def = body; } } else { // Check if it is an interface if (def.isInterface()) { env.error(where, "new.intf", c); return vset; } // Check for abstract class if (def.mustBeAbstract(env)) { env.error(where, "new.abstract", c); return vset; } } // Get the constructor that the "new" expression should call. field = def.matchMethod(env, sourceClass, idInit, argTypes); // Report an error if there is no matching constructor. if (field == null) { MemberDefinition anyInit = def.findAnyMethod(env, idInit); if (anyInit != null && new MethodExpression(where, right, anyInit, args) .diagnoseMismatch(env, args, argTypes)) return vset; String sig = c.getName().getName().toString(); sig = Type.tMethod(Type.tError, argTypes).typeString(sig, false, false); env.error(where, "unmatched.constr", sig, c); return vset; } if (field.isPrivate()) { ClassDefinition cdef = field.getClassDefinition(); if (cdef != sourceClass) { // Use access method. implMethod = cdef.getAccessMember(env, ctx, field, false); } } // Check for abstract anonymous class if (def.mustBeAbstract(env)) { env.error(where, "new.abstract", c); return vset; } if (field.reportDeprecated(env)) { env.error(where, "warn.constr.is.deprecated", field, field.getClassDefinition()); } // According to JLS 6.6.2, a protected constructor may be accessed // by a class instance creation expression only from within the // package in which it is defined. if (field.isProtected() && !(sourceClass.getName().getQualifier().equals( field.getClassDeclaration().getName().getQualifier()))) { env.error(where, "invalid.protected.constructor.use", sourceClass); } } catch (ClassNotFound ee) { env.error(where, "class.not.found", ee.name, opNames[op]); return vset; } catch (AmbiguousMember ee) { env.error(where, "ambig.constr", ee.field1, ee.field2); return vset; } // Cast arguments argTypes = field.getType().getArgumentTypes(); for (int i = 0 ; i < args.length ; i++) { args[i] = convert(env, ctx, argTypes[i], args[i]); } if (args.length > this.args.length) { outerArg = args[0]; // recopy the checked arg // maintain an accurate tree for (int i = 1 ; i < args.length ; i++) { this.args[i-1] = args[i]; } } // Throw the declared exceptions. ClassDeclaration exceptions[] = field.getExceptions(env); for (int i = 0 ; i < exceptions.length ; i++) { if (exp.get(exceptions[i]) == null) { exp.put(exceptions[i], this); } } type = t; return vset; } /** * Given a list of arguments for a constructor, * return a possibly modified list which includes the hidden * argument which initializes the uplevel self pointer. * @arg def the class which perhaps contains an outer link. * @arg outerArg if non-null, an explicit location in which to construct. */ public static Expression[] insertOuterLink(Environment env, Context ctx, long where, ClassDefinition def, Expression outerArg, Expression args[]) { if (!def.isTopLevel() && !def.isLocal()) { Expression args2[] = new Expression[1+args.length]; System.arraycopy(args, 0, args2, 1, args.length); try { if (outerArg == null) outerArg = ctx.findOuterLink(env, where, def.findAnyMethod(env, idInit)); } catch (ClassNotFound e) { // die somewhere else } args2[0] = outerArg; args = args2; } return args; } /** * Check void expression */ public Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) { return checkValue(env, ctx, vset, exp); } /** * Inline */ final int MAXINLINECOST = Statement.MAXINLINECOST; public Expression copyInline(Context ctx) { NewInstanceExpression e = (NewInstanceExpression)super.copyInline(ctx); if (outerArg != null) { e.outerArg = outerArg.copyInline(ctx); } return e; } Expression inlineNewInstance(Environment env, Context ctx, Statement s) { if (env.dump()) { System.out.println("INLINE NEW INSTANCE " + field + " in " + ctx.field); } LocalMember v[] = LocalMember.copyArguments(ctx, field); Statement body[] = new Statement[v.length + 2]; int o = 1; if (outerArg != null && !outerArg.type.isType(TC_VOID)) { o = 2; body[1] = new VarDeclarationStatement(where, v[1], outerArg); } else if (outerArg != null) { body[0] = new ExpressionStatement(where, outerArg); } for (int i = 0 ; i < args.length ; i++) { body[i+o] = new VarDeclarationStatement(where, v[i+o], args[i]); } //System.out.print("BEFORE:"); s.print(System.out); System.out.println(); body[body.length - 1] = (s != null) ? s.copyInline(ctx, false) : null; //System.out.print("COPY:"); body[body.length - 1].print(System.out); System.out.println(); //System.out.print("AFTER:"); s.print(System.out); System.out.println(); LocalMember.doneWithArguments(ctx, v); return new InlineNewInstanceExpression(where, type, field, new CompoundStatement(where, body)).inline(env, ctx); } public Expression inline(Environment env, Context ctx) { return inlineValue(env, ctx); } public Expression inlineValue(Environment env, Context ctx) { if (body != null) { body.inlineLocalClass(env); } ClassDefinition refc = field.getClassDefinition(); UplevelReference r = refc.getReferencesFrozen(); if (r != null) { r.willCodeArguments(env, ctx); } //right = right.inlineValue(env, ctx); try { if (outerArg != null) { if (outerArg.type.isType(TC_VOID)) outerArg = outerArg.inline(env, ctx); else outerArg = outerArg.inlineValue(env, ctx); } for (int i = 0 ; i < args.length ; i++) { args[i] = args[i].inlineValue(env, ctx); } // This 'false' that fy put in is inexplicable to me // the decision to not inline new instance expressions // should be revisited. - dps if (false && env.opt() && field.isInlineable(env, false) && (!ctx.field.isInitializer()) && ctx.field.isMethod() && (ctx.getInlineMemberContext(field) == null)) { Statement s = (Statement)field.getValue(env); if ((s == null) || (s.costInline(MAXINLINECOST, env, ctx) < MAXINLINECOST)) { return inlineNewInstance(env, ctx, s); } } } catch (ClassNotFound e) { throw new CompilerError(e); } if (outerArg != null && outerArg.type.isType(TC_VOID)) { Expression e = outerArg; outerArg = null; return new CommaExpression(where, e, this); } return this; } public int costInline(int thresh, Environment env, Context ctx) { if (body != null) { return thresh; // don't copy classes... } if (ctx == null) { return 2 + super.costInline(thresh, env, ctx); } // sourceClass is the current class trying to inline this method ClassDefinition sourceClass = ctx.field.getClassDefinition(); try { // We only allow the inlining if the current class can access // the field and the field's class; if ( sourceClass.permitInlinedAccess(env, field.getClassDeclaration()) && sourceClass.permitInlinedAccess(env, field)) { return 2 + super.costInline(thresh, env, ctx); } } catch (ClassNotFound e) { } return thresh; } /** * Code */ public void code(Environment env, Context ctx, Assembler asm) { codeCommon(env, ctx, asm, false); } public void codeValue(Environment env, Context ctx, Assembler asm) { codeCommon(env, ctx, asm, true); } @SuppressWarnings("fallthrough") private void codeCommon(Environment env, Context ctx, Assembler asm, boolean forValue) { asm.add(where, opc_new, field.getClassDeclaration()); if (forValue) { asm.add(where, opc_dup); } ClassDefinition refc = field.getClassDefinition(); UplevelReference r = refc.getReferencesFrozen(); if (r != null) { r.codeArguments(env, ctx, asm, where, field); } if (outerArg != null) { outerArg.codeValue(env, ctx, asm); switch (outerArg.op) { case THIS: case SUPER: case NEW: // guaranteed non-null break; case FIELD: { MemberDefinition f = ((FieldExpression)outerArg).field; if (f != null && f.isNeverNull()) { break; } // else fall through: } default: // Test for nullity by invoking some trivial operation // that can throw a NullPointerException. try { ClassDefinition c = env.getClassDefinition(idJavaLangObject); MemberDefinition getc = c.getFirstMatch(idGetClass); asm.add(where, opc_dup); asm.add(where, opc_invokevirtual, getc); asm.add(where, opc_pop); } catch (ClassNotFound e) { } } } if (implMethod != null) { // Constructor call will be via an access method. // Pass 'null' as the value of the dummy argument. asm.add(where, opc_aconst_null); } for (int i = 0 ; i < args.length ; i++) { args[i].codeValue(env, ctx, asm); } asm.add(where, opc_invokespecial, ((implMethod != null) ? implMethod : field)); } }