/* ******************************************************************* * 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.weaver.bcel; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import org.aspectj.apache.bcel.Constants; import org.aspectj.apache.bcel.classfile.Attribute; import org.aspectj.apache.bcel.classfile.ConstantPool; import org.aspectj.apache.bcel.classfile.Method; import org.aspectj.apache.bcel.classfile.Synthetic; import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; import org.aspectj.apache.bcel.generic.BranchHandle; import org.aspectj.apache.bcel.generic.ClassGenException; import org.aspectj.apache.bcel.generic.CodeExceptionGen; import org.aspectj.apache.bcel.generic.Instruction; import org.aspectj.apache.bcel.generic.InstructionBranch; import org.aspectj.apache.bcel.generic.InstructionHandle; import org.aspectj.apache.bcel.generic.InstructionList; import org.aspectj.apache.bcel.generic.InstructionSelect; import org.aspectj.apache.bcel.generic.InstructionTargeter; import org.aspectj.apache.bcel.generic.LineNumberTag; import org.aspectj.apache.bcel.generic.LocalVariableTag; import org.aspectj.apache.bcel.generic.MethodGen; import org.aspectj.apache.bcel.generic.ObjectType; import org.aspectj.apache.bcel.generic.Tag; import org.aspectj.apache.bcel.generic.TargetLostException; import org.aspectj.apache.bcel.generic.Type; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.ISourceLocation; import org.aspectj.weaver.AjAttribute; import org.aspectj.weaver.AjAttribute.WeaverVersionInfo; import org.aspectj.weaver.AnnotationAJ; import org.aspectj.weaver.BCException; import org.aspectj.weaver.ISourceContext; import org.aspectj.weaver.MemberImpl; import org.aspectj.weaver.NameMangler; import org.aspectj.weaver.ResolvedMember; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.Shadow; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.WeaverMessages; import org.aspectj.weaver.World; import org.aspectj.weaver.tools.Traceable; /** * A LazyMethodGen should be treated as a MethodGen. It's our way of abstracting over the low-level Method objects. It converts * through {@link MethodGen} to create and to serialize, but that's it. * *

* At any rate, there are two ways to create LazyMethodGens. One is from a method, which does work through MethodGen to do the * correct thing. The other is the creation of a completely empty LazyMethodGen, and it is used when we're constructing code from * scratch. * *

* We stay away from targeters for rangey things like Shadows and Exceptions. */ public final class LazyMethodGen implements Traceable { private int modifiers; private Type returnType; private final String name; private Type[] argumentTypes; // private final String[] argumentNames; private String[] declaredExceptions; private InstructionList body; private List attributes; private List newAnnotations; private List annotationsForRemoval; private AnnotationAJ[][] newParameterAnnotations; private final LazyClassGen enclosingClass; private BcelMethod memberView; private AjAttribute.EffectiveSignatureAttribute effectiveSignature; int highestLineNumber = 0; boolean wasPackedOptimally = false; private Method savedMethod = null; // Some tools that may post process the output bytecode do not long local variable tables // to be generated as one reason the tables may be missing in the first place is because // the bytecode is odd. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=470658 private final boolean originalMethodHasLocalVariableTable; /* * We use LineNumberTags and not Gens. * * This option specifies whether we let the BCEL classes create LineNumberGens and LocalVariableGens or if we make it create * LineNumberTags and LocalVariableTags. Up until 1.5.1 we always created Gens - then on return from the MethodGen ctor we took * them apart, reprocessed them all and created Tags. (see unpackLocals/unpackLineNumbers). As we have our own copy of Bcel, why * not create the right thing straightaway? So setting this to true will call the MethodGen ctor() in such a way that it creates * Tags - removing the need for unpackLocals/unpackLineNumbers - HOWEVER see the ensureAllLineNumberSetup() method for some * other relevant info. * * Whats the difference between a Tag and a Gen? A Tag is more lightweight, it doesn't know which instructions it targets, it * relies on the instructions targettingit - this reduces the amount of targeter manipulation we have to do. */ /** * This is nonnull if this method is the result of an "inlining". We currently copy methods into other classes for around * advice. We add this field so we can get JSR45 information correct. If/when we do _actual_ inlining, we'll need to subtype * LineNumberTag to have external line numbers. */ String fromFilename = null; private int maxLocals; private boolean canInline = true; private boolean isSynthetic = false; List matchedShadows; // Used for interface introduction - this is the type of the interface the method is technically on public ResolvedType definingType = null; static class LightweightBcelMethod extends BcelMethod { LightweightBcelMethod(BcelObjectType declaringType, Method method) { super(declaringType, method); // TODO Auto-generated constructor stub } } public LazyMethodGen(int modifiers, Type returnType, String name, Type[] paramTypes, String[] declaredExceptions, LazyClassGen enclosingClass) { // enclosingClass.getName() + ", " + returnType); this.memberView = null; // should be okay, since constructed ones aren't woven into this.modifiers = modifiers; this.returnType = returnType; this.name = name; this.argumentTypes = paramTypes; // this.argumentNames = Utility.makeArgNames(paramTypes.length); this.declaredExceptions = declaredExceptions; if (!Modifier.isAbstract(modifiers)) { body = new InstructionList(); setMaxLocals(calculateMaxLocals()); } else { body = null; } this.attributes = new ArrayList<>(); this.enclosingClass = enclosingClass; assertGoodBody(); this.originalMethodHasLocalVariableTable = true; // it is a new method, we want an lvar table // @AJ advice are not inlined by default since requires further analysis and weaving ordering control // TODO AV - improve - note: no room for improvement as long as aspects are reweavable // since the inlined version with wrappers and an to be done annotation to keep // inline state will be garbaged due to reweavable impl if (memberView != null && isAdviceMethod()) { if (enclosingClass.getType().isAnnotationStyleAspect()) { // TODO we could check for @Around advice as well this.canInline = false; } } } private int calculateMaxLocals() { int ret = Modifier.isStatic(modifiers) ? 0 : 1; // will there be a 'this'? for (Type type : argumentTypes) { ret += type.getSize(); } return ret; } // build from an existing method, lazy build saves most work for // initialization public LazyMethodGen(Method m, LazyClassGen enclosingClass) { savedMethod = m; this.enclosingClass = enclosingClass; if (!(m.isAbstract() || m.isNative()) && m.getCode() == null) { throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); } if ((m.isAbstract() || m.isNative()) && m.getCode() != null) { throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); } this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), m); this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable()!=null; this.modifiers = m.getModifiers(); this.name = m.getName(); // @AJ advice are not inlined by default since requires further analysis // and weaving ordering control // TODO AV - improve - note: no room for improvement as long as aspects // are reweavable // since the inlined version with wrappers and an to be done annotation // to keep // inline state will be garbaged due to reweavable impl if (memberView != null && isAdviceMethod()) { if (enclosingClass.getType().isAnnotationStyleAspect()) { // TODO we could check for @Around advice as well this.canInline = false; } } } private boolean isAbstractOrNative(int modifiers) { return Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers); } public LazyMethodGen(BcelMethod m, LazyClassGen enclosingClass) { savedMethod = m.getMethod(); this.enclosingClass = enclosingClass; if (!isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() == null) { throw new RuntimeException("bad non-abstract method with no code: " + m + " on " + enclosingClass); } if (isAbstractOrNative(m.getModifiers()) && savedMethod.getCode() != null) { throw new RuntimeException("bad abstract method with code: " + m + " on " + enclosingClass); } // this.memberView = new BcelMethod(enclosingClass.getBcelObjectType(), // m); this.memberView = m; this.modifiers = savedMethod.getModifiers(); this.name = m.getName(); this.originalMethodHasLocalVariableTable = savedMethod.getLocalVariableTable() != null; // @AJ advice are not inlined by default since requires further analysis // and weaving ordering control // TODO AV - improve - note: no room for improvement as long as aspects // are reweavable // since the inlined version with wrappers and an to be done annotation // to keep // inline state will be garbaged due to reweavable impl if (memberView != null && isAdviceMethod()) { if (enclosingClass.getType().isAnnotationStyleAspect()) { // TODO we could check for @Around advice as well this.canInline = false; } } } public boolean hasDeclaredLineNumberInfo() { return (memberView != null && memberView.hasDeclarationLineNumberInfo()); } public int getDeclarationLineNumber() { if (hasDeclaredLineNumberInfo()) { return memberView.getDeclarationLineNumber(); } else { return -1; } } public int getDeclarationOffset() { if (hasDeclaredLineNumberInfo()) { return memberView.getDeclarationOffset(); } else { return 0; } } public void addAnnotation(AnnotationAJ ax) { initialize(); if (memberView == null) { // If member view is null, we manage them in newAnnotations if (newAnnotations == null) { newAnnotations = new ArrayList<>(); } newAnnotations.add(ax); } else { memberView.addAnnotation(ax); } } public void removeAnnotation(ResolvedType annotationType) { initialize(); if (memberView == null) { // If member view is null, we manage them in newAnnotations if (annotationsForRemoval == null) { annotationsForRemoval = new ArrayList<>(); } annotationsForRemoval.add(annotationType); } else { memberView.removeAnnotation(annotationType); } } public void addParameterAnnotation(int parameterNumber, AnnotationAJ anno) { initialize(); if (memberView == null) { if (newParameterAnnotations == null) { // time to create it int pcount = getArgumentTypes().length; newParameterAnnotations = new AnnotationAJ[pcount][]; for (int i = 0; i < pcount; i++) { if (i == parameterNumber) { newParameterAnnotations[i] = new AnnotationAJ[1]; newParameterAnnotations[i][0] = anno; } else { newParameterAnnotations[i] = AnnotationAJ.EMPTY_ARRAY; } } } else { AnnotationAJ[] currentAnnoArray = newParameterAnnotations[parameterNumber]; AnnotationAJ[] newAnnoArray = new AnnotationAJ[currentAnnoArray.length + 1]; System.arraycopy(currentAnnoArray, 0, newAnnoArray, 0, currentAnnoArray.length); newAnnoArray[currentAnnoArray.length] = anno; newParameterAnnotations[parameterNumber] = newAnnoArray; } } else { memberView.addParameterAnnotation(parameterNumber, anno); } } public ResolvedType[] getAnnotationTypes() { initialize(); if (memberView == null && newAnnotations!=null && newAnnotations.size()!=0) { // TODO Ignoring removed annotations for now ResolvedType[] annotationTypes = new ResolvedType[newAnnotations.size()]; for (int a=0,len=newAnnotations.size();a 0) hasExceptionHandlers = true; int priority = len - 1; for (int i = 0; i < len; i++, priority--) { CodeExceptionGen exn = exns[i]; InstructionHandle start = Range.genStart(body, getOutermostExceptionStart(exn.getStartPC())); InstructionHandle end = Range.genEnd(body, getOutermostExceptionEnd(exn.getEndPC())); // this doesn't necessarily handle overlapping correctly!!! ExceptionRange er = new ExceptionRange(body, exn.getCatchType() == null ? null : BcelWorld.fromBcel(exn .getCatchType()), priority); er.associateWithTargets(start, end, exn.getHandlerPC()); exn.setStartPC(null); // also removes from target exn.setEndPC(null); // also removes from target exn.setHandlerPC(null); // also removes from target } gen.removeExceptionHandlers(); } } private InstructionHandle getOutermostExceptionStart(InstructionHandle ih) { while (true) { if (ExceptionRange.isExceptionStart(ih.getPrev())) { ih = ih.getPrev(); } else { return ih; } } } private InstructionHandle getOutermostExceptionEnd(InstructionHandle ih) { while (true) { if (ExceptionRange.isExceptionEnd(ih.getNext())) { ih = ih.getNext(); } else { return ih; } } } /** * On entry to this method we have a method whose instruction stream contains a few instructions that have line numbers assigned * to them (LineNumberTags). The aim is to ensure every instruction has the right line number. This is necessary because some of * them may be extracted out into other methods - and it'd be useful for them to maintain the source line number for debugging. */ public void ensureAllLineNumberSetup() { LineNumberTag lastKnownLineNumberTag = null; boolean skip = false; for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { skip = false; for (InstructionTargeter targeter : ih.getTargeters()) { if (targeter instanceof LineNumberTag) { lastKnownLineNumberTag = (LineNumberTag) targeter; skip = true; } } if (lastKnownLineNumberTag != null && !skip) { ih.addTargeter(lastKnownLineNumberTag); } } } // =============== public int allocateLocal(Type type) { return allocateLocal(type.getSize()); } public int allocateLocal(int slots) { int max = getMaxLocals(); setMaxLocals(max + slots); return max; } public Method getMethod() { if (savedMethod != null) { return savedMethod; // ??? this relies on gentle treatment of // constant pool } try { MethodGen gen = pack(); savedMethod = gen.getMethod(); return savedMethod; } catch (ClassGenException e) { enclosingClass .getBcelObjectType() .getResolvedTypeX() .getWorld() .showMessage( IMessage.ERROR, WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), this.getName(), e.getMessage()), this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); // throw e; PR 70201.... let the normal problem reporting // infrastructure deal with this rather than crashing. body = null; MethodGen gen = pack(); return gen.getMethod(); } catch (RuntimeException re) { if (re.getCause() instanceof ClassGenException) { enclosingClass .getBcelObjectType() .getResolvedTypeX() .getWorld() .showMessage( IMessage.ERROR, WeaverMessages.format(WeaverMessages.PROBLEM_GENERATING_METHOD, this.getClassName(), this.getName(), re.getCause().getMessage()), this.getMemberView() == null ? null : this.getMemberView().getSourceLocation(), null); // throw e; PR 70201.... let the normal problem reporting // infrastructure deal with this rather than crashing. body = null; MethodGen gen = pack(); return gen.getMethod(); } throw re; } } public void markAsChanged() { if (wasPackedOptimally) { throw new RuntimeException("Already packed method is being re-modified: " + getClassName() + " " + toShortString()); } initialize(); savedMethod = null; } // ============================= @Override public String toString() { BcelObjectType bot = enclosingClass.getBcelObjectType(); WeaverVersionInfo weaverVersion = (bot == null ? WeaverVersionInfo.CURRENT : bot.getWeaverVersionAttribute()); return toLongString(weaverVersion); } public String toShortString() { String access = org.aspectj.apache.bcel.classfile.Utility.accessToString(getAccessFlags()); StringBuilder buf = new StringBuilder(); if (!access.equals("")) { buf.append(access); buf.append(" "); } buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(getReturnType().getSignature(), true)); buf.append(" "); buf.append(getName()); buf.append("("); { int len = argumentTypes.length; if (len > 0) { buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[0].getSignature(), true)); for (int i = 1; i < argumentTypes.length; i++) { buf.append(", "); buf.append(org.aspectj.apache.bcel.classfile.Utility.signatureToString(argumentTypes[i].getSignature(), true)); } } } buf.append(")"); { int len = declaredExceptions != null ? declaredExceptions.length : 0; if (len > 0) { buf.append(" throws "); buf.append(declaredExceptions[0]); for (int i = 1; i < declaredExceptions.length; i++) { buf.append(", "); buf.append(declaredExceptions[i]); } } } return buf.toString(); } public String toLongString(WeaverVersionInfo weaverVersion) { ByteArrayOutputStream s = new ByteArrayOutputStream(); print(new PrintStream(s), weaverVersion); return new String(s.toByteArray()); } public void print(WeaverVersionInfo weaverVersion) { print(System.out, weaverVersion); } public void print(PrintStream out, WeaverVersionInfo weaverVersion) { out.print(" " + toShortString()); printAspectAttributes(out, weaverVersion); InstructionList body = getBody(); if (body == null) { out.println(";"); return; } out.println(":"); new BodyPrinter(out).run(); out.println(" end " + toShortString()); } private void printAspectAttributes(PrintStream out, WeaverVersionInfo weaverVersion) { ISourceContext context = null; if (enclosingClass != null && enclosingClass.getType() != null) { context = enclosingClass.getType().getSourceContext(); } List as = Utility.readAjAttributes(getClassName(), attributes.toArray(Attribute.NoAttributes), context, null, weaverVersion, new BcelConstantPoolReader(this.enclosingClass.getConstantPool())); if (!as.isEmpty()) { out.println(" " + as.get(0)); // XXX assuming exactly one // attribute, munger... } } private class BodyPrinter { Map labelMap = new HashMap<>(); InstructionList body; PrintStream out; ConstantPool pool; BodyPrinter(PrintStream out) { this.pool = enclosingClass.getConstantPool(); this.body = getBodyForPrint(); this.out = out; } BodyPrinter(PrintStream out, InstructionList il) { this.pool = enclosingClass.getConstantPool(); this.body = il; this.out = out; } void run() { // killNops(); assignLabels(); print(); } // label assignment void assignLabels() { LinkedList exnTable = new LinkedList<>(); String pendingLabel = null; // boolean hasPendingTargeters = false; int lcounter = 0; for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { // targeters for (InstructionTargeter t : ih.getTargeters()) { // [ // i // ] // ; if (t instanceof ExceptionRange) { // assert isRangeHandle(h); ExceptionRange r = (ExceptionRange) t; if (r.getStart() == ih) { insertHandler(r, exnTable); } } else if (t instanceof InstructionBranch) { if (pendingLabel == null) { pendingLabel = "L" + lcounter++; } } else { // assert isRangeHandle(h) } } if (pendingLabel != null) { labelMap.put(ih, pendingLabel); if (!Range.isRangeHandle(ih)) { pendingLabel = null; } } } int ecounter = 0; for (ExceptionRange er: exnTable) { String exceptionLabel = "E" + ecounter++; labelMap.put(Range.getRealStart(er.getHandler()), exceptionLabel); labelMap.put(er.getHandler(), exceptionLabel); } } // printing void print() { int depth = 0; int currLine = -1; bodyPrint: for (InstructionHandle ih = body.getStart(); ih != null; ih = ih.getNext()) { if (Range.isRangeHandle(ih)) { Range r = Range.getRange(ih); // don't print empty ranges, that is, ranges who contain no // actual instructions for (InstructionHandle xx = r.getStart(); Range.isRangeHandle(xx); xx = xx.getNext()) { if (xx == r.getEnd()) { continue bodyPrint; } } // doesn't handle nested: if (r.getStart().getNext() == // r.getEnd()) continue; if (r.getStart() == ih) { printRangeString(r, depth++); } else { if (r.getEnd() != ih) { throw new RuntimeException("bad"); } printRangeString(r, --depth); } } else { printInstruction(ih, depth); int line = getLineNumber(ih, currLine); if (line != currLine) { currLine = line; out.println(" (line " + line + ")"); } else { out.println(); } } } } void printRangeString(Range r, int depth) { printDepth(depth); out.println(getRangeString(r, labelMap)); } String getRangeString(Range r, Map labelMap) { if (r instanceof ExceptionRange) { ExceptionRange er = (ExceptionRange) r; return er.toString() + " -> " + labelMap.get(er.getHandler()); // // + " PRI " + er.getPriority(); } else { return r.toString(); } } void printDepth(int depth) { pad(BODY_INDENT); while (depth > 0) { out.print("| "); depth--; } } void printLabel(String s, int depth) { int space = Math.max(CODE_INDENT - depth * 2, 0); if (s == null) { pad(space); } else { space = Math.max(space - (s.length() + 2), 0); pad(space); out.print(s); out.print(": "); } } void printInstruction(InstructionHandle h, int depth) { printDepth(depth); printLabel(labelMap.get(h), depth); Instruction inst = h.getInstruction(); if (inst.isConstantPoolInstruction()) { out.print(Constants.OPCODE_NAMES[inst.opcode].toUpperCase()); out.print(" "); out.print(pool.constantToString(pool.getConstant(inst.getIndex()))); } else if (inst instanceof InstructionSelect) { InstructionSelect sinst = (InstructionSelect) inst; out.println(Constants.OPCODE_NAMES[sinst.opcode].toUpperCase()); int[] matches = sinst.getMatchs(); InstructionHandle[] targets = sinst.getTargets(); InstructionHandle defaultTarget = sinst.getTarget(); for (int i = 0, len = matches.length; i < len; i++) { printDepth(depth); printLabel(null, depth); out.print(" "); out.print(matches[i]); out.print(": \t"); out.println(labelMap.get(targets[i])); } printDepth(depth); printLabel(null, depth); out.print(" "); out.print("default: \t"); out.print(labelMap.get(defaultTarget)); } else if (inst instanceof InstructionBranch) { InstructionBranch brinst = (InstructionBranch) inst; out.print(Constants.OPCODE_NAMES[brinst.getOpcode()].toUpperCase()); out.print(" "); out.print(labelMap.get(brinst.getTarget())); } else if (inst.isLocalVariableInstruction()) { // LocalVariableInstruction lvinst = (LocalVariableInstruction) // inst; out.print(inst.toString(false).toUpperCase()); int index = inst.getIndex(); LocalVariableTag tag = getLocalVariableTag(h, index); if (tag != null) { out.print(" // "); out.print(tag.getType()); out.print(" "); out.print(tag.getName()); } } else { out.print(inst.toString(false).toUpperCase()); } } static final int BODY_INDENT = 4; static final int CODE_INDENT = 16; void pad(int size) { for (int i = 0; i < size; i++) { out.print(" "); } } } static LocalVariableTag getLocalVariableTag(InstructionHandle ih, int index) { for (InstructionTargeter t : ih.getTargeters()) { if (t instanceof LocalVariableTag) { LocalVariableTag lvt = (LocalVariableTag) t; if (lvt.getSlot() == index) { return lvt; } } } return null; } static int getLineNumber(InstructionHandle ih, int prevLine) { for (InstructionTargeter t : ih.getTargeters()) { if (t instanceof LineNumberTag) { return ((LineNumberTag) t).getLineNumber(); } } return prevLine; } public boolean isStatic() { return Modifier.isStatic(getAccessFlags()); } public boolean isAbstract() { return Modifier.isAbstract(getAccessFlags()); } public boolean isBridgeMethod() { return (getAccessFlags() & Constants.ACC_BRIDGE) != 0; } public void addExceptionHandler(InstructionHandle start, InstructionHandle end, InstructionHandle handlerStart, ObjectType catchType, boolean highPriority) { InstructionHandle start1 = Range.genStart(body, start); InstructionHandle end1 = Range.genEnd(body, end); ExceptionRange er = new ExceptionRange(body, (catchType == null ? null : BcelWorld.fromBcel(catchType)), highPriority); er.associateWithTargets(start1, end1, handlerStart); } public int getAccessFlags() { return modifiers; } public int getAccessFlagsWithoutSynchronized() { if (isSynchronized()) { return modifiers - Modifier.SYNCHRONIZED; } return modifiers; } public boolean isSynchronized() { return (modifiers & Modifier.SYNCHRONIZED) != 0; } public void setAccessFlags(int newFlags) { this.modifiers = newFlags; } public Type[] getArgumentTypes() { initialize(); return argumentTypes; } public LazyClassGen getEnclosingClass() { return enclosingClass; } public int getMaxLocals() { return maxLocals; } public String getName() { return name; } public String getGenericReturnTypeSignature() { if (memberView == null) { return getReturnType().getSignature(); } else { return memberView.getGenericReturnType().getSignature(); } } public Type getReturnType() { initialize(); return returnType; } public void setMaxLocals(int maxLocals) { this.maxLocals = maxLocals; } public InstructionList getBody() { markAsChanged(); return body; } public InstructionList getBodyForPrint() { return body; } public boolean hasBody() { if (savedMethod != null) { return savedMethod.getCode() != null; } return body != null; } public List getAttributes() { return attributes; } public String[] getDeclaredExceptions() { return declaredExceptions; } public String getClassName() { return enclosingClass.getName(); } // ---- packing! public MethodGen pack() { forceSyntheticForAjcMagicMembers(); // killNops(); int flags = getAccessFlags(); if (enclosingClass.getWorld().isJoinpointSynchronizationEnabled() && enclosingClass.getWorld().areSynchronizationPointcutsInUse()) { flags = getAccessFlagsWithoutSynchronized(); } MethodGen gen = new MethodGen(flags, getReturnType(), getArgumentTypes(), null, // getArgumentNames(), getName(), getEnclosingClass().getName(), new InstructionList(), getEnclosingClass().getConstantPool()); for (String declaredException : declaredExceptions) { gen.addException(declaredException); } for (Attribute attr : attributes) { gen.addAttribute(attr); } if (newAnnotations != null) { for (AnnotationAJ element : newAnnotations) { gen.addAnnotation(new AnnotationGen(((BcelAnnotation) element).getBcelAnnotation(), gen.getConstantPool(), true)); } } if (newParameterAnnotations != null) { for (int i = 0; i < newParameterAnnotations.length; i++) { AnnotationAJ[] annos = newParameterAnnotations[i]; for (AnnotationAJ anno : annos) { gen.addParameterAnnotation(i, new AnnotationGen(((BcelAnnotation) anno).getBcelAnnotation(), gen.getConstantPool(), true)); } } } if (memberView != null && memberView.getAnnotations() != null && memberView.getAnnotations().length != 0) { AnnotationAJ[] ans = memberView.getAnnotations(); for (AnnotationAJ an : ans) { AnnotationGen a = ((BcelAnnotation) an).getBcelAnnotation(); gen.addAnnotation(new AnnotationGen(a, gen.getConstantPool(), true)); } } if (isSynthetic) { if (enclosingClass.getWorld().isInJava5Mode()) { gen.setModifiers(gen.getModifiers() | Constants.ACC_SYNTHETIC); } if (!hasAttribute("Synthetic")) { // belt and braces, do the attribute even on Java 5 in addition to the modifier flag ConstantPool cpg = gen.getConstantPool(); int index = cpg.addUtf8("Synthetic"); gen.addAttribute(new Synthetic(index, 0, new byte[0], cpg)); } } if (hasBody()) { if (this.enclosingClass.getWorld().shouldFastPackMethods()) { if (isAdviceMethod() || getName().equals("")) { packBody(gen); } else { optimizedPackBody(gen); } } else { packBody(gen); } gen.setMaxLocals(true); gen.setMaxStack(); } else { gen.setInstructionList(null); } return gen; } private boolean hasAttribute(String attributeName) { for (Attribute attr: attributes) { if (attr.getName().equals(attributeName)) { return true; } } return false; } private void forceSyntheticForAjcMagicMembers() { if (NameMangler.isSyntheticMethod(getName(), inAspect())) { makeSynthetic(); } } private boolean inAspect() { BcelObjectType objectType = enclosingClass.getBcelObjectType(); return (objectType == null ? false : objectType.isAspect()); } public void makeSynthetic() { isSynthetic = true; } private static class LVPosition { InstructionHandle start = null; InstructionHandle end = null; } /** * fill the newly created method gen with our body, inspired by InstructionList.copy() */ public void packBody(MethodGen gen) { InstructionList fresh = gen.getInstructionList(); Map map = copyAllInstructionsExceptRangeInstructionsInto(fresh); // at this point, no rangeHandles are in fresh. Let's use that... /* * Update branch targets and insert various attributes. Insert our exceptionHandlers into a sorted list, so they can be * added in order later. */ InstructionHandle oldInstructionHandle = getBody().getStart(); InstructionHandle newInstructionHandle = fresh.getStart(); LinkedList exceptionList = new LinkedList<>(); Map localVariables = new HashMap<>(); int currLine = -1; int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); while (oldInstructionHandle != null) { if (map.get(oldInstructionHandle) == null) { // must be a range instruction since they're the only things we // didn't copy across handleRangeInstruction(oldInstructionHandle, exceptionList); // just increment ih. oldInstructionHandle = oldInstructionHandle.getNext(); } else { // assert map.get(ih) == jh Instruction oldInstruction = oldInstructionHandle.getInstruction(); Instruction newInstruction = newInstructionHandle.getInstruction(); if (oldInstruction instanceof InstructionBranch) { handleBranchInstruction(map, oldInstruction, newInstruction); } // now deal with line numbers // and store up info for local variables for (InstructionTargeter targeter : oldInstructionHandle.getTargeters()) { if (targeter instanceof LineNumberTag) { int line = ((LineNumberTag) targeter).getLineNumber(); if (line != currLine) { gen.addLineNumber(newInstructionHandle, line + lineNumberOffset); currLine = line; } } else if (targeter instanceof LocalVariableTag) { LocalVariableTag lvt = (LocalVariableTag) targeter; LVPosition p = localVariables.get(lvt); // If we don't know about it, create a new position and // store // If we do know about it - update its end position if (p == null) { LVPosition newp = new LVPosition(); newp.start = newp.end = newInstructionHandle; localVariables.put(lvt, newp); } else { p.end = newInstructionHandle; } } } // now continue oldInstructionHandle = oldInstructionHandle.getNext(); newInstructionHandle = newInstructionHandle.getNext(); } } addExceptionHandlers(gen, map, exceptionList); if (originalMethodHasLocalVariableTable || enclosingClass .getBcelObjectType() .getResolvedTypeX() .getWorld().generateNewLvts) { if (localVariables.size() == 0) { // Might be a case of 173978 where around advice on an execution join point // has caused everything to be extracted from the method and thus we // are left with no local variables, not even the ones for 'this' and // parameters passed to the method createNewLocalVariables(gen); } else { addLocalVariables(gen, localVariables); } } // JAVAC adds line number tables (with just one entry) to generated // accessor methods - this // keeps some tools that rely on finding at least some form of // linenumbertable happy. // Let's check if we have one - if we don't then let's add one. // TODO Could be made conditional on whether line debug info is being // produced if (gen.getLineNumbers().length == 0) { gen.addLineNumber(gen.getInstructionList().getStart(), 1); } } private void createNewLocalVariables(MethodGen gen) { gen.removeLocalVariables(); // ignore or for now if (!getName().startsWith("<")) { int slot = 0; InstructionHandle start = gen.getInstructionList().getStart(); InstructionHandle end = gen.getInstructionList().getEnd(); // Add a 'this' if non-static if (!isStatic()) { String cname = this.enclosingClass.getClassName(); if (cname == null) { return; // give up for now } Type enclosingType = BcelWorld.makeBcelType(UnresolvedType.forName(cname)); gen.addLocalVariable("this", enclosingType, slot++, start, end); } // Add entries for the method arguments String[] paramNames = (memberView == null ? null : memberView.getParameterNames()); if (paramNames != null) { for (int i = 0; i < argumentTypes.length; i++) { String pname = paramNames[i]; if (pname == null) { pname = "arg" + i; } gen.addLocalVariable(pname, argumentTypes[i], slot, start, end); slot += argumentTypes[i].getSize(); } } } } private World getWorld() { return enclosingClass.getBcelObjectType().getResolvedTypeX().getWorld(); } /* * Optimized packing that does a 'local packing' of the code rather than building a brand new method and packing into it. Only * usable when the packing is going to be done just once. */ public void optimizedPackBody(MethodGen gen) { InstructionList theBody = getBody(); InstructionHandle iHandle = theBody.getStart(); int currLine = -1; int lineNumberOffset = (fromFilename == null) ? 0 : getEnclosingClass().getSourceDebugExtensionOffset(fromFilename); Map localVariables = new HashMap<>(); LinkedList exceptionList = new LinkedList<>(); Set forDeletion = new HashSet<>(); Set branchInstructions = new HashSet<>(); // OPTIMIZE sort out in here: getRange()/insertHandler() and type of // exceptionList while (iHandle != null) { Instruction inst = iHandle.getInstruction(); // InstructionHandle nextInst = iHandle.getNext(); // OPTIMIZE remove this instructionhandle as it now points to // nowhere? if (inst == Range.RANGEINSTRUCTION) { Range r = Range.getRange(iHandle); if (r instanceof ExceptionRange) { ExceptionRange er = (ExceptionRange) r; if (er.getStart() == iHandle) { if (!er.isEmpty()) { // order is important, insert handlers in order of start insertHandler(er, exceptionList); } } } forDeletion.add(iHandle); } else { if (inst instanceof InstructionBranch) { branchInstructions.add((BranchHandle) iHandle); } for (InstructionTargeter targeter : iHandle.getTargetersCopy()) { if (targeter instanceof LineNumberTag) { int line = ((LineNumberTag) targeter).getLineNumber(); if (line != currLine) { gen.addLineNumber(iHandle, line + lineNumberOffset); currLine = line; } } else if (targeter instanceof LocalVariableTag) { LocalVariableTag lvt = (LocalVariableTag) targeter; LVPosition p = localVariables.get(lvt); // If we don't know about it, create a new position // and store // If we do know about it - update its end position if (p == null) { LVPosition newp = new LVPosition(); newp.start = newp.end = iHandle; localVariables.put(lvt, newp); } else { p.end = iHandle; } } } } iHandle = iHandle.getNext(); } for (BranchHandle branchHandle : branchInstructions) { handleBranchInstruction(branchHandle, forDeletion); } // now add exception handlers for (ExceptionRange r : exceptionList) { if (r.isEmpty()) { continue; } gen.addExceptionHandler(jumpForward(r.getRealStart(), forDeletion), jumpForward(r.getRealEnd(), forDeletion), jumpForward(r.getHandler(), forDeletion), (r.getCatchType() == null) ? null : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); } for (InstructionHandle handle : forDeletion) { try { theBody.delete(handle); } catch (TargetLostException e) { e.printStackTrace(); } } gen.setInstructionList(theBody); if (originalMethodHasLocalVariableTable || getWorld().generateNewLvts) { if (localVariables.size() == 0) { // Might be a case of 173978 where around advice on an execution join point // has caused everything to be extracted from the method and thus we // are left with no local variables, not even the ones for 'this' and // parameters passed to the method createNewLocalVariables(gen); } else { addLocalVariables(gen, localVariables); } } // JAVAC adds line number tables (with just one entry) to generated // accessor methods - this // keeps some tools that rely on finding at least some form of // linenumbertable happy. // Let's check if we have one - if we don't then let's add one. // TODO Could be made conditional on whether line debug info is being // produced if (gen.getLineNumbers().length == 0) { gen.addLineNumber(gen.getInstructionList().getStart(), 1); } wasPackedOptimally = true; } private void addLocalVariables(MethodGen gen, Map localVariables) { // now add local variables gen.removeLocalVariables(); // this next iteration _might_ be overkill, but we had problems with // bcel before with duplicate local variables. Now that we're patching // bcel we should be able to do without it if we're paranoid enough // through the rest of the compiler. InstructionHandle methodStart = gen.getInstructionList().getStart(); InstructionHandle methodEnd = gen.getInstructionList().getEnd(); // Determine how many 'slots' are used by parameters to the method. // Then below we can determine if a local variable is a parameter variable, if it is // we force its range to from the method start (as it may have been shuffled down // due to insertion of advice like cflow entry) int paramSlots = gen.isStatic() ? 0 : 1; Type[] argTypes = gen.getArgumentTypes(); if (argTypes != null) { for (Type argType : argTypes) { if (argType.getSize() == 2) { paramSlots += 2; } else { paramSlots += 1; } } } if (!this.enclosingClass.getWorld().generateNewLvts) { // Here the generateNewLvts option is used to control "Do not damage unusually positioned local // variables that represent method parameters". Strictly speaking local variables that represent // method parameters effectively have a bytecode range from 0..end_of_method - however some // tools generate bytecode that specifies a compressed range. The code below would normally // extend the parameter local variables to cover the full method but by setting paramSlots to -1 // here we cause the code below to avoid modifying any local vars that represent method // parameters. paramSlots = -1; } Map> duplicatedLocalMap = new HashMap<>(); for (LocalVariableTag tag : localVariables.keySet()) { // have we already added one with the same slot number and start // location? // if so, just continue. LVPosition lvpos = localVariables.get(tag); InstructionHandle start = (tag.getSlot() < paramSlots ? methodStart : lvpos.start); InstructionHandle end = (tag.getSlot() < paramSlots ? methodEnd : lvpos.end); Set slots = duplicatedLocalMap.get(start); if (slots == null) { slots = new HashSet<>(); duplicatedLocalMap.put(start, slots); } else if (slots.contains(tag.getSlot())) { // we already have a var starting at this tag with this slot continue; } slots.add(tag.getSlot()); Type t = tag.getRealType(); if (t == null) { t = BcelWorld.makeBcelType(UnresolvedType.forSignature(tag.getType())); } gen.addLocalVariable(tag.getName(), t, tag.getSlot(), start, end); } } private void addExceptionHandlers(MethodGen gen, Map map, Iterable exnList) { // now add exception handlers for (ExceptionRange r : exnList) { if (r.isEmpty()) { continue; } InstructionHandle rMappedStart = remap(r.getRealStart(), map); InstructionHandle rMappedEnd = remap(r.getRealEnd(), map); InstructionHandle rMappedHandler = remap(r.getHandler(), map); gen.addExceptionHandler(rMappedStart, rMappedEnd, rMappedHandler, (r.getCatchType() == null) ? null : (ObjectType) BcelWorld.makeBcelType(r.getCatchType())); } } private void handleBranchInstruction(Map map, Instruction oldInstruction, Instruction newInstruction) { InstructionBranch oldBranchInstruction = (InstructionBranch) oldInstruction; InstructionBranch newBranchInstruction = (InstructionBranch) newInstruction; InstructionHandle oldTarget = oldBranchInstruction.getTarget(); // old // target // New target is in hash map newBranchInstruction.setTarget(remap(oldTarget, map)); if (oldBranchInstruction instanceof InstructionSelect) { // Either LOOKUPSWITCH or TABLESWITCH InstructionHandle[] oldTargets = ((InstructionSelect) oldBranchInstruction).getTargets(); InstructionHandle[] newTargets = ((InstructionSelect) newBranchInstruction).getTargets(); for (int k = oldTargets.length - 1; k >= 0; k--) { // Update all targets newTargets[k] = remap(oldTargets[k], map); newTargets[k].addTargeter(newBranchInstruction); } } } private InstructionHandle jumpForward(InstructionHandle t, Set handlesForDeletion) { InstructionHandle target = t; if (handlesForDeletion.contains(target)) { do { target = target.getNext(); } while (handlesForDeletion.contains(target)); } return target; } /** * Process a branch instruction with respect to instructions that are about to be deleted. If the target for the branch is a * candidate for deletion, move it to the next valid instruction after the deleted target. */ private void handleBranchInstruction(BranchHandle branchHandle, Set handlesForDeletion) { InstructionBranch branchInstruction = (InstructionBranch) branchHandle.getInstruction(); InstructionHandle target = branchInstruction.getTarget(); // old target if (handlesForDeletion.contains(target)) { do { target = target.getNext(); } while (handlesForDeletion.contains(target)); branchInstruction.setTarget(target); } if (branchInstruction instanceof InstructionSelect) { // Either LOOKUPSWITCH or TABLESWITCH InstructionSelect iSelect = (InstructionSelect) branchInstruction; InstructionHandle[] targets = iSelect.getTargets(); for (int k = targets.length - 1; k >= 0; k--) { InstructionHandle oneTarget = targets[k]; if (handlesForDeletion.contains(oneTarget)) { do { oneTarget = oneTarget.getNext(); } while (handlesForDeletion.contains(oneTarget)); iSelect.setTarget(k, oneTarget); oneTarget.addTargeter(branchInstruction); } } } } private void handleRangeInstruction(InstructionHandle ih, LinkedList exnList) { // we're a range instruction Range r = Range.getRange(ih); if (r instanceof ExceptionRange) { ExceptionRange er = (ExceptionRange) r; if (er.getStart() == ih) { // System.err.println("er " + er); if (!er.isEmpty()) { // order is important, insert handlers in order of start insertHandler(er, exnList); } } } else { // we must be a shadow range or something equally useless, // so forget about doing anything } } /* * Make copies of all instructions, append them to the new list and associate old instruction references with the new ones, * i.e., a 1:1 mapping. */ private Map copyAllInstructionsExceptRangeInstructionsInto(InstructionList intoList) { Map map = new HashMap<>(); for (InstructionHandle ih = getBody().getStart(); ih != null; ih = ih.getNext()) { if (Range.isRangeHandle(ih)) { continue; } Instruction inst = ih.getInstruction(); Instruction copy = Utility.copyInstruction(inst); if (copy instanceof InstructionBranch) { map.put(ih, intoList.append((InstructionBranch) copy)); } else { map.put(ih, intoList.append(copy)); } } return map; } /** * This procedure should not currently be used. */ // public void killNops() { // InstructionHandle curr = body.getStart(); // while (true) { // if (curr == null) break; // InstructionHandle next = curr.getNext(); // if (curr.getInstruction() instanceof NOP) { // InstructionTargeter[] targeters = curr.getTargeters(); // if (targeters != null) { // for (int i = 0, len = targeters.length; i < len; i++) { // InstructionTargeter targeter = targeters[i]; // targeter.updateTarget(curr, next); // } // } // try { // body.delete(curr); // } catch (TargetLostException e) { // } // } // curr = next; // } // } // private static InstructionHandle fNext(InstructionHandle ih) { // while (true) { // if (ih.getInstruction()==Range.RANGEINSTRUCTION) ih = ih.getNext(); // else return ih; // } // } private static InstructionHandle remap(InstructionHandle handle, Map map) { while (true) { InstructionHandle ret = map.get(handle); if (ret == null) { handle = handle.getNext(); } else { return ret; } } } // Update to all these comments, ASC 11-01-2005 // The right thing to do may be to do more with priorities as // we create new exception handlers, but that is a relatively // complex task. In the meantime, just taking account of the // priority here enables a couple of bugs to be fixed to do // with using return or break in code that contains a finally // block (pr78021,pr79554). // exception ordering. // What we should be doing is dealing with priority inversions way earlier // than we are // and counting on the tree structure. In which case, the below code is in // fact right. // XXX THIS COMMENT BELOW IS CURRENTLY WRONG. // An exception A preceeds an exception B in the exception table iff: // * A and B were in the original method, and A preceeded B in the original // exception table // * If A has a higher priority than B, than it preceeds B. // * If A and B have the same priority, then the one whose START happens // EARLIEST has LEAST priority. // in short, the outermost exception has least priority. // we implement this with a LinkedList. We could possibly implement this // with a java.util.SortedSet, // but I don't trust the only implementation, TreeSet, to do the right // thing. /* private */static void insertHandler(ExceptionRange fresh, List l) { // Old implementation, simply: l.add(0,fresh); for (ListIterator iter = l.listIterator(); iter.hasNext();) { ExceptionRange r = iter.next(); // int freal = fresh.getRealStart().getPosition(); // int rreal = r.getRealStart().getPosition(); if (fresh.getPriority() >= r.getPriority()) { iter.previous(); iter.add(fresh); return; } } // we have reached the end l.add(fresh); } public boolean isPrivate() { return Modifier.isPrivate(getAccessFlags()); } public boolean isProtected() { return Modifier.isProtected(getAccessFlags()); } public boolean isDefault() { return !(isProtected() || isPrivate() || isPublic()); } public boolean isPublic() { return Modifier.isPublic(getAccessFlags()); } // ---- /** * A good body is a body with the following properties: * *

* * Where the shorthand "R is in body" means "R.start is in body, R.end is in body, and any InstructionHandle stored in a field * of R (such as an exception handle) is in body". */ public void assertGoodBody() { if (true) { return; // only enable for debugging } assertGoodBody(getBody(), toString()); } public static void assertGoodBody(InstructionList il, String from) { if (true) { return; // only to be enabled for debugging } // if (il == null) { // return; // } // Set body = new HashSet(); // Stack ranges = new Stack(); // for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { // body.add(ih); // if (ih.getInstruction() instanceof InstructionBranch) { // body.add(ih.getInstruction()); // } // } // // for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) { // assertGoodHandle(ih, body, ranges, from); // Iterator tIter = ih.getTargeters().iterator(); // while (tIter.hasNext()) { // assertGoodTargeter(tIter.next(), ih, body, from); // } // } } // private static void assertGoodHandle(InstructionHandle ih, Set body, Stack ranges, String from) { // Instruction inst = ih.getInstruction(); // if ((inst instanceof InstructionBranch) ^ (ih instanceof BranchHandle)) { // throw new BCException("bad instruction/handle pair in " + from); // } // if (Range.isRangeHandle(ih)) { // assertGoodRangeHandle(ih, body, ranges, from); // } else if (inst instanceof InstructionBranch) { // assertGoodBranchInstruction((BranchHandle) ih, (InstructionBranch) inst, body, ranges, from); // } // } // private static void assertGoodBranchInstruction(BranchHandle ih, InstructionBranch inst, Set body, Stack ranges, // String from) { // if (ih.getTarget() != inst.getTarget()) { // throw new BCException("bad branch instruction/handle pair in " + from); // } // InstructionHandle target = ih.getTarget(); // assertInBody(target, body, from); // assertTargetedBy(target, inst, from); // if (inst instanceof InstructionSelect) { // InstructionSelect sel = (InstructionSelect) inst; // InstructionHandle[] itargets = sel.getTargets(); // for (int k = itargets.length - 1; k >= 0; k--) { // assertInBody(itargets[k], body, from); // assertTargetedBy(itargets[k], inst, from); // } // } // } /** ih is an InstructionHandle or a BranchInstruction */ // private static void assertInBody(Object ih, Set body, String from) { // if (!body.contains(ih)) { // throw new BCException("thing not in body in " + from); // } // } // private static void assertGoodRangeHandle(InstructionHandle ih, Set body, Stack ranges, String from) { // Range r = getRangeAndAssertExactlyOne(ih, from); // assertGoodRange(r, body, from); // if (r.getStart() == ih) { // ranges.push(r); // } else if (r.getEnd() == ih) { // if (ranges.peek() != r) { // throw new BCException("bad range inclusion in " + from); // } // ranges.pop(); // } // } // private static void assertGoodRange(Range r, Set body, String from) { // assertInBody(r.getStart(), body, from); // assertRangeHandle(r.getStart(), from); // assertTargetedBy(r.getStart(), r, from); // // assertInBody(r.getEnd(), body, from); // assertRangeHandle(r.getEnd(), from); // assertTargetedBy(r.getEnd(), r, from); // // if (r instanceof ExceptionRange) { // ExceptionRange er = (ExceptionRange) r; // assertInBody(er.getHandler(), body, from); // assertTargetedBy(er.getHandler(), r, from); // } // } // private static void assertRangeHandle(InstructionHandle ih, String from) { // if (!Range.isRangeHandle(ih)) { // throw new BCException("bad range handle " + ih + " in " + from); // } // } private static void assertTargetedBy(InstructionHandle target, InstructionTargeter targeter, String from) { for (InstructionTargeter instructionTargeter : target.getTargeters()) { if (instructionTargeter == targeter) { return; } } throw new RuntimeException("bad targeting relationship in " + from); } private static void assertTargets(InstructionTargeter targeter, InstructionHandle target, String from) { if (targeter instanceof Range) { Range r = (Range) targeter; if (r.getStart() == target || r.getEnd() == target) { return; } if (r instanceof ExceptionRange) { if (((ExceptionRange) r).getHandler() == target) { return; } } } else if (targeter instanceof InstructionBranch) { InstructionBranch bi = (InstructionBranch) targeter; if (bi.getTarget() == target) { return; } if (targeter instanceof InstructionSelect) { InstructionSelect sel = (InstructionSelect) targeter; InstructionHandle[] itargets = sel.getTargets(); for (int k = itargets.length - 1; k >= 0; k--) { if (itargets[k] == target) { return; } } } } else if (targeter instanceof Tag) { return; } throw new BCException(targeter + " doesn't target " + target + " in " + from); } private static Range getRangeAndAssertExactlyOne(InstructionHandle ih, String from) { Range ret = null; Iterator tIter = ih.getTargeters().iterator(); if (!tIter.hasNext()) { throw new BCException("range handle with no range in " + from); } while (tIter.hasNext()) { InstructionTargeter ts = tIter.next(); if (ts instanceof Range) { if (ret != null) { throw new BCException("range handle with multiple ranges in " + from); } ret = (Range) ts; } } if (ret == null) { throw new BCException("range handle with no range in " + from); } return ret; } // private static void assertGoodTargeter(InstructionTargeter t, InstructionHandle ih, Set body, String from) { // assertTargets(t, ih, from); // if (t instanceof Range) { // assertGoodRange((Range) t, body, from); // } else if (t instanceof InstructionBranch) { // assertInBody(t, body, from); // } // } // ---- boolean isAdviceMethod() { if (memberView == null) { return false; } return memberView.getAssociatedShadowMunger() != null; } boolean isAjSynthetic() { if (memberView == null) { return true; } return memberView.isAjSynthetic(); } boolean isSynthetic() { if (memberView == null) { return false; } return memberView.isSynthetic(); } public ISourceLocation getSourceLocation() { if (memberView != null) { return memberView.getSourceLocation(); } return null; } public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { // if (memberView == null) return null; if (effectiveSignature != null) { return effectiveSignature; } return memberView.getEffectiveSignature(); } public void setEffectiveSignature(ResolvedMember member, Shadow.Kind kind, boolean shouldWeave) { this.effectiveSignature = new AjAttribute.EffectiveSignatureAttribute(member, kind, shouldWeave); } public String getSignature() { if (memberView != null) { return memberView.getSignature(); } return MemberImpl.typesToSignature(BcelWorld.fromBcel(getReturnType()), BcelWorld.fromBcel(getArgumentTypes()), false); } public String getParameterSignature() { if (memberView != null) { return memberView.getParameterSignature(); } return MemberImpl.typesToSignature(BcelWorld.fromBcel(getArgumentTypes())); } public BcelMethod getMemberView() { return memberView; } public void forcePublic() { markAsChanged(); modifiers = Utility.makePublic(modifiers); } public boolean getCanInline() { return canInline; } public void setCanInline(boolean canInline) { this.canInline = canInline; } public void addAttribute(Attribute attribute) { attributes.add(attribute); } public String toTraceString() { return toShortString(); } public ConstantPool getConstantPool() { return enclosingClass.getConstantPool(); } public static boolean isConstructor(LazyMethodGen aMethod) { return aMethod.getName().equals(""); } }