All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.compiler.TrampolineCompiler Maven / Gradle / Ivy

There is a newer version: 1.12.0
Show newest version
/*
 * Copyright (C) 2012 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;

import static org.robovm.compiler.Access.*;
import static org.robovm.compiler.Functions.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.FunctionAttribute.*;
import static org.robovm.compiler.llvm.Linkage.*;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.FunctionAttribute;
import org.robovm.compiler.llvm.FunctionDeclaration;
import org.robovm.compiler.llvm.FunctionRef;
import org.robovm.compiler.llvm.FunctionType;
import org.robovm.compiler.llvm.Global;
import org.robovm.compiler.llvm.Linkage;
import org.robovm.compiler.llvm.NullConstant;
import org.robovm.compiler.llvm.Ret;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Unreachable;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.trampoline.Anewarray;
import org.robovm.compiler.trampoline.Checkcast;
import org.robovm.compiler.trampoline.FieldAccessor;
import org.robovm.compiler.trampoline.GetField;
import org.robovm.compiler.trampoline.Instanceof;
import org.robovm.compiler.trampoline.Invoke;
import org.robovm.compiler.trampoline.Invokeinterface;
import org.robovm.compiler.trampoline.Invokespecial;
import org.robovm.compiler.trampoline.Invokevirtual;
import org.robovm.compiler.trampoline.LdcClass;
import org.robovm.compiler.trampoline.Multianewarray;
import org.robovm.compiler.trampoline.New;
import org.robovm.compiler.trampoline.PutField;
import org.robovm.compiler.trampoline.Trampoline;

import soot.ClassMember;
import soot.IntType;
import soot.Modifier;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;

/**
 * @author niklas
 *
 */
public class TrampolineCompiler {
    public static final String ATTEMPT_TO_WRITE_TO_FINAL_FIELD = "Attempt to write to final field %s.%s from class %s";
    public static final String EXPECTED_INTERFACE_BUT_FOUND_CLASS = "Expected interface but found class %s";
    public static final String EXPECTED_NON_STATIC_METHOD = "Expected non-static method %s.%s%s";
    public static final String EXPECTED_STATIC_METHOD = "Expected static method %s.%s%s";
    public static final String EXPECTED_CLASS_BUT_FOUND_INTERFACE = "Expected class but found interface %s";
    public static final String EXPECTED_NON_STATIC_FIELD = "Expected non-static field %s.%s";
    public static final String EXPECTED_STATIC_FIELD = "Expected static field %s.%s";
    public static final String NO_SUCH_FIELD_ERROR = "%s.%s";
    public static final String NO_SUCH_METHOD_ERROR = "%s.%s%s";
    
    private final Config config;
    private ModuleBuilder mb;
    private Set dependencies;

    public TrampolineCompiler(Config config) {
        this.config = config;
    }
    
    public Set getDependencies() {
        return dependencies;
    }

    private Linkage aliasLinkage() {
        /*
         * The {@link Linkage} used for alias functions used to be _private but
         * for some reason LLVM's assembler doesn't resolve private symbols
         * correctly when there is debug info in the module which causes weird
         * crashes. See #559.
         */
        return config.isDebug() ? external : _private;
    }

    private FunctionAttribute shouldInline() {
        /*
         * Alias function used to be inlined always before we had debug builds.
         * The problem with debug builds is that when the function is inlined we
         * lose line number information for that line which. So now we don't
         * inline alias functions in debug builds. See #559.
         */
        return config.isDebug() ? noinline : alwaysinline;
    }

