1 /* 2 * Copyright (c) 2010, 2013, 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 26 package jdk.nashorn.api.scripting; 27 28 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 30 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.InputStreamReader; 34 import java.io.Reader; 35 import java.lang.invoke.MethodHandles; 36 import java.lang.reflect.Method; 37 import java.lang.reflect.Modifier; 38 import java.net.URL; 39 import java.nio.charset.Charset; 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) { 123 throw new RuntimeException("no message resource found for message id: "+ msgId); 124 } 125 } 126 127 // load engine.js and return content as a char[] 128 @SuppressWarnings("resource") 129 private static char[] loadEngineJSSource() { 130 final String script = "resources/engine.js"; 131 try { 132 final InputStream is = AccessController.doPrivileged( 133 new PrivilegedExceptionAction<InputStream>() { 134 @Override 135 public InputStream run() throws Exception { 136 final URL url = NashornScriptEngine.class.getResource(script); 137 return url.openStream(); 138 } 139 }); 140 return Source.readFully(new InputStreamReader(is)); 141 } catch (final PrivilegedActionException | IOException e) { 142 if (Context.DEBUG) { 143 e.printStackTrace(); 144 } 145 throw new RuntimeException(e); 146 } 147 } 148 149 // Source object for engine.js 150 private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource()); 151 152 NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) { 153 this(factory, DEFAULT_OPTIONS, appLoader); 154 } 155 156 NashornScriptEngine(final NashornScriptEngineFactory factory, final String[] args, final ClassLoader appLoader) { 157 this.factory = factory; 158 final Options options = new Options("nashorn"); 159 options.process(args); 160 161 // throw ParseException on first error from script 162 final ErrorManager errMgr = new Context.ThrowErrorManager(); 163 // create new Nashorn Context 164 this.nashornContext = AccessController.doPrivileged(new PrivilegedAction<Context>() { 165 @Override 166 public Context run() { 167 try { 168 return new Context(options, errMgr, appLoader); 169 } catch (final RuntimeException e) { 170 if (Context.DEBUG) { 171 e.printStackTrace(); 172 } 173 throw e; 174 } 175 } 176 }, CREATE_CONTEXT_ACC_CTXT); 177 178 // cache this option that is used often 179 this._global_per_engine = nashornContext.getEnv()._global_per_engine; 180 181 // create new global object 182 this.global = createNashornGlobal(context); 183 // set the default ENGINE_SCOPE object for the default context 184 context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); 185 } 186 187 @Override 188 public Object eval(final Reader reader, final ScriptContext ctxt) throws ScriptException { 189 return evalImpl(makeSource(reader, ctxt), ctxt); 190 } 191 192 @Override 193 public Object eval(final String script, final ScriptContext ctxt) throws ScriptException { 194 return evalImpl(makeSource(script, ctxt), ctxt); 195 } 196 197 @Override 198 public ScriptEngineFactory getFactory() { 199 return factory; 200 } 201 202 @Override 203 public Bindings createBindings() { 204 if (_global_per_engine) { 205 // just create normal SimpleBindings. 206 // We use same 'global' for all Bindings. 207 return new SimpleBindings(); 208 } 209 return createGlobalMirror(null); 210 } 211 212 // Compilable methods 213 214 @Override 215 public CompiledScript compile(final Reader reader) throws ScriptException { 216 return asCompiledScript(makeSource(reader, context)); 217 } 218 219 @Override 220 public CompiledScript compile(final String str) throws ScriptException { 221 return asCompiledScript(makeSource(str, context)); 222 } 223 224 // Invocable methods 225 226 @Override 227 public Object invokeFunction(final String name, final Object... args) 228 throws ScriptException, NoSuchMethodException { 229 return invokeImpl(null, name, args); 230 } 231 232 @Override 233 public Object invokeMethod(final Object thiz, final String name, final Object... args) 234 throws ScriptException, NoSuchMethodException { 235 if (thiz == null) { 236 throw new IllegalArgumentException(getMessage("thiz.cannot.be.null")); 237 } 238 return invokeImpl(thiz, name, args); 239 } 240 241 @Override 242 public <T> T getInterface(final Class<T> clazz) { 243 return getInterfaceInner(null, clazz); 244 } 245 246 @Override 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(); 288 return new Source(url.toString(), url, cs); 289 } 290 return new Source(getScriptName(ctxt), Source.readFully(reader)); 291 } catch (final IOException e) { 292 throw new ScriptException(e); 293 } 294 } 295 296 private static Source makeSource(final String src, final ScriptContext ctxt) { 297 return new Source(getScriptName(ctxt), src); 298 } 299 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 ScriptObject 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 ScriptObject 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 ScriptObject 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 ScriptObject ctxtGlobal) throws ScriptException { 536 if (script == null) { 537 return null; 538 } 539 final ScriptObject 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 ScriptObject 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 ScriptObject 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 ScriptObject newGlobal) throws ScriptException { 606 final ScriptObject 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 ScriptObject global, final Context context) { 645 assert global instanceof GlobalObject: "Not a Global object"; 646 return ((GlobalObject)global).isOfContext(context); 647 } 648 }