| /* |
| * Copyright 2016, Google Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| package org.jf.smali; |
| |
| import com.google.common.collect.Lists; |
| import org.antlr.runtime.CommonTokenStream; |
| import org.antlr.runtime.Token; |
| import org.antlr.runtime.TokenSource; |
| import org.antlr.runtime.tree.CommonTree; |
| import org.antlr.runtime.tree.CommonTreeNodeStream; |
| import org.jf.dexlib2.Opcodes; |
| import org.jf.dexlib2.writer.builder.DexBuilder; |
| import org.jf.dexlib2.writer.io.FileDataStore; |
| |
| import javax.annotation.Nonnull; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.Arrays; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.*; |
| |
| public class Smali { |
| |
| /** |
| * Assemble the specified files, using the given options |
| * |
| * @param options a SmaliOptions object with the options to run smali with |
| * @param input The files/directories to process |
| * @return true if assembly completed with no errors, or false if errors were encountered |
| */ |
| public static boolean assemble(final SmaliOptions options, String... input) throws IOException { |
| return assemble(options, Arrays.asList(input)); |
| } |
| |
| /** |
| * Assemble the specified files, using the given options |
| * |
| * @param options a SmaliOptions object with the options to run smali with |
| * @param input The files/directories to process |
| * @return true if assembly completed with no errors, or false if errors were encountered |
| */ |
| public static boolean assemble(final SmaliOptions options, List<String> input) throws IOException { |
| LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>(); |
| |
| for (String fileToProcess: input) { |
| File argFile = new File(fileToProcess); |
| |
| if (!argFile.exists()) { |
| throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\""); |
| } |
| |
| if (argFile.isDirectory()) { |
| getSmaliFilesInDir(argFile, filesToProcessSet); |
| } else if (argFile.isFile()) { |
| filesToProcessSet.add(argFile); |
| } |
| } |
| |
| boolean errors = false; |
| |
| final DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel)); |
| |
| ExecutorService executor = Executors.newFixedThreadPool(options.jobs); |
| List<Future<Boolean>> tasks = Lists.newArrayList(); |
| |
| for (final File file: filesToProcessSet) { |
| tasks.add(executor.submit(new Callable<Boolean>() { |
| @Override public Boolean call() throws Exception { |
| return assembleSmaliFile(file, dexBuilder, options); |
| } |
| })); |
| } |
| |
| for (Future<Boolean> task: tasks) { |
| while(true) { |
| try { |
| try { |
| if (!task.get()) { |
| errors = true; |
| } |
| } catch (ExecutionException ex) { |
| throw new RuntimeException(ex); |
| } |
| } catch (InterruptedException ex) { |
| continue; |
| } |
| break; |
| } |
| } |
| |
| executor.shutdown(); |
| |
| if (errors) { |
| return false; |
| } |
| |
| dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile))); |
| |
| return true; |
| } |
| |
| private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) { |
| File[] files = dir.listFiles(); |
| if (files != null) { |
| for(File file: files) { |
| if (file.isDirectory()) { |
| getSmaliFilesInDir(file, smaliFiles); |
| } else if (file.getName().endsWith(".smali")) { |
| smaliFiles.add(file); |
| } |
| } |
| } |
| } |
| |
| private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options) |
| throws Exception { |
| FileInputStream fis = null; |
| try { |
| fis = new FileInputStream(smaliFile); |
| InputStreamReader reader = new InputStreamReader(fis, "UTF-8"); |
| |
| LexerErrorInterface lexer = new smaliFlexLexer(reader); |
| ((smaliFlexLexer)lexer).setSourceFile(smaliFile); |
| CommonTokenStream tokens = new CommonTokenStream((TokenSource)lexer); |
| |
| if (options.printTokens) { |
| tokens.getTokens(); |
| |
| for (int i=0; i<tokens.size(); i++) { |
| Token token = tokens.get(i); |
| if (token.getChannel() == smaliParser.HIDDEN) { |
| continue; |
| } |
| |
| System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText()); |
| } |
| |
| System.out.flush(); |
| } |
| |
| smaliParser parser = new smaliParser(tokens); |
| parser.setVerboseErrors(options.verboseErrors); |
| parser.setAllowOdex(options.allowOdexOpcodes); |
| parser.setApiLevel(options.apiLevel); |
| |
| smaliParser.smali_file_return result = parser.smali_file(); |
| |
| if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) { |
| return false; |
| } |
| |
| CommonTree t = result.getTree(); |
| |
| CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t); |
| treeStream.setTokenStream(tokens); |
| |
| if (options.printTokens) { |
| System.out.println(t.toStringTree()); |
| } |
| |
| smaliTreeWalker dexGen = new smaliTreeWalker(treeStream); |
| dexGen.setApiLevel(options.apiLevel); |
| |
| dexGen.setVerboseErrors(options.verboseErrors); |
| dexGen.setDexBuilder(dexBuilder); |
| dexGen.smali_file(); |
| |
| return dexGen.getNumberOfSyntaxErrors() == 0; |
| } finally { |
| if (fis != null) { |
| fis.close(); |
| } |
| } |
| } |
| } |