    public void compile(ModuleBuilder mb, Trampoline t) {
        this.mb = mb;
        this.dependencies = new HashSet();
        
        /*
         * Check if the target class exists and is accessible. Also check that
         * field accesses and method calls are compatible with the target 
         * field/method and that the field/method is accessible to the caller.
         * If any of the tests fail the weak trampoline function created by the
         * ClassCompiler will be overridden with a function which throws an
         * appropriate exception.
         */
        Function errorFn = new FunctionBuilder(t).linkage(external).build();
        if (!checkClassExists(errorFn, t) || !checkClassAccessible(errorFn, t)) {
            mb.addFunction(errorFn);
            return;
        }

        if (t instanceof New) {
            SootClass target = config.getClazzes().load(t.getTarget()).getSootClass();
            if (target.isAbstract() || target.isInterface()) {
                call(errorFn, BC_THROW_INSTANTIATION_ERROR, errorFn.getParameterRef(0), mb.getString(t.getTarget().replace('/', '.')));
                errorFn.add(new Unreachable());
                mb.addFunction(errorFn);
                return;
            }
            String fnName = Symbols.clinitWrapperSymbol(Symbols.allocatorSymbol(t.getTarget()));
            alias(t, fnName);
        } else if (t instanceof Instanceof) {
            if (isArray(t.getTarget())) {
                FunctionRef fnRef = createInstanceofArray((Instanceof) t);
                alias(t, fnRef.getName());
            } else {
                String fnName = Symbols.instanceofSymbol(t.getTarget());
                alias(t, fnName);
            }
        } else if (t instanceof Checkcast) {
            if (isArray(t.getTarget())) {
                FunctionRef fnRef = createCheckcastArray((Checkcast) t);
                alias(t, fnRef.getName());
            } else {
                String fnName = Symbols.checkcastSymbol(t.getTarget());
                alias(t, fnName);
            }
        } else if (t instanceof LdcClass) {
            if (isArray(t.getTarget())) {
                FunctionRef fnRef = createLdcArray((LdcClass) t);
                alias(t, fnRef.getName());
            } else {
                String fnName = Symbols.ldcExternalSymbol(t.getTarget());
                alias(t, fnName);
            }
        } else if (t instanceof Anewarray) {
            FunctionRef fnRef = createAnewarray((Anewarray) t);
            alias(t, fnRef.getName());
        } else if (t instanceof Multianewarray) {
            FunctionRef fnRef = createMultianewarray((Multianewarray) t);
            alias(t, fnRef.getName());
        } else if (t instanceof FieldAccessor) {
            SootField field = resolveField(errorFn, (FieldAccessor) t);
            if (field != null) {
                dependencies.add(getInternalName(field.getDeclaringClass()));
            }
            if (field == null || !checkMemberAccessible(errorFn, t, field)) {
                mb.addFunction(errorFn);
                return;
            }
            Clazz caller = config.getClazzes().load(t.getCallingClass());
            Clazz target = config.getClazzes().load(t.getTarget());
            if (!((FieldAccessor) t).isGetter() &&  field.isFinal() && caller != target) {
                // Only the class declaring a final field may write to it.
                // (Actually only / methods may write to it but we 
                // don't know which method is accessing the field at this point)
                throwIllegalAccessError(errorFn, ATTEMPT_TO_WRITE_TO_FINAL_FIELD, 
                        target, field.getName(), caller);
                mb.addFunction(errorFn);
                return;
            }
            if (!field.isStatic()) {
                createInlinedAccessorForInstanceField((FieldAccessor) t, field);   
            } else {
                createTrampolineAliasForField((FieldAccessor) t, field);
            }
        } else if (t instanceof Invokeinterface) {
            SootMethod rm = resolveInterfaceMethod(errorFn, (Invokeinterface) t);
            if (rm != null) {
                dependencies.add(getInternalName(rm.getDeclaringClass()));
            }
            if (rm == null || !checkMemberAccessible(errorFn, t, rm)) {
                mb.addFunction(errorFn);
                return;
            }
            createTrampolineAliasForMethod((Invoke) t, rm);
        } else if (t instanceof Invoke) {
            SootMethod method = resolveMethod(errorFn, (Invoke) t);
            if (method != null) {
                dependencies.add(getInternalName(method.getDeclaringClass()));
            }
            if (method == null || !checkMemberAccessible(errorFn, t, method)) {
                mb.addFunction(errorFn);
                return;
            }
            if (t instanceof Invokespecial && method.isAbstract()) {
                call(errorFn, BC_THROW_ABSTRACT_METHOD_ERROR, errorFn.getParameterRef(0), 
                        mb.getString(String.format(NO_SUCH_METHOD_ERROR, 
                                method.getDeclaringClass(), method.getName(), 
                                getDescriptor(method))));
                errorFn.add(new Unreachable());
                mb.addFunction(errorFn);
                return;
            }
            createTrampolineAliasForMethod((Invoke) t, method);
        }
    }

