Java tutorial
/* * 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 org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.util.BitSet; import java.util.List; /* XXX This handles all cases except when a subclass creates a less permissive * 'overriding' method (e.g., private) that cannot be invoked by other code. * In that case, we instrument the private method, but not the publicly * accessible 'overriden' method in the superclass, which could break things. * This gets really thorny if the overriding method is protected or default * access rather than private, so we ignore it for now since it shouldn't occur * in practice, at least for the classes we are instrumenting in the JDK. */ /** A simple adapter that, given a list of method prototypes, adds these methods * to the existing class if they are not found. Any methods that are added * merely call the superclass with the same method name and arguments. Useful * when all instrumentation must be performed in the current class, and some * methods are defined only in a superclass. */ public class StubAdapter extends ClassAdapter implements Opcodes { List stubList; BitSet found; String superName; public StubAdapter(ClassVisitor cv, List stubList) { super(cv); if (stubList == null) return; this.stubList = stubList; this.found = new BitSet(stubList.size()); } public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.superName = superName; cv.visit(version, access, name, signature, superName, interfaces); } private void buildStub(MethodDecl m) { String name = m.name(), desc = m.type(); int acc = m.access(); Type[] args = Type.getArgumentTypes(desc); Type ret = Type.getReturnType(desc); /* We want any subclasses to visit the generated stub, so we invoke * this.visitMethod() (which should be overridden by relevant * subclasses). */ MethodVisitor mv = visitMethod(acc, name, desc, null, null); if (mv == null) return; mv.visitCode(); int l = 1; mv.visitVarInsn(ALOAD, 0); for (int i = 0; i < args.length; l += args[i].getSize(), i++) mv.visitVarInsn(args[i].getOpcode(ILOAD), l); mv.visitMethodInsn(INVOKESPECIAL, superName, name, desc); mv.visitInsn(ret.getOpcode(IRETURN)); mv.visitMaxs(Math.max(l, ret.getSize()), l); mv.visitEnd(); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodDecl m = new MethodDecl(access, name, desc); if (stubList != null) { int i = stubList.indexOf(m); if (i != -1) found.set(i); } return cv.visitMethod(access, name, desc, signature, exceptions); } public final void visitEnd() { onBeginBuildStubs(); if (stubList != null) { int len = stubList.size(); for (int i = found.nextClearBit(0); i >= 0 && i < len; i = found.nextClearBit(i + 1)) buildStub((MethodDecl) stubList.get(i)); } onEndBuildStubs(); cv.visitEnd(); } protected void onBeginBuildStubs() { } protected void onEndBuildStubs() { } }