blob: cb018a0f51a80b0af94b73036f6c3bd1b03497d1 [file] [log] [blame]
/*
* 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);
}
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));
}
}