Logo Search packages:      
Sourcecode: aspectj version File versions

AdviceDeclaration.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 java.util.ArrayList;
import java.util.List;

import org.aspectj.ajdt.internal.compiler.lookup.AjTypeConstants;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.ajdt.internal.compiler.lookup.PrivilegedHandler;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.core.Flags;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
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.Annotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.CodeStream;
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.ExtraCompilerModifiers;
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.TypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.aspectj.weaver.Advice;
import org.aspectj.weaver.AdviceKind;
import org.aspectj.weaver.AjAttribute;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.UnresolvedType;

/**
 * Represents before, after and around advice in an aspect. Will generate a method corresponding to the body of the advice with an
 * attribute including additional information.
 * 
 * @author Jim Hugunin
 */
00053 public class AdviceDeclaration extends AjMethodDeclaration {
      public PointcutDesignator pointcutDesignator; // set during parsing
      int baseArgumentCount; // referenced by IfPseudoToken.makeArguments

      public Argument extraArgument; // set during parsing, referenced by Proceed

      public AdviceKind kind; // set during parsing, referenced by Proceed and AsmElementFormatter
      private int extraArgumentFlags = 0;

      public MethodBinding proceedMethodBinding; // set during this.resolveStaments, referenced by Proceed
      public List proceedCalls = new ArrayList(2); // populated during Proceed.findEnclosingAround

      private boolean proceedInInners;
      private ResolvedMember[] proceedCallSignatures;
      private boolean[] formalsUnchangedToProceed;
      private UnresolvedType[] declaredExceptions;

      public AdviceDeclaration(CompilationResult result, AdviceKind kind) {
            super(result);
            this.returnType = TypeReference.baseTypeReference(T_void, 0);
            this.kind = kind;
      }

      // override
      protected int generateInfoAttributes(ClassFile classFile) {
            List l = new ArrayList(1);
            l.add(new EclipseAttributeAdapter(makeAttribute()));
            addDeclarationStartLineAttribute(l, classFile);

            return classFile.generateMethodInfoAttribute(binding, false, l);
      }

      private AjAttribute makeAttribute() {
            if (kind == AdviceKind.Around) {
                  return new AjAttribute.AdviceAttribute(kind, pointcutDesignator.getPointcut(), extraArgumentFlags, sourceStart,
                              sourceEnd, null, proceedInInners, proceedCallSignatures, formalsUnchangedToProceed, declaredExceptions);
            } else {
                  return new AjAttribute.AdviceAttribute(kind, pointcutDesignator.getPointcut(), extraArgumentFlags, sourceStart,
                              sourceEnd, null);
            }
      }

      // override
      public void resolveStatements() {
            if (binding == null || ignoreFurtherInvestigation)
                  return;

            ClassScope upperScope = (ClassScope) scope.parent; // !!! safety

            modifiers = checkAndSetModifiers(modifiers, upperScope);
            int bindingModifiers = (modifiers | (binding.modifiers & ExtraCompilerModifiers.AccGenericSignature));
            binding.modifiers = bindingModifiers;

            if (kind == AdviceKind.AfterThrowing && extraArgument != null) {
                  TypeBinding argTb = extraArgument.binding.type;
                  TypeBinding expectedTb = upperScope.getJavaLangThrowable();
                  if (!argTb.isCompatibleWith(expectedTb)) {
                        scope.problemReporter().typeMismatchError(argTb, expectedTb, extraArgument);
                        ignoreFurtherInvestigation = true;
                        return;
                  }
            }

            pointcutDesignator.finishResolveTypes(this, this.binding, baseArgumentCount, upperScope.referenceContext.binding);

            if (binding == null || ignoreFurtherInvestigation)
                  return;

            if (kind == AdviceKind.Around) {
                  ReferenceBinding[] exceptions = new ReferenceBinding[] { upperScope.getJavaLangThrowable() };
                  proceedMethodBinding = new MethodBinding(Modifier.STATIC | Flags.AccSynthetic, "proceed".toCharArray(),
                              binding.returnType, resize(baseArgumentCount + 1, binding.parameters), exceptions, binding.declaringClass);
                  proceedMethodBinding.selector = CharOperation.concat(selector, proceedMethodBinding.selector);
            }

            super.resolveStatements(); // upperScope);
            if (binding != null)
                  determineExtraArgumentFlags();

            if (kind == AdviceKind.Around) {
                  int n = proceedCalls.size();
                  // EclipseFactory world = EclipseFactory.fromScopeLookupEnvironment(upperScope);

                  // System.err.println("access to: " + Arrays.asList(handler.getMembers()));

                  // XXX set these correctly
                  formalsUnchangedToProceed = new boolean[baseArgumentCount];
                  proceedCallSignatures = new ResolvedMember[0];
                  proceedInInners = false;
                  declaredExceptions = new UnresolvedType[0];

                  for (int i = 0; i < n; i++) {
                        Proceed call = (Proceed) proceedCalls.get(i);
                        if (call.inInner) {
                              // System.err.println("proceed in inner: " + call);
                              proceedInInners = true;
                              // XXX wrong
                              // proceedCallSignatures[i] = world.makeResolvedMember(call.binding);
                        }
                  }

                  // ??? should reorganize into AspectDeclaration
                  // if we have proceed in inners we won't ever be inlined so the code below is unneeded
                  if (!proceedInInners) {
                        PrivilegedHandler handler = (PrivilegedHandler) upperScope.referenceContext.binding.privilegedHandler;
                        if (handler == null) {
                              handler = new PrivilegedHandler((AspectDeclaration) upperScope.referenceContext);
                              // upperScope.referenceContext.binding.privilegedHandler = handler;
                        }

                        this.traverse(new MakeDeclsPublicVisitor(), (ClassScope) null);

                        AccessForInlineVisitor v = new AccessForInlineVisitor((AspectDeclaration) upperScope.referenceContext, handler);
                        ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.ACCESS_FOR_INLINE,
                                    selector);
                        this.traverse(v, (ClassScope) null);
                        CompilationAndWeavingContext.leavingPhase(tok);

                        // ??? if we found a construct that we can't inline, set
                        // proceedInInners so that we won't try to inline this body
                        if (!v.isInlinable)
                              proceedInInners = true;
                  }
            }
      }

      // called by Proceed.resolveType
      public int getDeclaredParameterCount() {
            // this only works before code generation
            return this.arguments.length - 3 - ((extraArgument == null) ? 0 : 1);
            // Advice.countOnes(extraArgumentFlags);
      }

      private void generateProceedMethod(ClassScope classScope, ClassFile classFile) {
            MethodBinding binding = proceedMethodBinding;

            classFile.generateMethodInfoHeader(binding);
            int methodAttributeOffset = classFile.contentsOffset;
            int attributeNumber = classFile.generateMethodInfoAttribute(binding, false, AstUtil.getAjSyntheticAttribute());
            int codeAttributeOffset = classFile.contentsOffset;
            classFile.generateCodeAttributeHeader();
            CodeStream codeStream = classFile.codeStream;
            codeStream.reset(this, classFile);

            // push the closure
            int nargs = binding.parameters.length;
            int closureIndex = 0;
            for (int i = 0; i < nargs - 1; i++) {
                  closureIndex += AstUtil.slotsNeeded(binding.parameters[i]);
            }

            codeStream.aload(closureIndex);

            // build the Object[]

            codeStream.generateInlinedValue(nargs - 1);
            codeStream.newArray(new ArrayBinding(classScope.getType(TypeConstants.JAVA_LANG_OBJECT,
                        TypeConstants.JAVA_LANG_OBJECT.length), 1, classScope.environment()));

            int index = 0;
            for (int i = 0; i < nargs - 1; i++) {
                  TypeBinding type = binding.parameters[i];
                  codeStream.dup();
                  codeStream.generateInlinedValue(i);
                  codeStream.load(type, index);
                  index += AstUtil.slotsNeeded(type);
                  if (type.isBaseType()) {
                        codeStream.invokestatic(AjTypeConstants.getConversionMethodToObject(classScope, type));
                  }

                  codeStream.aastore();
            }

            // call run
            ReferenceBinding closureType = (ReferenceBinding) binding.parameters[nargs - 1];
            MethodBinding runMethod = closureType.getMethods("run".toCharArray())[0];
            codeStream.invokevirtual(runMethod);

            TypeBinding returnType = binding.returnType;
            if (returnType.isBaseType()) {
                  codeStream.invokestatic(AjTypeConstants.getConversionMethodFromObject(classScope, returnType));
            } else {
                  codeStream.checkcast(returnType);
            }
            AstUtil.generateReturn(returnType, codeStream);
            codeStream.recordPositionsFrom(0, 1);
            classFile.completeCodeAttribute(codeAttributeOffset);
            attributeNumber++;
            classFile.completeMethodInfo(methodAttributeOffset, attributeNumber);
      }

      // override
      public void generateCode(ClassScope classScope, ClassFile classFile) {
            if (ignoreFurtherInvestigation)
                  return;

            super.generateCode(classScope, classFile);
            if (proceedMethodBinding != null) {
                  generateProceedMethod(classScope, classFile);
            }
      }

      private void determineExtraArgumentFlags() {
            if (extraArgument != null)
                  extraArgumentFlags |= Advice.ExtraArgument;

            ThisJoinPointVisitor tjp = new ThisJoinPointVisitor(this);
            extraArgumentFlags |= tjp.removeUnusedExtraArguments();
      }

      private static TypeBinding[] resize(int newSize, TypeBinding[] bindings) {
            int len = bindings.length;
            TypeBinding[] ret = new TypeBinding[newSize];
            System.arraycopy(bindings, 0, ret, 0, Math.min(newSize, len));
            return ret;
      }

      /**
       * Add either the @Before, @After, @Around, @AfterReturning or @AfterThrowing annotation
       */
