Java tutorial
/* * Copyright 2010-2012 VMware and contributors * * 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 org.springsource.loaded; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.springsource.loaded.Utils.ReturnType; /** * * @author Andy Clement * @since 0.5.0 */ class MethodCopier extends MethodVisitor implements Constants { private boolean isInterface; private String descriptor; private TypeDescriptor typeDescriptor; private String classname; private String suffix; private boolean hasFieldsRequiringAccessors; public MethodCopier(MethodVisitor mv, boolean isInterface, String descriptor, TypeDescriptor typeDescriptor, String classname, String suffix) { super(ASM5, mv); this.isInterface = isInterface; this.descriptor = descriptor; this.typeDescriptor = typeDescriptor; this.classname = classname; this.suffix = suffix; this.hasFieldsRequiringAccessors = this.typeDescriptor.getFieldsRequiringAccessors().length != 0; } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { // Rename 'this' to 'thiz' in executor otherwise Eclipse debugger will fail (static method with 'this') if (index == 0 && name.equals("this")) { super.visitLocalVariable("thiz", desc, signature, start, end, index); } else { super.visitLocalVariable(name, desc, signature, start, end, index); } } private FieldMember findFieldIfRequiresAccessorUsage(String owner, String name) { FieldMember[] fms = this.typeDescriptor.getFieldsRequiringAccessors(); for (FieldMember fm : fms) { if (fm.getName().equals(name) && (owner.equals(classname) || isOneOfOurSupertypes(owner))) { // && fm.getDeclaringTypeName().equals(owner)) { // possibly a match - testcase scenario: // 'owner=prot/SubThree' - this is what the FIELD instruction is working on // 'dfm=prot/Three' - this is the type that declared the field // 'classname=prot/SubThree' - this is the type we are currently operating on // in our other case though (with JDK Proxies) // owner=java/lang/reflect/Proxy // dfm=java/lang/reflect/Proxy // classname=$Proxy6 // (this is the funky InvocationHandler field called 'h' in Proxy) return fm; } } return null; } /** * Determine if the supplied type is a supertype of the current type we are modifying. This is used to determine if the owner we * have discovered for a field is one of our supertypes (and so, if it is protected, whether it is something that needs * redirecting through an accessor). * * @param type the type which may be one of this types supertypes * @return true if it is a supertype */ private boolean isOneOfOurSupertypes(String type) { String stypeName = typeDescriptor.getSupertypeName(); while (stypeName != null) { // TODO [bug] should stop at the first one that has a field in it? and check the field is protected, yada yada yada if (stypeName.equals(type)) { return true; } stypeName = typeDescriptor.getTypeRegistry().getDescriptorFor(stypeName).getSupertypeName(); } return false; } private TypeDescriptor getType(String type) { TypeDescriptor typeDescriptor = this.typeDescriptor.getTypeRegistry().getDescriptorFor(type); return typeDescriptor; } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { if (hasFieldsRequiringAccessors) { // Check if this field reference needs redirecting to an accessor FieldMember fm = findFieldIfRequiresAccessorUsage(owner, name); if (fm != null) { switch (opcode) { case GETFIELD: mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldGetterName(name), "()" + desc, false); return; case PUTFIELD: mv.visitMethodInsn(INVOKEVIRTUAL, classname, Utils.getProtectedFieldSetterName(name), "(" + desc + ")V", false); return; case GETSTATIC: mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldGetterName(name), "()" + desc, false); return; case PUTSTATIC: mv.visitMethodInsn(INVOKESTATIC, classname, Utils.getProtectedFieldSetterName(name), "(" + desc + ")V", false); return; } } } super.visitFieldInsn(opcode, owner, name, desc); } // TODO maybe do something if 'itf==true' @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, boolean itf) { // Is it a private method call? // TODO r$ check here because we use invokespecial to avoid virtual dispatch on field changes... if (opcode == INVOKESPECIAL && name.charAt(0) != '<' && !name.startsWith("r$")) { if (owner.equals(classname)) { // private method call // leaving the invokespecial alone will cause a verify error String descriptor = Utils.insertExtraParameter(owner, desc); super.visitMethodInsn(INVOKESTATIC, Utils.getExecutorName(classname, suffix), name, descriptor, false); return; } else { // super call // TODO Check if this is true: we can just call the catcher directly if there was one, there is no need // for a superdispatcher // Only need to redirect to the superdispatcher if it was a protected method TypeDescriptor supertypeDescriptor = getType(owner); MethodMember target = supertypeDescriptor.getByNameAndDescriptor(name + desc); if (target != null && target.isProtected()) { // A null target means that method is not in the supertype, so didn't get a superdispatcher super.visitMethodInsn(INVOKESPECIAL, classname, name + methodSuffixSuperDispatcher, desc, false); } else { super.visitMethodInsn(opcode, owner, name, desc, itf); } return; } } // Might be a private static method boolean done = false; if (opcode == INVOKESTATIC) { MethodMember mm = typeDescriptor.getByDescriptor(name, desc); if (mm != null && mm.isPrivate()) { super.visitMethodInsn(INVOKESTATIC, Utils.getExecutorName(classname, suffix), name, desc, false); done = true; } } if (!done) { super.visitMethodInsn(opcode, owner, name, desc, itf); } } @Override public void visitEnd() { if (isInterface) { // Create 'dummy methods' for an interface implementation createDummyMethodBody(); super.visitEnd(); } } private void createDummyMethodBody() { ReturnType returnType = Utils.getReturnTypeDescriptor(descriptor); int descriptorSize = Utils.getSize(descriptor); if (returnType.isVoid()) { super.visitInsn(RETURN); super.visitMaxs(1, descriptorSize); } else if (returnType.isPrimitive()) { super.visitLdcInsn(0); switch (returnType.descriptor.charAt(0)) { case 'B': case 'C': case 'I': case 'S': case 'Z': super.visitInsn(IRETURN); super.visitMaxs(2, descriptorSize); break; case 'D': super.visitInsn(DRETURN); super.visitMaxs(3, descriptorSize); break; case 'F': super.visitInsn(FRETURN); super.visitMaxs(2, descriptorSize); break; case 'J': super.visitInsn(LRETURN); super.visitMaxs(3, descriptorSize); break; default: throw new IllegalStateException(returnType.descriptor); } } else { // reference type super.visitInsn(ACONST_NULL); super.visitInsn(ARETURN); super.visitMaxs(1, descriptorSize); } } }