blob: a8d8ebee00307935958545b1ca86402e8fb2271f [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Dynamic Runtime Environment"
*
* Copyright 2011, 2012 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");
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;
}
}