org.aspectj.ajdt.internal.compiler.ast.InterTypeConstructorDeclaration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/* *******************************************************************
* 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.EclipseSourceLocation;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseTypeMunger;
import org.aspectj.ajdt.internal.compiler.lookup.InterTypeScope;
import org.aspectj.bridge.ISourceLocation;
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.ArrayAllocationExpression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Statement;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.StringLiteral;
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.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
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.MethodScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.AjcMemberMaker;
import org.aspectj.weaver.Constants;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.NewConstructorTypeMunger;
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 constructor declaration.
*
* This will generate two implementation methods in the aspect, the main one for the body of the constructor, and an additional
* preMethod
for the code that runs before the super constructor is called.
*
* @author Jim Hugunin
*/
public class InterTypeConstructorDeclaration extends InterTypeDeclaration {
private static final String SUPPRESSAJWARNINGS = "Lorg/aspectj/lang/annotation/SuppressAjWarnings;";
private static final String NOEXPLICITCONSTRUCTORCALL = "noExplicitConstructorCall";
private MethodDeclaration preMethod;
private ExplicitConstructorCall explicitConstructorCall = null;
public InterTypeConstructorDeclaration(CompilationResult result, TypeReference onType) {
super(result, onType);
}
public void parseStatements(Parser parser, CompilationUnitDeclaration unit) {
if (ignoreFurtherInvestigation)
return;
parser.parse(this, unit);
}
protected char[] getPrefix() {
return (NameMangler.ITD_PREFIX + "interConstructor$").toCharArray();
}
public void resolve(ClassScope upperScope) {
if (munger == null || binding == null)
ignoreFurtherInvestigation = true;
if (ignoreFurtherInvestigation)
return;
explicitConstructorCall = null;
if (statements != null && statements.length > 0 && statements[0] instanceof ExplicitConstructorCall) {
explicitConstructorCall = (ExplicitConstructorCall) statements[0];
statements = AstUtil.remove(0, statements);
}
preMethod = makePreMethod(upperScope, explicitConstructorCall);
binding.parameters = AstUtil.insert(onTypeBinding, binding.parameters);
this.arguments = AstUtil.insert(AstUtil.makeFinalArgument("ajc$this_".toCharArray(), onTypeBinding), this.arguments);
super.resolve(upperScope);
// after annotations have been resolved...
if (explicitConstructorCall == null) {
raiseNoFieldInitializersWarning();
}
}
/**
* Warning added in response to PR 62606 - if an ITD constructor does not make an explicit constructor call then field
* initializers in the target class will not be executed leading to unexpected behaviour.
*/
private void raiseNoFieldInitializersWarning() {
if (suppressingNoExplicitConstructorCall())
return;
EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope);
ISourceLocation location = new EclipseSourceLocation(scope.problemReporter().referenceContext.compilationResult(),
sourceStart(), sourceEnd());
world.getWorld().getLint().noExplicitConstructorCall.signal(null, location);
}
/**
* true iff constructor has @SuppressAjWarnings or @SuppressAjWarnings("xyz,noExplicitConstructorCall,def,...")
*
* @return
*/
private boolean suppressingNoExplicitConstructorCall() {
if (this.annotations == null)
return false;
for (int i = 0; i < this.annotations.length; i++) {
if (new String(this.annotations[i].resolvedType.signature()).equals(SUPPRESSAJWARNINGS)) {
if (this.annotations[i] instanceof MarkerAnnotation) {
return true;
} else if (this.annotations[i] instanceof SingleMemberAnnotation) {
SingleMemberAnnotation sma = (SingleMemberAnnotation) this.annotations[i];
if (sma.memberValue instanceof ArrayInitializer) {
ArrayInitializer memberValue = (ArrayInitializer) sma.memberValue;
for (int j = 0; j < memberValue.expressions.length; j++) {
if (memberValue.expressions[j] instanceof StringLiteral) {
StringLiteral val = (StringLiteral) memberValue.expressions[j];
if (new String(val.source()).equals(NOEXPLICITCONSTRUCTORCALL))
return true;
}
}
}
}
}
}
return false;
}
private MethodDeclaration makePreMethod(ClassScope scope, ExplicitConstructorCall explicitConstructorCall) {
EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(scope);
UnresolvedType aspectTypeX = world.fromBinding(binding.declaringClass);
UnresolvedType targetTypeX = world.fromBinding(onTypeBinding);
ArrayBinding objectArrayBinding = scope.createArrayType(scope.getJavaLangObject(), 1);
MethodDeclaration pre = new MethodDeclaration(compilationResult);
pre.modifiers = ClassFileConstants.AccPublic | ClassFileConstants.AccStatic;
pre.returnType = AstUtil.makeTypeReference(objectArrayBinding);
pre.selector = NameMangler.postIntroducedConstructor(aspectTypeX, targetTypeX).toCharArray();
pre.arguments = AstUtil.copyArguments(this.arguments);
// XXX should do exceptions
pre.scope = new MethodScope(scope, pre, true);
// ??? do we need to do anything with scope???
// Use the factory to build a semi-correct resolvedmember - then patch it up with
// reset calls. This is SAFE
ResolvedMemberImpl preIntroducedConstructorRM = world.makeResolvedMember(binding);
preIntroducedConstructorRM.resetName(NameMangler.preIntroducedConstructor(aspectTypeX, targetTypeX));
preIntroducedConstructorRM.resetModifiers(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL);
preIntroducedConstructorRM.resetReturnTypeToObjectArray();
pre.binding = world.makeMethodBinding(preIntroducedConstructorRM);
pre.bindArguments();
pre.bindThrownExceptions();
if (explicitConstructorCall == null) {
pre.statements = new Statement[] {};
} else {
pre.statements = new Statement[] { explicitConstructorCall };
}
InterTypeScope newParent = new InterTypeScope(scope, onTypeBinding);
pre.scope.parent = newParent;
pre.resolveStatements(); // newParent);
int nParams = pre.arguments.length;
MethodBinding explicitConstructor = null;
if (explicitConstructorCall != null) {
explicitConstructor = explicitConstructorCall.binding;
// If it is null then we are going to report something else is wrong with this code!
if (explicitConstructor != null && explicitConstructor.alwaysNeedsAccessMethod()) {
explicitConstructor = explicitConstructor.getAccessMethod(true);
}
}
int nExprs;
if (explicitConstructor == null)
nExprs = 0;
else
nExprs = explicitConstructor.parameters.length;
ArrayInitializer init = new ArrayInitializer();
init.expressions = new Expression[nExprs + nParams];
int index = 0;
for (int i = 0; i < nExprs; i++) {
if (i >= (explicitConstructorCall.arguments == null ? 0 : explicitConstructorCall.arguments.length)) {
init.expressions[index++] = new NullLiteral(0, 0);
continue;
}
Expression arg = explicitConstructorCall.arguments[i];
ResolvedMember conversionMethod = AjcMemberMaker.toObjectConversionMethod(world
.fromBinding(explicitConstructorCall.binding.parameters[i]));
if (conversionMethod != null) {
arg = new KnownMessageSend(world.makeMethodBindingForCall(conversionMethod), new CastExpression(new NullLiteral(0,
0), AstUtil.makeTypeReference(world.makeTypeBinding(AjcMemberMaker.CONVERSIONS_TYPE))),
new Expression[] { arg });
}
init.expressions[index++] = arg;
}
for (int i = 0; i < nParams; i++) {
LocalVariableBinding binding = pre.arguments[i].binding;
Expression arg = AstUtil.makeResolvedLocalVariableReference(binding);
ResolvedMember conversionMethod = AjcMemberMaker.toObjectConversionMethod(world.fromBinding(binding.type));
if (conversionMethod != null) {
arg = new KnownMessageSend(world.makeMethodBindingForCall(conversionMethod), new CastExpression(new NullLiteral(0,
0), AstUtil.makeTypeReference(world.makeTypeBinding(AjcMemberMaker.CONVERSIONS_TYPE))),
new Expression[] { arg });
}
init.expressions[index++] = arg;
}
init.binding = objectArrayBinding;
ArrayAllocationExpression newArray = new ArrayAllocationExpression();
newArray.initializer = init;
newArray.type = AstUtil.makeTypeReference(scope.getJavaLangObject());
newArray.dimensions = new Expression[1];
newArray.constant = Constant.NotAConstant;
pre.statements = new Statement[] { new ReturnStatement(newArray, 0, 0), };
return pre;
}
public EclipseTypeMunger build(ClassScope classScope) {
EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope);
resolveOnType(classScope);
if (ignoreFurtherInvestigation)
return null;
binding = classScope.referenceContext.binding.resolveTypesFor(binding);
if (isTargetAnnotation(classScope, "constructor"))
return null; // Error message output in isTargetAnnotation
if (isTargetEnum(classScope, "constructor"))
return null; // Error message output in isTargetEnum
if (onTypeBinding.isInterface()) {
classScope.problemReporter().signalError(sourceStart, sourceEnd, "can't define constructors on interfaces");
ignoreFurtherInvestigation = true;
return null;
}
if (onTypeBinding.isNestedType()) {
classScope.problemReporter().signalError(sourceStart, sourceEnd,
"can't define constructors on nested types (compiler limitation)");
ignoreFurtherInvestigation = true;
return null;
}
ResolvedType declaringTypeX = world.fromEclipse(onTypeBinding);
ResolvedType aspectType = world.fromEclipse(classScope.referenceContext.binding);
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'
ResolvedMemberImpl signature = world.makeResolvedMemberForITD(binding, onTypeBinding, interTypeScope.getRecoveryAliases());
signature.resetKind(Member.CONSTRUCTOR);
signature.resetName("");
int resetModifiers = declaredModifiers;
if (binding.isVarargs())
resetModifiers = resetModifiers | Constants.ACC_VARARGS;
signature.resetModifiers(resetModifiers);
ResolvedMember syntheticInterMember = AjcMemberMaker.interConstructor(declaringTypeX, signature, aspectType);
NewConstructorTypeMunger myMunger = new NewConstructorTypeMunger(signature, syntheticInterMember, null, null,
typeVariableAliases);
setMunger(myMunger);
myMunger.check(world.getWorld());
this.selector = binding.selector = NameMangler.postIntroducedConstructor(world.fromBinding(binding.declaringClass),
declaringTypeX).toCharArray();
return new EclipseTypeMunger(world, myMunger, aspectType, this);
}
private AjAttribute makeAttribute(EclipseFactory world) {
if (explicitConstructorCall != null && (explicitConstructorCall.binding != null)
&& !(explicitConstructorCall.binding instanceof ProblemMethodBinding)) {
MethodBinding explicitConstructor = explicitConstructorCall.binding;
if (explicitConstructor.alwaysNeedsAccessMethod()) {
explicitConstructor = explicitConstructor.getAccessMethod(true);
}
if (explicitConstructor instanceof ParameterizedMethodBinding) {
explicitConstructor = explicitConstructor.original();
}
((NewConstructorTypeMunger) munger).setExplicitConstructor(world.makeResolvedMember(explicitConstructor));
} else {
((NewConstructorTypeMunger) munger).setExplicitConstructor(new ResolvedMemberImpl(Member.CONSTRUCTOR, world
.fromBinding(onTypeBinding.superclass()), 0, UnresolvedType.VOID, "", UnresolvedType.NONE));
}
return new AjAttribute.TypeMunger(munger);
}
public void generateCode(ClassScope classScope, ClassFile classFile) {
if (ignoreFurtherInvestigation)
return;
EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(classScope);
classFile.extraAttributes.add(new EclipseAttributeAdapter(makeAttribute(world)));
super.generateCode(classScope, classFile);
// classFile.codeStream.generateAttributes &= ~ClassFileConstants.ATTR_VARS;
preMethod.generateCode(classScope, classFile);
}
protected Shadow.Kind getShadowKindForBody() {
return Shadow.ConstructorExecution;
}
}