mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-23 14:19:56 +00:00
8037082: java/lang/instrument/NativeMethodPrefixAgent.java failing
Reviewed-by: sla
This commit is contained in:
parent
34e103f04b
commit
82dd03274a
@ -274,9 +274,6 @@ com/sun/jdi/JdbReadTwiceTest.sh generic-all
|
||||
|
||||
# jdk_instrument
|
||||
|
||||
# 8037082
|
||||
java/lang/instrument/NativeMethodPrefixAgent.java generic-all
|
||||
|
||||
############################################################################
|
||||
|
||||
# svc_tools
|
||||
|
||||
@ -75,9 +75,9 @@ JAR="${COMPILEJAVA}/bin/jar"
|
||||
|
||||
cp ${TESTSRC}/${AGENT}.java .
|
||||
cp ${TESTSRC}/${APP}.java .
|
||||
rm -rf ilib
|
||||
mkdir ilib
|
||||
cp ${TESTSRC}/ilib/*.java ilib
|
||||
rm -rf asmlib
|
||||
mkdir asmlib
|
||||
cp ${TESTSRC}/asmlib/*.java asmlib
|
||||
rm -rf bootpath
|
||||
mkdir -p bootpath/bootreporter
|
||||
cp ${TESTSRC}/bootreporter/*.java bootpath/bootreporter
|
||||
@ -86,7 +86,7 @@ cd bootpath
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} bootreporter/*.java
|
||||
cd ..
|
||||
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ${AGENT}.java ilib/*.java
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -XDignore.symbol.file ${AGENT}.java asmlib/*.java
|
||||
${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath .${PATHSEP}bootpath ${APP}.java
|
||||
|
||||
echo "Manifest-Version: 1.0" > ${AGENT}.mf
|
||||
@ -98,6 +98,6 @@ while [ $# != 0 ] ; do
|
||||
shift
|
||||
done
|
||||
|
||||
${JAR} ${TESTTOOLVMOPTS} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class ilib/*.class
|
||||
${JAR} ${TESTTOOLVMOPTS} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class asmlib/*.class
|
||||
|
||||
# rm -rf ${AGENT}.java ilib ${AGENT}.mf ${AGENT}*.class
|
||||
# rm -rf ${AGENT}.java asmlib ${AGENT}.mf ${AGENT}*.class
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,7 +35,7 @@ import java.lang.instrument.*;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.io.*;
|
||||
|
||||
import ilib.*;
|
||||
import asmlib.*;
|
||||
|
||||
class NativeMethodPrefixAgent {
|
||||
|
||||
@ -62,14 +62,25 @@ class NativeMethodPrefixAgent {
|
||||
System.out.println(trname + ": " +
|
||||
(redef? "Retransforming " : "Loading ") + className);
|
||||
if (className != null) {
|
||||
Options opt = new Options();
|
||||
opt.shouldInstrumentNativeMethods = true;
|
||||
opt.trackerClassName = "bootreporter/StringIdCallbackReporter";
|
||||
opt.wrappedTrackerMethodName = "tracker";
|
||||
opt.fixedIndex = transformId;
|
||||
opt.wrappedPrefix = "wrapped_" + trname + "_";
|
||||
try {
|
||||
byte[] newcf = Inject.instrumentation(opt, loader, className, classfileBuffer);
|
||||
byte[] newcf = Instrumentor.instrFor(classfileBuffer)
|
||||
.addNativeMethodTrackingInjection(
|
||||
"wrapped_" + trname + "_",
|
||||
(h)->{
|
||||
h.push(h.getName());
|
||||
h.push(transformId);
|
||||
h.invokeStatic("bootreporter/StringIdCallbackReporter", "tracker", "(Ljava/lang/String;I)V", false);
|
||||
})
|
||||
.apply();
|
||||
/*** debugging ...
|
||||
if (newcf != null) {
|
||||
String fname = trname + (redef?"_redef" : "") + "/" + className;
|
||||
System.err.println("dumping to: " + fname);
|
||||
write_buffer(fname + "_before.class", classfileBuffer);
|
||||
write_buffer(fname + "_instr.class", newcf);
|
||||
}
|
||||
***/
|
||||
|
||||
return redef? null : newcf;
|
||||
} catch (Throwable ex) {
|
||||
System.err.println("ERROR: Injection failure: " + ex);
|
||||
@ -86,10 +97,14 @@ class NativeMethodPrefixAgent {
|
||||
// for debugging
|
||||
static void write_buffer(String fname, byte[]buffer) {
|
||||
try {
|
||||
FileOutputStream outStream = new FileOutputStream(fname);
|
||||
outStream.write(buffer, 0, buffer.length);
|
||||
outStream.close();
|
||||
} catch (Exception ex) {
|
||||
File f = new File(fname);
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
try (FileOutputStream outStream = new FileOutputStream(f)) {
|
||||
outStream.write(buffer, 0, buffer.length);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
System.err.println("EXCEPTION in write_buffer: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,8 +34,7 @@
|
||||
import java.lang.instrument.*;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.io.*;
|
||||
|
||||
import ilib.*;
|
||||
import asmlib.*;
|
||||
|
||||
class RetransformAgent {
|
||||
|
||||
@ -75,20 +74,25 @@ class RetransformAgent {
|
||||
// System.err.println("hook " + trname + ": " + className +
|
||||
// (redef? " REDEF" : " LOAD"));
|
||||
if ((redef? onRedef : onLoad) && className != null && className.equals(cname)) {
|
||||
Options opt = new Options();
|
||||
opt.shouldInstrumentIndexed = true;
|
||||
opt.shouldInstrumentCall = true;
|
||||
opt.targetMethod = nname;
|
||||
opt.fixedIndex = redef? redefIndex : loadIndex;
|
||||
opt.trackerClassName = "RetransformAgent";
|
||||
int fixedIndex = redef ? redefIndex : loadIndex;
|
||||
try {
|
||||
byte[] newcf = Inject.instrumentation(opt, loader, className, classfileBuffer);
|
||||
byte[] newcf = Instrumentor.instrFor(classfileBuffer)
|
||||
.addMethodEntryInjection(
|
||||
nname,
|
||||
(h)->{
|
||||
h.push(fixedIndex);
|
||||
h.invokeStatic("RetransformAgent", "callTracker", "(I)V", false);
|
||||
})
|
||||
.apply();
|
||||
/*** debugging ...
|
||||
String fname = trname + (redef?"_redef" : "");
|
||||
write_buffer(fname + "_before.class", classfileBuffer);
|
||||
write_buffer(fname + "_instr.class", newcf);
|
||||
if (newcf != null) {
|
||||
String fname = trname + (redef?"_redef" : "") + "/" + className;
|
||||
System.err.println("dumping to: " + fname);
|
||||
write_buffer(fname + "_before.class", classfileBuffer);
|
||||
write_buffer(fname + "_instr.class", newcf);
|
||||
}
|
||||
***/
|
||||
System.err.println(trname + ": " + className + " index: " + opt.fixedIndex +
|
||||
System.err.println(trname + ": " + className + " index: " + fixedIndex +
|
||||
(redef? " REDEF" : " LOAD") +
|
||||
" len before: " + classfileBuffer.length +
|
||||
" after: " + newcf.length);
|
||||
@ -104,10 +108,14 @@ class RetransformAgent {
|
||||
|
||||
static void write_buffer(String fname, byte[]buffer) {
|
||||
try {
|
||||
FileOutputStream outStream = new FileOutputStream(fname);
|
||||
outStream.write(buffer, 0, buffer.length);
|
||||
outStream.close();
|
||||
} catch (Exception ex) {
|
||||
File f = new File(fname);
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
try (FileOutputStream outStream = new FileOutputStream(f)) {
|
||||
outStream.write(buffer, 0, buffer.length);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
System.err.println("EXCEPTION in write_buffer: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
193
jdk/test/java/lang/instrument/asmlib/Instrumentor.java
Normal file
193
jdk/test/java/lang/instrument/asmlib/Instrumentor.java
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package asmlib;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
||||
public class Instrumentor {
|
||||
public static class InstrHelper {
|
||||
private final MethodVisitor mv;
|
||||
private final String name;
|
||||
|
||||
InstrHelper(MethodVisitor mv, String name) {
|
||||
this.mv = mv;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void invokeStatic(String owner, String name, String desc, boolean itf) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc, itf);
|
||||
}
|
||||
|
||||
public void invokeSpecial(String owner, String name, String desc) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc, false);
|
||||
}
|
||||
|
||||
public void invokeVirtual(String owner, String name, String desc) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc, false);
|
||||
}
|
||||
|
||||
public void push(int val) {
|
||||
if (val >= -1 && val <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + val);
|
||||
} else if (val >= Byte.MIN_VALUE && val <= Byte.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.BIPUSH, val);
|
||||
} else if (val >= Short.MIN_VALUE && val <= Short.MAX_VALUE) {
|
||||
mv.visitIntInsn(Opcodes.SIPUSH, val);
|
||||
} else {
|
||||
mv.visitLdcInsn(val);
|
||||
}
|
||||
}
|
||||
|
||||
public void push(Object val) {
|
||||
mv.visitLdcInsn(val);
|
||||
}
|
||||
|
||||
public void println(String s) {
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(System.class), "out", Type.getDescriptor(PrintStream.class));
|
||||
mv.visitLdcInsn(s);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(PrintStream.class), "println", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)), false);
|
||||
}
|
||||
}
|
||||
|
||||
public static Instrumentor instrFor(byte[] classData) {
|
||||
return new Instrumentor(classData);
|
||||
}
|
||||
|
||||
|
||||
private final ClassReader cr;
|
||||
private final ClassWriter output;
|
||||
private ClassVisitor instrumentingVisitor = null;
|
||||
private final AtomicInteger matches = new AtomicInteger(0);
|
||||
|
||||
private Instrumentor(byte[] classData) {
|
||||
cr = new ClassReader(classData);
|
||||
output = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
instrumentingVisitor = output;
|
||||
}
|
||||
|
||||
public synchronized Instrumentor addMethodEntryInjection(String methodName, Consumer<InstrHelper> injector) {
|
||||
instrumentingVisitor = new ClassVisitor(Opcodes.ASM5, instrumentingVisitor) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
|
||||
|
||||
if (name.equals(methodName)) {
|
||||
matches.getAndIncrement();
|
||||
|
||||
mv = new MethodVisitor(Opcodes.ASM5, mv) {
|
||||
@Override
|
||||
public void visitCode() {
|
||||
injector.accept(new InstrHelper(mv, name));
|
||||
}
|
||||
};
|
||||
}
|
||||
return mv;
|
||||
}
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized Instrumentor addNativeMethodTrackingInjection(String prefix, Consumer<InstrHelper> injector) {
|
||||
instrumentingVisitor = new ClassVisitor(Opcodes.ASM5, instrumentingVisitor) {
|
||||
private final Set<Consumer<ClassVisitor>> wmGenerators = new HashSet<>();
|
||||
private String className;
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.className = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
if ((access & Opcodes.ACC_NATIVE) != 0) {
|
||||
matches.getAndIncrement();
|
||||
|
||||
String newName = prefix + name;
|
||||
wmGenerators.add((v)->{
|
||||
MethodVisitor mv = v.visitMethod(access & ~Opcodes.ACC_NATIVE, name, desc, signature, exceptions);
|
||||
mv.visitCode();
|
||||
injector.accept(new InstrHelper(mv, name));
|
||||
Type[] argTypes = Type.getArgumentTypes(desc);
|
||||
Type retType = Type.getReturnType(desc);
|
||||
|
||||
boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
|
||||
if (!isStatic) {
|
||||
mv.visitIntInsn(Opcodes.ALOAD, 0); // load "this"
|
||||
}
|
||||
|
||||
// load the method parameters
|
||||
if (argTypes.length > 0) {
|
||||
int ptr = isStatic ? 0 : 1;
|
||||
for(Type argType : argTypes) {
|
||||
mv.visitIntInsn(argType.getOpcode(Opcodes.ILOAD), ptr);
|
||||
ptr += argType.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
mv.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKESPECIAL, className, newName, desc, false);
|
||||
mv.visitInsn(retType.getOpcode(Opcodes.IRETURN));
|
||||
|
||||
mv.visitMaxs(1, 1); // dummy call; let ClassWriter to deal with this
|
||||
mv.visitEnd();
|
||||
});
|
||||
return super.visitMethod(access, newName, desc, signature, exceptions);
|
||||
}
|
||||
return super.visitMethod(access, name, desc, signature, exceptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
wmGenerators.stream().forEach((e) -> {
|
||||
e.accept(cv);
|
||||
});
|
||||
super.visitEnd();
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized byte[] apply() {
|
||||
cr.accept(instrumentingVisitor, ClassReader.SKIP_DEBUG + ClassReader.EXPAND_FRAMES);
|
||||
|
||||
return matches.get() == 0 ? null : output.toByteArray();
|
||||
}
|
||||
}
|
||||
@ -1,273 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.CharArrayWriter;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ClassDump implements RuntimeConstants {
|
||||
|
||||
public static void dump(Options opt,
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
byte[] classfileBuffer) {
|
||||
ClassReaderWriter c = new ClassReaderWriter(classfileBuffer);
|
||||
(new ClassDump(className, c)).doit();
|
||||
}
|
||||
|
||||
static boolean verbose = true;
|
||||
|
||||
final String className;
|
||||
final ClassReaderWriter c;
|
||||
private final PrintStream output;
|
||||
|
||||
int constantPoolCount;
|
||||
int methodsCount;
|
||||
|
||||
ClassDump(String className, ClassReaderWriter c) {
|
||||
this.className = className;
|
||||
this.c = c;
|
||||
this.output = System.err;
|
||||
}
|
||||
|
||||
void doit() {
|
||||
int i;
|
||||
c.copy(4 + 2 + 2); // magic min/maj version
|
||||
constantPoolCount = c.copyU2();
|
||||
// copy old constant pool
|
||||
c.copyConstantPool(constantPoolCount);
|
||||
|
||||
traceln("ConstantPool size: " + constantPoolCount);
|
||||
|
||||
c.copy(2 + 2 + 2); // access, this, super
|
||||
int interfaceCount = c.copyU2();
|
||||
traceln("interfaceCount: " + interfaceCount);
|
||||
c.copy(interfaceCount * 2);
|
||||
copyFields(); // fields
|
||||
copyMethods(); // methods
|
||||
int attrCount = c.copyU2();
|
||||
traceln("class attrCount: " + attrCount);
|
||||
// copy the class attributes
|
||||
copyAttrs(attrCount);
|
||||
}
|
||||
|
||||
|
||||
void copyFields() {
|
||||
int count = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("fields count: " + count);
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
c.copy(6); // access, name, descriptor
|
||||
int attrCount = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("field attr count: " + attrCount);
|
||||
}
|
||||
copyAttrs(attrCount);
|
||||
}
|
||||
}
|
||||
|
||||
void copyMethods() {
|
||||
methodsCount = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("methods count: " + methodsCount);
|
||||
}
|
||||
for (int i = 0; i < methodsCount; ++i) {
|
||||
copyMethod();
|
||||
}
|
||||
}
|
||||
|
||||
void copyMethod() {
|
||||
int accessFlags = c.copyU2();// access flags
|
||||
int nameIndex = c.copyU2(); // name
|
||||
checkIndex(nameIndex, "Method name");
|
||||
String methodName = c.constantPoolString(nameIndex);
|
||||
traceln("method: " + methodName);
|
||||
int descriptorIndex = c.copyU2(); // descriptor
|
||||
checkIndex(descriptorIndex, "Method descriptor");
|
||||
int attrCount = c.copyU2(); // attribute count
|
||||
if (verbose) {
|
||||
System.out.println("method attr count: " + attrCount);
|
||||
}
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForMethod(methodName, accessFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void copyAttrs(int attrCount) {
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttr();
|
||||
}
|
||||
}
|
||||
|
||||
void copyAttr() {
|
||||
c.copy(2); // name
|
||||
int len = c.copyU4(); // attr len
|
||||
if (verbose) {
|
||||
System.out.println("attr len: " + len);
|
||||
}
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
|
||||
void copyAttrForMethod(String methodName, int accessFlags) {
|
||||
int nameIndex = c.copyU2(); // name
|
||||
// check for Code attr
|
||||
checkIndex(nameIndex, "Method attr name");
|
||||
if (nameIndex == c.codeAttributeIndex) {
|
||||
try {
|
||||
copyCodeAttr(methodName);
|
||||
} catch (IOException exc) {
|
||||
System.err.println("Code Exception - " + exc);
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
int len = c.copyU4(); // attr len
|
||||
traceln("method attr len: " + len);
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
}
|
||||
|
||||
void copyAttrForCode() throws IOException {
|
||||
int nameIndex = c.copyU2(); // name
|
||||
|
||||
checkIndex(nameIndex, "Code attr name");
|
||||
int len = c.copyU4(); // attr len
|
||||
traceln("code attr len: " + len);
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
|
||||
void copyCodeAttr(String methodName) throws IOException {
|
||||
traceln("Code attr found");
|
||||
int attrLength = c.copyU4(); // attr len
|
||||
checkLength(attrLength, "Code attr length");
|
||||
int maxStack = c.readU2(); // max stack
|
||||
c.copyU2(); // max locals
|
||||
int codeLength = c.copyU4(); // code length
|
||||
checkLength(codeLength, "Code length");
|
||||
|
||||
copyExceptionTable();
|
||||
|
||||
int attrCount = c.copyU2();
|
||||
checkLength(attrCount, "Code attr count");
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the exception table for this method code
|
||||
*/
|
||||
void copyExceptionTable() throws IOException {
|
||||
int tableLength = c.copyU2(); // exception table len
|
||||
checkLength(tableLength, "Exception Table length");
|
||||
if (tableLength > 0) {
|
||||
traceln();
|
||||
traceln("Exception table:");
|
||||
traceln(" from:old/new to:old/new target:old/new type");
|
||||
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
||||
int startPC = c.readU2();
|
||||
int endPC = c.readU2();
|
||||
int handlerPC = c.readU2();
|
||||
int catchType = c.copyU2();
|
||||
if (verbose) {
|
||||
traceFixedWidthInt(startPC, 6);
|
||||
traceFixedWidthInt(endPC, 6);
|
||||
traceFixedWidthInt(handlerPC, 6);
|
||||
trace(" ");
|
||||
if (catchType == 0)
|
||||
traceln("any");
|
||||
else {
|
||||
traceln("" + catchType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIndex(int index, String comment) {
|
||||
if (index > constantPoolCount) {
|
||||
output.println("ERROR BAD INDEX " + comment + " : " + index);
|
||||
} else {
|
||||
traceln(comment + " : " + index);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkLength(int length, String comment) {
|
||||
if (length > c.inputBytes().length) {
|
||||
output.println("ERROR BAD LENGTH " + comment + " : " + length);
|
||||
} else {
|
||||
traceln(comment + " : " + length);
|
||||
}
|
||||
}
|
||||
|
||||
private void trace(String str) {
|
||||
if (verbose) {
|
||||
output.print(str);
|
||||
}
|
||||
}
|
||||
|
||||
private void traceln(String str) {
|
||||
if (verbose) {
|
||||
output.println(str);
|
||||
}
|
||||
}
|
||||
|
||||
private void traceln() {
|
||||
if (verbose) {
|
||||
output.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void trace(int i) {
|
||||
if (verbose) {
|
||||
output.print(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an integer so that it takes 'length' characters in
|
||||
* the output. Temporary until formatting code is stable.
|
||||
*/
|
||||
private void traceFixedWidthInt(int x, int length) {
|
||||
if (verbose) {
|
||||
CharArrayWriter baStream = new CharArrayWriter();
|
||||
PrintWriter pStream = new PrintWriter(baStream);
|
||||
pStream.print(x);
|
||||
String str = baStream.toString();
|
||||
for (int cnt = length - str.length(); cnt > 0; --cnt)
|
||||
trace(" ");
|
||||
trace(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,263 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
class ClassReaderWriter implements RuntimeConstants {
|
||||
|
||||
int codeAttributeIndex;
|
||||
int lineNumberAttributeIndex;
|
||||
int localVarAttributeIndex;
|
||||
|
||||
private final byte[] orig;
|
||||
private final byte[] gen;
|
||||
private final int sectionLength;
|
||||
|
||||
private static final int GROWTH_FACTOR = 2;
|
||||
private static final int SECTIONS = 2;
|
||||
private static final String codeAttributeName = "Code";
|
||||
private static final String lineNumberAttributeName = "LineNumberTable";
|
||||
private static final String localVarAttributeName = "LocalVariableTable";
|
||||
|
||||
private int[] genSectionPos = new int[SECTIONS];
|
||||
|
||||
private int inputPos = 0;
|
||||
private int genPos = 0;
|
||||
private int markPos = 0;
|
||||
private int currentSection = 0;
|
||||
|
||||
private String[] constantPool;
|
||||
|
||||
ClassReaderWriter(byte[] orig) {
|
||||
this.orig = orig;
|
||||
sectionLength = orig.length * GROWTH_FACTOR;
|
||||
gen = new byte[sectionLength * SECTIONS];
|
||||
for (int section = 0; section < SECTIONS; ++section) {
|
||||
genSectionPos[section] = section * sectionLength;
|
||||
}
|
||||
}
|
||||
|
||||
int setSection(int section) {
|
||||
int prevSection = currentSection;
|
||||
genSectionPos[prevSection] = genPos;
|
||||
genPos = genSectionPos[section];
|
||||
currentSection = section;
|
||||
return prevSection;
|
||||
}
|
||||
|
||||
byte[] result() {
|
||||
int section;
|
||||
int totalLength = 0;
|
||||
|
||||
setSection(0); // save current section
|
||||
|
||||
for (section = 0; section < SECTIONS; ++section) {
|
||||
int sectionStart = section * sectionLength;
|
||||
int sectionGenLength = genSectionPos[section] - sectionStart;
|
||||
totalLength += sectionGenLength;
|
||||
}
|
||||
|
||||
byte[] newcf = new byte[totalLength];
|
||||
int written = 0;
|
||||
for (section = 0; section < SECTIONS; ++section) {
|
||||
int sectionStart = section * sectionLength;
|
||||
int sectionGenLength = genSectionPos[section] - sectionStart;
|
||||
System.arraycopy(gen, sectionStart, newcf, written, sectionGenLength);
|
||||
written += sectionGenLength;
|
||||
}
|
||||
|
||||
return newcf;
|
||||
}
|
||||
|
||||
int readU1() {
|
||||
return ((int)orig[inputPos++]) & 0xFF;
|
||||
}
|
||||
|
||||
int readU2() {
|
||||
int res = readU1();
|
||||
return (res << 8) + readU1();
|
||||
}
|
||||
|
||||
short readS2() {
|
||||
int res = readU1();
|
||||
return (short)((res << 8) + readU1());
|
||||
}
|
||||
|
||||
int readU4() {
|
||||
int res = readU2();
|
||||
return (res << 16) + readU2();
|
||||
}
|
||||
|
||||
void writeU1(int val) {
|
||||
gen[genPos++] = (byte)val;
|
||||
}
|
||||
|
||||
void writeU2(int val) {
|
||||
writeU1(val >> 8);
|
||||
writeU1(val & 0xFF);
|
||||
}
|
||||
|
||||
void writeU4(int val) {
|
||||
writeU2(val >> 16);
|
||||
writeU2(val & 0xFFFF);
|
||||
}
|
||||
|
||||
int copyU1() {
|
||||
int value = readU1();
|
||||
writeU1(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
int copyU2() {
|
||||
int value = readU2();
|
||||
writeU2(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
int copyU4() {
|
||||
int value = readU4();
|
||||
writeU4(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
void copy(int count) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
gen[genPos++] = orig[inputPos++];
|
||||
}
|
||||
}
|
||||
|
||||
void skip(int count) {
|
||||
inputPos += count;
|
||||
}
|
||||
|
||||
byte[] readBytes(int count) {
|
||||
byte[] bytes = new byte[count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
bytes[i] = orig[inputPos++];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void writeBytes(byte[] bytes) {
|
||||
for (int i = 0; i < bytes.length; ++i) {
|
||||
gen[genPos++] = bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
byte[] inputBytes() {
|
||||
return orig;
|
||||
}
|
||||
|
||||
int inputPosition() {
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
void setInputPosition(int pos) {
|
||||
inputPos = pos;
|
||||
}
|
||||
|
||||
void markLocalPositionStart() {
|
||||
markPos = inputPos;
|
||||
}
|
||||
|
||||
int localPosition() {
|
||||
return inputPos - markPos;
|
||||
}
|
||||
|
||||
void rewind() {
|
||||
setInputPosition(markPos);
|
||||
}
|
||||
|
||||
int generatedPosition() {
|
||||
return genPos;
|
||||
}
|
||||
|
||||
void randomAccessWriteU2(int pos, int val) {
|
||||
int savePos = genPos;
|
||||
genPos = pos;
|
||||
writeU2(val);
|
||||
genPos = savePos;
|
||||
}
|
||||
|
||||
void randomAccessWriteU4(int pos, int val) {
|
||||
int savePos = genPos;
|
||||
genPos = pos;
|
||||
writeU4(val);
|
||||
genPos = savePos;
|
||||
}
|
||||
|
||||
String constantPoolString(int index) {
|
||||
return constantPool[index];
|
||||
}
|
||||
|
||||
void copyConstantPool(int constantPoolCount){
|
||||
// copy const pool
|
||||
constantPool = new String[constantPoolCount];
|
||||
// index zero not in class file
|
||||
for (int i = 1; i < constantPoolCount; ++i) {
|
||||
int tag = readU1();
|
||||
writeU1(tag);
|
||||
switch (tag) {
|
||||
case CONSTANT_CLASS:
|
||||
case CONSTANT_STRING:
|
||||
copy(2);
|
||||
break;
|
||||
case CONSTANT_FIELD:
|
||||
case CONSTANT_METHOD:
|
||||
case CONSTANT_INTERFACEMETHOD:
|
||||
case CONSTANT_INTEGER:
|
||||
case CONSTANT_FLOAT:
|
||||
case CONSTANT_NAMEANDTYPE:
|
||||
copy(4);
|
||||
break;
|
||||
case CONSTANT_LONG:
|
||||
case CONSTANT_DOUBLE:
|
||||
copy(8);
|
||||
++i; // these take two CP entries - duh!
|
||||
break;
|
||||
case CONSTANT_UTF8:
|
||||
int len = copyU2();
|
||||
byte[] utf8 = readBytes(len);
|
||||
String str = null; // null to shut the compiler up
|
||||
try {
|
||||
str = new String(utf8, "UTF-8");
|
||||
} catch (Exception exc) {
|
||||
throw new Error("CP exception: " + exc);
|
||||
}
|
||||
constantPool[i] = str;
|
||||
if (str.equals(codeAttributeName)) {
|
||||
codeAttributeIndex = i;
|
||||
} else if (str.equals(lineNumberAttributeName)) {
|
||||
lineNumberAttributeIndex = i;
|
||||
} else if (str.equals(localVarAttributeName)) {
|
||||
localVarAttributeIndex = i;
|
||||
}
|
||||
writeBytes(utf8);
|
||||
break;
|
||||
default:
|
||||
throw new Error(i + " unexpected CP tag: " + tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
public class Info {
|
||||
final int counter;
|
||||
final String className;
|
||||
final String methodName;
|
||||
final int location;
|
||||
|
||||
Info(int counter, String className, String methodName, int location) {
|
||||
this.counter = counter;
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
@ -1,746 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Inject implements RuntimeConstants {
|
||||
|
||||
public static byte[] instrumentation(Options opt,
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
byte[] classfileBuffer) {
|
||||
ClassReaderWriter c = new ClassReaderWriter(classfileBuffer);
|
||||
(new Inject(className, c, loader == null, opt)).doit();
|
||||
return c.result();
|
||||
}
|
||||
|
||||
static boolean verbose = false;
|
||||
|
||||
final String className;
|
||||
final ClassReaderWriter c;
|
||||
final boolean isSystem;
|
||||
final Options options;
|
||||
|
||||
int constantPoolCount;
|
||||
int methodsCount;
|
||||
int methodsCountPos;
|
||||
int profiler;
|
||||
int wrappedTrackerIndex = 0;
|
||||
int thisClassIndex = 0;
|
||||
|
||||
TrackerInjector callInjector;
|
||||
TrackerInjector allocInjector;
|
||||
TrackerInjector defaultInjector;
|
||||
|
||||
static interface TrackerInjector extends Injector {
|
||||
void reinit(int tracker);
|
||||
int stackSize(int currentSize);
|
||||
}
|
||||
|
||||
static class SimpleInjector implements TrackerInjector {
|
||||
byte[] injection;
|
||||
|
||||
public int stackSize(int currentSize) {
|
||||
return currentSize;
|
||||
}
|
||||
|
||||
public void reinit(int tracker) {
|
||||
injection = new byte[3];
|
||||
injection[0] = (byte)opc_invokestatic;
|
||||
injection[1] = (byte)(tracker >> 8);
|
||||
injection[2] = (byte)tracker;
|
||||
}
|
||||
|
||||
public byte[] bytecodes(String className, String methodName, int location) {
|
||||
return injection;
|
||||
}
|
||||
}
|
||||
|
||||
static class ObjectInjector implements TrackerInjector {
|
||||
byte[] injection;
|
||||
|
||||
public int stackSize(int currentSize) {
|
||||
return currentSize + 1;
|
||||
}
|
||||
|
||||
public void reinit(int tracker) {
|
||||
injection = new byte[4];
|
||||
injection[0] = (byte)opc_dup;
|
||||
injection[1] = (byte)opc_invokestatic;
|
||||
injection[2] = (byte)(tracker >> 8);
|
||||
injection[3] = (byte)tracker;
|
||||
}
|
||||
|
||||
public byte[] bytecodes(String className, String methodName, int location) {
|
||||
return injection;
|
||||
}
|
||||
}
|
||||
|
||||
class IndexedInjector implements TrackerInjector {
|
||||
int counter = 0;
|
||||
int tracker;
|
||||
List<Info> infoList = new ArrayList<>();
|
||||
|
||||
public int stackSize(int currentSize) {
|
||||
return currentSize + 1;
|
||||
}
|
||||
|
||||
public void reinit(int tracker) {
|
||||
this.tracker = tracker;
|
||||
}
|
||||
|
||||
void dump(File outDir, String filename) throws IOException {
|
||||
try (FileOutputStream fileOut =
|
||||
new FileOutputStream(new File(outDir, filename));
|
||||
DataOutputStream dataOut = new DataOutputStream(fileOut))
|
||||
{
|
||||
String currentClassName = null;
|
||||
|
||||
dataOut.writeInt(infoList.size());
|
||||
for (Iterator<Info> it = infoList.iterator(); it.hasNext(); ) {
|
||||
Info info = it.next();
|
||||
if (!info.className.equals(currentClassName)) {
|
||||
dataOut.writeInt(123456); // class name marker
|
||||
currentClassName = info.className;
|
||||
dataOut.writeUTF(currentClassName);
|
||||
}
|
||||
dataOut.writeInt(info.location);
|
||||
dataOut.writeUTF(info.methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] bytecodes(String className, String methodName, int location) {
|
||||
byte[] injection = new byte[6];
|
||||
int injectedIndex = options.fixedIndex != 0? options.fixedIndex : ++counter;
|
||||
infoList.add(new Info(counter, className, methodName, location));
|
||||
injection[0] = (byte)opc_sipush;
|
||||
injection[1] = (byte)(injectedIndex >> 8);
|
||||
injection[2] = (byte)injectedIndex;
|
||||
injection[3] = (byte)opc_invokestatic;
|
||||
injection[4] = (byte)(tracker >> 8);
|
||||
injection[5] = (byte)tracker;
|
||||
return injection;
|
||||
}
|
||||
}
|
||||
|
||||
Inject(String className, ClassReaderWriter c, boolean isSystem, Options options) {
|
||||
this.className = className;
|
||||
this.c = c;
|
||||
this.isSystem = isSystem;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
void doit() {
|
||||
int i;
|
||||
c.copy(4 + 2 + 2); // magic min/maj version
|
||||
int constantPoolCountPos = c.generatedPosition();
|
||||
constantPoolCount = c.copyU2();
|
||||
// copy old constant pool
|
||||
c.copyConstantPool(constantPoolCount);
|
||||
|
||||
if (verbose) {
|
||||
System.out.println("ConstantPool expanded from: " +
|
||||
constantPoolCount);
|
||||
}
|
||||
|
||||
profiler = addClassToConstantPool(options.trackerClassName);
|
||||
if (options.shouldInstrumentNew || options.shouldInstrumentObjectInit) {
|
||||
if (options.shouldInstrumentIndexed) {
|
||||
if (allocInjector == null) {
|
||||
// first time - create it
|
||||
allocInjector = new IndexedInjector();
|
||||
}
|
||||
int allocTracker = addMethodToConstantPool(profiler,
|
||||
options.allocTrackerMethodName,
|
||||
"(I)V");
|
||||
allocInjector.reinit(allocTracker);
|
||||
} else if (options.shouldInstrumentObject) {
|
||||
if (allocInjector == null) {
|
||||
// first time - create it
|
||||
allocInjector = new ObjectInjector();
|
||||
}
|
||||
int allocTracker = addMethodToConstantPool(profiler,
|
||||
options.allocTrackerMethodName,
|
||||
"(Ljava/lang/Object;)V");
|
||||
allocInjector.reinit(allocTracker);
|
||||
} else {
|
||||
if (allocInjector == null) {
|
||||
// first time - create it
|
||||
allocInjector = new SimpleInjector();
|
||||
}
|
||||
int allocTracker = addMethodToConstantPool(profiler,
|
||||
options.allocTrackerMethodName,
|
||||
"()V");
|
||||
allocInjector.reinit(allocTracker);
|
||||
}
|
||||
defaultInjector = allocInjector;
|
||||
}
|
||||
if (options.shouldInstrumentCall) {
|
||||
if (options.shouldInstrumentIndexed) {
|
||||
if (callInjector == null) {
|
||||
// first time - create it
|
||||
callInjector = new IndexedInjector();
|
||||
}
|
||||
int callTracker = addMethodToConstantPool(profiler,
|
||||
options.callTrackerMethodName,
|
||||
"(I)V");
|
||||
callInjector.reinit(callTracker);
|
||||
} else {
|
||||
if (callInjector == null) {
|
||||
// first time - create it
|
||||
callInjector = new SimpleInjector();
|
||||
}
|
||||
int callTracker = addMethodToConstantPool(profiler,
|
||||
options.callTrackerMethodName,
|
||||
"()V");
|
||||
callInjector.reinit(callTracker);
|
||||
}
|
||||
defaultInjector = callInjector;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
System.out.println("To: " + constantPoolCount);
|
||||
}
|
||||
|
||||
c.setSection(1);
|
||||
|
||||
c.copy(2 + 2 + 2); // access, this, super
|
||||
int interfaceCount = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("interfaceCount: " + interfaceCount);
|
||||
}
|
||||
c.copy(interfaceCount * 2);
|
||||
copyFields(); // fields
|
||||
copyMethods(); // methods
|
||||
int attrCountPos = c.generatedPosition();
|
||||
int attrCount = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("class attrCount: " + attrCount);
|
||||
}
|
||||
// copy the class attributes
|
||||
copyAttrs(attrCount);
|
||||
|
||||
c.randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
|
||||
}
|
||||
|
||||
|
||||
void copyFields() {
|
||||
int count = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("fields count: " + count);
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
c.copy(6); // access, name, descriptor
|
||||
int attrCount = c.copyU2();
|
||||
if (verbose) {
|
||||
System.out.println("field attr count: " + attrCount);
|
||||
}
|
||||
copyAttrs(attrCount);
|
||||
}
|
||||
}
|
||||
|
||||
void copyMethods() {
|
||||
methodsCountPos = c.generatedPosition();
|
||||
methodsCount = c.copyU2();
|
||||
int initialMethodsCount = methodsCount;
|
||||
if (verbose) {
|
||||
System.out.println("methods count: " + methodsCount);
|
||||
}
|
||||
for (int i = 0; i < initialMethodsCount; ++i) {
|
||||
copyMethod();
|
||||
}
|
||||
}
|
||||
|
||||
void copyMethod() {
|
||||
int accessFlags = c.copyU2();// access flags
|
||||
if (options.shouldInstrumentNativeMethods && (accessFlags & ACC_NATIVE) != 0) {
|
||||
wrapNativeMethod(accessFlags);
|
||||
return;
|
||||
}
|
||||
int nameIndex = c.copyU2(); // name
|
||||
String methodName = c.constantPoolString(nameIndex);
|
||||
c.copyU2(); // descriptor
|
||||
int attrCount = c.copyU2(); // attribute count
|
||||
if (verbose) {
|
||||
System.out.println("methods attr count: " + attrCount);
|
||||
}
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForMethod(methodName, accessFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void wrapNativeMethod(int accessFlags) {
|
||||
// first, copy the native method with the name changed
|
||||
// accessFlags have already been copied
|
||||
int nameIndex = c.readU2(); // name
|
||||
String methodName = c.constantPoolString(nameIndex);
|
||||
String wrappedMethodName = options.wrappedPrefix + methodName;
|
||||
int wrappedNameIndex = writeCPEntryUtf8(wrappedMethodName);
|
||||
c.writeU2(wrappedNameIndex); // change to the wrapped name
|
||||
|
||||
int descriptorIndex = c.copyU2(); // descriptor index
|
||||
|
||||
int attrCount = c.copyU2(); // attribute count
|
||||
// need to replicate these attributes (esp Exceptions) in wrapper
|
||||
// so mark this location so we can rewind
|
||||
c.markLocalPositionStart();
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForMethod(methodName, accessFlags);
|
||||
}
|
||||
if (true) {
|
||||
System.err.println(" wrapped: " + methodName);
|
||||
}
|
||||
|
||||
// now write the wrapper method
|
||||
c.writeU2(accessFlags & ~ACC_NATIVE);
|
||||
c.writeU2(nameIndex); // original unwrapped name
|
||||
c.writeU2(descriptorIndex); // descriptor is the same
|
||||
|
||||
c.writeU2(attrCount + 1); // wrapped plus a code attribute
|
||||
// rewind to wrapped attributes
|
||||
c.rewind();
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForMethod(methodName, accessFlags);
|
||||
}
|
||||
|
||||
// generate a Code attribute for the wrapper method
|
||||
int wrappedIndex = addMethodToConstantPool(getThisClassIndex(),
|
||||
wrappedNameIndex,
|
||||
descriptorIndex);
|
||||
String descriptor = c.constantPoolString(descriptorIndex);
|
||||
createWrapperCodeAttr(nameIndex, accessFlags, descriptor, wrappedIndex);
|
||||
|
||||
// increment method count
|
||||
c.randomAccessWriteU2(methodsCountPos, ++methodsCount);
|
||||
}
|
||||
|
||||
void copyAttrs(int attrCount) {
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttr();
|
||||
}
|
||||
}
|
||||
|
||||
void copyAttr() {
|
||||
c.copy(2); // name
|
||||
int len = c.copyU4(); // attr len
|
||||
if (verbose) {
|
||||
System.out.println("attr len: " + len);
|
||||
}
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
|
||||
void copyAttrForMethod(String methodName, int accessFlags) {
|
||||
int nameIndex = c.copyU2(); // name
|
||||
// check for Code attr
|
||||
if (nameIndex == c.codeAttributeIndex) {
|
||||
try {
|
||||
copyCodeAttr(methodName);
|
||||
} catch (IOException exc) {
|
||||
System.err.println("Code Exception - " + exc);
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
int len = c.copyU4(); // attr len
|
||||
if (verbose) {
|
||||
System.out.println("method attr len: " + len);
|
||||
}
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
}
|
||||
|
||||
void copyAttrForCode(InjectBytecodes ib) throws IOException {
|
||||
int nameIndex = c.copyU2(); // name
|
||||
|
||||
// check for Code attr
|
||||
if (nameIndex == c.lineNumberAttributeIndex) {
|
||||
ib.copyLineNumberAttr();
|
||||
} else if (nameIndex == c.localVarAttributeIndex) {
|
||||
ib.copyLocalVarAttr();
|
||||
} else {
|
||||
int len = c.copyU4(); // attr len
|
||||
if (verbose) {
|
||||
System.out.println("code attr len: " + len);
|
||||
}
|
||||
c.copy(len); // attribute info
|
||||
}
|
||||
}
|
||||
|
||||
void copyCodeAttr(String methodName) throws IOException {
|
||||
if (verbose) {
|
||||
System.out.println("Code attr found");
|
||||
}
|
||||
int attrLengthPos = c.generatedPosition();
|
||||
int attrLength = c.copyU4(); // attr len
|
||||
int maxStack = c.readU2(); // max stack
|
||||
c.writeU2(defaultInjector == null? maxStack :
|
||||
defaultInjector.stackSize(maxStack)); // big enough for injected code
|
||||
c.copyU2(); // max locals
|
||||
int codeLengthPos = c.generatedPosition();
|
||||
int codeLength = c.copyU4(); // code length
|
||||
if (options.targetMethod != null && !options.targetMethod.equals(methodName)) {
|
||||
c.copy(attrLength - 8); // copy remainder minus already copied
|
||||
return;
|
||||
}
|
||||
if (isSystem) {
|
||||
if (codeLength == 1 && methodName.equals("finalize")) {
|
||||
if (verbose) {
|
||||
System.out.println("empty system finalizer not instrumented");
|
||||
}
|
||||
c.copy(attrLength - 8); // copy remainder minus already copied
|
||||
return;
|
||||
}
|
||||
if (codeLength == 1 && methodName.equals("<init>")) {
|
||||
if (verbose) {
|
||||
System.out.println("empty system constructor not instrumented");
|
||||
}
|
||||
if (!options.shouldInstrumentObjectInit) {
|
||||
c.copy(attrLength - 8); // copy remainder minus already copied
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (methodName.equals("<clinit>")) {
|
||||
if (verbose) {
|
||||
System.out.println("system class initializer not instrumented");
|
||||
}
|
||||
c.copy(attrLength - 8); // copy remainder minus already copied
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (options.shouldInstrumentObjectInit
|
||||
&& (!className.equals("java/lang/Object")
|
||||
|| !methodName.equals("<init>"))) {
|
||||
c.copy(attrLength - 8); // copy remainder minus already copied
|
||||
return;
|
||||
}
|
||||
|
||||
InjectBytecodes ib = new InjectBytecodes(c, codeLength, className, methodName);
|
||||
|
||||
if (options.shouldInstrumentNew) {
|
||||
ib.injectAfter(opc_new, allocInjector);
|
||||
ib.injectAfter(opc_newarray, allocInjector);
|
||||
ib.injectAfter(opc_anewarray, allocInjector);
|
||||
ib.injectAfter(opc_multianewarray, allocInjector);
|
||||
}
|
||||
if (options.shouldInstrumentCall) {
|
||||
ib.inject(0, callInjector.bytecodes(className, methodName, 0));
|
||||
}
|
||||
if (options.shouldInstrumentObjectInit) {
|
||||
ib.inject(0, allocInjector.bytecodes(className, methodName, 0));
|
||||
}
|
||||
|
||||
ib.adjustOffsets();
|
||||
|
||||
// fix up code length
|
||||
int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
|
||||
c.randomAccessWriteU4(codeLengthPos, newCodeLength);
|
||||
if (verbose) {
|
||||
System.out.println("code length old: " + codeLength +
|
||||
", new: " + newCodeLength);
|
||||
}
|
||||
|
||||
ib.copyExceptionTable();
|
||||
|
||||
int attrCount = c.copyU2();
|
||||
for (int i = 0; i < attrCount; ++i) {
|
||||
copyAttrForCode(ib);
|
||||
}
|
||||
|
||||
// fix up attr length
|
||||
int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
|
||||
c.randomAccessWriteU4(attrLengthPos, newAttrLength);
|
||||
if (verbose) {
|
||||
System.out.println("attr length old: " + attrLength +
|
||||
", new: " + newAttrLength);
|
||||
}
|
||||
}
|
||||
|
||||
int nextDescriptorIndex(String descriptor, int index) {
|
||||
switch (descriptor.charAt(index)) {
|
||||
case 'B': // byte
|
||||
case 'C': // char
|
||||
case 'I': // int
|
||||
case 'S': // short
|
||||
case 'Z': // boolean
|
||||
case 'F': // float
|
||||
case 'D': // double
|
||||
case 'J': // long
|
||||
return index + 1;
|
||||
case 'L': // object
|
||||
int i = index + 1;
|
||||
while (descriptor.charAt(i) != ';') {
|
||||
++i;
|
||||
}
|
||||
return i + 1;
|
||||
case '[': // array
|
||||
return nextDescriptorIndex(descriptor, index + 1);
|
||||
}
|
||||
throw new InternalError("should not reach here");
|
||||
}
|
||||
|
||||
int getWrappedTrackerIndex() {
|
||||
if (wrappedTrackerIndex == 0) {
|
||||
wrappedTrackerIndex = addMethodToConstantPool(profiler,
|
||||
options.wrappedTrackerMethodName,
|
||||
"(Ljava/lang/String;I)V");
|
||||
}
|
||||
return wrappedTrackerIndex;
|
||||
}
|
||||
|
||||
int getThisClassIndex() {
|
||||
if (thisClassIndex == 0) {
|
||||
thisClassIndex = addClassToConstantPool(className);
|
||||
}
|
||||
return thisClassIndex;
|
||||
}
|
||||
|
||||
int computeMaxLocals(String descriptor, int accessFlags) {
|
||||
int index = 1;
|
||||
int slot = 0;
|
||||
|
||||
if ((accessFlags & ACC_STATIC) == 0) {
|
||||
++slot;
|
||||
}
|
||||
char type;
|
||||
while ((type = descriptor.charAt(index)) != ')') {
|
||||
switch (type) {
|
||||
case 'B': // byte
|
||||
case 'C': // char
|
||||
case 'I': // int
|
||||
case 'S': // short
|
||||
case 'Z': // boolean
|
||||
case 'F': // float
|
||||
case 'L': // object
|
||||
case '[': // array
|
||||
++slot;
|
||||
break;
|
||||
case 'D': // double
|
||||
case 'J': // long
|
||||
slot += 2;
|
||||
break;
|
||||
}
|
||||
index = nextDescriptorIndex(descriptor, index);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
||||
void createWrapperCodeAttr(int methodNameIndex, int accessFlags,
|
||||
String descriptor, int wrappedIndex) {
|
||||
int maxLocals = computeMaxLocals(descriptor, accessFlags);
|
||||
|
||||
c.writeU2(c.codeAttributeIndex); //
|
||||
int attrLengthPos = c.generatedPosition();
|
||||
c.writeU4(0); // attr len -- fix up below
|
||||
c.writeU2(maxLocals + 4); // max stack
|
||||
c.writeU2(maxLocals); // max locals
|
||||
int codeLengthPos = c.generatedPosition();
|
||||
c.writeU4(0); // code length -- fix up below
|
||||
|
||||
int methodStringIndex = writeCPEntryString(methodNameIndex);
|
||||
|
||||
c.writeU1(opc_ldc_w);
|
||||
c.writeU2(methodStringIndex); // send the method name
|
||||
c.writeU1(opc_sipush);
|
||||
c.writeU2(options.fixedIndex);
|
||||
c.writeU1(opc_invokestatic);
|
||||
c.writeU2(getWrappedTrackerIndex());
|
||||
|
||||
// set-up args
|
||||
int index = 1;
|
||||
int slot = 0;
|
||||
if ((accessFlags & ACC_STATIC) == 0) {
|
||||
c.writeU1(opc_aload_0); // this
|
||||
++slot;
|
||||
}
|
||||
char type;
|
||||
while ((type = descriptor.charAt(index)) != ')') {
|
||||
switch (type) {
|
||||
case 'B': // byte
|
||||
case 'C': // char
|
||||
case 'I': // int
|
||||
case 'S': // short
|
||||
case 'Z': // boolean
|
||||
c.writeU1(opc_iload);
|
||||
c.writeU1(slot);
|
||||
++slot;
|
||||
break;
|
||||
case 'F': // float
|
||||
c.writeU1(opc_fload);
|
||||
c.writeU1(slot);
|
||||
++slot;
|
||||
break;
|
||||
case 'D': // double
|
||||
c.writeU1(opc_dload);
|
||||
c.writeU1(slot);
|
||||
slot += 2;
|
||||
break;
|
||||
case 'J': // long
|
||||
c.writeU1(opc_lload);
|
||||
c.writeU1(slot);
|
||||
slot += 2;
|
||||
break;
|
||||
case 'L': // object
|
||||
case '[': // array
|
||||
c.writeU1(opc_aload);
|
||||
c.writeU1(slot);
|
||||
++slot;
|
||||
break;
|
||||
}
|
||||
index = nextDescriptorIndex(descriptor, index);
|
||||
}
|
||||
|
||||
// call the wrapped version
|
||||
if ((accessFlags & ACC_STATIC) == 0) {
|
||||
c.writeU1(opc_invokevirtual);
|
||||
} else {
|
||||
c.writeU1(opc_invokestatic);
|
||||
}
|
||||
c.writeU2(wrappedIndex);
|
||||
|
||||
// return correct type
|
||||
switch (descriptor.charAt(index+1)) {
|
||||
case 'B': // byte
|
||||
case 'C': // char
|
||||
case 'I': // int
|
||||
case 'S': // short
|
||||
case 'Z': // boolean
|
||||
c.writeU1(opc_ireturn);
|
||||
break;
|
||||
case 'F': // float
|
||||
c.writeU1(opc_freturn);
|
||||
break;
|
||||
case 'D': // double
|
||||
c.writeU1(opc_dreturn);
|
||||
break;
|
||||
case 'J': // long
|
||||
c.writeU1(opc_lreturn);
|
||||
break;
|
||||
case 'L': // object
|
||||
case '[': // array
|
||||
c.writeU1(opc_areturn);
|
||||
break;
|
||||
case 'V': // void
|
||||
c.writeU1(opc_return);
|
||||
break;
|
||||
}
|
||||
|
||||
// end of code
|
||||
|
||||
// fix up code length
|
||||
int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
|
||||
c.randomAccessWriteU4(codeLengthPos, newCodeLength);
|
||||
|
||||
c.writeU2(0); // exception table length
|
||||
c.writeU2(0); // attribute count
|
||||
|
||||
// fix up attr length
|
||||
int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
|
||||
c.randomAccessWriteU4(attrLengthPos, newAttrLength);
|
||||
}
|
||||
|
||||
|
||||
int addClassToConstantPool(String className) {
|
||||
int prevSection = c.setSection(0);
|
||||
int classNameIndex = writeCPEntryUtf8(className);
|
||||
int classIndex = writeCPEntryClass(classNameIndex);
|
||||
c.setSection(prevSection);
|
||||
return classIndex;
|
||||
}
|
||||
|
||||
int addMethodToConstantPool(int classIndex,
|
||||
String methodName,
|
||||
String descr) {
|
||||
int prevSection = c.setSection(0);
|
||||
int methodNameIndex = writeCPEntryUtf8(methodName);
|
||||
int descrIndex = writeCPEntryUtf8(descr);
|
||||
c.setSection(prevSection);
|
||||
return addMethodToConstantPool(classIndex, methodNameIndex, descrIndex);
|
||||
}
|
||||
|
||||
int addMethodToConstantPool(int classIndex,
|
||||
int methodNameIndex,
|
||||
int descrIndex) {
|
||||
int prevSection = c.setSection(0);
|
||||
int nameAndTypeIndex = writeCPEntryNameAndType(methodNameIndex,
|
||||
descrIndex);
|
||||
int methodIndex = writeCPEntryMethodRef(classIndex, nameAndTypeIndex);
|
||||
c.setSection(prevSection);
|
||||
return methodIndex;
|
||||
}
|
||||
|
||||
int writeCPEntryUtf8(String str) {
|
||||
int prevSection = c.setSection(0);
|
||||
int len = str.length();
|
||||
c.writeU1(CONSTANT_UTF8); // Utf8 tag
|
||||
c.writeU2(len);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
c.writeU1(str.charAt(i));
|
||||
}
|
||||
c.setSection(prevSection);
|
||||
return constantPoolCount++;
|
||||
}
|
||||
|
||||
int writeCPEntryString(int utf8Index) {
|
||||
int prevSection = c.setSection(0);
|
||||
c.writeU1(CONSTANT_STRING);
|
||||
c.writeU2(utf8Index);
|
||||
c.setSection(prevSection);
|
||||
return constantPoolCount++;
|
||||
}
|
||||
|
||||
int writeCPEntryClass(int classNameIndex) {
|
||||
int prevSection = c.setSection(0);
|
||||
c.writeU1(CONSTANT_CLASS);
|
||||
c.writeU2(classNameIndex);
|
||||
c.setSection(prevSection);
|
||||
return constantPoolCount++;
|
||||
}
|
||||
|
||||
int writeCPEntryNameAndType(int nameIndex, int descrIndex) {
|
||||
int prevSection = c.setSection(0);
|
||||
c.writeU1(CONSTANT_NAMEANDTYPE);
|
||||
c.writeU2(nameIndex);
|
||||
c.writeU2(descrIndex);
|
||||
c.setSection(prevSection);
|
||||
return constantPoolCount++;
|
||||
}
|
||||
|
||||
int writeCPEntryMethodRef(int classIndex, int nameAndTypeIndex) {
|
||||
int prevSection = c.setSection(0);
|
||||
c.writeU1(CONSTANT_METHOD);
|
||||
c.writeU2(classIndex);
|
||||
c.writeU2(nameAndTypeIndex);
|
||||
c.setSection(prevSection);
|
||||
return constantPoolCount++;
|
||||
}
|
||||
}
|
||||
@ -1,769 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* An extension of BinaryCode that allows code to be printed.
|
||||
* Includes printing of disassembled byte codes, exception info,
|
||||
* local variable and line number info.
|
||||
*
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
class InjectBytecodes implements RuntimeConstants {
|
||||
|
||||
private final ClassReaderWriter c;
|
||||
private final PrintStream output;
|
||||
private final int length;
|
||||
private final int[] map;
|
||||
private final byte[] widening;
|
||||
private final Injector[] before = new Injector[256];
|
||||
private final Injector[] after = new Injector[256];
|
||||
private final String className;
|
||||
private final String methodName;
|
||||
private final Map<Integer,byte[]> snippets = new HashMap<>();
|
||||
|
||||
private int pos;
|
||||
private int newPos;
|
||||
|
||||
private class Span {
|
||||
final int delta;
|
||||
final int target;
|
||||
final int newDelta;
|
||||
final int newTarget;
|
||||
|
||||
Span(int delta) {
|
||||
this.delta = delta;
|
||||
this.target = pos + delta;
|
||||
this.newTarget = map[target];
|
||||
this.newDelta = newTarget - newPos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
InjectBytecodes(ClassReaderWriter c, int length,
|
||||
String className, String methodName) {
|
||||
this.c = c;
|
||||
this.output = System.out;
|
||||
this.length = length;
|
||||
this.map = new int[length + 1];
|
||||
this.widening = new byte[length + 1];
|
||||
this.className = className;
|
||||
this.methodName = methodName;
|
||||
c.markLocalPositionStart();
|
||||
for (int i = 0; i <= length; ++i) {
|
||||
map[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public void inject(int at, byte[] newCode) {
|
||||
snippets.put(new Integer(at), newCode);
|
||||
trace("external ");
|
||||
inject(at, newCode.length);
|
||||
}
|
||||
|
||||
private void inject(int at, int len) {
|
||||
if (Inject.verbose) {
|
||||
traceln("Injecting " + len + " at " + at);
|
||||
}
|
||||
for (int i = at; i <= length; ++i) {
|
||||
map[i] += len;
|
||||
}
|
||||
}
|
||||
|
||||
private void widen(int at, int len) {
|
||||
int delta = len - widening[at];
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceln("Widening to " + len + " at " + at);
|
||||
}
|
||||
inject(c.localPosition(), delta); // inject at end of instruction
|
||||
widening[at] = (byte)len; // mark at beginning of instruction
|
||||
}
|
||||
|
||||
public void injectBefore(int code, Injector inj) {
|
||||
before[code] = inj;
|
||||
}
|
||||
|
||||
public void injectAfter(int code, Injector inj) {
|
||||
after[code] = inj;
|
||||
}
|
||||
|
||||
private void trace(String str) {
|
||||
if (Inject.verbose) {
|
||||
output.print(str);
|
||||
}
|
||||
}
|
||||
|
||||
private void traceln(String str) {
|
||||
if (Inject.verbose) {
|
||||
output.println(str);
|
||||
}
|
||||
}
|
||||
|
||||
private void traceln() {
|
||||
if (Inject.verbose) {
|
||||
output.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void trace(int i) {
|
||||
if (Inject.verbose) {
|
||||
output.print(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an integer so that it takes 'length' characters in
|
||||
* the output. Temporary until formatting code is stable.
|
||||
*/
|
||||
private void traceFixedWidthInt(int x, int length) {
|
||||
if (Inject.verbose) {
|
||||
CharArrayWriter baStream = new CharArrayWriter();
|
||||
PrintWriter pStream = new PrintWriter(baStream);
|
||||
pStream.print(x);
|
||||
String str = baStream.toString();
|
||||
for (int cnt = length - str.length(); cnt > 0; --cnt)
|
||||
trace(" ");
|
||||
trace(str);
|
||||
}
|
||||
}
|
||||
|
||||
void adjustOffsets() throws IOException {
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceln("Method " + methodName);
|
||||
traceln();
|
||||
}
|
||||
c.rewind();
|
||||
while (c.localPosition() < length) {
|
||||
insertAtInstruction();
|
||||
}
|
||||
trace("Searching for adjustments...");
|
||||
c.rewind();
|
||||
while (c.localPosition() < length) {
|
||||
if (!adjustInstruction()) {
|
||||
c.rewind();
|
||||
traceln();
|
||||
traceln("Restarting adjustments after change...");
|
||||
}
|
||||
}
|
||||
// write the new bytecodes
|
||||
traceln();
|
||||
traceln();
|
||||
trace("Writing new code...");
|
||||
c.rewind();
|
||||
while (c.localPosition() < length) {
|
||||
writeInstruction();
|
||||
}
|
||||
if (!snippets.isEmpty()) {
|
||||
throw new Error("not all snippets written");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk one instruction inserting instrumentation at specified instructions
|
||||
*/
|
||||
private void insertAtInstruction() throws IOException {
|
||||
pos = c.localPosition();
|
||||
int opcode = c.readU1();
|
||||
if (opcode == opc_wide) {
|
||||
// no support for instrumenting wide instructions
|
||||
int wopcode = c.readU1();
|
||||
int lvIndex = c.readU2();
|
||||
switch (wopcode) {
|
||||
case opc_aload: case opc_astore:
|
||||
case opc_fload: case opc_fstore:
|
||||
case opc_iload: case opc_istore:
|
||||
case opc_lload: case opc_lstore:
|
||||
case opc_dload: case opc_dstore:
|
||||
case opc_ret:
|
||||
break;
|
||||
|
||||
case opc_iinc:
|
||||
c.readS2();
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid wide opcode: " + wopcode);
|
||||
}
|
||||
} else {
|
||||
Injector inj;
|
||||
|
||||
inj = before[opcode];
|
||||
if (inj != null) {
|
||||
inject(pos, inj.bytecodes(className, methodName, pos));
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case opc_tableswitch:{
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
c.skip(header - (pos+1)); // skip old padding
|
||||
|
||||
c.readU4();
|
||||
int low = c.readU4();
|
||||
int high = c.readU4();
|
||||
c.skip((high+1-low) * 4);
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_lookupswitch:{
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
c.skip(header - (pos+1)); // skip padding
|
||||
|
||||
c.readU4();
|
||||
int npairs = c.readU4();
|
||||
c.skip(npairs * 8);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int instrLen = opcLengths[opcode];
|
||||
c.skip(instrLen-1);
|
||||
}
|
||||
}
|
||||
inj = after[opcode];
|
||||
if (inj != null) {
|
||||
pos = c.localPosition();
|
||||
inject(pos, inj.bytecodes(className, methodName, pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk one instruction adjusting for insertions
|
||||
*/
|
||||
private boolean adjustInstruction() throws IOException {
|
||||
pos = c.localPosition();
|
||||
newPos = map[pos];
|
||||
int opcode = c.readU1();
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceFixedWidthInt(pos, 4);
|
||||
traceFixedWidthInt(newPos, 4);
|
||||
trace(" ");
|
||||
}
|
||||
if (opcode == opc_wide) {
|
||||
int wopcode = c.readU1();
|
||||
int lvIndex = c.readU2();
|
||||
if (Inject.verbose) {
|
||||
trace(opcNames[wopcode] + "_w ");
|
||||
}
|
||||
switch (wopcode) {
|
||||
case opc_aload: case opc_astore:
|
||||
case opc_fload: case opc_fstore:
|
||||
case opc_iload: case opc_istore:
|
||||
case opc_lload: case opc_lstore:
|
||||
case opc_dload: case opc_dstore:
|
||||
case opc_ret:
|
||||
trace(lvIndex);
|
||||
break;
|
||||
|
||||
case opc_iinc:
|
||||
int constVal = c.readS2();
|
||||
if (Inject.verbose) {
|
||||
trace(lvIndex + " " + constVal);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid wide opcode: " + wopcode);
|
||||
}
|
||||
} else {
|
||||
if (Inject.verbose) {
|
||||
trace(opcNames[opcode]);
|
||||
}
|
||||
switch (opcode) {
|
||||
|
||||
case opc_tableswitch:{
|
||||
int widened = widening[pos];
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
||||
|
||||
c.skip(header - (pos+1)); // skip old padding
|
||||
|
||||
Span defaultSkip = new Span(c.readU4());
|
||||
int low = c.readU4();
|
||||
int high = c.readU4();
|
||||
if (Inject.verbose) {
|
||||
trace(" " + low + " to " + high);
|
||||
trace(": default= [was] " + defaultSkip.target);
|
||||
trace(" [now] " + defaultSkip.newTarget);
|
||||
for (int i = low; i <= high; ++i) {
|
||||
Span jump = new Span(c.readU4());
|
||||
traceln("");
|
||||
trace('\t');
|
||||
traceFixedWidthInt(i, 5);
|
||||
trace(": " + jump.newTarget);
|
||||
}
|
||||
} else {
|
||||
c.skip((high+1-low) * 4);
|
||||
}
|
||||
int newPadding = newHeader - newPos;
|
||||
int oldPadding = header - pos;
|
||||
int deltaPadding = newPadding - oldPadding;
|
||||
if (widened != deltaPadding) {
|
||||
widen(pos, deltaPadding);
|
||||
return false; // cause restart
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_lookupswitch:{
|
||||
int widened = widening[pos];
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
||||
|
||||
c.skip(header - (pos+1)); // skip old padding
|
||||
|
||||
Span defaultSkip = new Span(c.readU4());
|
||||
int npairs = c.readU4();
|
||||
if (Inject.verbose) {
|
||||
trace(" npairs: " + npairs);
|
||||
trace(": default= [was] " + defaultSkip.target);
|
||||
trace(" [now] " + defaultSkip.newTarget);
|
||||
for (int i = 0; i< npairs; ++i) {
|
||||
int match = c.readU4();
|
||||
Span jump = new Span(c.readU4());
|
||||
traceln("");
|
||||
trace('\t');
|
||||
traceFixedWidthInt(match, 5);
|
||||
trace(": " + jump.newTarget);
|
||||
}
|
||||
} else {
|
||||
c.skip(npairs * 8);
|
||||
}
|
||||
int newPadding = newHeader - newPos;
|
||||
int oldPadding = header - pos;
|
||||
int deltaPadding = newPadding - oldPadding;
|
||||
if (widened != deltaPadding) {
|
||||
widen(pos, deltaPadding);
|
||||
return false; // cause restart
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_jsr: case opc_goto:
|
||||
case opc_ifeq: case opc_ifge: case opc_ifgt:
|
||||
case opc_ifle: case opc_iflt: case opc_ifne:
|
||||
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
|
||||
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
|
||||
case opc_if_acmpeq: case opc_if_acmpne:
|
||||
case opc_ifnull: case opc_ifnonnull: {
|
||||
int widened = widening[pos];
|
||||
Span jump = new Span(c.readS2());
|
||||
if (widened == 0) { // not yet widened
|
||||
int newDelta = jump.newDelta;
|
||||
if ((newDelta < -32768) || (newDelta > 32767)) {
|
||||
switch (opcode) {
|
||||
case opc_jsr: case opc_goto:
|
||||
widen(pos, 2); // will convert to wide
|
||||
break;
|
||||
default:
|
||||
widen(pos, 5); // will inject goto_w
|
||||
break;
|
||||
}
|
||||
return false; // cause restart
|
||||
}
|
||||
}
|
||||
if (Inject.verbose) {
|
||||
trace(" [was] " + jump.target + " ==> " +
|
||||
" [now] " + jump.newTarget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_jsr_w:
|
||||
case opc_goto_w: {
|
||||
Span jump = new Span(c.readU4());
|
||||
if (Inject.verbose) {
|
||||
trace(" [was] " + jump.target +
|
||||
" [now] " + jump.newTarget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int instrLen = opcLengths[opcode];
|
||||
c.skip(instrLen-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true; // successful return
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walk one instruction writing the transformed instruction.
|
||||
*/
|
||||
private void writeInstruction() throws IOException {
|
||||
pos = c.localPosition();
|
||||
newPos = map[pos];
|
||||
byte[] newCode = snippets.remove(new Integer(pos));
|
||||
if (newCode != null) {
|
||||
traceln();
|
||||
traceFixedWidthInt(pos, 4);
|
||||
trace(" ... -- Inserting new code");
|
||||
c.writeBytes(newCode);
|
||||
}
|
||||
int opcode = c.readU1();
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceFixedWidthInt(pos, 4);
|
||||
traceFixedWidthInt(newPos, 4);
|
||||
trace(" ");
|
||||
}
|
||||
if (opcode == opc_wide) {
|
||||
int wopcode = c.readU1();
|
||||
int lvIndex = c.readU2();
|
||||
if (Inject.verbose) {
|
||||
trace(opcNames[wopcode] + "_w ");
|
||||
}
|
||||
c.writeU1(opcode);
|
||||
c.writeU1(wopcode);
|
||||
c.writeU2(lvIndex);
|
||||
switch (wopcode) {
|
||||
case opc_aload: case opc_astore:
|
||||
case opc_fload: case opc_fstore:
|
||||
case opc_iload: case opc_istore:
|
||||
case opc_lload: case opc_lstore:
|
||||
case opc_dload: case opc_dstore:
|
||||
case opc_ret:
|
||||
trace(lvIndex);
|
||||
break;
|
||||
|
||||
case opc_iinc:
|
||||
int constVal = c.readS2();
|
||||
c.writeU2(constVal); // ??? U vs S
|
||||
if (Inject.verbose) {
|
||||
trace(lvIndex + " " + constVal);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error("Invalid wide opcode: " + wopcode);
|
||||
}
|
||||
} else {
|
||||
if (Inject.verbose) {
|
||||
trace(opcNames[opcode]);
|
||||
}
|
||||
switch (opcode) {
|
||||
|
||||
case opc_tableswitch:{
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
||||
|
||||
c.skip(header - (pos+1)); // skip old padding
|
||||
|
||||
Span defaultSkip = new Span(c.readU4());
|
||||
int low = c.readU4();
|
||||
int high = c.readU4();
|
||||
|
||||
c.writeU1(opcode); // copy instruction
|
||||
for (int i = newPos+1; i < newHeader; ++i) {
|
||||
c.writeU1(0); // write new padding
|
||||
}
|
||||
c.writeU4(defaultSkip.newDelta);
|
||||
c.writeU4(low);
|
||||
c.writeU4(high);
|
||||
|
||||
if (Inject.verbose) {
|
||||
trace(" " + low + " to " + high);
|
||||
trace(": default= [was] " + defaultSkip.target);
|
||||
trace(" [now] " + defaultSkip.newTarget);
|
||||
}
|
||||
for (int i = low; i <= high; ++i) {
|
||||
Span jump = new Span(c.readU4());
|
||||
c.writeU4(jump.newDelta);
|
||||
if (Inject.verbose) {
|
||||
traceln("");
|
||||
trace('\t');
|
||||
traceFixedWidthInt(i, 5);
|
||||
trace(": " + jump.newTarget);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_lookupswitch:{
|
||||
int header = (pos+1+3) & (~3); // 4byte boundry
|
||||
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
||||
|
||||
c.skip(header - (pos+1)); // skip old padding
|
||||
|
||||
Span defaultSkip = new Span(c.readU4());
|
||||
int npairs = c.readU4();
|
||||
if (Inject.verbose) {
|
||||
trace(" npairs: " + npairs);
|
||||
trace(": default= [was] " + defaultSkip.target);
|
||||
trace(" [now] " + defaultSkip.newTarget);
|
||||
}
|
||||
c.writeU1(opcode); // copy instruction
|
||||
for (int i = newPos+1; i < newHeader; ++i) {
|
||||
c.writeU1(0); // write new padding
|
||||
}
|
||||
c.writeU4(defaultSkip.newDelta);
|
||||
c.writeU4(npairs);
|
||||
for (int i = 0; i< npairs; ++i) {
|
||||
int match = c.readU4();
|
||||
Span jump = new Span(c.readU4());
|
||||
c.writeU4(match);
|
||||
c.writeU4(jump.newDelta);
|
||||
if (Inject.verbose) {
|
||||
traceln("");
|
||||
trace('\t');
|
||||
traceFixedWidthInt(match, 5);
|
||||
trace(": " + jump.newTarget);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_jsr: case opc_goto:
|
||||
case opc_ifeq: case opc_ifge: case opc_ifgt:
|
||||
case opc_ifle: case opc_iflt: case opc_ifne:
|
||||
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
|
||||
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
|
||||
case opc_if_acmpeq: case opc_if_acmpne:
|
||||
case opc_ifnull: case opc_ifnonnull: {
|
||||
int widened = widening[pos];
|
||||
Span jump = new Span(c.readS2());
|
||||
int newOpcode = opcode; // default to unchanged
|
||||
if (widened == 0) { // not widened
|
||||
c.writeU1(opcode); // rewrite instruction
|
||||
c.writeU2(jump.newDelta);
|
||||
} else if (widened == 2) { // wide form
|
||||
switch (opcode) {
|
||||
case opc_jsr:
|
||||
newOpcode = opc_jsr_w;
|
||||
break;
|
||||
case opc_goto:
|
||||
newOpcode = opc_jsr_w;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unexpected opcode: " +
|
||||
opcode);
|
||||
}
|
||||
c.writeU1(newOpcode); // write wide instruction
|
||||
c.writeU4(jump.newDelta); // write new and wide delta
|
||||
} else if (widened == 5) { // insert goto_w
|
||||
switch (opcode) {
|
||||
case opc_ifeq:
|
||||
newOpcode = opc_ifne;
|
||||
break;
|
||||
case opc_ifge:
|
||||
newOpcode = opc_iflt;
|
||||
break;
|
||||
case opc_ifgt:
|
||||
newOpcode = opc_ifle;
|
||||
break;
|
||||
case opc_ifle:
|
||||
newOpcode = opc_ifgt;
|
||||
break;
|
||||
case opc_iflt:
|
||||
newOpcode = opc_ifge;
|
||||
break;
|
||||
case opc_ifne:
|
||||
newOpcode = opc_ifeq;
|
||||
break;
|
||||
case opc_if_icmpeq:
|
||||
newOpcode = opc_if_icmpne;
|
||||
break;
|
||||
case opc_if_icmpne:
|
||||
newOpcode = opc_if_icmpeq;
|
||||
break;
|
||||
case opc_if_icmpge:
|
||||
newOpcode = opc_if_icmplt;
|
||||
break;
|
||||
case opc_if_icmpgt:
|
||||
newOpcode = opc_if_icmple;
|
||||
break;
|
||||
case opc_if_icmple:
|
||||
newOpcode = opc_if_icmpgt;
|
||||
break;
|
||||
case opc_if_icmplt:
|
||||
newOpcode = opc_if_icmpge;
|
||||
break;
|
||||
case opc_if_acmpeq:
|
||||
newOpcode = opc_if_acmpne;
|
||||
break;
|
||||
case opc_if_acmpne:
|
||||
newOpcode = opc_if_acmpeq;
|
||||
break;
|
||||
case opc_ifnull:
|
||||
newOpcode = opc_ifnonnull;
|
||||
break;
|
||||
case opc_ifnonnull:
|
||||
newOpcode = opc_ifnull;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unexpected opcode: " +
|
||||
opcode);
|
||||
}
|
||||
c.writeU1(newOpcode); // write inverse branch
|
||||
c.writeU2(3 + 5); // beyond if and goto_w
|
||||
c.writeU1(opc_goto_w);// add a goto_w
|
||||
c.writeU4(jump.newDelta); // write new and wide delta
|
||||
} else {
|
||||
throw new Error("unexpected widening");
|
||||
}
|
||||
|
||||
if (Inject.verbose) {
|
||||
trace(" [was] " + jump.target + " ==> " +
|
||||
opcNames[newOpcode] +
|
||||
" [now] " + jump.newTarget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case opc_jsr_w:
|
||||
case opc_goto_w: {
|
||||
Span jump = new Span(c.readU4());
|
||||
c.writeU1(opcode); // instruction itself
|
||||
c.writeU4(jump.newDelta);
|
||||
if (Inject.verbose) {
|
||||
trace(" [was] " + jump.target +
|
||||
" [now] " + jump.newTarget);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
int instrLen = opcLengths[opcode];
|
||||
c.writeU1(opcode); // instruction itself
|
||||
c.copy(instrLen-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the exception table for this method code
|
||||
*/
|
||||
void copyExceptionTable() throws IOException {
|
||||
int tableLength = c.copyU2(); // exception table len
|
||||
if (tableLength > 0) {
|
||||
traceln();
|
||||
traceln("Exception table:");
|
||||
traceln(" from:old/new to:old/new target:old/new type");
|
||||
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
||||
int startPC = c.readU2();
|
||||
int newStartPC = map[startPC];
|
||||
c.writeU2(newStartPC);
|
||||
int endPC = c.readU2();
|
||||
int newEndPC = map[endPC];
|
||||
c.writeU2(newEndPC);
|
||||
int handlerPC = c.readU2();
|
||||
int newHandlerPC = map[handlerPC];
|
||||
c.writeU2(newHandlerPC);
|
||||
int catchType = c.copyU2();
|
||||
if (Inject.verbose) {
|
||||
traceFixedWidthInt(startPC, 6);
|
||||
traceFixedWidthInt(newStartPC, 6);
|
||||
traceFixedWidthInt(endPC, 6);
|
||||
traceFixedWidthInt(newEndPC, 6);
|
||||
traceFixedWidthInt(handlerPC, 6);
|
||||
traceFixedWidthInt(newHandlerPC, 6);
|
||||
trace(" ");
|
||||
if (catchType == 0)
|
||||
traceln("any");
|
||||
else {
|
||||
traceln("" + catchType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the line number table for this method code
|
||||
*/
|
||||
void copyLineNumberAttr() throws IOException {
|
||||
// name index already read
|
||||
c.copy(4); // attr len
|
||||
int tableLength = c.copyU2(); // line table len
|
||||
if (tableLength > 0) {
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceln("Line numbers for method " + methodName);
|
||||
}
|
||||
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
||||
int startPC = c.readU2();
|
||||
int newStartPC = map[startPC];
|
||||
c.writeU2(newStartPC);
|
||||
int lineNumber = c.copyU2();
|
||||
if (Inject.verbose) {
|
||||
traceln(" line " + lineNumber +
|
||||
": [was] " + startPC +
|
||||
" [now] " + newStartPC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the local variable table for this method code
|
||||
*/
|
||||
void copyLocalVarAttr() throws IOException {
|
||||
// name index already read
|
||||
c.copy(4); // attr len
|
||||
int tableLength = c.copyU2(); // local var table len
|
||||
if (tableLength > 0) {
|
||||
if (Inject.verbose) {
|
||||
traceln();
|
||||
traceln("Local variables for method " + methodName);
|
||||
}
|
||||
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
||||
int startPC = c.readU2();
|
||||
int newStartPC = map[startPC];
|
||||
c.writeU2(newStartPC);
|
||||
int length = c.readU2();
|
||||
int endPC = startPC + length;
|
||||
int newEndPC = map[endPC];
|
||||
int newLength = newEndPC - newStartPC;
|
||||
c.writeU2(newLength);
|
||||
int nameIndex = c.copyU2();
|
||||
int descriptorIndex = c.copyU2();
|
||||
int index = c.copyU2();
|
||||
if (Inject.verbose) {
|
||||
trace(" ");
|
||||
trace(descriptorIndex);
|
||||
trace(" ");
|
||||
trace(nameIndex);
|
||||
traceln(" pc= [was] " + startPC + " [now] " + newStartPC +
|
||||
", length= [was] " + length + " [now] " + newLength +
|
||||
", slot=" + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
interface Injector {
|
||||
byte[] bytecodes(String className, String methodName, int location);
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
public class Options {
|
||||
public boolean shouldInstrumentNew = false;
|
||||
public boolean shouldInstrumentCall = false;
|
||||
public boolean shouldInstrumentIndexed = false;
|
||||
public boolean shouldInstrumentObject = false;
|
||||
public boolean shouldInstrumentObjectInit = false;
|
||||
public boolean shouldInstrumentNativeMethods = false;
|
||||
public String targetMethod = null;
|
||||
public int fixedIndex = 0;
|
||||
public String trackerClassName = "MyTracker";
|
||||
public String allocTrackerMethodName = "allocTracker";
|
||||
public String callTrackerMethodName = "callTracker";
|
||||
public String wrappedTrackerMethodName = "wrappedTracker";
|
||||
public String wrappedPrefix = "wrapped_up_";
|
||||
}
|
||||
@ -1,732 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package ilib;
|
||||
|
||||
public interface RuntimeConstants {
|
||||
|
||||
/* Signature Characters */
|
||||
char SIGC_VOID = 'V';
|
||||
String SIG_VOID = "V";
|
||||
char SIGC_BOOLEAN = 'Z';
|
||||
String SIG_BOOLEAN = "Z";
|
||||
char SIGC_BYTE = 'B';
|
||||
String SIG_BYTE = "B";
|
||||
char SIGC_CHAR = 'C';
|
||||
String SIG_CHAR = "C";
|
||||
char SIGC_SHORT = 'S';
|
||||
String SIG_SHORT = "S";
|
||||
char SIGC_INT = 'I';
|
||||
String SIG_INT = "I";
|
||||
char SIGC_LONG = 'J';
|
||||
String SIG_LONG = "J";
|
||||
char SIGC_FLOAT = 'F';
|
||||
String SIG_FLOAT = "F";
|
||||
char SIGC_DOUBLE = 'D';
|
||||
String SIG_DOUBLE = "D";
|
||||
char SIGC_ARRAY = '[';
|
||||
String SIG_ARRAY = "[";
|
||||
char SIGC_CLASS = 'L';
|
||||
String SIG_CLASS = "L";
|
||||
char SIGC_METHOD = '(';
|
||||
String SIG_METHOD = "(";
|
||||
char SIGC_ENDCLASS = ';';
|
||||
String SIG_ENDCLASS = ";";
|
||||
char SIGC_ENDMETHOD = ')';
|
||||
String SIG_ENDMETHOD = ")";
|
||||
char SIGC_PACKAGE = '/';
|
||||
String SIG_PACKAGE = "/";
|
||||
|
||||
/* Class File Constants */
|
||||
int JAVA_MAGIC = 0xcafebabe;
|
||||
int JAVA_MIN_SUPPORTED_VERSION = 45;
|
||||
int JAVA_MAX_SUPPORTED_VERSION = 48;
|
||||
int JAVA_MAX_SUPPORTED_MINOR_VERSION = 0;
|
||||
|
||||
/* Generate class file version for 1.1 by default */
|
||||
int JAVA_DEFAULT_VERSION = 45;
|
||||
int JAVA_DEFAULT_MINOR_VERSION = 3;
|
||||
|
||||
/* Constant table */
|
||||
int CONSTANT_UTF8 = 1;
|
||||
int CONSTANT_UNICODE = 2;
|
||||
int CONSTANT_INTEGER = 3;
|
||||
int CONSTANT_FLOAT = 4;
|
||||
int CONSTANT_LONG = 5;
|
||||
int CONSTANT_DOUBLE = 6;
|
||||
int CONSTANT_CLASS = 7;
|
||||
int CONSTANT_STRING = 8;
|
||||
int CONSTANT_FIELD = 9;
|
||||
int CONSTANT_METHOD = 10;
|
||||
int CONSTANT_INTERFACEMETHOD = 11;
|
||||
int CONSTANT_NAMEANDTYPE = 12;
|
||||
|
||||
/* Access and modifier flags */
|
||||
int ACC_PUBLIC = 0x00000001;
|
||||
int ACC_PRIVATE = 0x00000002;
|
||||
int ACC_PROTECTED = 0x00000004;
|
||||
int ACC_STATIC = 0x00000008;
|
||||
int ACC_FINAL = 0x00000010;
|
||||
int ACC_SYNCHRONIZED = 0x00000020;
|
||||
int ACC_VOLATILE = 0x00000040;
|
||||
int ACC_TRANSIENT = 0x00000080;
|
||||
int ACC_NATIVE = 0x00000100;
|
||||
int ACC_INTERFACE = 0x00000200;
|
||||
int ACC_ABSTRACT = 0x00000400;
|
||||
int ACC_SUPER = 0x00000020;
|
||||
int ACC_STRICT = 0x00000800;
|
||||
|
||||
/* Type codes */
|
||||
int T_CLASS = 0x00000002;
|
||||
int T_BOOLEAN = 0x00000004;
|
||||
int T_CHAR = 0x00000005;
|
||||
int T_FLOAT = 0x00000006;
|
||||
int T_DOUBLE = 0x00000007;
|
||||
int T_BYTE = 0x00000008;
|
||||
int T_SHORT = 0x00000009;
|
||||
int T_INT = 0x0000000a;
|
||||
int T_LONG = 0x0000000b;
|
||||
|
||||
/* Opcodes */
|
||||
int opc_try = -3;
|
||||
int opc_dead = -2;
|
||||
int opc_label = -1;
|
||||
int opc_nop = 0;
|
||||
int opc_aconst_null = 1;
|
||||
int opc_iconst_m1 = 2;
|
||||
int opc_iconst_0 = 3;
|
||||
int opc_iconst_1 = 4;
|
||||
int opc_iconst_2 = 5;
|
||||
int opc_iconst_3 = 6;
|
||||
int opc_iconst_4 = 7;
|
||||
int opc_iconst_5 = 8;
|
||||
int opc_lconst_0 = 9;
|
||||
int opc_lconst_1 = 10;
|
||||
int opc_fconst_0 = 11;
|
||||
int opc_fconst_1 = 12;
|
||||
int opc_fconst_2 = 13;
|
||||
int opc_dconst_0 = 14;
|
||||
int opc_dconst_1 = 15;
|
||||
int opc_bipush = 16;
|
||||
int opc_sipush = 17;
|
||||
int opc_ldc = 18;
|
||||
int opc_ldc_w = 19;
|
||||
int opc_ldc2_w = 20;
|
||||
int opc_iload = 21;
|
||||
int opc_lload = 22;
|
||||
int opc_fload = 23;
|
||||
int opc_dload = 24;
|
||||
int opc_aload = 25;
|
||||
int opc_iload_0 = 26;
|
||||
int opc_iload_1 = 27;
|
||||
int opc_iload_2 = 28;
|
||||
int opc_iload_3 = 29;
|
||||
int opc_lload_0 = 30;
|
||||
int opc_lload_1 = 31;
|
||||
int opc_lload_2 = 32;
|
||||
int opc_lload_3 = 33;
|
||||
int opc_fload_0 = 34;
|
||||
int opc_fload_1 = 35;
|
||||
int opc_fload_2 = 36;
|
||||
int opc_fload_3 = 37;
|
||||
int opc_dload_0 = 38;
|
||||
int opc_dload_1 = 39;
|
||||
int opc_dload_2 = 40;
|
||||
int opc_dload_3 = 41;
|
||||
int opc_aload_0 = 42;
|
||||
int opc_aload_1 = 43;
|
||||
int opc_aload_2 = 44;
|
||||
int opc_aload_3 = 45;
|
||||
int opc_iaload = 46;
|
||||
int opc_laload = 47;
|
||||
int opc_faload = 48;
|
||||
int opc_daload = 49;
|
||||
int opc_aaload = 50;
|
||||
int opc_baload = 51;
|
||||
int opc_caload = 52;
|
||||
int opc_saload = 53;
|
||||
int opc_istore = 54;
|
||||
int opc_lstore = 55;
|
||||
int opc_fstore = 56;
|
||||
int opc_dstore = 57;
|
||||
int opc_astore = 58;
|
||||
int opc_istore_0 = 59;
|
||||
int opc_istore_1 = 60;
|
||||
int opc_istore_2 = 61;
|
||||
int opc_istore_3 = 62;
|
||||
int opc_lstore_0 = 63;
|
||||
int opc_lstore_1 = 64;
|
||||
int opc_lstore_2 = 65;
|
||||
int opc_lstore_3 = 66;
|
||||
int opc_fstore_0 = 67;
|
||||
int opc_fstore_1 = 68;
|
||||
int opc_fstore_2 = 69;
|
||||
int opc_fstore_3 = 70;
|
||||
int opc_dstore_0 = 71;
|
||||
int opc_dstore_1 = 72;
|
||||
int opc_dstore_2 = 73;
|
||||
int opc_dstore_3 = 74;
|
||||
int opc_astore_0 = 75;
|
||||
int opc_astore_1 = 76;
|
||||
int opc_astore_2 = 77;
|
||||
int opc_astore_3 = 78;
|
||||
int opc_iastore = 79;
|
||||
int opc_lastore = 80;
|
||||
int opc_fastore = 81;
|
||||
int opc_dastore = 82;
|
||||
int opc_aastore = 83;
|
||||
int opc_bastore = 84;
|
||||
int opc_castore = 85;
|
||||
int opc_sastore = 86;
|
||||
int opc_pop = 87;
|
||||
int opc_pop2 = 88;
|
||||
int opc_dup = 89;
|
||||
int opc_dup_x1 = 90;
|
||||
int opc_dup_x2 = 91;
|
||||
int opc_dup2 = 92;
|
||||
int opc_dup2_x1 = 93;
|
||||
int opc_dup2_x2 = 94;
|
||||
int opc_swap = 95;
|
||||
int opc_iadd = 96;
|
||||
int opc_ladd = 97;
|
||||
int opc_fadd = 98;
|
||||
int opc_dadd = 99;
|
||||
int opc_isub = 100;
|
||||
int opc_lsub = 101;
|
||||
int opc_fsub = 102;
|
||||
int opc_dsub = 103;
|
||||
int opc_imul = 104;
|
||||
int opc_lmul = 105;
|
||||
int opc_fmul = 106;
|
||||
int opc_dmul = 107;
|
||||
int opc_idiv = 108;
|
||||
int opc_ldiv = 109;
|
||||
int opc_fdiv = 110;
|
||||
int opc_ddiv = 111;
|
||||
int opc_irem = 112;
|
||||
int opc_lrem = 113;
|
||||
int opc_frem = 114;
|
||||
int opc_drem = 115;
|
||||
int opc_ineg = 116;
|
||||
int opc_lneg = 117;
|
||||
int opc_fneg = 118;
|
||||
int opc_dneg = 119;
|
||||
int opc_ishl = 120;
|
||||
int opc_lshl = 121;
|
||||
int opc_ishr = 122;
|
||||
int opc_lshr = 123;
|
||||
int opc_iushr = 124;
|
||||
int opc_lushr = 125;
|
||||
int opc_iand = 126;
|
||||
int opc_land = 127;
|
||||
int opc_ior = 128;
|
||||
int opc_lor = 129;
|
||||
int opc_ixor = 130;
|
||||
int opc_lxor = 131;
|
||||
int opc_iinc = 132;
|
||||
int opc_i2l = 133;
|
||||
int opc_i2f = 134;
|
||||
int opc_i2d = 135;
|
||||
int opc_l2i = 136;
|
||||
int opc_l2f = 137;
|
||||
int opc_l2d = 138;
|
||||
int opc_f2i = 139;
|
||||
int opc_f2l = 140;
|
||||
int opc_f2d = 141;
|
||||
int opc_d2i = 142;
|
||||
int opc_d2l = 143;
|
||||
int opc_d2f = 144;
|
||||
int opc_i2b = 145;
|
||||
int opc_i2c = 146;
|
||||
int opc_i2s = 147;
|
||||
int opc_lcmp = 148;
|
||||
int opc_fcmpl = 149;
|
||||
int opc_fcmpg = 150;
|
||||
int opc_dcmpl = 151;
|
||||
int opc_dcmpg = 152;
|
||||
int opc_ifeq = 153;
|
||||
int opc_ifne = 154;
|
||||
int opc_iflt = 155;
|
||||
int opc_ifge = 156;
|
||||
int opc_ifgt = 157;
|
||||
int opc_ifle = 158;
|
||||
int opc_if_icmpeq = 159;
|
||||
int opc_if_icmpne = 160;
|
||||
int opc_if_icmplt = 161;
|
||||
int opc_if_icmpge = 162;
|
||||
int opc_if_icmpgt = 163;
|
||||
int opc_if_icmple = 164;
|
||||
int opc_if_acmpeq = 165;
|
||||
int opc_if_acmpne = 166;
|
||||
int opc_goto = 167;
|
||||
int opc_jsr = 168;
|
||||
int opc_ret = 169;
|
||||
int opc_tableswitch = 170;
|
||||
int opc_lookupswitch = 171;
|
||||
int opc_ireturn = 172;
|
||||
int opc_lreturn = 173;
|
||||
int opc_freturn = 174;
|
||||
int opc_dreturn = 175;
|
||||
int opc_areturn = 176;
|
||||
int opc_return = 177;
|
||||
int opc_getstatic = 178;
|
||||
int opc_putstatic = 179;
|
||||
int opc_getfield = 180;
|
||||
int opc_putfield = 181;
|
||||
int opc_invokevirtual = 182;
|
||||
int opc_invokespecial = 183;
|
||||
int opc_invokestatic = 184;
|
||||
int opc_invokeinterface = 185;
|
||||
int opc_xxxunusedxxx = 186;
|
||||
int opc_new = 187;
|
||||
int opc_newarray = 188;
|
||||
int opc_anewarray = 189;
|
||||
int opc_arraylength = 190;
|
||||
int opc_athrow = 191;
|
||||
int opc_checkcast = 192;
|
||||
int opc_instanceof = 193;
|
||||
int opc_monitorenter = 194;
|
||||
int opc_monitorexit = 195;
|
||||
int opc_wide = 196;
|
||||
int opc_multianewarray = 197;
|
||||
int opc_ifnull = 198;
|
||||
int opc_ifnonnull = 199;
|
||||
int opc_goto_w = 200;
|
||||
int opc_jsr_w = 201;
|
||||
int opc_breakpoint = 202;
|
||||
|
||||
/* Opcode Names */
|
||||
String opcNames[] = {
|
||||
"nop",
|
||||
"aconst_null",
|
||||
"iconst_m1",
|
||||
"iconst_0",
|
||||
"iconst_1",
|
||||
"iconst_2",
|
||||
"iconst_3",
|
||||
"iconst_4",
|
||||
"iconst_5",
|
||||
"lconst_0",
|
||||
"lconst_1",
|
||||
"fconst_0",
|
||||
"fconst_1",
|
||||
"fconst_2",
|
||||
"dconst_0",
|
||||
"dconst_1",
|
||||
"bipush",
|
||||
"sipush",
|
||||
"ldc",
|
||||
"ldc_w",
|
||||
"ldc2_w",
|
||||
"iload",
|
||||
"lload",
|
||||
"fload",
|
||||
"dload",
|
||||
"aload",
|
||||
"iload_0",
|
||||
"iload_1",
|
||||
"iload_2",
|
||||
"iload_3",
|
||||
"lload_0",
|
||||
"lload_1",
|
||||
"lload_2",
|
||||
"lload_3",
|
||||
"fload_0",
|
||||
"fload_1",
|
||||
"fload_2",
|
||||
"fload_3",
|
||||
"dload_0",
|
||||
"dload_1",
|
||||
"dload_2",
|
||||
"dload_3",
|
||||
"aload_0",
|
||||
"aload_1",
|
||||
"aload_2",
|
||||
"aload_3",
|
||||
"iaload",
|
||||
"laload",
|
||||
"faload",
|
||||
"daload",
|
||||
"aaload",
|
||||
"baload",
|
||||
"caload",
|
||||
"saload",
|
||||
"istore",
|
||||
"lstore",
|
||||
"fstore",
|
||||
"dstore",
|
||||
"astore",
|
||||
"istore_0",
|
||||
"istore_1",
|
||||
"istore_2",
|
||||
"istore_3",
|
||||
"lstore_0",
|
||||
"lstore_1",
|
||||
"lstore_2",
|
||||
"lstore_3",
|
||||
"fstore_0",
|
||||
"fstore_1",
|
||||
"fstore_2",
|
||||
"fstore_3",
|
||||
"dstore_0",
|
||||
"dstore_1",
|
||||
"dstore_2",
|
||||
"dstore_3",
|
||||
"astore_0",
|
||||
"astore_1",
|
||||
"astore_2",
|
||||
"astore_3",
|
||||
"iastore",
|
||||
"lastore",
|
||||
"fastore",
|
||||
"dastore",
|
||||
"aastore",
|
||||
"bastore",
|
||||
"castore",
|
||||
"sastore",
|
||||
"pop",
|
||||
"pop2",
|
||||
"dup",
|
||||
"dup_x1",
|
||||
"dup_x2",
|
||||
"dup2",
|
||||
"dup2_x1",
|
||||
"dup2_x2",
|
||||
"swap",
|
||||
"iadd",
|
||||
"ladd",
|
||||
"fadd",
|
||||
"dadd",
|
||||
"isub",
|
||||
"lsub",
|
||||
"fsub",
|
||||
"dsub",
|
||||
"imul",
|
||||
"lmul",
|
||||
"fmul",
|
||||
"dmul",
|
||||
"idiv",
|
||||
"ldiv",
|
||||
"fdiv",
|
||||
"ddiv",
|
||||
"irem",
|
||||
"lrem",
|
||||
"frem",
|
||||
"drem",
|
||||
"ineg",
|
||||
"lneg",
|
||||
"fneg",
|
||||
"dneg",
|
||||
"ishl",
|
||||
"lshl",
|
||||
"ishr",
|
||||
"lshr",
|
||||
"iushr",
|
||||
"lushr",
|
||||
"iand",
|
||||
"land",
|
||||
"ior",
|
||||
"lor",
|
||||
"ixor",
|
||||
"lxor",
|
||||
"iinc",
|
||||
"i2l",
|
||||
"i2f",
|
||||
"i2d",
|
||||
"l2i",
|
||||
"l2f",
|
||||
"l2d",
|
||||
"f2i",
|
||||
"f2l",
|
||||
"f2d",
|
||||
"d2i",
|
||||
"d2l",
|
||||
"d2f",
|
||||
"i2b",
|
||||
"i2c",
|
||||
"i2s",
|
||||
"lcmp",
|
||||
"fcmpl",
|
||||
"fcmpg",
|
||||
"dcmpl",
|
||||
"dcmpg",
|
||||
"ifeq",
|
||||
"ifne",
|
||||
"iflt",
|
||||
"ifge",
|
||||
"ifgt",
|
||||
"ifle",
|
||||
"if_icmpeq",
|
||||
"if_icmpne",
|
||||
"if_icmplt",
|
||||
"if_icmpge",
|
||||
"if_icmpgt",
|
||||
"if_icmple",
|
||||
"if_acmpeq",
|
||||
"if_acmpne",
|
||||
"goto",
|
||||
"jsr",
|
||||
"ret",
|
||||
"tableswitch",
|
||||
"lookupswitch",
|
||||
"ireturn",
|
||||
"lreturn",
|
||||
"freturn",
|
||||
"dreturn",
|
||||
"areturn",
|
||||
"return",
|
||||
"getstatic",
|
||||
"putstatic",
|
||||
"getfield",
|
||||
"putfield",
|
||||
"invokevirtual",
|
||||
"invokespecial",
|
||||
"invokestatic",
|
||||
"invokeinterface",
|
||||
"xxxunusedxxx",
|
||||
"new",
|
||||
"newarray",
|
||||
"anewarray",
|
||||
"arraylength",
|
||||
"athrow",
|
||||
"checkcast",
|
||||
"instanceof",
|
||||
"monitorenter",
|
||||
"monitorexit",
|
||||
"wide",
|
||||
"multianewarray",
|
||||
"ifnull",
|
||||
"ifnonnull",
|
||||
"goto_w",
|
||||
"jsr_w",
|
||||
"breakpoint"
|
||||
};
|
||||
|
||||
/* Opcode Lengths */
|
||||
int opcLengths[] = {
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
2,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
3,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
2,
|
||||
99,
|
||||
99,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
3,
|
||||
1,
|
||||
1,
|
||||
3,
|
||||
3,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
4,
|
||||
3,
|
||||
3,
|
||||
5,
|
||||
5,
|
||||
1
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user