--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package net.sourceforge.phpdt.internal.compiler.ast;
+
+import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
+import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
+import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
+import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
+import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
+
+/**
+ * Variation on allocation, where can be specified an enclosing instance and an anonymous type
+ */
+public class QualifiedAllocationExpression extends AllocationExpression {
+
+ //qualification may be on both side
+ public Expression enclosingInstance;
+ public AnonymousLocalTypeDeclaration anonymousType;
+ public ReferenceBinding superTypeBinding;
+
+ public QualifiedAllocationExpression() {
+ }
+
+ public QualifiedAllocationExpression(AnonymousLocalTypeDeclaration anonymousType) {
+ this.anonymousType = anonymousType;
+ }
+
+ public FlowInfo analyseCode(
+ BlockScope currentScope,
+ FlowContext flowContext,
+ FlowInfo flowInfo) {
+
+ // analyse the enclosing instance
+ if (enclosingInstance != null) {
+ flowInfo = enclosingInstance.analyseCode(currentScope, flowContext, flowInfo);
+ }
+
+ // check captured variables are initialized in current context (26134)
+ checkCapturedLocalInitializationIfNecessary(
+ this.superTypeBinding == null ? this.binding.declaringClass : this.superTypeBinding,
+ currentScope,
+ flowInfo);
+
+ // process arguments
+ if (arguments != null) {
+ for (int i = 0, count = arguments.length; i < count; i++) {
+ flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo);
+ }
+ }
+
+ // analyse the anonymous nested type
+ if (anonymousType != null) {
+ flowInfo = anonymousType.analyseCode(currentScope, flowContext, flowInfo);
+ }
+
+ // record some dependency information for exception types
+ ReferenceBinding[] thrownExceptions;
+ if (((thrownExceptions = binding.thrownExceptions).length) != 0) {
+ // check exception handling
+ flowContext.checkExceptionHandlers(
+ thrownExceptions,
+ this,
+ flowInfo,
+ currentScope);
+ }
+ 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;
+// codeStream.new_(allocatedType);
+// if (valueRequired) {
+// codeStream.dup();
+// }
+// // better highlight for allocation: display the type individually
+// codeStream.recordPositionsFrom(pc, type.sourceStart);
+//
+// // handling innerclass instance allocation - enclosing instance arguments
+// if (allocatedType.isNestedType()) {
+// codeStream.generateSyntheticEnclosingInstanceValues(
+// 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);
+// }
+// }
+// // handling innerclass instance allocation - outer local arguments
+// if (allocatedType.isNestedType()) {
+// codeStream.generateSyntheticOuterArgumentValues(
+// currentScope,
+// allocatedType,
+// this);
+// }
+//
+// // invoke constructor
+// if (syntheticAccessor == null) {
+// codeStream.invokespecial(binding);
+// } else {
+// // synthetic accessor got some extra arguments appended to its signature, which need values
+// for (int i = 0,
+// max = syntheticAccessor.parameters.length - binding.parameters.length;
+// i < max;
+// i++) {
+// codeStream.aconst_null();
+// }
+// codeStream.invokespecial(syntheticAccessor);
+// }
+// codeStream.recordPositionsFrom(pc, this.sourceStart);
+//
+// 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 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);
+ } else {
+ // locally propagate, since we already now the desired shape for sure
+ currentScope.propagateInnerEmulation(allocatedType, enclosingInstance != null);
+ }
+ }
+ }
+
+ public TypeBinding resolveType(BlockScope scope) {
+
+ // added for code assist...cannot occur with 'normal' code
+ if (anonymousType == null && enclosingInstance == null) {
+ return super.resolveType(scope);
+ }
+
+ // 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 enclosingInstanceType = null;
+ TypeBinding receiverType = null;
+ boolean hasError = false;
+ if (anonymousType == null) { //----------------no anonymous class------------------------
+ if ((enclosingInstanceType = enclosingInstance.resolveType(scope)) == null){
+ hasError = true;
+ } else if (enclosingInstanceType.isBaseType() || enclosingInstanceType.isArrayType()) {
+ scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(
+ enclosingInstanceType,
+ enclosingInstance);
+ hasError = true;
+ } else if ((this.resolvedType = receiverType = ((SingleTypeReference) type).resolveTypeEnclosing(
+ scope,
+ (ReferenceBinding) enclosingInstanceType)) == null) {
+ hasError = true;
+ }
+ // will check for null after args are resolved
+ 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){
+ hasError = true;
+ }
+ }
+ // limit of fault-tolerance
+ if (hasError) return receiverType;
+
+ if (!receiverType.canBeInstantiated()) {
+ scope.problemReporter().cannotInstantiate(type, receiverType);
+ return receiverType;
+ }
+ if ((this.binding = scope.getConstructor((ReferenceBinding) receiverType, argumentTypes, this))
+ .isValidBinding()) {
+ if (isMethodUseDeprecated(binding, scope))
+ scope.problemReporter().deprecatedMethod(this.binding, this);
+
+ if (arguments != null)
+ for (int i = 0; i < arguments.length; i++)
+ arguments[i].implicitWidening(this.binding.parameters[i], argumentTypes[i]);
+ } else {
+ if (this.binding.declaringClass == null)
+ this.binding.declaringClass = (ReferenceBinding) receiverType;
+ scope.problemReporter().invalidConstructor(this, this.binding);
+ return receiverType;
+ }
+
+ // The enclosing instance must be compatible with the innermost enclosing type
+ ReferenceBinding expectedType = this.binding.declaringClass.enclosingType();
+ if (enclosingInstanceType.isCompatibleWith(expectedType))
+ return receiverType;
+ scope.problemReporter().typeMismatchErrorActualTypeExpectedType(
+ this.enclosingInstance,
+ enclosingInstanceType,
+ expectedType);
+ return receiverType;
+ }
+
+ //--------------there is an anonymous type declaration-----------------
+ if (this.enclosingInstance != null) {
+ if ((enclosingInstanceType = this.enclosingInstance.resolveType(scope)) == null) {
+ hasError = true;
+ } else if (enclosingInstanceType.isBaseType() || enclosingInstanceType.isArrayType()) {
+ scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(
+ enclosingInstanceType,
+ this.enclosingInstance);
+ hasError = true;
+ } else {
+ receiverType = ((SingleTypeReference) type).resolveTypeEnclosing(
+ scope,
+ (ReferenceBinding) enclosingInstanceType);
+ }
+ } else {
+ receiverType = type.resolveType(scope);
+ }
+ if (receiverType == null) {
+ hasError = true;
+ } else if (((ReferenceBinding) receiverType).isFinal()) {
+ scope.problemReporter().anonymousClassCannotExtendFinalClass(type, receiverType);
+ hasError = true;
+ }
+ 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) {
+ hasError = true;
+ }
+ }
+ // limit of fault-tolerance
+ if (hasError) {
+ return receiverType;
+ }
+
+ // an anonymous class inherits from java.lang.Object when declared "after" an interface
+ this.superTypeBinding =
+ receiverType.isInterface() ? scope.getJavaLangObject() : (ReferenceBinding) receiverType;
+ MethodBinding inheritedBinding =
+ scope.getConstructor(this.superTypeBinding, argumentTypes, this);
+ if (!inheritedBinding.isValidBinding()) {
+ if (inheritedBinding.declaringClass == null)
+ inheritedBinding.declaringClass = this.superTypeBinding;
+ scope.problemReporter().invalidConstructor(this, inheritedBinding);
+ return null;
+ }
+ if (enclosingInstance != null) {
+ if (!enclosingInstanceType.isCompatibleWith(inheritedBinding.declaringClass.enclosingType())) {
+ scope.problemReporter().typeMismatchErrorActualTypeExpectedType(
+ enclosingInstance,
+ enclosingInstanceType,
+ 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) receiverType);
+ anonymousType.resolve(scope);
+ binding = anonymousType.createsInternalConstructorWithBinding(inheritedBinding);
+ return anonymousType.binding; // 1.2 change
+ }
+
+ public String toStringExpression() {
+ return this.toStringExpression(0);
+ }
+
+ public String toStringExpression(int tab) {
+
+ String s = ""; //$NON-NLS-1$
+ if (enclosingInstance != null)
+ s += enclosingInstance.toString() + "."; //$NON-NLS-1$
+ s += super.toStringExpression();
+ if (anonymousType != null) {
+ s += anonymousType.toString(tab);
+ } //allows to restart just after the } one line under ....
+ return s;
+ }
+
+ public void traverse(ASTVisitor 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);
+ }
+}