8029364: NashornException to expose thrown object
authorsundar
Mon, 02 Dec 2013 18:19:26 +0530
changeset 22370 c150d042ffff
parent 22369 4771a8ce9037
child 22371 e6b607104dc6
8029364: NashornException to expose thrown object Reviewed-by: lagergren, jlaskey
nashorn/src/jdk/nashorn/api/scripting/NashornException.java
nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java
nashorn/test/script/basic/JDK-8029364.js
nashorn/test/script/basic/JDK-8029364.js.EXPECTED
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornException.java	Wed Nov 27 14:13:52 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornException.java	Mon Dec 02 18:19:26 2013 +0530
@@ -29,6 +29,7 @@
 import java.util.List;
 import jdk.nashorn.internal.codegen.CompilerConstants;
 import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.ScriptObject;
 
 /**
  * This is base exception for all Nashorn exceptions. These originate from
@@ -49,6 +50,8 @@
     private final int line;
     // script column number
     private final int column;
+    // underlying ECMA error object - lazily initialized
+    private Object ecmaError;
 
     /** script source name used for "engine.js" */
     public static final String ENGINE_SCRIPT_SOURCE_NAME = "nashorn:engine/resources/engine.js";
@@ -188,4 +191,33 @@
         }
         return buf.toString();
     }
+
+    protected Object getThrown() {
+        return null;
+    }
+
+    protected NashornException initEcmaError(final ScriptObject global) {
+        if (ecmaError != null) {
+            return this; // initialized already!
+        }
+
+        final Object thrown = getThrown();
+        if (thrown instanceof ScriptObject) {
+            ecmaError = ScriptObjectMirror.wrap(thrown, global);
+        } else {
+            ecmaError = thrown;
+        }
+
+        return this;
+    }
+
+    /**
+     * Return the underlying ECMA error object, if available.
+     *
+     * @return underlying ECMA Error object's mirror or whatever was thrown
+     *         from script such as a String, Number or a Boolean.
+     */
+    public Object getEcmaError() {
+        return ecmaError;
+    }
 }
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Wed Nov 27 14:13:52 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Mon Dec 02 18:19:26 2013 +0530
@@ -476,16 +476,19 @@
     private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
         name.getClass(); // null check
 
+        ScriptObject invokeGlobal = null;
         ScriptObjectMirror selfMirror = null;
         if (selfObject instanceof ScriptObjectMirror) {
             selfMirror = (ScriptObjectMirror)selfObject;
             if (! isOfContext(selfMirror.getHomeGlobal(), nashornContext)) {
                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
             }
+            invokeGlobal = selfMirror.getHomeGlobal();
         } else if (selfObject instanceof ScriptObject) {
             // invokeMethod called from script code - in which case we may get 'naked' ScriptObject
             // Wrap it with oldGlobal to make a ScriptObjectMirror for the same.
             final ScriptObject oldGlobal = Context.getGlobal();
+            invokeGlobal = oldGlobal;
             if (oldGlobal == null) {
                 throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
             }
@@ -498,6 +501,7 @@
         } else if (selfObject == null) {
             // selfObject is null => global function call
             final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);
+            invokeGlobal = ctxtGlobal;
             selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal);
         }
 
@@ -509,7 +513,7 @@
                 if (cause instanceof NoSuchMethodException) {
                     throw (NoSuchMethodException)cause;
                 }
-                throwAsScriptException(e);
+                throwAsScriptException(e, invokeGlobal);
                 throw new AssertionError("should not reach here");
             }
         }
@@ -543,7 +547,7 @@
             }
             return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
         } catch (final Exception e) {
-            throwAsScriptException(e);
+            throwAsScriptException(e, ctxtGlobal);
             throw new AssertionError("should not reach here");
         } finally {
             if (globalChanged) {
@@ -552,7 +556,7 @@
         }
     }
 
-    private static void throwAsScriptException(final Exception e) throws ScriptException {
+    private static void throwAsScriptException(final Exception e, final ScriptObject global) throws ScriptException {
         if (e instanceof ScriptException) {
             throw (ScriptException)e;
         } else if (e instanceof NashornException) {
@@ -560,6 +564,7 @@
             final ScriptException se = new ScriptException(
                 ne.getMessage(), ne.getFileName(),
                 ne.getLineNumber(), ne.getColumnNumber());
+            ne.initEcmaError(global);
             se.initCause(e);
             throw se;
         } else if (e instanceof RuntimeException) {
@@ -605,7 +610,7 @@
 
             return nashornContext.compileScript(source, newGlobal);
         } catch (final Exception e) {
-            throwAsScriptException(e);
+            throwAsScriptException(e, newGlobal);
             throw new AssertionError("should not reach here");
         } finally {
             if (globalChanged) {
--- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Wed Nov 27 14:13:52 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Mon Dec 02 18:19:26 2013 +0530
@@ -108,6 +108,8 @@
             }
 
             throw new RuntimeException("not a function: " + toString());
+        } catch (final NashornException ne) {
+            throw ne.initEcmaError(global);
         } catch (final RuntimeException | Error e) {
             throw e;
         } catch (final Throwable t) {
@@ -135,6 +137,8 @@
             }
 
             throw new RuntimeException("not a constructor: " + toString());
+        } catch (final NashornException ne) {
+            throw ne.initEcmaError(global);
         } catch (final RuntimeException | Error e) {
             throw e;
         } catch (final Throwable t) {
@@ -182,6 +186,8 @@
             }
 
             throw new NoSuchMethodException("No such function " + functionName);
+        } catch (final NashornException ne) {
+            throw ne.initEcmaError(global);
         } catch (final RuntimeException | Error e) {
             throw e;
         } catch (final Throwable t) {
@@ -717,6 +723,8 @@
         }
         try {
             return callable.call();
+        } catch (final NashornException ne) {
+            throw ne.initEcmaError(global);
         } catch (final RuntimeException e) {
             throw e;
         } catch (final Exception e) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java	Wed Nov 27 14:13:52 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ECMAException.java	Mon Dec 02 18:19:26 2013 +0530
@@ -88,6 +88,7 @@
      * Get the thrown object
      * @return thrown object
      */
+    @Override
     public Object getThrown() {
         return thrown;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8029364.js	Mon Dec 02 18:19:26 2013 +0530
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8029364: NashornException to expose thrown object
+ *
+ * @test
+ * @run
+ */
+
+var m = new javax.script.ScriptEngineManager();
+var e = m.getEngineByName("nashorn");
+var g = e.eval("this");
+try {
+    e.eval("var e = new Error('foo'); e.bar = 33; throw e");
+} catch (se) {
+    // ScriptException instance's cause is a NashornException
+    print(se.getClass());
+    var cause = se.cause;
+    print(cause.getClass());
+    // NashornException instance has 'ecmaError' bean getter
+    print(cause.ecmaError);
+    // access to underlying ECMA Error object
+    print(cause.ecmaError instanceof g.Error);
+    print(cause.ecmaError.name);
+    print(cause.ecmaError.message);
+    print(cause.ecmaError.bar);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8029364.js.EXPECTED	Mon Dec 02 18:19:26 2013 +0530
@@ -0,0 +1,7 @@
+class javax.script.ScriptException
+class jdk.nashorn.internal.runtime.ECMAException
+Error: foo
+true
+Error
+foo
+33