]> source.dussan.org Git - aspectj.git/blob
fc0d421546df1ed8b0e667d815943097716440b0
[aspectj.git] /
1 /* *******************************************************************
2  * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
3  * All rights reserved. 
4  * This program and the accompanying materials are made available 
5  * under the terms of the Eclipse Public License v1.0 
6  * which accompanies this distribution and is available at 
7  * http://www.eclipse.org/legal/epl-v10.html 
8  *  
9  * Contributors: 
10  *     PARC     initial implementation 
11  * ******************************************************************/
12
13 package org.aspectj.ajdt.internal.compiler.ast;
14
15 import java.lang.reflect.Modifier;
16
17 import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
18 import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger;
19 import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter;
20 import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile;
21 import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
22 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
23 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
24 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
25 import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
26 import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
27 import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream;
28 import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.Opcodes;
29 import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowContext;
30 import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowInfo;
31 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
32 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
33 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
34 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
35 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
36 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
37 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits;
38 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
39 import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
40 import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
41 import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
42 import org.aspectj.weaver.AjAttribute;
43 import org.aspectj.weaver.AjcMemberMaker;
44 import org.aspectj.weaver.Constants;
45 import org.aspectj.weaver.NameMangler;
46 import org.aspectj.weaver.NewMethodTypeMunger;
47 import org.aspectj.weaver.ResolvedMember;
48 import org.aspectj.weaver.ResolvedMemberImpl;
49 import org.aspectj.weaver.ResolvedType;
50 import org.aspectj.weaver.Shadow;
51 import org.aspectj.weaver.UnresolvedType;
52
53 /**
54  * An inter-type method declaration.
55  * 
56  * @author Jim Hugunin
57  */
58 public class InterTypeMethodDeclaration extends InterTypeDeclaration {
59         public InterTypeMethodDeclaration(CompilationResult result, TypeReference onType) {
60                 super(result, onType); 
61         }
62
63         @Override
64         public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
65                 if (ignoreFurtherInvestigation)
66                         return;
67                 if (!Modifier.isAbstract(declaredModifiers)) {
68                         parser.parse(this, unit);
69                 }
70         }
71
72         @Override
73         protected char[] getPrefix() {
74                 return (NameMangler.ITD_PREFIX + "interMethod$").toCharArray();
75         }
76
77         public boolean isFinal() {
78                 return (declaredModifiers & ClassFileConstants.AccFinal) != 0;
79         }
80
81 //      public boolean isAbstract() {
82 //              boolean b = (declaredModifiers & ClassFileConstants.AccAbstract) != 0;
83 //              return b;//super.isAbstract();
84 //      }
85
86         @Override
87         public void analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo) {
88                 if (Modifier.isAbstract(declaredModifiers))
89                         return;
90
91                 super.analyseCode(classScope, flowContext, flowInfo);
92         }
93
94         @Override
95         public void resolve(ClassScope upperScope) {
96                 if (munger == null)
97                         ignoreFurtherInvestigation = true;
98                 if (binding == null)
99                         ignoreFurtherInvestigation = true;
100                 if (ignoreFurtherInvestigation)
101                         return;
102
103                 if (!Modifier.isStatic(declaredModifiers)) {
104                         this.arguments = AstUtil.insert(AstUtil.makeFinalArgument("ajc$this_".toCharArray(), onTypeBinding), this.arguments);
105                         binding.parameters = AstUtil.insert(onTypeBinding, binding.parameters);
106                         
107                         // If the inserted argument is a generic type, we should include the associated type variables to ensure
108                         // the generated signature is correct (it will be checked by eclipse when this type is consumed in binary form).
109                         TypeVariableBinding onTypeTVBs[] = onTypeBinding.typeVariables();
110                         if (onTypeTVBs!=null && onTypeTVBs.length!=0) {
111                                 // The type parameters don't seem to need to be correct
112         //                      TypeParameter tp = new TypeParameter();
113         //                      tp.binding = tvb[0];
114         //                      tp.name = tvb[0].sourceName;
115         //                      this.typeParameters = AstUtil.insert(tp,this.typeParameters);
116                                 binding.typeVariables = AstUtil.insert(onTypeBinding.typeVariables(), binding.typeVariables);
117                         }
118                 }
119
120                 super.resolve(upperScope);
121         }
122
123         @Override
124         public void resolveStatements() {
125                 checkAndSetModifiersForMethod();
126                 if ((modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0) {
127                         if ((declaredModifiers & ClassFileConstants.AccAbstract) == 0)
128                                 scope.problemReporter().methodNeedBody(this);
129                 } else {
130                         // the method HAS a body --> abstract native modifiers are forbiden
131                         if (((declaredModifiers & ClassFileConstants.AccAbstract) != 0))
132                                 scope.problemReporter().methodNeedingNoBody(this);
133                 }
134
135                 // XXX AMC we need to do this, but I'm not 100% comfortable as I don't
136                 // know why the return type is wrong in this case. Also, we don't seem to need
137                 // to do it for args...
138                 if (munger.getSignature().getReturnType().isRawType()) {
139                         if (!binding.returnType.isRawType()) {
140                                 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope);
141                                 binding.returnType = world.makeTypeBinding(munger.getSignature().getReturnType());
142                         }
143                 }
144
145                 // check @Override annotation - based on MethodDeclaration.resolveStatements() @Override processing
146                 checkOverride: {
147                         if (this.binding == null)
148                                 break checkOverride;
149                         if (this.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5)
150                                 break checkOverride;
151                         boolean hasOverrideAnnotation = (this.binding.tagBits & TagBits.AnnotationOverride) != 0;
152
153                         // Need to verify
154                         if (hasOverrideAnnotation) {
155
156                                 // Work out the real method binding that we can use for comparison
157                                 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope);
158                                 MethodBinding realthing = world.makeMethodBinding(munger.getSignature(), munger.getTypeVariableAliases());
159
160                                 boolean reportError = true;
161                                 // Go up the hierarchy, looking for something we override
162                                 ReferenceBinding supertype = onTypeBinding.superclass();
163                                 while (supertype != null && reportError) {
164                                         MethodBinding[] possibles = supertype.getMethods(declaredSelector);
165                                         for (int i = 0; i < possibles.length; i++) {
166                                                 MethodBinding mb = possibles[i];
167
168                                                 boolean couldBeMatch = true;
169                                                 if (mb.parameters.length != realthing.parameters.length)
170                                                         couldBeMatch = false;
171                                                 else {
172                                                         for (int j = 0; j < mb.parameters.length && couldBeMatch; j++) {
173                                                                 if (!mb.parameters[j].equals(realthing.parameters[j]))
174                                                                         couldBeMatch = false;
175                                                         }
176                                                 }
177                                                 // return types compatible? (allow for covariance)
178                                                 if (couldBeMatch && !returnType.resolvedType.isCompatibleWith(mb.returnType))
179                                                         couldBeMatch = false;
180                                                 if (couldBeMatch)
181                                                         reportError = false;
182                                         }
183                                         supertype = supertype.superclass(); // superclass of object is null
184                                 }
185                                 // If we couldn't find something we override, report the error
186                                 if (reportError)
187                                         ((AjProblemReporter) this.scope.problemReporter()).itdMethodMustOverride(this, realthing);
188                         }
189                 }
190
191                 if (!Modifier.isAbstract(declaredModifiers))
192                         super.resolveStatements();
193                 if (Modifier.isStatic(declaredModifiers)) {
194                         // Check the target for ITD is not an interface
195                         if (onTypeBinding.isInterface()) {
196                                 scope.problemReporter().signalError(sourceStart, sourceEnd, "methods in interfaces cannot be declared static");
197                         }
198                 }
199         }
200
201         @Override
202         public EclipseTypeMunger build(ClassScope classScope) {
203                 EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(classScope);
204
205                 resolveOnType(classScope);
206                 if (ignoreFurtherInvestigation)
207                         return null;
208
209                 binding = classScope.referenceContext.binding.resolveTypesFor(binding);
210                 if (binding == null) {
211                         // if binding is null, we failed to find a type used in the method params, this error
212                         // has already been reported.
213                         this.ignoreFurtherInvestigation = true;
214                         // return null;
215                         throw new AbortCompilationUnit(compilationResult, null);
216                 }
217
218                 if (isTargetAnnotation(classScope, "method"))
219                         return null; // Error message output in isTargetAnnotation
220                 if (isTargetEnum(classScope, "method"))
221                         return null; // Error message output in isTargetEnum
222
223                 if (interTypeScope == null)
224                         return null; // We encountered a problem building the scope, don't continue - error already reported
225
226                 // This signature represents what we want consumers of the targetted type to 'see'
227                 // must use the factory method to build it since there may be typevariables from the binding
228                 // referred to in the parameters/returntype
229                 ResolvedMemberImpl sig = factory.makeResolvedMemberForITD(binding, onTypeBinding, interTypeScope.getRecoveryAliases());
230                 sig.resetName(new String(declaredSelector));
231                 int resetModifiers = declaredModifiers;
232                 if (binding.isVarargs())
233                         resetModifiers = resetModifiers | Constants.ACC_VARARGS;
234                 sig.resetModifiers(resetModifiers);
235                 NewMethodTypeMunger myMunger = new NewMethodTypeMunger(sig, null, typeVariableAliases);
236                 setMunger(myMunger);
237                 ResolvedType aspectType = factory.fromEclipse(classScope.referenceContext.binding);
238                 ResolvedMember me = myMunger.getInterMethodBody(aspectType);
239                 this.selector = binding.selector = me.getName().toCharArray();
240                 return new EclipseTypeMunger(factory, myMunger, aspectType, this);
241         }
242
243         private AjAttribute makeAttribute() {
244                 return new AjAttribute.TypeMunger(munger);
245         }
246
247         @Override
248         public void generateCode(ClassScope classScope, ClassFile classFile) {
249                 if (ignoreFurtherInvestigation) {
250                         // System.err.println("no code for " + this);
251                         return;
252                 }
253
254                 classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute()));
255
256                 if (!Modifier.isAbstract(declaredModifiers)) {
257                         super.generateCode(classScope, classFile); // this makes the interMethodBody
258                 }
259
260                 // annotations on the ITD declaration get put on this method
261                 generateDispatchMethod(classScope, classFile);
262         }
263
264         public void generateDispatchMethod(ClassScope classScope, ClassFile classFile) {
265                 EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope);
266
267                 UnresolvedType aspectType = world.fromBinding(classScope.referenceContext.binding);
268                 ResolvedMember signature = munger.getSignature();
269
270                 ResolvedMember dispatchMember = AjcMemberMaker.interMethodDispatcher(signature, aspectType);
271                 MethodBinding dispatchBinding = world.makeMethodBinding(dispatchMember, munger.getTypeVariableAliases(), munger
272                                 .getSignature().getDeclaringType());
273                 MethodBinding introducedMethod = world.makeMethodBinding(AjcMemberMaker.interMethod(signature, aspectType, onTypeBinding
274                                 .isInterface()), munger.getTypeVariableAliases());
275
276                 classFile.generateMethodInfoHeader(dispatchBinding);
277                 int methodAttributeOffset = classFile.contentsOffset;
278
279                 // Watch out! We are passing in 'binding' here (instead of dispatchBinding) so that
280                 // the dispatch binding attributes will include the annotations from the 'binding'.
281                 // There is a chance that something else on the binding (e.g. throws clause) might
282                 // damage the attributes generated for the dispatch binding.
283                 int attributeNumber = classFile.generateMethodInfoAttributes(binding, makeEffectiveSignatureAttribute(signature,
284                                 Shadow.MethodCall, false));
285                 int codeAttributeOffset = classFile.contentsOffset;
286                 classFile.generateCodeAttributeHeader();
287                 CodeStream codeStream = classFile.codeStream;
288                 codeStream.reset(this, classFile);
289                 codeStream.initializeMaxLocals(dispatchBinding);
290
291                 Argument[] itdArgs = this.arguments;
292                 if (itdArgs != null) {
293                         for (int a = 0; a < itdArgs.length; a++) {
294                                 LocalVariableBinding lvb = itdArgs[a].binding;
295                                 LocalVariableBinding lvbCopy = new LocalVariableBinding(lvb.name, lvb.type, lvb.modifiers, true);
296                                 // e37: have to create a declaration so that the check in ClassFile (line 2538) won't skip it
297                                 lvbCopy.declaration = new LocalDeclaration(itdArgs[a].name,0,0);
298                                 codeStream.record(lvbCopy);
299                                 lvbCopy.recordInitializationStartPC(0);
300                                 lvbCopy.resolvedPosition = lvb.resolvedPosition;
301                         }
302                 }
303
304                 MethodBinding methodBinding = introducedMethod;
305                 TypeBinding[] parameters = methodBinding.parameters;
306                 int length = parameters.length;
307                 int resolvedPosition;
308                 if (methodBinding.isStatic())
309                         resolvedPosition = 0;
310                 else {
311                         codeStream.aload_0();
312                         resolvedPosition = 1;
313                 }
314                 for (int i = 0; i < length; i++) {
315                         codeStream.load(parameters[i], resolvedPosition);
316                         if ((parameters[i] == TypeBinding.DOUBLE) || (parameters[i] == TypeBinding.LONG))
317                                 resolvedPosition += 2;
318                         else
319                                 resolvedPosition++;
320                 }
321                 // TypeBinding type;
322                 if (methodBinding.isStatic())
323                         codeStream.invoke(Opcodes.OPC_invokestatic,methodBinding,null);
324                 else {
325                         if (methodBinding.declaringClass.isInterface()) {
326                                 codeStream.invoke(Opcodes.OPC_invokeinterface, methodBinding, null);
327                         } else {
328                                 codeStream.invoke(Opcodes.OPC_invokevirtual, methodBinding, null);
329                         }
330                 }
331                 AstUtil.generateReturn(dispatchBinding.returnType, codeStream);
332
333                 // tag the local variables as used throughout the method
334                 if (itdArgs != null && codeStream.locals != null) {
335                         for (int a = 0; a < itdArgs.length; a++) {
336                                 if (codeStream.locals[a] != null) {
337                                         codeStream.locals[a].recordInitializationEndPC(codeStream.position);
338                                 }
339                         }
340                 }
341                 classFile.completeCodeAttribute(codeAttributeOffset);
342                 attributeNumber++;
343                 classFile.completeMethodInfo(binding,methodAttributeOffset, attributeNumber);
344         }
345
346         @Override
347         protected Shadow.Kind getShadowKindForBody() {
348                 return Shadow.MethodExecution;
349         }
350
351         // XXX this code is copied from MethodScope, with a few adjustments for ITDs...
352         private void checkAndSetModifiersForMethod() {
353
354                 // for reported problems, we want the user to see the declared selector
355                 char[] realSelector = this.selector;
356                 this.selector = declaredSelector;
357
358                 final ReferenceBinding declaringClass = this.binding.declaringClass;
359                 if ((declaredModifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0)
360                         scope.problemReporter().duplicateModifierForMethod(onTypeBinding, this);
361
362                 // after this point, tests on the 16 bits reserved.
363                 int realModifiers = declaredModifiers & ExtraCompilerModifiers.AccJustFlag;
364
365                 // check for abnormal modifiers
366                 int unexpectedModifiers = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected
367                                 | ClassFileConstants.AccAbstract | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal
368                                 | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp);
369                 if ((realModifiers & unexpectedModifiers) != 0) {
370                         scope.problemReporter().illegalModifierForMethod(this);
371                         declaredModifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~unexpectedModifiers;
372                 }
373
374                 // check for incompatible modifiers in the visibility bits, isolate the visibility bits
375                 int accessorBits = realModifiers
376                                 & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate);
377                 if ((accessorBits & (accessorBits - 1)) != 0) {
378                         scope.problemReporter().illegalVisibilityModifierCombinationForMethod(onTypeBinding, this);
379
380                         // need to keep the less restrictive so disable Protected/Private as necessary
381                         if ((accessorBits & ClassFileConstants.AccPublic) != 0) {
382                                 if ((accessorBits & ClassFileConstants.AccProtected) != 0)
383                                         declaredModifiers &= ~ClassFileConstants.AccProtected;
384                                 if ((accessorBits & ClassFileConstants.AccPrivate) != 0)
385                                         declaredModifiers &= ~ClassFileConstants.AccPrivate;
386                         } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) {
387                                 declaredModifiers &= ~ClassFileConstants.AccPrivate;
388                         }
389                 }
390
391                 // check for modifiers incompatible with abstract modifier
392                 if ((declaredModifiers & ClassFileConstants.AccAbstract) != 0) {
393                         int incompatibleWithAbstract = ClassFileConstants.AccStatic | ClassFileConstants.AccFinal
394                                         | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp;
395                         if ((declaredModifiers & incompatibleWithAbstract) != 0)
396                                 scope.problemReporter().illegalAbstractModifierCombinationForMethod(onTypeBinding, this);
397                         if (!onTypeBinding.isAbstract())
398                                 scope.problemReporter().abstractMethodInAbstractClass((SourceTypeBinding) onTypeBinding, this);
399                 }
400
401                 /*
402                  * DISABLED for backward compatibility with javac (if enabled should also mark private methods as final) // methods from a
403                  * final class are final : 8.4.3.3 if (methodBinding.declaringClass.isFinal()) modifiers |= AccFinal;
404                  */
405                 // native methods cannot also be tagged as strictfp
406                 if ((declaredModifiers & ClassFileConstants.AccNative) != 0 && (declaredModifiers & ClassFileConstants.AccStrictfp) != 0)
407                         scope.problemReporter().nativeMethodsCannotBeStrictfp(onTypeBinding, this);
408
409                 // static members are only authorized in a static member or top level type
410                 if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic())
411                         scope.problemReporter().unexpectedStaticModifierForMethod(onTypeBinding, this);
412
413                 // restore the true selector now that any problems have been reported
414                 this.selector = realSelector;
415         }
416 }