Skip to main content
summaryrefslogtreecommitdiffstats
blob: 1adcaeffac963b67f8dc8a7b606d1627b7cf6d4b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**********************************************************************
 * This file is part of the "Object Teams Runtime Environment"
 * 
 * Copyright 2009 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
 * $Id: LiftingParticipantTransformation.java 23408 2010-02-03 18:07:35Z stephan $
 * 
 * Please visit http://www.objectteams.org for updates and contact.
 * 
 * Contributors:
 * 	Stephan Herrmann - Initial API and implementation
 **********************************************************************/
package org.eclipse.objectteams.otre;


import org.objectteams.Team;

import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ALOAD;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.DUP;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.IFNULL;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LDC;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.NEW;
import org.apache.bcel.generic.NOP;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.POP;
import org.apache.bcel.generic.Type;

/**
 * If the property ot.lifting.participant is set transform all lift methods and insert 
 * static calls to createRole(Team,Object,String)Object; to the registered lifting participant
 * before creating a new role. If createRole returns non-null then that value is taken
 * as the new role, otherwise lifting proceeds as normal, i.e., normally creates a new role.
 *  
 * @author stephan
 * @since 1.3.1
 */
public class LiftingParticipantTransformation extends ObjectTeamsTransformation {
	

	private static String PARTICIPANT_NAME = System.getProperty("ot.lifting.participant");
	private static boolean checked = false;
	
	final private static String LIFT_PREFIX = "_OT$liftTo"; 

	private static final String WRONG_ROLE_EXCEPTION = "org.objectteams.WrongRoleException";
	private static final String LIFTING_FAILED_EXCEPTION = "org.objectteams.LiftingFailedException";
	private static final String LIFTING_VETO_EXCEPTION = "org.objectteams.LiftingVetoException";
	
	private static final ObjectType iLiftingParticipant = new ObjectType("org.objectteams.ILiftingParticipant"); 

	private static final String CREATE_ROLE_METHOD = "createRole";
	private static final String LIFTING_PARTICIPANT_FIELD = "_OT$liftingParticipant";
	
	public LiftingParticipantTransformation(ClassLoader loader, SharedState state) { super(loader, state); }

	public void doTransformCode(ClassGen cg) 
	{
		if (PARTICIPANT_NAME == null) return;
	
		if (!classNeedsTeamExtensions(cg)) return;
		
		synchronized (LiftingParticipantTransformation.class) {
			if (!checked) {
				try {
					// install a shared instance into class Team:
					Class<?> participantClass = loader.loadClass(PARTICIPANT_NAME);
					Team.class.getField(LIFTING_PARTICIPANT_FIELD).set(null, participantClass.newInstance());
				} catch (Exception e) {
					new IllegalArgumentException("Lifting participant "+PARTICIPANT_NAME+" is invalid.", e).printStackTrace();
					PARTICIPANT_NAME = null;
				}
				checked = true;
			}
		}
		
    	factory = new InstructionFactory(cg);
    	ConstantPoolGen cpg        = cg.getConstantPool();
    	String          class_name = cg.getClassName();
    	
    	// FIXME(SH): evaluate inclusion/exclusion filter per className
			
    	Method[] methods = cg.getMethods();
    	for (int i=0; i<methods.length; i++) {
    		Method m           = methods[i];
    		if (!m.getName().startsWith(LIFT_PREFIX))
    			continue;
    		
			cg.replaceMethod(m, m = weaveLiftingParticipant(m, class_name, cpg));
    	}
	}

	private Method weaveLiftingParticipant(Method m, String className, ConstantPoolGen cpg) {
		MethodGen mg = newMethodGen(m, className, cpg);
		InstructionList il = mg.getInstructionList();
		InstructionHandle[] ihs = il.getInstructionHandles();
		for (int i=0; i<ihs.length; i++) {
			InstructionHandle ih = ihs[i];
			if (ih.getInstruction() instanceof NEW) 
			{
				NEW newInstr = (NEW) ih.getInstruction();
				Type newType = newInstr.getType(cpg);
				String newTypeName = newType.toString();
				
				// don't transform creation of these exceptions:
				if (newTypeName.equals(LIFTING_FAILED_EXCEPTION)) continue;
				if (newTypeName.equals(LIFTING_VETO_EXCEPTION)) continue;
				if (newTypeName.equals(WRONG_ROLE_EXCEPTION)) continue;
				
				ih.setInstruction(new NOP()); // keep this handle for the enclosing switch
				
				InstructionList inset = new InstructionList();
				// fetch instance of lifting participant from Team._OT$liftingParticipant
				inset.append(factory.createFieldAccess(teamClassType.getClassName(),  
													   LIFTING_PARTICIPANT_FIELD, 
													   iLiftingParticipant, 
													   Constants.GETSTATIC));
				inset.append(new ALOAD(0)); 						// load the team
				inset.append(new ALOAD(1));							// load the base
				inset.append(new LDC(cpg.addString(newTypeName)));	// load the role class name
				inset.append(factory.createInvoke(iLiftingParticipant.getClassName(),	// receiver type 
												  CREATE_ROLE_METHOD, 					// method
												  object, 								// return type
												  new Type[] {teamType, object, string},// arg types
												  Constants.INVOKEINTERFACE));
				inset.append(new DUP());	// keep value after null-check
				BranchInstruction isNull = new IFNULL(null);
				inset.append(isNull);
				inset.append(factory.createCast(object, newType));
				// let goto skip: 0: new R, 1: dup, 2: aload_0, 3: aload_1, (4: cast if needed), 4: o. 5: invokespecial<init>
				int invokeOffset = 4;
				if (!(ihs[i+invokeOffset].getInstruction() instanceof INVOKESPECIAL))
					invokeOffset++;
				inset.append(new GOTO(ihs[i+invokeOffset+1])); // one past above sequence
				
				// continue here if null, i.e., perform the original new-instruction
				InstructionHandle goOn = inset.append(new POP()); // discard dup'ed value from above
				isNull.setTarget(goOn);
				inset.append(newInstr); // re-insert deleted first instruction
				
				il.append(ih, inset);
			}
		}
		return mg.getMethod();
	}
}

Back to the top