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; /** * @author Andy Clement * @since 0.5.0 */ class ConstructorCopier extends MethodVisitor implements Constants { private final static int preInvokeSpecial = 0; private final static int postInvokeSpecial = 1; // It is important to know when an INVOKESPECIAL is hit, whether it is our actual one that delegates to the super or just // one being invoked due to some early object construction prior to the real INVOKESPECIAL running. By tracking // how many unitialized objects there are (count the NEWs) and how many INVOKESPECIALs have occurred, it is possible // to identify the right one. private int state = preInvokeSpecial; private int unitializedObjectsCount = 0; private TypeDescriptor typeDescriptor; private String suffix; private String classname; public ConstructorCopier(MethodVisitor mv, TypeDescriptor typeDescriptor, String suffix, String classname) { super(ASM5, mv); this.typeDescriptor = typeDescriptor; this.suffix = suffix; this.classname = classname; } @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); } } @Override public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { super.visitFieldInsn(opcode, owner, name, desc); } @Override public void visitTypeInsn(final int opcode, final String type) { if (opcode == NEW) { unitializedObjectsCount++; } super.visitTypeInsn(opcode, type); } // TODO may need to pay attention itf==true @Override public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, boolean itf) { // If this is an invokespecial, first determine if it is the one of interest (the one calling our super constructor) if (opcode == INVOKESPECIAL && name.charAt(0) == '<') { if (unitializedObjectsCount != 0) { unitializedObjectsCount--; } else { // This looks like our INVOKESPECIAL if (state == preInvokeSpecial) { // special case for calling jlObject, do nothing! if (owner.equals("java/lang/Object")) { mv.visitInsn(POP); } else { // Need to replace this INVOKESPECIAL call. String supertypename = typeDescriptor.getSupertypeName(); ReloadableType superRtype = typeDescriptor.getReloadableType().getTypeRegistry() .getReloadableSuperType(supertypename); if (superRtype == null) { // supertype was not reloadable. This either means it really isn't (doesn't match what we consider reloadable) // or it just hasn't been loaded yet. // In a real scenario supertypes will get loaded first always and this can't happen (the latter case) - it happens in tests // because they don't actively load all their bits and pieces in a hierarchical way. Given that on a reloadable boundary // the magic ctors are setup to call a default ctor, we can assume that above the boundary the object has been initialized. // this means we don't need to call a super __init__ or __execute... if (typeDescriptor.getReloadableType().getTypeRegistry() .isReloadableTypeName(supertypename)) { superRtype = typeDescriptor.getReloadableType().getTypeRegistry() .getReloadableSuperType(supertypename); throw new IllegalStateException("The supertype " + supertypename.replace('/', '.') + " has not been loaded as a reloadabletype"); } Utils.insertPopsForAllParameters(mv, desc); mv.visitInsn(POP); // pop 'this' } else { // Check the original form of the supertype for a constructor to call MethodMember existingCtor = (superRtype == null ? null : superRtype.getTypeDescriptor().getConstructor(desc)); if (existingCtor == null) { // It did not exist in the original supertype version, need to use dynamic dispatch method // collapse the arguments on the stack Utils.collapseStackToArray(mv, desc); // now the stack is the instance then the params mv.visitInsn(SWAP); mv.visitInsn(DUP_X1); // no stack is instance then params then instance mv.visitLdcInsn("<init>" + desc); mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(), mDynamicDispatchName, mDynamicDispatchDescriptor, false); mv.visitInsn(POP); } else { // it did exist in the original, so there will be parallel constructor mv.visitMethodInsn(INVOKESPECIAL, typeDescriptor.getSupertypeName(), mInitializerName, desc, false); } } } state = postInvokeSpecial; return; } } } // 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) != '<' && owner.equals(classname) && !name.startsWith("r$")) { // 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); } else { boolean done = false; // TODO dup of code in method copier - can we refactor? 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); } } } }