src/jdk/nashorn/api/scripting/NashornScriptEngine.java

Print this page
rev 737 : 8029364: NashornException to expose thrown object
Reviewed-by: lagergren, jlaskey
rev 744 : 8031359: Invocable.getInterface() works incorrectly if interface has default methods
Reviewed-by: attila, hannesw
rev 760 : 8037400: Remove getInitialMap getters and GlobalObject interface
Reviewed-by: lagergren, jlaskey, attila


  40 import java.security.AccessControlContext;
  41 import java.security.AccessController;
  42 import java.security.Permissions;
  43 import java.security.PrivilegedAction;
  44 import java.security.PrivilegedActionException;
  45 import java.security.PrivilegedExceptionAction;
  46 import java.security.ProtectionDomain;
  47 import java.text.MessageFormat;
  48 import java.util.Locale;
  49 import java.util.ResourceBundle;
  50 import javax.script.AbstractScriptEngine;
  51 import javax.script.Bindings;
  52 import javax.script.Compilable;
  53 import javax.script.CompiledScript;
  54 import javax.script.Invocable;
  55 import javax.script.ScriptContext;
  56 import javax.script.ScriptEngine;
  57 import javax.script.ScriptEngineFactory;
  58 import javax.script.ScriptException;
  59 import javax.script.SimpleBindings;

  60 import jdk.nashorn.internal.runtime.Context;
  61 import jdk.nashorn.internal.runtime.ErrorManager;
  62 import jdk.nashorn.internal.runtime.GlobalObject;
  63 import jdk.nashorn.internal.runtime.Property;
  64 import jdk.nashorn.internal.runtime.ScriptFunction;
  65 import jdk.nashorn.internal.runtime.ScriptObject;
  66 import jdk.nashorn.internal.runtime.ScriptRuntime;
  67 import jdk.nashorn.internal.runtime.Source;
  68 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
  69 import jdk.nashorn.internal.runtime.options.Options;
  70 
  71 /**
  72  * JSR-223 compliant script engine for Nashorn. Instances are not created directly, but rather returned through
  73  * {@link NashornScriptEngineFactory#getScriptEngine()}. Note that this engine implements the {@link Compilable} and
  74  * {@link Invocable} interfaces, allowing for efficient precompilation and repeated execution of scripts.
  75  * @see NashornScriptEngineFactory
  76  */
  77 
  78 public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
  79     /**
  80      * Key used to associate Nashorn global object mirror with arbitrary Bindings instance.
  81      */
  82     public static final String NASHORN_GLOBAL = "nashorn.global";
  83 
  84     // commonly used access control context objects
  85     private static AccessControlContext createPermAccCtxt(final String permName) {
  86         final Permissions perms = new Permissions();
  87         perms.add(new RuntimePermission(permName));
  88         return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
  89     }
  90 
  91     private static final AccessControlContext CREATE_CONTEXT_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_CONTEXT);
  92     private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT  = createPermAccCtxt(Context.NASHORN_CREATE_GLOBAL);
  93 
  94     // the factory that created this engine
  95     private final ScriptEngineFactory factory;
  96     // underlying nashorn Context - 1:1 with engine instance
  97     private final Context             nashornContext;
  98     // do we want to share single Nashorn global instance across ENGINE_SCOPEs?
  99     private final boolean             _global_per_engine;
 100     // This is the initial default Nashorn global object.
 101     // This is used as "shared" global if above option is true.
 102     private final ScriptObject        global;
 103     // initialized bit late to be made 'final'.
 104     // Property object for "context" property of global object.
 105     private volatile Property         contextProperty;
 106 
 107     // default options passed to Nashorn Options object
 108     private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
 109 
 110     // Nashorn script engine error message management
 111     private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages";
 112 
 113     private static final ResourceBundle MESSAGES_BUNDLE;
 114     static {
 115         MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault());
 116     }
 117 
 118     // helper to get Nashorn script engine error message
 119     private static String getMessage(final String msgId, final String... args) {
 120         try {
 121             return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args);
 122         } catch (final java.util.MissingResourceException e) {


 247     public <T> T getInterface(final Object thiz, final Class<T> clazz) {
 248         if (thiz == null) {
 249             throw new IllegalArgumentException(getMessage("thiz.cannot.be.null"));
 250         }
 251         return getInterfaceInner(thiz, clazz);
 252     }
 253 
 254     // These are called from the "engine.js" script
 255 
 256     /**
 257      * This hook is used to search js global variables exposed from Java code.
 258      *
 259      * @param self 'this' passed from the script
 260      * @param ctxt current ScriptContext in which name is searched
 261      * @param name name of the variable searched
 262      * @return the value of the named variable
 263      */
 264     public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
 265         if (ctxt != null) {
 266             final int scope = ctxt.getAttributesScope(name);
 267             final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt);
 268             if (scope != -1) {
 269                 return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
 270             }
 271 
 272             if (self == UNDEFINED) {
 273                 // scope access and so throw ReferenceError
 274                 throw referenceError(ctxtGlobal, "not.defined", name);
 275             }
 276         }
 277 
 278         return UNDEFINED;
 279     }
 280 
 281     // Implementation only below this point
 282 
 283     private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
 284         try {
 285             if (reader instanceof URLReader) {
 286                 final URL url = ((URLReader)reader).getURL();
 287                 final Charset cs = ((URLReader)reader).getCharset();


 300     private static String getScriptName(final ScriptContext ctxt) {
 301         final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
 302         return (val != null) ? val.toString() : "<eval>";
 303     }
 304 
 305     private <T> T getInterfaceInner(final Object thiz, final Class<T> clazz) {
 306         if (clazz == null || !clazz.isInterface()) {
 307             throw new IllegalArgumentException(getMessage("interface.class.expected"));
 308         }
 309 
 310         // perform security access check as early as possible
 311         final SecurityManager sm = System.getSecurityManager();
 312         if (sm != null) {
 313             if (! Modifier.isPublic(clazz.getModifiers())) {
 314                 throw new SecurityException(getMessage("implementing.non.public.interface", clazz.getName()));
 315             }
 316             Context.checkPackageAccess(clazz);
 317         }
 318 
 319         ScriptObject realSelf = null;
 320         ScriptObject realGlobal = null;
 321         if(thiz == null) {
 322             // making interface out of global functions
 323             realSelf = realGlobal = getNashornGlobalFrom(context);
 324         } else if (thiz instanceof ScriptObjectMirror) {
 325             final ScriptObjectMirror mirror = (ScriptObjectMirror)thiz;
 326             realSelf = mirror.getScriptObject();
 327             realGlobal = mirror.getHomeGlobal();
 328             if (! isOfContext(realGlobal, nashornContext)) {
 329                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 330             }
 331         } else if (thiz instanceof ScriptObject) {
 332             // called from script code.
 333             realSelf = (ScriptObject)thiz;
 334             realGlobal = Context.getGlobal();
 335             if (realGlobal == null) {
 336                 throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
 337             }
 338 
 339             if (! isOfContext(realGlobal, nashornContext)) {
 340                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 341             }
 342         }
 343 
 344         if (realSelf == null) {
 345             throw new IllegalArgumentException(getMessage("interface.on.non.script.object"));
 346         }
 347 
 348         try {
 349             final ScriptObject oldGlobal = Context.getGlobal();
 350             final boolean globalChanged = (oldGlobal != realGlobal);
 351             try {
 352                 if (globalChanged) {
 353                     Context.setGlobal(realGlobal);
 354                 }
 355 
 356                 if (! isInterfaceImplemented(clazz, realSelf)) {
 357                     return null;
 358                 }
 359                 return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz,
 360                         MethodHandles.publicLookup()).invoke(realSelf));
 361             } finally {
 362                 if (globalChanged) {
 363                     Context.setGlobal(oldGlobal);
 364                 }
 365             }
 366         } catch(final RuntimeException|Error e) {
 367             throw e;
 368         } catch(final Throwable t) {
 369             throw new RuntimeException(t);
 370         }
 371     }
 372 
 373     // Retrieve nashorn Global object for a given ScriptContext object
 374     private ScriptObject getNashornGlobalFrom(final ScriptContext ctxt) {
 375         if (_global_per_engine) {
 376             // shared single global object for all ENGINE_SCOPE Bindings
 377             return global;
 378         }
 379 
 380         final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE);
 381         // is this Nashorn's own Bindings implementation?
 382         if (bindings instanceof ScriptObjectMirror) {
 383             final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)bindings);
 384             if (sobj != null) {
 385                 return sobj;
 386             }
 387         }
 388 
 389         // Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it!
 390         Object scope = bindings.get(NASHORN_GLOBAL);
 391         if (scope instanceof ScriptObjectMirror) {
 392             final ScriptObject sobj = globalFromMirror((ScriptObjectMirror)scope);
 393             if (sobj != null) {
 394                 return sobj;
 395             }
 396         }
 397 
 398         // We didn't find associated nashorn global mirror in the Bindings given!
 399         // Create new global instance mirror and associate with the Bindings.
 400         final ScriptObjectMirror mirror = createGlobalMirror(ctxt);
 401         bindings.put(NASHORN_GLOBAL, mirror);
 402         return mirror.getScriptObject();
 403     }
 404 
 405     // Retrieve nashorn Global object from a given ScriptObjectMirror
 406     private ScriptObject globalFromMirror(final ScriptObjectMirror mirror) {
 407         ScriptObject sobj = mirror.getScriptObject();
 408         if (sobj instanceof GlobalObject && isOfContext(sobj, nashornContext)) {
 409             return sobj;
 410         }
 411 
 412         return null;
 413     }
 414 
 415     // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object
 416     private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) {
 417         final ScriptObject newGlobal = createNashornGlobal(ctxt);
 418         return new ScriptObjectMirror(newGlobal, newGlobal);
 419     }
 420 
 421     // Create a new Nashorn Global object
 422     private ScriptObject createNashornGlobal(final ScriptContext ctxt) {
 423         final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() {
 424             @Override
 425             public ScriptObject run() {
 426                 try {
 427                     return nashornContext.newGlobal();
 428                 } catch (final RuntimeException e) {
 429                     if (Context.DEBUG) {
 430                         e.printStackTrace();
 431                     }
 432                     throw e;
 433                 }
 434             }
 435         }, CREATE_GLOBAL_ACC_CTXT);
 436 
 437         nashornContext.initGlobal(newGlobal);
 438 
 439         final int NON_ENUMERABLE_CONSTANT = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE;
 440         // current ScriptContext exposed as "context"
 441         // "context" is non-writable from script - but script engine still
 442         // needs to set it and so save the context Property object
 443         contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, ctxt);
 444         // current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
 445         // NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
 446         // in the Global of a Context we just created - both the Context and the Global were just created and can not be
 447         // seen from another thread outside of this constructor.
 448         newGlobal.addOwnProperty("engine", NON_ENUMERABLE_CONSTANT, this);
 449         // global script arguments with undefined value
 450         newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
 451         // file name default is null
 452         newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null);
 453         // evaluate engine.js initialization script this new global object
 454         try {
 455             evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal);
 456         } catch (final ScriptException exp) {
 457             throw new RuntimeException(exp);
 458         }
 459         return newGlobal;
 460     }
 461 
 462     // scripts should see "context" and "engine" as variables in the given global object
 463     private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) {
 464         // set "context" global variable via contextProperty - because this
 465         // property is non-writable
 466         contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
 467         Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
 468         if (args == null || args == UNDEFINED) {
 469             args = ScriptRuntime.EMPTY_ARRAY;
 470         }
 471         // if no arguments passed, expose it
 472         if (! (args instanceof ScriptObject)) {
 473             args = ((GlobalObject)ctxtGlobal).wrapAsObject(args);
 474             ctxtGlobal.set("arguments", args, false);
 475         }
 476     }
 477 
 478     private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
 479         name.getClass(); // null check
 480 

 481         ScriptObjectMirror selfMirror = null;
 482         if (selfObject instanceof ScriptObjectMirror) {
 483             selfMirror = (ScriptObjectMirror)selfObject;
 484             if (! isOfContext(selfMirror.getHomeGlobal(), nashornContext)) {
 485                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 486             }

 487         } else if (selfObject instanceof ScriptObject) {
 488             // invokeMethod called from script code - in which case we may get 'naked' ScriptObject
 489             // Wrap it with oldGlobal to make a ScriptObjectMirror for the same.
 490             final ScriptObject oldGlobal = Context.getGlobal();

 491             if (oldGlobal == null) {
 492                 throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
 493             }
 494 
 495             if (! isOfContext(oldGlobal, nashornContext)) {
 496                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 497             }
 498 
 499             selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal);
 500         } else if (selfObject == null) {
 501             // selfObject is null => global function call
 502             final ScriptObject ctxtGlobal = getNashornGlobalFrom(context);

 503             selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal);
 504         }
 505 
 506         if (selfMirror != null) {
 507             try {
 508                 return ScriptObjectMirror.translateUndefined(selfMirror.callMember(name, args));
 509             } catch (final Exception e) {
 510                 final Throwable cause = e.getCause();
 511                 if (cause instanceof NoSuchMethodException) {
 512                     throw (NoSuchMethodException)cause;
 513                 }
 514                 throwAsScriptException(e);
 515                 throw new AssertionError("should not reach here");
 516             }
 517         }
 518 
 519         // Non-script object passed as selfObject
 520         throw new IllegalArgumentException(getMessage("interface.on.non.script.object"));
 521     }
 522 
 523     private Object evalImpl(final Source src, final ScriptContext ctxt) throws ScriptException {
 524         return evalImpl(compileImpl(src, ctxt), ctxt);
 525     }
 526 
 527     private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException {
 528         return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt));
 529     }
 530 
 531     private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final ScriptObject ctxtGlobal) throws ScriptException {
 532         if (script == null) {
 533             return null;
 534         }
 535         final ScriptObject oldGlobal = Context.getGlobal();
 536         final boolean globalChanged = (oldGlobal != ctxtGlobal);
 537         try {
 538             if (globalChanged) {
 539                 Context.setGlobal(ctxtGlobal);
 540             }
 541 
 542             // set ScriptContext variables if ctxt is non-null
 543             if (ctxt != null) {
 544                 setContextVariables(ctxtGlobal, ctxt);
 545             }
 546             return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
 547         } catch (final Exception e) {
 548             throwAsScriptException(e);
 549             throw new AssertionError("should not reach here");
 550         } finally {
 551             if (globalChanged) {
 552                 Context.setGlobal(oldGlobal);
 553             }
 554         }
 555     }
 556 
 557     private static void throwAsScriptException(final Exception e) throws ScriptException {
 558         if (e instanceof ScriptException) {
 559             throw (ScriptException)e;
 560         } else if (e instanceof NashornException) {
 561             final NashornException ne = (NashornException)e;
 562             final ScriptException se = new ScriptException(
 563                 ne.getMessage(), ne.getFileName(),
 564                 ne.getLineNumber(), ne.getColumnNumber());

 565             se.initCause(e);
 566             throw se;
 567         } else if (e instanceof RuntimeException) {
 568             throw (RuntimeException)e;
 569         } else {
 570             // wrap any other exception as ScriptException
 571             throw new ScriptException(e);
 572         }
 573     }
 574 
 575     private CompiledScript asCompiledScript(final Source source) throws ScriptException {
 576         final ScriptFunction func = compileImpl(source, context);
 577         return new CompiledScript() {
 578             @Override
 579             public Object eval(final ScriptContext ctxt) throws ScriptException {
 580                 final ScriptObject globalObject = getNashornGlobalFrom(ctxt);
 581                 // Are we running the script in the correct global?
 582                 if (func.getScope() == globalObject) {
 583                     return evalImpl(func, ctxt, globalObject);
 584                 }
 585                 // ScriptContext with a different global. Compile again!
 586                 // Note that we may still hit per-global compilation cache.
 587                 return evalImpl(compileImpl(source, ctxt), ctxt, globalObject);
 588             }
 589             @Override
 590             public ScriptEngine getEngine() {
 591                 return NashornScriptEngine.this;
 592             }
 593         };
 594     }
 595 
 596     private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
 597         return compileImpl(source, getNashornGlobalFrom(ctxt));
 598     }
 599 
 600     private ScriptFunction compileImpl(final Source source, final ScriptObject newGlobal) throws ScriptException {
 601         final ScriptObject oldGlobal = Context.getGlobal();
 602         final boolean globalChanged = (oldGlobal != newGlobal);
 603         try {
 604             if (globalChanged) {
 605                 Context.setGlobal(newGlobal);
 606             }
 607 
 608             return nashornContext.compileScript(source, newGlobal);
 609         } catch (final Exception e) {
 610             throwAsScriptException(e);
 611             throw new AssertionError("should not reach here");
 612         } finally {
 613             if (globalChanged) {
 614                 Context.setGlobal(oldGlobal);
 615             }
 616         }
 617     }
 618 
 619     private static boolean isInterfaceImplemented(final Class<?> iface, final ScriptObject sobj) {
 620         for (final Method method : iface.getMethods()) {
 621             // ignore methods of java.lang.Object class
 622             if (method.getDeclaringClass() == Object.class) {
 623                 continue;
 624             }
 625 





 626             Object obj = sobj.get(method.getName());
 627             if (! (obj instanceof ScriptFunction)) {
 628                 return false;
 629             }
 630         }
 631         return true;
 632     }
 633 
 634     private static boolean isOfContext(final ScriptObject global, final Context context) {
 635         assert global instanceof GlobalObject: "Not a Global object";
 636         return ((GlobalObject)global).isOfContext(context);
 637     }
 638 }


  40 import java.security.AccessControlContext;
  41 import java.security.AccessController;
  42 import java.security.Permissions;
  43 import java.security.PrivilegedAction;
  44 import java.security.PrivilegedActionException;
  45 import java.security.PrivilegedExceptionAction;
  46 import java.security.ProtectionDomain;
  47 import java.text.MessageFormat;
  48 import java.util.Locale;
  49 import java.util.ResourceBundle;
  50 import javax.script.AbstractScriptEngine;
  51 import javax.script.Bindings;
  52 import javax.script.Compilable;
  53 import javax.script.CompiledScript;
  54 import javax.script.Invocable;
  55 import javax.script.ScriptContext;
  56 import javax.script.ScriptEngine;
  57 import javax.script.ScriptEngineFactory;
  58 import javax.script.ScriptException;
  59 import javax.script.SimpleBindings;
  60 import jdk.nashorn.internal.objects.Global;
  61 import jdk.nashorn.internal.runtime.Context;
  62 import jdk.nashorn.internal.runtime.ErrorManager;

  63 import jdk.nashorn.internal.runtime.Property;
  64 import jdk.nashorn.internal.runtime.ScriptFunction;
  65 import jdk.nashorn.internal.runtime.ScriptObject;
  66 import jdk.nashorn.internal.runtime.ScriptRuntime;
  67 import jdk.nashorn.internal.runtime.Source;
  68 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
  69 import jdk.nashorn.internal.runtime.options.Options;
  70 
  71 /**
  72  * JSR-223 compliant script engine for Nashorn. Instances are not created directly, but rather returned through
  73  * {@link NashornScriptEngineFactory#getScriptEngine()}. Note that this engine implements the {@link Compilable} and
  74  * {@link Invocable} interfaces, allowing for efficient precompilation and repeated execution of scripts.
  75  * @see NashornScriptEngineFactory
  76  */
  77 
  78 public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
  79     /**
  80      * Key used to associate Nashorn global object mirror with arbitrary Bindings instance.
  81      */
  82     public static final String NASHORN_GLOBAL = "nashorn.global";
  83 
  84     // commonly used access control context objects
  85     private static AccessControlContext createPermAccCtxt(final String permName) {
  86         final Permissions perms = new Permissions();
  87         perms.add(new RuntimePermission(permName));
  88         return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
  89     }
  90 
  91     private static final AccessControlContext CREATE_CONTEXT_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_CONTEXT);
  92     private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT  = createPermAccCtxt(Context.NASHORN_CREATE_GLOBAL);
  93 
  94     // the factory that created this engine
  95     private final ScriptEngineFactory factory;
  96     // underlying nashorn Context - 1:1 with engine instance
  97     private final Context             nashornContext;
  98     // do we want to share single Nashorn global instance across ENGINE_SCOPEs?
  99     private final boolean             _global_per_engine;
 100     // This is the initial default Nashorn global object.
 101     // This is used as "shared" global if above option is true.
 102     private final Global              global;
 103     // initialized bit late to be made 'final'.
 104     // Property object for "context" property of global object.
 105     private volatile Property         contextProperty;
 106 
 107     // default options passed to Nashorn Options object
 108     private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" };
 109 
 110     // Nashorn script engine error message management
 111     private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages";
 112 
 113     private static final ResourceBundle MESSAGES_BUNDLE;
 114     static {
 115         MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault());
 116     }
 117 
 118     // helper to get Nashorn script engine error message
 119     private static String getMessage(final String msgId, final String... args) {
 120         try {
 121             return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args);
 122         } catch (final java.util.MissingResourceException e) {


 247     public <T> T getInterface(final Object thiz, final Class<T> clazz) {
 248         if (thiz == null) {
 249             throw new IllegalArgumentException(getMessage("thiz.cannot.be.null"));
 250         }
 251         return getInterfaceInner(thiz, clazz);
 252     }
 253 
 254     // These are called from the "engine.js" script
 255 
 256     /**
 257      * This hook is used to search js global variables exposed from Java code.
 258      *
 259      * @param self 'this' passed from the script
 260      * @param ctxt current ScriptContext in which name is searched
 261      * @param name name of the variable searched
 262      * @return the value of the named variable
 263      */
 264     public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
 265         if (ctxt != null) {
 266             final int scope = ctxt.getAttributesScope(name);
 267             final Global ctxtGlobal = getNashornGlobalFrom(ctxt);
 268             if (scope != -1) {
 269                 return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal);
 270             }
 271 
 272             if (self == UNDEFINED) {
 273                 // scope access and so throw ReferenceError
 274                 throw referenceError(ctxtGlobal, "not.defined", name);
 275             }
 276         }
 277 
 278         return UNDEFINED;
 279     }
 280 
 281     // Implementation only below this point
 282 
 283     private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
 284         try {
 285             if (reader instanceof URLReader) {
 286                 final URL url = ((URLReader)reader).getURL();
 287                 final Charset cs = ((URLReader)reader).getCharset();


 300     private static String getScriptName(final ScriptContext ctxt) {
 301         final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
 302         return (val != null) ? val.toString() : "<eval>";
 303     }
 304 
 305     private <T> T getInterfaceInner(final Object thiz, final Class<T> clazz) {
 306         if (clazz == null || !clazz.isInterface()) {
 307             throw new IllegalArgumentException(getMessage("interface.class.expected"));
 308         }
 309 
 310         // perform security access check as early as possible
 311         final SecurityManager sm = System.getSecurityManager();
 312         if (sm != null) {
 313             if (! Modifier.isPublic(clazz.getModifiers())) {
 314                 throw new SecurityException(getMessage("implementing.non.public.interface", clazz.getName()));
 315             }
 316             Context.checkPackageAccess(clazz);
 317         }
 318 
 319         ScriptObject realSelf = null;
 320         Global realGlobal = null;
 321         if(thiz == null) {
 322             // making interface out of global functions
 323             realSelf = realGlobal = getNashornGlobalFrom(context);
 324         } else if (thiz instanceof ScriptObjectMirror) {
 325             final ScriptObjectMirror mirror = (ScriptObjectMirror)thiz;
 326             realSelf = mirror.getScriptObject();
 327             realGlobal = mirror.getHomeGlobal();
 328             if (! isOfContext(realGlobal, nashornContext)) {
 329                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 330             }
 331         } else if (thiz instanceof ScriptObject) {
 332             // called from script code.
 333             realSelf = (ScriptObject)thiz;
 334             realGlobal = Context.getGlobal();
 335             if (realGlobal == null) {
 336                 throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
 337             }
 338 
 339             if (! isOfContext(realGlobal, nashornContext)) {
 340                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 341             }
 342         }
 343 
 344         if (realSelf == null) {
 345             throw new IllegalArgumentException(getMessage("interface.on.non.script.object"));
 346         }
 347 
 348         try {
 349             final Global oldGlobal = Context.getGlobal();
 350             final boolean globalChanged = (oldGlobal != realGlobal);
 351             try {
 352                 if (globalChanged) {
 353                     Context.setGlobal(realGlobal);
 354                 }
 355 
 356                 if (! isInterfaceImplemented(clazz, realSelf)) {
 357                     return null;
 358                 }
 359                 return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz,
 360                         MethodHandles.publicLookup()).invoke(realSelf));
 361             } finally {
 362                 if (globalChanged) {
 363                     Context.setGlobal(oldGlobal);
 364                 }
 365             }
 366         } catch(final RuntimeException|Error e) {
 367             throw e;
 368         } catch(final Throwable t) {
 369             throw new RuntimeException(t);
 370         }
 371     }
 372 
 373     // Retrieve nashorn Global object for a given ScriptContext object
 374     private Global getNashornGlobalFrom(final ScriptContext ctxt) {
 375         if (_global_per_engine) {
 376             // shared single global object for all ENGINE_SCOPE Bindings
 377             return global;
 378         }
 379 
 380         final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE);
 381         // is this Nashorn's own Bindings implementation?
 382         if (bindings instanceof ScriptObjectMirror) {
 383             final Global glob = globalFromMirror((ScriptObjectMirror)bindings);
 384             if (glob != null) {
 385                 return glob;
 386             }
 387         }
 388 
 389         // Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it!
 390         Object scope = bindings.get(NASHORN_GLOBAL);
 391         if (scope instanceof ScriptObjectMirror) {
 392             final Global glob = globalFromMirror((ScriptObjectMirror)scope);
 393             if (glob != null) {
 394                 return glob;
 395             }
 396         }
 397 
 398         // We didn't find associated nashorn global mirror in the Bindings given!
 399         // Create new global instance mirror and associate with the Bindings.
 400         final ScriptObjectMirror mirror = createGlobalMirror(ctxt);
 401         bindings.put(NASHORN_GLOBAL, mirror);
 402         return mirror.getHomeGlobal();
 403     }
 404 
 405     // Retrieve nashorn Global object from a given ScriptObjectMirror
 406     private Global globalFromMirror(final ScriptObjectMirror mirror) {
 407         ScriptObject sobj = mirror.getScriptObject();
 408         if (sobj instanceof Global && isOfContext((Global)sobj, nashornContext)) {
 409             return (Global)sobj;
 410         }
 411 
 412         return null;
 413     }
 414 
 415     // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object
 416     private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) {
 417         final Global newGlobal = createNashornGlobal(ctxt);
 418         return new ScriptObjectMirror(newGlobal, newGlobal);
 419     }
 420 
 421     // Create a new Nashorn Global object
 422     private Global createNashornGlobal(final ScriptContext ctxt) {
 423         final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() {
 424             @Override
 425             public Global run() {
 426                 try {
 427                     return nashornContext.newGlobal();
 428                 } catch (final RuntimeException e) {
 429                     if (Context.DEBUG) {
 430                         e.printStackTrace();
 431                     }
 432                     throw e;
 433                 }
 434             }
 435         }, CREATE_GLOBAL_ACC_CTXT);
 436 
 437         nashornContext.initGlobal(newGlobal);
 438 
 439         final int NON_ENUMERABLE_CONSTANT = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE;
 440         // current ScriptContext exposed as "context"
 441         // "context" is non-writable from script - but script engine still
 442         // needs to set it and so save the context Property object
 443         contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, ctxt);
 444         // current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
 445         // NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
 446         // in the Global of a Context we just created - both the Context and the Global were just created and can not be
 447         // seen from another thread outside of this constructor.
 448         newGlobal.addOwnProperty("engine", NON_ENUMERABLE_CONSTANT, this);
 449         // global script arguments with undefined value
 450         newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
 451         // file name default is null
 452         newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null);
 453         // evaluate engine.js initialization script this new global object
 454         try {
 455             evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal);
 456         } catch (final ScriptException exp) {
 457             throw new RuntimeException(exp);
 458         }
 459         return newGlobal;
 460     }
 461 
 462     // scripts should see "context" and "engine" as variables in the given global object
 463     private void setContextVariables(final Global ctxtGlobal, final ScriptContext ctxt) {
 464         // set "context" global variable via contextProperty - because this
 465         // property is non-writable
 466         contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
 467         Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
 468         if (args == null || args == UNDEFINED) {
 469             args = ScriptRuntime.EMPTY_ARRAY;
 470         }
 471         // if no arguments passed, expose it
 472         if (! (args instanceof ScriptObject)) {
 473             args = ctxtGlobal.wrapAsObject(args);
 474             ctxtGlobal.set("arguments", args, false);
 475         }
 476     }
 477 
 478     private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
 479         name.getClass(); // null check
 480 
 481         Global invokeGlobal = null;
 482         ScriptObjectMirror selfMirror = null;
 483         if (selfObject instanceof ScriptObjectMirror) {
 484             selfMirror = (ScriptObjectMirror)selfObject;
 485             if (! isOfContext(selfMirror.getHomeGlobal(), nashornContext)) {
 486                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 487             }
 488             invokeGlobal = selfMirror.getHomeGlobal();
 489         } else if (selfObject instanceof ScriptObject) {
 490             // invokeMethod called from script code - in which case we may get 'naked' ScriptObject
 491             // Wrap it with oldGlobal to make a ScriptObjectMirror for the same.
 492             final Global oldGlobal = Context.getGlobal();
 493             invokeGlobal = oldGlobal;
 494             if (oldGlobal == null) {
 495                 throw new IllegalArgumentException(getMessage("no.current.nashorn.global"));
 496             }
 497 
 498             if (! isOfContext(oldGlobal, nashornContext)) {
 499                 throw new IllegalArgumentException(getMessage("script.object.from.another.engine"));
 500             }
 501 
 502             selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal);
 503         } else if (selfObject == null) {
 504             // selfObject is null => global function call
 505             final Global ctxtGlobal = getNashornGlobalFrom(context);
 506             invokeGlobal = ctxtGlobal;
 507             selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal);
 508         }
 509 
 510         if (selfMirror != null) {
 511             try {
 512                 return ScriptObjectMirror.translateUndefined(selfMirror.callMember(name, args));
 513             } catch (final Exception e) {
 514                 final Throwable cause = e.getCause();
 515                 if (cause instanceof NoSuchMethodException) {
 516                     throw (NoSuchMethodException)cause;
 517                 }
 518                 throwAsScriptException(e, invokeGlobal);
 519                 throw new AssertionError("should not reach here");
 520             }
 521         }
 522 
 523         // Non-script object passed as selfObject
 524         throw new IllegalArgumentException(getMessage("interface.on.non.script.object"));
 525     }
 526 
 527     private Object evalImpl(final Source src, final ScriptContext ctxt) throws ScriptException {
 528         return evalImpl(compileImpl(src, ctxt), ctxt);
 529     }
 530 
 531     private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException {
 532         return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt));
 533     }
 534 
 535     private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
 536         if (script == null) {
 537             return null;
 538         }
 539         final Global oldGlobal = Context.getGlobal();
 540         final boolean globalChanged = (oldGlobal != ctxtGlobal);
 541         try {
 542             if (globalChanged) {
 543                 Context.setGlobal(ctxtGlobal);
 544             }
 545 
 546             // set ScriptContext variables if ctxt is non-null
 547             if (ctxt != null) {
 548                 setContextVariables(ctxtGlobal, ctxt);
 549             }
 550             return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
 551         } catch (final Exception e) {
 552             throwAsScriptException(e, ctxtGlobal);
 553             throw new AssertionError("should not reach here");
 554         } finally {
 555             if (globalChanged) {
 556                 Context.setGlobal(oldGlobal);
 557             }
 558         }
 559     }
 560 
 561     private static void throwAsScriptException(final Exception e, final Global global) throws ScriptException {
 562         if (e instanceof ScriptException) {
 563             throw (ScriptException)e;
 564         } else if (e instanceof NashornException) {
 565             final NashornException ne = (NashornException)e;
 566             final ScriptException se = new ScriptException(
 567                 ne.getMessage(), ne.getFileName(),
 568                 ne.getLineNumber(), ne.getColumnNumber());
 569             ne.initEcmaError(global);
 570             se.initCause(e);
 571             throw se;
 572         } else if (e instanceof RuntimeException) {
 573             throw (RuntimeException)e;
 574         } else {
 575             // wrap any other exception as ScriptException
 576             throw new ScriptException(e);
 577         }
 578     }
 579 
 580     private CompiledScript asCompiledScript(final Source source) throws ScriptException {
 581         final ScriptFunction func = compileImpl(source, context);
 582         return new CompiledScript() {
 583             @Override
 584             public Object eval(final ScriptContext ctxt) throws ScriptException {
 585                 final Global globalObject = getNashornGlobalFrom(ctxt);
 586                 // Are we running the script in the correct global?
 587                 if (func.getScope() == globalObject) {
 588                     return evalImpl(func, ctxt, globalObject);
 589                 }
 590                 // ScriptContext with a different global. Compile again!
 591                 // Note that we may still hit per-global compilation cache.
 592                 return evalImpl(compileImpl(source, ctxt), ctxt, globalObject);
 593             }
 594             @Override
 595             public ScriptEngine getEngine() {
 596                 return NashornScriptEngine.this;
 597             }
 598         };
 599     }
 600 
 601     private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException {
 602         return compileImpl(source, getNashornGlobalFrom(ctxt));
 603     }
 604 
 605     private ScriptFunction compileImpl(final Source source, final Global newGlobal) throws ScriptException {
 606         final Global oldGlobal = Context.getGlobal();
 607         final boolean globalChanged = (oldGlobal != newGlobal);
 608         try {
 609             if (globalChanged) {
 610                 Context.setGlobal(newGlobal);
 611             }
 612 
 613             return nashornContext.compileScript(source, newGlobal);
 614         } catch (final Exception e) {
 615             throwAsScriptException(e, newGlobal);
 616             throw new AssertionError("should not reach here");
 617         } finally {
 618             if (globalChanged) {
 619                 Context.setGlobal(oldGlobal);
 620             }
 621         }
 622     }
 623 
 624     private static boolean isInterfaceImplemented(final Class<?> iface, final ScriptObject sobj) {
 625         for (final Method method : iface.getMethods()) {
 626             // ignore methods of java.lang.Object class
 627             if (method.getDeclaringClass() == Object.class) {
 628                 continue;
 629             }
 630 
 631             // skip check for default methods - non-abstract, interface methods
 632             if (! Modifier.isAbstract(method.getModifiers())) {
 633                 continue;
 634             }
 635 
 636             Object obj = sobj.get(method.getName());
 637             if (! (obj instanceof ScriptFunction)) {
 638                 return false;
 639             }
 640         }
 641         return true;
 642     }
 643 
 644     private static boolean isOfContext(final Global global, final Context context) {
 645         return global.isOfContext(context);

 646     }
 647 }