Skip to content

Commit ea75557

Browse files
committed
Update InterpreterJVM.java
1 parent 3c5904d commit ea75557

File tree

1 file changed

+146
-62
lines changed

1 file changed

+146
-62
lines changed

drjava/src/edu/rice/cs/drjava/model/repl/newjvm/InterpreterJVM.java

Lines changed: 146 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,39 @@
11
/*BEGIN_COPYRIGHT_BLOCK
22
*
3-
* Copyright (c) 2001-2019, JavaPLT group at Rice University (drjava@rice.edu). All rights reserved.
3+
* Copyright (c) 2001-2017, JavaPLT group at Rice University (drjava@rice.edu)
4+
* All rights reserved.
45
*
5-
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
6-
* following conditions are met:
7-
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
8-
* disclaimer.
9-
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10-
* following disclaimer in the documentation and/or other materials provided with the distribution.
11-
* * Neither the names of DrJava, the JavaPLT group, Rice University, nor the names of its contributors may be used
12-
* to endorse or promote products derived from this software without specific prior written permission.
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions are met:
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above copyright
11+
* notice, this list of conditions and the following disclaimer in the
12+
* documentation and/or other materials provided with the distribution.
13+
* * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
14+
* names of its contributors may be used to endorse or promote products
15+
* derived from this software without specific prior written permission.
1316
*
14-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
15-
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16-
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17-
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18-
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
19-
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
20-
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2128
*
22-
* This software is Open Source Initiative approved Open Source Software. Open Source Initative Approved is a trademark
23-
* of the Open Source Initiative.
29+
* This software is Open Source Initiative approved Open Source Software.
30+
* Open Source Initative Approved is a trademark of the Open Source Initiative.
2431
*
25-
* This file is part of DrJava. Download the current version of this project from http://www.drjava.org/ or
26-
* http://sourceforge.net/projects/drjava/
32+
* This file is part of DrJava. Download the current version of this project
33+
* from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
2734
*
2835
* END_COPYRIGHT_BLOCK*/
36+
2937
package edu.rice.cs.drjava.model.repl.newjvm;
3038

3139
import java.util.*;
@@ -61,6 +69,12 @@
6169
import edu.rice.cs.dynamicjava.symbol.*;
6270
import edu.rice.cs.dynamicjava.symbol.type.Type;
6371

72+
//for JShell because dynamicJava does not have support for java9+
73+
import jdk.jshell.*;
74+
import java.util.List;
75+
import java.util.Locale;
76+
import java.util.stream.Collectors;
77+
6478
// For Windows focus fix
6579
import javax.swing.JDialog;
6680

