|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.tools.tree; |
|
|
|
import sun.tools.java.*; |
|
import sun.tools.asm.Assembler; |
|
import java.util.Hashtable; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public |
|
class NewInstanceExpression extends NaryExpression { |
|
MemberDefinition field; |
|
Expression outerArg; |
|
ClassDefinition body; |
|
|
|
|
|
MemberDefinition implMethod = null; |
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public Expression getOuterArg() { |
|
return outerArg; |
|
} |
|
|
|
int precedence() { |
|
return 100; |
|
} |
|
|
|
public Expression order() { |
|
|
|
if (outerArg != null && opPrecedence[FIELD] > outerArg.precedence()) { |
|
UnaryExpression e = (UnaryExpression)outerArg; |
|
outerArg = e.right; |
|
e.right = order(); |
|
return e; |
|
} |
|
return this; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Vset checkValue(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
|
|
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. |
|
|
|
alreadyChecked = outerArg; |
|
|
|
|
|
Identifier typeName = FieldExpression.toIdentifier(right); |
|
|
|
// According to the inner classes spec, the type name in a |
|
|
|
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 |
|
|
|
ClassDefinition oc = env.getClassDefinition(outerArg.type); |
|
Identifier nm = oc.resolveInnerClass(env, typeName); |
|
right = new TypeExpression(right.where, Type.tClass(nm)); |
|
|
|
env.resolve(right.where, ctx.field.getClassDefinition(), |
|
right.type); |
|
// and fall through to env.getClassDefinition() below |
|
} |
|
} |
|
|
|
if (!(right instanceof TypeExpression)) { |
|
|
|
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 |
|
|
|
if (def == null) { |
|
type = Type.tError; |
|
return vset; |
|
} |
|
|
|
|
|
Expression args[] = this.args; |
|
args = NewInstanceExpression. |
|
insertOuterLink(env, ctx, where, def, outerArg, args); |
|
if (args.length > this.args.length) |
|
outerArg = args[0]; |
|
else if (outerArg != null) |
|
|
|
outerArg = new CommaExpression(outerArg.where, outerArg, null); |
|
|
|
|
|
Type argTypes[] = new Type[args.length]; |
|
|
|
for (int i = 0 ; i < args.length ; i++) { |
|
|
|
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 { |
|
|
|
if (hasErrors) { |
|
type = Type.tError; |
|
return vset; |
|
} |
|
|
|
|
|
|
|
ClassDefinition sourceClass = ctx.field.getClassDefinition(); |
|
|
|
ClassDeclaration c = env.getClassDeclaration(t); |
|
|
|
|
|
if (body != null) { |
|
|
|
Identifier packageName = sourceClass.getName().getQualifier(); |
|
|
|
|
|
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 |
|
|
|
superDef = env.getClassDefinition(idJavaLangObject); |
|
} else { |
|
|
|
superDef = def; |
|
} |
|
|
|
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 |
|
|
|
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. |
|
|
|
t = body.getClassDeclaration().getType(); |
|
|
|
def = body; |
|
} |
|
} else { |
|
|
|
if (def.isInterface()) { |
|
env.error(where, "new.intf", c); |
|
return vset; |
|
} |
|
|
|
|
|
if (def.mustBeAbstract(env)) { |
|
env.error(where, "new.abstract", c); |
|
return vset; |
|
} |
|
} |
|
|
|
|
|
field = def.matchMethod(env, sourceClass, idInit, argTypes); |
|
|
|
|
|
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) { |
|
|
|
implMethod = cdef.getAccessMember(env, ctx, field, false); |
|
} |
|
} |
|
|
|
|
|
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 |
|
|
|
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; |
|
} |
|
|
|
|
|
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]; |
|
|
|
for (int i = 1 ; i < args.length ; i++) { |
|
this.args[i-1] = args[i]; |
|
} |
|
} |
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
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; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public Vset check(Environment env, Context ctx, Vset vset, Hashtable exp) { |
|
return checkValue(env, ctx, vset, exp); |
|
} |
|
|
|
|
|
|
|
*/ |
|
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]); |
|
} |
|
|
|
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(); |
|
|
|
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 |
|
|
|
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; |
|
} |
|
if (ctx == null) { |
|
return 2 + super.costInline(thresh, env, ctx); |
|
} |
|
|
|
ClassDefinition sourceClass = ctx.field.getClassDefinition(); |
|
try { |
|
// We only allow the inlining if the current class can access |
|
|
|
if ( sourceClass.permitInlinedAccess(env, field.getClassDeclaration()) |
|
&& sourceClass.permitInlinedAccess(env, field)) { |
|
return 2 + super.costInline(thresh, env, ctx); |
|
} |
|
} catch (ClassNotFound e) { |
|
} |
|
return thresh; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
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: |
|
|
|
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 |
|
|
|
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. |
|
|
|
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)); |
|
} |
|
} |