15fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver/*
25fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * Copyright 2013, Google Inc.
35fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * All rights reserved.
45fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *
55fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * Redistribution and use in source and binary forms, with or without
65fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * modification, are permitted provided that the following conditions are
75fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * met:
85fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *
95fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *     * Redistributions of source code must retain the above copyright
105fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * notice, this list of conditions and the following disclaimer.
115fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *     * Redistributions in binary form must reproduce the above
125fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * copyright notice, this list of conditions and the following disclaimer
135fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * in the documentation and/or other materials provided with the
145fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * distribution.
155fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *     * Neither the name of Google Inc. nor the names of its
165fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * contributors may be used to endorse or promote products derived from
175fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * this software without specific prior written permission.
185fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver *
195fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
205fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
215fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
225fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
235fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
245fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
255fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
265fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
275fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
285fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
295fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
305fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver */
315fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
325fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverpackage org.jf.dexlib2.analysis;
335fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
345fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport com.google.common.io.Files;
355fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.iface.ClassDef;
365fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.iface.Method;
375fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.iface.instruction.InlineIndexInstruction;
385fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.immutable.ImmutableMethod;
395fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.immutable.ImmutableMethodParameter;
405fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
415fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport org.jf.dexlib2.immutable.util.ParamUtil;
425fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
435fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport javax.annotation.Nonnull;
445fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.io.*;
455fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.nio.charset.Charset;
465fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.util.ArrayList;
475fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.util.List;
485fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.util.regex.Matcher;
495fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverimport java.util.regex.Pattern;
505fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
515fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruverpublic class CustomInlineMethodResolver extends InlineMethodResolver {
525fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    @Nonnull private final ClassPath classPath;
535fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    @Nonnull private final Method[] inlineMethods;
545fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
555fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    public CustomInlineMethodResolver(@Nonnull ClassPath classPath, @Nonnull String inlineTable) {
565fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        this.classPath = classPath;
575fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
585fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        StringReader reader = new StringReader(inlineTable);
595fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        List<String> lines = new ArrayList<String>();
605fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
615fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        BufferedReader br = new BufferedReader(reader);
625fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
635fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        try {
645fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            String line = br.readLine();
655fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
665fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            while (line != null) {
675fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                if (line.length() > 0) {
685fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                    lines.add(line);
695fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                }
705fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
715fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                line = br.readLine();
725fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            }
735fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        } catch (IOException ex) {
745fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            throw new RuntimeException("Error while parsing inline table", ex);
755fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
765fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
775fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        inlineMethods = new Method[lines.size()];
785fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
795fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        for (int i=0; i<inlineMethods.length; i++) {
805fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            inlineMethods[i] = parseAndResolveInlineMethod(lines.get(i));
815fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
825fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    }
835fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
845fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    public CustomInlineMethodResolver(@Nonnull ClassPath classPath, @Nonnull File inlineTable) throws IOException {
855fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        this(classPath, Files.toString(inlineTable, Charset.forName("UTF-8")));
865fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    }
875fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
885fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    @Override
895fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    @Nonnull
905fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    public Method resolveExecuteInline(@Nonnull AnalyzedInstruction analyzedInstruction) {
915fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        InlineIndexInstruction instruction = (InlineIndexInstruction)analyzedInstruction.instruction;
925fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        int methodIndex = instruction.getInlineIndex();
935fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
945fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        if (methodIndex < 0 || methodIndex >= inlineMethods.length) {
955fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            throw new RuntimeException("Invalid method index: " + methodIndex);
965fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
975fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        return inlineMethods[methodIndex];
985fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    }
995fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1005fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    private static final Pattern longMethodPattern = Pattern.compile("(L[^;]+;)->([^(]+)\\(([^)]*)\\)(.+)");
1015fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1025fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    @Nonnull
1035fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    private Method parseAndResolveInlineMethod(@Nonnull String inlineMethod) {
1045fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        Matcher m = longMethodPattern.matcher(inlineMethod);
1055fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        if (!m.matches()) {
1065fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            assert false;
1075fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            throw new RuntimeException("Invalid method descriptor: " + inlineMethod);
1085fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
1095fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1105fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        String className = m.group(1);
1115fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        String methodName = m.group(2);
1125fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        Iterable<ImmutableMethodParameter> methodParams = ParamUtil.parseParamString(m.group(3));
1135fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        String methodRet = m.group(4);
1145fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        ImmutableMethodReference methodRef = new ImmutableMethodReference(className, methodName, methodParams,
1155fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                methodRet);
1165fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1175fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        int accessFlags = 0;
1185fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1195fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        boolean resolved = false;
1205fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        TypeProto typeProto = classPath.getClass(className);
1215fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        if (typeProto instanceof ClassProto) {
1225fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            ClassDef classDef = ((ClassProto)typeProto).getClassDef();
1235fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            for (Method method: classDef.getMethods()) {
1245fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                if (method.equals(methodRef)) {
1255fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                    resolved = true;
1265fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                    accessFlags = method.getAccessFlags();
1275fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                    break;
1285fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver                }
1295fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            }
1305fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
1315fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1325fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        if (!resolved) {
1335fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver            throw new RuntimeException("Cannot resolve inline method: " + inlineMethod);
1345fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        }
1355fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver
1365fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver        return new ImmutableMethod(className, methodName, methodParams, methodRet, accessFlags, null, null);
1375fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver    }
1385fa302678ce3a8e08fa8d2e8dbc5424781e751a6Ben Gruver}
139