/* ******************************************************************* * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v1.0 * which accompanies this distribution and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * PARC initial implementation * ******************************************************************/ package org.aspectj.ajdt.internal.compiler.ast; import java.lang.reflect.Modifier; import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory; import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger; import org.aspectj.ajdt.internal.compiler.problem.AjProblemReporter; import org.aspectj.org.eclipse.jdt.internal.compiler.ClassFile; import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.aspectj.org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TagBits; import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser; import org.aspectj.org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit; import org.aspectj.weaver.AjAttribute; import org.aspectj.weaver.AjcMemberMaker; import org.aspectj.weaver.Constants; import org.aspectj.weaver.NameMangler; import org.aspectj.weaver.NewMethodTypeMunger; import org.aspectj.weaver.ResolvedMember; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.Shadow; import org.aspectj.weaver.UnresolvedType; /** * An inter-type method declaration. * * @author Jim Hugunin */ 00054 public class InterTypeMethodDeclaration extends InterTypeDeclaration { public InterTypeMethodDeclaration(CompilationResult result, TypeReference onType) { super(result, onType); } public void parseStatements(Parser parser, CompilationUnitDeclaration unit) { if (ignoreFurtherInvestigation) return; if (!Modifier.isAbstract(declaredModifiers)) { parser.parse(this, unit); } } protected char[] getPrefix() { return (NameMangler.ITD_PREFIX + "interMethod$").toCharArray(); } public boolean isFinal() { return (declaredModifiers & ClassFileConstants.AccFinal) != 0; } public void analyseCode(ClassScope currentScope, InitializationFlowContext flowContext, FlowInfo flowInfo) { if (Modifier.isAbstract(declaredModifiers)) return; super.analyseCode(currentScope, flowContext, flowInfo); } public void resolve(ClassScope upperScope) { if (munger == null) ignoreFurtherInvestigation = true; if (binding == null) ignoreFurtherInvestigation = true; if (ignoreFurtherInvestigation) return; if (!Modifier.isStatic(declaredModifiers)) { this.arguments = AstUtil.insert(AstUtil.makeFinalArgument("ajc$this_".toCharArray(), onTypeBinding), this.arguments); binding.parameters = AstUtil.insert(onTypeBinding, binding.parameters); } super.resolve(upperScope); } public void resolveStatements() { checkAndSetModifiersForMethod(); if ((modifiers & ExtraCompilerModifiers.AccSemicolonBody) != 0) { if ((declaredModifiers & ClassFileConstants.AccAbstract) == 0) scope.problemReporter().methodNeedBody(this); } else { // the method HAS a body --> abstract native modifiers are forbiden if (((declaredModifiers & ClassFileConstants.AccAbstract) != 0)) scope.problemReporter().methodNeedingNoBody(this); } // XXX AMC we need to do this, but I'm not 100% comfortable as I don't // know why the return type is wrong in this case. Also, we don't seem to need // to do it for args... if (munger.getSignature().getReturnType().isRawType()) { if (!binding.returnType.isRawType()) { EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope); binding.returnType = world.makeTypeBinding(munger.getSignature().getReturnType()); } } // check @Override annotation - based on MethodDeclaration.resolveStatements() @Override processing checkOverride: { if (this.binding == null) break checkOverride; if (this.scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) break checkOverride; boolean hasOverrideAnnotation = (this.binding.tagBits & TagBits.AnnotationOverride) != 0; // Need to verify if (hasOverrideAnnotation) { // Work out the real method binding that we can use for comparison EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope); MethodBinding realthing = world.makeMethodBinding(munger.getSignature(), munger.getTypeVariableAliases()); boolean reportError = true; // Go up the hierarchy, looking for something we override ReferenceBinding supertype = onTypeBinding.superclass(); while (supertype != null && reportError) { MethodBinding[] possibles = supertype.getMethods(declaredSelector); for (int i = 0; i < possibles.length; i++) { MethodBinding mb = possibles[i]; boolean couldBeMatch = true; if (mb.parameters.length != realthing.parameters.length) couldBeMatch = false; else { for (int j = 0; j < mb.parameters.length && couldBeMatch; j++) { if (!mb.parameters[j].equals(realthing.parameters[j])) couldBeMatch = false; } } // return types compatible? (allow for covariance) if (couldBeMatch && !returnType.resolvedType.isCompatibleWith(mb.returnType)) couldBeMatch = false; if (couldBeMatch) reportError = false; } supertype = supertype.superclass(); // superclass of object is null } // If we couldn't find something we override, report the error if (reportError) ((AjProblemReporter) this.scope.problemReporter()).itdMethodMustOverride(this, realthing); } } if (!Modifier.isAbstract(declaredModifiers)) super.resolveStatements(); if (Modifier.isStatic(declaredModifiers)) { // Check the target for ITD is not an interface if (onTypeBinding.isInterface()) { scope.problemReporter().signalError(sourceStart, sourceEnd, "methods in interfaces cannot be declared static"); } } } 00175 public EclipseTypeMunger build(ClassScope classScope) { EclipseFactory factory = EclipseFactory.fromScopeLookupEnvironment(classScope); resolveOnType(classScope); if (ignoreFurtherInvestigation) return null; binding = classScope.referenceContext.binding.resolveTypesFor(binding); if (binding == null) { // if binding is null, we failed to find a type used in the method params, this error // has already been reported. this.ignoreFurtherInvestigation = true; // return null; throw new AbortCompilationUnit(compilationResult, null); } if (isTargetAnnotation(classScope, "method")) return null; // Error message output in isTargetAnnotation if (isTargetEnum(classScope, "method")) return null; // Error message output in isTargetEnum if (interTypeScope == null) return null; // We encountered a problem building the scope, don't continue - error already reported // This signature represents what we want consumers of the targetted type to 'see' // must use the factory method to build it since there may be typevariables from the binding // referred to in the parameters/returntype ResolvedMember sig = factory.makeResolvedMemberForITD(binding, onTypeBinding, interTypeScope.getRecoveryAliases()); sig.resetName(new String(declaredSelector)); int resetModifiers = declaredModifiers; if (binding.isVarargs()) resetModifiers = resetModifiers | Constants.ACC_VARARGS; sig.resetModifiers(resetModifiers); NewMethodTypeMunger myMunger = new NewMethodTypeMunger(sig, null, typeVariableAliases); setMunger(myMunger); ResolvedType aspectType = factory.fromEclipse(classScope.referenceContext.binding); ResolvedMember me = myMunger.getInterMethodBody(aspectType); this.selector = binding.selector = me.getName().toCharArray(); return new EclipseTypeMunger(factory, myMunger, aspectType, this); } private AjAttribute makeAttribute() { return new AjAttribute.TypeMunger(munger); } public void generateCode(ClassScope classScope, ClassFile classFile) { if (ignoreFurtherInvestigation) { // System.err.println("no code for " + this); return; } classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute())); if (!Modifier.isAbstract(declaredModifiers)) { super.generateCode(classScope, classFile); // this makes the interMethodBody } // annotations on the ITD declaration get put on this method generateDispatchMethod(classScope, classFile); } public void generateDispatchMethod(ClassScope classScope, ClassFile classFile) { EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope); UnresolvedType aspectType = world.fromBinding(classScope.referenceContext.binding); ResolvedMember signature = munger.getSignature(); ResolvedMember dispatchMember = AjcMemberMaker.interMethodDispatcher(signature, aspectType); MethodBinding dispatchBinding = world.makeMethodBinding(dispatchMember, munger.getTypeVariableAliases(), munger .getSignature().getDeclaringType()); MethodBinding introducedMethod = world.makeMethodBinding(AjcMemberMaker.interMethod(signature, aspectType, onTypeBinding .isInterface()), munger.getTypeVariableAliases()); classFile.generateMethodInfoHeader(dispatchBinding); int methodAttributeOffset = classFile.contentsOffset; // Watch out! We are passing in 'binding' here (instead of dispatchBinding) so that // the dispatch binding attributes will include the annotations from the 'binding'. // There is a chance that something else on the binding (e.g. throws clause) might // damage the attributes generated for the dispatch binding. int attributeNumber = classFile.generateMethodInfoAttribute(binding, false, makeEffectiveSignatureAttribute(signature, Shadow.MethodCall, false)); int codeAttributeOffset = classFile.contentsOffset; classFile.generateCodeAttributeHeader(); CodeStream codeStream = classFile.codeStream; codeStream.reset(this, classFile); codeStream.initializeMaxLocals(dispatchBinding); Argument[] itdArgs = this.arguments; if (itdArgs != null) { for (int a = 0; a < itdArgs.length; a++) { LocalVariableBinding lvb = itdArgs[a].binding; LocalVariableBinding lvbCopy = new LocalVariableBinding(lvb.name, lvb.type, lvb.modifiers, true); codeStream.record(lvbCopy); lvbCopy.recordInitializationStartPC(0); lvbCopy.resolvedPosition = lvb.resolvedPosition; } } MethodBinding methodBinding = introducedMethod; TypeBinding[] parameters = methodBinding.parameters; int length = parameters.length; int resolvedPosition; if (methodBinding.isStatic()) resolvedPosition = 0; else { codeStream.aload_0(); resolvedPosition = 1; } for (int i = 0; i < length; i++) { codeStream.load(parameters[i], resolvedPosition); if ((parameters[i] == TypeBinding.DOUBLE) || (parameters[i] == TypeBinding.LONG)) resolvedPosition += 2; else resolvedPosition++; } // TypeBinding type; if (methodBinding.isStatic()) codeStream.invokestatic(methodBinding); else { if (methodBinding.declaringClass.isInterface()) { codeStream.invokeinterface(methodBinding); } else { codeStream.invokevirtual(methodBinding); } } AstUtil.generateReturn(dispatchBinding.returnType, codeStream); // tag the local variables as used throughout the method if (itdArgs != null && codeStream.locals != null) { for (int a = 0; a < itdArgs.length; a++) { if (codeStream.locals[a] != null) { codeStream.locals[a].recordInitializationEndPC(codeStream.position); } } } classFile.completeCodeAttribute(codeAttributeOffset); attributeNumber++; classFile.completeMethodInfo(methodAttributeOffset, attributeNumber); } protected Shadow.Kind getShadowKindForBody() { return Shadow.MethodExecution; } // XXX this code is copied from MethodScope, with a few adjustments for ITDs... private void checkAndSetModifiersForMethod() { // for reported problems, we want the user to see the declared selector char[] realSelector = this.selector; this.selector = declaredSelector; final ReferenceBinding declaringClass = this.binding.declaringClass; if ((declaredModifiers & ExtraCompilerModifiers.AccAlternateModifierProblem) != 0) scope.problemReporter().duplicateModifierForMethod(onTypeBinding, this); // after this point, tests on the 16 bits reserved. int realModifiers = declaredModifiers & ExtraCompilerModifiers.AccJustFlag; // check for abnormal modifiers int unexpectedModifiers = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccAbstract | ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp); if ((realModifiers & unexpectedModifiers) != 0) { scope.problemReporter().illegalModifierForMethod(this); declaredModifiers &= ~ExtraCompilerModifiers.AccJustFlag | ~unexpectedModifiers; } // check for incompatible modifiers in the visibility bits, isolate the visibility bits int accessorBits = realModifiers & (ClassFileConstants.AccPublic | ClassFileConstants.AccProtected | ClassFileConstants.AccPrivate); if ((accessorBits & (accessorBits - 1)) != 0) { scope.problemReporter().illegalVisibilityModifierCombinationForMethod(onTypeBinding, this); // need to keep the less restrictive so disable Protected/Private as necessary if ((accessorBits & ClassFileConstants.AccPublic) != 0) { if ((accessorBits & ClassFileConstants.AccProtected) != 0) declaredModifiers &= ~ClassFileConstants.AccProtected; if ((accessorBits & ClassFileConstants.AccPrivate) != 0) declaredModifiers &= ~ClassFileConstants.AccPrivate; } else if ((accessorBits & ClassFileConstants.AccProtected) != 0 && (accessorBits & ClassFileConstants.AccPrivate) != 0) { declaredModifiers &= ~ClassFileConstants.AccPrivate; } } // check for modifiers incompatible with abstract modifier if ((declaredModifiers & ClassFileConstants.AccAbstract) != 0) { int incompatibleWithAbstract = ClassFileConstants.AccStatic | ClassFileConstants.AccFinal | ClassFileConstants.AccSynchronized | ClassFileConstants.AccNative | ClassFileConstants.AccStrictfp; if ((declaredModifiers & incompatibleWithAbstract) != 0) scope.problemReporter().illegalAbstractModifierCombinationForMethod(onTypeBinding, this); if (!onTypeBinding.isAbstract()) scope.problemReporter().abstractMethodInAbstractClass((SourceTypeBinding) onTypeBinding, this); } /* * DISABLED for backward compatibility with javac (if enabled should also mark private methods as final) // methods from a * final class are final : 8.4.3.3 if (methodBinding.declaringClass.isFinal()) modifiers |= AccFinal; */ // native methods cannot also be tagged as strictfp if ((declaredModifiers & ClassFileConstants.AccNative) != 0 && (declaredModifiers & ClassFileConstants.AccStrictfp) != 0) scope.problemReporter().nativeMethodsCannotBeStrictfp(onTypeBinding, this); // static members are only authorized in a static member or top level type if (((realModifiers & ClassFileConstants.AccStatic) != 0) && declaringClass.isNestedType() && !declaringClass.isStatic()) scope.problemReporter().unexpectedStaticModifierForMethod(onTypeBinding, this); // restore the true selector now that any problems have been reported this.selector = realSelector; } }