Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.robovm.compiler.plugin.objc.ObjCMemberPlugin Maven / Gradle / Ivy
/*
* Copyright (C) 2014 Trillian Mobile AB
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.robovm.compiler.plugin.objc;
import static org.robovm.compiler.Annotations.*;
import static soot.Modifier.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.robovm.compiler.Annotations.Visibility;
import org.robovm.compiler.CompilerException;
import org.robovm.compiler.ModuleBuilder;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.plugin.AbstractCompilerPlugin;
import org.robovm.compiler.plugin.CompilerPlugin;
import soot.Body;
import soot.BooleanType;
import soot.Local;
import soot.Modifier;
import soot.PatchingChain;
import soot.RefLikeType;
import soot.RefType;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootFieldRef;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.SootResolver;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.VoidType;
import soot.jimple.ClassConstant;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.Jimple;
import soot.jimple.StaticInvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.StringConstant;
import soot.tagkit.AnnotationStringElem;
import soot.tagkit.AnnotationTag;
import soot.util.Chain;
/**
* {@link CompilerPlugin} which transforms Objective-C methods and properties
* to @Bridge methods. Also adds corresponding @Callback methods for each
* method and property.
*/
public class ObjCMemberPlugin extends AbstractCompilerPlugin {
public static final String OBJC_ANNOTATIONS_PACKAGE = "org/robovm/objc/annotation";
public static final String METHOD = "L" + OBJC_ANNOTATIONS_PACKAGE + "/Method;";
public static final String PROPERTY = "L" + OBJC_ANNOTATIONS_PACKAGE + "/Property;";
public static final String BIND_SELECTOR = "L" + OBJC_ANNOTATIONS_PACKAGE + "/BindSelector;";
public static final String NOT_IMPLEMENTED = "L" + OBJC_ANNOTATIONS_PACKAGE + "/NotImplemented;";
public static final String SELECTOR = "org.robovm.objc.Selector";
public static final String OBJC_SUPER = "org.robovm.objc.ObjCSuper";
public static final String OBJC_CLASS = "org.robovm.objc.ObjCClass";
public static final String OBJC_OBJECT = "org.robovm.objc.ObjCObject";
public static final String OBJC_RUNTIME = "org.robovm.objc.ObjCRuntime";
public static final String OBJC_EXTENSIONS = "org.robovm.objc.ObjCExtensions";
private boolean initialized = false;
private SootClass org_robovm_objc_ObjCClass = null;
private SootClass org_robovm_objc_ObjCSuper = null;
private SootClass org_robovm_objc_ObjCObject = null;
private SootClass org_robovm_objc_ObjCRuntime = null;
private SootClass org_robovm_objc_ObjCExtensions = null;
private SootClass org_robovm_objc_Selector = null;
private SootClass java_lang_String = null;
private SootClass java_lang_Class = null;
private SootMethodRef org_robovm_objc_Selector_register = null;
private SootMethodRef org_robovm_objc_ObjCObject_getSuper = null;
private SootFieldRef org_robovm_objc_ObjCObject_customClass = null;
private SootMethodRef org_robovm_objc_ObjCClass_getByType = null;
private SootMethodRef org_robovm_objc_ObjCRuntime_bind = null;
private SootMethodRef org_robovm_objc_ObjCObject_updateStrongRef = null;
private SootMethodRef org_robovm_objc_ObjCExtensions_updateStrongRef = null;
static SootMethod getOrCreateStaticInitializer(SootClass sootClass) {
for (SootMethod m : sootClass.getMethods()) {
if ("".equals(m.getName())) {
return m;
}
}
SootMethod m = new SootMethod("", Collections.emptyList(), VoidType.v(), STATIC);
Body body = Jimple.v().newBody(m);
body.getUnits().add(Jimple.v().newReturnVoidStmt());
m.setActiveBody(body);
sootClass.addMethod(m);
return m;
}
private String getSelectorFieldName(String selectorName) {
return "$sel$" + selectorName.replace(':', '$');
}
private SootField getSelectorField(String selectorName) {
return new SootField(getSelectorFieldName(selectorName),
org_robovm_objc_Selector.getType(), STATIC | PRIVATE | FINAL);
}
@SuppressWarnings("unchecked")
private SootMethod getMsgSendMethod(String selectorName, SootMethod method, boolean isCallback,
Type receiverType, boolean extensions) {
List paramTypes = new ArrayList<>();
if (extensions) {
paramTypes.add(method.getParameterType(0));
} else if (method.isStatic()) {
paramTypes.add(org_robovm_objc_ObjCClass.getType());
} else {
paramTypes.add(receiverType == null ? method.getDeclaringClass().getType() : receiverType);
}
paramTypes.add(org_robovm_objc_Selector.getType());
if (extensions) {
paramTypes.addAll(method.getParameterTypes().subList(1, method.getParameterTypes().size()));
} else {
paramTypes.addAll(method.getParameterTypes());
}
SootMethod m = new SootMethod((isCallback ? "$cb$": "$m$") + selectorName.replace(':', '$'),
paramTypes, method.getReturnType(), STATIC | PRIVATE | (isCallback ? 0 : NATIVE));
copyAnnotations(method, m, Visibility.RuntimeVisible);
if (extensions) {
copyParameterAnnotations(method, m, 0, 1, 0, Visibility.RuntimeVisible);
if (method.getParameterCount() > 1) {
copyParameterAnnotations(method, m, 1, method.getParameterCount(), 1, Visibility.RuntimeVisible);
}
} else {
copyParameterAnnotations(method, m, 0, method.getParameterCount(), 2, Visibility.RuntimeVisible);
}
return m;
}
private SootMethod getMsgSendMethod(String selectorName, SootMethod method, boolean extensions) {
return getMsgSendMethod(selectorName, method, false, null, extensions);
}
@SuppressWarnings("unchecked")
private SootMethod getMsgSendSuperMethod(String selectorName, SootMethod method) {
List paramTypes = new ArrayList<>();
paramTypes.add(org_robovm_objc_ObjCSuper.getType());
paramTypes.add(org_robovm_objc_Selector.getType());
paramTypes.addAll(method.getParameterTypes());
SootMethod m = new SootMethod("$m$super$" + selectorName.replace(':', '$'),
paramTypes, method.getReturnType(), STATIC | PRIVATE | NATIVE);
copyAnnotations(method, m, Visibility.RuntimeVisible);
copyParameterAnnotations(method, m, 0, method.getParameterCount(), 2, Visibility.RuntimeVisible);
return m;
}
private SootMethod getCallbackMethod(String selectorName, SootMethod method, Type receiverType) {
return getMsgSendMethod(selectorName, method, true, receiverType, false);
}
private void addBindCall(SootClass sootClass) {
Jimple j = Jimple.v();
SootMethod clinit = getOrCreateStaticInitializer(sootClass);
Body body = clinit.retrieveActiveBody();
String internalName = sootClass.getName().replace('.', '/');
ClassConstant c = ClassConstant.v(internalName);
Chain units = body.getUnits();
// Don't call bind if there's already a call in the static initializer
for (Unit unit : units) {
if (unit instanceof InvokeStmt) {
InvokeStmt stmt = (InvokeStmt) unit;
if (stmt.getInvokeExpr() instanceof StaticInvokeExpr) {
StaticInvokeExpr expr = (StaticInvokeExpr) stmt.getInvokeExpr();
SootMethodRef ref = expr.getMethodRef();
if (ref.isStatic() && ref.declaringClass().equals(org_robovm_objc_ObjCRuntime)
&& ref.name().equals("bind")) {
if (ref.parameterTypes().isEmpty() || expr.getArg(0).equals(c)) {
return;
}
}
}
}
}
// Call ObjCRuntime.bind()
units.insertBefore(
j.newInvokeStmt(
j.newStaticInvokeExpr(
org_robovm_objc_ObjCRuntime_bind,
ClassConstant.v(internalName))),
units.getLast()
);
}
private void addObjCClassField(SootClass sootClass) {
Jimple j = Jimple.v();
SootMethod clinit = getOrCreateStaticInitializer(sootClass);
Body body = clinit.retrieveActiveBody();
Local objCClass = Jimple.v().newLocal("$objCClass", org_robovm_objc_ObjCClass.getType());
body.getLocals().add(objCClass);
Chain units = body.getUnits();
SootField f = new SootField("$objCClass", org_robovm_objc_ObjCClass.getType(),
STATIC | PRIVATE | FINAL);
sootClass.addField(f);
units.insertBefore(
Arrays.asList(
j.newAssignStmt(
objCClass,
j.newStaticInvokeExpr(
org_robovm_objc_ObjCClass_getByType,
ClassConstant.v(sootClass.getName().replace('.', '/')))),
j.newAssignStmt(
j.newStaticFieldRef(f.makeRef()),
objCClass)
),
units.getLast()
);
}
private void registerSelectors(SootClass sootClass, Set 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 units = body.getUnits();
for (String selectorName : selectors) {
SootField f = getSelectorField(selectorName);
sootClass.addField(f);
units.insertBefore(
Arrays.asList(
j.newAssignStmt(
sel,
j.newStaticInvokeExpr(
org_robovm_objc_Selector_register,
StringConstant.v(selectorName))),
j.newAssignStmt(
j.newStaticFieldRef(f.makeRef()),
sel)
),
units.getLast()
);
}
}
private void init(Config config) {
if (initialized) {
return;
}
if (config.getClazzes().load(OBJC_OBJECT.replace('.', '/')) == null) {
initialized = true;
return;
}
SootResolver r = SootResolver.v();
// These have to be resolved to HIERARCHY so that isPhantom() works properly
org_robovm_objc_ObjCObject = r.resolveClass(OBJC_OBJECT, SootClass.HIERARCHY);
org_robovm_objc_ObjCExtensions = r.resolveClass(OBJC_EXTENSIONS, SootClass.HIERARCHY);
// These only have to be DANGLING
org_robovm_objc_ObjCClass = r.makeClassRef(OBJC_CLASS);
org_robovm_objc_ObjCSuper = r.makeClassRef(OBJC_SUPER);
org_robovm_objc_ObjCRuntime = r.makeClassRef(OBJC_RUNTIME);
org_robovm_objc_Selector = r.makeClassRef(SELECTOR);
SootClass java_lang_Object = r.makeClassRef("java.lang.Object");
java_lang_String = r.makeClassRef("java.lang.String");
java_lang_Class = r.makeClassRef("java.lang.Class");
org_robovm_objc_Selector_register =
Scene.v().makeMethodRef(
org_robovm_objc_Selector,
"register",
Arrays.asList(java_lang_String.getType()),
org_robovm_objc_Selector.getType(), true);
org_robovm_objc_ObjCObject_getSuper =
Scene.v().makeMethodRef(
org_robovm_objc_ObjCObject,
"getSuper",
Collections.emptyList(),
org_robovm_objc_ObjCSuper.getType(), false);
org_robovm_objc_ObjCObject_updateStrongRef =
Scene.v().makeMethodRef(
org_robovm_objc_ObjCObject,
"updateStrongRef",
Arrays.asList(
java_lang_Object.getType(),
java_lang_Object.getType()),
VoidType.v(), false);
org_robovm_objc_ObjCClass_getByType =
Scene.v().makeMethodRef(
org_robovm_objc_ObjCClass,
"getByType",
Arrays.asList(java_lang_Class.getType()),
org_robovm_objc_ObjCClass.getType(), true);
org_robovm_objc_ObjCRuntime_bind =
Scene.v().makeMethodRef(
org_robovm_objc_ObjCRuntime,
"bind",
Arrays.asList(java_lang_Class.getType()),
VoidType.v(), true);
org_robovm_objc_ObjCObject_customClass =
Scene.v().makeFieldRef(
org_robovm_objc_ObjCObject,
"customClass", BooleanType.v(), false);
org_robovm_objc_ObjCExtensions_updateStrongRef =
Scene.v().makeMethodRef(
org_robovm_objc_ObjCExtensions,
"updateStrongRef",
Arrays.asList(
org_robovm_objc_ObjCObject.getType(),
java_lang_Object.getType(),
java_lang_Object.getType()),
VoidType.v(), true);
initialized = true;
}
private boolean isObjCObject(SootClass cls) {
if (org_robovm_objc_ObjCObject == null || org_robovm_objc_ObjCObject.isPhantom()) {
return false;
}
while (cls != org_robovm_objc_ObjCObject && cls.hasSuperclass()) {
cls = cls.getSuperclass();
}
return cls == org_robovm_objc_ObjCObject;
}
private boolean isObjCExtensions(SootClass cls) {
if (org_robovm_objc_ObjCExtensions == null || org_robovm_objc_ObjCExtensions.isPhantom()) {
return false;
}
while (cls != org_robovm_objc_ObjCExtensions && cls.hasSuperclass()) {
cls = cls.getSuperclass();
}
return cls == org_robovm_objc_ObjCExtensions;
}
@Override
public void beforeClass(Config config, Clazz clazz, ModuleBuilder moduleBuilder) {
init(config);
SootClass sootClass = clazz.getSootClass();
boolean extensions = false;
if (!sootClass.isInterface()
&& (isObjCObject(sootClass) || (extensions = isObjCExtensions(sootClass)))) {
Set selectors = new TreeSet<>();
Set overridables = new HashSet<>();
for (SootMethod method : sootClass.getMethods()) {
if (!"".equals(method.getName()) && !"".equals(method.getName())) {
transformMethod(config, clazz, sootClass, method, selectors, overridables, extensions);
}
}
addBindCall(sootClass);
if (!extensions) {
addObjCClassField(sootClass);
}
registerSelectors(sootClass, selectors);
}
}
private static List l(E head, List tail) {
LinkedList l = new LinkedList<>(tail);
l.addFirst(head);
return l;
}
private boolean isOverridable(SootMethod method) {
if (method.isStatic() || method.isPrivate()
|| (method.getModifiers() & Modifier.FINAL) != 0
|| (method.getDeclaringClass().getModifiers() & Modifier.FINAL) != 0) {
return false;
}
return true;
}
private boolean checkOverridable(Set overridables, String selectorName, SootMethod method) {
boolean b = isOverridable(method);
if (b && overridables.contains(selectorName)) {
throw new CompilerException("Found multiple overridable @Method or @Property "
+ "methods in " + method.getDeclaringClass() + " with the selector '"
+ selectorName + "'.");
}
return b;
}
private void transformMethod(Config config, Clazz clazz, SootClass sootClass,
SootMethod method, Set selectors, Set overridables, boolean extensions) {
AnnotationTag methodAnno = getAnnotation(method, METHOD);
if (methodAnno != null) {
if (extensions && !(method.isStatic() && method.isNative())) {
throw new CompilerException("Objective-C @Method method "
+ method + " in extension class must be static and native.");
}
// Determine the selector
String selectorName = readStringElem(methodAnno, "selector", "").trim();
if (selectorName.length() == 0) {
StringBuilder sb = new StringBuilder(method.getName());
int argCount = method.getParameterCount();
for (int i = extensions ? 1 : 0; i < argCount; i++) {
sb.append(':');
}
selectorName = sb.toString();
}
// Create the @Bridge and @Callback methods needed for this selector
if (!extensions && (method.getModifiers() & Modifier.FINAL) == 0) {
Type receiverType = ObjCProtocolProxyPlugin.isObjCProxy(sootClass)
? sootClass.getInterfaces().getFirst().getType()
: sootClass.getType();
createCallback(sootClass, method, selectorName, receiverType);
}
if (method.isNative()) {
if (checkOverridable(overridables, selectorName, method)) {
overridables.add(selectorName);
}
selectors.add(selectorName);
createBridge(sootClass, method, selectorName, false, extensions);
}
} else {
AnnotationTag propertyAnno = getAnnotation(method, PROPERTY);
if (propertyAnno != null) {
// Validate
if (extensions && !(method.isStatic() && method.isNative())) {
throw new CompilerException("Objective-C @Property method "
+ method + " in extension class must be static and native.");
}
int getterParamCount = extensions ? 1 : 0;
int setterParamCount = extensions ? 2 : 1;
if (method.getReturnType() != VoidType.v() && method.getParameterCount() != getterParamCount
|| method.getReturnType() == VoidType.v() && method.getParameterCount() != setterParamCount) {
if (!extensions) {
throw new CompilerException("Objective-C @Property method " + method
+ " does not have a supported signature. @Property getter methods"
+ " must take 0 arguments and must not return void. "
+ "@Property setter methods must take 1 argument and return void.");
}
throw new CompilerException("Objective-C @Property method " + method + " in extension class"
+ " does not have a supported signature. @Property getter methods in extension classes"
+ " must take 1 argument (the 'this' reference) and must not return void. "
+ "@Property setter methods in extension classes must "
+ "take 2 arguments (first is the 'this' reference) and return void.");
}
boolean isGetter = method.getReturnType() != VoidType.v();
// Determine the selector
String selectorName = readStringElem(propertyAnno, "selector", "").trim();
if (selectorName.length() == 0) {
String methodName = method.getName();
if (!(isGetter && methodName.startsWith("get") && methodName.length() > 3)
&& !(isGetter && methodName.startsWith("is") && methodName.length() > 2)
&& !(!isGetter && methodName.startsWith("set") && methodName.length() > 3)) {
throw new CompilerException("Invalid Objective-C @Property method name "
+ method + ". @Property methods without an explicit selector value "
+ "must follow the Java beans property method naming convention.");
}
selectorName = methodName;
if (isGetter) {
selectorName = methodName.startsWith("is")
? methodName.substring(2)
: methodName.substring(3);
selectorName = selectorName.substring(0, 1).toLowerCase()
+ selectorName.substring(1);
} else {
selectorName += ":";
}
}
// Create the @Bridge and @Callback methods needed for this selector
if (!extensions && (method.getModifiers() & Modifier.FINAL) == 0) {
Type receiverType = ObjCProtocolProxyPlugin.isObjCProxy(sootClass)
? sootClass.getInterfaces().getFirst().getType()
: sootClass.getType();
createCallback(sootClass, method, selectorName, receiverType);
}
if (method.isNative()) {
if (checkOverridable(overridables, selectorName, method)) {
overridables.add(selectorName);
}
selectors.add(selectorName);
boolean strongRefSetter = !isGetter && readBooleanElem(propertyAnno, "strongRef", false);
createBridge(sootClass, method, selectorName, strongRefSetter, extensions);
}
}
}
}
private void createCallback(SootClass sootClass, SootMethod method, String selectorName, Type receiverType) {
Jimple j = Jimple.v();
SootMethod callbackMethod = getCallbackMethod(selectorName, method, receiverType);
sootClass.addMethod(callbackMethod);
addCallbackAnnotation(callbackMethod);
addBindSelectorAnnotation(callbackMethod, selectorName);
Body body = j.newBody(callbackMethod);
callbackMethod.setActiveBody(body);
PatchingChain units = body.getUnits();
Local thiz = null;
if (!method.isStatic()) {
thiz = j.newLocal("$this", receiverType);
body.getLocals().add(thiz);
units.add(j.newIdentityStmt(thiz, j.newParameterRef(receiverType, 0)));
}
LinkedList args = new LinkedList<>();
for (int i = 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 + 2)));
args.add(p);
}
Local ret = null;
if (method.getReturnType() != VoidType.v()) {
ret = j.newLocal("$ret", method.getReturnType());
body.getLocals().add(ret);
}
SootMethodRef targetMethod = method.makeRef();
if (((RefType) receiverType).getSootClass().isInterface()) {
@SuppressWarnings("unchecked")
List parameterTypes = method.getParameterTypes();
targetMethod = Scene.v().makeMethodRef(
((RefType) receiverType).getSootClass(),
method.getName(),
parameterTypes,
method.getReturnType(), false);
}
InvokeExpr expr = method.isStatic()
? j.newStaticInvokeExpr(targetMethod, args)
: (((RefType) receiverType).getSootClass().isInterface()
? j.newInterfaceInvokeExpr(thiz, targetMethod, args)
: j.newVirtualInvokeExpr(thiz, targetMethod, args));
units.add(
ret == null
? j.newInvokeStmt(expr)
: j.newAssignStmt(ret, expr)
);
if (ret != null) {
units.add(j.newReturnStmt(ret));
} else {
units.add(j.newReturnVoidStmt());
}
}
private SootMethod findStrongRefGetter(SootClass sootClass,
final SootMethod method, boolean extensions) {
String setterPropName = readStringElem(getAnnotation(method, PROPERTY), "name", "").trim();
if (setterPropName.length() == 0) {
String methodName = method.getName();
if (!methodName.startsWith("set") || methodName.length() == 3) {
throw new CompilerException("Failed to determine the property "
+ "name from the @Property method " + method
+ ". Either specify the name explicitly in the @Property "
+ "annotation or rename the method according to the Java "
+ "beans property setter method naming convention.");
}
setterPropName = methodName.substring(3);
setterPropName = setterPropName.substring(0, 1).toLowerCase() + setterPropName.substring(1);
}
int paramCount = extensions ? 1 : 0;
Type propType = method.getParameterType(extensions ? 1 : 0);
for (SootMethod m : sootClass.getMethods()) {
if (m != method && method.isStatic() == m.isStatic()
&& m.getParameterCount() == paramCount && m.getReturnType().equals(propType)) {
AnnotationTag propertyAnno = getAnnotation(m, PROPERTY);
if (propertyAnno != null) {
String getterPropName = readStringElem(propertyAnno, "name", "").trim();
if (getterPropName.length() == 0) {
String methodName = m.getName();
if (!methodName.startsWith("get") || methodName.length() == 3) {
// Not a candidate. No name set and not a Java beans style getter
continue;
}
getterPropName = methodName.substring(3);
getterPropName = getterPropName.substring(0, 1).toLowerCase() + getterPropName.substring(1);
}
if (setterPropName.equals(getterPropName)) {
return m;
}
}
}
}
throw new CompilerException("Failed to determine the getter method "
+ "corresponding to the strong ref @Property setter method " + method
+ ". The getter must either specify the name explicitly in the @Property "
+ "annotation or be named according to the Java "
+ "beans property getter method naming convention.");
}
private void createBridge(SootClass sootClass, SootMethod method, String selectorName,
boolean strongRefSetter, boolean extensions) {
Jimple j = Jimple.v();
SootMethod msgSendMethod = getMsgSendMethod(selectorName, method, extensions);
sootClass.addMethod(msgSendMethod);
addBridgeAnnotation(msgSendMethod);
SootMethod msgSendSuperMethod = null;
if (!extensions && !method.isStatic()) {
msgSendSuperMethod = getMsgSendSuperMethod(selectorName, method);
sootClass.addMethod(msgSendSuperMethod);
addBridgeAnnotation(msgSendSuperMethod);
}
method.setModifiers(method.getModifiers() & ~NATIVE);
if (isOverridable(method)) {
addNotImplementedAnnotation(method, selectorName);
}
Body body = j.newBody(method);
method.setActiveBody(body);
PatchingChain 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 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 =
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 (!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", method.getReturnType());
body.getLocals().add(ret);
}
StaticInvokeExpr invokeMsgSendExpr =
j.newStaticInvokeExpr(
msgSendMethod.makeRef(),
l(thiz != null ? thiz : objCClass, args));
Stmt invokeMsgSendStmt = ret == null
? j.newInvokeStmt(invokeMsgSendExpr)
: j.newAssignStmt(ret, invokeMsgSendExpr);
if (customClass != null) {
// if $customClass == 0 goto
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(
msgSendSuperMethod.makeRef(),
l(zuper, args));
units.add(
ret == null
? j.newInvokeStmt(invokeMsgSendSuperExpr)
: j.newAssignStmt(ret, invokeMsgSendSuperExpr)
);
if (ret != null) {
units.add(j.newReturnStmt(ret));
} else {
units.add(j.newReturnVoidStmt());
}
}
units.add(invokeMsgSendStmt);
if (ret != null) {
units.add(j.newReturnStmt(ret));
} else {
units.add(j.newReturnVoidStmt());
}
}
static void addBridgeAnnotation(SootMethod method) {
addRuntimeVisibleAnnotation(method, BRIDGE);
}
static void addCallbackAnnotation(SootMethod method) {
addRuntimeVisibleAnnotation(method, CALLBACK);
}
static void addBindSelectorAnnotation(SootMethod method, String selectorName) {
AnnotationTag annotationTag = new AnnotationTag(BIND_SELECTOR, 1);
annotationTag.addElem(new AnnotationStringElem(selectorName, 's', "value"));
addRuntimeVisibleAnnotation(method, annotationTag);
}
static void addNotImplementedAnnotation(SootMethod method, String selectorName) {
AnnotationTag annotationTag = new AnnotationTag(NOT_IMPLEMENTED, 1);
annotationTag.addElem(new AnnotationStringElem(selectorName, 's', "value"));
addRuntimeVisibleAnnotation(method, annotationTag);
}
}