/* ******************************************************************* * 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; } }