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

Java tutorial

Introduction

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

Source

/**********************************************************************
 * This file is part of "Object Teams Dynamic Runtime Environment"
 * 
 * Copyright 2011, 2015 GK Software AG.
 * 
 * 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 java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.objectteams.otredyn.transformer.names.ClassNames;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

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

/**
 * This adapter adds some instructions to the front of the main-method.
 * These instructions will instantiate and globally activate all teams 
 * that are listed in the team config file.
 * The team config file is specified via the <code>ot.teamconfig</code> property.
 */
public class AddGlobalTeamActivationAdapter extends ClassVisitor {

    /** Initialized from property <tt>ot.teamconfig</tt>. */
    private final static String TEAM_CONFIG_FILE = System.getProperty("ot.teamconfig");
    /**   Marker for comment lines in the team config file. */
    private final static String COMMENT_MARKER = "#";

    private static boolean done = false;

    private AddGlobalTeamActivationAdapter(ClassVisitor cv) {
        super(ASM_API, cv);
    }

    /**
     * Adds the one and only {@link AddGlobalTeamActivationAdapter} to the given multiAdaptor,
     * if needed and if not already done.
     * @param multiAdapter
     * @param writer
     */
    synchronized public static void checkAddVisitor(MultiClassAdapter multiAdapter, ClassWriter writer) {
        if (done || TEAM_CONFIG_FILE == null)
            return;
        multiAdapter.addVisitor(new AddGlobalTeamActivationAdapter(writer));
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        synchronized (AddGlobalTeamActivationAdapter.class) {
            if (!done && isMainMethod(name, desc, access)) {
                done = true;
                final MethodVisitor methodVisitor = cv.visitMethod(access, name, desc, null, null);
                return new AdviceAdapter(this.api, methodVisitor, access, name, desc) {
                    @Override
                    protected void onMethodEnter() {
                        List<String> teams = getTeamsFromConfigFile();
                        for (String aTeam : teams) {
                            Label start, end, typeHandler, ctorHandler, after;

                            String aTeamSlash = aTeam.replace('.', '/');

                            // new SomeTeam():
                            methodVisitor.visitLabel(start = new Label());
                            methodVisitor.visitTypeInsn(Opcodes.NEW, aTeamSlash);
                            //       .activate(Team.ALL_THREADS):
                            methodVisitor.visitInsn(Opcodes.DUP);
                            methodVisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, aTeamSlash, "<init>", "()V",
                                    false);
                            methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, ClassNames.TEAM_SLASH, "ALL_THREADS",
                                    "Ljava/lang/Thread;");
                            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, aTeamSlash, "activate",
                                    "(Ljava/lang/Thread;)V", false);

                            methodVisitor.visitLabel(end = new Label());
                            methodVisitor.visitJumpInsn(Opcodes.GOTO, after = new Label());

                            // catch (ClassNotFoundException, NoClassDefFoundError):
                            //   System.err.println(...)
                            methodVisitor.visitLabel(typeHandler = new Label());
                            methodVisitor.visitInsn(Opcodes.POP); // discard the exception
                            methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
                                    "Ljava/io/PrintStream;");
                            methodVisitor.visitLdcInsn("Config error: Team class '" + aTeam + "' in config file '"
                                    + TEAM_CONFIG_FILE + "' can not be found!");
                            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                                    "(Ljava/lang/String;)V", false);
                            methodVisitor.visitJumpInsn(Opcodes.GOTO, after);
                            methodVisitor.visitTryCatchBlock(start, end, typeHandler,
                                    "java/lang/ClassNotFoundException");
                            // dup to avoid stackmap errors (ASM bug at 1.8)
                            methodVisitor.visitLabel(typeHandler = new Label());
                            methodVisitor.visitInsn(Opcodes.POP); // discard the exception
                            methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
                                    "Ljava/io/PrintStream;");
                            methodVisitor.visitLdcInsn("Config error: Team class '" + aTeam + "' in config file '"
                                    + TEAM_CONFIG_FILE + "' can not be found!");
                            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                                    "(Ljava/lang/String;)V", false);
                            methodVisitor.visitJumpInsn(Opcodes.GOTO, after);
                            //
                            methodVisitor.visitTryCatchBlock(start, end, typeHandler,
                                    "java/lang/NoClassDefFoundError");

                            // catch (NoSuchMethodError):
                            //   System.err.println(...)
                            methodVisitor.visitLabel(ctorHandler = new Label());
                            methodVisitor.visitInsn(Opcodes.POP); // discard the exception
                            methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "err",
                                    "Ljava/io/PrintStream;");
                            methodVisitor.visitLdcInsn(
                                    "Activation failed: Team class '" + aTeam + "' has no default constuctor!");
                            methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                                    "(Ljava/lang/String;)V", false);
                            methodVisitor.visitTryCatchBlock(start, end, ctorHandler,
                                    "java/lang/NoSuchMethodError");

                            methodVisitor.visitLabel(after);
                        }
                    }

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

    private boolean isMainMethod(String name, String desc, int access) {
        if (access != (Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC))
            return false;
        if (!"main".equals(name))
            return false;
        if (!"([Ljava/lang/String;)V".equals(desc))
            return false;
        return true;
    }

    /**
     * @return a list of teams in the team initialization config file
     */
    private static List<String> getTeamsFromConfigFile() {
        List<String> result = new ArrayList<String>();
        BufferedReader in = null;
        try {
            FileInputStream fstream = new FileInputStream(TEAM_CONFIG_FILE);
            in = new BufferedReader(new InputStreamReader(fstream));
            while (in.ready()) {
                String nextLine = in.readLine();
                String nextTeam = nextLine.trim();
                if (nextTeam.startsWith(COMMENT_MARKER))
                    continue; // this is a comment line
                if (!nextTeam.equals("")) {
                    result.add(nextTeam.trim());
                }
            }
        } catch (Exception e) {
            System.err.println("File input error: config file '" + TEAM_CONFIG_FILE + "' can not be found!");
        } finally {
            if (in != null)
                try {
                    in.close();
                } catch (IOException e) {
                    // nothing
                }
        }
        return result;
    }
}