@@ -78,8 +92,8 @@
7892
* @version $Id$
7993
*/
8094
public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRemoteI, JUnitModelCallback {
81-
82-
public static final Log _log = new Log("GlobalModel.txt", false);
95+
/** log for use in debugging */
96+
public static final Log _log = new Log("InterpreterJVM.txt", false);
8397

8498
/** Singleton instance of this class. */
8599
public static final InterpreterJVM ONLY = new InterpreterJVM();
@@ -92,21 +106,26 @@ public class InterpreterJVM extends AbstractSlaveJVM implements InterpreterJVMRe
92106
private final Interpreter _defaultInterpreter;
93107
private final Map<String, Interpreter> _interpreters;
94108
private final Set<Interpreter> _busyInterpreters;
109+
private final JShell _js;
95110
// The following variable appears to be useless.
96111
// private final Map<String, Pair<TypeContext, RuntimeBindings>> _environments;
97112

98113
private final ClassPathManager _classPathManager;
99114
private final ClassLoader _interpreterLoader;
115+
private StringBuilder output_buf;
116+
private StringBuilder input_buf;
117+
private int num_input_braces;
100118

101119
// Lock object for ensuring mutual exclusion on updates and compound accesses
102120
private final Object _stateLock = new Object();
103121

104122
/** Responsible for running JUnit tests in this JVM. */
105123
private final JUnitTestManager _junitTestManager;
106124

125+
107126
/** Remote reference to the MainJVM class in DrJava's primary JVM. Assigned ONLY once. */
108127
private volatile MainJVMRemoteI _mainJVM;
109-
128+
110129
/** Private constructor; use the singleton ONLY instance. */
111130
private InterpreterJVM() {
112131
super("Reset Interactions Thread", "Poll DrJava Thread");
@@ -122,10 +141,31 @@ private InterpreterJVM() {
122141
// _interpreterOptions = Options.DEFAULT;
123142
_interpreterOptions = new InteractionsPaneOptions();
124143
_defaultInterpreter = new Interpreter(_interpreterOptions, _interpreterLoader);
144+
// _defaultInterpreter =
125145
_interpreters = new HashMap<String,Interpreter>();
126146
_busyInterpreters = new HashSet<Interpreter>();
127147
// _environments = new HashMap<String, Pair<TypeContext, RuntimeBindings>>();
128148
_activeInterpreter = Pair.make("", _defaultInterpreter);
149+
150+
// Create a StringBuilder to capture the JShell Output
151+
output_buf = new StringBuilder();
152+
input_buf = new StringBuilder();
153+
num_input_braces = 0;
154+
// Create a PrintStream that will write to our StringBuilder
155+
PrintStream printStream = new PrintStream(new OutputStream() {
156+
@Override
157+
public void write(int b) {
158+
output_buf.append((char) b);
159+
}
160+
});
161+
162+
try {
163+
_js = JShell.builder().out(printStream).err(printStream).build();
164+
} catch(IllegalStateException e) {
165+
//Potentially exit system or try to create a new JShell instance
166+
System.err.println("JShell is not available in this environment");
167+
throw new RuntimeException(e);
168+
}
129169
}
130170

131171
/** Actions to perform when this JVM is started (through its superclass, AbstractSlaveJVM). Not synchronized
@@ -144,11 +184,12 @@ protected String _getInput() {
144184
}
145185
}
146186
});
147-
148-
// redirect stdout
187+
149188
System.setOut(new PrintStream(new OutputStreamRedirector() {
150189
public void print(String s) {
151-
try { _mainJVM.systemOutPrint(s); }
190+
try {
191+
_mainJVM.systemOutPrint(s);
192+
}
152193
catch (RemoteException re) {
153194
error.log(re);
154195
throw new UnexpectedException("Main JVM can't be reached for output.\n" + re);
@@ -213,7 +254,9 @@ private boolean isBusyInterpreter(Interpreter i) {
213254
* local state.
214255
* @param s Source code to interpret.
215256
*/
216-
public InterpretResult interpret(String s) { return interpret(s, _activeInterpreter.second()); }
257+
public InterpretResult interpret(String s) {
258+
return interpret(s, _activeInterpreter.second());
259+
}
217260

218261
/** Interprets the given string of source code with the given interpreter. The result is returned to
219262
* MainJVM via the interpretResult method.
@@ -229,48 +272,87 @@ public InterpretResult interpret(String s, String name) {
229272
}
230273
return interpret(s, i);
231274
}
232-
275+
276+
/**
277+
* Interprets the given string of code with an instance of JShell and returns the result wrapped in a InterpretResult object
278+
* Currently does NOT support differentiation between different types of results, only treats them as objects.
279+
* Currently does NOT support printing of basic expressions, i.e. "2 + 2" will not print 4 (but it should)
280+
* @param code - the code to be interpreted
281+
* @return InterpretResult - the result of the interpretation
282+
* @throws InterpreterException - in the case the JShell instance is not available or an error occurs during interpretation
283+
*/
284+
private InterpretResult interpretWithJShell(StringBuilder input_buf) throws InterpreterException {
285+
InterpretResult res = null;
286+
//TD set verbosity level so it doesn't freak out over things like semicolons
287+
if (_js == null) {
288+
//TODO create InterpretResultException here
289+
return InterpretResult.exception(new EvaluatorException(new Throwable("An error has occured that has caused the Interpreter to not be available.")));
290+
}
291+
292+
// System.out.println("Evalulating: " + input_buf.toString());
293+
List<SnippetEvent> events = _js.eval(input_buf.toString());
294+
//getting rid of new line character at the end
295+
if (output_buf.length() > 0 && output_buf.charAt(output_buf.length() - 1) == '\n') {
296+
output_buf.setLength(output_buf.length() - 1);
297+
}
298+
299+
//TODO Jshell adds a lot of fluff to the output, try and only get the cause of the error
300+
for (SnippetEvent e : events) {
301+
if (e.status() == Snippet.Status.REJECTED) {
302+
Diag diagnostics = _js.diagnostics(e.snippet()).collect(Collectors.toList()).get(0);
303+
output_buf.append(diagnostics.getMessage(Locale.getDefault()));
304+
res = InterpretResult.exception(new EvaluatorException(new Throwable(output_buf.toString())));
305+
}
306+
}
307+
308+
if (res == null) {
309+
//Setting InterpretResult as an object value rather than doing case work as JShell will internally keep track of state, bypassing need for us to do so
310+
res = InterpretResult.objectValue(output_buf.toString(), "JShellOutput");
311+
}
312+
313+
output_buf.setLength(0);
314+
//clear input after setting multiple lines
315+
input_buf.setLength(0);
316+
return res;
317+
}
318+
233319
private InterpretResult interpret(String input, Interpreter interpreter) {
234-
debug.logStart("Interpret " + input);
235-
320+
// System.out.println("Received input: " + input);
236321
boolean available = addBusyInterpreter(interpreter);
237322
if (! available) { debug.logEnd(); return InterpretResult.busy(); }
238-
323+
239324
// set the thread context class loader, this way NextGen and Mint can use the interpreter's class loader
240325
Thread.currentThread().setContextClassLoader(_interpreterLoader); // _interpreterLoader is final
241-
242-
Option<Object> result = null;
243-
try { result = interpreter.interpret(input); }
326+
InterpretResult result = null;
327+
328+
//Branching based on state of input_buffer to decide if we shoudl evalutate or not (i.e. when we are in a multi-line statement)
329+
input_buf.append(input);
330+
if (input.trim().endsWith("{")) {
331+
num_input_braces += 1;
332+
}
333+
334+
if (num_input_braces > 0) {
335+
if (input.endsWith("}")) {
336+
num_input_braces -= 1;
337+
}
338+
}
339+
340+
341+
try {
342+
if (num_input_braces == 0) {
343+
result = interpretWithJShell(input_buf);
344+
}
345+
}
244346
catch (InterpreterException e) { debug.logEnd(); return InterpretResult.exception(e); }
245347
catch (Throwable e) { debug.logEnd(); return InterpretResult.unexpectedException(e); }
246348
finally { removeBusyInterpreter(interpreter); }
247-
248-
return result.apply(new OptionVisitor<Object, InterpretResult>() {
249-
public InterpretResult forNone() { return InterpretResult.noValue(); }
250-
public InterpretResult forSome(Object obj) {
251-
if (obj instanceof String) { debug.logEnd(); return InterpretResult.stringValue((String) obj); }
252-
else if (obj instanceof Character) { debug.logEnd(); return InterpretResult.charValue((Character) obj); }
253-
else if (obj instanceof Number) { debug.logEnd(); return InterpretResult.numberValue((Number) obj); }
254-
else if (obj instanceof Boolean) { debug.logEnd(); return InterpretResult.booleanValue((Boolean) obj); }
255-
else {
256-
try {
257-
String resultString = TextUtil.toString(obj);
258-
String resultTypeStr = null;
259-
if (obj!=null) {
260-
Class<?> c = obj.getClass();
261-
resultTypeStr = getClassName(c);
262-
}
263-
debug.logEnd();
264-
return InterpretResult.objectValue(resultString,resultTypeStr);
265-
}
266-
catch (Throwable t) {
267-
// an exception occurred during toString
268-
debug.logEnd();
269-
return InterpretResult.exception(new EvaluatorException(t));
270-
}
271-
}
272-
}
273-
});
349+
350+
if (result == null) {
351+
//TODO create something more robust here
352+
return InterpretResult.noValue();
353+
}
354+
355+
return result;
274356
}
275357

276358
/** Gets the value of the variable with the given name in the current interpreter.
@@ -533,7 +615,9 @@ public List<String> findTestClasses(List<String> classNames,
533615
* and does not involve mutable local state.
534616
* @return false if no test suite is cached; true otherwise
535617
*/
536-
public boolean runTestSuite() throws RemoteException { return _junitTestManager.runTestSuite(); }
618+
public boolean runTestSuite() throws RemoteException {
619+
return _junitTestManager.runTestSuite();
620+
}
537621

538622
/** Notifies Main JVM that JUnit has been invoked on a non TestCase class. Unsynchronized because it contains a
539623
* remote call and does not involve mutable local state.

0 commit comments

Comments
 (0)