OLD | NEW |
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 com.google.common.io.ByteStreams; | |
8 | |
9 import org.objectweb.asm.ClassReader; | 7 import org.objectweb.asm.ClassReader; |
10 import org.objectweb.asm.ClassVisitor; | 8 import org.objectweb.asm.ClassVisitor; |
11 import org.objectweb.asm.ClassWriter; | 9 import org.objectweb.asm.ClassWriter; |
12 import org.objectweb.asm.MethodVisitor; | 10 import org.objectweb.asm.MethodVisitor; |
13 import org.objectweb.asm.Opcodes; | 11 import org.objectweb.asm.Opcodes; |
14 | 12 |
15 import java.io.BufferedInputStream; | 13 import java.io.BufferedInputStream; |
16 import java.io.BufferedOutputStream; | 14 import java.io.BufferedOutputStream; |
| 15 import java.io.ByteArrayOutputStream; |
| 16 import java.io.FileInputStream; |
17 import java.io.FileOutputStream; | 17 import java.io.FileOutputStream; |
18 import java.io.IOException; | 18 import java.io.IOException; |
19 import java.util.Collections; | 19 import java.io.InputStream; |
20 import java.util.jar.JarEntry; | 20 import java.nio.file.Files; |
21 import java.util.jar.JarFile; | 21 import java.nio.file.Path; |
22 import java.util.jar.JarOutputStream; | 22 import java.nio.file.Paths; |
| 23 import java.nio.file.StandardCopyOption; |
| 24 import java.util.zip.ZipEntry; |
| 25 import java.util.zip.ZipInputStream; |
| 26 import java.util.zip.ZipOutputStream; |
23 | 27 |
24 /** | 28 /** |
25 * An application that enables Java ASSERT statements by modifying Java bytecode
. It takes in a JAR | 29 * An application that enables Java ASSERT statements by modifying Java bytecode
. It takes in a JAR |
26 * file, modifies bytecode of classes that use ASSERT, and outputs the bytecode
to a new JAR file. | 30 * file, modifies bytecode of classes that use ASSERT, and outputs the bytecode
to a new JAR file. |
27 */ | 31 */ |
28 class AssertionEnabler { | 32 class AssertionEnabler { |
| 33 static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled"; |
29 static final String CLASS_FILE_SUFFIX = ".class"; | 34 static final String CLASS_FILE_SUFFIX = ".class"; |
30 static final String STATIC_INITIALIZER_NAME = "<clinit>"; | 35 static final String STATIC_INITIALIZER_NAME = "<clinit>"; |
31 static final String ASSERTION_DISABLED_NAME = "$assertionsDisabled"; | 36 static final String TEMPORARY_FILE_SUFFIX = ".temp"; |
| 37 |
| 38 static final int BUFFER_SIZE = 16384; |
32 | 39 |
33 static class AssertionEnablerVisitor extends ClassVisitor { | 40 static class AssertionEnablerVisitor extends ClassVisitor { |
34 AssertionEnablerVisitor(ClassWriter writer) { | 41 AssertionEnablerVisitor(ClassWriter writer) { |
35 super(Opcodes.ASM5, writer); | 42 super(Opcodes.ASM5, writer); |
36 } | 43 } |
37 | 44 |
38 @Override | 45 @Override |
39 public MethodVisitor visitMethod(final int access, final String name, St
ring desc, | 46 public MethodVisitor visitMethod(final int access, final String name, St
ring desc, |
40 String signature, String[] exceptions) { | 47 String signature, String[] exceptions) { |
41 // Patch static initializer. | 48 // Patch static initializer. |
(...skipping 19 matching lines...) Expand all Loading... |
61 } else { | 68 } else { |
62 super.visitFieldInsn(opcode, owner, name, desc); | 69 super.visitFieldInsn(opcode, owner, name, desc); |
63 } | 70 } |
64 } | 71 } |
65 }; | 72 }; |
66 } | 73 } |
67 return super.visitMethod(access, name, desc, signature, exceptions); | 74 return super.visitMethod(access, name, desc, signature, exceptions); |
68 } | 75 } |
69 } | 76 } |
70 | 77 |
| 78 static byte[] readAllBytes(InputStream inputStream) throws IOException { |
| 79 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); |
| 80 int numRead = 0; |
| 81 byte[] data = new byte[BUFFER_SIZE]; |
| 82 while ((numRead = inputStream.read(data, 0, data.length)) != -1) { |
| 83 buffer.write(data, 0, numRead); |
| 84 } |
| 85 |
| 86 return buffer.toByteArray(); |
| 87 } |
| 88 |
71 static void enableAssertionInJar(String inputJarPath, String outputJarPath)
{ | 89 static void enableAssertionInJar(String inputJarPath, String outputJarPath)
{ |
72 try (JarOutputStream outputStream = new JarOutputStream( | 90 String tempJarPath = outputJarPath + TEMPORARY_FILE_SUFFIX; |
73 new BufferedOutputStream(new FileOutputStream(outputJarPath))))
{ | 91 try (ZipInputStream inputStream = new ZipInputStream( |
74 JarFile jarFile = new JarFile(inputJarPath); | 92 new BufferedInputStream(new FileInputStream(inputJarPath))); |
75 for (JarEntry entry : Collections.list(jarFile.entries())) { | 93 ZipOutputStream tempStream = new ZipOutputStream( |
76 try (BufferedInputStream inputStream = new BufferedInputStream( | 94 new BufferedOutputStream(new FileOutputStream(tempJarPath)))
) { |
77 jarFile.getInputStream(entry))) { | 95 ZipEntry entry = null; |
78 byte[] byteCode = ByteStreams.toByteArray(inputStream); | |
79 | 96 |
80 if (entry.isDirectory() || !entry.getName().endsWith(CLASS_F
ILE_SUFFIX)) { | 97 while ((entry = inputStream.getNextEntry()) != null) { |
81 outputStream.putNextEntry(entry); | 98 byte[] byteCode = readAllBytes(inputStream); |
82 outputStream.write(byteCode); | 99 |
83 outputStream.closeEntry(); | 100 if (entry.isDirectory() || !entry.getName().endsWith(CLASS_FILE_
SUFFIX)) { |
84 continue; | 101 tempStream.putNextEntry(entry); |
85 } | 102 tempStream.write(byteCode); |
86 ClassReader reader = new ClassReader(byteCode); | 103 tempStream.closeEntry(); |
87 ClassWriter writer = new ClassWriter(reader, 0); | 104 continue; |
88 reader.accept(new AssertionEnablerVisitor(writer), 0); | |
89 byte[] patchedByteCode = writer.toByteArray(); | |
90 outputStream.putNextEntry(new JarEntry(entry.getName())); | |
91 outputStream.write(patchedByteCode); | |
92 outputStream.closeEntry(); | |
93 } | 105 } |
| 106 ClassReader reader = new ClassReader(byteCode); |
| 107 ClassWriter writer = new ClassWriter(reader, 0); |
| 108 reader.accept(new AssertionEnablerVisitor(writer), 0); |
| 109 byte[] patchedByteCode = writer.toByteArray(); |
| 110 tempStream.putNextEntry(new ZipEntry(entry.getName())); |
| 111 tempStream.write(patchedByteCode); |
| 112 tempStream.closeEntry(); |
94 } | 113 } |
95 } catch (IOException e) { | 114 } catch (IOException e) { |
96 throw new RuntimeException(e); | 115 throw new RuntimeException(e); |
97 } | 116 } |
| 117 |
| 118 try { |
| 119 Path src = Paths.get(tempJarPath); |
| 120 Path dest = Paths.get(outputJarPath); |
| 121 Files.move(src, dest, StandardCopyOption.REPLACE_EXISTING); |
| 122 } catch (IOException ioException) { |
| 123 throw new RuntimeException(ioException); |
| 124 } |
98 } | 125 } |
99 | 126 |
100 public static void main(String[] args) { | 127 public static void main(String[] args) { |
101 if (args.length != 2) { | 128 if (args.length != 2) { |
102 System.out.println("Incorrect number of arguments."); | 129 System.out.println("Incorrect number of arguments."); |
103 System.out.println("Example usage: java_assertion_enabler input.jar
output.jar"); | 130 System.out.println("Example usage: java_assertion_enabler input.jar
output.jar"); |
104 System.exit(-1); | 131 System.exit(-1); |
105 } | 132 } |
106 enableAssertionInJar(args[0], args[1]); | 133 enableAssertionInJar(args[0], args[1]); |
107 } | 134 } |
108 } | 135 } |
OLD | NEW |