comparison graal/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java @ 21568:3b8bbf51d320

Truffle/Debugging: add the Truffle DebugEngine and supporting code, as well as add a crude command-line debugging tool used mainly to test the DebugEngine. Migrate the small tols out of project com.oracle.truffle.api into the new project com.oracle.truffle.tools.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 26 May 2015 16:38:13 -0700
parents
children 894f82515e38
comparison
equal deleted inserted replaced
21470:1bbef57f9a38 21568:3b8bbf51d320
1 /*
2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.oracle.truffle.tools.debug.shell.client;
26
27 import java.io.*;
28 import java.util.*;
29
30 import jline.console.*;
31
32 import com.oracle.truffle.api.*;
33 import com.oracle.truffle.api.source.*;
34 import com.oracle.truffle.tools.debug.shell.*;
35
36 /**
37 * A very simple line-oriented, language-agnostic debugging client shell: the first step toward a
38 * general, extensible debugging framework designed to be adapted for remote debugging.
39 * <p>
40 * The architecture of this debugging framework is modeled loosely on <a
41 * href="https://github.com/clojure/tools.nrepl">nREPL</a>, a network REPL developed by the Clojure
42 * community with a focus on generality:
43 * <ul>
44 * <li>Client and (possibly remote) server communicate via <em>messages</em> carried over some
45 * <em>transport</em>;</li>
46 * <li>A message is a <em>map</em> of key/value pairs;</li>
47 * <li>Keys and values are <em>strings</em>;</li>
48 * <li>The client sends messages as <em>requests</em> to a server;</li>
49 * <li>A server dispatches each incoming request to an appropriate <em>handler</em> that takes
50 * appropriate action and responds to the client with one or more messages; and</li>
51 * <li>Many implementations of the <em>transport</em> are possible.</li>
52 * </ul>
53 * <p>
54 * <strong>Compromises:</strong>
55 * <p>
56 * In order to get
57 * <ol>
58 * <li>A debugging session should start from this shell, but there is no machinery in place for
59 * doing that; instead, an entry into the language implementation creates both the server and this
60 * shell;</li>
61 * <li>The current startup sequence is based on method calls, not messages;</li>
62 * <li>Only a very few request types and keys are implemented, omitting for example request and
63 * session ids;</li>
64 * <li>Message passing is synchronous and "transported" via method calls;</li>
65 * <li>Asynchrony is emulated by having each call to the server pass only a message, and by having
66 * the server return only a list of messages.</li>
67 * </ol>
68 *
69 * @see REPLServer
70 * @see REPLMessage
71 */
72 public class SimpleREPLClient implements REPLClient {
73
74 private static final String REPLY_PREFIX = "==> ";
75 private static final String FAIL_PREFIX = "**> ";
76 private static final String WARNING_PREFIX = "!!> ";
77 private static final String TRACE_PREFIX = ">>> ";
78 private static final String[] NULL_ARGS = new String[0];
79
80 static final String INFO_LINE_FORMAT = " %s\n";
81 static final String CODE_LINE_FORMAT = " %3d %s\n";
82 static final String CODE_LINE_BREAK_FORMAT = "--> %3d %s\n";
83
84 private static final String STACK_FRAME_FORMAT = " %3d: at %s in %s line =\"%s\"\n";
85 private static final String STACK_FRAME_SELECTED_FORMAT = "==> %3d: at %s in %s line =\"%s\"\n";
86
87 private final ExecutionContext executionContext; // Language context
88
89 // Top level commands
90 private final Map<String, REPLCommand> commandMap = new HashMap<>();
91 private final Collection<String> commandNames = new TreeSet<>();
92
93 // Local options
94 private final Map<String, LocalOption> localOptions = new HashMap<>();
95 private final Collection<String> optionNames = new TreeSet<>();
96
97 // Current local context
98 ClientContextImpl clientContext;
99
100 // Cheating for the prototype; prototype startup now happens from the language server.
101 // So this isn't used.
102 public static void main(String[] args) {
103 final SimpleREPLClient repl = new SimpleREPLClient(null, null);
104 repl.start();
105 }
106
107 private final ConsoleReader reader;
108
109 private final PrintStream writer;
110
111 private final REPLServer replServer;
112
113 private final LocalOption astDepthOption = new IntegerOption(9, "astdepth", "default depth for AST display");
114
115 private final LocalOption autoWhereOption = new BooleanOption(true, "autowhere", "run the \"where\" command after each navigation");
116
117 private final LocalOption autoNodeOption = new BooleanOption(false, "autonode", "run the \"truffle node\" command after each navigation");
118
119 private final LocalOption autoSubtreeOption = new BooleanOption(false, "autosubtree", "run the \"truffle subtree\" command after each navigation");
120
121 private final LocalOption autoASTOption = new BooleanOption(false, "autoast", "run the \"truffle ast\" command after each navigation");
122
123 private final LocalOption listSizeOption = new IntegerOption(25, "listsize", "default number of lines to list");
124
125 private final LocalOption traceMessagesOption = new BooleanOption(false, "tracemessages", "trace REPL messages between client and server");
126
127 private final LocalOption verboseBreakpointInfoOption = new BooleanOption(true, "verbosebreakpointinfo", "\"info breakpoint\" displays more info");
128
129 private void addOption(LocalOption localOption) {
130 final String optionName = localOption.getName();
131 localOptions.put(optionName, localOption);
132 optionNames.add(optionName);
133 }
134
135 /**
136 * Non-null when the user has named a file other than where halted, providing context for
137 * commands such as "break"; if no explicit selection, then defaults to where halted. This is
138 * session state, so it persists across halting contexts.
139 */
140 private Source selectedSource = null;
141
142 public SimpleREPLClient(ExecutionContext context, REPLServer replServer) {
143 this.executionContext = context;
144 this.replServer = replServer;
145 this.writer = System.out;
146 try {
147 this.reader = new ConsoleReader();
148 } catch (IOException e) {
149 throw new RuntimeException("Unable to create console " + e);
150 }
151
152 addCommand(backtraceCommand);
153 addCommand(REPLRemoteCommand.BREAK_AT_LINE_CMD);
154 addCommand(REPLRemoteCommand.BREAK_AT_LINE_ONCE_CMD);
155 addCommand(REPLRemoteCommand.BREAK_AT_THROW_CMD);
156 addCommand(REPLRemoteCommand.BREAK_AT_THROW_ONCE_CMD);
157 addCommand(REPLRemoteCommand.CLEAR_BREAK_CMD);
158 addCommand(REPLRemoteCommand.CONDITION_BREAK_CMD);
159 addCommand(REPLRemoteCommand.CONTINUE_CMD);
160 addCommand(REPLRemoteCommand.DELETE_CMD);
161 addCommand(REPLRemoteCommand.DISABLE_CMD);
162 addCommand(REPLRemoteCommand.DOWN_CMD);
163 addCommand(REPLRemoteCommand.ENABLE_CMD);
164 addCommand(evalCommand);
165 addCommand(fileCommand);
166 addCommand(REPLRemoteCommand.FRAME_CMD);
167 addCommand(helpCommand);
168 addCommand(infoCommand);
169 addCommand(REPLRemoteCommand.KILL_CMD);
170 addCommand(listCommand);
171 addCommand(REPLRemoteCommand.LOAD_RUN_CMD);
172 addCommand(REPLRemoteCommand.LOAD_STEP_CMD);
173 addCommand(quitCommand);
174 addCommand(setCommand);
175 addCommand(REPLRemoteCommand.STEP_INTO_CMD);
176 addCommand(REPLRemoteCommand.STEP_OUT_CMD);
177 addCommand(REPLRemoteCommand.STEP_OVER_CMD);
178 addCommand(truffleCommand);
179 addCommand(REPLRemoteCommand.UP_CMD);
180 addCommand(whereCommand);
181
182 infoCommand.addCommand(infoBreakCommand);
183 infoCommand.addCommand(infoLanguageCommand);
184 infoCommand.addCommand(infoSetCommand);
185
186 truffleCommand.addCommand(truffleASTCommand);
187 truffleCommand.addCommand(truffleNodeCommand);
188 truffleCommand.addCommand(truffleSubtreeCommand);
189
190 addOption(astDepthOption);
191 addOption(autoASTOption);
192 addOption(autoNodeOption);
193 addOption(autoSubtreeOption);
194 addOption(autoWhereOption);
195 addOption(listSizeOption);
196 addOption(traceMessagesOption);
197 addOption(verboseBreakpointInfoOption);
198 }
199
200 public void start() {
201
202 REPLMessage startReply = replServer.start();
203
204 if (startReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
205 clientContext.displayFailReply(startReply.get(REPLMessage.DISPLAY_MSG));
206 throw new RuntimeException("Can't start REPL server");
207 }
208
209 this.clientContext = new ClientContextImpl(null, null);
210
211 try {
212 clientContext.startSession();
213 } finally {
214 clientContext.displayReply("Goodbye from " + executionContext.getLanguageShortName() + "/REPL");
215 }
216
217 }
218
219 public void addCommand(REPLCommand replCommand) {
220 final String commandName = replCommand.getCommand();
221 final String abbreviation = replCommand.getAbbreviation();
222
223 commandNames.add(commandName);
224 commandMap.put(commandName, replCommand);
225 if (abbreviation != null) {
226 commandMap.put(abbreviation, replCommand);
227 }
228 }
229
230 private class ClientContextImpl implements REPLClientContext {
231
232 private final ClientContextImpl predecessor;
233 private final int level;
234
235 // Information about where the execution is halted
236 /** The source where execution, if any, is halted; null if none. */
237 private Source haltedSource = null;
238 /** The line number where execution, if any, is halted; 0 if none. */
239 private int haltedLineNumber = 0;
240 /** The stack where execution, if any, is halted; null if none. Evaluated lazily. */
241 private List<REPLFrame> frames = null;
242
243 /** The frame number currently selected by user. */
244 private int selectedFrameNumber = 0;
245
246 private String currentPrompt;
247
248 /**
249 * Create a new context on the occasion of an execution halting.
250 */
251 public ClientContextImpl(ClientContextImpl predecessor, REPLMessage message) {
252 this.predecessor = predecessor;
253 this.level = predecessor == null ? 0 : predecessor.level + 1;
254
255 if (message != null) {
256 try {
257 this.haltedSource = Source.fromFileName(message.get(REPLMessage.SOURCE_NAME));
258 selectedSource = this.haltedSource;
259 try {
260 haltedLineNumber = Integer.parseInt(message.get(REPLMessage.LINE_NUMBER));
261 } catch (NumberFormatException e) {
262 haltedLineNumber = 0;
263 }
264 } catch (IOException e1) {
265 this.haltedSource = null;
266 this.haltedLineNumber = 0;
267 }
268 }
269 updatePrompt();
270 }
271
272 private void selectSource(String fileName) {
273 try {
274 selectedSource = Source.fromFileName(fileName);
275 } catch (IOException e1) {
276 selectedSource = null;
277 }
278 updatePrompt();
279 }
280
281 private void updatePrompt() {
282 if (level == 0) {
283 // 0-level context; no executions halted.
284 if (selectedSource == null) {
285 final String languageName = executionContext.getLanguageShortName();
286 currentPrompt = languageName == null ? "() " : "(" + languageName + ") ";
287 } else {
288 currentPrompt = "(" + selectedSource.getShortName() + ") ";
289 }
290 } else if (selectedSource != null && selectedSource != haltedSource) {
291 // User is focusing somewhere else than the current locn; show no line number.
292 final StringBuilder sb = new StringBuilder();
293 sb.append("(<" + Integer.toString(level) + "> ");
294 sb.append(selectedSource.getShortName());
295 sb.append(") ");
296 currentPrompt = sb.toString();
297 } else {
298 // Prompt reveals where currently halted.
299 final StringBuilder sb = new StringBuilder();
300 sb.append("(<" + Integer.toString(level) + "> ");
301 sb.append(haltedSource == null ? "??" : haltedSource.getShortName());
302 if (haltedLineNumber > 0) {
303 sb.append(":" + Integer.toString(haltedLineNumber));
304 }
305 sb.append(") ");
306 currentPrompt = sb.toString();
307 }
308
309 }
310
311 public Source source() {
312 return haltedSource;
313 }
314
315 public int lineNumber() {
316 return haltedLineNumber;
317 }
318
319 public List<REPLFrame> frames() {
320 if (frames == null) {
321 final REPLMessage request = new REPLMessage(REPLMessage.OP, REPLMessage.BACKTRACE);
322 final REPLMessage[] replies = sendToServer(request);
323 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
324 return null;
325 }
326 frames = new ArrayList<>();
327 for (REPLMessage reply : replies) {
328 final int index = reply.getIntValue(REPLMessage.FRAME_NUMBER);
329 final String locationFilePath = reply.get(REPLMessage.FILE_PATH);
330 final Integer locationLineNumber = reply.getIntValue(REPLMessage.LINE_NUMBER);
331 final String locationDescription = reply.get(REPLMessage.SOURCE_LOCATION);
332 final String name = reply.get(REPLMessage.METHOD_NAME);
333 final String sourceLineText = reply.get(REPLMessage.SOURCE_LINE_TEXT);
334 frames.add(new REPLFrameImpl(index, locationFilePath, locationLineNumber, locationDescription, name, sourceLineText));
335 }
336 frames = Collections.unmodifiableList(frames);
337 }
338 return frames;
339 }
340
341 public int level() {
342 return this.level;
343 }
344
345 public Source getSelectedSource() {
346 return selectedSource == null ? haltedSource : selectedSource;
347 }
348
349 public int getSelectedFrameNumber() {
350 return selectedFrameNumber;
351 }
352
353 public String stringQuery(String op) {
354 assert op != null;
355 REPLMessage request = null;
356 switch (op) {
357 case REPLMessage.TRUFFLE_AST:
358 request = truffleASTCommand.createRequest(clientContext, NULL_ARGS);
359 break;
360 case REPLMessage.TRUFFLE_SUBTREE:
361 request = truffleSubtreeCommand.createRequest(clientContext, NULL_ARGS);
362 break;
363 default:
364 request = new REPLMessage();
365 request.put(REPLMessage.OP, op);
366 }
367 if (request == null) {
368 return null;
369 }
370 final REPLMessage[] replies = sendToServer(request);
371 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
372 return null;
373 }
374 return replies[0].get(REPLMessage.DISPLAY_MSG);
375 }
376
377 public void selectFrameNumber(int frameNumber) {
378 this.selectedFrameNumber = frameNumber;
379 }
380
381 void displayWhere() {
382 if (level == 0) {
383 displayFailReply("no active execution");
384 return;
385 }
386
387 Source whereSource = null;
388 int whereLineNumber = 0;
389
390 if (selectedFrameNumber == 0) {
391 whereSource = haltedSource;
392 whereLineNumber = haltedLineNumber;
393 } else {
394 final REPLFrame frame = frames().get(selectedFrameNumber);
395 final String locationFileName = frame.locationFilePath();
396 if (locationFileName != null) {
397 try {
398 whereSource = Source.fromFileName(locationFileName);
399 } catch (IOException e) {
400 }
401 }
402 whereLineNumber = frame.locationLineNumber();
403 }
404 if (whereSource == null) {
405 displayFailReply("Frame " + selectedFrameNumber + ": source unavailable");
406 return;
407 }
408 final int listSize = listSizeOption.getInt();
409
410 final int fileLineCount = whereSource.getLineCount();
411 final String code = whereSource.getCode();
412
413 writer.println("Frame " + selectedFrameNumber + ": " + whereSource.getShortName() + "\n");
414 final int halfListSize = listSize / 2;
415 final int startLineNumber = Math.max(1, whereLineNumber - halfListSize);
416 final int lastLineNumber = Math.min(startLineNumber + listSize - 1, fileLineCount);
417 for (int line = startLineNumber; line <= lastLineNumber; line++) {
418 final int offset = whereSource.getLineStartOffset(line);
419 final String lineText = code.substring(offset, offset + whereSource.getLineLength(line));
420 if (line == whereLineNumber) {
421 writer.format(CODE_LINE_BREAK_FORMAT, line, lineText);
422 } else {
423 writer.format(CODE_LINE_FORMAT, line, lineText);
424 }
425 }
426 }
427
428 public void displayStack() {
429 final List<REPLFrame> frameList = frames();
430 if (frameList == null) {
431 writer.println("<empty stack>");
432 } else {
433 for (REPLFrame frame : frameList) {
434 String sourceLineText = frame.sourceLineText();
435 if (sourceLineText == null) {
436 sourceLineText = "<??>";
437 }
438 if (frame.index() == selectedFrameNumber) {
439 writer.format(STACK_FRAME_SELECTED_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText);
440 } else {
441 writer.format(STACK_FRAME_FORMAT, frame.index(), frame.locationDescription(), frame.name(), sourceLineText);
442 }
443 }
444 }
445 }
446
447 public void displayInfo(String message) {
448 writer.format(INFO_LINE_FORMAT, message);
449 }
450
451 public void displayReply(String message) {
452 writer.println(REPLY_PREFIX + message);
453 }
454
455 public void displayFailReply(String message) {
456 writer.println(FAIL_PREFIX + message);
457 }
458
459 public void displayWarnings(String warnings) {
460 for (String warning : warnings.split("\\n")) {
461 writer.println(WARNING_PREFIX + warning);
462 }
463 }
464
465 public void traceMessage(String message) {
466 writer.println(TRACE_PREFIX + message);
467 }
468
469 public void startSession() {
470
471 while (true) {
472 try {
473 String[] args;
474 String line = reader.readLine(currentPrompt).trim();
475 if (line.startsWith("eval ")) {
476 args = new String[]{"eval", line.substring(5)};
477 } else {
478 args = line.split("[ \t]+");
479 }
480 if (args.length == 0) {
481 break;
482 }
483 final String cmd = args[0];
484
485 if (cmd.isEmpty()) {
486 continue;
487 }
488
489 REPLCommand command = commandMap.get(cmd);
490 while (command instanceof REPLIndirectCommand) {
491 if (traceMessagesOption.getBool()) {
492 traceMessage("Executing indirect: " + command.getCommand());
493 }
494 command = ((REPLIndirectCommand) command).getCommand(args);
495 }
496 if (command == null) {
497 clientContext.displayFailReply("Unrecognized command \"" + cmd + "\"");
498 continue;
499 }
500 if (command instanceof REPLLocalCommand) {
501 if (traceMessagesOption.getBool()) {
502 traceMessage("Executing local: " + command.getCommand());
503 }
504 ((REPLLocalCommand) command).execute(args);
505
506 } else if (command instanceof REPLRemoteCommand) {
507 final REPLRemoteCommand remoteCommand = (REPLRemoteCommand) command;
508
509 final REPLMessage request = remoteCommand.createRequest(clientContext, args);
510 if (request == null) {
511 continue;
512 }
513
514 REPLMessage[] replies = sendToServer(request);
515
516 remoteCommand.processReply(clientContext, replies);
517 } else {
518 assert false; // Should not happen.
519 }
520
521 } catch (REPLContinueException ex) {
522 break;
523 } catch (IOException e) {
524 e.printStackTrace();
525 }
526 }
527
528 }
529
530 private REPLMessage[] sendToServer(REPLMessage request) {
531 if (traceMessagesOption.getBool()) {
532 clientContext.traceMessage("Sever request:");
533 request.print(writer, " ");
534 }
535
536 REPLMessage[] replies = replServer.receive(request);
537
538 assert replies != null && replies.length > 0;
539 if (traceMessagesOption.getBool()) {
540 if (replies.length > 1) {
541 clientContext.traceMessage("Received " + replies.length + " server replies");
542 int replyCount = 0;
543 for (REPLMessage reply : replies) {
544 clientContext.traceMessage("Server Reply " + replyCount++ + ":");
545 reply.print(writer, " ");
546 }
547 } else {
548 clientContext.traceMessage("Received reply:");
549 replies[0].print(writer, " ");
550 }
551 }
552 return replies;
553 }
554
555 private final class REPLFrameImpl implements REPLFrame {
556
557 private final int index;
558 private final String locationFilePath;
559 private final Integer locationLineNumber;
560 private final String locationDescription;
561 private final String name;
562 private final String sourceLineText;
563
564 REPLFrameImpl(int index, String locationFilePath, Integer locationLineNumber, String locationDescription, String name, String sourceLineText) {
565 this.index = index;
566 this.locationFilePath = locationFilePath;
567 this.locationLineNumber = locationLineNumber;
568 this.locationDescription = locationDescription;
569 this.name = name;
570 this.sourceLineText = sourceLineText;
571 }
572
573 public int index() {
574 return index;
575 }
576
577 public String locationFilePath() {
578 return locationFilePath;
579 }
580
581 public Integer locationLineNumber() {
582 return locationLineNumber;
583 }
584
585 public String locationDescription() {
586 return locationDescription;
587 }
588
589 public String name() {
590 return name;
591 }
592
593 public String sourceLineText() {
594 return sourceLineText;
595 }
596
597 }
598
599 }
600
601 // Cheating with synchrony: asynchronous replies should arrive here, but don't.
602 @Override
603 public REPLMessage receive(REPLMessage request) {
604 final String result = request.get("result");
605 clientContext.displayReply(result != null ? result : request.toString());
606 return null;
607 }
608
609 /**
610 * Cheating with synchrony: take a direct call from the server that execution has halted and
611 * we've entered a nested debugging context.
612 */
613 public void halted(REPLMessage message) {
614
615 // Push a new context for where we've stopped.
616 clientContext = new ClientContextImpl(clientContext, message);
617 final String warnings = message.get(REPLMessage.WARNINGS);
618 if (warnings != null) {
619 clientContext.displayWarnings(warnings);
620 }
621 if (autoWhereOption.getBool()) {
622 clientContext.displayWhere();
623 }
624 if (autoNodeOption.getBool()) {
625 final String result = clientContext.stringQuery(REPLMessage.TRUFFLE_NODE);
626 if (result != null) {
627 displayTruffleNode(result);
628 }
629 }
630 if (autoASTOption.getBool()) {
631 final String result = clientContext.stringQuery(REPLMessage.TRUFFLE_AST);
632 if (result != null) {
633 displayTruffleAST(result);
634 }
635 }
636 if (autoSubtreeOption.getBool()) {
637 final String result = clientContext.stringQuery(REPLMessage.TRUFFLE_SUBTREE);
638 if (result != null) {
639 displayTruffleSubtree(result);
640 }
641 }
642
643 try {
644 clientContext.startSession();
645 } finally {
646
647 // To continue execution, pop the context and return
648 this.clientContext = clientContext.predecessor;
649 }
650 }
651
652 /**
653 * A command that can be executed without (direct) communication with the server; it may rely on
654 * some other method that goes to the server for information.
655 */
656 private abstract class REPLLocalCommand extends REPLCommand {
657
658 public REPLLocalCommand(String command, String abbreviation, String description) {
659 super(command, abbreviation, description);
660 }
661
662 abstract void execute(String[] args);
663 }
664
665 /**
666 * A command that redirects to other commands, based on arguments.
667 */
668 private abstract class REPLIndirectCommand extends REPLCommand {
669
670 public REPLIndirectCommand(String command, String abbreviation, String description) {
671 super(command, abbreviation, description);
672 }
673
674 abstract void addCommand(REPLCommand command);
675
676 abstract REPLCommand getCommand(String[] args);
677 }
678
679 private final REPLCommand backtraceCommand = new REPLLocalCommand("backtrace", "bt", "Display current stack") {
680
681 @Override
682 void execute(String[] args) {
683 if (clientContext.level == 0) {
684 clientContext.displayFailReply("no active execution");
685 } else {
686 clientContext.displayStack();
687 }
688 }
689 };
690
691 private final REPLCommand evalCommand = new REPLRemoteCommand("eval", null, "Evaluate a string, in context of the current frame if any") {
692
693 private int evalCounter = 0;
694
695 @Override
696 public REPLMessage createRequest(REPLClientContext context, String[] args) {
697 if (args.length > 1) {
698 final String code = args[1];
699 if (!code.isEmpty()) {
700 // Create a fake entry in the file maps and cache, based on this unique name
701 final String fakeFileName = "<eval" + ++evalCounter + ">";
702 Source.fromNamedText(fakeFileName, code);
703 final REPLMessage request = new REPLMessage();
704 request.put(REPLMessage.OP, REPLMessage.EVAL);
705 request.put(REPLMessage.CODE, code);
706 request.put(REPLMessage.SOURCE_NAME, fakeFileName);
707 if (clientContext.level > 0) {
708 // Specify a requested execution context, if one exists; otherwise top level
709 request.put(REPLMessage.FRAME_NUMBER, Integer.toString(context.getSelectedFrameNumber()));
710 }
711 return request;
712 }
713 }
714 return null;
715 }
716 };
717
718 private final REPLCommand fileCommand = new REPLRemoteCommand("file", null, "Set/display current file for viewing") {
719
720 final String[] help = {"file: display current file path", "file <filename>: Set file to be current file for viewing"};
721
722 @Override
723 public String[] getHelp() {
724 return help;
725 }
726
727 @Override
728 public REPLMessage createRequest(REPLClientContext context, String[] args) {
729 if (args.length == 1) {
730 final Source source = clientContext.getSelectedSource();
731 if (source == null) {
732 clientContext.displayFailReply("no file currently selected");
733 } else {
734 clientContext.displayReply(source.getPath());
735 }
736 return null;
737 }
738 final REPLMessage request = new REPLMessage();
739 request.put(REPLMessage.OP, REPLMessage.FILE);
740 request.put(REPLMessage.SOURCE_NAME, args[1]);
741 return request;
742 }
743
744 @Override
745 void processReply(REPLClientContext context, REPLMessage[] replies) {
746 REPLMessage firstReply = replies[0];
747
748 if (firstReply.get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
749 final String result = firstReply.get(REPLMessage.DISPLAY_MSG);
750 clientContext.displayFailReply(result != null ? result : firstReply.toString());
751 return;
752 }
753 final String fileName = firstReply.get(REPLMessage.SOURCE_NAME);
754 final String path = firstReply.get(REPLMessage.FILE_PATH);
755 clientContext.selectSource(path == null ? fileName : path);
756 clientContext.displayReply(clientContext.getSelectedSource().getPath());
757
758 for (int i = 1; i < replies.length; i++) {
759 REPLMessage reply = replies[i];
760 final String result = reply.get(REPLMessage.DISPLAY_MSG);
761 clientContext.displayInfo(result != null ? result : reply.toString());
762 }
763 }
764
765 };
766
767 private final REPLCommand helpCommand = new REPLLocalCommand("help", null, "Describe commands") {
768
769 final String[] help = {"help: list available commands", "help <command>: additional information about <command>"};
770
771 @Override
772 public String[] getHelp() {
773 return help;
774 }
775
776 @Override
777 public void execute(String[] args) {
778
779 if (args.length == 1) {
780 clientContext.displayReply("Available commands:");
781 for (String commandName : commandNames) {
782 final REPLCommand command = commandMap.get(commandName);
783 if (command == null) {
784 clientContext.displayInfo(commandName + ": Error, no implementation for command");
785 } else {
786 final String abbrev = command.getAbbreviation();
787 if (abbrev == null) {
788 clientContext.displayInfo(commandName + ": " + command.getDescription());
789 } else {
790 clientContext.displayInfo(commandName + "(" + abbrev + "): " + command.getDescription());
791 }
792 }
793 }
794 } else {
795 final String cmdName = args[1];
796 final REPLCommand cmd = commandMap.get(cmdName);
797 if (cmd == null) {
798 clientContext.displayReply("command \"" + cmdName + "\" not recognized");
799 } else {
800 final String[] helpLines = cmd.getHelp();
801 if (helpLines == null) {
802 clientContext.displayReply("\"" + cmdName + "\":");
803 } else if (helpLines.length == 1) {
804 clientContext.displayInfo(helpLines[0]);
805 } else {
806 clientContext.displayReply("\"" + cmdName + "\":");
807 for (String line : helpLines) {
808 clientContext.displayInfo(line);
809 }
810 }
811 }
812 }
813 }
814 };
815
816 private final REPLIndirectCommand infoCommand = new REPLIndirectCommand(REPLMessage.INFO, null, "Additional information on topics") {
817
818 // "Info" commands
819 private final Map<String, REPLCommand> infoCommandMap = new HashMap<>();
820 private final Collection<String> infoCommandNames = new TreeSet<>();
821
822 @Override
823 public String[] getHelp() {
824 final ArrayList<String> lines = new ArrayList<>();
825 for (String infoCommandName : infoCommandNames) {
826 final REPLCommand cmd = infoCommandMap.get(infoCommandName);
827 if (cmd == null) {
828 lines.add("\"" + REPLMessage.INFO + " " + infoCommandName + "\" not implemented");
829 } else {
830 lines.add("\"" + REPLMessage.INFO + " " + infoCommandName + "\": " + cmd.getDescription());
831 }
832 }
833 return lines.toArray(new String[0]);
834 }
835
836 @Override
837 void addCommand(REPLCommand replCommand) {
838 final String commandName = replCommand.getCommand();
839 final String abbreviation = replCommand.getAbbreviation();
840
841 infoCommandNames.add(commandName);
842 infoCommandMap.put(commandName, replCommand);
843 if (abbreviation != null) {
844 infoCommandMap.put(abbreviation, replCommand);
845 }
846 }
847
848 @Override
849 REPLCommand getCommand(String[] args) {
850 if (args.length == 1) {
851 clientContext.displayFailReply("info topic not specified; try \"help info\"");
852 return null;
853 }
854 final String topic = args[1];
855 REPLCommand command = infoCommandMap.get(topic);
856 if (command == null) {
857 clientContext.displayFailReply("topic \"" + topic + "\" not recognized");
858 return null;
859 }
860
861 return command;
862 }
863
864 };
865
866 private final REPLCommand infoBreakCommand = new REPLRemoteCommand("breakpoint", "break", "info about breakpoints") {
867
868 @Override
869 public REPLMessage createRequest(REPLClientContext context, String[] args) {
870 final REPLMessage request = new REPLMessage();
871 request.put(REPLMessage.OP, REPLMessage.BREAKPOINT_INFO);
872 return request;
873 }
874
875 @Override
876 void processReply(REPLClientContext context, REPLMessage[] replies) {
877 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
878 clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG));
879 } else {
880 Arrays.sort(replies, new Comparator<REPLMessage>() {
881
882 public int compare(REPLMessage o1, REPLMessage o2) {
883 try {
884 final int n1 = Integer.parseInt(o1.get(REPLMessage.BREAKPOINT_ID));
885 final int n2 = Integer.parseInt(o2.get(REPLMessage.BREAKPOINT_ID));
886 return Integer.compare(n1, n2);
887 } catch (Exception ex) {
888 }
889 return 0;
890 }
891
892 });
893 clientContext.displayReply("Breakpoints set:");
894 for (REPLMessage message : replies) {
895 final StringBuilder sb = new StringBuilder();
896
897 sb.append(Integer.parseInt(message.get(REPLMessage.BREAKPOINT_ID)) + ": ");
898 sb.append("@" + message.get(REPLMessage.INFO_VALUE));
899 sb.append(" (state=" + message.get(REPLMessage.BREAKPOINT_STATE));
900 if (verboseBreakpointInfoOption.getBool()) {
901 sb.append(", group=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_GROUP_ID)));
902 sb.append(", hits=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_HIT_COUNT)));
903 sb.append(", ignore=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_IGNORE_COUNT)));
904 }
905 final String condition = message.get(REPLMessage.BREAKPOINT_CONDITION);
906 if (condition != null) {
907 sb.append(", condition=\"" + condition + "\"");
908 }
909 sb.append(")");
910 clientContext.displayInfo(sb.toString());
911 }
912 }
913 }
914 };
915
916 private final REPLCommand infoLanguageCommand = new REPLRemoteCommand("language", "lang", "language and implementation details") {
917
918 final String[] help = {"info language: list details about the language implementation"};
919
920 @Override
921 public String[] getHelp() {
922 return help;
923 }
924
925 @Override
926 public REPLMessage createRequest(REPLClientContext context, String[] args) {
927 final REPLMessage request = new REPLMessage();
928 request.put(REPLMessage.OP, REPLMessage.INFO);
929 request.put(REPLMessage.TOPIC, REPLMessage.LANGUAGE);
930 return request;
931 }
932
933 @Override
934 void processReply(REPLClientContext context, REPLMessage[] replies) {
935 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
936 clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG));
937 } else {
938 clientContext.displayReply("Language info:");
939 for (REPLMessage message : replies) {
940 final StringBuilder sb = new StringBuilder();
941 sb.append(message.get(REPLMessage.INFO_KEY));
942 sb.append(": ");
943 sb.append(message.get(REPLMessage.INFO_VALUE));
944 clientContext.displayInfo(sb.toString());
945 }
946 }
947 }
948 };
949
950 private final REPLCommand infoSetCommand = new REPLLocalCommand("set", null, "info about settings") {
951
952 final String[] help = {"info sets: list local options that can be set"};
953
954 @Override
955 public String[] getHelp() {
956 return help;
957 }
958
959 @Override
960 public void execute(String[] args) {
961
962 clientContext.displayReply("Settable options:");
963
964 for (String optionName : optionNames) {
965 final LocalOption localOption = localOptions.get(optionName);
966 if (localOption == null) {
967 clientContext.displayInfo(localOption + ": Error, no implementation for option");
968 } else {
969 clientContext.displayInfo(optionName + "=" + localOption.getValue() + ": " + localOption.getDescription());
970 }
971 }
972 }
973 };
974
975 private final REPLCommand listCommand = new REPLLocalCommand("list", null, "Display selected source file") {
976
977 final String[] help = {"list: list <listsize> lines of selected file (see option \"listsize\")", "list all: list all lines", "list <n>: list <listsize> lines centered around line <n>"};
978
979 private Source lastListedSource = null;
980
981 private int nextLineToList = 1;
982
983 @Override
984 public String[] getHelp() {
985 return help;
986 }
987
988 @Override
989 public void execute(String[] args) {
990 final Source source = clientContext.getSelectedSource();
991 if (source == null) {
992 clientContext.displayFailReply("No selected file");
993 reset();
994 return;
995 }
996 final int listSize = listSizeOption.getInt();
997
998 if (args.length == 1) {
999 if (!source.equals(lastListedSource)) {
1000 reset();
1001 } else if (nextLineToList > source.getLineCount()) {
1002 reset();
1003 }
1004 final int lastListedLine = printLines(source, nextLineToList, listSize);
1005 lastListedSource = source;
1006 nextLineToList = lastListedLine > source.getLineCount() ? 1 : lastListedLine + 1;
1007 } else if (args.length == 2) {
1008 reset();
1009 if (args[1].equals("all")) {
1010 printLines(source, 1, source.getLineCount());
1011 } else {
1012 try {
1013 final int line = Integer.parseInt(args[1]);
1014 final int halfListSize = listSize / 2;
1015 final int start = Math.max(1, line - halfListSize);
1016 final int count = Math.min(source.getLineCount() + 1 - start, listSize);
1017 printLines(source, start, count);
1018 } catch (NumberFormatException e) {
1019 clientContext.displayFailReply("\"" + args[1] + "\" not recognized");
1020 }
1021
1022 }
1023 }
1024 }
1025
1026 private int printLines(Source printSource, int start, int listSize) {
1027
1028 clientContext.displayReply(printSource.getShortName() + ":");
1029 final int lastLineNumber = Math.min(start + listSize - 1, printSource.getLineCount());
1030 for (int line = start; line <= lastLineNumber; line++) {
1031 writer.format(CODE_LINE_FORMAT, line, printSource.getCode(line));
1032 }
1033 return lastLineNumber;
1034 }
1035
1036 /**
1037 * Forget where we were in a sequence of list commands with no arguments
1038 */
1039 private void reset() {
1040 lastListedSource = clientContext.getSelectedSource();
1041 nextLineToList = 1;
1042 }
1043 };
1044
1045 private final REPLCommand quitCommand = new REPLRemoteCommand("quit", "q", "Quit execution and REPL") {
1046
1047 @Override
1048 public REPLMessage createRequest(REPLClientContext context, String[] args) {
1049 final REPLMessage request = new REPLMessage();
1050 request.put(REPLMessage.OP, REPLMessage.QUIT);
1051 return request;
1052 }
1053
1054 };
1055
1056 private final REPLCommand setCommand = new REPLLocalCommand("set", null, "set <option>=<value>") {
1057
1058 @Override
1059 public String[] getHelp() {
1060 return new String[]{"Sets an option \"set <option-name>=<value>\"; see also \"info set\""};
1061 }
1062
1063 @Override
1064 public void execute(String[] args) {
1065 REPLMessage request = null;
1066 if (args.length == 1) {
1067 clientContext.displayFailReply("No option specified, try \"help set\"");
1068 } else if (args.length == 2) {
1069 String[] split = new String[0];
1070 try {
1071 split = args[1].split("=");
1072 } catch (Exception ex) {
1073 }
1074 if (split.length == 0) {
1075 clientContext.displayFailReply("Arguments not understood, try \"help set\"");
1076 } else if (split.length == 1) {
1077 clientContext.displayFailReply("No option value specified, try \"help set\"");
1078 } else if (split.length > 2) {
1079 clientContext.displayFailReply("Arguments not understood, try \"help set\"");
1080 } else {
1081 final String optionName = split[0];
1082 final String newValue = split[1];
1083 final LocalOption localOption = localOptions.get(optionName);
1084 if (localOption != null) {
1085 if (!localOption.setValue(newValue)) {
1086 clientContext.displayFailReply("Invalid option value \"" + newValue + "\"");
1087 }
1088 clientContext.displayInfo(localOption.name + " = " + localOption.getValue());
1089 } else {
1090 request = new REPLMessage();
1091 request.put(REPLMessage.OP, REPLMessage.SET);
1092 request.put(REPLMessage.OPTION, optionName);
1093 request.put(REPLMessage.VALUE, newValue);
1094 }
1095 }
1096 } else {
1097 clientContext.displayFailReply("Arguments not understood, try \"help set\"");
1098 }
1099 }
1100 };
1101
1102 private final REPLIndirectCommand truffleCommand = new REPLIndirectCommand(REPLMessage.TRUFFLE, "t", "Access to Truffle internals") {
1103
1104 // "Truffle" commands
1105 private final Map<String, REPLCommand> truffleCommandMap = new HashMap<>();
1106 private final Collection<String> truffleCommandNames = new TreeSet<>();
1107
1108 @Override
1109 public String[] getHelp() {
1110 final ArrayList<String> lines = new ArrayList<>();
1111 for (String truffleCommandName : truffleCommandNames) {
1112 final REPLCommand cmd = truffleCommandMap.get(truffleCommandName);
1113 if (cmd == null) {
1114 lines.add("\"" + REPLMessage.TRUFFLE + " " + truffleCommandName + "\" not implemented");
1115 } else {
1116 for (String line : cmd.getHelp()) {
1117 lines.add(line);
1118 }
1119 }
1120 }
1121 return lines.toArray(new String[0]);
1122 }
1123
1124 @Override
1125 void addCommand(REPLCommand replCommand) {
1126 final String commandName = replCommand.getCommand();
1127 final String abbreviation = replCommand.getAbbreviation();
1128
1129 truffleCommandNames.add(commandName);
1130 truffleCommandMap.put(commandName, replCommand);
1131 if (abbreviation != null) {
1132 truffleCommandMap.put(abbreviation, replCommand);
1133 }
1134 }
1135
1136 @Override
1137 REPLCommand getCommand(String[] args) {
1138 if (args.length == 1) {
1139 clientContext.displayFailReply("truffle request not specified; try \"help truffle\"");
1140 return null;
1141 }
1142 final String topic = args[1];
1143 REPLCommand command = truffleCommandMap.get(topic);
1144 if (command == null) {
1145 clientContext.displayFailReply("truffle request \"" + topic + "\" not recognized");
1146 return null;
1147 }
1148 return command;
1149 }
1150 };
1151
1152 private final REPLRemoteCommand truffleASTCommand = new REPLRemoteCommand("ast", null, "print the AST that contains the current node") {
1153
1154 final String[] help = {"truffle ast: print the AST subtree that contains current node (see \"set treedepth\")",
1155 "truffle ast <n>: print the AST subtree that contains current node to a maximum depth of <n>"};
1156
1157 @Override
1158 public String[] getHelp() {
1159 return help;
1160 }
1161
1162 @Override
1163 public REPLMessage createRequest(REPLClientContext context, String[] args) {
1164 if (clientContext.level() == 0) {
1165 context.displayFailReply("no active execution");
1166 return null;
1167 }
1168
1169 final REPLMessage request = new REPLMessage();
1170 request.put(REPLMessage.OP, REPLMessage.TRUFFLE);
1171 request.put(REPLMessage.TOPIC, REPLMessage.AST);
1172
1173 int astDepth = astDepthOption.getInt();
1174 if (args.length > 2) {
1175 final String depthText = args[2];
1176 try {
1177 astDepth = Integer.parseInt(depthText);
1178 } catch (NumberFormatException e) {
1179 }
1180 }
1181 request.put(REPLMessage.AST_DEPTH, Integer.toString(astDepth));
1182 return request;
1183 }
1184
1185 @Override
1186 void processReply(REPLClientContext context, REPLMessage[] replies) {
1187 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
1188 clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG));
1189 } else {
1190 clientContext.displayReply("AST containing the Current Node:");
1191 for (REPLMessage message : replies) {
1192 for (String line : message.get(REPLMessage.DISPLAY_MSG).split("\n")) {
1193 clientContext.displayInfo(line);
1194 }
1195 }
1196 }
1197 }
1198 };
1199
1200 private void displayTruffleAST(String text) {
1201 clientContext.displayReply("AST containing Current Node:");
1202 for (String line : text.split("\n")) {
1203 clientContext.displayInfo(line);
1204 }
1205 }
1206
1207 private final REPLRemoteCommand truffleNodeCommand = new REPLRemoteCommand("node", null, "describe current AST node") {
1208
1209 final String[] help = {"truffle node: describe the AST node at the current execution context"};
1210
1211 @Override
1212 public String[] getHelp() {
1213 return help;
1214 }
1215
1216 @Override
1217 public REPLMessage createRequest(REPLClientContext context, String[] args) {
1218 if (clientContext.level() == 0) {
1219 context.displayFailReply("no active execution");
1220 return null;
1221 }
1222 final REPLMessage request = new REPLMessage();
1223 request.put(REPLMessage.OP, REPLMessage.TRUFFLE_NODE);
1224 return request;
1225 }
1226
1227 @Override
1228 void processReply(REPLClientContext context, REPLMessage[] replies) {
1229 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
1230 clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG));
1231 } else {
1232 displayTruffleNode(replies[0].get(REPLMessage.DISPLAY_MSG));
1233 }
1234 }
1235 };
1236
1237 private void displayTruffleNode(String nodeString) {
1238 clientContext.displayReply("Current Node: " + nodeString);
1239 }
1240
1241 private final REPLRemoteCommand truffleSubtreeCommand = new REPLRemoteCommand("subtree", "sub", "print the AST subtree rooted at the current node") {
1242
1243 final String[] help = {"truffle sub: print the AST subtree at the current node (see \"set treedepth\")", "truffle sub <n>: print the AST subtree at the current node to maximum depth <n>",
1244 "truffle subtree: print the AST subtree at the current node (see \"set treedepth\")", "truffle sub <n>: print the AST subtree at the current node to maximum depth <n>"};
1245
1246 @Override
1247 public String[] getHelp() {
1248 return help;
1249 }
1250
1251 @Override
1252 public REPLMessage createRequest(REPLClientContext context, String[] args) {
1253 if (clientContext.level() == 0) {
1254 context.displayFailReply("no active execution");
1255 return null;
1256 }
1257
1258 final REPLMessage request = new REPLMessage();
1259 request.put(REPLMessage.OP, REPLMessage.TRUFFLE);
1260 request.put(REPLMessage.TOPIC, REPLMessage.SUBTREE);
1261
1262 int astDepth = astDepthOption.getInt();
1263 if (args.length > 2) {
1264 final String depthText = args[2];
1265 try {
1266 astDepth = Integer.parseInt(depthText);
1267 } catch (NumberFormatException e) {
1268 }
1269 }
1270 request.put(REPLMessage.AST_DEPTH, Integer.toString(astDepth));
1271 return request;
1272 }
1273
1274 @Override
1275 void processReply(REPLClientContext context, REPLMessage[] replies) {
1276 if (replies[0].get(REPLMessage.STATUS).equals(REPLMessage.FAILED)) {
1277 clientContext.displayFailReply(replies[0].get(REPLMessage.DISPLAY_MSG));
1278 } else {
1279 clientContext.displayReply("AST subtree at Current Node:");
1280 for (REPLMessage message : replies) {
1281 for (String line : message.get(REPLMessage.DISPLAY_MSG).split("\n")) {
1282 clientContext.displayInfo(line);
1283 }
1284 }
1285 }
1286 }
1287 };
1288
1289 private void displayTruffleSubtree(String text) {
1290 clientContext.displayReply("AST subtree at Current Node:");
1291 for (String line : text.split("\n")) {
1292 clientContext.displayInfo(line);
1293 }
1294 }
1295
1296 private final REPLCommand whereCommand = new REPLLocalCommand("where", null, "Show code around current break location") {
1297
1298 @Override
1299 public void execute(String[] args) {
1300 clientContext.displayWhere();
1301 }
1302 };
1303
1304 private abstract static class LocalOption {
1305 private final String name;
1306 private final String description;
1307
1308 protected LocalOption(String name, String description) {
1309 this.name = name;
1310 this.description = description;
1311 }
1312
1313 public String getName() {
1314 return name;
1315 }
1316
1317 public String getDescription() {
1318 return description;
1319 }
1320
1321 public abstract boolean setValue(String newValue);
1322
1323 public boolean getBool() {
1324 assert false;
1325 return false;
1326 }
1327
1328 public int getInt() {
1329 assert false;
1330 return 0;
1331 }
1332
1333 public abstract String getValue();
1334 }
1335
1336 private static final class BooleanOption extends LocalOption {
1337
1338 private Boolean value;
1339
1340 public BooleanOption(boolean value, String name, String description) {
1341 super(name, description);
1342 this.value = value;
1343 }
1344
1345 @Override
1346 public boolean setValue(String newValue) {
1347 final Boolean valueOf = Boolean.valueOf(newValue);
1348 if (valueOf == null) {
1349 return false;
1350 }
1351 value = valueOf;
1352 return true;
1353 }
1354
1355 @Override
1356 public boolean getBool() {
1357 return value;
1358 }
1359
1360 @Override
1361 public String getValue() {
1362 return value.toString();
1363 }
1364 }
1365
1366 private static final class IntegerOption extends LocalOption {
1367
1368 private Integer value;
1369
1370 public IntegerOption(int value, String name, String description) {
1371 super(name, description);
1372 this.value = value;
1373 }
1374
1375 @Override
1376 public boolean setValue(String newValue) {
1377 Integer valueOf;
1378 try {
1379 valueOf = Integer.valueOf(newValue);
1380 } catch (NumberFormatException e) {
1381 return false;
1382 }
1383 value = valueOf;
1384 return true;
1385 }
1386
1387 @Override
1388 public int getInt() {
1389 return value;
1390 }
1391
1392 @Override
1393 public String getValue() {
1394 return value.toString();
1395 }
1396
1397 }
1398
1399 }