org.springsource.loaded.ConstructorCopier.java Source code

Java tutorial

Introduction

Here is the source code for org.springsource.loaded.ConstructorCopier.java

Source

/*
 * 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);
            }
        }
    }
}