2 JPC-RR: A x86 PC Hardware Emulator
5 Copyright (C) 2007-2009 Isis Innovation Limited
6 Copyright (C) 2009-2010 H. Ilari Liusvaara
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License version 2 as published by
10 the Free Software Foundation.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 Based on JPC x86 PC Hardware emulator,
22 A project from the Physics Dept, The University of Oxford
24 Details about original JPC can be found at:
26 www-jpc.physics.ox.ac.uk
30 package org
.jpc
.pluginsbase
;
33 import org
.jpc
.emulator
.*;
34 import java
.lang
.reflect
.*;
35 import static org
.jpc
.Misc
.errorDialog
;
39 private Set
<Plugin
> plugins
;
40 private Set
<Plugin
> nonRegisteredPlugins
;
41 private boolean manualShutdown
;
42 private boolean shutDown
;
43 private boolean commandComplete
;
44 private volatile boolean shuttingDown
;
45 private volatile boolean running
;
46 private volatile boolean valueReturned
;
47 private volatile Object
[] returnValueObj
;
50 //Create plugin manager.
53 plugins
= new HashSet
<Plugin
>();
54 nonRegisteredPlugins
= new HashSet
<Plugin
>();
55 Runtime
.getRuntime().addShutdownHook(new ShutdownHook());
56 manualShutdown
= true;
62 public boolean isShuttingDown()
67 //Shut down and exit the emulator program.
68 public void shutdownEmulator()
70 boolean doAgain
= true;
72 Set
<Plugin
> plugins2
= new HashSet
<Plugin
>();
79 for(Plugin plugin
: plugins
) {
80 System
.err
.println("Informational: Shutting down " + plugin
.getClass().getName() + "...");
81 if(plugin
.systemShutdown())
82 System
.err
.println("Informational: Shut down " + plugin
.getClass().getName() + ".");
95 //Signal reconnect event to all plugins.
96 public synchronized void reconnect(PC pc
)
101 //All non-registered plugins become registered as we will recconnect them.
102 plugins
.addAll(nonRegisteredPlugins
);
103 nonRegisteredPlugins
.clear();
105 for(Plugin plugin
: plugins
) {
106 System
.err
.println("Informational: Reconnecting " + plugin
.getClass().getName() + "...");
107 plugin
.reconnect(pc
);
108 System
.err
.println("Informational: Reconnected " + plugin
.getClass().getName() + "...");
112 //Signal pc stop event to all plugins.
113 public synchronized void pcStopped()
115 for(Plugin plugin
: plugins
) {
120 for(Plugin plugin
: nonRegisteredPlugins
) {
121 System
.err
.println("Informational: Reconnecting " + plugin
.getClass().getName() + "...");
122 plugin
.reconnect(currentPC
);
123 System
.err
.println("Informational: Reconnected " + plugin
.getClass().getName() + "...");
125 //All non-registered plugins become registered as we recconnected them.
126 plugins
.addAll(nonRegisteredPlugins
);
127 nonRegisteredPlugins
.clear();
131 //Signal pc start event to all plugins.
132 public synchronized void pcStarted()
134 for(Plugin plugin
: plugins
) {
140 private final boolean reinterpretable(Class
<?
> type
, Object argument
)
144 if(argument
.getClass() == type
)
146 if(type
== String
.class)
148 if(type
== Integer
.class)
150 Integer
.decode(argument
.toString());
152 } catch(NumberFormatException e
) {
155 if(type
== Long
.class)
157 Long
.decode(argument
.toString());
159 } catch(NumberFormatException e
) {
165 private final boolean methodOk(Method method
, Object
[] args
)
167 Class
<?
>[] argumentTypes
= method
.getParameterTypes();
168 if(argumentTypes
.length
== 0)
169 return (args
== null || args
.length
== 0);
171 for(int i
= 0; i
< argumentTypes
.length
; i
++) {
172 Class
<?
> subType
= argumentTypes
[i
].getComponentType();
173 if(subType
== null) {
174 if(argIterator
< args
.length
) {
175 if(!reinterpretable(argumentTypes
[i
], args
[argIterator
++]))
179 } else if(argIterator
== args
.length
) {
181 for(int j
= 0; j
< args
.length
- argIterator
; j
++)
182 if(!reinterpretable(subType
, args
[argIterator
++]))
188 private final boolean namesMatch(String cmd
, String method
)
190 if(!method
.startsWith("eci_"))
192 cmd
= cmd
.replaceAll("-", "_");
193 return method
.substring(4).equals(cmd
);
196 private final Method
chooseMethod(Class
<?
> clazz
, String cmd
, Object
[] args
)
198 for(Method method
: clazz
.getDeclaredMethods())
199 if(namesMatch(cmd
, method
.getName()) && methodOk(method
, args
)) {
205 private final Object
reinterpretToType(Class
<?
> type
, Object argument
)
207 //FIXME: Add more cases.
210 if(argument
.getClass() == type
)
212 if(type
== String
.class)
213 return argument
.toString();
214 else if(type
== Integer
.class) {
216 return new Integer(Integer
.decode(argument
.toString()));
217 } catch(NumberFormatException e
) {
218 return null; //Doesn't convert.
220 } else if(type
== Long
.class) {
222 return new Long(Long
.decode(argument
.toString()));
223 } catch(NumberFormatException e
) {
224 return null; //Doesn't convert.
227 return null; //Reinterpretation not possible.
231 private final Object
[] prepareArguments(Method method
, Object
[] args
)
233 Class
<?
>[] argumentTypes
= method
.getParameterTypes();
234 Object
[] ret
= new Object
[argumentTypes
.length
];
235 if(argumentTypes
.length
== 0)
238 for(int i
= 0; i
< argumentTypes
.length
; i
++) {
239 Class
<?
> subType
= argumentTypes
[i
].getComponentType();
240 if(subType
== null) {
241 if(argIterator
< args
.length
)
242 ret
[i
] = reinterpretToType(argumentTypes
[i
], args
[argIterator
++]);
244 System
.err
.println("Warning: Ran out of arguments for ECI (incorrect method array argument?).");
247 } else if(argIterator
== args
.length
) {
250 int elts
= args
.length
- argIterator
;
251 ret
[i
] = Array
.newInstance(subType
, elts
);
252 for(int j
= 0; j
< elts
; j
++)
253 Array
.set(ret
[i
], j
, reinterpretToType(subType
, args
[argIterator
++]));
259 private final boolean invokeCommand(Plugin plugin
, String cmd
, Object
[] args
, boolean synchronous
)
261 boolean done
= false;
262 boolean inherentlySynchronous
= false;
263 Class
<?
> targetClass
= plugin
.getClass();
264 Method choosenMethod
= null;
265 Object
[] callArgs
= null;
267 choosenMethod
= chooseMethod(targetClass
, cmd
, args
);
268 commandComplete
= false;
270 if(choosenMethod
!= null) {
271 callArgs
= prepareArguments(choosenMethod
, args
);
272 if(choosenMethod
.getReturnType() == void.class) {
274 choosenMethod
.invoke(plugin
, callArgs
);
276 } catch(InvocationTargetException e
) {
277 errorDialog(e
.getCause(), "Error in ECI method", null, "Ignore");
278 } catch(Exception e
) {
279 System
.err
.println("Error calling ECI method: " + e
.getMessage());
281 inherentlySynchronous
= true;
282 } else if(choosenMethod
.getReturnType() == boolean.class) {
285 ret
= choosenMethod
.invoke(plugin
, callArgs
);
287 } catch(InvocationTargetException e
) {
288 errorDialog(e
.getCause(), "Error in ECI method", null, "Ignore");
289 } catch(Exception e
) {
290 System
.err
.println("Error calling ECI method: " + e
.getMessage());
292 if(ret
!= null && ret
instanceof Boolean
)
293 inherentlySynchronous
= !(((Boolean
)ret
).booleanValue());
295 inherentlySynchronous
= true;
297 System
.err
.println("Error: Bad return type '" + choosenMethod
.getReturnType() + "' for ECI.");
298 inherentlySynchronous
= true; //Bad calls are always synchronous.
301 inherentlySynchronous
= true; //Bad calls are always synchronous.
304 while(synchronous
&& !inherentlySynchronous
&& !commandComplete
)
309 } catch(Exception e
) {
314 //Invoke the external command interface.
315 public void invokeExternalCommand(String cmd
, Object
[] args
)
317 boolean done
= false;
318 for(Plugin plugin
: plugins
)
319 done
= invokeCommand(plugin
, cmd
, args
, false) || done
;
321 System
.err
.println("Warning: ECI invocation '" + cmd
+ "' not delivereble.");
324 //Invoke the external command interface.
325 public void invokeExternalCommandSynchronous(String cmd
, String
[] args
)
327 boolean done
= false;
328 for(Plugin plugin
: plugins
)
329 done
= invokeCommand(plugin
, cmd
, args
, true) || done
;
331 System
.err
.println("Warning: Synchronous ECI invocation '" + cmd
+ "' not delivereble.");
334 //Invoke the external command interface.
335 public synchronized Object
[] invokeExternalCommandReturn(String cmd
, String
[] args
)
337 valueReturned
= false;
338 returnValueObj
= null;
339 for(Plugin plugin
: plugins
) {
340 invokeCommand(plugin
, cmd
, args
, true);
342 return returnValueObj
;
344 System
.err
.println("Warning: ECI call '" + cmd
+ "' not delivereble.");
348 //Signal completion of command.
349 public synchronized void returnValue(Object
... ret
)
351 returnValueObj
= ret
;
352 valueReturned
= true;
353 commandComplete
= true;
357 //Signal completion of command.
358 public synchronized void signalCommandCompletion()
360 commandComplete
= true;
364 //Add new plugin and invoke main thread for it.
365 public synchronized void registerPlugin(Plugin plugin
)
367 if(currentPC
== null || !running
)
370 nonRegisteredPlugins
.add(plugin
);
371 (new PluginThread(plugin
)).start();
373 if(currentPC
!= null && !running
) {
374 System
.err
.println("Informational: Reconnecting " + plugin
.getClass().getName() + "...");
375 plugin
.reconnect(currentPC
);
376 System
.err
.println("Informational: Reconnected " + plugin
.getClass().getName() + "...");
380 public synchronized boolean unregisterPlugin(Plugin plugin
)
382 if(nonRegisteredPlugins
.contains(plugin
)) {
383 nonRegisteredPlugins
.remove(plugin
);
384 System
.err
.println("Informational: Shutting down " + plugin
.getClass().getName() + "...");
385 plugin
.systemShutdown();
386 System
.err
.println("Informational: Shut down " + plugin
.getClass().getName() + ".");
389 System
.err
.println("Informational: Shutting down " + plugin
.getClass().getName() + "...");
390 if(plugin
.systemShutdown()) {
391 System
.err
.println("Informational: Shut down " + plugin
.getClass().getName() + ".");
392 plugins
.remove(plugin
);
395 System
.err
.println("Error: " + plugin
.getClass().getName() + " does not want to shut down.");
401 private class ShutdownHook
extends Thread
405 manualShutdown
= false;
410 private class PluginThread
extends Thread
412 private Plugin plugin
;
414 public PluginThread(Plugin _plugin
)