Further work on machine launch configs
authorDaniel Kirschten <daniel.kirschten@gmx.de>
Sun, 29 Sep 2019 12:36:56 +0000 (14:36 +0200)
committerDaniel Kirschten <daniel.kirschten@gmx.de>
Sun, 29 Sep 2019 12:36:56 +0000 (14:36 +0200)
plugins/net.mograsim.plugin.core/plugin.xml
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineLaunchConfigType.java
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java [new file with mode: 0644]
plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java [new file with mode: 0644]

index 0c1efa6..1120682 100644 (file)
     <launchConfigurationType
           delegate="net.mograsim.plugin.launch.MachineLaunchConfigType"
           id="net.mograsim.plugin.core.launchmachine.type"
-          modes="run, debug"
+          modes="debug"
           name="Mograsim machine">
     </launchConfigurationType>
  </extension>
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineDebugTarget.java
new file mode 100644 (file)
index 0000000..5270c76
--- /dev/null
@@ -0,0 +1,283 @@
+package net.mograsim.plugin.launch;
+
+import java.util.Arrays;
+
+import org.eclipse.core.resources.IMarkerDelta;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IBreakpoint;
+import org.eclipse.debug.core.model.IDebugElement;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStepFilters;
+import org.eclipse.debug.core.model.IThread;
+
+import net.mograsim.machine.Machine;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineDebugTarget extends PlatformObject implements IDebugTarget
+{
+       private final MachineProcess process;
+
+       public MachineDebugTarget(MachineProcess process)
+       {
+               this.process = process;
+
+               DebugPlugin.getDefault().addDebugEventListener(es -> Arrays.stream(es).filter(e -> e.getSource() == process).forEach(e ->
+               {
+                       switch (e.getKind())
+                       {
+                       case DebugEvent.RESUME:
+                               fireResumeEvent(e.getDetail());
+                               break;
+                       case DebugEvent.SUSPEND:
+                               fireSuspendEvent(e.getDetail());
+                               break;
+                       case DebugEvent.TERMINATE:
+                               fireTerminateEvent();
+                               break;
+                       default:
+                               // ignore
+                       }
+               }));
+
+               getLaunch().addDebugTarget(this);
+               fireCreationEvent();
+       }
+
+       public Machine getMachine()
+       {
+               return process.getMachine();
+       }
+
+       @Override
+       public String getName() throws DebugException
+       {
+               return process.getName();
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return this;
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return process.getLaunch();
+       }
+
+       public void setExecutionSpeed(double speed)
+       {
+               process.setExecutionSpeed(speed);
+       }
+
+       @Override
+       public boolean isSuspended()
+       {
+               return process.isSuspended();
+       }
+
+       @Override
+       public boolean canSuspend()
+       {
+               return process.canSuspend();
+       }
+
+       @Override
+       public void suspend() throws DebugException
+       {
+               process.suspend();
+       }
+
+       @Override
+       public boolean canResume()
+       {
+               return process.canResume();
+       }
+
+       @Override
+       public void resume() throws DebugException
+       {
+               process.resume();
+       }
+
+       @Override
+       public boolean isTerminated()
+       {
+               return process.isTerminated();
+       }
+
+       @Override
+       public boolean canTerminate()
+       {
+               return process.canTerminate();
+       }
+
+       @Override
+       public void terminate() throws DebugException
+       {
+               process.terminate();
+       }
+
+       @Override
+       public boolean supportsBreakpoint(IBreakpoint breakpoint)
+       {
+               return false;
+       }
+
+       @Override
+       public void breakpointAdded(IBreakpoint breakpoint)
+       {
+               // ignore; we don't support breakpoints
+       }
+
+       @Override
+       public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta)
+       {
+               // ignore; we don't support breakpoints
+       }
+
+       @Override
+       public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta)
+       {
+               // ignore; we don't support breakpoints
+       }
+
+       @Override
+       public boolean isDisconnected()
+       {
+               return false;
+       }
+
+       @Override
+       public boolean canDisconnect()
+       {
+               return false;
+       }
+
+       @Override
+       public void disconnect() throws DebugException
+       {
+               throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.NOT_SUPPORTED,
+                               "Can't disconnect from a MachineDebugTarget", null));
+       }
+
+       @Override
+       public boolean supportsStorageRetrieval()
+       {
+               return true;
+       }
+
+       @Override
+       public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException
+       {
+               return new MainMemoryBlock(this, startAddress, length);
+       }
+
+       @Override
+       public MachineProcess getProcess()
+       {
+               return process;
+       }
+
+       @Override
+       public boolean hasThreads() throws DebugException
+       {
+               return false;
+       }
+
+       @Override
+       public IThread[] getThreads() throws DebugException
+       {
+               return new IThread[0];
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T> T getAdapter(Class<T> adapter)
+       {
+               if (adapter == IDebugElement.class)
+                       return (T) this;
+
+               // leave this here; maybe we implement IStepFilters someday
+               if (adapter == IStepFilters.class)
+                       if (this instanceof IStepFilters)
+                               return (T) getDebugTarget();
+
+               if (adapter == IDebugTarget.class)
+                       return (T) getDebugTarget();
+
+               if (adapter == ILaunch.class)
+                       return (T) getLaunch();
+
+               if (adapter == IProcess.class)
+                       return (T) getProcess();
+
+               // CONTEXTLAUNCHING
+               if (adapter == ILaunchConfiguration.class)
+                       return (T) getLaunch().getLaunchConfiguration();
+
+               return super.getAdapter(adapter);
+       }
+
+       /**
+        * Fires a creation event for this debug element.
+        */
+       private void fireCreationEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+       }
+
+       /**
+        * Fires a resume for this debug element with the specified detail code.
+        *
+        * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
+        */
+       private void fireResumeEvent(int detail)
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+       }
+
+       /**
+        * Fires a suspend event for this debug element with the specified detail code.
+        *
+        * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
+        */
+       private void fireSuspendEvent(int detail)
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+       }
+
+       /**
+        * Fires a terminate event for this debug element.
+        */
+       private void fireTerminateEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+       }
+
+       /**
+        * Fires a debug event.
+        *
+        * @param event debug event to fire
+        */
+       private static void fireEvent(DebugEvent event)
+       {
+               DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] { event });
+       }
+}
\ No newline at end of file
index 03c481a..27aca7e 100644 (file)
@@ -1,18 +1,19 @@
 package net.mograsim.plugin.launch;
 