    private void alias(Trampoline t, String fnName) {
        FunctionRef aliasee = new FunctionRef(fnName, t.getFunctionType());
        if (!mb.hasSymbol(fnName)) {
            mb.addFunctionDeclaration(new FunctionDeclaration(aliasee));
        }
        Function fn = new FunctionBuilder(t).linkage(aliasLinkage()).attribs(shouldInline(), optsize).build();
        Value result = call(fn, aliasee, fn.getParameterRefs());
        fn.add(new Ret(result));
        mb.addFunction(fn);
    }
    
    private void createTrampolineAliasForField(FieldAccessor t, SootField field) {
        String fnName = t.isGetter() ? Symbols.getterSymbol(field) : Symbols.setterSymbol(field);
        if (t.isStatic()) {
            fnName = Symbols.clinitWrapperSymbol(fnName);
        }
        alias(t, fnName);
    }

    private void createInlinedAccessorForInstanceField(FieldAccessor t, SootField field) {
        Function fn = new FunctionBuilder(t).linkage(aliasLinkage()).attribs(shouldInline(), optsize).build();

        List classFields = Collections.emptyList();
        StructureType classType = new StructureType();
        List instanceFields = getInstanceFields(config.getOs(), config.getArch(), field.getDeclaringClass());
        StructureType instanceType = getInstanceType(config.getOs(), config.getArch(), field.getDeclaringClass());
        if (t.isGetter()) {
            ClassCompiler.createFieldGetter(fn, field, classFields, classType, instanceFields, instanceType);
        } else {
            ClassCompiler.createFieldSetter(fn, field, classFields, classType, instanceFields, instanceType);
        }
        
        mb.addFunction(fn);
    }
    
    private void createTrampolineAliasForMethod(Invoke t, SootMethod rm) {
        String fnName = null;
        if (t instanceof Invokeinterface) {
            fnName = Symbols.lookupWrapperSymbol(rm);
        } else if (t instanceof Invokevirtual 
                && !Modifier.isFinal(rm.getDeclaringClass().getModifiers()) 
                && !Modifier.isFinal(rm.getModifiers())
                && !Modifier.isPrivate(rm.getModifiers())) {
            fnName = Symbols.lookupWrapperSymbol(rm);
        } else if (rm.isSynchronized()) {
            fnName = Symbols.synchronizedWrapperSymbol(rm);
        } else {
            fnName = Symbols.methodSymbol(rm);
        }
        if (t.isStatic()) {
            fnName = Symbols.clinitWrapperSymbol(fnName);
        }
        alias(t, fnName);
    }
    
    private Value callLdcArray(Function function, String targetClass) {
        FunctionRef fnRef = createLdcArray(targetClass);
        return call(function, fnRef, function.getParameterRef(0));
    }
    
    private FunctionRef createLdcArray(LdcClass t) {
        return createLdcArray(t.getTarget());
    }

    private FunctionRef createLdcArray(String targetClass) {
        if (isPrimitiveComponentType(targetClass)) {
            throw new IllegalArgumentException();
        }
        String fnName = Symbols.arrayldcSymbol(targetClass);
        FunctionRef fnRef = new FunctionRef(fnName, new FunctionType(OBJECT_PTR, ENV_PTR));
        if (!mb.hasSymbol(fnName)) {
            Function fn = new FunctionBuilder(fnRef).name(fnName).linkage(weak).build();
            Global g = new Global(Symbols.arrayPtrSymbol(targetClass), weak, new NullConstant(OBJECT_PTR));
            if (!mb.hasSymbol(g.getName())) {
                mb.addGlobal(g);
            }
            FunctionRef ldcArrayClassFn = BC_LDC_ARRAY_BOOT_CLASS;
            if (!isPrimitiveBaseType(targetClass)) {
                Clazz baseType = config.getClazzes().load(getBaseType(targetClass));
                if (!baseType.isInBootClasspath()) {
                    ldcArrayClassFn = BC_LDC_ARRAY_CLASS;
                }
            }
            Value arrayClass = call(fn, ldcArrayClassFn, fn.getParameterRef(0), g.ref(), mb.getString(targetClass));
            fn.add(new Ret(arrayClass));
            mb.addFunction(fn);
        }
        return fnRef;
    }

