use of soot.Body in project robovm by robovm.
the class MethodCompiler method getTrapsAt.
private List<Trap> getTrapsAt(Unit u) {
List<Trap> result = this.trapsAt.get(u);
if (result == null) {
Body body = sootMethod.getActiveBody();
Chain<Trap> traps = body.getTraps();
if (traps.isEmpty()) {
result = Collections.emptyList();
} else {
result = new ArrayList<Trap>();
PatchingChain<Unit> units = body.getUnits();
for (Trap trap : traps) {
Unit beginUnit = trap.getBeginUnit();
Unit endUnit = trap.getEndUnit();
if (beginUnit != endUnit && u != endUnit) {
if (u == beginUnit || (units.follows(u, beginUnit) && units.follows(endUnit, u))) {
result.add(trap);
}
}
}
}
this.trapsAt.put(u, result);
}
return result;
}
use of soot.Body in project robovm by robovm.
the class MethodCompiler method doCompile.
protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
function = createMethodFunction(method);
moduleBuilder.addFunction(function);
this.moduleBuilder = moduleBuilder;
env = function.getParameterRef(0);
trapsAt = new HashMap<Unit, List<Trap>>();
Body body = method.retrieveActiveBody();
NopStmt prependedNop = null;
if (method.isStatic() && !body.getUnits().getFirst().getBoxesPointingToThis().isEmpty()) {
// Fix for issue #1. This prevents an NPE in Soot's ArrayBoundsCheckerAnalysis. The NPE
// occurs for static methods which start with a unit that is the target of some other
// unit. We work around this by inserting a nop statement as the first unit in such
// methods. See http://www.sable.mcgill.ca/listarchives/soot-list/msg01397.html.
Unit insertionPoint = body.getUnits().getFirst();
prependedNop = Jimple.v().newNopStmt();
body.getUnits().getNonPatchingChain().insertBefore(prependedNop, insertionPoint);
}
PackManager.v().getPack("jtp").apply(body);
PackManager.v().getPack("jop").apply(body);
PackManager.v().getPack("jap").apply(body);
if (body.getUnits().getFirst() == prependedNop && prependedNop.getBoxesPointingToThis().isEmpty()) {
// Remove the nop we inserted above to work around the bug in Soot's
// ArrayBoundsCheckerAnalysis which has now been run.
body.getUnits().getNonPatchingChain().removeFirst();
}
PatchingChain<Unit> units = body.getUnits();
Map<Unit, List<Unit>> branchTargets = getBranchTargets(body);
Map<Unit, Integer> trapHandlers = getTrapHandlers(body);
Map<Unit, Integer> selChanges = new HashMap<Unit, Integer>();
int multiANewArrayMaxDims = 0;
Set<Local> locals = new HashSet<Local>();
boolean emitCheckStackOverflow = false;
for (Unit unit : units) {
if (unit instanceof DefinitionStmt) {
DefinitionStmt stmt = (DefinitionStmt) unit;
if (stmt.getLeftOp() instanceof Local) {
Local local = (Local) stmt.getLeftOp();
if (!locals.contains(local)) {
Type type = getLocalType(local.getType());
Alloca alloca = new Alloca(function.newVariable(local.getName(), type), type);
alloca.attach(local);
function.add(alloca);
locals.add(local);
}
}
if (stmt.getRightOp() instanceof NewMultiArrayExpr) {
NewMultiArrayExpr expr = (NewMultiArrayExpr) stmt.getRightOp();
multiANewArrayMaxDims = Math.max(multiANewArrayMaxDims, expr.getSizeCount());
}
if (stmt.getRightOp() instanceof InvokeExpr) {
emitCheckStackOverflow = true;
}
}
if (unit instanceof InvokeStmt) {
emitCheckStackOverflow = true;
}
}
dims = null;
if (multiANewArrayMaxDims > 0) {
dims = function.newVariable("dims", new PointerType(new ArrayType(multiANewArrayMaxDims, I32)));
function.add(new Alloca(dims, new ArrayType(multiANewArrayMaxDims, I32)));
}
if (emitCheckStackOverflow) {
call(CHECK_STACK_OVERFLOW);
}
Value trycatchContext = null;
if (!body.getTraps().isEmpty()) {
List<List<Trap>> recordedTraps = new ArrayList<List<Trap>>();
for (Unit unit : units) {
// Calculate the predecessor units of unit
Set<Unit> incoming = new HashSet<Unit>();
if (units.getFirst() != unit && units.getPredOf(unit).fallsThrough()) {
incoming.add(units.getPredOf(unit));
}
if (branchTargets.keySet().contains(unit)) {
incoming.addAll(branchTargets.get(unit));
}
if (unit == units.getFirst() || trapHandlers.containsKey(unit) || trapsDiffer(unit, incoming)) {
List<Trap> traps = getTrapsAt(unit);
if (traps.isEmpty()) {
selChanges.put(unit, 0);
} else {
int index = recordedTraps.indexOf(traps);
if (index == -1) {
index = recordedTraps.size();
recordedTraps.add(traps);
}
selChanges.put(unit, index + 1);
}
}
}
StructureConstantBuilder landingPadsPtrs = new StructureConstantBuilder();
for (List<Trap> traps : recordedTraps) {
StructureConstantBuilder landingPads = new StructureConstantBuilder();
for (Trap trap : traps) {
SootClass exClass = trap.getException();
StructureConstantBuilder landingPad = new StructureConstantBuilder();
if ("java.lang.Throwable".equals(exClass.getName()) || exClass.isPhantom()) {
landingPad.add(new NullConstant(I8_PTR));
} else {
catches.add(getInternalName(exClass));
if (exClass == sootClass) {
/*
* The class being compiled is an exception class
* with a catch clause which catches itself. We
* cannot reference the info struct directly since
* we don't know the type of it and it hasn't been
* emitted by ClassCompiler yet. Use the internal
* i8* alias instead which ClassCompiler will emit.
* See #1007.
*/
landingPad.add(new AliasRef(Symbols.infoStructSymbol(getInternalName(exClass)) + "_i8ptr", I8_PTR));
} else {
Global g = new Global(Symbols.infoStructSymbol(getInternalName(exClass)), I8_PTR, true);
if (!moduleBuilder.hasSymbol(g.getName())) {
moduleBuilder.addGlobal(g);
}
landingPad.add(g.ref());
}
}
landingPad.add(new IntegerConstant(trapHandlers.get(trap.getHandlerUnit()) + 1));
landingPads.add(landingPad.build());
}
landingPads.add(new StructureConstantBuilder().add(new NullConstant(I8_PTR)).add(new IntegerConstant(0)).build());
Global g = moduleBuilder.newGlobal(landingPads.build(), true);
landingPadsPtrs.add(new ConstantBitcast(g.ref(), I8_PTR));
}
Global g = moduleBuilder.newGlobal(landingPadsPtrs.build(), true);
Variable ctx = function.newVariable(TRYCATCH_CONTEXT_PTR);
Variable bcCtx = function.newVariable(BC_TRYCATCH_CONTEXT_PTR);
function.add(new Alloca(bcCtx, BC_TRYCATCH_CONTEXT));
Variable selPtr = function.newVariable(new PointerType(I32));
function.add(new Getelementptr(selPtr, bcCtx.ref(), 0, 0, 1));
function.add(new Store(new IntegerConstant(0), selPtr.ref()));
Variable bcCtxLandingPadsPtr = function.newVariable(I8_PTR_PTR);
function.add(new Getelementptr(bcCtxLandingPadsPtr, bcCtx.ref(), 0, 1));
function.add(new Store(new ConstantBitcast(g.ref(), I8_PTR), bcCtxLandingPadsPtr.ref()));
function.add(new Bitcast(ctx, bcCtx.ref(), TRYCATCH_CONTEXT_PTR));
trycatchContext = ctx.ref();
Value result = call(RVM_TRYCATCH_ENTER, env, trycatchContext);
Map<IntegerConstant, BasicBlockRef> alt = new TreeMap<IntegerConstant, BasicBlockRef>();
for (Entry<Unit, Integer> entry : trapHandlers.entrySet()) {
alt.put(new IntegerConstant(entry.getValue() + 1), function.newBasicBlockRef(new Label(entry.getKey())));
}
function.add(new Switch(result, function.newBasicBlockRef(new Label(units.getFirst())), alt));
if (!branchTargets.containsKey(units.getFirst())) {
function.newBasicBlock(new Label(units.getFirst()));
}
}
if ("<clinit>".equals(method.getName())) {
initializeClassFields();
}
for (Unit unit : units) {
if (branchTargets.containsKey(unit) || trapHandlers.containsKey(unit)) {
BasicBlock oldBlock = function.getCurrentBasicBlock();
function.newBasicBlock(new Label(unit));
if (oldBlock != null) {
Instruction last = oldBlock.last();
if (last == null || !isTerminator(last)) {
oldBlock.add(new Br(function.newBasicBlockRef(new Label(unit))));
}
}
}
if (selChanges.containsKey(unit)) {
int sel = selChanges.get(unit);
// trycatchContext->sel = sel
Variable selPtr = function.newVariable(new PointerType(I32));
function.add(new Getelementptr(selPtr, trycatchContext, 0, 1)).attach(unit);
function.add(new Store(new IntegerConstant(sel), selPtr.ref())).attach(unit);
}
if (unit instanceof DefinitionStmt) {
assign((DefinitionStmt) unit);
} else if (unit instanceof ReturnStmt) {
if (!body.getTraps().isEmpty()) {
trycatchLeave(function);
}
return_((ReturnStmt) unit);
} else if (unit instanceof ReturnVoidStmt) {
if (!body.getTraps().isEmpty()) {
trycatchLeave(function);
}
returnVoid((ReturnVoidStmt) unit);
} else if (unit instanceof IfStmt) {
if_((IfStmt) unit);
} else if (unit instanceof LookupSwitchStmt) {
lookupSwitch((LookupSwitchStmt) unit);
} else if (unit instanceof TableSwitchStmt) {
tableSwitch((TableSwitchStmt) unit);
} else if (unit instanceof GotoStmt) {
goto_((GotoStmt) unit);
} else if (unit instanceof ThrowStmt) {
throw_((ThrowStmt) unit);
} else if (unit instanceof InvokeStmt) {
invoke((InvokeStmt) unit);
} else if (unit instanceof EnterMonitorStmt) {
enterMonitor((EnterMonitorStmt) unit);
} else if (unit instanceof ExitMonitorStmt) {
exitMonitor((ExitMonitorStmt) unit);
} else if (unit instanceof NopStmt) {
nop((NopStmt) unit);
} else {
throw new IllegalArgumentException("Unknown Unit type: " + unit.getClass());
}
}
if (this.className.equals("java/lang/Object") && "<init>".equals(method.getName())) {
// If it is the object will be registered for finalization.
for (BasicBlock bb : function.getBasicBlocks()) {
if (bb.last() instanceof Ret) {
// Insert a call to register_finalizable() before this ret
Call call = new Call(REGISTER_FINALIZABLE, env, function.getParameterRef(1));
call.attach(bb.last().getAttachment(Unit.class));
bb.insertBefore(bb.last(), call);
}
}
}
return function;
}
use of soot.Body in project robovm by robovm.
the class ObjCMemberPlugin method createBridge.
private void createBridge(SootClass sootClass, SootMethod method, String selectorName, boolean strongRefSetter, boolean extensions) {
Jimple j = Jimple.v();
boolean usingGenericInstanceMethod = false;
SootMethod msgSendMethod = getMsgSendMethod(selectorName, method, extensions);
/*
* Add the method even if we might remove it later to make marshaler
* lookup on the method work as expected.
*/
sootClass.addMethod(msgSendMethod);
addBridgeAnnotation(msgSendMethod);
SootMethodRef msgSendMethodRef = getGenericMsgSendReplacementMethod(msgSendMethod);
if (!msgSendMethodRef.declaringClass().getType().equals(msgSendMethod.getDeclaringClass().getType())) {
/*
* There's a generic objc_msgSend method we can use. Remove
* msgSendMethod from the class.
*/
sootClass.removeMethod(msgSendMethod);
/*
* Can we use a generic <name>_instance method from $M? If we can we
* won't have to make a call to objc_msgSendSuper.
*/
if (!method.isStatic()) {
// Yes!
msgSendMethodRef = Scene.v().makeMethodRef(msgSendMethodRef.declaringClass(), msgSendMethodRef.name() + "_instance", msgSendMethodRef.parameterTypes(), msgSendMethodRef.returnType(), true);
usingGenericInstanceMethod = true;
}
}
SootMethodRef msgSendSuperMethodRef = null;
if (!usingGenericInstanceMethod && !extensions && !method.isStatic()) {
SootMethod msgSendSuperMethod = getMsgSendSuperMethod(selectorName, method);
/*
* Add the method even if we might remove it later to make marshaler
* lookup on the method work as expected.
*/
sootClass.addMethod(msgSendSuperMethod);
addBridgeAnnotation(msgSendSuperMethod);
msgSendSuperMethodRef = getGenericMsgSendSuperReplacementMethod(msgSendSuperMethod);
if (!msgSendSuperMethodRef.declaringClass().getType().equals(msgSendSuperMethod.getDeclaringClass().getType())) {
/*
* There's a generic objc_msgSendSuper method we can use. Remove
* msgSendSuperMethod from the class.
*/
sootClass.removeMethod(msgSendSuperMethod);
}
}
method.setModifiers(method.getModifiers() & ~NATIVE);
Body body = j.newBody(method);
method.setActiveBody(body);
PatchingChain<Unit> units = body.getUnits();
Local thiz = null;
if (extensions) {
thiz = j.newLocal("$this", method.getParameterType(0));
body.getLocals().add(thiz);
units.add(j.newIdentityStmt(thiz, j.newParameterRef(method.getParameterType(0), 0)));
} else if (!method.isStatic()) {
thiz = j.newLocal("$this", sootClass.getType());
body.getLocals().add(thiz);
units.add(j.newIdentityStmt(thiz, j.newThisRef(sootClass.getType())));
}
LinkedList<Value> args = new LinkedList<>();
for (int i = extensions ? 1 : 0; i < method.getParameterCount(); i++) {
Type t = method.getParameterType(i);
Local p = j.newLocal("$p" + i, t);
body.getLocals().add(p);
units.add(j.newIdentityStmt(p, j.newParameterRef(t, i)));
args.add(p);
}
Local objCClass = null;
if (!extensions && method.isStatic()) {
objCClass = j.newLocal("$objCClass", org_robovm_objc_ObjCClass.getType());
body.getLocals().add(objCClass);
units.add(j.newAssignStmt(objCClass, j.newStaticFieldRef(Scene.v().makeFieldRef(sootClass, "$objCClass", org_robovm_objc_ObjCClass.getType(), true))));
}
if (strongRefSetter) {
Type propType = method.getParameterType(extensions ? 1 : 0);
if (propType instanceof RefLikeType) {
SootMethodRef getter = findStrongRefGetter(sootClass, method, extensions).makeRef();
Local before = j.newLocal("$before", propType);
body.getLocals().add(before);
units.add(j.newAssignStmt(before, extensions ? j.newStaticInvokeExpr(getter, thiz) : (objCClass != null ? j.newStaticInvokeExpr(getter) : j.newVirtualInvokeExpr(thiz, getter))));
Value after = args.get(0);
if (extensions) {
units.add(j.newInvokeStmt(j.newStaticInvokeExpr(org_robovm_objc_ObjCExtensions_updateStrongRef, Arrays.asList(thiz, before, after))));
} else {
units.add(j.newInvokeStmt(j.newVirtualInvokeExpr(objCClass != null ? objCClass : thiz, org_robovm_objc_ObjCObject_updateStrongRef, before, after)));
}
}
}
Local sel = j.newLocal("$sel", org_robovm_objc_Selector.getType());
body.getLocals().add(sel);
// $sel = <selector>
units.add(j.newAssignStmt(sel, j.newStaticFieldRef(Scene.v().makeFieldRef(sootClass, getSelectorFieldName(selectorName), org_robovm_objc_Selector.getType(), true))));
args.addFirst(sel);
Local customClass = null;
if (!usingGenericInstanceMethod && !extensions && !Modifier.isFinal(sootClass.getModifiers()) && !method.isStatic()) {
customClass = j.newLocal("$customClass", BooleanType.v());
body.getLocals().add(customClass);
units.add(j.newAssignStmt(customClass, j.newInstanceFieldRef(thiz, org_robovm_objc_ObjCObject_customClass)));
}
Local ret = null;
if (method.getReturnType() != VoidType.v()) {
ret = j.newLocal("$ret", msgSendMethodRef.returnType());
body.getLocals().add(ret);
}
Local castRet = null;
if (!msgSendMethodRef.returnType().equals(method.getReturnType())) {
/*
* We're calling a generic method in $M which returns an NSObject.
* We need to cast that to the return type declared by the method
* being generated.
*/
castRet = j.newLocal("$castRet", method.getReturnType());
body.getLocals().add(castRet);
}
StaticInvokeExpr invokeMsgSendExpr = j.newStaticInvokeExpr(msgSendMethodRef, l(thiz != null ? thiz : objCClass, args));
Stmt invokeMsgSendStmt = ret == null ? j.newInvokeStmt(invokeMsgSendExpr) : j.newAssignStmt(ret, invokeMsgSendExpr);
if (customClass != null) {
// if $customClass == 0 goto <invokeMsgSendStmt>
units.add(j.newIfStmt(j.newEqExpr(customClass, IntConstant.v(0)), invokeMsgSendStmt));
// $super = this.getSuper()
Local zuper = j.newLocal("$super", org_robovm_objc_ObjCSuper.getType());
body.getLocals().add(zuper);
units.add(j.newAssignStmt(zuper, j.newVirtualInvokeExpr(body.getThisLocal(), org_robovm_objc_ObjCObject_getSuper)));
StaticInvokeExpr invokeMsgSendSuperExpr = j.newStaticInvokeExpr(msgSendSuperMethodRef, l(zuper, args));
units.add(ret == null ? j.newInvokeStmt(invokeMsgSendSuperExpr) : j.newAssignStmt(ret, invokeMsgSendSuperExpr));
if (ret != null) {
if (castRet != null) {
units.add(j.newAssignStmt(castRet, j.newCastExpr(ret, castRet.getType())));
units.add(j.newReturnStmt(castRet));
} else {
units.add(j.newReturnStmt(ret));
}
} else {
units.add(j.newReturnVoidStmt());
}
}
units.add(invokeMsgSendStmt);
if (ret != null) {
if (castRet != null) {
units.add(j.newAssignStmt(castRet, j.newCastExpr(ret, castRet.getType())));
units.add(j.newReturnStmt(castRet));
} else {
units.add(j.newReturnStmt(ret));
}
} else {
units.add(j.newReturnVoidStmt());
}
}
use of soot.Body in project robovm by robovm.
the class ObjCMemberPlugin method registerSelectors.
private void registerSelectors(SootClass sootClass, Set<String> selectors) {
Jimple j = Jimple.v();
SootMethod clinit = getOrCreateStaticInitializer(sootClass);
Body body = clinit.retrieveActiveBody();
Local sel = Jimple.v().newLocal("$sel", org_robovm_objc_Selector.getType());
body.getLocals().add(sel);
Chain<Unit> units = body.getUnits();
for (String selectorName : selectors) {
SootField f = getSelectorField(selectorName);
sootClass.addField(f);
units.insertBefore(Arrays.<Unit>asList(j.newAssignStmt(sel, j.newStaticInvokeExpr(org_robovm_objc_Selector_register, StringConstant.v(selectorName))), j.newAssignStmt(j.newStaticFieldRef(f.makeRef()), sel)), units.getLast());
}
}
Aggregations