summaryrefslogtreecommitdiff
path: root/deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java')
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java149
1 files changed, 149 insertions, 0 deletions
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java
new file mode 100644
index 0000000000..8d6ae69483
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java
@@ -0,0 +1,149 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.bytecode;
+
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+import static org.chromium.bytecode.TypeUtils.CONTEXT;
+import static org.chromium.bytecode.TypeUtils.VOID;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Set;
+
+/**
+ * A ClassVisitor for injecting ModuleInstaller.initActivity(activity) method call
+ * into Activity's attachBaseContext() method. The goal is to eventually invoke
+ * SplitCompat.install() method if running with the binary that has bundle support
+ * enabled. This needs to happen for activities that were not built with SplitCompat
+ * support.
+ */
+class SplitCompatClassAdapter extends ClassVisitor {
+ private static final String ANDROID_APP_ACTIVITY_CLASS_NAME = "android/app/Activity";
+ private static final String ATTACH_BASE_CONTEXT_METHOD_NAME = "attachBaseContext";
+ private static final String ATTACH_BASE_CONTEXT_DESCRIPTOR =
+ TypeUtils.getMethodDescriptor(VOID, CONTEXT);
+
+ private static final String MODULE_INSTALLER_CLASS_NAME =
+ "org/chromium/components/module_installer/ModuleInstaller";
+ private static final String INIT_ACTIVITY_METHOD_NAME = "initActivity";
+ private static final String INIT_ACTIVITY_DESCRIPTOR =
+ TypeUtils.getMethodDescriptor(VOID, CONTEXT);
+
+ private boolean mShouldTransform;
+
+ private Set<String> mClassNames;
+
+ private ClassLoader mClassLoader;
+
+ /**
+ * Creates instance of SplitCompatClassAdapter.
+ *
+ * @param visitor
+ * @param classNames Names of classes into which the attachBaseContext method will be
+ * injected. Currently, we'll only consider classes for bytecode rewriting only if
+ * they inherit directly from android.app.Activity & not already contain
+ * attachBaseContext method.
+ * @param classLoader
+ */
+ SplitCompatClassAdapter(ClassVisitor visitor, Set<String> classNames, ClassLoader classLoader) {
+ super(Opcodes.ASM5, visitor);
+
+ mShouldTransform = false;
+ mClassNames = classNames;
+ mClassLoader = classLoader;
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, signature, superName, interfaces);
+
+ if (mClassNames.contains(name)) {
+ if (!isSubclassOfActivity(name)) {
+ throw new RuntimeException(name
+ + " should be transformed but does not inherit from android.app.Activity");
+ }
+
+ mShouldTransform = true;
+ }
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ // Check if current method matches attachBaseContext & we're supposed to emit code - if so,
+ // fail.
+ if (mShouldTransform && name.equals(ATTACH_BASE_CONTEXT_METHOD_NAME)) {
+ throw new RuntimeException(ATTACH_BASE_CONTEXT_METHOD_NAME + " method already exists");
+ }
+
+ return super.visitMethod(access, name, descriptor, signature, exceptions);
+ }
+
+ @Override
+ public void visitEnd() {
+ if (mShouldTransform) {
+ // If we reached this place, it means we're rewriting a class that inherits from
+ // Activity and there was no exception thrown due to existence of attachBaseContext
+ // method - emit code.
+ emitAttachBaseContext();
+ }
+
+ super.visitEnd();
+ }
+
+ /**
+ * Generates:
+ *
+ * <pre>
+ * protected void attachBaseContext(Context base) {
+ * super.attachBaseContext(base);
+ * ModuleInstaller.initActivity(this);
+ * }
+ * </pre>
+ */
+ private void emitAttachBaseContext() {
+ MethodVisitor mv = super.visitMethod(ACC_PROTECTED, ATTACH_BASE_CONTEXT_METHOD_NAME,
+ ATTACH_BASE_CONTEXT_DESCRIPTOR, null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0); // load "this" on stack
+ mv.visitVarInsn(ALOAD, 1); // load first method parameter on stack (Context)
+ mv.visitMethodInsn(INVOKESPECIAL, ANDROID_APP_ACTIVITY_CLASS_NAME,
+ ATTACH_BASE_CONTEXT_METHOD_NAME,
+ ATTACH_BASE_CONTEXT_DESCRIPTOR); // invoke super's attach base context
+ mv.visitVarInsn(ALOAD, 0); // load "this" on stack
+ mv.visitMethodInsn(INVOKESTATIC, MODULE_INSTALLER_CLASS_NAME, INIT_ACTIVITY_METHOD_NAME,
+ INIT_ACTIVITY_DESCRIPTOR);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2); // max stack size - 2, max locals - 2
+ mv.visitEnd();
+ }
+
+ /**
+ * Checks whether passed in class inherits from android.app.Activity.
+ * @param name Name of the class to be checked.
+ * @return true if class inherits from android.app.Activity, false otherwise.
+ */
+ private boolean isSubclassOfActivity(String name) {
+ Class<?> activityClass = loadClass(ANDROID_APP_ACTIVITY_CLASS_NAME);
+ Class<?> candidateClass = loadClass(name);
+ return activityClass.isAssignableFrom(candidateClass);
+ }
+
+ private Class<?> loadClass(String className) {
+ try {
+ return mClassLoader.loadClass(className.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}