summaryrefslogtreecommitdiff
path: root/deps/v8/build/android/bytecode/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/build/android/bytecode/java/org')
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/AssertionEnablerClassAdapter.java109
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java293
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java167
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomClassLoaderClassWriter.java51
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomResourcesClassAdapter.java302
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/SplitCompatClassAdapter.java149
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java83
-rw-r--r--deps/v8/build/android/bytecode/java/org/chromium/bytecode/TypeUtils.java87
8 files changed, 1241 insertions, 0 deletions
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/AssertionEnablerClassAdapter.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/AssertionEnablerClassAdapter.java
new file mode 100644
index 0000000000..0a903a60f9
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/AssertionEnablerClassAdapter.java
@@ -0,0 +1,109 @@
+// Copyright 2017 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.chromium.bytecode.TypeUtils.ASSERTION_ERROR;
+import static org.chromium.bytecode.TypeUtils.BUILD_HOOKS;
+import static org.chromium.bytecode.TypeUtils.VOID;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * An ClassVisitor for replacing Java ASSERT statements with a function by modifying Java bytecode.
+ *
+ * We do this in two steps, first step is to enable assert.
+ * Following bytecode is generated for each class with ASSERT statements:
+ * 0: ldc #8 // class CLASSNAME
+ * 2: invokevirtual #9 // Method java/lang/Class.desiredAssertionStatus:()Z
+ * 5: ifne 12
+ * 8: iconst_1
+ * 9: goto 13
+ * 12: iconst_0
+ * 13: putstatic #2 // Field $assertionsDisabled:Z
+ * Replaces line #13 to the following:
+ * 13: pop
+ * Consequently, $assertionsDisabled is assigned the default value FALSE.
+ * This is done in the first if statement in overridden visitFieldInsn. We do this per per-assert.
+ *
+ * Second step is to replace assert statement with a function:
+ * The followed instructions are generated by a java assert statement:
+ * getstatic #3 // Field $assertionsDisabled:Z
+ * ifne 118 // Jump to instruction as if assertion if not enabled
+ * ...
+ * ifne 19
+ * new #4 // class java/lang/AssertionError
+ * dup
+ * ldc #5 // String (don't have this line if no assert message given)
+ * invokespecial #6 // Method java/lang/AssertionError.
+ * athrow
+ * Replace athrow with:
+ * invokestatic #7 // Method org/chromium/base/JavaExceptionReporter.assertFailureHandler
+ * goto 118
+ * JavaExceptionReporter.assertFailureHandler is a function that handles the AssertionError,
+ * 118 is the instruction to execute as if assertion if not enabled.
+ */
+class AssertionEnablerClassAdapter extends ClassVisitor {
+ AssertionEnablerClassAdapter(ClassVisitor visitor) {
+ super(Opcodes.ASM5, visitor);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, String desc,
+ String signature, String[] exceptions) {
+ return new RewriteAssertMethodVisitor(
+ Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ static class RewriteAssertMethodVisitor extends MethodVisitor {
+ static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled";
+ static final String INSERT_INSTRUCTION_NAME = "assertFailureHandler";
+ static final String INSERT_INSTRUCTION_DESC =
+ TypeUtils.getMethodDescriptor(VOID, ASSERTION_ERROR);
+ static final boolean INSERT_INSTRUCTION_ITF = false;
+
+ boolean mStartLoadingAssert;
+ Label mGotoLabel;
+
+ public RewriteAssertMethodVisitor(int api, MethodVisitor mv) {
+ super(api, mv);
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ if (opcode == Opcodes.PUTSTATIC && name.equals(ASSERTION_DISABLED_NAME)) {
+ super.visitInsn(Opcodes.POP); // enable assert
+ } else if (opcode == Opcodes.GETSTATIC && name.equals(ASSERTION_DISABLED_NAME)) {
+ mStartLoadingAssert = true;
+ super.visitFieldInsn(opcode, owner, name, desc);
+ } else {
+ super.visitFieldInsn(opcode, owner, name, desc);
+ }
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ if (mStartLoadingAssert && opcode == Opcodes.IFNE && mGotoLabel == null) {
+ mGotoLabel = label;
+ }
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ if (!mStartLoadingAssert || opcode != Opcodes.ATHROW) {
+ super.visitInsn(opcode);
+ } else {
+ super.visitMethodInsn(Opcodes.INVOKESTATIC, BUILD_HOOKS, INSERT_INSTRUCTION_NAME,
+ INSERT_INSTRUCTION_DESC, INSERT_INSTRUCTION_ITF);
+ super.visitJumpInsn(Opcodes.GOTO, mGotoLabel);
+ mStartLoadingAssert = false;
+ mGotoLabel = null;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java
new file mode 100644
index 0000000000..37dc192d81
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java
@@ -0,0 +1,293 @@
+// Copyright 2017 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.ClassWriter.COMPUTE_FRAMES;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Java application that takes in an input jar, performs a series of bytecode transformations,
+ * and generates an output jar.
+ *
+ * Two types of transformations are performed:
+ * 1) Enabling assertions via {@link AssertionEnablerClassAdapter}
+ * 2) Providing support for custom resources via {@link CustomResourcesClassAdapter}
+ */
+class ByteCodeProcessor {
+ private static final String CLASS_FILE_SUFFIX = ".class";
+ private static final String TEMPORARY_FILE_SUFFIX = ".temp";
+ private static final int BUFFER_SIZE = 16384;
+ private static boolean sVerbose;
+ private static boolean sIsPrebuilt;
+ private static boolean sShouldAssert;
+ private static boolean sShouldUseCustomResources;
+ private static boolean sShouldUseThreadAnnotations;
+ private static boolean sShouldCheckClassPath;
+ private static ClassLoader sDirectClassPathClassLoader;
+ private static ClassLoader sFullClassPathClassLoader;
+ private static Set<String> sFullClassPathJarPaths;
+ private static Set<String> sSplitCompatClassNames;
+ private static ClassPathValidator sValidator;
+
+ private static class EntryDataPair {
+ private final ZipEntry mEntry;
+ private final byte[] mData;
+
+ private EntryDataPair(ZipEntry mEntry, byte[] mData) {
+ this.mEntry = mEntry;
+ this.mData = mData;
+ }
+
+ private static EntryDataPair create(String zipPath, byte[] data) {
+ ZipEntry entry = new ZipEntry(zipPath);
+ entry.setMethod(ZipEntry.STORED);
+ entry.setTime(0);
+ entry.setSize(data.length);
+ CRC32 crc = new CRC32();
+ crc.update(data);
+ entry.setCrc(crc.getValue());
+ return new EntryDataPair(entry, data);
+ }
+ }
+
+ private static EntryDataPair processEntry(ZipEntry entry, byte[] data)
+ throws ClassPathValidator.ClassNotLoadedException {
+ // Copy all non-.class files to the output jar.
+ if (entry.isDirectory() || !entry.getName().endsWith(CLASS_FILE_SUFFIX)) {
+ return new EntryDataPair(entry, data);
+ }
+
+ ClassReader reader = new ClassReader(data);
+
+ if (sShouldCheckClassPath) {
+ sValidator.validateClassPathsAndOutput(reader, sDirectClassPathClassLoader,
+ sFullClassPathClassLoader, sFullClassPathJarPaths, sIsPrebuilt, sVerbose);
+ }
+
+ ClassWriter writer;
+ if (sShouldUseCustomResources) {
+ // Use the COMPUTE_FRAMES flag to have asm figure out the stack map frames.
+ // This is necessary because GCMBaseIntentService in android_gcm_java contains
+ // incorrect stack map frames. This option slows down processing time by 2x.
+ writer = new CustomClassLoaderClassWriter(
+ sFullClassPathClassLoader, reader, COMPUTE_FRAMES);
+ } else {
+ writer = new ClassWriter(reader, 0);
+ }
+ ClassVisitor chain = writer;
+ /* DEBUGGING:
+ To see the bytecode for a specific class:
+ if (entry.getName().contains("YourClassName")) {
+ chain = new TraceClassVisitor(chain, new PrintWriter(System.out));
+ }
+ To see objectweb.asm code that will generate bytecode for a given class:
+ java -cp "third_party/ow2_asm/lib/asm-5.0.1.jar:third_party/ow2_asm/lib/"\
+ "asm-util-5.0.1.jar:out/Debug/lib.java/jar_containing_yourclass.jar" \
+ org.objectweb.asm.util.ASMifier org.package.YourClassName
+ */
+ if (sShouldUseThreadAnnotations) {
+ chain = new ThreadAssertionClassAdapter(chain);
+ }
+ if (sShouldAssert) {
+ chain = new AssertionEnablerClassAdapter(chain);
+ }
+ if (sShouldUseCustomResources) {
+ chain = new CustomResourcesClassAdapter(
+ chain, reader.getClassName(), reader.getSuperName(), sFullClassPathClassLoader);
+ }
+ if (!sSplitCompatClassNames.isEmpty()) {
+ chain = new SplitCompatClassAdapter(
+ chain, sSplitCompatClassNames, sFullClassPathClassLoader);
+ }
+ reader.accept(chain, 0);
+ byte[] patchedByteCode = writer.toByteArray();
+ return EntryDataPair.create(entry.getName(), patchedByteCode);
+ }
+
+ private static void process(String inputJarPath, String outputJarPath)
+ throws ClassPathValidator.ClassNotLoadedException, ExecutionException,
+ InterruptedException {
+ String tempJarPath = outputJarPath + TEMPORARY_FILE_SUFFIX;
+ ExecutorService executorService =
+ Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+ try (ZipInputStream inputStream = new ZipInputStream(
+ new BufferedInputStream(new FileInputStream(inputJarPath)));
+ ZipOutputStream tempStream = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(tempJarPath)))) {
+ List<Future<EntryDataPair>> list = new ArrayList<>();
+ while (true) {
+ ZipEntry entry = inputStream.getNextEntry();
+ if (entry == null) {
+ break;
+ }
+ byte[] data = readAllBytes(inputStream);
+ list.add(executorService.submit(() -> processEntry(entry, data)));
+ }
+ executorService.shutdown(); // This is essential in order to avoid waiting infinitely.
+ // Write the zip file entries in order to preserve determinism.
+ for (Future<EntryDataPair> futurePair : list) {
+ EntryDataPair pair = futurePair.get();
+ tempStream.putNextEntry(pair.mEntry);
+ tempStream.write(pair.mData);
+ tempStream.closeEntry();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ Path src = Paths.get(tempJarPath);
+ Path dest = Paths.get(outputJarPath);
+ Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException ioException) {
+ throw new RuntimeException(ioException);
+ }
+
+ if (sValidator.hasErrors()) {
+ System.err.println("Direct classpath is incomplete. To fix, add deps on the "
+ + "GN target(s) that provide:");
+ for (Map.Entry<String, Map<String, Set<String>>> entry :
+ sValidator.getErrors().entrySet()) {
+ printValidationError(System.err, entry.getKey(), entry.getValue());
+ }
+ System.exit(1);
+ }
+ }
+
+ private static void printValidationError(
+ PrintStream out, String jarName, Map<String, Set<String>> missingClasses) {
+ out.print(" * ");
+ out.println(jarName);
+ int i = 0;
+ final int numErrorsPerJar = 2;
+ // The list of missing classes is non-exhaustive because each class that fails to validate
+ // reports only the first missing class.
+ for (Map.Entry<String, Set<String>> entry : missingClasses.entrySet()) {
+ String missingClass = entry.getKey();
+ Set<String> filesThatNeededIt = entry.getValue();
+ out.print(" * ");
+ if (i == numErrorsPerJar) {
+ out.print(String.format("And %d more...", missingClasses.size() - numErrorsPerJar));
+ break;
+ }
+ out.print(missingClass.replace('/', '.'));
+ out.print(" (needed by ");
+ out.print(filesThatNeededIt.iterator().next().replace('/', '.'));
+ if (filesThatNeededIt.size() > 1) {
+ out.print(String.format(" and %d more", filesThatNeededIt.size() - 1));
+ }
+ out.println(")");
+ i++;
+ }
+ }
+
+ private static byte[] readAllBytes(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int numRead = 0;
+ byte[] data = new byte[BUFFER_SIZE];
+ while ((numRead = inputStream.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, numRead);
+ }
+ return buffer.toByteArray();
+ }
+
+ /**
+ * Loads a list of jars and returns a ClassLoader capable of loading all classes found in the
+ * given jars.
+ */
+ static ClassLoader loadJars(Collection<String> paths) {
+ URL[] jarUrls = new URL[paths.size()];
+ int i = 0;
+ for (String path : paths) {
+ try {
+ jarUrls[i++] = new File(path).toURI().toURL();
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new URLClassLoader(jarUrls);
+ }
+
+ public static void main(String[] args) throws ClassPathValidator.ClassNotLoadedException,
+ ExecutionException, InterruptedException {
+ // Invoke this script using //build/android/gyp/bytecode_processor.py
+ int currIndex = 0;
+ String inputJarPath = args[currIndex++];
+ String outputJarPath = args[currIndex++];
+ sVerbose = args[currIndex++].equals("--verbose");
+ sIsPrebuilt = args[currIndex++].equals("--is-prebuilt");
+ sShouldAssert = args[currIndex++].equals("--enable-assert");
+ sShouldUseCustomResources = args[currIndex++].equals("--enable-custom-resources");
+ sShouldUseThreadAnnotations = args[currIndex++].equals("--enable-thread-annotations");
+ sShouldCheckClassPath = args[currIndex++].equals("--enable-check-class-path");
+ int sdkJarsLength = Integer.parseInt(args[currIndex++]);
+ List<String> sdkJarPaths =
+ Arrays.asList(Arrays.copyOfRange(args, currIndex, currIndex + sdkJarsLength));
+ currIndex += sdkJarsLength;
+
+ int directJarsLength = Integer.parseInt(args[currIndex++]);
+ ArrayList<String> directClassPathJarPaths = new ArrayList<>();
+ directClassPathJarPaths.add(inputJarPath);
+ directClassPathJarPaths.addAll(sdkJarPaths);
+ directClassPathJarPaths.addAll(
+ Arrays.asList(Arrays.copyOfRange(args, currIndex, currIndex + directJarsLength)));
+ currIndex += directJarsLength;
+ sDirectClassPathClassLoader = loadJars(directClassPathJarPaths);
+
+ // Load list of class names that need to be fixed.
+ int splitCompatClassNamesLength = Integer.parseInt(args[currIndex++]);
+ sSplitCompatClassNames = new HashSet<>();
+ sSplitCompatClassNames.addAll(Arrays.asList(
+ Arrays.copyOfRange(args, currIndex, currIndex + splitCompatClassNamesLength)));
+ currIndex += splitCompatClassNamesLength;
+
+ // Load all jars that are on the classpath for the input jar for analyzing class hierarchy.
+ sFullClassPathJarPaths = new HashSet<>();
+ sFullClassPathJarPaths.clear();
+ sFullClassPathJarPaths.add(inputJarPath);
+ sFullClassPathJarPaths.addAll(sdkJarPaths);
+ sFullClassPathJarPaths.addAll(
+ Arrays.asList(Arrays.copyOfRange(args, currIndex, args.length)));
+ sFullClassPathClassLoader = loadJars(sFullClassPathJarPaths);
+ sFullClassPathJarPaths.removeAll(directClassPathJarPaths);
+
+ sValidator = new ClassPathValidator();
+ process(inputJarPath, outputJarPath);
+ }
+}
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java
new file mode 100644
index 0000000000..c35c3f6820
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java
@@ -0,0 +1,167 @@
+// Copyright 2018 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 org.objectweb.asm.ClassReader;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+/**
+ * Checks classpaths (given as ClassLoaders) by reading the constant pool of the class file and
+ * attempting to load every referenced class. If there are some that are unable to be found, it
+ * stores a helpful error message if it knows where it might find them, and exits the program if it
+ * can't find the class with any given classpath.
+ */
+public class ClassPathValidator {
+ // Map of missing .jar -> Missing class -> Classes that failed.
+ // TreeMap so that error messages have sorted list of jars.
+ private final Map<String, Map<String, Set<String>>> mErrors = new TreeMap<>();
+
+ static class ClassNotLoadedException extends ClassNotFoundException {
+ private final String mClassName;
+
+ ClassNotLoadedException(String className, Throwable ex) {
+ super("Couldn't load " + className, ex);
+ mClassName = className;
+ }
+
+ public String getClassName() {
+ return mClassName;
+ }
+ }
+
+ private static void printAndQuit(ClassNotLoadedException e, ClassReader classReader,
+ boolean verbose) throws ClassNotLoadedException {
+ System.err.println("Class \"" + e.getClassName()
+ + "\" not found on any classpath. Used by class \"" + classReader.getClassName()
+ + "\"");
+ if (verbose) {
+ throw e;
+ }
+ System.exit(1);
+ }
+
+ private static void validateClass(ClassLoader classLoader, String className)
+ throws ClassNotLoadedException {
+ if (className.startsWith("[")) {
+ // Dealing with an array type which isn't encoded nicely in the constant pool.
+ // For example, [[Lorg/chromium/Class$1;
+ className = className.substring(className.lastIndexOf('[') + 1);
+ if (className.charAt(0) == 'L' && className.endsWith(";")) {
+ className = className.substring(1, className.length() - 1);
+ } else {
+ // Bailing out if we have an non-class array type.
+ // This could be something like [B
+ return;
+ }
+ }
+ if (className.matches(".*\\bR(\\$\\w+)?$")) {
+ // Resources in R.java files are not expected to be valid at this stage in the build.
+ return;
+ }
+ if (className.matches("^libcore\\b.*")) {
+ // libcore exists on devices, but is not included in the Android sdk as it is a private
+ // API.
+ return;
+ }
+ try {
+ classLoader.loadClass(className.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw new ClassNotLoadedException(className, e);
+ } catch (NoClassDefFoundError e) {
+ // We assume that this is caused by another class that is not going to able to be
+ // loaded, so we will skip this and let that class fail with ClassNotFoundException.
+ }
+ }
+
+ /**
+ * Given a .class file, see if every class referenced in the main class' constant pool can be
+ * loaded by the given ClassLoader.
+ *
+ * @param classReader .class file interface for reading the constant pool.
+ * @param classLoader classpath you wish to validate.
+ * @throws ClassNotLoadedException thrown if it can't load a certain class.
+ */
+ private static void validateClassPath(ClassReader classReader, ClassLoader classLoader)
+ throws ClassNotLoadedException {
+ char[] charBuffer = new char[classReader.getMaxStringLength()];
+ // According to the Java spec, the constant pool is indexed from 1 to constant_pool_count -
+ // 1. See https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
+ for (int i = 1; i < classReader.getItemCount(); i++) {
+ int offset = classReader.getItem(i);
+ // Class entries correspond to 7 in the constant pool
+ // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4
+ if (offset > 0 && classReader.readByte(offset - 1) == 7) {
+ validateClass(classLoader, classReader.readUTF8(offset, charBuffer));
+ }
+ }
+ }
+
+ public void validateClassPathsAndOutput(ClassReader classReader,
+ ClassLoader directClassPathClassLoader, ClassLoader fullClassPathClassLoader,
+ Collection<String> jarsOnlyInFullClassPath, boolean isPrebuilt, boolean verbose)
+ throws ClassNotLoadedException {
+ if (isPrebuilt) {
+ // Prebuilts only need transitive dependencies checked, not direct dependencies.
+ try {
+ validateClassPath(classReader, fullClassPathClassLoader);
+ } catch (ClassNotLoadedException e) {
+ printAndQuit(e, classReader, verbose);
+ }
+ } else {
+ try {
+ validateClassPath(classReader, directClassPathClassLoader);
+ } catch (ClassNotLoadedException e) {
+ try {
+ validateClass(fullClassPathClassLoader, e.getClassName());
+ } catch (ClassNotLoadedException d) {
+ printAndQuit(d, classReader, verbose);
+ }
+ if (verbose) {
+ System.err.println("Class \"" + e.getClassName()
+ + "\" not found in direct dependencies,"
+ + " but found in indirect dependiences.");
+ }
+ // Iterating through all jars that are in the full classpath but not the direct
+ // classpath to find which one provides the class we are looking for.
+ for (String jarPath : jarsOnlyInFullClassPath) {
+ try {
+ ClassLoader smallLoader =
+ ByteCodeProcessor.loadJars(Collections.singletonList(jarPath));
+ validateClass(smallLoader, e.getClassName());
+ Map<String, Set<String>> failedClassesByMissingClass = mErrors.get(jarPath);
+ if (failedClassesByMissingClass == null) {
+ // TreeMap so that error messages have sorted list of classes.
+ failedClassesByMissingClass = new TreeMap<>();
+ mErrors.put(jarPath, failedClassesByMissingClass);
+ }
+ Set<String> failedClasses =
+ failedClassesByMissingClass.get(e.getClassName());
+ if (failedClasses == null) {
+ failedClasses = new TreeSet<>();
+ failedClassesByMissingClass.put(e.getClassName(), failedClasses);
+ }
+ failedClasses.add(classReader.getClassName());
+ break;
+ } catch (ClassNotLoadedException f) {
+ }
+ }
+ }
+ }
+ }
+
+ public Map<String, Map<String, Set<String>>> getErrors() {
+ return mErrors;
+ }
+
+ public boolean hasErrors() {
+ return !mErrors.isEmpty();
+ }
+}
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomClassLoaderClassWriter.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomClassLoaderClassWriter.java
new file mode 100644
index 0000000000..3a52c85d56
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomClassLoaderClassWriter.java
@@ -0,0 +1,51 @@
+// Copyright 2017 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 org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * A ClassWriter that uses a custom class loader.
+ */
+class CustomClassLoaderClassWriter extends ClassWriter {
+ private ClassLoader mClassLoader;
+
+ public CustomClassLoaderClassWriter(ClassLoader classLoader, ClassReader reader, int flags) {
+ super(reader, flags);
+ this.mClassLoader = classLoader;
+ }
+
+ /**
+ * The only modifications from the org.objectweb.asm.ClassWriter implementations is that this
+ * method is final and it uses a custom ClassLoader.
+ *
+ * See https://github.com/llbit/ow2-asm/blob/master/src/org/objectweb/asm/ClassWriter.java.
+ */
+ @Override
+ protected final String getCommonSuperClass(final String type1, final String type2) {
+ Class<?> c, d;
+ try {
+ c = Class.forName(type1.replace('/', '.'), false, mClassLoader);
+ d = Class.forName(type2.replace('/', '.'), false, mClassLoader);
+ } catch (Exception e) {
+ throw new RuntimeException(e.toString());
+ }
+ if (c.isAssignableFrom(d)) {
+ return type1;
+ }
+ if (d.isAssignableFrom(c)) {
+ return type2;
+ }
+ if (c.isInterface() || d.isInterface()) {
+ return "java/lang/Object";
+ } else {
+ do {
+ c = c.getSuperclass();
+ } while (!c.isAssignableFrom(d));
+ return c.getName().replace('.', '/');
+ }
+ }
+}
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomResourcesClassAdapter.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomResourcesClassAdapter.java
new file mode 100644
index 0000000000..96205b8815
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/CustomResourcesClassAdapter.java
@@ -0,0 +1,302 @@
+// Copyright 2017 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_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACONST_NULL;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.ARETURN;
+import static org.objectweb.asm.Opcodes.ASM5;
+import static org.objectweb.asm.Opcodes.BIPUSH;
+import static org.objectweb.asm.Opcodes.GETSTATIC;
+import static org.objectweb.asm.Opcodes.IFNE;
+import static org.objectweb.asm.Opcodes.IF_ICMPGE;
+import static org.objectweb.asm.Opcodes.ILOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.RETURN;
+
+import static org.chromium.bytecode.TypeUtils.ASSET_MANAGER;
+import static org.chromium.bytecode.TypeUtils.BOOLEAN;
+import static org.chromium.bytecode.TypeUtils.BUILD_HOOKS_ANDROID;
+import static org.chromium.bytecode.TypeUtils.CONFIGURATION;
+import static org.chromium.bytecode.TypeUtils.CONTEXT;
+import static org.chromium.bytecode.TypeUtils.CONTEXT_WRAPPER;
+import static org.chromium.bytecode.TypeUtils.INT;
+import static org.chromium.bytecode.TypeUtils.RESOURCES;
+import static org.chromium.bytecode.TypeUtils.STRING;
+import static org.chromium.bytecode.TypeUtils.THEME;
+import static org.chromium.bytecode.TypeUtils.VOID;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A ClassVisitor for providing access to custom resources via BuildHooksAndroid.
+ *
+ * The goal of this class is to provide hooks into all places where android resources
+ * are available so that they can be modified before use. This is done by rewriting the bytecode
+ * for all callable definitions of certain Context methods, specifically:
+ * - getResources
+ * - getAssets
+ * - getTheme
+ * - setTheme
+ * - createConfigurationContext
+ *
+ * Only classes at the framework boundary are rewritten since presumably all other indirect Context
+ * subclasses will end up calling their respective super methods (i.e. we bytecode rewrite
+ * BaseChromiumApplication since it extends Application, but not ContentApplication since it
+ * extends a non-framework subclass.
+ */
+class CustomResourcesClassAdapter extends ClassVisitor {
+ private static final String IS_ENABLED_METHOD = "isEnabled";
+ private static final String IS_ENABLED_DESCRIPTOR = TypeUtils.getMethodDescriptor(BOOLEAN);
+ // Cached since this is used so often.
+ private static final String GET_IDENTIFIER_DESCRIPTOR =
+ TypeUtils.getMethodDescriptor(INT, STRING, STRING, STRING);
+
+ // Existing methods are more difficult to handle, and not currently needed.
+ private static final List<String> PROHIBITED_METHODS = Arrays.asList(
+ TypeUtils.getMethodSignature("getResources", RESOURCES),
+ TypeUtils.getMethodSignature("getAssets", ASSET_MANAGER),
+ TypeUtils.getMethodSignature("getTheme", THEME),
+ TypeUtils.getMethodSignature("createConfigurationContext", CONTEXT, CONFIGURATION),
+ TypeUtils.getMethodSignature("setTheme", VOID, INT));
+
+ private boolean mShouldTransform;
+ private String mClassName;
+ private String mSuperClassName;
+ private ClassLoader mClassLoader;
+
+ CustomResourcesClassAdapter(ClassVisitor visitor, String className, String superClassName,
+ ClassLoader classLoader) {
+ super(ASM5, visitor);
+ this.mClassName = className;
+ this.mSuperClassName = superClassName;
+ this.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);
+ mShouldTransform = shouldTransform();
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, String desc,
+ String signature, String[] exceptions) {
+ if (mShouldTransform) {
+ String methodSignature = name + desc;
+ if (requiresModifyingExisting(methodSignature)) {
+ throw new RuntimeException("Rewriting existing methods not supported: " + mClassName
+ + "#" + methodSignature);
+ }
+ }
+ return new RewriteGetIdentifierMethodVisitor(
+ super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ @Override
+ public void visitEnd() {
+ if (mShouldTransform) {
+ delegateCreateConfigurationContext();
+ delegateSetTheme();
+ delegateGet("getAssets", ASSET_MANAGER);
+ delegateGet("getTheme", THEME);
+ delegateGet("getResources", RESOURCES);
+ }
+ super.visitEnd();
+ }
+
+ private boolean requiresModifyingExisting(String methodDescriptor) {
+ return PROHIBITED_METHODS.contains(methodDescriptor);
+ }
+
+ private boolean shouldTransform() {
+ if (!isDescendantOfContext()) {
+ return false;
+ }
+ if (!superClassIsFrameworkClass()) {
+ return false;
+ }
+ return !superClassIsContextWrapper();
+ }
+
+ private boolean superClassIsFrameworkClass() {
+ return loadClass(mSuperClassName).getProtectionDomain().toString().contains("android.jar");
+ }
+
+ private boolean isDescendantOfContext() {
+ return isSubClass(mClassName, CONTEXT);
+ }
+
+ private boolean superClassIsContextWrapper() {
+ return mSuperClassName.equals(CONTEXT_WRAPPER);
+ }
+
+ private boolean isSubClass(String candidate, String other) {
+ Class<?> candidateClazz = loadClass(candidate);
+ Class<?> parentClazz = loadClass(other);
+ return parentClazz.isAssignableFrom(candidateClazz);
+ }
+
+ private Class<?> loadClass(String className) {
+ try {
+ return mClassLoader.loadClass(className.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Remaps Resources.getIdentifier() method calls to use BuildHooksAndroid.
+ *
+ * resourceObj.getIdentifier(String, String, String) becomes:
+ * BuildHooksAndroid.getIdentifier(resourceObj, String, String, String);
+ */
+ private static final class RewriteGetIdentifierMethodVisitor extends MethodVisitor {
+ RewriteGetIdentifierMethodVisitor(MethodVisitor mv) {
+ super(ASM5, mv);
+ }
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String desc, boolean itf) {
+ String methodName = "getIdentifier";
+ if (opcode == INVOKEVIRTUAL && owner.equals(RESOURCES) && name.equals(methodName)
+ && desc.equals(GET_IDENTIFIER_DESCRIPTOR)) {
+ super.visitMethodInsn(INVOKESTATIC, BUILD_HOOKS_ANDROID, methodName,
+ TypeUtils.getMethodDescriptor(INT, RESOURCES, STRING, STRING, STRING), itf);
+ } else {
+ super.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+ }
+ }
+
+ /**
+ * Generates:
+ *
+ * <pre>
+ * public Context createConfigurationContext(Configuration configuration) {
+ * // createConfigurationContext does not exist before API level 17.
+ * if (Build.VERSION.SDK_INT < 17) return null;
+ * if (!BuildHooksAndroid.isEnabled()) return super.createConfigurationContext(configuration);
+ * return BuildHooksAndroid.createConfigurationContext(
+ * super.createConfigurationContext(configuration));
+ * }
+ * </pre>
+ * }
+ */
+ private void delegateCreateConfigurationContext() {
+ String methodName = "createConfigurationContext";
+ String methodDescriptor = TypeUtils.getMethodDescriptor(CONTEXT, CONFIGURATION);
+ MethodVisitor mv = super.visitMethod(ACC_PUBLIC, methodName, methodDescriptor, null, null);
+ mv.visitCode();
+ mv.visitFieldInsn(GETSTATIC, "android/os/Build$VERSION", "SDK_INT", INT);
+ mv.visitIntInsn(BIPUSH, 17);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IF_ICMPGE, l0);
+ mv.visitInsn(ACONST_NULL);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitMethodInsn(
+ INVOKESTATIC, BUILD_HOOKS_ANDROID, IS_ENABLED_METHOD, IS_ENABLED_DESCRIPTOR, false);
+ Label l1 = new Label();
+ mv.visitJumpInsn(IFNE, l1);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, mSuperClassName, methodName, methodDescriptor, false);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l1);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, mSuperClassName, methodName, methodDescriptor, false);
+ mv.visitMethodInsn(INVOKESTATIC, BUILD_HOOKS_ANDROID, methodName,
+ TypeUtils.getMethodDescriptor(CONTEXT, CONTEXT), false);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+
+ /**
+ * Generates:
+ *
+ * <pre>
+ * public void setTheme(int theme) {
+ * if (!BuildHooksAndroid.isEnabled()) {
+ * super.setTheme(theme);
+ * return;
+ * }
+ * BuildHooksAndroid.setTheme(this, theme);
+ * }
+ * </pre>
+ */
+ private void delegateSetTheme() {
+ String methodName = "setTheme";
+ String methodDescriptor = TypeUtils.getMethodDescriptor(VOID, INT);
+ String buildHooksMethodDescriptor = TypeUtils.getMethodDescriptor(VOID, CONTEXT, INT);
+ MethodVisitor mv = super.visitMethod(ACC_PUBLIC, methodName, methodDescriptor, null, null);
+ mv.visitCode();
+ mv.visitMethodInsn(
+ INVOKESTATIC, BUILD_HOOKS_ANDROID, IS_ENABLED_METHOD, IS_ENABLED_DESCRIPTOR, false);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFNE, l0);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitMethodInsn(INVOKESPECIAL, mSuperClassName, methodName, methodDescriptor, false);
+ mv.visitInsn(RETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ILOAD, 1);
+ mv.visitMethodInsn(
+ INVOKESTATIC, BUILD_HOOKS_ANDROID, methodName, buildHooksMethodDescriptor, false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+ }
+
+ /**
+ * Generates:
+ *
+ * <pre>
+ * public returnType methodName() {
+ * if (!BuildHooksAndroid.isEnabled()) return super.methodName();
+ * return BuildHooksAndroid.methodName(this);
+ * }
+ * </pre>
+ */
+ private void delegateGet(String methodName, String returnType) {
+ String getMethodDescriptor = TypeUtils.getMethodDescriptor(returnType);
+ String buildHooksGetMethodDescriptor = TypeUtils.getMethodDescriptor(returnType, CONTEXT);
+ MethodVisitor mv =
+ super.visitMethod(ACC_PUBLIC, methodName, getMethodDescriptor, null, null);
+ mv.visitCode();
+ mv.visitMethodInsn(
+ INVOKESTATIC, BUILD_HOOKS_ANDROID, IS_ENABLED_METHOD, IS_ENABLED_DESCRIPTOR, false);
+ Label l0 = new Label();
+ mv.visitJumpInsn(IFNE, l0);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, mSuperClassName, methodName, getMethodDescriptor, false);
+ mv.visitInsn(ARETURN);
+ mv.visitLabel(l0);
+ mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESTATIC, BUILD_HOOKS_ANDROID, methodName,
+ buildHooksGetMethodDescriptor, false);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+}
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);
+ }
+ }
+}
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java
new file mode 100644
index 0000000000..3f50b25f3e
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java
@@ -0,0 +1,83 @@
+// Copyright 2018 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.ASM5;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A ClassVisitor which adds calls to
+ * {@link org.chromium.base.ThreadUtils}'s assertOnUiThread/assertOnBackgroundThread when the
+ * corresponding {@link android.support.annotation.UiThread} or
+ * {@link android.support.annotation.WorkerThread} annotations are present. The function calls
+ * are placed at the start of the method.
+ */
+class ThreadAssertionClassAdapter extends ClassVisitor {
+ private static final String THREAD_UTILS_DESCRIPTOR = "org/chromium/base/ThreadUtils";
+ private static final String THREAD_UTILS_SIGNATURE = "()V";
+ private static final String UI_THREAD_ANNOTATION_DESCRIPTOR =
+ "Landroid/support/annotation/UiThread;";
+ private static final String WORKER_THREAD_ANNOTATION_DESCRIPTOR =
+ "Landroid/support/annotation/WorkerThread;";
+
+ ThreadAssertionClassAdapter(ClassVisitor visitor) {
+ super(ASM5, visitor);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(final int access, final String name, String desc,
+ String signature, String[] exceptions) {
+ return new AddAssertMethodVisitor(
+ super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ private static class AddAssertMethodVisitor extends MethodVisitor {
+ String mAssertMethodName = "";
+
+ AddAssertMethodVisitor(MethodVisitor mv) {
+ super(ASM5, mv);
+ }
+
+ /**
+ * Call for annotations on the method. Checks if the annotation is @UiThread
+ * or @WorkerThread, and if so will set the mAssertMethodName property to the name of the
+ * method to call in order to assert that a method is running on the intented thread.
+ *
+ * @param descriptor Annotation descriptor containing its name and package.
+ */
+ @Override
+ public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
+ switch (descriptor) {
+ case UI_THREAD_ANNOTATION_DESCRIPTOR:
+ mAssertMethodName = "assertOnUiThread";
+ break;
+ case WORKER_THREAD_ANNOTATION_DESCRIPTOR:
+ mAssertMethodName = "assertOnBackgroundThread";
+ break;
+ default:
+ break;
+ }
+
+ return super.visitAnnotation(descriptor, visible);
+ }
+
+ /**
+ * Called to start visiting code. Will also insert the assertOnXThread methods at the start
+ * of the method if needed.
+ */
+ @Override
+ public void visitCode() {
+ super.visitCode();
+ if (!mAssertMethodName.equals("")) {
+ visitMethodInsn(INVOKESTATIC, THREAD_UTILS_DESCRIPTOR, mAssertMethodName,
+ THREAD_UTILS_SIGNATURE, false);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/deps/v8/build/android/bytecode/java/org/chromium/bytecode/TypeUtils.java b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/TypeUtils.java
new file mode 100644
index 0000000000..ed2dc2dc24
--- /dev/null
+++ b/deps/v8/build/android/bytecode/java/org/chromium/bytecode/TypeUtils.java
@@ -0,0 +1,87 @@
+// Copyright 2017 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 org.objectweb.asm.Type;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility methods for accessing {@link Type}s Strings.
+ *
+ * Useful definitions to keep in mind when using this class:
+ * Internal name - The fully qualified name for a type with dots replaced by slashes. Not really
+ * relevant for primitive types.
+ * Type descriptor - Single letters for primitive types, "L" + internal name + ";" for class types.
+ *
+ * The methods in this class accept internal names or primitive type descriptors.
+ */
+class TypeUtils {
+ static final String ASSERTION_ERROR = "java/lang/AssertionError";
+ static final String ASSET_MANAGER = "android/content/res/AssetManager";
+ static final String BUILD_HOOKS = "org/chromium/build/BuildHooks";
+ static final String BUILD_HOOKS_ANDROID = "org/chromium/build/BuildHooksAndroid";
+ static final String CONFIGURATION = "android/content/res/Configuration";
+ static final String CONTEXT = "android/content/Context";
+ static final String CONTEXT_WRAPPER = "android/content/ContextWrapper";
+ static final String RESOURCES = "android/content/res/Resources";
+ static final String STRING = "java/lang/String";
+ static final String THEME = "android/content/res/Resources$Theme";
+
+ static final String BOOLEAN = "Z";
+ static final String INT = "I";
+ static final String VOID = "V";
+ private static final Map<String, Type> PRIMITIVE_DESCRIPTORS;
+ static {
+ PRIMITIVE_DESCRIPTORS = new HashMap<>();
+ PRIMITIVE_DESCRIPTORS.put(Type.BOOLEAN_TYPE.toString(), Type.BOOLEAN_TYPE);
+ PRIMITIVE_DESCRIPTORS.put(Type.INT_TYPE.toString(), Type.INT_TYPE);
+ PRIMITIVE_DESCRIPTORS.put(Type.VOID_TYPE.toString(), Type.VOID_TYPE);
+ }
+
+ /**
+ * Returns the full method signature with internal names.
+ *
+ * @param methodName Name of the method (ex. "getResources").
+ * @param returnType Internal name for the return type.
+ * @param argumentTypes List of internal names for argument types.
+ * @return String representation of the method signature.
+ */
+ static String getMethodSignature(
+ String methodName, String returnType, String... argumentTypes) {
+ return methodName + getMethodDescriptor(returnType, argumentTypes);
+ }
+
+ /**
+ * Builds a method descriptor suitable for use with {@link org.objectweb.asm.MethodVisitor}.
+ *
+ * @param returnType Internal name for the return type of the method (primitive or class).
+ * @param argumentTypes Internal names for the argument types (primitive or class).
+ * @return The generated method descriptor.
+ */
+ static String getMethodDescriptor(String returnType, String... argumentTypes) {
+ Type[] typedArguments = new Type[argumentTypes.length];
+ for (int i = 0; i < argumentTypes.length; ++i) {
+ // Argument list should be empty in this case, not V (void).
+ assert !Type.VOID_TYPE.toString().equals(argumentTypes[i]);
+ typedArguments[i] = convert(argumentTypes[i]);
+ }
+ return Type.getMethodDescriptor(convert(returnType), typedArguments);
+ }
+
+ /**
+ * Converts an internal name for a type to a {@link Type}.
+ *
+ * @param type Internal name for a type (primitive or class).
+ * @return The resulting Type.
+ */
+ private static Type convert(String type) {
+ if (PRIMITIVE_DESCRIPTORS.containsKey(type)) {
+ return PRIMITIVE_DESCRIPTORS.get(type);
+ }
+ return Type.getObjectType(type);
+ }
+}