/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dodola.anole.lib;
import com.android.build.api.transform.Transform;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Files;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Implementation of the {@link Transform} to run the byte code enhancement logic on compiled
* classes in order to support runtime hot swapping.
*/
public class InstantRunTransform {
/**
* Use asm to generate a concrete subclass of the AppPathLoaderImpl class.
* It only implements one method :
* String[] getPatchedClasses();
* <p>
* The method is supposed to return the list of classes that were patched in this iteration.
* This will be used by the InstantRun runtime to load all patched classes and register them
* as overrides on the original classes.2 class files.
*
* @param patchFileContents list of patched class names.
* @param outputDir output directory where to generate the .class file in.
* @return the generated .class files
*/
public static File writePatchFileContents(
List<String> patchFileContents, File outputDir) {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER,
IncrementalVisitor.APP_PATCHES_LOADER_IMPL, null,
IncrementalVisitor.ABSTRACT_PATCHES_LOADER_IMPL, null);
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
IncrementalVisitor.ABSTRACT_PATCHES_LOADER_IMPL,
"<init>", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC,
"getPatchedClasses", "()[Ljava/lang/String;", null, null);
mv.visitCode();
mv.visitIntInsn(Opcodes.BIPUSH, patchFileContents.size());
mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/String");
for (int index = 0; index < patchFileContents.size(); index++) {
mv.visitInsn(Opcodes.DUP);
mv.visitIntInsn(Opcodes.BIPUSH, index);
mv.visitLdcInsn(patchFileContents.get(index));
mv.visitInsn(Opcodes.AASTORE);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(4, 1);
mv.visitEnd();
}
cw.visitEnd();
byte[] classBytes = cw.toByteArray();
File outputFile = new File(outputDir, IncrementalVisitor.APP_PATCHES_LOADER_IMPL + ".class");
try {
Files.createParentDirs(outputFile);
Files.write(classBytes, outputFile);
// add the files to the list of files to be processed by subsequent tasks.
return outputFile;
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}