1 /*******************************************************************************
2  * Copyright (c) 2005, 2018 Wind River Systems and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Bjorn Freeman-Benson - initial API and implementation
13  *     Pawel Piech (Wind River) - ported PDA Virtual Machine to Java (Bug 261400)
14  *     IBM Coporation - bug fixing
15  *******************************************************************************/
16 package org.eclipse.debug.examples.pdavm;
17 
18 import java.io.BufferedReader;
19 import java.io.FileReader;
20 import java.io.IOException;
21 import java.io.InputStreamReader;
22 import java.io.OutputStream;
23 import java.io.PrintStream;
24 import java.io.StringWriter;
25 import java.net.ServerSocket;
26 import java.net.Socket;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.StringTokenizer;
36 import java.util.TreeSet;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39 
40 /**
41  * Push Down Automata interpreter.
42  *
43  * @since 3.5
44  */
45 public class PDAVirtualMachine {
46 
47 	static class Stack extends LinkedList<Object> {
48 		private static final long serialVersionUID = 1L;
49 
50 		@Override
pop()51 		public Object pop() {
52 			return isEmpty() ? Integer.valueOf(0) : remove(size() - 1);
53 		}
54 
55 		@Override
push(Object value)56 		public void push(Object value) {
57 			add(value);
58 		}
59 	}
60 
61 	static class Register {
Register(String name)62 		Register(String name) {
63 			fName = name;
64 		}
65 		String fName;
66 		String fGroup = "<no_group>"; //$NON-NLS-1$
67 		boolean fIsWriteable = true;
68 		Map<String, BitField> fBitFields = new LinkedHashMap<>(0);
69 		int fValue;
70 	}
71 
72 	static class BitField {
BitField(String name)73 		BitField(String name) {
74 			fName = name;
75 		}
76 		String fName;
77 		int fBitOffset;
78 		int fBitCount;
79 		Map<String, Integer> fMnemonics = new LinkedHashMap<>(0);
80 	}
81 
82 	Map<String, Register> fRegisters = new LinkedHashMap<>(0);
83 
84 	class Args {
85 		final String[] fArgs;
86 
87 		int next = 0;
88 
Args(String[] args)89 		Args(String[] args) {
90 			fArgs = args;
91 		}
92 
hasNextArg()93 		boolean hasNextArg() {
94 			return fArgs.length > next;
95 		}
96 
getNextStringArg()97 		String getNextStringArg() {
98 			if (fArgs.length > next) {
99 				return fArgs[next++];
100 			}
101 			return ""; //$NON-NLS-1$
102 		}
103 
getNextIntArg()104 		int getNextIntArg() {
105 			String arg = getNextStringArg();
106 			try {
107 				return Integer.parseInt(arg);
108 			} catch (NumberFormatException e) {
109 			}
110 			return 0;
111 		}
112 
getNextBooleanArg()113 		boolean getNextBooleanArg() {
114 			String arg = getNextStringArg();
115 			try {
116 				return Boolean.getBoolean(arg);
117 			} catch (NumberFormatException e) {
118 			}
119 			return false;
120 		}
121 
getNextIntOrStringArg()122 		Object getNextIntOrStringArg() {
123 			String arg = getNextStringArg();
124 			try {
125 				return Integer.valueOf(arg);
126 			} catch (NumberFormatException e) {
127 			}
128 			return arg;
129 		}
130 
getThreadArg()131 		PDAThread getThreadArg() {
132 			int id = getNextIntArg();
133 			return fThreads.get( Integer.valueOf(id) );
134 		}
135 	}
136 
137 	class PDAThread {
138 		final int fID;
139 
140 		/** The push down automata data stack (the data stack). */
141 		final Stack fStack = new Stack();
142 
143 		/**
144 		 * PDAThread copy of the code. It can differ from the program if
145 		 * performing an evaluation.
146 		 */
147 		String[] fThreadCode;
148 
149 		/** PDAThread copy of the labels. */
150 		Map<String, Integer> fThreadLabels;
151 
152 		/** The stack of stack frames (the control stack) */
153 		final List<Frame> fFrames = new LinkedList<>();
154 
155 		/** Current stack frame (not includced in fFrames) */
156 		Frame fCurrentFrame;
157 
158 		/**
159 		 * The run flag is true if the thread is running. If the run flag is
160 		 * false, the thread exits the next time the main instruction loop runs.
161 		 */
162 		boolean fRun = true;
163 
164 		String fSuspend = null;
165 
166 		boolean fStep = false;
167 
168 		boolean fStepReturn = false;
169 
170 		int fSavedPC;
171 
172 		boolean fPerformingEval = false;
173 
PDAThread(int id, String function, int pc)174 		PDAThread(int id, String function, int pc) {
175 			fID = id;
176 			fCurrentFrame = new Frame(function, pc);
177 			fThreadCode = fCode;
178 			fThreadLabels = fLabels;
179 		}
180 	}
181 
182 	final Map<Integer, PDAThread> fThreads = new LinkedHashMap<>();
183 
184 	int fNextThreadId = 1;
185 
186 	boolean fStarted = true;
187 	/**
188 	 * The code is stored as an array of strings, each line of the source file
189 	 * being one entry in the array.
190 	 */
191 	final String[] fCode;
192 
193 	/** A mapping of labels to indicies in the code array */
194 	final Map<String, Integer> fLabels;
195 
196 	/** Each stack frame is a mapping of variable names to values. */
197 	class Frame {
198 		final Map<String, Object> fLocalVariables = new LinkedHashMap<>();
199 
200 		/**
201 		 * The name of the function in this frame
202 		 */
203 		final String fFunction;
204 
205 		/**
206 		 * The current program counter in the frame the pc points to the next
207 		 * instruction to be executed
208 		 */
209 		int fPC;
210 
Frame(String function, int pc)211 		Frame(String function, int pc) {
212 			fFunction = function;
213 			fPC = pc;
214 		}
215 
set(String name, Object value)216 		void set(String name, Object value) {
217 			if (name.startsWith("$")) { //$NON-NLS-1$
218 				setRegisterValue(name, value);
219 			} else {
220 				fLocalVariables.put(name, value);
221 			}
222 		}
223 
get(String name)224 		Object get(String name) {
225 			if (name.startsWith("$")) { //$NON-NLS-1$
226 				return getRegisterValue(name);
227 			} else {
228 				return fLocalVariables.get(name);
229 			}
230 		}
231 	}
232 
setRegisterValue(String name, Object value)233 	void setRegisterValue(String name, Object value) {
234 		Register reg = fRegisters.get(getRegisterPartOfName(name));
235 		if (reg == null) {
236 			return;
237 		}
238 		String bitFieldName = getBitFieldPartOfName(name);
239 		if (bitFieldName != null) {
240 			BitField bitField = reg.fBitFields.get(bitFieldName);
241 			if (bitField == null) {
242 				return;
243 			}
244 			Integer intValue = null;
245 			if (value instanceof Integer) {
246 				intValue = (Integer)value;
247 			} else if (value instanceof String) {
248 				intValue = bitField.fMnemonics.get(value);
249 			}
250 			if (intValue != null) {
251 				int bitFieldMask = 2^(bitField.fBitCount - 1);
252 				int registerMask = ~(bitFieldMask << bitField.fBitOffset);
253 				int bitFieldValue = intValue.intValue() & bitFieldMask;
254 				reg.fValue = (reg.fValue & registerMask) | (bitFieldValue << bitField.fBitOffset);
255 			}
256 		} else if (value instanceof Integer) {
257 			reg.fValue = ((Integer)value).intValue();
258 		}
259 	}
260 
getRegisterValue(String name)261 	Object getRegisterValue(String name) {
262 		Register reg = fRegisters.get(getRegisterPartOfName(name));
263 		if (reg == null) {
264 			return null;
265 		}
266 		String bitFieldName = getBitFieldPartOfName(name);
267 		if (bitFieldName != null) {
268 			BitField bitField = reg.fBitFields.get(bitFieldName);
269 			if (bitField == null) {
270 				return null;
271 			}
272 			int bitFieldMask = 2^(bitField.fBitCount - 1);
273 			int registerMask = bitFieldMask << bitField.fBitOffset;
274 			return Integer.valueOf( (reg.fValue & registerMask) >> bitField.fBitOffset );
275 		} else {
276 			return Integer.valueOf(reg.fValue);
277 		}
278 	}
279 
280 	/**
281 	 * Breakpoints are stored per each each line of code.  The boolean indicates
282 	 * whether the whole VM should suspend or just the triggering thread.
283 	 */
284 	final Map<Integer, Boolean> fBreakpoints = new HashMap<>();
285 
286 	/**
287 	 * The suspend flag is true if the VM should suspend running the program and
288 	 * just listen for debug commands.
289 	 */
290 	String fSuspendVM;
291 
292 	/** Flag indicating whether the debugger is performing a step. */
293 	boolean fStepVM = false;
294 
295 	/** Flag indicating whether the debugger is performing a step return */
296 	boolean fStepReturnVM = false;
297 
298 	int fSteppingThread = 0;
299 
300 	/** Name of the pda program being debugged */
301 	final String fFilename;
302 
303 	/** The command line argument to start a debug session. */
304 	final boolean fDebug;
305 
306 	/** The port to listen for debug commands on */
307 	final int fCommandPort;
308 
309 	/**
310 	 * Command socket for receiving debug commands and sending command responses
311 	 */
312 	Socket fCommandSocket;
313 
314 	/** Command socket reader */
315 	BufferedReader fCommandReceiveStream;
316 
317 	/** Command socket write stream. */
318 	OutputStream fCommandResponseStream;
319 
320 	/** The port to send debug events to */
321 	final int fEventPort;
322 
323 	/** Event socket */
324 	Socket fEventSocket;
325 
326 	/** Event socket and write stream. */
327 	OutputStream fEventStream;
328 
329 	/** The eventstops table holds which events cause suspends and which do not. */
330 	final Map<String, Boolean> fEventStops = new HashMap<>();
331 	{
332 		fEventStops.put("unimpinstr", Boolean.FALSE); //$NON-NLS-1$
333 		fEventStops.put("nosuchlabel", Boolean.FALSE); //$NON-NLS-1$
334 	}
335 
336 	/**
337 	 * The watchpoints table holds watchpoint information.
338 	 * <p/>
339 	 * variablename_stackframedepth => N
340 	 * <ul>
341 	 * <li>N = 0 is no watch</li>
342 	 * <li>N = 1 is read watch</li>
343 	 * <li>N = 2 is write watch</li>
344 	 * <li>N = 3 is both, etc.</li>
345 	 */
346 	final Map<String, Integer> fWatchpoints = new HashMap<>();
347 
main(String[] args)348 	public static void main(String[] args) {
349 		String programFile = args.length >= 1 ? args[0] : null;
350 		if (programFile == null) {
351 			System.err.println("Error: No program specified"); //$NON-NLS-1$
352 			return;
353 		}
354 
355 		String debugFlag = args.length >= 2 ? args[1] : ""; //$NON-NLS-1$
356 		boolean debug = "-debug".equals(debugFlag); //$NON-NLS-1$
357 		int commandPort = 0;
358 		int eventPort = 0;
359 
360 		if (debug) {
361 			String commandPortStr = args.length >= 3 ? args[2] : ""; //$NON-NLS-1$
362 			try {
363 				commandPort = Integer.parseInt(commandPortStr);
364 			} catch (NumberFormatException e) {
365 				System.err.println("Error: Invalid command port"); //$NON-NLS-1$
366 				return;
367 			}
368 
369 			String eventPortStr = args.length >= 4 ? args[3] : ""; //$NON-NLS-1$
370 			try {
371 				eventPort = Integer.parseInt(eventPortStr);
372 			} catch (NumberFormatException e) {
373 				System.err.println("Error: Invalid event port"); //$NON-NLS-1$
374 				return;
375 			}
376 		}
377 
378 		PDAVirtualMachine pdaVM = null;
379 		try {
380 			pdaVM = new PDAVirtualMachine(programFile, debug, commandPort, eventPort);
381 			pdaVM.startDebugger();
382 		} catch (IOException e) {
383 			System.err.println("Error: " + e); //$NON-NLS-1$
384 			return;
385 		}
386 		pdaVM.run();
387 	}
388 
PDAVirtualMachine(String inputFile, boolean debug, int commandPort, int eventPort)389 	PDAVirtualMachine(String inputFile, boolean debug, int commandPort, int eventPort) throws IOException {
390 		fFilename = inputFile;
391 
392 		// Load all the code into memory
393 		StringWriter stringWriter = new StringWriter();
394 		List<String> code = new LinkedList<>();
395 		try (FileReader fileReader = new FileReader(inputFile)) {
396 			int c = fileReader.read();
397 			while (c != -1) {
398 				if (c == '\n') {
399 					code.add(stringWriter.toString().trim());
400 					stringWriter = new StringWriter();
401 				} else {
402 					stringWriter.write(c);
403 				}
404 				c = fileReader.read();
405 			}
406 		}
407 
408 		code.add(stringWriter.toString().trim());
409 		fCode = code.toArray(new String[code.size()]);
410 
411 		fLabels = mapLabels(fCode);
412 
413 		fDebug = debug;
414 		fCommandPort = commandPort;
415 		fEventPort = eventPort;
416 	}
417 
418 	/**
419 	 * Initializes the labels map
420 	 */
mapLabels(String[] code)421 	Map<String, Integer> mapLabels(String[] code) {
422 		Map<String, Integer> labels = new HashMap<>();
423 		for (int i = 0; i < code.length; i++) {
424 			if (code[i].length() != 0 && code[i].charAt(0) == ':') {
425 				labels.put(code[i].substring(1), Integer.valueOf(i));
426 			}
427 		}
428 		return labels;
429 	}
430 
sendCommandResponse(String response)431 	void sendCommandResponse(String response) {
432 		try {
433 			fCommandResponseStream.write(response.getBytes());
434 			fCommandResponseStream.flush();
435 		} catch (IOException e) {
436 		}
437 	}
438 
sendDebugEvent(String event, boolean error)439 	void sendDebugEvent(String event, boolean error) {
440 		if (fDebug) {
441 			try {
442 				fEventStream.write(event.getBytes());
443 				fEventStream.write('\n');
444 				fEventStream.flush();
445 			} catch (IOException e) {
446 				System.err.println("Error: " + e); //$NON-NLS-1$
447 				System.exit(1);
448 			}
449 		} else if (error) {
450 			System.err.println("Error: " + event); //$NON-NLS-1$
451 		}
452 	}
453 
startDebugger()454 	void startDebugger() throws IOException {
455 		if (fDebug) {
456 			System.out.println("-debug " + fCommandPort + " " + fEventPort); //$NON-NLS-1$ //$NON-NLS-2$
457 		}
458 
459 		try (ServerSocket commandServerSocket = new ServerSocket(fCommandPort)) {
460 			fCommandSocket = commandServerSocket.accept();
461 			fCommandReceiveStream = new BufferedReader(new InputStreamReader(fCommandSocket.getInputStream()));
462 			fCommandResponseStream = new PrintStream(fCommandSocket.getOutputStream());
463 		}
464 
465 		try (ServerSocket eventServerSocket = new ServerSocket(fEventPort)) {
466 			fEventSocket = eventServerSocket.accept();
467 			fEventStream = new PrintStream(fEventSocket.getOutputStream());
468 		}
469 
470 		System.out.println("debug connection accepted"); //$NON-NLS-1$
471 
472 		fSuspendVM = "client"; //$NON-NLS-1$
473 	}
474 
run()475 	void run() {
476 		int id = fNextThreadId++;
477 		sendDebugEvent("vmstarted", false); //$NON-NLS-1$
478 		fThreads.put(Integer.valueOf(id), new PDAThread(id, "main", 0)); //$NON-NLS-1$
479 		if (fDebug) {
480 			sendDebugEvent("started " + id, false); //$NON-NLS-1$
481 		}
482 
483 		boolean allThreadsSuspended = false;
484 		while (!fThreads.isEmpty()) {
485 			checkForBreakpoint();
486 
487 			if (fSuspendVM != null) {
488 				debugUI();
489 			} else {
490 				yieldToDebug(allThreadsSuspended);
491 				if (fSuspendVM != null) {
492 					// Received a command to suspend VM, skip executing threads.
493 					continue;
494 				}
495 			}
496 
497 			PDAThread[] threadsCopy = fThreads.values().toArray(new PDAThread[fThreads.size()]);
498 			allThreadsSuspended = true;
499 			for (int i = 0; i < threadsCopy.length; i++) {
500 				PDAThread thread = threadsCopy[i];
501 				if (thread.fSuspend == null) {
502 					allThreadsSuspended = false;
503 
504 					String instruction = thread.fThreadCode[thread.fCurrentFrame.fPC];
505 					thread.fCurrentFrame.fPC++;
506 					doOneInstruction(thread, instruction);
507 					if (thread.fCurrentFrame.fPC >= thread.fThreadCode.length) {
508 						// Thread reached end of code, exit from the thread.
509 						thread.fRun = false;
510 					} else if (thread.fStepReturn) {
511 						// If this thread is in a step-return operation, check
512 						// if we've returned from a call.
513 						instruction = thread.fThreadCode[thread.fCurrentFrame.fPC];
514 						if ("return".equals(instruction)) { //$NON-NLS-1$
515 							// Note: this will only be triggered if the current
516 							// thread also has the fStepReturn flag set.
517 							if (fStepReturnVM) {
518 								fSuspendVM = thread.fID + " step"; //$NON-NLS-1$
519 							} else {
520 								thread.fSuspend = "step"; //$NON-NLS-1$
521 							}
522 						}
523 					}
524 					if (!thread.fRun) {
525 						sendDebugEvent("exited " + thread.fID, false); //$NON-NLS-1$
526 						fThreads.remove(Integer.valueOf(thread.fID));
527 					} else if (thread.fSuspend != null) {
528 						sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); //$NON-NLS-1$ //$NON-NLS-2$
529 						thread.fStep = thread.fStepReturn = thread.fPerformingEval = false;
530 					}
531 				}
532 			}
533 
534 			// Force thread context switch to avoid starving out other
535 			// processes in the system.
536 			Thread.yield();
537 		}
538 
539 		sendDebugEvent("vmterminated", false); //$NON-NLS-1$
540 		if (fDebug) {
541 			try {
542 				fCommandReceiveStream.close();
543 				fCommandResponseStream.close();
544 				fCommandSocket.close();
545 				fEventStream.close();
546 				fEventSocket.close();
547 			} catch (IOException e) {
548 				System.out.println("Error: " + e); //$NON-NLS-1$
549 			}
550 		}
551 
552 	}
553 
doOneInstruction(PDAThread thread, String instr)554 	void doOneInstruction(PDAThread thread, String instr) {
555 		StringTokenizer tokenizer = new StringTokenizer(instr);
556 		String op = tokenizer.nextToken();
557 		List<String> tokens = new LinkedList<>();
558 		while (tokenizer.hasMoreTokens()) {
559 			tokens.add(tokenizer.nextToken());
560 		}
561 		Args args = new Args( tokens.toArray(new String[tokens.size()]) );
562 
563 		boolean opValid = true;
564 		if (op.equals("add")) { //$NON-NLS-1$
565 			iAdd(thread, args);
566 		} else if (op.equals("branch_not_zero")) { //$NON-NLS-1$
567 			iBranchNotZero(thread, args);
568 		} else if (op.equals("call")) { //$NON-NLS-1$
569 			iCall(thread, args);
570 		} else if (op.equals("dec")) { //$NON-NLS-1$
571 			iDec(thread, args);
572 		} else if (op.equals("def")) { //$NON-NLS-1$
573 			iDef(thread, args);
574 		} else if (op.equals("dup")) { //$NON-NLS-1$
575 			iDup(thread, args);
576 		} else if (op.equals("exec")) { //$NON-NLS-1$
577 			iExec(thread, args);
578 		} else if (op.equals("halt")) { //$NON-NLS-1$
579 			iHalt(thread, args);
580 		} else if (op.equals("output")) { //$NON-NLS-1$
581 			iOutput(thread, args);
582 		} else if (op.equals("pop")) { //$NON-NLS-1$
583 			iPop(thread, args);
584 		} else if (op.equals("push")) { //$NON-NLS-1$
585 			iPush(thread, args);
586 		} else if (op.equals("return")) { //$NON-NLS-1$
587 			iReturn(thread, args);
588 		} else if (op.equals("var")) { //$NON-NLS-1$
589 			iVar(thread, args);
590 		} else if (op.equals("xyzzy")) { //$NON-NLS-1$
591 			iInternalEndEval(thread, args);
592 		} else if (op.startsWith(":")) {} // label //$NON-NLS-1$
593 		else if (op.startsWith("#")) {} // comment //$NON-NLS-1$
594 		else {
595 			opValid = false;
596 		}
597 
598 		if (!opValid) {
599 			sendDebugEvent("unimplemented instruction " + op, true); //$NON-NLS-1$
600 			if ( fEventStops.get("unimpinstr").booleanValue() ) { //$NON-NLS-1$
601 				fSuspendVM = thread.fID + " event unimpinstr"; //$NON-NLS-1$
602 				thread.fCurrentFrame.fPC--;
603 			}
604 		} else if (thread.fStep) {
605 			if (fStepVM) {
606 				fSuspendVM = thread.fID + " step"; //$NON-NLS-1$
607 				fStepVM = false;
608 			} else {
609 				thread.fSuspend = "step"; //$NON-NLS-1$
610 			}
611 			thread.fStep = false;
612 		}
613 	}
614 
checkForBreakpoint()615 	void checkForBreakpoint() {
616 		if (fDebug) {
617 			for (Iterator<PDAThread> itr = fThreads.values().iterator(); itr.hasNext();) {
618 				PDAThread thread = itr.next();
619 				Integer pc = Integer.valueOf(thread.fCurrentFrame.fPC);
620 				// Suspend for breakpoint if:
621 				// - the VM is not yet set to suspend, for e.g. as a result of step end,
622 				// - the thread is not yet suspended and is not performing an evaluation
623 				// - the breakpoints table contains a breakpoint for the given line.
624 				if (fSuspendVM == null &&
625 					thread.fSuspend == null && !thread.fPerformingEval &&
626 					fBreakpoints.containsKey(pc))
627 				{
628 					if ( fBreakpoints.get(pc).booleanValue() ) {
629 						fSuspendVM = thread.fID + " breakpoint " + pc; //$NON-NLS-1$
630 					} else {
631 						thread.fSuspend = "breakpoint " + pc; //$NON-NLS-1$
632 						thread.fStep = thread.fStepReturn = false;
633 						sendDebugEvent("suspended " + thread.fID + " " + thread.fSuspend, false); //$NON-NLS-1$ //$NON-NLS-2$
634 					}
635 				}
636 			}
637 		}
638 	}
639 
640 	/**
641 	 * After each instruction, we check the debug command channel for control input. If
642 	 * there are commands, process them.
643 	 */
yieldToDebug(boolean allThreadsSuspended)644 	void yieldToDebug(boolean allThreadsSuspended) {
645 		if (fDebug) {
646 			String line = ""; //$NON-NLS-1$
647 			try {
648 				if (allThreadsSuspended || fCommandReceiveStream.ready()) {
649 					line = fCommandReceiveStream.readLine();
650 					processDebugCommand(line);
651 				}
652 			} catch (IOException e) {
653 				System.err.println("Error: " + e); //$NON-NLS-1$
654 				System.exit(1);
655 			}
656 		}
657 	}
658 
659 	/**
660 	 *  Service the debugger commands while the VM is suspended
661 	 */
debugUI()662 	void debugUI() {
663 		if (!fStarted) {
664 			sendDebugEvent("vmsuspended " + fSuspendVM, false); //$NON-NLS-1$
665 		} else {
666 			fStarted = false;
667 		}
668 
669 		// Clear all stepping flags.  In case the VM suspended while
670 		// a step operation was being performed for the VM or some thread.
671 		fStepVM = fStepReturnVM = false;
672 		for (Iterator<PDAThread> itr = fThreads.values().iterator(); itr.hasNext();) {
673 			PDAThread thread = itr.next();
674 			thread.fSuspend = null;
675 			thread.fStep = thread.fStepReturn = thread.fPerformingEval = false;
676 		}
677 
678 		while (fSuspendVM != null) {
679 			String line = ""; //$NON-NLS-1$
680 			try {
681 				line = fCommandReceiveStream.readLine();
682 			} catch (IOException e) {
683 				System.err.println("Error: " + e); //$NON-NLS-1$
684 				System.exit(1);
685 				return;
686 			}
687 			processDebugCommand(line);
688 		}
689 
690 		if (fStepVM || fStepReturnVM) {
691 			sendDebugEvent("vmresumed step", false); //$NON-NLS-1$
692 		} else {
693 			sendDebugEvent("vmresumed client", false); //$NON-NLS-1$
694 		}
695 	}
696 
processDebugCommand(String line)697 	void processDebugCommand(String line) {
698 		StringTokenizer tokenizer = new StringTokenizer(line.trim());
699 		if (line.length() == 0) {
700 			return;
701 		}
702 
703 		String command = tokenizer.nextToken();
704 		List<String> tokens = new LinkedList<>();
705 		while (tokenizer.hasMoreTokens()) {
706 			tokens.add(tokenizer.nextToken());
707 		}
708 		Args args = new Args( tokens.toArray(new String[tokens.size()]));
709 
710 		if ("children".equals(command)) { //$NON-NLS-1$
711 			debugChildren(args);
712 		} else if ("clear".equals(command)) { //$NON-NLS-1$
713 			debugClearBreakpoint(args);
714 		} else if ("data".equals(command)) { //$NON-NLS-1$
715 			debugData(args);
716 		} else if ("drop".equals(command)) { //$NON-NLS-1$
717 			debugDropFrame(args);
718 		} else if ("eval".equals(command)) { //$NON-NLS-1$
719 			debugEval(args);
720 		} else if ("eventstop".equals(command)) { //$NON-NLS-1$
721 			debugEventStop(args);
722 		} else if ("frame".equals(command)) { //$NON-NLS-1$
723 			debugFrame(args);
724 		} else if ("groups".equals(command)) { //$NON-NLS-1$
725 			debugGroups(args);
726 		} else if ("popdata".equals(command)) { //$NON-NLS-1$
727 			debugPopData(args);
728 		} else if ("pushdata".equals(command)) { //$NON-NLS-1$
729 			debugPushData(args);
730 		} else if ("registers".equals(command)) { //$NON-NLS-1$
731 			debugRegisters(args);
732 		} else if ("restart".equals(command)) { //$NON-NLS-1$
733 			debugRestart(args);
734 		} else if ("resume".equals(command)) { //$NON-NLS-1$
735 			debugResume(args);
736 		} else if ("set".equals(command)) { //$NON-NLS-1$
737 			debugSetBreakpoint(args);
738 		} else if ("setdata".equals(command)) { //$NON-NLS-1$
739 			debugSetData(args);
740 		} else if ("setvar".equals(command)) { //$NON-NLS-1$
741 			debugSetVariable(args);
742 		} else if ("stack".equals(command)) { //$NON-NLS-1$
743 			debugStack(args);
744 		} else if ("stackdepth".equals(command)) { //$NON-NLS-1$
745 			debugStackDepth(args);
746 		} else if ("state".equals(command)) { //$NON-NLS-1$
747 			debugState(args);
748 		} else if ("step".equals(command)) { //$NON-NLS-1$
749 			debugStep(args);
750 		} else if ("stepreturn".equals(command)) { //$NON-NLS-1$
751 			debugStepReturn(args);
752 		} else if ("suspend".equals(command)) { //$NON-NLS-1$
753 			debugSuspend(args);
754 		} else if ("terminate".equals(command)) { //$NON-NLS-1$
755 			debugTerminate();
756 		} else if ("threads".equals(command)) { //$NON-NLS-1$
757 			debugThreads();
758 		} else if ("var".equals(command)) { //$NON-NLS-1$
759 			debugVar(args);
760 		} else if ("vmresume".equals(command)) { //$NON-NLS-1$
761 			debugVMResume();
762 		} else if ("vmsuspend".equals(command)) { //$NON-NLS-1$
763 			debugVMSuspend();
764 		} else if ("watch".equals(command)) { //$NON-NLS-1$
765 			debugWatch(args);
766 		} else {
767 			sendCommandResponse("error: invalid command\n"); //$NON-NLS-1$
768 		}
769 	}
770 
debugChildren(Args args)771 	void debugChildren(Args args) {
772 		PDAThread thread = args.getThreadArg();
773 		if (thread == null) {
774 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
775 			return;
776 		}
777 
778 		int sfnumber = args.getNextIntArg();
779 		String var = args.getNextStringArg();
780 
781 		Frame frame = sfnumber >= thread.fFrames.size()
782 			? thread.fCurrentFrame : (Frame)thread.fFrames.get(sfnumber);
783 
784 		String varDot = var + "."; //$NON-NLS-1$
785 		List<String> children = new ArrayList<>();
786 		for (Iterator<String> itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) {
787 			String localVar = itr.next();
788 			if (localVar.startsWith(varDot) && localVar.indexOf('.', varDot.length() + 1) == -1) {
789 				children.add(localVar);
790 			}
791 		}
792 
793 		StringBuilder result = new StringBuilder();
794 		for (Iterator<String> itr = children.iterator(); itr.hasNext();) {
795 			result.append(itr.next());
796 			result.append('|');
797 		}
798 		result.append('\n');
799 
800 		sendCommandResponse(result.toString());
801 	}
802 
debugClearBreakpoint(Args args)803 	void debugClearBreakpoint(Args args) {
804 		int line = args.getNextIntArg();
805 
806 		fBreakpoints.remove( Integer.valueOf(line) );
807 		sendCommandResponse("ok\n"); //$NON-NLS-1$
808 	}
809 
810 	private static Pattern fPackPattern = Pattern.compile("%([a-fA-F0-9][a-fA-F0-9])"); //$NON-NLS-1$
811 
debugData(Args args)812 	void debugData(Args args) {
813 		PDAThread thread = args.getThreadArg();
814 		if (thread == null) {
815 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
816 			return;
817 		}
818 
819 		StringBuilder result = new StringBuilder();
820 		for (Iterator<?> itr = thread.fStack.iterator(); itr.hasNext();) {
821 			result.append(itr.next());
822 			result.append('|');
823 		}
824 		result.append('\n');
825 		sendCommandResponse(result.toString());
826 	}
827 
debugDropFrame(Args args)828 	void debugDropFrame(Args args) {
829 		PDAThread thread = args.getThreadArg();
830 		if (thread == null) {
831 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
832 			return;
833 		}
834 
835 		if (!thread.fFrames.isEmpty()) {
836 			thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1);
837 		}
838 		thread.fCurrentFrame.fPC--;
839 		sendCommandResponse("ok\n"); //$NON-NLS-1$
840 		if (fSuspendVM != null) {
841 			sendDebugEvent("vmresumed drop", false); //$NON-NLS-1$
842 			sendDebugEvent("vmsuspended " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$
843 		} else {
844 			sendDebugEvent("resumed " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$
845 			sendDebugEvent("suspended " + thread.fID + " drop", false); //$NON-NLS-1$ //$NON-NLS-2$
846 		}
847 	}
848 
debugEval(Args args)849 	void debugEval(Args args) {
850 		if (fSuspendVM != null) {
851 			sendCommandResponse("error: cannot evaluate while vm is suspended\n");         //$NON-NLS-1$
852 			return;
853 		}
854 
855 		PDAThread thread = args.getThreadArg();
856 		if (thread == null) {
857 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
858 			return;
859 		}
860 
861 		if (thread.fSuspend == null) {
862 			sendCommandResponse("error: thread running\n"); //$NON-NLS-1$
863 			return;
864 		}
865 
866 		StringTokenizer tokenizer = new StringTokenizer(args.getNextStringArg(), "|"); //$NON-NLS-1$
867 		tokenizer.countTokens();
868 
869 		int numEvalLines = tokenizer.countTokens();
870 		thread.fThreadCode = new String[fCode.length + numEvalLines + 1];
871 		System.arraycopy(fCode, 0, thread.fThreadCode, 0, fCode.length);
872 		for (int i = 0; i < numEvalLines; i++) {
873 			String line = tokenizer.nextToken();
874 			StringBuilder lineBuf = new StringBuilder(line.length());
875 			Matcher matcher = fPackPattern.matcher(line);
876 			int lastMatchEnd = 0;
877 			while (matcher.find()) {
878 				lineBuf.append(line.substring(lastMatchEnd, matcher.start()));
879 				String charCode = line.substring(matcher.start() + 1, matcher.start() + 3);
880 				try {
881 					lineBuf.append((char) Integer.parseInt(charCode, 16));
882 				} catch (NumberFormatException e) {
883 				}
884 				lastMatchEnd = matcher.end();
885 			}
886 			if (lastMatchEnd < line.length()) {
887 				lineBuf.append(line.substring(lastMatchEnd));
888 			}
889 			thread.fThreadCode[fCode.length + i] = lineBuf.toString();
890 		}
891 		thread.fThreadCode[fCode.length + numEvalLines] = "xyzzy"; //$NON-NLS-1$
892 		thread.fThreadLabels = mapLabels(fCode);
893 
894 		thread.fSavedPC = thread.fCurrentFrame.fPC;
895 		thread.fCurrentFrame.fPC = fCode.length;
896 		thread.fPerformingEval = true;
897 
898 		thread.fSuspend = null;
899 
900 		sendCommandResponse("ok\n"); //$NON-NLS-1$
901 
902 		sendDebugEvent("resumed " + thread.fID + " eval", false); //$NON-NLS-1$ //$NON-NLS-2$
903 	}
904 
debugEventStop(Args args)905 	void debugEventStop(Args args) {
906 		String event = args.getNextStringArg();
907 		int stop = args.getNextIntArg();
908 		fEventStops.put(event, Boolean.valueOf(stop > 0));
909 		sendCommandResponse("ok\n"); //$NON-NLS-1$
910 	}
911 
debugTerminate()912 	void debugTerminate() {
913 		sendCommandResponse("ok\n"); //$NON-NLS-1$
914 		sendDebugEvent("vmterminated", false); //$NON-NLS-1$
915 		System.exit(0);
916 	}
917 
debugFrame(Args args)918 	void debugFrame(Args args) {
919 		PDAThread thread = args.getThreadArg();
920 		if (thread == null) {
921 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
922 			return;
923 		}
924 
925 		int sfnumber = args.getNextIntArg();
926 		Frame frame = null;
927 		if (sfnumber >= thread.fFrames.size()) {
928 			frame = thread.fCurrentFrame;
929 		} else {
930 			frame = thread.fFrames.get(sfnumber);
931 		}
932 		sendCommandResponse(printFrame(frame) + "\n"); //$NON-NLS-1$
933 	}
934 
935 	/**
936 	 * @param args
937 	 */
debugGroups(Args args)938 	void debugGroups(Args args) {
939 		TreeSet<String> groups = new TreeSet<>();
940 		for (Iterator<Register> itr = fRegisters.values().iterator(); itr.hasNext();) {
941 			Register reg = itr.next();
942 			groups.add(reg.fGroup);
943 		}
944 		StringBuilder response = new StringBuilder();
945 		for (Iterator<String> itr = groups.iterator(); itr.hasNext();) {
946 			response.append(itr.next());
947 			response.append('|');
948 		}
949 		response.append('\n');
950 		sendCommandResponse(response.toString());
951 	}
952 
debugPopData(Args args)953 	void debugPopData(Args args) {
954 		PDAThread thread = args.getThreadArg();
955 		if (thread == null) {
956 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
957 			return;
958 		}
959 
960 		thread.fStack.pop();
961 		sendCommandResponse("ok\n"); //$NON-NLS-1$
962 	}
963 
debugPushData(Args args)964 	void debugPushData(Args args) {
965 		PDAThread thread = args.getThreadArg();
966 		if (thread == null) {
967 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
968 			return;
969 		}
970 
971 		Object val = args.getNextIntOrStringArg();
972 		thread.fStack.push(val);
973 		sendCommandResponse("ok\n"); //$NON-NLS-1$
974 	}
975 
debugRegisters(Args args)976 	void debugRegisters(Args args) {
977 		String group = args.getNextStringArg();
978 
979 		StringBuilder response = new StringBuilder();
980 		for (Iterator<Register> itr = fRegisters.values().iterator(); itr.hasNext();) {
981 			Register reg = itr.next();
982 			if (group.equals(reg.fGroup)) {
983 				response.append(reg.fName);
984 				response.append(' ');
985 				response.append(reg.fIsWriteable);
986 				for (Iterator<BitField> itr2 = reg.fBitFields.values().iterator(); itr2.hasNext();) {
987 					BitField bitField = itr2.next();
988 					response.append('|');
989 					response.append(bitField.fName);
990 					response.append(' ');
991 					response.append(bitField.fBitOffset);
992 					response.append(' ');
993 					response.append(bitField.fBitCount);
994 					response.append(' ');
995 					for (Iterator<Entry<String, Integer>> itr3 = bitField.fMnemonics.entrySet().iterator(); itr3.hasNext();) {
996 						Entry<String, Integer> mnemonicEntry = itr3.next();
997 						response.append(mnemonicEntry.getKey());
998 						response.append(' ');
999 						response.append(mnemonicEntry.getValue());
1000 						response.append(' ');
1001 					}
1002 				}
1003 
1004 				response.append('#');
1005 			}
1006 		}
1007 		response.append('\n');
1008 		sendCommandResponse(response.toString());
1009 	}
1010 
1011 	/**
1012 	 * @param args
1013 	 */
debugRestart(Args args)1014 	void debugRestart(Args args) {
1015 		fSuspendVM = "restart"; //$NON-NLS-1$
1016 
1017 		for (Iterator<Integer> itr = fThreads.keySet().iterator(); itr.hasNext();) {
1018 			Integer id = itr.next();
1019 			sendDebugEvent("exited " + id, false);             //$NON-NLS-1$
1020 		}
1021 		fThreads.clear();
1022 
1023 		int id = fNextThreadId++;
1024 		fThreads.put(Integer.valueOf(id), new PDAThread(id, "main", 0)); //$NON-NLS-1$
1025 		sendDebugEvent("started " + id, false);             //$NON-NLS-1$
1026 
1027 		fRegisters.clear();
1028 
1029 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1030 	}
1031 
debugResume(Args args)1032 	void debugResume(Args args) {
1033 		PDAThread thread = args.getThreadArg();
1034 		if (thread == null) {
1035 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1036 			return;
1037 		}
1038 		if (fSuspendVM != null) {
1039 			sendCommandResponse("error: cannot resume thread when vm is suspended\n"); //$NON-NLS-1$
1040 			return;
1041 		}
1042 		if (thread.fSuspend == null) {
1043 			sendCommandResponse("error: thread already running\n"); //$NON-NLS-1$
1044 			return;
1045 		}
1046 
1047 		thread.fSuspend = null;
1048 		sendDebugEvent("resumed " + thread.fID + " client", false); //$NON-NLS-1$ //$NON-NLS-2$
1049 
1050 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1051 	}
1052 
debugSetBreakpoint(Args args)1053 	void debugSetBreakpoint(Args args) {
1054 		int line = args.getNextIntArg();
1055 		int stopVM = args.getNextIntArg();
1056 
1057 		fBreakpoints.put(Integer.valueOf(line), Boolean.valueOf(stopVM != 0));
1058 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1059 	}
1060 
debugSetData(Args args)1061 	void debugSetData(Args args) {
1062 		PDAThread thread = args.getThreadArg();
1063 		if (thread == null) {
1064 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1065 			return;
1066 		}
1067 
1068 		int offset = args.getNextIntArg();
1069 		Object val = args.getNextIntOrStringArg();
1070 
1071 		if (offset < thread.fStack.size()) {
1072 			thread.fStack.set(offset, val);
1073 		} else {
1074 			thread.fStack.add(0, val);
1075 		}
1076 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1077 	}
1078 
debugSetVariable(Args args)1079 	void debugSetVariable(Args args) {
1080 		PDAThread thread = args.getThreadArg();
1081 		if (thread == null) {
1082 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1083 			return;
1084 		}
1085 
1086 		int sfnumber = args.getNextIntArg();
1087 		String var = args.getNextStringArg();
1088 		Object val = args.getNextIntOrStringArg();
1089 		while (args.hasNextArg()) {
1090 			val = val + " " + args.getNextStringArg(); //$NON-NLS-1$
1091 		}
1092 
1093 		if (sfnumber >= thread.fFrames.size()) {
1094 			thread.fCurrentFrame.set(var, val);
1095 		} else {
1096 			thread.fFrames.get(sfnumber).set(var, val);
1097 		}
1098 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1099 	}
1100 
debugStack(Args args)1101 	void debugStack(Args args) {
1102 		PDAThread thread = args.getThreadArg();
1103 		if (thread == null) {
1104 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1105 			return;
1106 		}
1107 
1108 		StringBuilder result = new StringBuilder();
1109 
1110 		for (Iterator<Frame> itr = thread.fFrames.iterator(); itr.hasNext();) {
1111 			Frame frame = itr.next();
1112 			result.append(printFrame(frame));
1113 			result.append('#');
1114 		}
1115 		result.append(printFrame(thread.fCurrentFrame));
1116 		result.append('\n');
1117 		sendCommandResponse(result.toString());
1118 	}
1119 
debugStackDepth(Args args)1120 	void debugStackDepth(Args args) {
1121 		PDAThread thread = args.getThreadArg();
1122 		if (thread == null) {
1123 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1124 			return;
1125 		}
1126 		sendCommandResponse( (thread.fFrames.size() + 1) + "\n" ); //$NON-NLS-1$
1127 	}
1128 
1129 
1130 	/**
1131 	 * The stack frame output is: frame # frame # frame ... where each frame is:
1132 	 * filename | line number | function name | var | var | var | var ...
1133 	 */
printFrame(Frame frame)1134 	private String printFrame(Frame frame) {
1135 		StringBuilder buf = new StringBuilder();
1136 		buf.append(fFilename);
1137 		buf.append('|');
1138 		buf.append(frame.fPC);
1139 		buf.append('|');
1140 		buf.append(frame.fFunction);
1141 		for (Iterator<String> itr = frame.fLocalVariables.keySet().iterator(); itr.hasNext();) {
1142 			String var = itr.next();
1143 			if (var.indexOf('.') == -1) {
1144 				buf.append('|');
1145 				buf.append(var);
1146 			}
1147 		}
1148 		return buf.toString();
1149 	}
1150 
debugState(Args args)1151 	void debugState(Args args) {
1152 		PDAThread thread = args.getThreadArg();
1153 		String response = null;
1154 		if (thread == null) {
1155 			response = fSuspendVM == null ? "running" : fSuspendVM; //$NON-NLS-1$
1156 		} else if (fSuspendVM != null) {
1157 			response = "vm"; //$NON-NLS-1$
1158 		} else {
1159 			response = thread.fSuspend == null ? "running" : thread.fSuspend; //$NON-NLS-1$
1160 		}
1161 		sendCommandResponse(response + "\n"); //$NON-NLS-1$
1162 	}
1163 
debugStep(Args args)1164 	void debugStep(Args args) {
1165 		PDAThread thread = args.getThreadArg();
1166 		if (thread == null) {
1167 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1168 			return;
1169 		}
1170 
1171 		// Set suspend to null to allow the debug loop to exit back to the
1172 		// instruction loop and thus run an instruction. However, we want to
1173 		// come back to the debug loop right away, so the step flag is set to
1174 		// true which will cause the suspend flag to get set to true when we
1175 		// get to the next instruction.
1176 		if (fSuspendVM != null) {
1177 			// All threads are suspended, so suspend all threads again when
1178 			// step completes.
1179 			fSuspendVM = null;
1180 			fStepVM = true;
1181 			// Also mark the thread that initiated the step to mark it as
1182 			// the triggering thread when suspending.
1183 			thread.fStep = true;
1184 		} else {
1185 			if (thread.fSuspend == null) {
1186 				sendCommandResponse("error: thread already running\n"); //$NON-NLS-1$
1187 				return;
1188 			}
1189 			thread.fSuspend = null;
1190 			thread.fStep = true;
1191 			sendDebugEvent("resumed " + thread.fID + " step", false); //$NON-NLS-1$ //$NON-NLS-2$
1192 		}
1193 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1194 	}
1195 
debugStepReturn(Args args)1196 	void debugStepReturn(Args args) {
1197 		PDAThread thread = args.getThreadArg();
1198 		if (thread == null) {
1199 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1200 			return;
1201 		}
1202 
1203 		if (fSuspendVM != null) {
1204 			fSuspendVM = null;
1205 			fStepReturnVM = true;
1206 			thread.fStepReturn = true;
1207 		} else {
1208 			if (thread.fSuspend == null) {
1209 				sendCommandResponse("error: thread running\n"); //$NON-NLS-1$
1210 				return;
1211 			}
1212 			thread.fSuspend = null;
1213 			thread.fStepReturn = true;
1214 			sendDebugEvent("resumed " + thread.fID + " step", false); //$NON-NLS-1$ //$NON-NLS-2$
1215 		}
1216 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1217 	}
1218 
debugSuspend(Args args)1219 	void debugSuspend(Args args) {
1220 		PDAThread thread = args.getThreadArg();
1221 		if (thread == null) {
1222 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1223 			return;
1224 		}
1225 		if (fSuspendVM != null) {
1226 			sendCommandResponse("error: vm already suspended\n"); //$NON-NLS-1$
1227 			return;
1228 		}
1229 		if (thread.fSuspend != null) {
1230 			sendCommandResponse("error: thread already suspended\n"); //$NON-NLS-1$
1231 			return;
1232 		}
1233 
1234 		thread.fSuspend = "client"; //$NON-NLS-1$
1235 		sendDebugEvent("suspended " + thread.fID + " client", false); //$NON-NLS-1$ //$NON-NLS-2$
1236 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1237 	}
1238 
debugThreads()1239 	void debugThreads() {
1240 		StringBuilder response = new StringBuilder();
1241 		for (Iterator<Integer> itr = fThreads.keySet().iterator(); itr.hasNext();) {
1242 			response.append(itr.next());
1243 			response.append(' ');
1244 		}
1245 		sendCommandResponse(response.toString().trim() + "\n"); //$NON-NLS-1$
1246 	}
1247 
debugVar(Args args)1248 	void debugVar(Args args) {
1249 		PDAThread thread = args.getThreadArg();
1250 		if (thread == null) {
1251 			sendCommandResponse("error: invalid thread\n"); //$NON-NLS-1$
1252 			return;
1253 		}
1254 
1255 		int sfnumber = args.getNextIntArg();
1256 		String var = args.getNextStringArg();
1257 
1258 		Frame frame = sfnumber >= thread.fFrames.size()
1259 			? thread.fCurrentFrame : (Frame)thread.fFrames.get(sfnumber);
1260 
1261 		Object val = frame.get(var);
1262 		if (val == null) {
1263 			sendCommandResponse("error: variable undefined\n"); //$NON-NLS-1$
1264 		} else {
1265 			sendCommandResponse(val + "\n"); //$NON-NLS-1$
1266 		}
1267 	}
1268 
debugVMResume()1269 	void debugVMResume() {
1270 		if (fSuspendVM == null) {
1271 			sendCommandResponse("error: vm already running\n"); //$NON-NLS-1$
1272 			return;
1273 		}
1274 
1275 		fSuspendVM = null;
1276 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1277 	}
1278 
debugVMSuspend()1279 	void debugVMSuspend() {
1280 		if (fSuspendVM != null) {
1281 			sendCommandResponse("error: vm already suspended\n"); //$NON-NLS-1$
1282 			return;
1283 		}
1284 
1285 		fSuspendVM = "client"; //$NON-NLS-1$
1286 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1287 	}
1288 
debugWatch(Args args)1289 	void debugWatch(Args args) {
1290 		String funcAndVar = args.getNextStringArg();
1291 		int flags = args.getNextIntArg();
1292 		fWatchpoints.put(funcAndVar, Integer.valueOf(flags));
1293 		sendCommandResponse("ok\n"); //$NON-NLS-1$
1294 	}
1295 
1296 	/**
1297 	 * @param thread
1298 	 * @param args
1299 	 */
iAdd(PDAThread thread, Args args)1300 	void iAdd(PDAThread thread, Args args) {
1301 		Object val1 = thread.fStack.pop();
1302 		Object val2 = thread.fStack.pop();
1303 		if (val1 instanceof Integer && val2 instanceof Integer) {
1304 			int intVal1 = ((Integer) val1).intValue();
1305 			int intVal2 = ((Integer) val2).intValue();
1306 			thread.fStack.push( Integer.valueOf(intVal1 + intVal2) );
1307 		} else {
1308 			thread.fStack.push( Integer.valueOf(-1) );
1309 		}
1310 	}
1311 
iBranchNotZero(PDAThread thread, Args args)1312 	void iBranchNotZero(PDAThread thread, Args args) {
1313 		Object val = thread.fStack.pop();
1314 		if (val instanceof Integer && ((Integer) val).intValue() != 0) {
1315 			String label = args.getNextStringArg();
1316 			if (thread.fThreadLabels.containsKey(label)) {
1317 				thread.fCurrentFrame.fPC = thread.fThreadLabels.get(label).intValue();
1318 			} else {
1319 				sendDebugEvent("no such label " + label, true); //$NON-NLS-1$
1320 				if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$
1321 					fSuspendVM = thread.fID + " event nosuchlabel"; //$NON-NLS-1$
1322 					thread.fStack.push(val);
1323 					thread.fCurrentFrame.fPC--;
1324 				}
1325 			}
1326 		}
1327 	}
1328 
iCall(PDAThread thread, Args args)1329 	void iCall(PDAThread thread, Args args) {
1330 		String label = args.getNextStringArg();
1331 		if (thread.fThreadLabels.containsKey(label)) {
1332 			thread.fFrames.add(thread.fCurrentFrame);
1333 			thread.fCurrentFrame = new Frame(label, thread.fThreadLabels.get(label).intValue());
1334 		} else {
1335 			sendDebugEvent("no such label " + label, true); //$NON-NLS-1$
1336 			if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$
1337 				fSuspendVM = thread.fID + " event nosuchlabel"; //$NON-NLS-1$
1338 				thread.fCurrentFrame.fPC--;
1339 			}
1340 		}
1341 	}
1342 
1343 	/**
1344 	 * @param thread
1345 	 * @param args
1346 	 */
iDec(PDAThread thread, Args args)1347 	void iDec(PDAThread thread, Args args) {
1348 		Object val = thread.fStack.pop();
1349 		if (val instanceof Integer) {
1350 			val = Integer.valueOf(((Integer) val).intValue() - 1);
1351 		}
1352 		thread.fStack.push(val);
1353 	}
1354 
1355 	/**
1356 	 * @param thread
1357 	 * @param args
1358 	 */
iDef(PDAThread thread, Args args)1359 	void iDef(PDAThread thread, Args args) {
1360 		String type = args.getNextStringArg();
1361 
1362 		String name = args.getNextStringArg();
1363 		String regName = getRegisterPartOfName(name);
1364 		String bitFieldName = getBitFieldPartOfName(name);
1365 
1366 		if ("register".equals(type)) { //$NON-NLS-1$
1367 			Register reg = new Register(regName);
1368 			reg.fGroup = args.getNextStringArg();
1369 			fRegisters.put(regName, reg);
1370 			reg.fIsWriteable = args.getNextBooleanArg();
1371 		} else if ("bitfield".equals(type)) { //$NON-NLS-1$
1372 			Register reg = fRegisters.get(regName);
1373 			if (reg == null) {
1374 				return;
1375 			}
1376 			BitField bitField = new BitField(bitFieldName);
1377 			bitField.fBitOffset = args.getNextIntArg();
1378 			bitField.fBitCount = args.getNextIntArg();
1379 			reg.fBitFields.put(bitFieldName, bitField);
1380 		} else if ("mnemonic".equals(type)) { //$NON-NLS-1$
1381 			Register reg = fRegisters.get(regName);
1382 			if (reg == null) {
1383 				return;
1384 			}
1385 			BitField bitField = reg.fBitFields.get(bitFieldName);
1386 			if (bitField == null) {
1387 				return;
1388 			}
1389 			bitField.fMnemonics.put(args.getNextStringArg(), Integer.valueOf(args.getNextIntArg()));
1390 		}
1391 		sendDebugEvent("registers", false); //$NON-NLS-1$
1392 	}
1393 
getRegisterPartOfName(String name)1394 	private String getRegisterPartOfName(String name) {
1395 		if (name.startsWith("$")) { //$NON-NLS-1$
1396 			int end = name.indexOf('.');
1397 			end = end != -1 ? end : name.length();
1398 			return name.substring(1, end);
1399 		}
1400 		return null;
1401 	}
1402 
getBitFieldPartOfName(String name)1403 	private String getBitFieldPartOfName(String name) {
1404 		int start = name.indexOf('.');
1405 		if (name.startsWith("$") && start != -1) { //$NON-NLS-1$
1406 			return name.substring(start + 1, name.length());
1407 		}
1408 		return null;
1409 	}
1410 
1411 	/**
1412 	 * @param thread
1413 	 * @param args
1414 	 */
iDup(PDAThread thread, Args args)1415 	void iDup(PDAThread thread, Args args) {
1416 		Object val = thread.fStack.pop();
1417 		thread.fStack.push(val);
1418 		thread.fStack.push(val);
1419 	}
1420 
iExec(PDAThread thread, Args args)1421 	void iExec(PDAThread thread, Args args) {
1422 		String label = args.getNextStringArg();
1423 		if (fLabels.containsKey(label)) {
1424 			int id = fNextThreadId++;
1425 			fThreads.put( Integer.valueOf(id), new PDAThread(id, label, fLabels.get(label).intValue()) );
1426 			sendDebugEvent("started " + id, false); //$NON-NLS-1$
1427 		} else {
1428 			sendDebugEvent("no such label " + label, true); //$NON-NLS-1$
1429 			if ( fEventStops.get("nosuchlabel").booleanValue() ) { //$NON-NLS-1$
1430 				thread.fSuspend = "event nosuchlabel"; //$NON-NLS-1$
1431 				thread.fCurrentFrame.fPC--;
1432 			}
1433 		}
1434 	}
1435 
1436 	/**
1437 	 * @param thread
1438 	 * @param args
1439 	 */
iHalt(PDAThread thread, Args args)1440 	void iHalt(PDAThread thread, Args args) {
1441 		thread.fRun = false;
1442 	}
1443 
1444 	/**
1445 	 * @param thread
1446 	 * @param args
1447 	 */
iOutput(PDAThread thread, Args args)1448 	void iOutput(PDAThread thread, Args args) {
1449 		System.out.println(thread.fStack.pop());
1450 	}
1451 
iPop(PDAThread thread, Args args)1452 	void iPop(PDAThread thread, Args args) {
1453 		String arg = args.getNextStringArg();
1454 		if (arg.startsWith("$")) { //$NON-NLS-1$
1455 			String var = arg.substring(1);
1456 			thread.fCurrentFrame.set(var, thread.fStack.pop());
1457 			String key = thread.fCurrentFrame.fFunction + "::" + var; //$NON-NLS-1$
1458 			if ( fWatchpoints.containsKey(key) && (fWatchpoints.get(key).intValue() & 2) != 0 ) {
1459 				fSuspendVM = thread.fID + " watch write " + key; //$NON-NLS-1$
1460 			}
1461 		} else {
1462 			thread.fStack.pop();
1463 		}
1464 	}
1465 
iPush(PDAThread thread, Args args)1466 	void iPush(PDAThread thread, Args args) {
1467 		String arg = args.getNextStringArg();
1468 		while (arg.length() != 0) {
1469 			if (arg.startsWith("$")) { //$NON-NLS-1$
1470 				String var = arg.substring(1);
1471 				Object val = thread.fCurrentFrame.get(var);
1472 				if (val == null)
1473 				 {
1474 					val = "<undefined>"; //$NON-NLS-1$
1475 				}
1476 				thread.fStack.push(val);
1477 				String key = thread.fCurrentFrame.fFunction + "::" + var; //$NON-NLS-1$
1478 				if (fWatchpoints.containsKey(key) && (fWatchpoints.get(key).intValue() & 1) != 0) {
1479 					fSuspendVM = thread.fID + " watch read " + key; //$NON-NLS-1$
1480 				}
1481 			} else {
1482 				Object val = arg;
1483 				if (args.hasNextArg()) {
1484 					while (args.hasNextArg()) {
1485 						val = val + " " + args.getNextStringArg(); //$NON-NLS-1$
1486 					}
1487 				} else {
1488 					try {
1489 						val = Integer.valueOf(arg);
1490 					} catch (NumberFormatException e) {
1491 					}
1492 				}
1493 				thread.fStack.push(val);
1494 			}
1495 
1496 			arg = args.getNextStringArg();
1497 		}
1498 	}
1499 
1500 	/**
1501 	 * @param thread
1502 	 * @param args
1503 	 */
iReturn(PDAThread thread, Args args)1504 	void iReturn(PDAThread thread, Args args) {
1505 		if (!thread.fFrames.isEmpty()) {
1506 			thread.fCurrentFrame = thread.fFrames.remove(thread.fFrames.size() - 1);
1507 		} else {
1508 			// Execution returned from the top frame, which means this thread
1509 			// should exit.
1510 			thread.fRun = false;
1511 		}
1512 	}
1513 
iVar(PDAThread thread, Args args)1514 	void iVar(PDAThread thread, Args args) {
1515 		String var = args.getNextStringArg();
1516 		thread.fCurrentFrame.set(var, Integer.valueOf(0));
1517 	}
1518 
1519 	/**
1520 	 * @param thread
1521 	 * @param args
1522 	 */
iInternalEndEval(PDAThread thread, Args args)1523 	void iInternalEndEval(PDAThread thread, Args args) {
1524 		Object result = thread.fStack.pop();
1525 		thread.fThreadCode = fCode;
1526 		thread.fThreadLabels = fLabels;
1527 		thread.fCurrentFrame.fPC = thread.fSavedPC;
1528 		sendDebugEvent("evalresult " + result, false); //$NON-NLS-1$
1529 		thread.fSuspend = "eval"; //$NON-NLS-1$
1530 		thread.fPerformingEval = false;
1531 	}
1532 
1533 }
1534