use of soot.Type in project robovm by robovm.
the class LambdaClassGenerator method pushArguments.
private void pushArguments(SootClass caller, String lambdaClassName, MethodVisitor mv, GeneratorAdapter caster, List<Type> parameters, List<Type> invokedParameters, SootMethodHandle implMethod, SootMethodType instantiatedMethodType, boolean isInstanceMethod) {
// create the object itself
if (implMethod.getReferenceKind() == SootMethodHandle.REF_newInvokeSpecial) {
mv.visitTypeInsn(NEW, implMethod.getMethodRef().declaringClass().getName().replace('.', '/'));
mv.visitInsn(DUP);
}
// push the captured arguments
for (int i = 0; i < invokedParameters.size(); i++) {
Object obj = invokedParameters.get(i);
Type captureType = (Type) obj;
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, lambdaClassName, "arg$" + (i + 1), Types.getDescriptor(captureType));
}
// push the functional interface parameters
// first check if the parameters include the received, e.g.
// "hello"::contains
// would be called on "hello" and not on the class containing the
// invoke dynamic call. We need to handle that parameter separately as
// it's not part of the method signature of the implementation
boolean paramsContainReceiver = isInstanceMethod & !caller.getName().equals(implMethod.getMethodRef().declaringClass().getName()) && parameters.size() > implMethod.getMethodRef().parameterTypes().size();
int paramsIndex = 0;
// we start at slot index 1, because this occupies
int localIndex = 1;
// slot 0
if (paramsContainReceiver && !parameters.isEmpty()) {
Type param = parameters.get(0);
mv.visitVarInsn(loadOpcodeForType(param), localIndex);
castOrWiden(mv, caster, param, implMethod.getMethodRef().declaringClass().getType());
localIndex += slotsForType(param);
paramsIndex++;
}
int samParamsOffset = implMethod.getMethodRef().parameterTypes().size() - parameters.size() + (paramsContainReceiver ? 1 : 0);
for (int i = 0; paramsIndex < parameters.size(); paramsIndex++, i++) {
Type param = parameters.get(paramsIndex);
mv.visitVarInsn(loadOpcodeForType(param), localIndex);
castOrWiden(mv, caster, param, (Type) implMethod.getMethodRef().parameterTypes().get(samParamsOffset + i));
localIndex += slotsForType(param);
}
}
use of soot.Type in project robovm by robovm.
the class LambdaPlugin method transformMethod.
private void transformMethod(Config config, Clazz clazz, SootClass sootClass, SootMethod method, ModuleBuilder moduleBuilder) throws IOException {
if (!method.isConcrete()) {
return;
}
int tmpCounter = 0;
Body body = method.retrieveActiveBody();
PatchingChain<Unit> units = body.getUnits();
for (Unit unit = units.getFirst(); unit != null; unit = body.getUnits().getSuccOf(unit)) {
if (unit instanceof DefinitionStmt) {
if (((DefinitionStmt) unit).getRightOp() instanceof DynamicInvokeExpr) {
DynamicInvokeExpr expr = (DynamicInvokeExpr) ((DefinitionStmt) unit).getRightOp();
if (isLambdaBootstrapMethod(expr.getBootstrapMethodRef())) {
LambdaClassGenerator generator = null;
synchronized (generators) {
generator = generators.get(sootClass);
if (generator == null) {
generator = new LambdaClassGenerator();
generators.put(sootClass, generator);
}
}
List<Value> bsmArgs = expr.getBootstrapArgs();
SootClass caller = sootClass;
String invokedName = expr.getMethodRef().name();
SootMethodRef invokedType = expr.getMethodRef();
SootMethodType samMethodType = (SootMethodType) bsmArgs.get(0);
SootMethodHandle implMethod = (SootMethodHandle) bsmArgs.get(1);
SootMethodType instantiatedMethodType = (SootMethodType) bsmArgs.get(2);
try {
LambdaClass callSite = null;
List<Type> markerInterfaces = new ArrayList<>();
List<SootMethodType> bridgeMethods = new ArrayList<>();
if (expr.getBootstrapMethodRef().name().equals("altMetafactory")) {
int flags = ((IntConstant) bsmArgs.get(3)).value;
int bsmArgsIdx = 4;
if ((flags & FLAG_MARKERS) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
for (int i = 0; i < count; i++) {
Object value = bsmArgs.get(bsmArgsIdx++);
if (value instanceof Type) {
markerInterfaces.add((Type) value);
} else if (value instanceof ClassConstant) {
String className = ((ClassConstant) value).getValue().replace('/', '.');
markerInterfaces.add(SootResolver.v().resolveClass(className, SootClass.HIERARCHY).getType());
}
}
}
if ((flags & FLAG_BRIDGES) > 0) {
int count = ((IntConstant) bsmArgs.get(bsmArgsIdx++)).value;
for (int i = 0; i < count; i++) {
bridgeMethods.add((SootMethodType) bsmArgs.get(bsmArgsIdx++));
}
}
}
// see issue #1087
if (bridgeMethods.size() == 0) {
SootClass targetType = SootResolver.v().resolveClass(invokedType.returnType().toString().replace('/', '.'), SootClass.SIGNATURES);
String samDescriptor = Types.getDescriptor(samMethodType.getParameterTypes(), samMethodType.getReturnType());
for (SootMethod targetTypeMethod : targetType.getMethods()) {
boolean isBridgeMethod = targetTypeMethod.getName().equals(invokedName);
isBridgeMethod &= targetTypeMethod.getName().equals(invokedName);
isBridgeMethod &= targetTypeMethod.getParameterCount() == samMethodType.getParameterTypes().size();
isBridgeMethod &= ((targetTypeMethod.getModifiers() & BRIDGE) != 0);
isBridgeMethod &= ((targetTypeMethod.getModifiers() & SYNTHETIC) != 0);
if (isBridgeMethod) {
String targetTypeMethodDesc = Types.getDescriptor(targetTypeMethod);
if (!targetTypeMethodDesc.equals(samDescriptor)) {
bridgeMethods.add(new BridgeMethodType(targetTypeMethod.getReturnType(), targetTypeMethod.getParameterTypes()));
}
}
}
}
// generate the lambda class
callSite = generator.generate(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType, markerInterfaces, bridgeMethods);
File f = clazz.getPath().getGeneratedClassFile(callSite.getLambdaClassName());
FileUtils.writeByteArrayToFile(f, callSite.getClassData());
// The lambda class is created after the caller is
// compiled.
// This prevents the triggering of a recompile of
// the caller.
f.setLastModified(clazz.lastModified());
SootClass lambdaClass = SootResolver.v().makeClassRef(callSite.getLambdaClassName().replace('/', '.'));
Local l = (Local) ((DefinitionStmt) unit).getLeftOp();
Type samType = callSite.getTargetMethodReturnType();
LinkedList<Unit> newUnits = new LinkedList<>();
if (callSite.getTargetMethodName().equals("<init>")) {
// Constant lambda. Create an instance once and
// reuse for
// every call.
String fieldName = lambdaClass.getName().substring(lambdaClass.getName().lastIndexOf('.') + 1);
SootField field = new SootField(fieldName, lambdaClass.getType(), Modifier.STATIC | Modifier.PRIVATE | Modifier.TRANSIENT | 0x1000);
method.getDeclaringClass().addField(field);
// l = LambdaClass.lambdaField
newUnits.add(Jimple.v().newAssignStmt(l, Jimple.v().newStaticFieldRef(field.makeRef())));
// if l != null goto succOfInvokedynamic
newUnits.add(Jimple.v().newIfStmt(Jimple.v().newNeExpr(l, NullConstant.v()), units.getSuccOf(unit)));
// $tmpX = new LambdaClass()
Local tmp = Jimple.v().newLocal("$tmp" + (tmpCounter++), lambdaClass.getType());
body.getLocals().add(tmp);
newUnits.add(Jimple.v().newAssignStmt(tmp, Jimple.v().newNewExpr(lambdaClass.getType())));
newUnits.add(Jimple.v().newInvokeStmt(Jimple.v().newSpecialInvokeExpr(tmp, Scene.v().makeConstructorRef(lambdaClass, Collections.<Type>emptyList()))));
// LambdaClass.lambdaField = $tmpX
newUnits.add(Jimple.v().newAssignStmt(Jimple.v().newStaticFieldRef(field.makeRef()), tmp));
// l = $tmpX
newUnits.add(Jimple.v().newAssignStmt(l, tmp));
} else {
// Static factory method returns the lambda to
// use.
newUnits.add(Jimple.v().newAssignStmt(l, Jimple.v().newStaticInvokeExpr(Scene.v().makeMethodRef(lambdaClass, callSite.getTargetMethodName(), callSite.getTargetMethodParameters(), samType, true), expr.getArgs())));
}
units.insertAfter(newUnits, unit);
units.remove(unit);
unit = newUnits.getLast();
} catch (Throwable e) {
// LambdaConversionException at runtime.
throw new CompilerException(e);
}
}
}
}
}
}
use of soot.Type in project robovm by robovm.
the class ObjCMemberPlugin method createCallback.
private void createCallback(SootClass sootClass, SootMethod method, SootMethod annotatedMethod, String selectorName, Type receiverType) {
Jimple j = Jimple.v();
SootMethod callbackMethod = getCallbackMethod(selectorName, method, annotatedMethod, receiverType);
sootClass.addMethod(callbackMethod);
addCallbackAnnotation(callbackMethod);
addBindSelectorAnnotation(callbackMethod, selectorName);
if (!hasAnnotation(annotatedMethod, TYPE_ENCODING) && (isCustomClass(sootClass) || ObjCProtocolProxyPlugin.isObjCProxy(sootClass))) {
String encoding = generateTypeEncoding(callbackMethod);
try {
addTypeEncodingAnnotation(callbackMethod, encoding);
} catch (IllegalArgumentException e) {
throw new CompilerException("Failed to determine method type encoding for method " + method + ": " + e.getMessage());
}
}
Body body = j.newBody(callbackMethod);
callbackMethod.setActiveBody(body);
PatchingChain<Unit> 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<Value> 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<Type> 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());
}
}
use of soot.Type in project robovm by robovm.
the class ObjCMemberPlugin method getMsgSendMethod.
@SuppressWarnings("unchecked")
private SootMethod getMsgSendMethod(String selectorName, SootMethod method, SootMethod annotatedMethod, boolean isCallback, Type receiverType, boolean extensions) {
List<Type> 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(annotatedMethod, m, extensions);
createGenericSignatureForMsgSend(annotatedMethod, m, paramTypes, extensions);
return m;
}
use of soot.Type in project robovm by robovm.
the class ObjCMemberPlugin method transformMethod.
private void transformMethod(Config config, Clazz clazz, SootClass sootClass, SootMethod method, Set<String> selectors, Set<String> overridables, boolean extensions) {
AnnotationTag annotation = getAnnotation(method, METHOD);
if (annotation != null) {
if (extensions && !(method.isStatic() && method.isNative())) {
throw new CompilerException("Objective-C @Method method " + method + " in extension class must be static and native.");
}
transformObjCMethod(annotation, sootClass, method, selectors, overridables, extensions);
return;
}
annotation = getAnnotation(method, IBACTION);
if (annotation != null) {
if (method.isStatic() || method.isNative()) {
throw new CompilerException("Objective-C @IBAction method " + method + " must not be static or native.");
}
int paramCount = method.getParameterCount();
Type param1 = paramCount > 0 ? method.getParameterType(0) : null;
Type param2 = paramCount > 1 ? method.getParameterType(1) : null;
if (method.getReturnType() != VoidType.v() || paramCount > 2 || (param1 != null && (!isNSObject(param1) && !isNSObject(param1))) || (param2 != null && (!isUIEvent(param2) || isNSObject(param1)))) {
throw new CompilerException("Objective-C @IBAction method " + method + " does not have a supported signature. @IBAction methods" + " must return void and either take no arguments, 1 argument of type NSObject" + ", or 2 arguments of types NSObject and UIEvent.");
}
transformObjCMethod(annotation, sootClass, method, selectors, overridables, extensions);
return;
}
annotation = getAnnotation(method, PROPERTY);
if (annotation != null) {
if (extensions && !(method.isStatic() && method.isNative())) {
throw new CompilerException("Objective-C @Property method " + method + " in extension class must be static and native.");
}
transformObjCProperty(annotation, "@Property", sootClass, method, selectors, overridables, extensions);
return;
}
annotation = getAnnotation(method, IBOUTLET);
if (annotation != null) {
if (method.isStatic()) {
throw new CompilerException("Objective-C @IBOutlet method " + method + " must not be static.");
}
transformObjCProperty(annotation, "@IBOutlet", sootClass, method, selectors, overridables, extensions);
return;
}
annotation = getAnnotation(method, IBOUTLETCOLLECTION);
if (annotation != null) {
if (method.isStatic()) {
throw new CompilerException("Objective-C @IBOutletCollection method " + method + " must not be static.");
}
if (method.getReturnType() != VoidType.v() && !isNSArray(method.getReturnType()) || method.getReturnType() == VoidType.v() && method.getParameterCount() == 1 && !isNSArray(method.getParameterType(0))) {
throw new CompilerException("Objective-C @IBOutletCollection method " + method + " does not have a supported signature. " + "@IBOutletCollection getter methods must return NSArray. " + "@IBOutletCollection setter methods must have 1 parameter of type NSArray.");
}
transformObjCProperty(annotation, "@IBOutletCollection", sootClass, method, selectors, overridables, extensions);
return;
}
if (!method.isStatic() && !method.isNative() && !method.isAbstract() && !method.isPrivate() && isCustomClass(sootClass) && !hasAnnotation(method, NOT_IMPLEMENTED)) {
/*
* Create a @Callback for this method if it overrides a
* @Method/@Property method in a superclass/interface.
*/
List<SootMethod> superMethods = findOverriddenMethods(sootClass, method);
for (SootMethod superMethod : superMethods) {
if (createCustomClassCallbackIfNeeded(sootClass, method, superMethod)) {
break;
}
}
}
}
Aggregations