| /* |
| * 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<Object, Object> 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<Object, Object> 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)); |
| } |
| } |