    private FunctionRef createInstanceofArray(Instanceof t) {
        String fnName = Symbols.arrayinstanceofSymbol(t.getTarget());
        if (!mb.hasSymbol(fnName)) {
            Function fn = new FunctionBuilder(t).name(fnName).linkage(weak).build();
            Value arrayClass = callLdcArray(fn, t.getTarget());
            Value result = call(fn, BC_INSTANCEOF_ARRAY, fn.getParameterRef(0), arrayClass, fn.getParameterRef(1));
            fn.add(new Ret(result));
            mb.addFunction(fn);
        }
        return new FunctionRef(fnName, t.getFunctionType());
    }

    private FunctionRef createCheckcastArray(Checkcast t) {
        String fnName = Symbols.arraycheckcastSymbol(t.getTarget());
        if (!mb.hasSymbol(fnName)) {
            Function fn = new FunctionBuilder(t).name(fnName).linkage(weak).build();
            Value arrayClass = callLdcArray(fn, t.getTarget());
            Value result = call(fn, BC_CHECKCAST_ARRAY, fn.getParameterRef(0), arrayClass, fn.getParameterRef(1));
            fn.add(new Ret(result));
            mb.addFunction(fn);
        }
        return new FunctionRef(fnName, t.getFunctionType());
    }

    private FunctionRef createAnewarray(Anewarray t) {
        String fnName = Symbols.anewarraySymbol(t.getTarget());
        if (!mb.hasSymbol(fnName)) {
            Function fn = new FunctionBuilder(t).name(fnName).linkage(weak).build();
            Value arrayClass = callLdcArray(fn, t.getTarget());
            Value result = call(fn, BC_NEW_OBJECT_ARRAY, fn.getParameterRef(0), 
                  fn.getParameterRef(1), arrayClass);
            fn.add(new Ret(result));
            mb.addFunction(fn);
        }
        return new FunctionRef(fnName, t.getFunctionType());
    }

    private FunctionRef createMultianewarray(Multianewarray t) {
        String fnName = Symbols.multianewarraySymbol(t.getTarget());
        if (!mb.hasSymbol(fnName)) {
            Function fn = new FunctionBuilder(t).name(fnName).linkage(weak).build();
            Value arrayClass = callLdcArray(fn, t.getTarget());
            Value result = call(fn, BC_NEW_MULTI_ARRAY, fn.getParameterRef(0), 
                  fn.getParameterRef(1), fn.getParameterRef(2), arrayClass);
            fn.add(new Ret(result));
            mb.addFunction(fn);
        }
        return new FunctionRef(fnName, t.getFunctionType());
    }

    private boolean checkClassExists(Function f, Trampoline t) {
        String targetClassName = t.getTarget();
        if (isArray(targetClassName)) {
            if (isPrimitiveBaseType(targetClassName)) {
                return true;
            }
            targetClassName = getBaseType(targetClassName);
        }
        
        Clazz target = config.getClazzes().load(targetClassName);
        if (target != null) {
            Clazz caller = config.getClazzes().load(t.getCallingClass());
            // If caller is in the bootclasspath it only sees classes in the bootclasspath
            if (!caller.isInBootClasspath() || target.isInBootClasspath()) {
                return true;
            }
        }
        
        call(f, BC_THROW_NO_CLASS_DEF_FOUND_ERROR, f.getParameterRef(0), 
                mb.getString(t.getTarget()));
        f.add(new Unreachable());
        return false;
    }

