diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/build/android/bytecode | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-85-based.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/build/android/bytecode')
4 files changed, 163 insertions, 291 deletions
diff --git a/chromium/build/android/bytecode/BUILD.gn b/chromium/build/android/bytecode/BUILD.gn index f9e1baf2604..4d29aca9dbc 100644 --- a/chromium/build/android/bytecode/BUILD.gn +++ b/chromium/build/android/bytecode/BUILD.gn @@ -4,13 +4,10 @@ import("//build/config/android/rules.gni") -assert(current_toolchain == default_toolchain) - -java_binary("java_bytecode_rewriter") { +java_binary("bytecode_processor") { sources = [ "java/org/chromium/bytecode/ByteCodeProcessor.java", "java/org/chromium/bytecode/ClassPathValidator.java", - "java/org/chromium/bytecode/ThreadAssertionClassAdapter.java", "java/org/chromium/bytecode/TypeUtils.java", ] main_class = "org.chromium.bytecode.ByteCodeProcessor" @@ -18,5 +15,6 @@ java_binary("java_bytecode_rewriter") { "//third_party/android_deps:org_ow2_asm_asm_java", "//third_party/android_deps:org_ow2_asm_asm_util_java", ] - wrapper_script_name = "helper/java_bytecode_rewriter" + wrapper_script_name = "helper/bytecode_processor" + enable_bytecode_checks = false } diff --git a/chromium/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java b/chromium/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java index 636e094cf02..b767f4f0890 100644 --- a/chromium/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java +++ b/chromium/build/android/bytecode/java/org/chromium/bytecode/ByteCodeProcessor.java @@ -5,40 +5,29 @@ package org.chromium.bytecode; 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.HashMap; 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.concurrent.TimeUnit; 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 @@ -46,154 +35,55 @@ import java.util.zip.ZipOutputStream; */ 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 sShouldUseThreadAnnotations; - private static boolean sShouldCheckClassPath; private static ClassLoader sDirectClassPathClassLoader; private static ClassLoader sFullClassPathClassLoader; private static Set<String> sFullClassPathJarPaths; private static Set<String> sMissingClassesAllowlist; + private static Map<String, String> sJarToGnTarget; 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); - } - + private static Void processEntry(ZipEntry entry, byte[] data) { ClassReader reader = new ClassReader(data); - if (sShouldCheckClassPath) { - sValidator.validateClassPathsAndOutput(reader, sDirectClassPathClassLoader, - sFullClassPathClassLoader, sFullClassPathJarPaths, sIsPrebuilt, sVerbose, - sMissingClassesAllowlist); - } - - ClassWriter writer = new ClassWriter(reader, 0); - ClassVisitor chain = writer; - /* DEBUGGING: - To see objectweb.asm code that will generate bytecode for a given class: - - java -cp - "third_party/android_deps/libs/org_ow2_asm_asm/asm-7.0.jar:third_party/android_deps/libs/org_ow2_asm_asm_util/asm-util-7.0.jar:out/Debug/lib.java/jar_containing_yourclass.jar" - org.objectweb.asm.util.ASMifier org.package.YourClassName - - See this pdf for more details: https://asm.ow2.io/asm4-guide.pdf - - To see the bytecode for a specific class, uncomment this code with your class name: - - if (entry.getName().contains("YOUR_CLASS_NAME")) { - chain = new TraceClassVisitor(chain, new PrintWriter(System.out)); + if (sIsPrebuilt) { + sValidator.validateFullClassPath( + reader, sFullClassPathClassLoader, sMissingClassesAllowlist); + } else { + sValidator.validateDirectClassPath(reader, sDirectClassPathClassLoader, + sFullClassPathClassLoader, sFullClassPathJarPaths, sMissingClassesAllowlist, + sVerbose); } - */ - if (sShouldUseThreadAnnotations) { - chain = new ThreadAssertionClassAdapter(chain); - } - reader.accept(chain, 0); - byte[] patchedByteCode = writer.toByteArray(); - return EntryDataPair.create(entry.getName(), patchedByteCode); + return null; } - private static void process(String inputJarPath, String outputJarPath) - throws ClassPathValidator.ClassNotLoadedException, ExecutionException, - InterruptedException { - String tempJarPath = outputJarPath + TEMPORARY_FILE_SUFFIX; + private static void process(String gnTarget, String inputJarPath) + throws ExecutionException, InterruptedException { 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<>(); + new BufferedInputStream(new FileInputStream(inputJarPath)))) { while (true) { ZipEntry entry = inputStream.getNextEntry(); if (entry == null) { break; } byte[] data = readAllBytes(inputStream); - list.add(executorService.submit(() -> processEntry(entry, data))); + 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(); - } + executorService.awaitTermination(1, TimeUnit.HOURS); } 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()); - } + sValidator.printAll(gnTarget, sJarToGnTarget); 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; @@ -235,12 +125,10 @@ class ByteCodeProcessor { ExecutionException, InterruptedException { // Invoke this script using //build/android/gyp/bytecode_processor.py int currIndex = 0; + String gnTarget = args[currIndex++]; String inputJarPath = args[currIndex++]; - String outputJarPath = args[currIndex++]; sVerbose = args[currIndex++].equals("--verbose"); sIsPrebuilt = args[currIndex++].equals("--is-prebuilt"); - sShouldUseThreadAnnotations = args[currIndex++].equals("--enable-thread-annotations"); - sShouldCheckClassPath = args[currIndex++].equals("--enable-check-class-path"); sMissingClassesAllowlist = new HashSet<>(); currIndex = parseListArgument(args, currIndex, sMissingClassesAllowlist); @@ -254,19 +142,26 @@ class ByteCodeProcessor { currIndex = parseListArgument(args, currIndex, directClassPathJarPaths); sDirectClassPathClassLoader = loadJars(directClassPathJarPaths); + ArrayList<String> fullClassPathJarPaths = new ArrayList<>(); + currIndex = parseListArgument(args, currIndex, fullClassPathJarPaths); + ArrayList<String> gnTargets = new ArrayList<>(); + parseListArgument(args, currIndex, gnTargets); + sJarToGnTarget = new HashMap<>(); + assert fullClassPathJarPaths.size() == gnTargets.size(); + for (int i = 0; i < fullClassPathJarPaths.size(); ++i) { + sJarToGnTarget.put(fullClassPathJarPaths.get(i), gnTargets.get(i)); + } + // 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))); - + sFullClassPathJarPaths.addAll(fullClassPathJarPaths); sFullClassPathClassLoader = loadJars(sFullClassPathJarPaths); sFullClassPathJarPaths.removeAll(directClassPathJarPaths); sValidator = new ClassPathValidator(); - process(inputJarPath, outputJarPath); + process(gnTarget, inputJarPath); } } diff --git a/chromium/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java b/chromium/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java index f3ed501873d..ce1803fca8a 100644 --- a/chromium/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java +++ b/chromium/build/android/bytecode/java/org/chromium/bytecode/ClassPathValidator.java @@ -6,12 +6,14 @@ package org.chromium.bytecode; import org.objectweb.asm.ClassReader; +import java.io.PrintStream; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.function.Consumer; /** * Checks classpaths (given as ClassLoaders) by reading the constant pool of the class file and @@ -20,9 +22,18 @@ import java.util.TreeSet; * can't find the class with any given classpath. */ public class ClassPathValidator { + // Number of warnings to print. + private static final int MAX_MISSING_CLASS_WARNINGS = 4; + // Number of missing classes to show per missing jar. + private static final int MAX_ERRORS_PER_JAR = 2; // 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<>(); + private final Map<String, Map<String, Set<String>>> mDirectErrors = + Collections.synchronizedMap(new TreeMap<>()); + // Missing classes we only track the first one for each jar. + // Map of missingClass -> srcClass. + private final Map<String, String> mMissingClasses = + Collections.synchronizedMap(new TreeMap<>()); static class ClassNotLoadedException extends ClassNotFoundException { private final String mClassName; @@ -37,17 +48,6 @@ public class ClassPathValidator { } } - 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("[")) { @@ -87,10 +87,10 @@ public class ClassPathValidator { * * @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. + * @param errorConsumer Called for each missing class. */ - private static void validateClassPath(ClassReader classReader, ClassLoader classLoader) - throws ClassNotLoadedException { + private static void validateClassPath(ClassReader classReader, ClassLoader classLoader, + Consumer<ClassNotLoadedException> errorConsumer) { 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 @@ -99,73 +99,135 @@ public class ClassPathValidator { // 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)); + try { + validateClass(classLoader, classReader.readUTF8(offset, charBuffer)); + } catch (ClassNotLoadedException e) { + errorConsumer.accept(e); + } } } } - public void validateClassPathsAndOutput(ClassReader classReader, - ClassLoader directClassPathClassLoader, ClassLoader fullClassPathClassLoader, - Collection<String> jarsOnlyInFullClassPath, boolean isPrebuilt, boolean verbose, - Set<String> missingClassAllowlist) throws ClassNotLoadedException { - if (isPrebuilt) { - // Prebuilts only need transitive dependencies checked, not direct dependencies. + public void validateFullClassPath(ClassReader classReader, ClassLoader fullClassLoader, + Set<String> missingClassAllowlist) { + // Prebuilts only need transitive dependencies checked, not direct dependencies. + validateClassPath(classReader, fullClassLoader, (e) -> { + if (!missingClassAllowlist.contains(e.getClassName())) { + addMissingError(classReader.getClassName(), e.getClassName()); + } + }); + } + + public void validateDirectClassPath(ClassReader classReader, ClassLoader directClassLoader, + ClassLoader fullClassLoader, Collection<String> jarsOnlyInFullClassPath, + Set<String> missingClassAllowlist, boolean verbose) { + validateClassPath(classReader, directClassLoader, (e) -> { try { - validateClassPath(classReader, fullClassPathClassLoader); - } catch (ClassNotLoadedException e) { + validateClass(fullClassLoader, e.getClassName()); + } catch (ClassNotLoadedException d) { if (!missingClassAllowlist.contains(e.getClassName())) { - printAndQuit(e, classReader, verbose); + addMissingError(classReader.getClassName(), e.getClassName()); } + return; } - } else { - try { - validateClassPath(classReader, directClassPathClassLoader); - } catch (ClassNotLoadedException e) { + 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 { - validateClass(fullClassPathClassLoader, e.getClassName()); - } catch (ClassNotLoadedException d) { - if (!missingClassAllowlist.contains(d.getClassName())) { - 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) { - } + ClassLoader smallLoader = + ByteCodeProcessor.loadJars(Collections.singletonList(jarPath)); + validateClass(smallLoader, e.getClassName()); + addDirectError(jarPath, classReader.getClassName(), e.getClassName()); + break; + } catch (ClassNotLoadedException f) { } } - } + }); + } + + private void addMissingError(String srcClass, String missingClass) { + mMissingClasses.put(missingClass, srcClass); } - public Map<String, Map<String, Set<String>>> getErrors() { - return mErrors; + private void addDirectError(String jarPath, String srcClass, String missingClass) { + synchronized (mDirectErrors) { + Map<String, Set<String>> failedClassesByMissingClass = mDirectErrors.get(jarPath); + if (failedClassesByMissingClass == null) { + // TreeMap so that error messages have sorted list of classes. + failedClassesByMissingClass = new TreeMap<>(); + mDirectErrors.put(jarPath, failedClassesByMissingClass); + } + Set<String> failedClasses = failedClassesByMissingClass.get(missingClass); + if (failedClasses == null) { + failedClasses = new TreeSet<>(); + failedClassesByMissingClass.put(missingClass, failedClasses); + } + failedClasses.add(srcClass); + } } public boolean hasErrors() { - return !mErrors.isEmpty(); + return !mDirectErrors.isEmpty() || !mMissingClasses.isEmpty(); + } + + private static void printValidationError( + PrintStream out, String gnTarget, Map<String, Set<String>> missingClasses) { + out.print(" * "); + out.println(gnTarget); + int i = 0; + // 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 == MAX_ERRORS_PER_JAR) { + out.print(String.format( + "And %d more...", missingClasses.size() - MAX_ERRORS_PER_JAR)); + 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++; + } + } + + public void printAll(String gnTarget, Map<String, String> jarToGnTarget) { + String streamer = "============================="; + System.err.println(); + System.err.println(streamer + " Dependency Checks Failed " + streamer); + System.err.println("Target: " + gnTarget); + if (!mMissingClasses.isEmpty()) { + int i = 0; + for (Map.Entry<String, String> entry : mMissingClasses.entrySet()) { + if (++i > MAX_MISSING_CLASS_WARNINGS) { + System.err.println(String.format("... and %d more.", + mMissingClasses.size() - MAX_MISSING_CLASS_WARNINGS)); + break; + } + System.err.println(String.format( + "Class \"%s\" not found on any classpath. Used by class \"%s\"", + entry.getKey(), entry.getValue())); + } + System.err.println(); + } + if (!mDirectErrors.isEmpty()) { + System.err.println("Direct classpath is incomplete. To fix, add deps on:"); + for (Map.Entry<String, Map<String, Set<String>>> entry : mDirectErrors.entrySet()) { + printValidationError( + System.err, jarToGnTarget.get(entry.getKey()), entry.getValue()); + } + System.err.println(); + } } } diff --git a/chromium/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java b/chromium/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java deleted file mode 100644 index 0feae07aabf..00000000000 --- a/chromium/build/android/bytecode/java/org/chromium/bytecode/ThreadAssertionClassAdapter.java +++ /dev/null @@ -1,83 +0,0 @@ -// 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.ASM7; -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 androidx.annotation.UiThread} or - * {@link androidx.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(ASM7, 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(ASM7, 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 |