289eca26a8df0ec477d638b6c3980f8317cbd4cb
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / QualifiedAllocationExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.codegen.*;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.lookup.*;
17
18 /**
19  * Variation on allocation, where can optionally be specified any of:
20  * - leading enclosing instance
21  * - trailing anonymous type
22  * - generic type arguments for generic constructor invocation
23  */
24 public class QualifiedAllocationExpression extends AllocationExpression {
25         
26         //qualification may be on both side
27         public Expression enclosingInstance;
28         public TypeDeclaration anonymousType;
29         public ReferenceBinding superTypeBinding;
30         
31         public QualifiedAllocationExpression() {
32                 // for subtypes
33         }
34
35         public QualifiedAllocationExpression(TypeDeclaration anonymousType) {
36                 this.anonymousType = anonymousType;
37                 anonymousType.allocation = this;
38         }
39
40         public FlowInfo analyseCode(
41                 BlockScope currentScope,
42                 FlowContext flowContext,
43                 FlowInfo flowInfo) {
44
45                 // analyse the enclosing instance
46                 if (enclosingInstance != null) {
47                         flowInfo = enclosingInstance.analyseCode(currentScope, flowContext, flowInfo);
48                 }
49                 
50                 // check captured variables are initialized in current context (26134)
51                 ReferenceBinding allocatedType = this.superTypeBinding == null ? this.binding.declaringClass : this.superTypeBinding;
52                 checkCapturedLocalInitializationIfNecessary(
53                         (ReferenceBinding) allocatedType.erasure(),
54                         currentScope, 
55                         flowInfo);
56                 
57                 // process arguments
58                 if (arguments != null) {
59                         for (int i = 0, count = arguments.length; i < count; i++) {
60                                 flowInfo = arguments[i].analyseCode(currentScope, flowContext, flowInfo);
61                         }
62                 }
63
64                 // analyse the anonymous nested type
65                 if (anonymousType != null) {
66                         flowInfo = anonymousType.analyseCode(currentScope, flowContext, flowInfo);
67                 }
68
69                 // record some dependency information for exception types
70                 ReferenceBinding[] thrownExceptions;
71                 if (((thrownExceptions = binding.thrownExceptions).length) != 0) {
72                         // check exception handling
73                         flowContext.checkExceptionHandlers(
74                                 thrownExceptions,
75                                 this,
76                                 flowInfo,
77                                 currentScope);
78                 }
79                 manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
80                 manageSyntheticAccessIfNecessary(currentScope, flowInfo);
81                 return flowInfo;
82         }
83
84         public Expression enclosingInstance() {
85
86                 return enclosingInstance;
87         }
88
89         public void generateCode(
90                 BlockScope currentScope,
91                 CodeStream codeStream,
92                 boolean valueRequired) {
93
94                 int pc = codeStream.position;
95                 ReferenceBinding allocatedType = this.codegenBinding.declaringClass;
96                 codeStream.new_(allocatedType);
97                 if (valueRequired) {
98                         codeStream.dup();
99                 }
100                 // better highlight for allocation: display the type individually
101                 if (this.type != null) { // null for enum constant body
102                         codeStream.recordPositionsFrom(pc, this.type.sourceStart);
103                 } else {
104                         // push enum constant name and ordinal
105                         codeStream.ldc(String.valueOf(enumConstant.name));
106                         codeStream.generateInlinedValue(enumConstant.binding.id);
107                 }
108                 // handling innerclass instance allocation - enclosing instance arguments
109                 if (allocatedType.isNestedType()) {
110                         codeStream.generateSyntheticEnclosingInstanceValues(
111                                 currentScope,
112                                 allocatedType,
113                                 enclosingInstance(),
114                                 this);
115                 }
116                 // generate the arguments for constructor
117                 generateArguments(binding, arguments, currentScope, codeStream);
118                 // handling innerclass instance allocation - outer local arguments
119                 if (allocatedType.isNestedType()) {
120                         codeStream.generateSyntheticOuterArgumentValues(
121                                 currentScope,
122                                 allocatedType,
123                                 this);
124                 }
125                 
126                 // invoke constructor
127                 if (syntheticAccessor == null) {
128                         codeStream.invokespecial(this.codegenBinding);
129                 } else {
130                         // synthetic accessor got some extra arguments appended to its signature, which need values
131                         for (int i = 0,
132                                 max = syntheticAccessor.parameters.length - this.codegenBinding.parameters.length;
133                                 i < max;
134                                 i++) {
135                                 codeStream.aconst_null();
136                         }
137                         codeStream.invokespecial(syntheticAccessor);
138                 }
139                 codeStream.generateImplicitConversion(this.implicitConversion);
140                 codeStream.recordPositionsFrom(pc, this.sourceStart);
141
142                 if (anonymousType != null) {
143                         anonymousType.generateCode(currentScope, codeStream);
144                 }
145         }
146         
147         public boolean isSuperAccess() {
148
149                 // necessary to lookup super constructor of anonymous type
150                 return anonymousType != null;
151         }
152         
153         /* Inner emulation consists in either recording a dependency 
154          * link only, or performing one level of propagation.
155          *
156          * Dependency mechanism is used whenever dealing with source target
157          * types, since by the time we reach them, we might not yet know their
158          * exact need.
159          */
160         public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
161
162                 if (!flowInfo.isReachable()) return;
163                 ReferenceBinding allocatedTypeErasure = (ReferenceBinding) binding.declaringClass.erasure();
164
165                 // perform some emulation work in case there is some and we are inside a local type only
166                 if (allocatedTypeErasure.isNestedType()
167                         && currentScope.enclosingSourceType().isLocalType()) {
168
169                         if (allocatedTypeErasure.isLocalType()) {
170                                 ((LocalTypeBinding) allocatedTypeErasure).addInnerEmulationDependent(currentScope, enclosingInstance != null);
171                         } else {
172                                 // locally propagate, since we already now the desired shape for sure
173                                 currentScope.propagateInnerEmulation(allocatedTypeErasure, enclosingInstance != null);
174                         }
175                 }
176         }
177
178         public StringBuffer printExpression(int indent, StringBuffer output) {
179
180                 if (enclosingInstance != null)
181                         enclosingInstance.printExpression(0, output).append('.'); 
182                 super.printExpression(0, output);
183                 if (anonymousType != null) {
184                         anonymousType.print(indent, output);
185                 }
186                 return output;
187         }
188         
189         public TypeBinding resolveType(BlockScope scope) {
190
191                 // added for code assist...cannot occur with 'normal' code
192                 if (this.anonymousType == null && this.enclosingInstance == null) {
193                         return super.resolveType(scope);
194                 }
195
196                 // Propagate the type checking to the arguments, and checks if the constructor is defined.
197                 // ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
198                 // ClassInstanceCreationExpression ::= Name '.' 'new' SimpleName '(' ArgumentListopt ')' ClassBodyopt
199                 
200                 constant = NotAConstant;
201                 TypeBinding enclosingInstanceType = null;
202                 TypeBinding receiverType = null;
203                 boolean hasError = false;
204                 boolean enclosingInstanceContainsCast = false;
205                 boolean argsContainCast = false;
206                 
207                 if (enclosingInstance != null) {
208                         if (enclosingInstance instanceof CastExpression) {
209                                 enclosingInstance.bits |= IgnoreNeedForCastCheckMASK; // will check later on
210                                 enclosingInstanceContainsCast = true;
211                         }
212                         if ((enclosingInstanceType = enclosingInstance.resolveType(scope)) == null){
213                                 hasError = true;
214                         } else if (enclosingInstanceType.isBaseType() || enclosingInstanceType.isArrayType()) {
215                                 scope.problemReporter().illegalPrimitiveOrArrayTypeForEnclosingInstance(
216                                         enclosingInstanceType,
217                                         enclosingInstance);
218                                 hasError = true;
219                         } else if (type instanceof QualifiedTypeReference) {
220                                 scope.problemReporter().illegalUsageOfQualifiedTypeReference((QualifiedTypeReference)type);
221                                 hasError = true;
222                         } else {
223                                 receiverType = ((SingleTypeReference) type).resolveTypeEnclosing(scope, (ReferenceBinding) enclosingInstanceType);
224                                 if (receiverType != null && enclosingInstanceContainsCast) {
225                                         CastExpression.checkNeedForEnclosingInstanceCast(scope, enclosingInstance, enclosingInstanceType, receiverType);
226                                 }
227                         }
228                 } else {
229                         if (this.type == null) {
230                                 // initialization of an enum constant
231                                 receiverType = scope.enclosingSourceType();
232                         } else {
233                                 receiverType = this.type.resolveType(scope, true /* check bounds*/);
234                         }                       
235                 }
236                 if (receiverType == null) {
237                         hasError = true;
238                 } else if (((ReferenceBinding) receiverType).isFinal() && this.anonymousType != null) {
239                         if (!receiverType.isEnum()) {
240                                 scope.problemReporter().anonymousClassCannotExtendFinalClass(type, receiverType);
241                         }
242                         hasError = true;
243                 }
244                 // resolve type arguments (for generic constructor call)
245                 if (this.typeArguments != null) {
246                         int length = this.typeArguments.length;
247                         this.genericTypeArguments = new TypeBinding[length];
248                         for (int i = 0; i < length; i++) {
249                                 TypeBinding argType = this.typeArguments[i].resolveType(scope, true /* check bounds*/);
250                                 if (argType == null) return null; // error already reported
251                                 this.genericTypeArguments[i] = argType;
252                         }
253                 }
254                 
255                 // will check for null after args are resolved
256                 TypeBinding[] argumentTypes = NoParameters;
257                 if (arguments != null) {
258                         int length = arguments.length;
259                         argumentTypes = new TypeBinding[length];
260                         for (int i = 0; i < length; i++) {
261                                 Expression argument = this.arguments[i];
262                                 if (argument instanceof CastExpression) {
263                                         argument.bits |= IgnoreNeedForCastCheckMASK; // will check later on
264                                         argsContainCast = true;
265                                 }
266                                 if ((argumentTypes[i] = argument.resolveType(scope)) == null){
267                                         hasError = true;
268                                 }
269                         }
270                 }
271                 // limit of fault-tolerance
272                 if (hasError) return this.resolvedType = receiverType;
273                 if (this.anonymousType == null) {
274                         // qualified allocation with no anonymous type
275                         ReferenceBinding allocationType = (ReferenceBinding) receiverType;
276                         if (!receiverType.canBeInstantiated()) {
277                                 scope.problemReporter().cannotInstantiate(type, receiverType);
278                                 return this.resolvedType = receiverType;
279                         }
280                         if ((this.binding = scope.getConstructor(allocationType, argumentTypes, this)).isValidBinding()) {
281                                 if (isMethodUseDeprecated(binding, scope)) {
282                                         scope.problemReporter().deprecatedMethod(this.binding, this);
283                                 }
284                                 if (this.arguments != null)
285                                         checkInvocationArguments(scope, null, allocationType, binding, this.arguments, argumentTypes, argsContainCast, this);
286                         } else {
287                                 if (this.binding.declaringClass == null) {
288                                         this.binding.declaringClass = allocationType;
289                                 }
290                                 scope.problemReporter().invalidConstructor(this, this.binding);
291                                 return this.resolvedType = receiverType;
292                         }
293
294                         // The enclosing instance must be compatible with the innermost enclosing type
295                         ReferenceBinding expectedType = this.binding.declaringClass.enclosingType();
296                         if (expectedType != enclosingInstanceType) // must call before computeConversion() and typeMismatchError()
297                                 scope.compilationUnitScope().recordTypeConversion(expectedType, enclosingInstanceType);
298                         if (enclosingInstanceType.isCompatibleWith(expectedType) || scope.isBoxingCompatibleWith(enclosingInstanceType, expectedType)) {
299                                 enclosingInstance.computeConversion(scope, expectedType, enclosingInstanceType);
300                                 return receiverType;
301                         }
302                         scope.problemReporter().typeMismatchError(enclosingInstanceType, expectedType, this.enclosingInstance);
303                         return this.resolvedType = receiverType;
304                 }
305
306                 if (receiverType.isTypeVariable()) {
307                         receiverType = new ProblemReferenceBinding(receiverType.sourceName(), (ReferenceBinding)receiverType, ProblemReasons.IllegalSuperTypeVariable);
308                         scope.problemReporter().invalidType(this, receiverType);
309                         return null;
310                 }
311                 // anonymous type scenario
312                 // an anonymous class inherits from java.lang.Object when declared "after" an interface
313                 this.superTypeBinding = receiverType.isInterface() ? scope.getJavaLangObject() : (ReferenceBinding) receiverType;
314                 // insert anonymous type in scope
315                 scope.addAnonymousType(this.anonymousType, (ReferenceBinding) receiverType);
316                 this.anonymousType.resolve(scope);              
317                 
318                 if ((receiverType.tagBits & TagBits.HasDirectWildcard) != 0) {
319                         scope.problemReporter().superTypeCannotUseWildcard(anonymousType.binding, this.type, receiverType);
320                 }               
321                 // find anonymous super constructor
322                 MethodBinding inheritedBinding = scope.getConstructor(this.superTypeBinding, argumentTypes, this);
323                 if (!inheritedBinding.isValidBinding()) {
324                         if (inheritedBinding.declaringClass == null) {
325                                 inheritedBinding.declaringClass = this.superTypeBinding;
326                         }
327                         scope.problemReporter().invalidConstructor(this, inheritedBinding);
328                         return this.resolvedType = anonymousType.binding;
329                 }
330                 if (enclosingInstance != null) {
331                         ReferenceBinding targetEnclosing = inheritedBinding.declaringClass.enclosingType();
332                         if (targetEnclosing == null) {
333                                 scope.problemReporter().unnecessaryEnclosingInstanceSpecification(enclosingInstance, (ReferenceBinding)receiverType);
334                                 return this.resolvedType = anonymousType.binding;
335                         } else if (!enclosingInstanceType.isCompatibleWith(targetEnclosing) && !scope.isBoxingCompatibleWith(enclosingInstanceType, targetEnclosing)) {
336                                 scope.problemReporter().typeMismatchError(enclosingInstanceType, targetEnclosing, enclosingInstance);
337                                 return this.resolvedType = anonymousType.binding;
338                         }
339                         enclosingInstance.computeConversion(scope, targetEnclosing, enclosingInstanceType);
340                 }
341                 if (this.arguments != null)
342                         checkInvocationArguments(scope, null, this.superTypeBinding, inheritedBinding, this.arguments, argumentTypes, argsContainCast, this);
343
344                 // Update the anonymous inner class : superclass, interface  
345                 binding = anonymousType.createDefaultConstructorWithBinding(inheritedBinding);
346                 return this.resolvedType = anonymousType.binding; // 1.2 change
347         }
348         
349         public void traverse(ASTVisitor visitor, BlockScope scope) {
350
351                 if (visitor.visit(this, scope)) {
352                         if (enclosingInstance != null)
353                                 enclosingInstance.traverse(visitor, scope);
354                         if (this.typeArguments != null) {
355                                 for (int i = 0, typeArgumentsLength = this.typeArguments.length; i < typeArgumentsLength; i++) {
356                                         this.typeArguments[i].traverse(visitor, scope);
357                                 }                                       
358                         }
359                         if (this.type != null) // case of enum constant
360                                 this.type.traverse(visitor, scope);
361                         if (arguments != null) {
362                                 int argumentsLength = arguments.length;
363                                 for (int i = 0; i < argumentsLength; i++)
364                                         arguments[i].traverse(visitor, scope);
365                         }
366                         if (anonymousType != null)
367                                 anonymousType.traverse(visitor, scope);
368                 }
369                 visitor.endVisit(this, scope);
370         }
371 }