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