blob: fc0d421546df1ed8b0e667d815943097716440b0 [file] [log] [blame]
/* *******************************************************************
* 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.LocalDeclaration;
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.codegen.Opcodes;
import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.aspectj.org.eclipse.jdt.internal.compiler.flow.FlowInfo;
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.lookup.TypeVariableBinding;
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.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.UnresolvedType;
/**
* An inter-type method declaration.
*
* @author Jim Hugunin
*/
public class InterTypeMethodDeclaration extends InterTypeDeclaration {
public InterTypeMethodDeclaration(CompilationResult result, TypeReference onType) {
super(result, onType);
}
@Override
public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
if (ignoreFurtherInvestigation)
return;
if (!Modifier.isAbstract(declaredModifiers)) {
parser.parse(this, unit);
}
}
@Override
protected char[] getPrefix() {
return (NameMangler.ITD_PREFIX + "interMethod$").toCharArray();
}
public boolean isFinal() {
return (declaredModifiers & ClassFileConstants.AccFinal) != 0;
}
// public boolean isAbstract() {
// boolean b = (declaredModifiers & ClassFileConstants.AccAbstract) != 0;
// return b;//super.isAbstract();
// }
@Override
public void analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo) {
if (Modifier.isAbstract(declaredModifiers))
return;
super.analyseCode(classScope, flowContext, flowInfo);
}
@Override
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);
// If the inserted argument is a generic type, we should include the associated type variables to ensure
// the generated signature is correct (it will be checked by eclipse when this type is consumed in binary form).
TypeVariableBinding onTypeTVBs[] = onTypeBinding.typeVariables();
if (onTypeTVBs!=null && onTypeTVBs.length!=0) {
// The type parameters don't seem to need to be correct
// TypeParameter tp = new TypeParameter();
// tp.binding = tvb[0];
// tp.name = tvb[0].sourceName;
// this.typeParameters = AstUtil.insert(tp,this.typeParameters);
binding.typeVariables = AstUtil.insert(onTypeBinding.typeVariables(), binding.typeVariables);
}
}
super.resolve(upperScope);
}
@Override
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");
}
}
}
@Override
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
ResolvedMemberImpl 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);
}
@Override
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.generateMethodInfoAttributes(binding, 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);
// e37: have to create a declaration so that the check in ClassFile (line 2538) won't skip it
lvbCopy.declaration = new LocalDeclaration(itdArgs[a].name,0,0);
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.invoke(Opcodes.OPC_invokestatic,methodBinding,null);
else {
if (methodBinding.declaringClass.isInterface()) {
codeStream.invoke(Opcodes.OPC_invokeinterface, methodBinding, null);
} else {
codeStream.invoke(Opcodes.OPC_invokevirtual, methodBinding, null);
}
}
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(binding,methodAttributeOffset, attributeNumber);
}
@Override
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;
}
}