001: /*
002: * $Header: /cvs/j3dfly/J3dEditor/src/org/jdesktop/j3dedit/interposer/InterposerSetup.java,v 1.3 2005/04/22 19:49:29 paulby Exp $
003: *
004: * Sun Public License Notice
005: *
006: * The contents of this file are subject to the Sun Public License Version
007: * 1.0 (the "License"). You may not use this file except in compliance with
008: * the License. A copy of the License is available at http://www.sun.com/
009: *
010: * The Original Code is the Java 3D(tm) Scene Graph Editor.
011: * The Initial Developer of the Original Code is Paul Byrne.
012: * Portions created by Paul Byrne are Copyright (C) 2002.
013: * All Rights Reserved.
014: *
015: * Contributor(s): Paul Byrne.
016: *
017: **/
018: package org.jdesktop.j3dedit.interposer;
019:
020: import java.util.Iterator;
021: import java.util.Map;
022: import java.util.List;
023: import java.util.HashMap;
024: import com.sun.jdi.VirtualMachine;
025: import com.sun.jdi.Bootstrap;
026: import com.sun.jdi.ClassType;
027: import com.sun.jdi.connect.AttachingConnector;
028: import com.sun.jdi.connect.Connector;
029: import com.sun.jdi.event.*;
030: import com.sun.jdi.request.*;
031:
032: import org.apache.bcel.Repository;
033: import org.apache.bcel.classfile.*;
034: import org.apache.bcel.generic.*;
035: import org.apache.bcel.Constants;
036:
037: /**
038: *
039: * @author Paul Byrne
040: * @version $Id: InterposerSetup.java,v 1.3 2005/04/22 19:49:29 paulby Exp $
041: */
042: public class InterposerSetup {
043:
044: private final static String callbackClass = "org.jdesktop.j3dedit.interposerext.InterposerCallbacks";
045:
046: //private final static String callbackClass = "com.sun.j3d.demos.j3dedit.scenegrapheditor.InterposerEditorCallbacks";
047:
048: /** Creates a new instance of test */
049: public InterposerSetup() {
050: setup(config());
051: }
052:
053: private void setup(HashMap monitor) {
054:
055: VirtualMachine vm;
056: vm = connect();
057: //System.out.println("Can get byte codes "+vm.canGetBytecodes() );
058: //System.out.println("Can Redefine classes "+vm.canRedefineClasses() );
059:
060: EventRequestManager mgr = vm.eventRequestManager();
061: ClassPrepareRequest localePrep = mgr
062: .createClassPrepareRequest();
063: /*
064: Iterator it = monitor.keySet().iterator();
065: while( it.hasNext() ) {
066: String str = (String)it.next();
067: System.out.println(str);
068: localePrep.addClassFilter( str );
069: }
070: */
071: localePrep.addClassFilter("javax.media.j3d.*");
072:
073: localePrep.setEnabled(true);
074:
075: vm.resume();
076:
077: EventQueue eventQueue = vm.eventQueue();
078: EventSet evtSet;
079: EventIterator evtIt;
080: Event evt;
081: boolean finished = false;
082: do {
083: try {
084: evtSet = eventQueue.remove();
085: evtIt = evtSet.eventIterator();
086: do {
087: evt = evtIt.nextEvent();
088: if (evt instanceof VMDeathEvent)
089: finished = true;
090: else if (evt instanceof ClassPrepareEvent) {
091: if (((ClassPrepareEvent) evt).referenceType() instanceof ClassType) {
092: ClassType origClass = (ClassType) ((ClassPrepareEvent) evt)
093: .referenceType();
094:
095: //System.out.println("Lookup "+origClass.name()+" "+monitor.get( origClass.name() ));
096: MonitorClass c = (MonitorClass) monitor
097: .get(origClass.name());
098: if (c != null) {
099: vm.suspend();
100: addCallbacks(vm, origClass, c);
101: vm.resume();
102: }
103: }
104: }
105: } while (evtIt.hasNext());
106: evtSet.resume();
107: } catch (InterruptedException e) {
108: e.printStackTrace();
109: }
110: } while (!finished);
111:
112: }
113:
114: private void addCallbacks(VirtualMachine vm, ClassType origClass,
115: MonitorClass monitorClass) {
116:
117: JavaClass clazz = Repository.lookupClass(origClass.name());
118: Method[] methods = clazz.getMethods();
119: ConstantPoolGen cp = new ConstantPoolGen(clazz
120: .getConstantPool());
121: InstructionFactory factory = new InstructionFactory(cp);
122:
123: MonitorMethod monitorMethods[] = monitorClass.getMethods();
124:
125: System.out.println("Adding callbacks to class "
126: + origClass.name());
127:
128: for (int j = 0; j < monitorMethods.length; j++)
129: for (int i = 0; i < methods.length; i++) {
130: //System.out.println( methods[i].getName() +" "+monitorMethods[j].getMethodName() );
131: if (monitorMethods[j].equals(methods[i])) {
132: System.out.println(" " + methods[i]);
133:
134: Code methodCode = methods[i].getCode();
135: MethodGen newMethod = new MethodGen(methods[i],
136: origClass.name(), cp);
137: InstructionList instructions = newMethod
138: .getInstructionList();
139: InstructionList newInstructions = new InstructionList();
140:
141: if (monitorMethods[j] instanceof MonitorMethodWithParams) {
142: // We are calling a static method so we would not
143: // normally put 'this' (ALOAD_0).
144: // However we need to pass this to the method
145: newInstructions.append(factory.ALOAD_0);
146:
147: // TODO check how many arguments we should pass
148: // TODO Handle different types correctly
149: Type[] argTypes = monitorMethods[j]
150: .getArgTypes();
151: for (int t = 1; t < argTypes.length; t++)
152: newInstructions.append(factory.createLoad(
153: argTypes[t], t));
154:
155: /*
156: if (argTypes[t]==Type.INT) {
157: newInstructions.append( factory.ILOAD_1 ); // First parameter object
158: } else if (argTypes[t]==Type.OBJECT) {
159: newInstructions.append( factory.ALOAD_1 ); // First parameter object
160: } else
161: throw new RuntimeException( "Unsupported parameter type");
162: */
163:
164: newInstructions.append(factory.createInvoke(
165: callbackClass, monitorMethods[j]
166: .getCallbackMethod(),
167: (Type) Type.VOID, monitorMethods[j]
168: .getArgTypes(),
169: LocalVariable.INVOKESTATIC));
170: } else if (monitorMethods[j] instanceof ReturnFromMethod) {
171: newInstructions.append(factory
172: .createReturn(Type.VOID));
173: } else {
174: newInstructions.append(factory.createInvoke(
175: callbackClass, monitorMethods[j]
176: .getCallbackMethod(),
177: Type.VOID, Type.NO_ARGS,
178: LocalVariable.INVOKESTATIC));
179: }
180: instructions.insert(newInstructions);
181:
182: newMethod.setInstructionList(instructions);
183:
184: //System.out.println( instructions );
185:
186: methods[i] = newMethod.getMethod();
187: i = methods.length;
188: }
189: }
190: clazz.setMethods(methods);
191: clazz.setConstantPool(cp.getFinalConstantPool());
192:
193: java.util.HashMap map = new java.util.HashMap();
194: map.put(origClass, clazz.getBytes());
195: vm.redefineClasses(map);
196: }
197:
198: private VirtualMachine connect() {
199: AttachingConnector connector = getConnector();
200: if (connector == null)
201: throw new RuntimeException("Connector is null");
202: Map args = connector.defaultArguments();
203:
204: Connector.Argument a = (Connector.Argument) args.get("port");
205: a.setValue("8124");
206: try {
207: return connector.attach(args);
208: } catch (Exception e) {
209: e.printStackTrace();
210: }
211:
212: return null;
213: }
214:
215: private AttachingConnector getConnector() {
216: Connector obj = null;
217: Iterator connectors = Bootstrap.virtualMachineManager()
218: .attachingConnectors().iterator();
219: while (connectors.hasNext()) {
220: obj = (Connector) connectors.next();
221: if (obj.name().equals("com.sun.jdi.SocketAttach"))
222: return (AttachingConnector) obj;
223: }
224:
225: return null;
226: }
227:
228: /**
229: * @param args the command line arguments
230: */
231: public static void main(String[] args) {
232: new InterposerSetup();
233: }
234:
235: private HashMap config() {
236: HashMap ret = new HashMap();
237: addClass(
238: ret,
239: new MonitorClass(
240: "javax.media.j3d.Locale",
241: new MonitorMethod[] {
242: new MonitorMethodWithParams(
243: "addBranchGraph",
244: new String[] { "javax.media.j3d.BranchGroup" },
245: new Type[] { Type.OBJECT },
246: "localeAddBranchGraph"),
247: new MonitorMethodWithParams(
248: "removeBranchGraph",
249: new String[] { "javax.media.j3d.BranchGroup" },
250: new Type[] { Type.OBJECT },
251: "localeRemoveBranchGraph") }));
252:
253: addClass(ret,
254: new MonitorClass("javax.media.j3d.BranchGroup",
255: new MonitorMethod[] { new ReturnFromMethod(
256: "compile") }));
257:
258: addClass(
259: ret,
260: new MonitorClass(
261: "javax.media.j3d.Group",
262: new MonitorMethod[] {
263: new MonitorMethodWithParams(
264: "addChild",
265: new String[] { "javax.media.j3d.Node" },
266: new Type[] { Type.OBJECT },
267: "groupAddChild"),
268: new MonitorMethodWithParams("setChild",
269: new String[] {
270: "javax.media.j3d.Node",
271: "int" },
272: new Type[] { Type.OBJECT,
273: Type.INT },
274: "groupSetChild"),
275: new MonitorMethodWithParams(
276: "removeAllChildren",
277: new String[] {}, new Type[] {},
278: "groupRemoveAllChildren"),
279: new MonitorMethodWithParams(
280: "removeChild",
281: new String[] { "javax.media.j3d.Node" },
282: new Type[] { Type.OBJECT },
283: "groupRemoveChild"),
284: new MonitorMethodWithParams(
285: "removeChild",
286: new String[] { "int" },
287: new Type[] { Type.INT },
288: "groupRemoveChild") }));
289: /*
290: addClass( ret,
291: new MonitorClass( "javax.media.j3d.TransformGroup", new MonitorMethod[] {
292: new MonitorMethod( "setTransform", "tgSetTransform" ) })
293: );
294:
295: **/
296: return ret;
297: }
298:
299: private void addClass(HashMap map, MonitorClass mc) {
300: map.put(mc.getClassName(), mc);
301: }
302:
303: /**
304: * The class in which the Methods are being tracked
305: */
306: class MonitorClass {
307: private String className;
308:
309: private MonitorMethod[] methods;
310:
311: public MonitorClass(String className, MonitorMethod[] methods) {
312: this .className = className;
313: this .methods = methods;
314: }
315:
316: public String getClassName() {
317: return className;
318: }
319:
320: public MonitorMethod[] getMethods() {
321: return methods;
322: }
323: }
324:
325: class MonitorMethod {
326: /**
327: * The name of the method to which the interposer will add code
328: */
329: protected String methodName;
330:
331: /**
332: * The method that will be called by the interposed code
333: */
334: protected String callbackMethod;
335:
336: public MonitorMethod(String methodName, String callbackMethod) {
337: this .methodName = methodName;
338: this .callbackMethod = callbackMethod;
339: }
340:
341: public String getMethodName() {
342: return methodName;
343: }
344:
345: public String getCallbackMethod() {
346: return callbackMethod;
347: }
348:
349: Type[] getArgTypes() {
350: return Type.NO_ARGS;
351: }
352:
353: /**
354: * Check if the method represented by this class matches
355: * the one represented by method
356: */
357: public boolean equals(Method method) {
358: if (!method.getName().equals(methodName))
359: return false;
360:
361: return true;
362: }
363: }
364:
365: class MonitorMethodWithParams extends MonitorMethod {
366: protected String[] argTypeNames;
367: protected Type[] argTypes;
368:
369: /**
370: * void addChild( javax.media.j3d.Node a )
371: *
372: * new MonitorMethodWithParams( "addChild",
373: * new String[] { "javax.media.j3d.Node" },
374: * new Type[] { Type.OBJECT },
375: * "someMethod" );
376: *
377: * @params argTypeNames The names of each type of argument
378: * @params argTypes The BCEL type used in the call to the Interposer API
379: */
380: public MonitorMethodWithParams(String methodName,
381: String[] argTypeNames, Type[] argTypes,
382: String callbackMethod) {
383: super (methodName, callbackMethod);
384: this .argTypeNames = argTypeNames;
385: this .argTypes = new Type[argTypes.length + 1];
386:
387: this .argTypes[0] = Type.OBJECT; // Space for object being changed
388: for (int i = 0; i < argTypes.length; i++)
389: this .argTypes[i + 1] = argTypes[i];
390: }
391:
392: Type[] getArgTypes() {
393: return argTypes;
394: }
395:
396: /**
397: * Check if the method represented by this class matches
398: * the one represented by method
399: */
400: public boolean equals(Method method) {
401: if (!method.getName().equals(methodName))
402: return false;
403:
404: ConstantUtf8 c;
405: c = (ConstantUtf8) method.getConstantPool()
406: .getConstant(method.getSignatureIndex(),
407: Constants.CONSTANT_Utf8);
408: String signature = c.getBytes();
409:
410: String types[] = Utility
411: .methodSignatureArgumentTypes(signature);
412:
413: if (types.length != argTypeNames.length)
414: return false;
415:
416: boolean match = true;
417: for (int t = 0; t < types.length && match; t++)
418: if (!types[t].equals(argTypeNames[t]))
419: match = false;
420:
421: return match;
422: }
423: }
424:
425: /**
426: * Cause the method to return as soon as it's called
427: */
428: class ReturnFromMethod extends MonitorMethod {
429: public ReturnFromMethod(String methodName) {
430: super(methodName, null);
431: }
432: }
433: }
|