org.aspectj.weaver.bcel.ShadowRange Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjweaver Show documentation
Show all versions of aspectjweaver Show documentation
The AspectJ weaver introduces advices to java classes
/* *******************************************************************
* 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 org.aspectj.apache.bcel.generic.Instruction;
import org.aspectj.apache.bcel.generic.InstructionBranch;
import org.aspectj.apache.bcel.generic.InstructionFactory;
import org.aspectj.apache.bcel.generic.InstructionHandle;
import org.aspectj.apache.bcel.generic.InstructionLV;
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.LocalVariableTag;
import org.aspectj.apache.bcel.generic.RET;
import org.aspectj.apache.bcel.generic.TargetLostException;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.IntMap;
import org.aspectj.weaver.Shadow;
final class ShadowRange extends Range {
private BcelShadow shadow;
// ---- initialization
/**
* After this constructor is called, this range is not well situated unless both {@link #associateWithTargets} and
* {@link #associateWithShadow} are called.
*/
public ShadowRange(InstructionList body) {
super(body);
}
protected void associateWithTargets(InstructionHandle start, InstructionHandle end) {
// assert body.contains(start) && body.contains(end);
this.start = start;
this.end = end;
start.addTargeter(this);
end.addTargeter(this);
}
public void associateWithShadow(BcelShadow shadow) {
this.shadow = shadow;
shadow.setRange(this);
}
// ----
public Shadow.Kind getKind() {
return shadow.getKind();
}
@Override
public String toString() {
return shadow.toString();
}
void extractInstructionsInto(LazyMethodGen freshMethod, IntMap remap, boolean addReturn) {
LazyMethodGen.assertGoodBody(getBody(), toString());
freshMethod.assertGoodBody();
InstructionList freshBody = freshMethod.getBody();
for (InstructionHandle oldIh = start.getNext(); oldIh != end; oldIh = oldIh.getNext()) {
// first we copy the instruction itself.
Instruction oldI = oldIh.getInstruction();
Instruction freshI = (oldI == RANGEINSTRUCTION) ? oldI : Utility.copyInstruction(oldI);
// Now we add it to the new instruction list.
InstructionHandle freshIh;
if (freshI instanceof InstructionBranch) {
// If it's a targeting instruction,
// update the target(s) to point to the new copy instead of the old copy.
InstructionBranch oldBranch = (InstructionBranch) oldI;
InstructionBranch freshBranch = (InstructionBranch) freshI;
InstructionHandle oldTarget = oldBranch.getTarget();
oldTarget.removeTargeter(oldBranch);
oldTarget.addTargeter(freshBranch);
if (freshBranch instanceof InstructionSelect) {
InstructionSelect oldSelect = (InstructionSelect) oldI;
InstructionSelect freshSelect = (InstructionSelect) freshI;
InstructionHandle[] oldTargets = freshSelect.getTargets();
for (int k = oldTargets.length - 1; k >= 0; k--) {
oldTargets[k].removeTargeter(oldSelect);
oldTargets[k].addTargeter(freshSelect);
}
}
freshIh = freshBody.append(freshBranch);
} else {
freshIh = freshBody.append(freshI);
}
// if source comes before target:
// source <--> target
// --> [process: target.removeTargeter(source); target.addTargeter(sourcecopy)]
// source ---------\
// v
// sourcecopy <--> target
// --> [ process: sourcecopy.updateTarget(target, targetcopy) ]
// source ----> target
// sourcecopy <--> targetcopy
// if target comes before source
// target <--> source
// --> [process: source.updateTarget(target, targetcopy) ]
// target
// targetcopy <--> source
// --> [process: targetcopy.removeTargeter(source); targetcopy.addTargeter(sourcecopy)]
// target source
// v
// targetcopy <--> sourcecopy
// now deal with the old instruction's targeters. Update them all to point to us
// instead of the old instruction. We use updateTarget to do this. One goal is
// to make sure we remove all targeters from the old guy, so we can successfully
// delete it.
for (InstructionTargeter source : oldIh.getTargetersCopy()) {
if (source instanceof LocalVariableTag) {
Shadow.Kind kind = getKind();
if (kind == Shadow.AdviceExecution || kind == Shadow.ConstructorExecution || kind == Shadow.MethodExecution
|| kind == Shadow.PreInitialization || kind == Shadow.Initialization
|| kind == Shadow.StaticInitialization) {
LocalVariableTag sourceLocalVariableTag = (LocalVariableTag) source;
if (sourceLocalVariableTag.getSlot() == 0) {
// might be 'this' so should be renamed if being dumped in a static method 277616
if (sourceLocalVariableTag.getName().equals("this")) {
sourceLocalVariableTag.setName("ajc$this");
}
}
// if we're extracting a whole block we can do this...
source.updateTarget(oldIh, freshIh);
} else {
// XXX destroying local variable info
// but only for a call or get join point, so no big deal
source.updateTarget(oldIh, null);
}
} else if (source instanceof Range) {
// exceptions and shadows are just moved
((Range) source).updateTarget(oldIh, freshIh, freshBody);
} else {
// line numbers can be shared,
// branches will be copied along with us.
source.updateTarget(oldIh, freshIh);
}
}
// we're now done with the old instruction entirely, and will ignore them through
// the rest of this loop. The only time we'll see them again is a second pass to
// delete them.
// now deal with local variable instructions. If this points to a remapped
// frame location, update the instruction's index. If this doesn't,
// do compaction/expansion: allocate a new local variable, and modify the remap
// to handle it. XXX We're doing the safe thing and allocating ALL these local variables
// as double-wides, in case the location is found to hold a double-wide later.
if (freshI.isLocalVariableInstruction() || freshI instanceof RET) {
// IndexedInstruction indexedI = (IndexedInstruction) freshI;
int oldIndex = freshI.getIndex();
int freshIndex;
if (!remap.hasKey(oldIndex)) {
freshIndex = freshMethod.allocateLocal(2);
remap.put(oldIndex, freshIndex);
} else {
freshIndex = remap.get(oldIndex);
}
if (freshI instanceof RET) {
freshI.setIndex(freshIndex);
} else {
freshI = ((InstructionLV) freshI).setIndexAndCopyIfNecessary(freshIndex);
freshIh.setInstruction(freshI);
}
}
// System.err.println("JUST COPIED: " +
// oldIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool())
// + " INTO " +
// freshIh.getInstruction().toString(freshMethod.getEnclosingClass().getConstantPoolGen().getConstantPool()));
}
// now go through again and update variable slots that have been altered as a result
// of remapping...
for (InstructionHandle newIh = freshBody.getStart(); newIh != freshBody.getEnd(); newIh = newIh.getNext()) {
for (InstructionTargeter source : newIh.getTargeters()) {
if (source instanceof LocalVariableTag) {
LocalVariableTag lvt = (LocalVariableTag) source;
if (!lvt.isRemapped() && remap.hasKey(lvt.getSlot())) {
lvt.updateSlot(remap.get(lvt.getSlot()));
}
}
}
}
// we've now copied out all the instructions.
// now delete the instructions... we've already taken care of the damn
// targets, but since TargetLostException is checked, we have to do this stuff.
try {
for (InstructionHandle oldIh = start.getNext(); oldIh != end;) {
InstructionHandle next = oldIh.getNext();
body.delete(oldIh);
oldIh = next;
}
} catch (TargetLostException e) {
throw new BCException("shouldn't have gotten a target lost");
}
// now add the return, if one is warranted.
InstructionHandle ret = null;
if (addReturn) {
// we really should pull this out somewhere...
ret = freshBody.append(InstructionFactory.createReturn(freshMethod.getReturnType()));
}
// and remap all the old targeters of the end handle of the range to the return.
for (InstructionTargeter t : end.getTargetersCopy()) {
if (t == this) {
continue;
}
if (!addReturn) {
throw new BCException("range has target, but we aren't adding a return");
} else {
t.updateTarget(end, ret);
}
}
LazyMethodGen.assertGoodBody(getBody(), toString());
freshMethod.assertGoodBody();
}
public BcelShadow getShadow() {
return shadow;
}
}