00273       public void addAtAspectJAnnotations() {
            Annotation adviceAnnotation = null;
            String pointcutExpression = pointcutDesignator.getPointcut().toString();
            String extraArgumentName = "";
            if (extraArgument != null) {
                  extraArgumentName = new String(extraArgument.name);
            }
            String argNames = buildArgNameRepresentation();

            if (kind == AdviceKind.Before) {
                  adviceAnnotation = AtAspectJAnnotationFactory.createBeforeAnnotation(pointcutExpression, argNames,
                              declarationSourceStart);
            } else if (kind == AdviceKind.After) {
                  adviceAnnotation = AtAspectJAnnotationFactory.createAfterAnnotation(pointcutExpression, argNames,
                              declarationSourceStart);
            } else if (kind == AdviceKind.AfterReturning) {
                  adviceAnnotation = AtAspectJAnnotationFactory.createAfterReturningAnnotation(pointcutExpression, argNames,
                              extraArgumentName, declarationSourceStart);
            } else if (kind == AdviceKind.AfterThrowing) {
                  adviceAnnotation = AtAspectJAnnotationFactory.createAfterThrowingAnnotation(pointcutExpression, argNames,
                              extraArgumentName, declarationSourceStart);
            } else if (kind == AdviceKind.Around) {
                  adviceAnnotation = AtAspectJAnnotationFactory.createAroundAnnotation(pointcutExpression, argNames,
                              declarationSourceStart);
            }
            AtAspectJAnnotationFactory.addAnnotation(this, adviceAnnotation, this.scope);
      }

      private String buildArgNameRepresentation() {
            StringBuffer args = new StringBuffer();
            int numArgsWeCareAbout = getDeclaredParameterCount();
            if (this.arguments != null) {
                  for (int i = 0; i < numArgsWeCareAbout; i++) {
                        if (i != 0)
                              args.append(",");
                        args.append(new String(this.arguments[i].name));
                  }
            }
            if (extraArgument != null) {
                  if (numArgsWeCareAbout > 0) {
                        args.append(",");
                  }
                  args.append(new String(extraArgument.name));
            }
            return args.toString();
      }

      // override, Called by ClassScope.postParse
      public void postParse(TypeDeclaration typeDec) {
            AspectDeclaration aspectDecl = (AspectDeclaration) typeDec;
            int adviceSequenceNumberInType = aspectDecl.adviceCounter++;

            StringBuffer stringifiedPointcut = new StringBuffer(30);
            pointcutDesignator.print(0, stringifiedPointcut);
            this.selector = NameMangler.adviceName(EclipseFactory.getName(typeDec.binding).replace('.', '_'), kind,
                        adviceSequenceNumberInType, stringifiedPointcut.toString().hashCode()).toCharArray();
            if (arguments != null) {
                  baseArgumentCount = arguments.length;
            }

            if (kind == AdviceKind.Around) {
                  extraArgument = makeFinalArgument("ajc$aroundClosure", AjTypeConstants.getAroundClosureType());
            }

            int addedArguments = 3;
            if (extraArgument != null) {
                  addedArguments += 1;
            }

            arguments = extendArgumentsLength(arguments, addedArguments);

            int index = baseArgumentCount;
            if (extraArgument != null) {
                  arguments[index++] = extraArgument;
            }

            arguments[index++] = makeFinalArgument("thisJoinPointStaticPart", AjTypeConstants.getJoinPointStaticPartType());
            arguments[index++] = makeFinalArgument("thisJoinPoint", AjTypeConstants.getJoinPointType());
            arguments[index++] = makeFinalArgument("thisEnclosingJoinPointStaticPart", AjTypeConstants.getJoinPointStaticPartType());

            if (pointcutDesignator.isError()) {
                  this.ignoreFurtherInvestigation = true;
            }
            pointcutDesignator.postParse(typeDec, this);
      }

      private int checkAndSetModifiers(int modifiers, ClassScope scope) {
            if (modifiers == 0)
                  return Modifier.PUBLIC;
            else if (modifiers == Modifier.STRICT)
                  return Modifier.PUBLIC | Modifier.STRICT;
            else {
                  tagAsHavingErrors();
                  scope.problemReporter().signalError(declarationSourceStart, sourceStart - 1,
                              "illegal modifier on advice, only strictfp is allowed");
                  return Modifier.PUBLIC;
            }
      }

      // called by IfPseudoToken
      public static Argument[] addTjpArguments(Argument[] arguments) {
            int index = arguments.length;
            arguments = extendArgumentsLength(arguments, 3);

            arguments[index++] = makeFinalArgument("thisJoinPointStaticPart", AjTypeConstants.getJoinPointStaticPartType());
            arguments[index++] = makeFinalArgument("thisJoinPoint", AjTypeConstants.getJoinPointType());
            arguments[index++] = makeFinalArgument("thisEnclosingJoinPointStaticPart", AjTypeConstants.getJoinPointStaticPartType());

            return arguments;
      }

      private static Argument makeFinalArgument(String name, TypeReference typeRef) {
            long pos = 0; // XXX encode start and end location
            return new Argument(name.toCharArray(), pos, typeRef, Modifier.FINAL);
      }

      private static Argument[] extendArgumentsLength(Argument[] args, int addedArguments) {
            if (args == null) {
                  return new Argument[addedArguments];
            }
            int len = args.length;
            Argument[] ret = new Argument[len + addedArguments];
            System.arraycopy(args, 0, ret, 0, len);
            return ret;
      }

      // public String toString(int tab) {
      // String s = tabString(tab);
      // if (modifiers != AccDefault) {
      // s += modifiersString(modifiers);
      // }
      //
      // if (kind == AdviceKind.Around) {
      // s += returnTypeToString(0);
      // }
      //
      //          s += new String(selector) + "("; //$NON-NLS-1$
      // if (arguments != null) {
      // for (int i = 0; i < arguments.length; i++) {
      // s += arguments[i].toString(0);
      // if (i != (arguments.length - 1))
      //                            s = s + ", "; //$NON-NLS-1$
      // };
      // };
      //          s += ")"; //$NON-NLS-1$
      //          
      // if (extraArgument != null) {
      // s += "(" + extraArgument.toString(0) + ")";
      // }
      //          
      //          
      //          
      // if (thrownExceptions != null) {
      //                s += " throws "; //$NON-NLS-1$
      // for (int i = 0; i < thrownExceptions.length; i++) {
      // s += thrownExceptions[i].toString(0);
      // if (i != (thrownExceptions.length - 1))
      //                            s = s + ", "; //$NON-NLS-1$
      // };
      // };
      //          
      // s += ": ";
      // if (pointcutDesignator != null) {
      // s += pointcutDesignator.toString(0);
      // }
      //
      // s += toStringStatements(tab + 1);
      // return s;
      // }

      public StringBuffer printBody(int indent, StringBuffer output) {
            output.append(": ");
            if (pointcutDesignator != null) {
                  output.append(pointcutDesignator.toString());
            }
            return super.printBody(indent, output);
      }

      public StringBuffer printReturnType(int indent, StringBuffer output) {
            if (this.kind == AdviceKind.Around) {
                  return super.printReturnType(indent, output);
            }
            return output;
      }
}

Generated by  Doxygen 1.6.0   Back to index