-import java.io.IOException;
-
+import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResourceChangeEvent;
 import org.eclipse.core.resources.IResourceChangeListener;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.ILaunch;
 import org.eclipse.debug.core.ILaunchConfiguration;
 import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
 
+import net.mograsim.machine.MachineDefinition;
 import net.mograsim.plugin.MograsimActivator;
+import net.mograsim.plugin.nature.MachineContext;
+import net.mograsim.plugin.nature.ProjectMachineContext;
 
 public class MachineLaunchConfigType extends LaunchConfigurationDelegate
 {
@@ -23,25 +24,26 @@ public class MachineLaunchConfigType extends LaunchConfigurationDelegate
        public MachineLaunchConfigType()
        {
                this.resChangedListener = this::resourceChanged;
+               ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener,
+                               // IResourceChangeEvent.POST_BUILD |
+                               IResourceChangeEvent.POST_CHANGE |
+                               // IResourceChangeEvent.PRE_BUILD |
+                               // IResourceChangeEvent.PRE_CLOSE |
+                               // IResourceChangeEvent.PRE_DELETE |
+                               // IResourceChangeEvent.PRE_REFRESH |
+                                               0);
        }
 
        @Override
        public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException
        {
-               ResourcesPlugin.getWorkspace().addResourceChangeListener(resChangedListener,
-                               IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_BUILD
-                                               | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_REFRESH);
-               System.out.println("launch");
-               // TODO start a machine
-               ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "\"echo Press Enter... &&pause>NUL && echo finished\"");
-               try
-               {
-                       launch.addProcess(DebugPlugin.newProcess(launch, pb.start(), ""));
-               }
-               catch (IOException e)
-               {
-                       e.printStackTrace();
-               }
+               String projName = configuration.getAttribute(PROJECT_ATTR, "");
+               IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);
+               MachineContext machineContext = ProjectMachineContext.getMachineContextOf(project);
+               MachineDefinition machineDefinition = machineContext.getMachineDefinition().orElseThrow();
+
+               MachineDebugTarget debugTarget = new MachineDebugTarget(new MachineProcess(launch, machineDefinition));
+               debugTarget.setExecutionSpeed(10d);
        }
 
        private void resourceChanged(IResourceChangeEvent event)
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MachineProcess.java
new file mode 100644 (file)
index 0000000..b22d9c5
--- /dev/null
@@ -0,0 +1,265 @@
+package net.mograsim.plugin.launch;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugEvent;
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.core.model.IStreamsProxy;
+import org.eclipse.debug.core.model.ISuspendResume;
+
+import net.mograsim.logic.model.LogicExecuter;
+import net.mograsim.machine.Machine;
+import net.mograsim.machine.MachineDefinition;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MachineProcess extends PlatformObject implements IProcess, ISuspendResume
+{
+       private final ILaunch launch;
+       private final Machine machine;
+       private final LogicExecuter exec;
+
+       private boolean running;
+
+       public MachineProcess(ILaunch launch, MachineDefinition machineDefinition)
+       {
+               this.launch = launch;
+               this.machine = machineDefinition.createNew();
+               this.exec = new LogicExecuter(machine.getTimeline());
+
+               exec.startLiveExecution();
+               running = true;
+
+               launch.addProcess(this);
+               fireCreationEvent();
+       }
+
+       public Machine getMachine()
+       {
+               return machine;
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return launch;
+       }
+
+       public String getName()
+       {
+               return "Mograsim machine \"" + machine.getDefinition().getId() + '"';
+       }
+
+       @Override
+       public String getLabel()
+       {
+               return getName() + " (" + getStatusString() + ')';
+       }
+
+       public String getStatusString()
+       {
+               return isTerminated() ? "terminated" : isSuspended() ? "paused" : "running";
+       }
+
+       public void setExecutionSpeed(double speed)
+       {
+               exec.setSpeedFactor(speed);
+       }
+
+       @Override
+       public boolean isSuspended()
+       {
+               return exec.isPaused();
+       }
+
+       @Override
+       public boolean canSuspend()
+       {
+               return !isTerminated() && !isSuspended();
+       }
+
+       @Override
+       public void suspend() throws DebugException
+       {
+               if (isTerminated())
+                       throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+                                       "Can't suspend a terminated MachineProcess", null));
+               if (isSuspended())
+                       throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+                                       "Can't suspend a suspended MachineProcess", null));
+
+               exec.pauseLiveExecution();
+               fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
+       }
+
+       @Override
+       public boolean canResume()
+       {
+               return !isTerminated() && isSuspended();
+       }
+
+       @Override
+       public void resume() throws DebugException
+       {
+               if (isTerminated())
+                       throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+                                       "Can't resume a terminated MachineProcess", null));
+               if (!isSuspended())
+                       throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+                                       "Can't resume a non-suspended MachineProcess", null));
+
+               exec.unpauseLiveExecution();
+               fireResumeEvent(DebugEvent.CLIENT_REQUEST);
+       }
+
+       @Override
+       public boolean isTerminated()
+       {
+               return !running;
+       }
+
+       @Override
+       public boolean canTerminate()
+       {
+               return !isTerminated();
+       }
+
+       @Override
+       public void terminate() throws DebugException
+       {
+               if (isTerminated())
+                       return;
+
+               exec.stopLiveExecution();
+               running = false;
+               fireTerminateEvent();
+       }
+
+       @Override
+       public int getExitValue() throws DebugException
+       {
+               if (!isTerminated())
+                       throw new DebugException(new Status(IStatus.ERROR, MograsimActivator.PLUGIN_ID, DebugException.TARGET_REQUEST_FAILED,
+                                       "Can't get the exit value of a running process", null));
+
+               return 0;
+       }
+
+       @Override
+       public IStreamsProxy getStreamsProxy()
+       {
+               return null;
+       }
+
+       private Map<String, String> attributes;
+
+       @Override
+       public void setAttribute(String key, String value)
+       {
+               if (attributes == null)
+                       attributes = new HashMap<>(5);
+               Object origVal = attributes.get(key);
+               if (origVal != null && origVal.equals(value))
+                       return;
+               attributes.put(key, value);
+               fireChangeEvent();
+       }
+
+       @Override
+       public String getAttribute(String key)
+       {
+               if (attributes == null)
+                       return null;
+               return attributes.get(key);
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T> T getAdapter(Class<T> adapter)
+       {
+               if (adapter.equals(IProcess.class))
+                       return (T) this;
+
+               if (adapter.equals(IDebugTarget.class))
+               {
+                       ILaunch launch = getLaunch();
+                       IDebugTarget[] targets = launch.getDebugTargets();
+                       for (int i = 0; i < targets.length; i++)
+                               if (this.equals(targets[i].getProcess()))
+                                       return (T) targets[i];
+                       return null;
+               }
+
+               if (adapter.equals(ILaunch.class))
+                       return (T) getLaunch();
+
+               if (adapter.equals(ILaunchConfiguration.class))
+                       return (T) getLaunch().getLaunchConfiguration();
+
+               return super.getAdapter(adapter);
+       }
+
+       /**
+        * Fires a creation event.
+        */
+       private void fireCreationEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.CREATE));
+       }
+
+       /**
+        * Fires a change event.
+        */
+       private void fireChangeEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
+       }
+
+       /**
+        * Fires a resume for this debug element with the specified detail code.
+        *
+        * @param detail detail code for the resume event, such as <code>DebugEvent.STEP_OVER</code>
+        */
+       private void fireResumeEvent(int detail)
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.RESUME, detail));
+       }
+
+       /**
+        * Fires a suspend event for this debug element with the specified detail code.
+        *
+        * @param detail detail code for the suspend event, such as <code>DebugEvent.BREAKPOINT</code>
+        */
+       private void fireSuspendEvent(int detail)
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.SUSPEND, detail));
+       }
+
+       /**
+        * Fires a terminate event.
+        */
+       private void fireTerminateEvent()
+       {
+               fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
+       }
+
+       /**
+        * Fires the given debug event.
+        *
+        * @param event debug event to fire
+        */
+       private static void fireEvent(DebugEvent event)
+       {
+               DebugPlugin manager = DebugPlugin.getDefault();
+               if (manager != null)
+                       manager.fireDebugEventSet(new DebugEvent[] { event });
+       }
+}
\ No newline at end of file
diff --git a/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java b/plugins/net.mograsim.plugin.core/src/net/mograsim/plugin/launch/MainMemoryBlock.java
new file mode 100644 (file)
index 0000000..b068fc0
--- /dev/null
@@ -0,0 +1,98 @@
+package net.mograsim.plugin.launch;
+
+import org.eclipse.debug.core.DebugException;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.model.IDebugTarget;
+import org.eclipse.debug.core.model.IMemoryBlock;
+
+import net.mograsim.machine.MainMemory;
+import net.mograsim.plugin.MograsimActivator;
+
+public class MainMemoryBlock implements IMemoryBlock
+{
+       private final MachineDebugTarget debugTarget;
+       private final MainMemory mem;
+       private final long startAddress;
+       private final int length;
+
+       public MainMemoryBlock(MachineDebugTarget debugTarget, long startAddress, long length)
+       {
+               if (length < 0)
+                       throw new IllegalArgumentException("Can't create a memory block of negative size");
+               if (length > Integer.MAX_VALUE)
+                       throw new IllegalArgumentException("Can't create a memory block bigger than Integer.MAX_VALUE (" + Integer.MAX_VALUE + "\"");
+               if (startAddress % 2 != 0)
+                       throw new IllegalArgumentException("Can't create an unaligned memory block");
+
+               this.debugTarget = debugTarget;
+               this.mem = debugTarget.getMachine().getMainMemory();
+               this.startAddress = startAddress;
+               this.length = (int) length;
+       }
+
+       @Override
+       public String getModelIdentifier()
+       {
+               return MograsimActivator.PLUGIN_ID;
+       }
+
+       @Override
+       public IDebugTarget getDebugTarget()
+       {
+               return debugTarget;
+       }
+
+       @Override
+       public ILaunch getLaunch()
+       {
+               return debugTarget.getLaunch();
+       }
+
+       @Override
+       public <T> T getAdapter(Class<T> adapter)
+       {
+               // TODO
+               return null;
+       }
+
+       @Override
+       public long getStartAddress()
+       {
+               return startAddress;
+       }
+
+       @Override
+       public long getLength()
+       {
+               return length;
+       }
+
+       @Override
+       public byte[] getBytes() throws DebugException
+       {
+               // TODO speedup. Maybe make a method for this in MainMemory?
+               byte[] bs = new byte[length];
+               int i;
+               long j;
+               for (i = 0, j = startAddress / 2; i < length; i++)
+               {
+                       short word = mem.getCellAsBigInteger(j).shortValueExact();
+                       bs[i + 0] = (byte) (word & 0xFF);
+                       bs[i + 1] = (byte) (word >>> 8);
+               }
+               return bs;
+       }
+
+       @Override
+       public boolean supportsValueModification()
+       {
+               // TODO
+               return false;
+       }
+
+       @Override
+       public void setValue(long offset, byte[] bytes) throws DebugException
+       {
+               // TODO
+       }
+}
\ No newline at end of file