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