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