/*
* Copyright 2009-2012 Michael Dalton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jtaint;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;
/** Add taint tracking to StringBuilder (and StringBuffer objects). Methods
* that modify the StringBuilder/StringBuffer are instrumented to
* propagate taint. Additionally, new constructors and methods are provided to
* initialize taint and query taint information, respectively.
*
* This class is actually a skeleton that is instantiated by replacing
* @builder@ with StringBuffer or StringBuilder, respectively. See build.xml.
*/
public class @builder@Adapter extends StubAdapter implements Opcodes
{
private static final List methodList;
private String className;
private int version;
static {
List l = new ArrayList();
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Z)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(C)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"([C)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"([CII)Ljava/lang/@builder@;"));
if (VmInfo.version() >= VmInfo.VERSION1_5) {
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Ljava/lang/CharSequence;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Ljava/lang/CharSequence;II)Ljava/lang/@builder@;"));
}
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(D)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(F)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(I)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(J)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Ljava/lang/Object;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Ljava/lang/String;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "append",
"(Ljava/lang/StringBuffer;)Ljava/lang/@builder@;"));
if (VmInfo.version() >= VmInfo.VERSION1_5)
l.add(new MethodDecl(ACC_PUBLIC, "appendCodePoint",
"(I)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "delete",
"(II)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "deleteCharAt",
"(I)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(IZ)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(IC)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(I[C)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(I[CII)Ljava/lang/@builder@;"));
if (VmInfo.version() >= VmInfo.VERSION1_5) {
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(ILjava/lang/CharSequence;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(ILjava/lang/CharSequence;II)Ljava/lang/@builder@;"));
}
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(ID)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(IF)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(II)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(IJ)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(ILjava/lang/Object;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "insert",
"(ILjava/lang/String;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "replace",
"(IILjava/lang/String;)Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "reverse",
"()Ljava/lang/@builder@;"));
l.add(new MethodDecl(ACC_PUBLIC, "setCharAt",
"(IC)V"));
l.add(new MethodDecl(ACC_PUBLIC, "setLength",
"(I)V"));
l.add(new MethodDecl(ACC_PUBLIC, "subSequence",
"(II)Ljava/lang/CharSequence;"));
l.add(new MethodDecl(ACC_PUBLIC, "substring",
"(I)Ljava/lang/String;"));
l.add(new MethodDecl(ACC_PUBLIC, "substring",
"(II)Ljava/lang/String;"));
l.add(new MethodDecl(ACC_PUBLIC, "toString",
"()Ljava/lang/String;"));
methodList = l;
}
public @builder@Adapter(ClassVisitor cv) { super(cv, methodList); }
public void visit(int version, int access, String name,
String signature, String superName,
String[] interfaces)
{
this.className = name;
this.version = version;
super.visit(version, access, name, signature, superName, interfaces);
}
/** Equivalent to the following Java code:
*
* public method(arg1...argN?, CharSequence cs, int begin, int end) {
* return method(arg1...argN?, String.valueOf(cs).substring(begin,end));
* }
*/
private void replaceCharSequenceII(MethodVisitor mv, String name,
String desc, int argOffset)
{
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
int l = 1;
Type[] t = Type.getArgumentTypes(desc);
Type r = Type.getReturnType(desc);
Type[] u = new Type[argOffset + 1];
System.arraycopy(t, 0, u, 0, argOffset);
u[argOffset] = Type.getObjectType("java/lang/String");
for (int i = 0; i < argOffset; l += t[i].getSize(), i++)
mv.visitVarInsn(t[i].getOpcode(ILOAD), l);
mv.visitVarInsn(ALOAD, l); /* CharSequence */
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String",
"valueOf", "(Ljava/lang/Object;)Ljava/lang/String;");
mv.visitVarInsn(ILOAD, l+1); /* beginIndex */
mv.visitVarInsn(ILOAD, l+2); /* endIndex */
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "substring",
"(II)Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, className, name,
Type.getMethodDescriptor(r, u));
mv.visitInsn(ARETURN);
mv.visitMaxs(l + 3, l + 3);
mv.visitEnd();
}
/** Equivalent to the following Java code:
*
* public StringBuilder(String s) {
* this(s.length() + 16);
* append(s);
* }
*/
private void replaceConstructorString(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "length",
"()I");
mv.visitIntInsn(BIPUSH, 16);
mv.visitInsn(IADD);
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", "(I)V");
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, className, "append",
"(Ljava/lang/String;)Ljava/lang/@builder@;");
mv.visitInsn(RETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* public StringBuilder(CharSequence cs) {
* this(cs.toString());
* }
*/
private void replaceConstructorCharSequence(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object",
"toString", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKESPECIAL, className, "<init>",
"(Ljava/lang/String;)V");
mv.visitInsn(RETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* String subString(int begin) {
* return substring(begin, count);
* }
*/
private void replaceSubstringI(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "count", "I");
mv.visitMethodInsn(INVOKEVIRTUAL, className, "substring",
"(II)Ljava/lang/String;");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* public String substring(int begin, int end) {
* return toString().substring(begin, end);
* }
*/
private void replaceSubstringII(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKEVIRTUAL, className, "toString",
"()Ljava/lang/String;");
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"substring", "(II)Ljava/lang/String;");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* CharSequence subSequence(int begin, int end) {
* return substring(begin, end);
* }
*/
private void replaceSubSequence(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, className, "substring",
"(II)Ljava/lang/String;");
mv.visitInsn(ARETURN);
mv.visitMaxs(3, 3);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* public String toString() {
* s = new String(value, 0, count);
* if (taint == null || !taint.isTainted())
* return s;
* return new String(s, taint);
* }
*/
private void replaceToString(MethodVisitor mv) {
mv.visitCode();
mv.visitTypeInsn(NEW, "java/lang/String");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "value", "[C");
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "count", "I");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>",
"([CII)V");
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
Label l0 = new Label();
mv.visitJumpInsn(IFNONNULL, l0);
mv.visitInsn(ARETURN);
mv.visitLabel(l0);
if (version == V1_6)
mv.visitFrame(F_SAME1, 0, null, 1,
new Object[] { "java/lang/String" });
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitMethodInsn(INVOKEVIRTUAL, "jtaint/Taint",
"isTainted", "()Z");
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
mv.visitInsn(ARETURN);
mv.visitLabel(l1);
if (version == V1_6)
mv.visitFrame(F_SAME1, 0, null, 1,
new Object[] { "java/lang/String" });
mv.visitTypeInsn(NEW, "java/lang/String");
mv.visitInsn(DUP_X1);
mv.visitInsn(SWAP);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>",
"(Ljava/lang/String;Ljtaint/Taint;)V");
mv.visitInsn(ARETURN);
mv.visitMaxs(5, 1);
mv.visitEnd();
}
/** Equivalent to the following Java code:
* public StringBuilder appendCodePoint(int c) {
* return append(Character.toChars(c));
* }
*/
private void replaceAppendCodePoint(MethodVisitor mv) {
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character",
"toChars", "(I)[C");
mv.visitMethodInsn(INVOKEVIRTUAL, className, "append",
"([C)Ljava/lang/@builder@;");
mv.visitInsn(ARETURN);
mv.visitMaxs(2, 2);
mv.visitEnd();
}
/** When called on a function methodName, define methodName to convert its
* arguments to a String using String.valueOf, and then recursively invoke
* methodName on the String value. Used for instrumenting append and insert
* methods.
*
* Instrumenting the method methodName produces bytecode equivalent to the
* following Java code:
*
* public StringBuilder methodName(T savedArg0...savedArgN, T arg0...argM) {
* return methodName(savedArg0...savedArgN, String.valueOf(arg0...argM);
* }
*/
private void replaceWithStringConverter(MethodVisitor mv, String name,
String desc, int argsSaved)
{
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
int l = 1;
Type[] t = Type.getArgumentTypes(desc);
for (int i = 0; i < t.length; l += t[i].getSize(), i++)
mv.visitVarInsn(t[i].getOpcode(ILOAD), l);
Type stringType = Type.getObjectType("java/lang/String");
Type[] valueArgs = new Type[t.length - argsSaved];
System.arraycopy(t, argsSaved, valueArgs, 0, t.length - argsSaved);
if (valueArgs.length == 1 && valueArgs[0].getSort() == Type.OBJECT)
valueArgs[0] = Type.getObjectType("java/lang/Object");
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf",
Type.getMethodDescriptor(stringType, valueArgs));
Type methodRet = Type.getReturnType(desc);
Type[] methodArgs = new Type[argsSaved + 1];
System.arraycopy(t, 0, methodArgs, 0, argsSaved);
methodArgs[argsSaved] = stringType;
mv.visitMethodInsn(INVOKEVIRTUAL, className, name,
Type.getMethodDescriptor(methodRet, methodArgs));
mv.visitInsn(ARETURN);
mv.visitMaxs(l, l);
mv.visitEnd();
}
private void buildStringWrapper(final MethodVisitor mv,
final String methodName,
final String desc)
{
final int offset = "append".equals(methodName) ? 0 : 1;
new InstrumentationLockBuilder(mv, version, className, methodName, desc)
{
public void onUnlocked() {
String taintDesc;
if (offset == 0) /* Append */
taintDesc = "(Ljtaint/Taint;ILjtaint/Taint;I)Ljtaint/Taint;";
else /* Insert */
taintDesc = "(Ljtaint/Taint;IILjtaint/Taint;I)Ljtaint/Taint;";
/* Handle null arguments safely */
mv.visitVarInsn(ALOAD, offset + 1);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/String",
"valueOf",
"(Ljava/lang/Object;)Ljava/lang/String;");
mv.visitVarInsn(ASTORE, offset + 1);
/* Now update taint -- the string argument
* or the current StringBuilder may have a null taint,
* so we rely on a helper method
*/
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "count", "I");
/* Fun situation -- Note here that the count has already been
* updated by the append function, so we actually need to
* subtract the string argument's length to compute the correct
* value to pass to TaintUtil. TaintUtil is expected the
* number of characters in the StringBuilder _BEFORE_ the
* append
*/
mv.visitVarInsn(ALOAD, offset + 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"length", "()I");
mv.visitInsn(ISUB);
if (offset == 1)
mv.visitVarInsn(ILOAD, offset);
mv.visitVarInsn(ALOAD, offset + 1);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
ByteCodeUtil.internalName("taint"),
"()Ljtaint/Taint;");
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"length", "()I");
mv.visitMethodInsn(INVOKESTATIC, "jtaint/TaintUtil",
methodName, taintDesc);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(SWAP);
mv.visitFieldInsn(PUTFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
}
}.build();
}
private void buildDeleteCharAtWrapper(final MethodVisitor mv,
final String methodName,
final String desc)
{
new InstrumentationLockBuilder(mv, version, className, methodName, desc)
{
public void onUnlocked() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitInsn(DUP);
Label l = new Label();
mv.visitJumpInsn(IFNULL, l);
mv.visitVarInsn(ILOAD, 1);
mv.visitIincInsn(1, 1);
mv.visitVarInsn(ILOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "jtaint/Taint",
"delete", "(II)Ljtaint/Taint;");
mv.visitLabel(l);
if (version == V1_6)
mv.visitFrame(F_SAME1, 0, null, 1,
new Object[] { "jtaint/Taint" });
mv.visitInsn(POP);
}
public void visitMaxs(int nStack, int nLocal) {
mv.visitMaxs(Math.max(nStack, 3), nLocal);
}
}.build();
}
private void buildReplaceWrapper(final MethodVisitor mv,
final String methodName,
final String desc)
{
new InstrumentationLockBuilder(mv, version, className, methodName, desc)
{
public void onUnlocked() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
/* Fixup -- TaintUtil is expecting the length of
* StringBuilder _before_ the replace occurred. For
* a call to replace(int begin, int end, String s),
* count + (end - begin) - s.length() is the old count
*/
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className, "count", "I");
mv.visitVarInsn(ILOAD, 2);
mv.visitVarInsn(ILOAD, 1);
mv.visitInsn(ISUB);
mv.visitInsn(IADD);
mv.visitVarInsn(ALOAD, 3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"length", "()I");
mv.visitInsn(ISUB);
/* Now load begin/end */
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ILOAD, 2);
/* And finally string taint/length */
mv.visitVarInsn(ALOAD, 3);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
ByteCodeUtil.internalName("taint"),
"()Ljtaint/Taint;");
mv.visitInsn(SWAP);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String",
"length", "()I");
mv.visitMethodInsn(INVOKESTATIC, "jtaint/TaintUtil",
"replace",
"(Ljtaint/Taint;IIILjtaint/Taint;I)Ljtaint/Taint;");
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(SWAP);
mv.visitFieldInsn(PUTFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
}
}.build();
}
private void buildSetCharAtWrapper(final MethodVisitor mv,
final String methodName,
final String desc)
{
new InstrumentationLockBuilder(mv, version, className, methodName, desc)
{
public void onUnlocked() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitInsn(DUP);
Label l = new Label();
mv.visitJumpInsn(IFNULL, l);
mv.visitInsn(DUP);
mv.visitVarInsn(ILOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "jtaint/Taint",
"clear", "(I)V");
mv.visitLabel(l);
if (version == V1_6)
mv.visitFrame(F_SAME1, 0, null, 1,
new Object[] { "jtaint/Taint" });
mv.visitInsn(POP);
}
}.build();
}
private void buildGenericWrapper(final MethodVisitor mv,
final String methodName,
final String desc)
{
new InstrumentationLockBuilder(mv, version, className, methodName, desc)
{
public void onUnlocked() {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, className,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;");
mv.visitInsn(DUP);
Label l0 = new Label();
mv.visitJumpInsn(IFNULL, l0);
Type[] t = Type.getArgumentTypes(desc);
Type r = Type.getObjectType("jtaint/Taint");
int l = 1;
for (int i = 0; i < t.length; l += t[i].getSize(), i++)
mv.visitVarInsn(t[i].getOpcode(ILOAD), l);
mv.visitMethodInsn(INVOKEVIRTUAL, "jtaint/Taint",
methodName, Type.getMethodDescriptor(r, t));
mv.visitLabel(l0);
if (version == V1_6)
mv.visitFrame(F_SAME1, 0, null, 1,
new Object[] { "jtaint/Taint" });
mv.visitInsn(POP);
}
}.build();
}
public MethodVisitor visitMethod(final int access,
final String name, final String desc, String signature,
String[] exceptions)
{
final MethodVisitor mv = super.visitMethod(access, name, desc,
signature, exceptions);
final int offset = "append".equals(name) ? 0 : 1;
/* First instrument all 'special' methods - constructors or private
* methods
*/
if ("<init>".equals(name)) {
if ("(Ljava/lang/String;)V".equals(desc)) {
replaceConstructorString(mv);
return null;
} else if ("(Ljava/lang/CharSequence;)V".equals(desc)) {
replaceConstructorCharSequence(mv);
return null;
}
return mv;
}
/* Any non-special method that is to be instrumented is on methodList */
if (!methodList.contains(new MethodDecl(access, name, desc)))
return mv;
if ("toString".equals(name)) {
replaceToString(mv);
return null;
} else if ("subSequence".equals(name)) {
replaceSubSequence(mv);
return null;
} else if ("substring".equals(name)) {
if ("(I)Ljava/lang/String;".equals(desc))
replaceSubstringI(mv);
else
replaceSubstringII(mv);
return null;
} else if ("appendCodePoint".equals(name)) {
replaceAppendCodePoint(mv);
return null;
} else if (("append".equals(name) &&
"(Ljava/lang/CharSequence;II)Ljava/lang/@builder@;".equals(desc))
|| ("insert".equals(name) &&
"(ILjava/lang/CharSequence;II)Ljava/lang/@builder@;".equals(desc)))
{
replaceCharSequenceII(mv, name, desc, offset);
return null;
} else if (("append".equals(name) &&
!"(Ljava/lang/String;)Ljava/lang/@builder@;".equals(desc))
|| ("insert".equals(name) &&
!"(ILjava/lang/String;)Ljava/lang/@builder@;".equals(desc)))
{
/* Convert the append or insert arguments to String, and
* invoke append(String) or insert(offset, String)
*/
replaceWithStringConverter(mv, name, desc, offset);
return null;
}
/* Build wrapper */
if ("delete".equals(name) || "reverse".equals(name) ||
"setLength".equals(name))
buildGenericWrapper(mv, name, desc);
else if ("append".equals(name) || "insert".equals(name))
buildStringWrapper(mv, name, desc);
else if ("deleteCharAt".equals(name))
buildDeleteCharAtWrapper(mv, name, desc);
else if ("replace".equals(name))
buildReplaceWrapper(mv, name, desc);
else /* "setCharAt".equals(name) */
buildSetCharAtWrapper(mv, name, desc);
return super.visitMethod(ByteCodeUtil.inheritAccessFlags(access)
+ ACC_PRIVATE,
ByteCodeUtil.internalName(name), desc,
signature, exceptions);
}
public void onEndBuildStubs() {
cv.visitField(ACC_PRIVATE + ACC_TRANSIENT,
ByteCodeUtil.internalName("taint"),
"Ljtaint/Taint;", null, null).visitEnd();
InstrumentationLockBuilder.visitEnd(cv, className);
}
public static InstrumentationBuilder builder() {
return Builder.getInstance();
}
private static class Builder implements InstrumentationBuilder {
private static final Builder b = new Builder();
public static InstrumentationBuilder getInstance() { return b; }
public ClassVisitor build(ClassVisitor cv) {
return new @builder@Adapter(cv);
}
}
}