Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: build/android/java_assertion_enabler/java/org/chromium/javaassertionenabler/AssertionEnabler.java

Issue 2971063003: Handle Java assert using a customized function in base (Closed)
Patch Set: fix tests Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.javaassertionenabler; 5 package org.chromium.javaassertionenabler;
6 6
7 import org.objectweb.asm.ClassReader; 7 import org.objectweb.asm.ClassReader;
8 import org.objectweb.asm.ClassVisitor; 8 import org.objectweb.asm.ClassVisitor;
9 import org.objectweb.asm.ClassWriter; 9 import org.objectweb.asm.ClassWriter;
10 import org.objectweb.asm.Label;
10 import org.objectweb.asm.MethodVisitor; 11 import org.objectweb.asm.MethodVisitor;
11 import org.objectweb.asm.Opcodes; 12 import org.objectweb.asm.Opcodes;
12 13
13 import java.io.BufferedInputStream; 14 import java.io.BufferedInputStream;
14 import java.io.BufferedOutputStream; 15 import java.io.BufferedOutputStream;
15 import java.io.ByteArrayOutputStream; 16 import java.io.ByteArrayOutputStream;
16 import java.io.FileInputStream; 17 import java.io.FileInputStream;
17 import java.io.FileOutputStream; 18 import java.io.FileOutputStream;
18 import java.io.IOException; 19 import java.io.IOException;
19 import java.io.InputStream; 20 import java.io.InputStream;
20 import java.nio.file.Files; 21 import java.nio.file.Files;
21 import java.nio.file.Path; 22 import java.nio.file.Path;
22 import java.nio.file.Paths; 23 import java.nio.file.Paths;
23 import java.nio.file.StandardCopyOption; 24 import java.nio.file.StandardCopyOption;
24 import java.util.zip.ZipEntry; 25 import java.util.zip.ZipEntry;
25 import java.util.zip.ZipInputStream; 26 import java.util.zip.ZipInputStream;
26 import java.util.zip.ZipOutputStream; 27 import java.util.zip.ZipOutputStream;
27 28
28 /** 29 /**
29 * An application that enables Java ASSERT statements by modifying Java bytecode . It takes in a JAR 30 * An application that replace Java ASSERT statements with a function by modifyi ng Java bytecode. It
30 * file, modifies bytecode of classes that use ASSERT, and outputs the bytecode to a new JAR file. 31 * takes in a JAR file, modifies bytecode of classes that use ASSERT, and output s the bytecode to a
32 * new JAR file.
33 *
34 * We do this in two steps, first step is to enable assert.
35 * Following bytecode is generated for each class with ASSERT statements:
36 * 0: ldc #8 // class CLASSNAME
37 * 2: invokevirtual #9 // Method java/lang/Class.desiredAssertionStatus:()Z
38 * 5: ifne 12
39 * 8: iconst_1
40 * 9: goto 13
41 * 12: iconst_0
42 * 13: putstatic #2 // Field $assertionsDisabled:Z
43 * Replaces line #13 to the following:
44 * 13: pop
45 * Consequently, $assertionsDisabled is assigned the default value FALSE.
46 * This is done in the first if statement in overridden visitFieldInsn. We do th is per per-assert.
47 *
48 * Second step is to replace assert statement with a function:
49 * The followed instructions are generated by a java assert statement:
50 * getstatic #3 // Field $assertionsDisabled:Z
51 * ifne 118 // Jump to instruction as if assertion if not enabled
52 * ...
53 * ifne 19
54 * new #4 // class java/lang/AssertionError
55 * dup
56 * ldc #5 // String (don't have this line if no assert message giv en)
57 * invokespecial #6 // Method java/lang/AssertionError.
58 * athrow
59 * Replace athrow with:
60 * invokestatic #7 // Method org/chromium/base/JavaExceptionReporter.assert FailureHandler
61 * goto 118
62 * JavaExceptionReporter.assertFailureHandler is a function that handles the Ass ertionError,
63 * 118 is the instruction to execute as if assertion if not enabled.
31 */ 64 */
32 class AssertionEnabler { 65 class AssertionEnabler {
33 static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled";
34 static final String CLASS_FILE_SUFFIX = ".class"; 66 static final String CLASS_FILE_SUFFIX = ".class";
35 static final String STATIC_INITIALIZER_NAME = "<clinit>";
36 static final String TEMPORARY_FILE_SUFFIX = ".temp"; 67 static final String TEMPORARY_FILE_SUFFIX = ".temp";
37
38 static final int BUFFER_SIZE = 16384; 68 static final int BUFFER_SIZE = 16384;
39 69
40 static class AssertionEnablerVisitor extends ClassVisitor { 70 static class AssertionEnablerVisitor extends ClassVisitor {
41 AssertionEnablerVisitor(ClassWriter writer) { 71 AssertionEnablerVisitor(ClassWriter writer) {
42 super(Opcodes.ASM5, writer); 72 super(Opcodes.ASM5, writer);
43 } 73 }
44 74
45 @Override 75 @Override
46 public MethodVisitor visitMethod(final int access, final String name, St ring desc, 76 public MethodVisitor visitMethod(final int access, final String name, St ring desc,
47 String signature, String[] exceptions) { 77 String signature, String[] exceptions) {
48 // Patch static initializer. 78 return new RewriteAssertMethodVisitorWriter(
49 if ((access & Opcodes.ACC_STATIC) != 0 && name.equals(STATIC_INITIAL IZER_NAME)) { 79 Opcodes.ASM5, super.visitMethod(access, name, desc, signatur e, exceptions)) {};
50 return new MethodVisitor(Opcodes.ASM5,
51 super.visitMethod(access, name, desc, signature, excepti ons)) {
52 // The following bytecode is generated for each class with A SSERT statements:
53 // 0: ldc #8 // class CLASSNAME
54 // 2: invokevirtual #9 // Method java/lang/Class.desiredAsse rtionStatus:()Z
55 // 5: ifne 12
56 // 8: iconst_1
57 // 9: goto 13
58 // 12: iconst_0
59 // 13: putstatic #2 // Field $assertionsDisabled:Z
60 //
61 // This function replaces line #13 to the following:
62 // 13: pop
63 // Consequently, $assertionsDisabled is assigned the default value FALSE.
64 @Override
65 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
66 if (opcode == Opcodes.PUTSTATIC && name.equals(ASSERTION _DISABLED_NAME)) {
67 mv.visitInsn(Opcodes.POP);
68 } else {
69 super.visitFieldInsn(opcode, owner, name, desc);
70 }
71 }
72 };
73 }
74 return super.visitMethod(access, name, desc, signature, exceptions);
75 } 80 }
76 } 81 }
77 82
83 static class RewriteAssertMethodVisitorWriter extends MethodVisitor {
84 static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled";
85 static final String INSERT_INSTRUCTION_OWNER = "org/chromium/buildhooks/ BuildHooks";
86 static final String INSERT_INSTRUCTION_NAME = "assertFailureHandler";
87 static final String INSERT_INSTRUCTION_DESC = "(Ljava/lang/AssertionErro r;)V";
88 static final boolean INSERT_INSTRUCTION_ITF = false;
89
90 boolean mStartLoadingAssert;
91 Label mGotoLabel;
92
93 public RewriteAssertMethodVisitorWriter(int api, MethodVisitor mv) {
94 super(api, mv);
95 }
96
97 @Override
98 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
99 if (opcode == Opcodes.PUTSTATIC && name.equals(ASSERTION_DISABLED_NA ME)) {
100 super.visitInsn(Opcodes.POP); // enable assert
101 } else if (opcode == Opcodes.GETSTATIC && name.equals(ASSERTION_DISA BLED_NAME)) {
102 mStartLoadingAssert = true;
103 super.visitFieldInsn(opcode, owner, name, desc);
104 } else {
105 super.visitFieldInsn(opcode, owner, name, desc);
106 }
107 }
108
109 @Override
110 public void visitJumpInsn(int opcode, Label label) {
111 if (mStartLoadingAssert && opcode == Opcodes.IFNE && mGotoLabel == n ull) {
112 mGotoLabel = label;
113 }
114 super.visitJumpInsn(opcode, label);
115 }
116
117 @Override
118 public void visitInsn(int opcode) {
119 if (!mStartLoadingAssert || opcode != Opcodes.ATHROW) {
120 super.visitInsn(opcode);
121 } else {
122 super.visitMethodInsn(Opcodes.INVOKESTATIC, INSERT_INSTRUCTION_O WNER,
123 INSERT_INSTRUCTION_NAME, INSERT_INSTRUCTION_DESC, INSERT _INSTRUCTION_ITF);
124 super.visitJumpInsn(Opcodes.GOTO, mGotoLabel);
125 mStartLoadingAssert = false;
126 mGotoLabel = null;
127 }
128 }
129 }
130
78 static byte[] readAllBytes(InputStream inputStream) throws IOException { 131 static byte[] readAllBytes(InputStream inputStream) throws IOException {
79 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 132 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
80 int numRead = 0; 133 int numRead = 0;
81 byte[] data = new byte[BUFFER_SIZE]; 134 byte[] data = new byte[BUFFER_SIZE];
82 while ((numRead = inputStream.read(data, 0, data.length)) != -1) { 135 while ((numRead = inputStream.read(data, 0, data.length)) != -1) {
83 buffer.write(data, 0, numRead); 136 buffer.write(data, 0, numRead);
84 } 137 }
85
86 return buffer.toByteArray(); 138 return buffer.toByteArray();
87 } 139 }
88 140
89 static void enableAssertionInJar(String inputJarPath, String outputJarPath) { 141 static void enableAssertionInJar(String inputJarPath, String outputJarPath) {
90 String tempJarPath = outputJarPath + TEMPORARY_FILE_SUFFIX; 142 String tempJarPath = outputJarPath + TEMPORARY_FILE_SUFFIX;
91 try (ZipInputStream inputStream = new ZipInputStream( 143 try (ZipInputStream inputStream = new ZipInputStream(
92 new BufferedInputStream(new FileInputStream(inputJarPath))); 144 new BufferedInputStream(new FileInputStream(inputJarPath))) ;
93 ZipOutputStream tempStream = new ZipOutputStream( 145 ZipOutputStream tempStream = new ZipOutputStream(
94 new BufferedOutputStream(new FileOutputStream(tempJarPath))) ) { 146 new BufferedOutputStream(new FileOutputStream(tempJarPat h)))) {
95 ZipEntry entry = null; 147 ZipEntry entry = null;
96 148
97 while ((entry = inputStream.getNextEntry()) != null) { 149 while ((entry = inputStream.getNextEntry()) != null) {
98 byte[] byteCode = readAllBytes(inputStream); 150 byte[] byteCode = readAllBytes(inputStream);
99 151
100 if (entry.isDirectory() || !entry.getName().endsWith(CLASS_FILE_ SUFFIX)) { 152 if (entry.isDirectory() || !entry.getName().endsWith(CLASS_FILE_ SUFFIX)) {
101 tempStream.putNextEntry(entry); 153 tempStream.putNextEntry(entry);
102 tempStream.write(byteCode); 154 tempStream.write(byteCode);
103 tempStream.closeEntry(); 155 tempStream.closeEntry();
104 continue; 156 continue;
(...skipping 18 matching lines...) Expand all
123 throw new RuntimeException(ioException); 175 throw new RuntimeException(ioException);
124 } 176 }
125 } 177 }
126 178
127 public static void main(String[] args) { 179 public static void main(String[] args) {
128 if (args.length != 2) { 180 if (args.length != 2) {
129 System.out.println("Incorrect number of arguments."); 181 System.out.println("Incorrect number of arguments.");
130 System.out.println("Example usage: java_assertion_enabler input.jar output.jar"); 182 System.out.println("Example usage: java_assertion_enabler input.jar output.jar");
131 System.exit(-1); 183 System.exit(-1);
132 } 184 }
133 enableAssertionInJar(args[0], args[1]); 185 String inputJarPath = args[0];
186 String outputJarPath = args[1];
187 enableAssertionInJar(inputJarPath, outputJarPath);
134 } 188 }
135 } 189 }
OLDNEW
« no previous file with comments | « build/android/buildhooks/java/org/chromium/buildhooks/Callback.java ('k') | build/config/android/internal_rules.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698