All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.ajdt.internal.compiler.ast.InterTypeMethodDeclaration Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/* *******************************************************************
 * 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 v 2.0
 * which accompanies this distribution and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * 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 (MethodBinding mb : possibles) {
						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 (Argument itdArg : itdArgs) {
				LocalVariableBinding lvb = itdArg.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(itdArg.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 (TypeBinding parameter : parameters) {
			codeStream.load(parameter, resolvedPosition);
			if ((parameter == TypeBinding.DOUBLE) || (parameter == 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,scope);
		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;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy