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