Applying pteague's patch (re #685)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / QualifiedAllocationExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
16 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
17 import net.sourceforge.phpdt.internal.compiler.lookup.LocalTypeBinding;
18 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
19 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
20 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
21
22 /**
23  * Variation on allocation, where can be specified an enclosing instance and an
24  * anonymous type
25  */
26 public class QualifiedAllocationExpression extends AllocationExpression {
27
28         // qualification may be on both side
29         public Expression enclosingInstance;
30
31         public AnonymousLocalTypeDeclaration anonymousType;
32
33         public ReferenceBinding superTypeBinding;
34
35         public QualifiedAllocationExpression() {
36         }
37
38         public QualifiedAllocationExpression(
39                         AnonymousLocalTypeDeclaration anonymousType) {
40                 this.anonymousType = anonymousType;
41         }
42
43         public FlowInfo analyseCode(BlockScope currentScope,
44                         FlowContext flowContext, FlowInfo flowInfo) {
45
46                 // analyse the enclosing instance
47                 if (enclosingInstance != null) {
48                         flowInfo = enclosingInstance.analyseCode(currentScope, flowContext,
49                                         flowInfo);
50                 }
51
52                 // check captured variables are initialized in current context (26134)
53                 checkCapturedLocalInitializationIfNecessary(
54                                 this.superTypeBinding == null ? this.binding.declaringClass
55                                                 : this.superTypeBinding, currentScope, 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,
61                                                 flowInfo);
62                         }
63                 }
64
65                 // analyse the anonymous nested type
66                 if (anonymousType != null) {
67                         flowInfo = anonymousType.analyseCode(currentScope, flowContext,
68                                         flowInfo);
69                 }
70
71                 // record some dependency information for exception types
72                 ReferenceBinding[] thrownExceptions;
73                 if (((thrownExceptions = binding.thrownExceptions).length) != 0) {
74                         // check exception handling
75                         flowContext.checkExceptionHandlers(thrownExceptions, this,
76                                         flowInfo, currentScope);
77                 }
78                 manageEnclosingInstanceAccessIfNecessary(currentScope);
79                 manageSyntheticAccessIfNecessary(currentScope);
80                 return flowInfo;
81         }
82
83         public Expression enclosingInstance() {
84
85                 return enclosingInstance;
86         }
87
88         // public void generateCode(
89         // BlockScope currentScope,
90         // CodeStream codeStream,
91         // boolean valueRequired) {
92         //
93         // int pc = codeStream.position;
94         // ReferenceBinding allocatedType = binding.declaringClass;
95         // codeStream.new_(allocatedType);
96         // if (valueRequired) {
97         // codeStream.dup();
98         // }
99         // // better highlight for allocation: display the type individually
100         // codeStream.recordPositionsFrom(pc, type.sourceStart);
101         //
102         // // handling innerclass instance allocation - enclosing instance arguments
103         // if (allocatedType.isNestedType()) {
104         // codeStream.generateSyntheticEnclosingInstanceValues(
105         // currentScope,
106         // allocatedType,
107         // enclosingInstance(),
108         // this);
109         // }
110         // // generate the arguments for constructor
111         // if (arguments != null) {
112         // for (int i = 0, count = arguments.length; i < count; i++) {
113         // arguments[i].generateCode(currentScope, codeStream, true);
114         // }
115         // }
116         // // handling innerclass instance allocation - outer local arguments
117         // if (allocatedType.isNestedType()) {
118         // codeStream.generateSyntheticOuterArgumentValues(
119         // currentScope,
120         // allocatedType,
121         // this);
122         // }
123         //              
124         // // invoke constructor
125         // if (syntheticAccessor == null) {
126         // codeStream.invokespecial(binding);
127         // } else {
128         // // synthetic accessor got some extra arguments appended to its signature,
129         // which need values
130         // for (int i = 0,
131         // max = syntheticAccessor.parameters.length - binding.parameters.length;
132         // i < max;
133         // i++) {
134         // codeStream.aconst_null();
135         // }
136         // codeStream.invokespecial(syntheticAccessor);
137         // }
138         // codeStream.recordPositionsFrom(pc, this.sourceStart);
139         //
140         // if (anonymousType != null) {
141         // anonymousType.generateCode(currentScope, codeStream);
142         // }
143         // }
144
145         public boolean isSuperAccess() {
146
147                 // necessary to lookup super constructor of anonymous type
148                 return anonymousType != null;
149         }
150
151         /*
152          * Inner emulation consists in either recording a dependency link only, or
153          * performing one level of propagation.
154          * 
155          * Dependency mechanism is used whenever dealing with source target types,
156          * since by the time we reach them, we might not yet know their exact need.
157          */
158         public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope) {
159
160                 ReferenceBinding allocatedType;
161
162                 // perform some emulation work in case there is some and we are inside a
163                 // local type only
164                 if ((allocatedType = binding.declaringClass).isNestedType()
165                                 && currentScope.enclosingSourceType().isLocalType()) {
166
167                         if (allocatedType.isLocalType()) {
168                                 ((LocalTypeBinding) allocatedType).addInnerEmulationDependent(
169                                                 currentScope, enclosingInstance != null);
170                         } else {
171                                 // locally propagate, since we already now the desired shape for
172                                 // sure
173                                 currentScope.propagateInnerEmulation(allocatedType,
174                                                 enclosingInstance != null);
175                         }
176                 }
177         }
178
179         public TypeBinding resolveType(BlockScope scope) {
180
181                 // added for code assist...cannot occur with 'normal' code
182                 if (anonymousType == null && enclosingInstance == null) {
183                         return super.resolveType(scope);
184                 }
185
186                 // Propagate the type checking to the arguments, and checks if the
187                 // constructor is defined.
188                 // ClassInstanceCreationExpression ::= Primary '.' 'new' SimpleName '('
189                 // ArgumentListopt ')' ClassBodyopt
190                 // ClassInstanceCreationExpression ::= Name '.' 'new' SimpleName '('
191                 // ArgumentListopt ')' ClassBodyopt
192                 // ==> by construction, when there is an enclosing instance the typename
193                 // may NOT be qualified
194                 // ==> therefore by construction the type is always a
195                 // SingleTypeReferenceType instead of being either
196                 // sometime a SingleTypeReference and sometime a QualifedTypeReference
197
198                 constant = NotAConstant;
199                 TypeBinding enclosingInstanceType = null;
200                 TypeBinding receiverType = null;
201                 boolean hasError = false;
202                 if (anonymousType == null) { // ----------------no anonymous
203                                                                                 // class------------------------
204                         if ((enclosingInstanceType = enclosingInstance.resolveType(scope)) == null) {
205                                 hasError = true;
206                         } else if (enclosingInstanceType.isBaseType()
207                                         || enclosingInstanceType.isArrayType()) {
208                                 scope.problemReporter()
209                                                 .illegalPrimitiveOrArrayTypeForEnclosingInstance(
210                                                                 enclosingInstanceType, enclosingInstance);
211                                 hasError = true;
212                         } else if ((this.resolvedType = receiverType = ((SingleTypeReference) type)
213                                         .resolveTypeEnclosing(scope,
214                                                         (ReferenceBinding) enclosingInstanceType)) == null) {
215                                 hasError = true;
216                         }
217                         // will check for null after args are resolved
218                         TypeBinding[] argumentTypes = NoParameters;
219                         if (arguments != null) {
220                                 int length = arguments.length;
221                                 argumentTypes = new TypeBinding[length];
222                                 for (int i = 0; i < length; i++)
223                                         if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) {
224                                                 hasError = true;
225                                         }
226                         }
227                         // limit of fault-tolerance
228                         if (hasError)
229                                 return receiverType;
230
231                         if (!receiverType.canBeInstantiated()) {
232                                 scope.problemReporter().cannotInstantiate(type, receiverType);
233                                 return receiverType;
234                         }
235                         if ((this.binding = scope.getConstructor(
236                                         (ReferenceBinding) receiverType, argumentTypes, this))
237                                         .isValidBinding()) {
238                                 if (isMethodUseDeprecated(binding, scope))
239                                         scope.problemReporter()
240                                                         .deprecatedMethod(this.binding, this);
241
242                                 if (arguments != null)
243                                         for (int i = 0; i < arguments.length; i++)
244                                                 arguments[i].implicitWidening(
245                                                                 this.binding.parameters[i], argumentTypes[i]);
246                         } else {
247                                 if (this.binding.declaringClass == null)
248                                         this.binding.declaringClass = (ReferenceBinding) receiverType;
249                                 scope.problemReporter().invalidConstructor(this, this.binding);
250                                 return receiverType;
251                         }
252
253                         // The enclosing instance must be compatible with the innermost
254                         // enclosing type
255                         ReferenceBinding expectedType = this.binding.declaringClass
256                                         .enclosingType();
257                         if (enclosingInstanceType.isCompatibleWith(expectedType))
258                                 return receiverType;
259                         scope.problemReporter()
260                                         .typeMismatchErrorActualTypeExpectedType(
261                                                         this.enclosingInstance, enclosingInstanceType,
262                                                         expectedType);
263                         return receiverType;
264                 }
265
266                 // --------------there is an anonymous type declaration-----------------
267                 if (this.enclosingInstance != null) {
268                         if ((enclosingInstanceType = this.enclosingInstance
269                                         .resolveType(scope)) == null) {
270                                 hasError = true;
271                         } else if (enclosingInstanceType.isBaseType()
272                                         || enclosingInstanceType.isArrayType()) {
273                                 scope.problemReporter()
274                                                 .illegalPrimitiveOrArrayTypeForEnclosingInstance(
275                                                                 enclosingInstanceType, this.enclosingInstance);
276                                 hasError = true;
277                         } else {
278                                 receiverType = ((SingleTypeReference) type)
279                                                 .resolveTypeEnclosing(scope,
280                                                                 (ReferenceBinding) enclosingInstanceType);
281                         }
282                 } else {
283                         receiverType = type.resolveType(scope);
284                 }
285                 if (receiverType == null) {
286                         hasError = true;
287                 } else if (((ReferenceBinding) receiverType).isFinal()) {
288                         scope.problemReporter().anonymousClassCannotExtendFinalClass(type,
289                                         receiverType);
290                         hasError = true;
291                 }
292                 TypeBinding[] argumentTypes = NoParameters;
293                 if (arguments != null) {
294                         int length = arguments.length;
295                         argumentTypes = new TypeBinding[length];
296                         for (int i = 0; i < length; i++)
297                                 if ((argumentTypes[i] = arguments[i].resolveType(scope)) == null) {
298                                         hasError = true;
299                                 }
300                 }
301                 // limit of fault-tolerance
302                 if (hasError) {
303                         return receiverType;
304                 }
305
306                 // an anonymous class inherits from java.lang.Object when declared
307                 // "after" an interface
308                 this.superTypeBinding = receiverType.isInterface() ? scope
309                                 .getJavaLangObject() : (ReferenceBinding) receiverType;
310                 MethodBinding inheritedBinding = scope.getConstructor(
311                                 this.superTypeBinding, argumentTypes, this);
312                 if (!inheritedBinding.isValidBinding()) {
313                         if (inheritedBinding.declaringClass == null)
314                                 inheritedBinding.declaringClass = this.superTypeBinding;
315                         scope.problemReporter().invalidConstructor(this, inheritedBinding);
316                         return null;
317                 }
318                 if (enclosingInstance != null) {
319                         if (!enclosingInstanceType
320                                         .isCompatibleWith(inheritedBinding.declaringClass
321                                                         .enclosingType())) {
322                                 scope
323                                                 .problemReporter()
324                                                 .typeMismatchErrorActualTypeExpectedType(
325                                                                 enclosingInstance, enclosingInstanceType,
326                                                                 inheritedBinding.declaringClass.enclosingType());
327                                 return null;
328                         }
329                 }
330
331                 // this promotion has to be done somewhere: here or inside the
332                 // constructor of the
333                 // anonymous class. We do it here while the constructor of the inner is
334                 // then easier.
335                 if (arguments != null)
336                         for (int i = 0; i < arguments.length; i++)
337                                 arguments[i].implicitWidening(inheritedBinding.parameters[i],
338                                                 argumentTypes[i]);
339
340                 // Update the anonymous inner class : superclass, interface
341                 scope.addAnonymousType(anonymousType, (ReferenceBinding) receiverType);
342                 anonymousType.resolve(scope);
343                 binding = anonymousType
344                                 .createsInternalConstructorWithBinding(inheritedBinding);
345                 return anonymousType.binding; // 1.2 change
346         }
347
348         public String toStringExpression() {
349                 return this.toStringExpression(0);
350         }
351
352         public String toStringExpression(int tab) {
353
354                 String s = ""; //$NON-NLS-1$
355                 if (enclosingInstance != null)
356                         s += enclosingInstance.toString() + "."; //$NON-NLS-1$
357                 s += super.toStringExpression();
358                 if (anonymousType != null) {
359                         s += anonymousType.toString(tab);
360                 } // allows to restart just after the } one line under ....
361                 return s;
362         }
363
364         public void traverse(ASTVisitor visitor, BlockScope scope) {
365
366                 if (visitor.visit(this, scope)) {
367                         if (enclosingInstance != null)
368                                 enclosingInstance.traverse(visitor, scope);
369                         type.traverse(visitor, scope);
370                         if (arguments != null) {
371                                 int argumentsLength = arguments.length;
372                                 for (int i = 0; i < argumentsLength; i++)
373                                         arguments[i].traverse(visitor, scope);
374                         }
375                         if (anonymousType != null)
376                                 anonymousType.traverse(visitor, scope);
377                 }
378                 visitor.endVisit(this, scope);
379         }
380 }