org.eclipse.objectteams.otredyn.bytecode.asm.AddAfterClassLoadingHook.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.objectteams.otredyn.bytecode.asm.AddAfterClassLoadingHook.java

Source

/**********************************************************************
 * This file is part of "Object Teams Dynamic Runtime Environment"
 * 
 * Copyright 2009, 2012 Stephan Herrmann.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Please visit http://www.eclipse.org/objectteams for updates and contact.
 * 
 * Contributors:
 *      Stephan Herrmann - Initial API and implementation
 **********************************************************************/
package org.eclipse.objectteams.otredyn.bytecode.asm;

import org.eclipse.objectteams.otredyn.bytecode.AbstractBoundClass;
import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AdviceAdapter;

import org.objectweb.asm.Opcodes;

import static org.eclipse.objectteams.otredyn.bytecode.Types.ACCESS_STATIC;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.RETURN;

import static org.eclipse.objectteams.otredyn.bytecode.asm.AsmBoundClass.ASM_API;

/**
 * This adapter adds static initialization that gives the TeamManager
 * a chance to trigger class redefinition after the class has been defined successfully.
 * This is needed if a weaving task is recorded after load-time weaving has been performed
 * and before defining the class has finished.
 * 
 * Also the per-team-class mapping of accessIds is initialized at that point.
 * 
 * @author stephan
 *
 */
public class AddAfterClassLoadingHook extends ClassVisitor {

    // the method to look for or add:
    private static final String CLINIT_NAME = "<clinit>";
    private static final String CLINIT_DESC = "()V";

    // the method to invoke:
    private static final String TARGET_CLASS_NAME = ClassNames.TEAM_MANAGER_SLASH;
    private static final String TARGET_METHOD_NAME = "handleTeamLoaded";
    private static final String TARGET_METHOD_DESC = "(Ljava/lang/Class;)V";

    boolean needToAdd = true;
    AbstractBoundClass clazz;

    public AddAfterClassLoadingHook(ClassVisitor arg0, AbstractBoundClass clazz) {
        super(ASM_API, arg0);
        this.clazz = clazz;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        if (CLINIT_NAME.equals(name)) {
            // clinit already exists, add our statement to the front:
            this.needToAdd = false;
            final MethodVisitor clinit = cv.visitMethod(access, name, desc, null, null);
            return new AdviceAdapter(this.api, clinit, access, name, desc) {
                @Override
                protected void onMethodEnter() {
                    createHookCall(clinit);
                }

                @Override
                public void visitMaxs(int maxStack, int maxLocals) {
                    super.visitMaxs(Math.max(1, maxStack), maxLocals);
                }
            };
        }
        return null;
    }

    @Override
    public void visitEnd() {
        if (needToAdd) {
            // no clinit found, add one now:
            MethodVisitor mv = cv.visitMethod(ACCESS_STATIC, CLINIT_NAME, CLINIT_DESC, null, null);
            mv.visitCode();
            createHookCall(mv);
            mv.visitInsn(RETURN);
            mv.visitMaxs(1, 0);
            mv.visitEnd();
        }
    }

    void createHookCall(MethodVisitor clinit) {
        if (clazz.isTeam())
            clinit.visitLdcInsn(Type.getObjectType(clazz.getName().replace('.', '/')));
        else
            clinit.visitInsn(Opcodes.ACONST_NULL);
        clinit.visitMethodInsn(INVOKESTATIC, TARGET_CLASS_NAME, TARGET_METHOD_NAME, TARGET_METHOD_DESC, false);
    }
}