package org.eclipse.jdt.internal.compiler.ast; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.jdt.internal.compiler.IAbstractSyntaxTreeVisitor; | |
import org.eclipse.jdt.internal.compiler.impl.*; | |
import org.eclipse.jdt.internal.compiler.codegen.*; | |
import org.eclipse.jdt.internal.compiler.flow.*; | |
import org.eclipse.jdt.internal.compiler.lookup.*; | |
public class QualifiedAllocationExpression extends AllocationExpression { | |
//qualification may be on both side | |
public Expression enclosingInstance; | |
public AnonymousLocalTypeDeclaration anonymousType; | |
public QualifiedAllocationExpression() { | |
} | |
public QualifiedAllocationExpression(AnonymousLocalTypeDeclaration anonymous) { | |
anonymousType = anonymous ; | |
} | |
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { | |
// variation on allocation, where can be specified an enclosing instance and an anonymous type | |
// analyse the enclosing instance | |
if (enclosingInstance != null) { | |
flowInfo = enclosingInstance.analyseCode(currentScope, flowContext, flowInfo); | |
} | |
// process arguments | |
if (arguments != null) { | |
for (int i = 0, count = arguments.length; i < count; i++) { | |
flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo); | |
} | |
} | |
// record some dependency information for exception types | |
int count; | |
ReferenceBinding[] thrownExceptions; | |
if ((count = (thrownExceptions = binding.thrownExceptions).length) != 0) { | |
// check exception handling | |
flowContext.checkExceptionHandlers(thrownExceptions, this, flowInfo, currentScope); | |
} | |
// analyse the anonymous nested type | |
if (anonymousType != null) { | |
flowInfo = anonymousType.analyseCode(currentScope, flowContext, flowInfo); | |
} | |
manageEnclosingInstanceAccessIfNecessary(currentScope); | |
manageSyntheticAccessIfNecessary(currentScope); | |
return flowInfo; | |
} | |
public Expression enclosingInstance() { | |
return enclosingInstance; | |
} | |
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) { | |
int pc = codeStream.position; | |
ReferenceBinding allocatedType = binding.declaringClass; | |
if (allocatedType.isLocalType()) { | |
LocalTypeBinding localType = (LocalTypeBinding) allocatedType; | |
localType.constantPoolName(codeStream.classFile.outerMostEnclosingClassFile().computeConstantPoolName(localType)); | |
} | |
if (syntheticAccessor == null) { | |
codeStream.new_(allocatedType); | |
if (valueRequired) { | |
codeStream.dup(); | |
} | |
// better highlight for allocation: display the type individually | |
codeStream.recordPositionsFrom(pc, type); | |
} | |
// handling innerclass instance allocation | |
if (allocatedType.isNestedType()) { // make sure its name is computed before arguments, since may be necessary for argument emulation | |
codeStream.generateSyntheticArgumentValues(currentScope, allocatedType, enclosingInstance(), this); | |
} | |
// generate the arguments for constructor | |
if (arguments != null) { | |
for (int i = 0, count = arguments.length; i < count; i++) { | |
arguments[i].generateCode(currentScope, codeStream, true); | |
} | |
} | |
// invoke constructor | |
if (syntheticAccessor == null) { | |
codeStream.invokespecial(binding); | |
} else { | |
codeStream.invokestatic(syntheticAccessor); | |
} | |
codeStream.recordPositionsFrom(pc, this); | |
if (anonymousType != null) { | |
anonymousType.generateCode(currentScope, codeStream); | |
} | |
} | |
public boolean isSuperAccess() { | |
// necessary to lookup super constructor of anonymous type | |
return anonymousType != null; | |
} | |
/* Inner emulation consists in either recording a dependency | |
* link only, or performing one level of propagation. | |
* | |
* Dependency mechanism is used whenever dealing with source target | |
* types, since by the time we reach them, we might not yet know their | |
* exact need. | |
*/ | |
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope) { | |
ReferenceBinding invocationType, allocatedType; | |
// perform some emulation work in case there is some and we are inside a local type only | |
if ((allocatedType = binding.declaringClass).isNestedType() && currentScope.enclosingSourceType().isLocalType()) { | |
if (allocatedType.isLocalType()){ | |
((LocalTypeBinding)allocatedType).addInnerEmulationDependent(currentScope, enclosingInstance != null, false); // request cascade of accesses | |
} else { | |
// locally propagate, since we already now the desired shape for sure | |
currentScope.propagateInnerEmulation(allocatedType, enclosingInstance != null, false); // request cascade of accesses | |
} | |
} | |
} | |
public TypeBinding resolveType(BlockScope scope) { | |
if (anonymousType == null && enclosingInstance == null) | |
return super.resolveType(scope); // added for code assist... is not possible with 'normal' code | |
// Propagate the type checking to the arguments, and checks if the constructor is defined. | |
// ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt | |
// ClassInstanceCreationExpression ::= Name '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt | |
// ==> by construction, when there is an enclosing instance the typename may NOT be qualified | |
// ==> therefore by construction the type is always a SingleTypeReferenceType instead of being either | |
// sometime a SingleTypeReference and sometime a QualifedTypeReference | |
constant = NotAConstant; | |
TypeBinding enclosingInstTb = null; | |
TypeBinding recType; | |
if (anonymousType == null) { | |
//----------------no anonymous class------------------------ | |
if ((enclosingInstTb = enclosingInstance.resolveType(scope)) == null) | |
return null; | |
if (enclosingInstTb.isBaseType() | enclosingInstTb.isArrayType()) { | |
scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(enclosingInstTb, enclosingInstance); | |
return null; | |
} | |
recType = ((SingleTypeReference) type).resolveTypeEnclosing(scope, (ReferenceBinding) enclosingInstTb); // will check for null after args are resolved | |
TypeBinding[] argumentTypes = NoParameters; | |
if (arguments != null) { | |
boolean argHasError = false; | |
int length = arguments.length; | |
argumentTypes = new TypeBinding[length]; | |
for (int i = 0; i < length; i++) | |
if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) | |
argHasError = true; | |
if (argHasError) | |
return recType; | |
} | |
if (recType == null) | |
return null; | |
if (!recType.canBeInstantiated()) { | |
scope.problemReporter().cannotInstantiate(type, recType); | |
return recType; | |
} | |
if ((binding = scope.getConstructor((ReferenceBinding) recType, argumentTypes, this)).isValidBinding()) { | |
if (isMethodUseDeprecated(binding, scope)) | |
scope.problemReporter().deprecatedMethod(binding, this); | |
if (arguments != null) | |
for (int i = 0; i < arguments.length; i++) | |
arguments[i].implicitWidening(binding.parameters[i], argumentTypes[i]); | |
} else { | |
if (binding.declaringClass == null) | |
binding.declaringClass = (ReferenceBinding) recType; | |
scope.problemReporter().invalidConstructor(this, binding); | |
return recType; | |
} | |
// The enclosing instance must be compatible with the innermost enclosing type | |
ReferenceBinding expectedType = binding.declaringClass.enclosingType(); | |
if (scope.areTypesCompatible(enclosingInstTb, expectedType)) | |
return recType; | |
scope.problemReporter().typeMismatchErrorActualTypeExpectedType(enclosingInstance, enclosingInstTb, expectedType); | |
return recType; | |
} | |
//--------------there is an anonymous type declaration----------------- | |
if (enclosingInstance != null) { | |
if ((enclosingInstTb = enclosingInstance.resolveType(scope)) == null) | |
return null; | |
if (enclosingInstTb.isBaseType() | enclosingInstTb.isArrayType()) { | |
scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(enclosingInstTb, enclosingInstance); | |
return null; | |
} | |
} | |
// due to syntax-construction, recType is a ReferenceBinding | |
recType = | |
(enclosingInstance == null) | |
? type.resolveType(scope) | |
: ((SingleTypeReference) type).resolveTypeEnclosing(scope, (ReferenceBinding) enclosingInstTb); | |
if (recType == null) | |
return null; | |
if (((ReferenceBinding) recType).isFinal()) { | |
scope.problemReporter().anonymousClassCannotExtendFinalClass(type, recType); | |
return null; | |
} | |
TypeBinding[] argumentTypes = NoParameters; | |
if (arguments != null) { | |
int length = arguments.length; | |
argumentTypes = new TypeBinding[length]; | |
for (int i = 0; i < length; i++) | |
if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) | |
return null; | |
} | |
// an anonymous class inherits from java.lang.Object when declared "after" an interface | |
ReferenceBinding superBinding = recType.isInterface() ? scope.getJavaLangObject() : (ReferenceBinding) recType; | |
MethodBinding inheritedBinding = scope.getConstructor(superBinding, argumentTypes, this); | |
if (!inheritedBinding.isValidBinding()) { | |
if (inheritedBinding.declaringClass == null) | |
inheritedBinding.declaringClass = superBinding; | |
scope.problemReporter().invalidConstructor(this, inheritedBinding); | |
return null; | |
} | |
if (enclosingInstance != null) { | |
if (!scope.areTypesCompatible(enclosingInstTb, inheritedBinding.declaringClass.enclosingType())) { | |
scope.problemReporter().typeMismatchErrorActualTypeExpectedType(enclosingInstance, enclosingInstTb, inheritedBinding.declaringClass.enclosingType()); | |
return null; | |
} | |
} | |
// this promotion has to be done somewhere: here or inside the constructor of the | |
// anonymous class. We do it here while the constructor of the inner is then easier. | |
if (arguments != null) | |
for (int i = 0; i < arguments.length; i++) | |
arguments[i].implicitWidening(inheritedBinding.parameters[i], argumentTypes[i]); | |
// Update the anonymous inner class : superclass, interface | |
scope.addAnonymousType(anonymousType, (ReferenceBinding) recType); | |
anonymousType.resolve(scope); | |
binding = anonymousType.createsInternalConstructorWithBinding(inheritedBinding); | |
return anonymousType.binding; // 1.2 change | |
} | |
public String toStringExpression(int tab) { | |
/*slow code */ | |
String s = ""/*nonNLS*/; | |
if (enclosingInstance != null) | |
s += enclosingInstance.toString() + "."/*nonNLS*/; | |
s += super.toStringExpression(tab); | |
if (anonymousType != null) { | |
s += anonymousType.toString(tab); | |
} //allows to restart just after the } one line under .... | |
return s; | |
} | |
public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) { | |
if (visitor.visit(this, scope)) { | |
if (enclosingInstance != null) enclosingInstance.traverse(visitor, scope); | |
type.traverse(visitor, scope); | |
if (arguments != null) { | |
int argumentsLength = arguments.length; | |
for (int i = 0; i < argumentsLength; i++) | |
arguments[i].traverse(visitor, scope); | |
} | |
if (anonymousType != null) anonymousType.traverse(visitor, scope); | |
} | |
visitor.endVisit(this, scope); | |
} | |
} |