Logo Search packages:      
Sourcecode: aspectj version File versions  Download package

InterTypeConstructorDeclaration.java

/* *******************************************************************
 * 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
 * <code>preMethod</code> for the code that runs before the super constructor is called.
 * 
 * @author Jim Hugunin
 */
00068 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.
       */
00117       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
       */
00131       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
            ResolvedMember 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;
      }

00259       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'
            ResolvedMember signature = world.makeResolvedMemberForITD(binding, onTypeBinding, interTypeScope.getRecoveryAliases());
            signature.resetKind(Member.CONSTRUCTOR);
            signature.resetName("<init>");
            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, ResolvedType.VOID, "<init>", 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;
      }

}

Generated by  Doxygen 1.6.0   Back to index