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