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+
2937package edu .rice .cs .drjava .model .repl .newjvm ;
3038
3139import java .util .*;
6169import edu .rice .cs .dynamicjava .symbol .*;
6270import 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
6579import javax .swing .JDialog ;
6680
7892 * @version $Id$
7993 */
8094public 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