    private boolean checkClassAccessible(Function f, Trampoline t) {
        Clazz caller = config.getClazzes().load(t.getCallingClass());
        
        String targetClassName = t.getTarget();
        if (isArray(targetClassName)) {
            if (isPrimitiveBaseType(targetClassName)) {
                return true;
            }
            targetClassName = getBaseType(targetClassName);
        }
        Clazz target = config.getClazzes().load(targetClassName);
        if (Access.checkClassAccessible(target, caller)) {
            return true;
        }
        throwIllegalAccessError(f, ILLEGAL_ACCESS_ERROR_CLASS, 
                target, caller);
        f.add(new Unreachable());
        return false;
    }

    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) {
                // The runtime class type is not available. Classloading will fail earlier so let's
                // 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;
    }
    
    private void throwNoSuchMethodError(Function f, Invoke invoke) {
        call(f, BC_THROW_NO_SUCH_METHOD_ERROR, f.getParameterRef(0), 
                mb.getString(String.format(NO_SUCH_METHOD_ERROR, 
                        invoke.getTarget().replace('/', '.'), 
                        invoke.getMethodName(), invoke.getMethodDesc())));
        f.add(new Unreachable());
    }
    
    private void throwNoSuchFieldError(Function f, FieldAccessor accessor) {
        call(f, BC_THROW_NO_SUCH_FIELD_ERROR, f.getParameterRef(0), 
                mb.getString(String.format(NO_SUCH_FIELD_ERROR, 
                        accessor.getTarget().replace('/', '.'), 
                        accessor.getFieldName())));
        f.add(new Unreachable());
    }
    
    private void throwIncompatibleChangeError(Function f, String message, Object ... args) {
        call(f, BC_THROW_INCOMPATIBLE_CLASS_CHANGE_ERROR, f.getParameterRef(0), 
                mb.getString(String.format(message, args)));
        f.add(new Unreachable());
    }
    
    private void throwIllegalAccessError(Function f, String message, Object ... args) {
        call(f, BC_THROW_ILLEGAL_ACCESS_ERROR, f.getParameterRef(0), 
                mb.getString(String.format(message, args)));
        f.add(new Unreachable());
    }
    
    private SootField resolveField(Function f, FieldAccessor t) {
        SootClass target = config.getClazzes().load(t.getTarget()).getSootClass();
        String name = t.getFieldName();
        String desc = t.getFieldDesc();
        SootField field = resolveField(target, name, desc);
        if (field == null) {
            throwNoSuchFieldError(f, t);
            return null;
        }
        
        if (!field.isStatic() && t.isStatic()) {
            throwIncompatibleChangeError(f, EXPECTED_STATIC_FIELD, 
                    field.getDeclaringClass(), t.getFieldName());
            return null;
        }
        if (field.isStatic() && !t.isStatic()) {
            throwIncompatibleChangeError(f, EXPECTED_NON_STATIC_FIELD, 
                    field.getDeclaringClass(), t.getFieldName());
            return null;
        }
        return field;
    }
    
    private SootField resolveField(SootClass clazz, String name, String desc) {
        if (clazz != null && !clazz.isPhantom()) {
            SootField field = getField(clazz, name, desc);
            if (field != null) {
                return field;
            }
            for (SootClass interfaze : clazz.getInterfaces()) {
                field = resolveField(interfaze, name, desc);
                if (field != null) {
                    return field;
                }
            }
            if (!clazz.isInterface() && clazz.hasSuperclass()) {
                return resolveField(clazz.getSuperclass(), name, desc);
            }
        }
        return null;
    }
    
    private SootMethod resolveMethod(Function f, Invoke t) {
        SootClass target = config.getClazzes().load(t.getTarget()).getSootClass();
        String name = t.getMethodName();
        String desc = t.getMethodDesc();
        if ("".equals(name) && t instanceof Invokespecial) {
            SootMethod method = getMethod(target, name, desc);
            if (method != null) {
                return method;
            }
        }
        if ("".equals(name) || "".equals(name)) {
            // This is not part of method resolution but we 
            // need to handle it somehow.
            throwNoSuchMethodError(f, t);
            return null;
        }
        SootMethod method = resolveMethod(target, name, desc);
        if (method == null) {
            throwNoSuchMethodError(f, t);
            return null;
        }
        if (t.isStatic() && !method.isStatic()) {
            throwIncompatibleChangeError(f, EXPECTED_STATIC_METHOD, 
                    target, name, desc);
            return null;
        } else if (!t.isStatic() && method.isStatic()) {
            throwIncompatibleChangeError(f, EXPECTED_NON_STATIC_METHOD, 
                    target, name, desc);
            return null;
        }        
        return method;
    }
    
    private SootMethod resolveMethod(SootClass clazz, String name, String desc) {
        if (clazz != null && !clazz.isPhantom()) {
            SootMethod method = getMethod(clazz, name, desc);
            if (method != null) {
                return method;
            }
            if (name.equals("sizeOf") && isStruct(clazz)) {
                method = new SootMethod("sizeOf", Collections.EMPTY_LIST, IntType.v(), 
                        Modifier.PUBLIC | Modifier.STATIC);
                method.setDeclaringClass(clazz);
                method.setDeclared(true);
                return method;
            }

            SootClass c = !clazz.isInterface() && clazz.hasSuperclass() ? clazz.getSuperclass() : null;
            while (c != null) {
                method = getMethod(c, name, desc);
                if (method != null) {
                    return method;
                }
                c = !c.isInterface() && c.hasSuperclass() ? c.getSuperclass() : null;
            }

            c = clazz;
            while (c != null) {
                for (SootClass interfaze : c.getInterfaces()) {
                    method = resolveInterfaceMethod(interfaze, name, desc);
                    if (method != null) {
                        return method;
                    }
                }
                c = !c.isInterface() && c.hasSuperclass() ? c.getSuperclass() : null;
            }
        }
        return null;
    }
    
    private SootMethod resolveInterfaceMethod(Function f, Invokeinterface t) {
        SootClass target = config.getClazzes().load(t.getTarget()).getSootClass();
        String name = t.getMethodName();
        String desc = t.getMethodDesc();
        if (!target.isInterface()) {
            throwIncompatibleChangeError(f, EXPECTED_INTERFACE_BUT_FOUND_CLASS, target);
            return null;
        }
        if ("".equals(name) || "".equals(name)) {
            // This is not part of interface method resolution but we 
            // need to handle it somehow.
            throwNoSuchMethodError(f, t);
            return null;
        }
        SootMethod method = resolveInterfaceMethod(target, name, desc);
        if (method == null) {
            SootClass javaLangObject = config.getClazzes().load("java/lang/Object").getSootClass();
            method = getMethod(javaLangObject, name, desc);
        }
        if (method == null) {
            throwNoSuchMethodError(f, t);
            return null;
        }
        if (method.isStatic()) {
            throwIncompatibleChangeError(f, EXPECTED_NON_STATIC_METHOD, 
                    target, name, desc);
            return null;
        }
        return method;
    }
    
    private SootMethod resolveInterfaceMethod(SootClass clazz, String name, String desc) {
        if (clazz != null && !clazz.isPhantom()) {
            SootMethod method = getMethod(clazz, name, desc);
            if (method != null) {
                return method;
            }

            for (SootClass interfaze : clazz.getInterfaces()) {
                method = resolveInterfaceMethod(interfaze, name, desc);
                if (method != null) {
                    return method;
                }
            }
        }
        return null;
    }
    
    private SootField getField(SootClass clazz, String name, String desc) {
        for (SootField f : clazz.getFields()) {
            if (name.equals(f.getName()) && desc.equals(getDescriptor(f))) {
                return f;
            }
        }
        return null;
    }
    
    private SootMethod getMethod(SootClass clazz, String name, String desc) {
        for (SootMethod m : clazz.getMethods()) {
            if (name.equals(m.getName()) && desc.equals(getDescriptor(m))) {
                return m;
            }
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy