Merge branch threads into development
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / launch / MachineDebugTarget.java
1 package net.mograsim.plugin.launch;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.function.Consumer;
6
7 import org.eclipse.core.resources.IMarkerDelta;
8 import org.eclipse.core.runtime.IStatus;
9 import org.eclipse.core.runtime.PlatformObject;
10 import org.eclipse.core.runtime.Status;
11 import org.eclipse.debug.core.DebugEvent;
12 import org.eclipse.debug.core.DebugException;
13 import org.eclipse.debug.core.DebugPlugin;
14 import org.eclipse.debug.core.ILaunch;
15 import org.eclipse.debug.core.ILaunchConfiguration;
16 import org.eclipse.debug.core.model.IBreakpoint;
17 import org.eclipse.debug.core.model.IDebugElement;
18 import org.eclipse.debug.core.model.IDebugTarget;
19 import org.eclipse.debug.core.model.IMemoryBlock;
20 import org.eclipse.debug.core.model.IMemoryBlockExtension;
21 import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
22 import org.eclipse.debug.core.model.IProcess;
23 import org.eclipse.debug.core.model.IStepFilters;
24 import org.eclipse.debug.core.model.IThread;
25
26 import net.mograsim.logic.model.LogicExecuter;
27 import net.mograsim.machine.Machine;
28 import net.mograsim.machine.MachineDefinition;
29 import net.mograsim.plugin.MograsimActivator;
30 import net.mograsim.plugin.launch.MachineLaunchConfigType.MachineLaunchParams;
31
32 public class MachineDebugTarget extends PlatformObject implements IDebugTarget, IMemoryBlockRetrievalExtension
33 {
34         private final static boolean USE_PSEUDO_THREAD = true;
35
36         private final ILaunch launch;
37         private final Machine machine;
38         private final LogicExecuter exec;
39         private final MachineThread thread;
40
41         private boolean running;
42
43         private final List<Consumer<Double>> executionSpeedListeners;
44
45         private final MachineLaunchParams launchParams;
46
47         public MachineDebugTarget(ILaunch launch, MachineLaunchParams launchParams, MachineDefinition machineDefinition)
48         {
49                 this.launch = launch;
50                 this.machine = machineDefinition.createNew();
51                 this.exec = new LogicExecuter(machine.getTimeline());
52
53                 this.executionSpeedListeners = new ArrayList<>();
54                 this.launchParams = launchParams;
55
56                 exec.startLiveExecution();
57                 running = true;
58
59                 getLaunch().addDebugTarget(this);
60                 fireCreationEvent();
61
62                 // create after creating ourself
63                 this.thread = USE_PSEUDO_THREAD ? new MachineThread(this) : null;
64         }
65
66         public Machine getMachine()
67         {
68                 return machine;
69         }
70
71         @Override
72         public String getName() throws DebugException
73         {
74                 return "Mograsim machine \"" + machine.getDefinition().getId() + '"';
75         }
76
77         @Override
78         public String getModelIdentifier()
79         {
80                 return MograsimActivator.PLUGIN_ID;
81         }
82
83         @Override
84         public IDebugTarget getDebugTarget()
85         {
86                 return this;
87         }
88
89         @Override
90         public ILaunch getLaunch()
91         {
92                 return launch;
93         }
94
95         public MachineLaunchParams getLaunchParams()
96         {
97                 return launchParams;
98         }
99
100         public double getExecutionSpeed()
101         {
102                 return exec.getSpeedFactor();
103         }
104
105         public void setExecutionSpeed(double speed)
106         {
107                 if (getExecutionSpeed() != speed)
108                 {
109                         exec.setSpeedFactor(speed);
110                         callExecutionSpeedListener(speed);
111                 }
112         }
113
114         @Override
115         public boolean isSuspended()
116         {
117                 return exec.isPaused();
118         }
119
120         @Override
121         public boolean canSuspend()
122         {
123                 return !isTerminated() && !isSuspended();
124         }
125
126         @Override
127         public void suspend() throws DebugException
128         {
129                 if (isTerminated())
130                         throwDebugException("Can't suspend a terminated MachineProcess");
131                 if (isSuspended())
132                         throwDebugException("Can't suspend a suspended MachineProcess");
133
134                 exec.pauseLiveExecution();
135                 fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
136         }
137
138         @Override
139         public boolean canResume()
140         {
141                 return !isTerminated() && isSuspended();
142         }
143
144         @Override
145         public void resume() throws DebugException
146         {
147                 if (isTerminated())
148                         throwDebugException("Can't resume a terminated MachineProcess");
149                 if (!isSuspended())
150                         throwDebugException("Can't resume a non-suspended MachineProcess");
151
152                 exec.unpauseLiveExecution();
153                 fireResumeEvent(DebugEvent.CLIENT_REQUEST);
154         }
155
156         @Override
157         public boolean isTerminated()
158         {
159                 return !running;
160         }
161
162         @Override
163         public boolean canTerminate()
164         {
165                 return !isTerminated();
166         }
167
168         @Override
169         public void terminate() throws DebugException
170         {
171                 if (isTerminated())
172                         return;
173
174                 exec.stopLiveExecution();
175                 running = false;
176                 fireTerminateEvent();
177         }
178
179         @Override
180         public boolean supportsBreakpoint(IBreakpoint breakpoint)
181         {
182                 return false;
183         }
184
185         @Override
186         public void breakpointAdded(IBreakpoint breakpoint)
187         {
188                 // ignore; we don't support breakpoints
189         }
190
191         @Override
192         public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta)
193         {
194                 // ignore; we don't support breakpoints
195         }
196
197         @Override
198         public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta)
199         {
200                 // ignore; we don't support breakpoints
201         }
202
203         @Override
204         public boolean isDisconnected()
205         {
206                 return false;
207         }
208
209         @Override
210         public boolean canDisconnect()
211         {
212                 return false;
213         }
214
215         @Override
216         public void disconnect() throws DebugException
217         {
218                 throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.NOT_SUPPORTED,
219                                 "Can't disconnect from a MachineDebugTarget", null));
220         }
221
222         @Override
223         public boolean supportsStorageRetrieval()
224         {
225                 return true;
226         }
227
228         @SuppressWarnings("deprecation") // TODO can we throw a DebugException instead?
229         @Override
230         public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
231         {
232                 return new MainMemoryBlock(this, startAddress, length);
233         }
234
235         @Override
236         public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException
237         {
238                 return new MainMemoryBlockExtension(this, expression, context);
239         }
240
241         @Override
242         public IProcess getProcess()
243         {
244                 return null;
245         }
246
247         @Override
248         public boolean hasThreads() throws DebugException
249         {
250                 return USE_PSEUDO_THREAD;
251         }
252
253         @Override
254         public IThread[] getThreads() throws DebugException
255         {
256                 return thread == null ? new IThread[0] : new IThread[] { thread };
257         }
258
259         public void addExecutionSpeedListener(Consumer<Double> executionSpeedListener)
260         {
261                 executionSpeedListeners.add(executionSpeedListener);
262         }
263
264         public void removeExecutionSpeedListener(Consumer<Double> executionSpeedListener)
265         {
266                 executionSpeedListeners.remove(executionSpeedListener);
267         }
268
269         private void callExecutionSpeedListener(double executionSpeed)
270         {
271                 executionSpeedListeners.forEach(l -> l.accept(executionSpeed));
272         }
273
274         @SuppressWarnings("unchecked")
275         @Override
276         public <T> T getAdapter(Class<T> adapter)
277         {
278                 if (adapter == IDebugElement.class)
279                         return (T) this;
280
281                 // leave this here; maybe we implement IStepFilters someday
282                 if (adapter == IStepFilters.class)
283                         if (this instanceof IStepFilters)
284                                 return (T) getDebugTarget();
285
286                 if (adapter == IDebugTarget.class)
287                         return (T) getDebugTarget();
288
289                 if (adapter == ILaunch.class)
290                         return (T) getLaunch();
291
292                 // CONTEXTLAUNCHING
293                 if (adapter == ILaunchConfiguration.class)
294                         return (T) getLaunch().getLaunchConfiguration();
295
296                 return super.getAdapter(adapter);
297         }
298
299         /**
300          * Fires a creation event for this debug element.
301          */
302         private void fireCreationEvent()
303         {
304                 fireEvent(new DebugEvent(this, DebugEvent.CREATE));
305         }
306
307         /**
308          * Fires a resume for this debug element with the specified detail code.
309          *
310          * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
311          */
312         private void fireResumeEvent(int detail)
313         {
314                 fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
315         }
316
317         /**
318          * Fires a suspend event for this debug element with the specified detail code.
319          *
320          * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
321          */
322         private void fireSuspendEvent(int detail)
323         {
324                 fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
325         }
326
327         /**
328          * Fires a terminate event for this debug element.
329          */
330         private void fireTerminateEvent()
331         {
332                 fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
333         }
334
335         /**
336          * Fires a debug event.
337          *
338          * @param event debug event to fire
339          */
340         private static void fireEvent(DebugEvent event)
341         {
342                 DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
343         }
344
345         private static void throwDebugException(String message) throws DebugException
346         {
347                 throw new DebugException(
348                                 new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED, message, null));
349         }
350 }