use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.
the class MethodCompiler method invokeExpr.
private Value invokeExpr(Stmt stmt, InvokeExpr expr) {
SootMethodRef methodRef = expr.getMethodRef();
ArrayList<Value> args = new ArrayList<Value>();
args.add(env);
if (!(expr instanceof StaticInvokeExpr)) {
Value base = immediate(stmt, (Immediate) ((InstanceInvokeExpr) expr).getBase());
checkNull(stmt, base);
args.add(base);
}
int i = 0;
for (soot.Value sootArg : (List<soot.Value>) expr.getArgs()) {
Value arg = immediate(stmt, (Immediate) sootArg);
args.add(narrowFromI32Value(stmt, getType(methodRef.parameterType(i)), arg));
i++;
}
Value result = null;
FunctionRef functionRef = config.isDebug() ? null : Intrinsics.getIntrinsic(sootMethod, stmt, expr);
if (functionRef == null) {
Trampoline trampoline = null;
String targetClassName = getInternalName(methodRef.declaringClass());
String methodName = methodRef.name();
String methodDesc = getDescriptor(methodRef);
if (expr instanceof SpecialInvokeExpr) {
soot.Type runtimeType = ((SpecialInvokeExpr) expr).getBase().getType();
String runtimeClassName = runtimeType == NullType.v() ? targetClassName : getInternalName(runtimeType);
trampoline = new Invokespecial(this.className, targetClassName, methodName, methodDesc, runtimeClassName);
} else if (expr instanceof StaticInvokeExpr) {
trampoline = new Invokestatic(this.className, targetClassName, methodName, methodDesc);
} else if (expr instanceof VirtualInvokeExpr) {
soot.Type runtimeType = ((VirtualInvokeExpr) expr).getBase().getType();
String runtimeClassName = runtimeType == NullType.v() ? targetClassName : getInternalName(runtimeType);
trampoline = new Invokevirtual(this.className, targetClassName, methodName, methodDesc, runtimeClassName);
} else if (expr instanceof InterfaceInvokeExpr) {
trampoline = new Invokeinterface(this.className, targetClassName, methodName, methodDesc);
}
trampolines.add(trampoline);
if (canCallDirectly(expr)) {
SootMethod method = this.sootMethod.getDeclaringClass().getMethod(methodRef.name(), methodRef.parameterTypes(), methodRef.returnType());
if (method.isSynchronized()) {
functionRef = FunctionBuilder.synchronizedWrapper(method).ref();
} else {
functionRef = createMethodFunction(method).ref();
}
} else {
functionRef = trampoline.getFunctionRef();
}
}
result = call(stmt, functionRef, args.toArray(new Value[0]));
if (result != null) {
return widenToI32Value(stmt, result, methodRef.returnType().equals(CharType.v()));
} else {
return null;
}
}
use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.
the class ClassCompiler method compile.
private void compile(Clazz clazz, OutputStream out) throws IOException {
javaMethodCompiler.reset(clazz);
bridgeMethodCompiler.reset(clazz);
callbackMethodCompiler.reset(clazz);
nativeMethodCompiler.reset(clazz);
structMemberMethodCompiler.reset(clazz);
globalValueMethodCompiler.reset(clazz);
ClazzInfo ci = clazz.resetClazzInfo();
mb = new ModuleBuilder();
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeClass(config, clazz, mb);
}
sootClass = clazz.getSootClass();
trampolines = new HashMap<>();
catches = new HashSet<String>();
classFields = getClassFields(config.getOs(), config.getArch(), sootClass);
instanceFields = getInstanceFields(config.getOs(), config.getArch(), sootClass);
classType = getClassType(config.getOs(), config.getArch(), sootClass);
instanceType = getInstanceType(config.getOs(), config.getArch(), sootClass);
attributesEncoder.encode(mb, sootClass);
// will never be initialized.
if (!sootClass.declaresMethodByName("<clinit>") && hasConstantValueTags(classFields)) {
SootMethod clinit = new SootMethod("<clinit>", Collections.EMPTY_LIST, VoidType.v(), Modifier.STATIC);
JimpleBody body = Jimple.v().newBody(clinit);
clinit.setActiveBody(body);
body.getUnits().add(new JReturnVoidStmt());
this.sootClass.addMethod(clinit);
}
if (isStruct(sootClass)) {
SootMethod _sizeOf = new SootMethod("_sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PROTECTED | Modifier.NATIVE);
sootClass.addMethod(_sizeOf);
SootMethod sizeOf = new SootMethod("sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC | Modifier.NATIVE);
sootClass.addMethod(sizeOf);
}
mb.addInclude(getClass().getClassLoader().getResource(String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
mb.addInclude(getClass().getClassLoader().getResource("header.ll"));
mb.addFunction(createLdcClass());
mb.addFunction(createLdcClassWrapper());
Function allocator = createAllocator();
mb.addFunction(allocator);
mb.addFunction(createClassInitWrapperFunction(allocator.ref()));
for (SootField f : sootClass.getFields()) {
Function getter = createFieldGetter(f, classFields, classType, instanceFields, instanceType);
Function setter = createFieldSetter(f, classFields, classType, instanceFields, instanceType);
mb.addFunction(getter);
mb.addFunction(setter);
if (f.isStatic() && !f.isPrivate()) {
mb.addFunction(createClassInitWrapperFunction(getter.ref()));
if (!f.isFinal()) {
mb.addFunction(createClassInitWrapperFunction(setter.ref()));
}
}
}
// After this point no changes to methods/fields may be done by CompilerPlugins.
ci.initClassInfo();
for (SootMethod method : sootClass.getMethods()) {
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.beforeMethod(config, clazz, method, mb);
}
String name = method.getName();
Function function = null;
if (hasBridgeAnnotation(method)) {
function = bridgeMethod(method);
} else if (hasGlobalValueAnnotation(method)) {
function = globalValueMethod(method);
} else if (isStruct(sootClass) && ("_sizeOf".equals(name) || "sizeOf".equals(name) || hasStructMemberAnnotation(method))) {
function = structMember(method);
} else if (method.isNative()) {
function = nativeMethod(method);
} else if (!method.isAbstract()) {
function = method(method);
}
if (hasCallbackAnnotation(method)) {
callbackMethod(method);
}
if (!name.equals("<clinit>") && !name.equals("<init>") && !method.isPrivate() && !method.isStatic() && !Modifier.isFinal(method.getModifiers()) && !Modifier.isFinal(sootClass.getModifiers())) {
createLookupFunction(method);
}
if (method.isStatic() && !name.equals("<clinit>")) {
String fnName = method.isSynchronized() ? Symbols.synchronizedWrapperSymbol(method) : Symbols.methodSymbol(method);
FunctionRef fn = new FunctionRef(fnName, getFunctionType(method));
mb.addFunction(createClassInitWrapperFunction(fn));
}
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
if (function != null) {
compilerPlugin.afterMethod(config, clazz, method, mb, function);
}
}
}
for (Trampoline trampoline : trampolines.keySet()) {
Set<String> deps = new HashSet<String>();
Set<Triple<String, String, String>> mDeps = new HashSet<>();
trampolineResolver.compile(mb, clazz, trampoline, deps, mDeps);
for (SootMethod m : trampolines.get(trampoline)) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
mi.addClassDependencies(deps, false);
mi.addInvokeMethodDependencies(mDeps, false);
}
}
/*
* Add method dependencies from overriding methods to the overridden
* super method(s). These will be reversed by the DependencyGraph to
* create edges from the super/interface method to the overriding
* method.
*/
Map<SootMethod, Set<SootMethod>> overriddenMethods = getOverriddenMethods(this.sootClass);
for (SootMethod from : overriddenMethods.keySet()) {
MethodInfo mi = ci.getMethod(from.getName(), getDescriptor(from));
for (SootMethod to : overriddenMethods.get(from)) {
mi.addSuperMethodDependency(getInternalName(to.getDeclaringClass()), to.getName(), getDescriptor(to), false);
}
}
/*
* Edge case. A method in a superclass might satisfy an interface method
* in the interfaces implemented by this class. See e.g. the abstract
* class HashMap$HashIterator which doesn't implement Iterator but has
* the hasNext() and other methods. We add a dependency from the current
* class to the super method to ensure it's included if the current
* class is linked in.
*/
if (sootClass.hasSuperclass()) {
for (SootClass interfaze : getImmediateInterfaces(sootClass)) {
for (SootMethod m : interfaze.getMethods()) {
if (!m.isStatic()) {
try {
this.sootClass.getMethod(m.getName(), m.getParameterTypes());
} catch (RuntimeException e) {
/*
* Not found. Find the implementation in
* superclasses.
*/
SootMethod superMethod = null;
for (SootClass sc = sootClass.getSuperclass(); sc.hasSuperclass(); sc = sc.getSuperclass()) {
try {
SootMethod candidate = sc.getMethod(m.getName(), m.getParameterTypes());
if (!candidate.isStatic()) {
superMethod = candidate;
break;
}
} catch (RuntimeException e2) {
// Not found.
}
}
if (superMethod != null) {
ci.addSuperMethodDependency(getInternalName(superMethod.getDeclaringClass()), superMethod.getName(), getDescriptor(superMethod), false);
}
}
}
}
}
}
Global classInfoStruct = null;
try {
if (!sootClass.isInterface()) {
config.getVTableCache().get(sootClass);
}
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), Linkage.weak, createClassInfoStruct());
} catch (IllegalArgumentException e) {
// VTable throws this if any of the superclasses of the class is actually an interface.
// Shouldn't happen frequently but the DRLVM test suite has some tests for this.
// The Linker will take care of making sure the class cannot be loaded at runtime.
classInfoStruct = new Global(Symbols.infoStructSymbol(clazz.getInternalName()), I8_PTR, true);
}
mb.addGlobal(classInfoStruct);
/*
* Emit an internal i8* alias for the info struct which MethodCompiler
* can use when referencing this info struct in exception landing pads
* in methods in the same class. See #1007.
*/
mb.addAlias(new Alias(classInfoStruct.getName() + "_i8ptr", Linkage._private, new ConstantBitcast(classInfoStruct.ref(), I8_PTR)));
Function infoFn = FunctionBuilder.infoStruct(sootClass);
infoFn.add(new Ret(new ConstantBitcast(classInfoStruct.ref(), I8_PTR_PTR)));
mb.addFunction(infoFn);
for (CompilerPlugin compilerPlugin : config.getCompilerPlugins()) {
compilerPlugin.afterClass(config, clazz, mb);
}
OutputStreamWriter writer = new OutputStreamWriter(out, "UTF-8");
mb.build().write(writer);
writer.flush();
ci.setCatchNames(catches);
// Make sure no class or interface has zero dependencies
ci.addClassDependency("java/lang/Object", false);
if (sootClass.hasSuperclass() && !sootClass.isInterface()) {
ci.addClassDependency(getInternalName(sootClass.getSuperclass()), false);
}
for (SootClass iface : sootClass.getInterfaces()) {
ci.addClassDependency(getInternalName(iface), false);
}
for (SootField f : sootClass.getFields()) {
addClassDependencyIfNeeded(clazz, f.getType(), false);
}
for (SootMethod m : sootClass.getMethods()) {
MethodInfo mi = ci.getMethod(m.getName(), getDescriptor(m));
addClassDependencyIfNeeded(clazz, mi, m.getReturnType(), false);
@SuppressWarnings("unchecked") List<soot.Type> paramTypes = (List<soot.Type>) m.getParameterTypes();
for (soot.Type type : paramTypes) {
addClassDependencyIfNeeded(clazz, mi, type, false);
}
}
ci.addClassDependencies(attributesEncoder.getDependencies(), false);
ci.addClassDependencies(catches, false);
for (Trampoline t : trampolines.keySet()) {
if (t instanceof Checkcast) {
ci.addCheckcast(t.getTarget());
} else if (t instanceof Instanceof) {
ci.addInstanceof(t.getTarget());
} else if (t instanceof Invokevirtual || t instanceof Invokeinterface) {
ci.addInvoke(t.getTarget() + "." + ((Invoke) t).getMethodName() + ((Invoke) t).getMethodDesc());
}
}
clazz.saveClazzInfo();
}
use of org.robovm.compiler.trampoline.Invokevirtual in project robovm by robovm.
the class TrampolineCompiler method checkMemberAccessible.
private boolean checkMemberAccessible(Function f, Trampoline t, ClassMember member) {
Clazz caller = config.getClazzes().load(t.getCallingClass());
Clazz target = config.getClazzes().load(member.getDeclaringClass().getName().replace('.', '/'));
String runtimeClassName = null;
runtimeClassName = t instanceof Invokevirtual ? ((Invokevirtual) t).getRuntimeClass() : runtimeClassName;
runtimeClassName = t instanceof Invokespecial ? ((Invokespecial) t).getRuntimeClass() : runtimeClassName;
runtimeClassName = t instanceof GetField ? ((GetField) t).getRuntimeClass() : runtimeClassName;
runtimeClassName = t instanceof PutField ? ((PutField) t).getRuntimeClass() : runtimeClassName;
SootClass runtimeClass = null;
if (runtimeClassName != null && !isArray(runtimeClassName)) {
Clazz c = config.getClazzes().load(runtimeClassName);
if (c == null) {
// just return true here.
return true;
}
runtimeClass = c.getSootClass();
}
if (Access.checkMemberAccessible(member, caller, target, runtimeClass)) {
return true;
}
if (member instanceof SootMethod) {
SootMethod method = (SootMethod) member;
throwIllegalAccessError(f, ILLEGAL_ACCESS_ERROR_METHOD, method.getDeclaringClass(), method.getName(), getDescriptor(method), caller.getSootClass());
} else {
SootField field = (SootField) member;
throwIllegalAccessError(f, ILLEGAL_ACCESS_ERROR_FIELD, field.getDeclaringClass(), field.getName(), caller.getSootClass());
}
f.add(new Unreachable());
return false;
}
Aggregations