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 import static jdk.nashorn.internal.runtime.Source.sourceFor; 31 32 import java.io.IOException; 33 import java.io.Reader; 34 import java.lang.invoke.MethodHandles; 35 import java.lang.reflect.Method; 36 import java.lang.reflect.Modifier; 37 import java.net.URL; 38 import java.security.AccessControlContext; 39 import java.security.AccessController; 40 import java.security.Permissions; 41 import java.security.PrivilegedAction; 42 import java.security.PrivilegedActionException; 43 import java.security.PrivilegedExceptionAction; 44 import java.security.ProtectionDomain; 45 import java.text.MessageFormat; 46 import java.util.Locale; 47 import java.util.ResourceBundle; 48 import javax.script.AbstractScriptEngine; 49 import javax.script.Bindings; 50 import javax.script.Compilable; 51 import javax.script.CompiledScript; 52 import javax.script.Invocable; 53 import javax.script.ScriptContext; 54 import javax.script.ScriptEngine; 55 import javax.script.ScriptEngineFactory; 56 import javax.script.ScriptException; 57 import javax.script.SimpleBindings; 58 import jdk.nashorn.internal.objects.Global; 59 import jdk.nashorn.internal.runtime.Context; 60 import jdk.nashorn.internal.runtime.ErrorManager; 61 import jdk.nashorn.internal.runtime.Property; 62 import jdk.nashorn.internal.runtime.ScriptFunction; 63 import jdk.nashorn.internal.runtime.ScriptObject; 64 import jdk.nashorn.internal.runtime.ScriptRuntime; 65 import jdk.nashorn.internal.runtime.Source; 66 import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory; 67 import jdk.nashorn.internal.runtime.options.Options; 68 69 /** 70 * JSR-223 compliant script engine for Nashorn. Instances are not created directly, but rather returned through 71 * {@link NashornScriptEngineFactory#getScriptEngine()}. Note that this engine implements the {@link Compilable} and 72 * {@link Invocable} interfaces, allowing for efficient precompilation and repeated execution of scripts. 73 * @see NashornScriptEngineFactory 74 */ 75 76 public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable, Invocable { 77 /** 78 * Key used to associate Nashorn global object mirror with arbitrary Bindings instance. 79 */ 80 public static final String NASHORN_GLOBAL = "nashorn.global"; 81 82 // commonly used access control context objects 83 private static AccessControlContext createPermAccCtxt(final String permName) { 84 final Permissions perms = new Permissions(); 85 perms.add(new RuntimePermission(permName)); 86 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 87 } 88 89 private static final AccessControlContext CREATE_CONTEXT_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_CONTEXT); 90 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_GLOBAL); 91 92 // the factory that created this engine 93 private final ScriptEngineFactory factory; 94 // underlying nashorn Context - 1:1 with engine instance 95 private final Context nashornContext; 96 // do we want to share single Nashorn global instance across ENGINE_SCOPEs? 97 private final boolean _global_per_engine; 98 // This is the initial default Nashorn global object. 99 // This is used as "shared" global if above option is true. 100 private final Global global; 101 // initialized bit late to be made 'final'. 102 // Property object for "context" property of global object. 103 private volatile Property contextProperty; 104 105 // default options passed to Nashorn Options object 106 private static final String[] DEFAULT_OPTIONS = new String[] { "-doe" }; 107 108 // Nashorn script engine error message management 109 private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages"; 110 111 private static final ResourceBundle MESSAGES_BUNDLE; 112 static { 113 MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault()); 114 } 115 116 // helper to get Nashorn script engine error message 117 private static String getMessage(final String msgId, final String... args) { 118 try { 119 return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args); 120 } catch (final java.util.MissingResourceException e) { 121 throw new RuntimeException("no message resource found for message id: "+ msgId); 122 } 123 } 124 125 // load engine.js 126 @SuppressWarnings("resource") 127 private static Source loadEngineJSSource() { 128 final String script = "resources/engine.js"; 129 try { 130 return AccessController.doPrivileged( 131 new PrivilegedExceptionAction<Source>() { 132 @Override 133 public Source run() throws IOException { 134 final URL url = NashornScriptEngine.class.getResource(script); 135 return sourceFor(NashornException.ENGINE_SCRIPT_SOURCE_NAME, url); 136 } 137 } 138 ); 139 } catch (final PrivilegedActionException e) { 140 if (Context.DEBUG) { 141 e.printStackTrace(); 142 } 143 throw new RuntimeException(e); 144 } 145 } 146 147 // Source object for engine.js 148 private static final Source ENGINE_SCRIPT_SRC = loadEngineJSSource(); 149 150 NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) { 151 this(factory, DEFAULT_OPTIONS, appLoader); 152 } 153 154 NashornScriptEngine(final NashornScriptEngineFactory factory, final String[] args, final ClassLoader appLoader) { 155 this.factory = factory; 156 final Options options = new Options("nashorn"); 157 options.process(args); 158 159 // throw ParseException on first error from script 160 final ErrorManager errMgr = new Context.ThrowErrorManager(); 161 // create new Nashorn Context 162 this.nashornContext = AccessController.doPrivileged(new PrivilegedAction<Context>() { 163 @Override 164 public Context run() { 165 try { 166 return new Context(options, errMgr, appLoader); 167 } catch (final RuntimeException e) { 168 if (Context.DEBUG) { 169 e.printStackTrace(); 170 } 171 throw e; 172 } 173 } 174 }, CREATE_CONTEXT_ACC_CTXT); 175 176 // cache this option that is used often 177 this._global_per_engine = nashornContext.getEnv()._global_per_engine; 178 179 // create new global object 180 this.global = createNashornGlobal(context); 181 // set the default ENGINE_SCOPE object for the default context 182 context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); 183 } 184 185 @Override 186 public Object eval(final Reader reader, final ScriptContext ctxt) throws ScriptException { 187 return evalImpl(makeSource(reader, ctxt), ctxt); 188 } 189 190 @Override 191 public Object eval(final String script, final ScriptContext ctxt) throws ScriptException { 192 return evalImpl(makeSource(script, ctxt), ctxt); 193 } 194 195 @Override 196 public ScriptEngineFactory getFactory() { 197 return factory; 198 } 199 200 @Override 201 public Bindings createBindings() { 202 if (_global_per_engine) { 203 // just create normal SimpleBindings. 204 // We use same 'global' for all Bindings. 205 return new SimpleBindings(); 206 } 207 return createGlobalMirror(null); 208 } 209 210 // Compilable methods 211 212 @Override 213 public CompiledScript compile(final Reader reader) throws ScriptException { 214 return asCompiledScript(makeSource(reader, context)); 215 } 216 217 @Override 218 public CompiledScript compile(final String str) throws ScriptException { 219 return asCompiledScript(makeSource(str, context)); 220 } 221 222 // Invocable methods 223 224 @Override 225 public Object invokeFunction(final String name, final Object... args) 226 throws ScriptException, NoSuchMethodException { 227 return invokeImpl(null, name, args); 228 } 229 230 @Override 231 public Object invokeMethod(final Object thiz, final String name, final Object... args) 232 throws ScriptException, NoSuchMethodException { 233 if (thiz == null) { 234 throw new IllegalArgumentException(getMessage("thiz.cannot.be.null")); 235 } 236 return invokeImpl(thiz, name, args); 237 } 238 239 @Override 240 public <T> T getInterface(final Class<T> clazz) { 241 return getInterfaceInner(null, clazz); 242 } 243 244 @Override 245 public <T> T getInterface(final Object thiz, final Class<T> clazz) { 246 if (thiz == null) { 247 throw new IllegalArgumentException(getMessage("thiz.cannot.be.null")); 248 } 249 return getInterfaceInner(thiz, clazz); 250 } 251 252 // These are called from the "engine.js" script 253 254 /** 255 * This hook is used to search js global variables exposed from Java code. 256 * 257 * @param self 'this' passed from the script 258 * @param ctxt current ScriptContext in which name is searched 259 * @param name name of the variable searched 260 * @return the value of the named variable 261 */ 262 public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) { 263 if (ctxt != null) { 264 final int scope = ctxt.getAttributesScope(name); 265 final Global ctxtGlobal = getNashornGlobalFrom(ctxt); 266 if (scope != -1) { 267 return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal); 268 } 269 270 if (self == UNDEFINED) { 271 // scope access and so throw ReferenceError 272 throw referenceError(ctxtGlobal, "not.defined", name); 273 } 274 } 275 276 return UNDEFINED; 277 } 278 279 // Implementation only below this point 280 281 private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException { 282 try { 283 return sourceFor(getScriptName(ctxt), reader); 284 } catch (IOException e) { 285 throw new ScriptException(e); 286 } 287 } 288 289 private static Source makeSource(final String src, final ScriptContext ctxt) { 290 return sourceFor(getScriptName(ctxt), src); 291 } 292 293 private static String getScriptName(final ScriptContext ctxt) { 294 final Object val = ctxt.getAttribute(ScriptEngine.FILENAME); 295 return (val != null) ? val.toString() : "<eval>"; 296 } 297 298 private <T> T getInterfaceInner(final Object thiz, final Class<T> clazz) { 299 if (clazz == null || !clazz.isInterface()) { 300 throw new IllegalArgumentException(getMessage("interface.class.expected")); 301 } 302 303 // perform security access check as early as possible 304 final SecurityManager sm = System.getSecurityManager(); 305 if (sm != null) { 306 if (! Modifier.isPublic(clazz.getModifiers())) { 307 throw new SecurityException(getMessage("implementing.non.public.interface", clazz.getName())); 308 } 309 Context.checkPackageAccess(clazz); 310 } 311 312 ScriptObject realSelf = null; 313 Global realGlobal = null; 314 if(thiz == null) { 315 // making interface out of global functions 316 realSelf = realGlobal = getNashornGlobalFrom(context); 317 } else if (thiz instanceof ScriptObjectMirror) { 318 final ScriptObjectMirror mirror = (ScriptObjectMirror)thiz; 319 realSelf = mirror.getScriptObject(); 320 realGlobal = mirror.getHomeGlobal(); 321 if (! isOfContext(realGlobal, nashornContext)) { 322 throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); 323 } 324 } else if (thiz instanceof ScriptObject) { 325 // called from script code. 326 realSelf = (ScriptObject)thiz; 327 realGlobal = Context.getGlobal(); 328 if (realGlobal == null) { 329 throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); 330 } 331 332 if (! isOfContext(realGlobal, nashornContext)) { 333 throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); 334 } 335 } 336 337 if (realSelf == null) { 338 throw new IllegalArgumentException(getMessage("interface.on.non.script.object")); 339 } 340 341 try { 342 final Global oldGlobal = Context.getGlobal(); 343 final boolean globalChanged = (oldGlobal != realGlobal); 344 try { 345 if (globalChanged) { 346 Context.setGlobal(realGlobal); 347 } 348 349 if (! isInterfaceImplemented(clazz, realSelf)) { 350 return null; 351 } 352 return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz, 353 MethodHandles.publicLookup()).invoke(realSelf)); 354 } finally { 355 if (globalChanged) { 356 Context.setGlobal(oldGlobal); 357 } 358 } 359 } catch(final RuntimeException|Error e) { 360 throw e; 361 } catch(final Throwable t) { 362 throw new RuntimeException(t); 363 } 364 } 365 366 // Retrieve nashorn Global object for a given ScriptContext object 367 private Global getNashornGlobalFrom(final ScriptContext ctxt) { 368 if (_global_per_engine) { 369 // shared single global object for all ENGINE_SCOPE Bindings 370 return global; 371 } 372 373 final Bindings bindings = ctxt.getBindings(ScriptContext.ENGINE_SCOPE); 374 // is this Nashorn's own Bindings implementation? 375 if (bindings instanceof ScriptObjectMirror) { 376 final Global glob = globalFromMirror((ScriptObjectMirror)bindings); 377 if (glob != null) { 378 return glob; 379 } 380 } 381 382 // Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it! 383 Object scope = bindings.get(NASHORN_GLOBAL); 384 if (scope instanceof ScriptObjectMirror) { 385 final Global glob = globalFromMirror((ScriptObjectMirror)scope); 386 if (glob != null) { 387 return glob; 388 } 389 } 390 391 // We didn't find associated nashorn global mirror in the Bindings given! 392 // Create new global instance mirror and associate with the Bindings. 393 final ScriptObjectMirror mirror = createGlobalMirror(ctxt); 394 bindings.put(NASHORN_GLOBAL, mirror); 395 return mirror.getHomeGlobal(); 396 } 397 398 // Retrieve nashorn Global object from a given ScriptObjectMirror 399 private Global globalFromMirror(final ScriptObjectMirror mirror) { 400 ScriptObject sobj = mirror.getScriptObject(); 401 if (sobj instanceof Global && isOfContext((Global)sobj, nashornContext)) { 402 return (Global)sobj; 403 } 404 405 return null; 406 } 407 408 // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object 409 private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) { 410 final Global newGlobal = createNashornGlobal(ctxt); 411 return new ScriptObjectMirror(newGlobal, newGlobal); 412 } 413 414 // Create a new Nashorn Global object 415 private Global createNashornGlobal(final ScriptContext ctxt) { 416 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 417 @Override 418 public Global run() { 419 try { 420 return nashornContext.newGlobal(); 421 } catch (final RuntimeException e) { 422 if (Context.DEBUG) { 423 e.printStackTrace(); 424 } 425 throw e; 426 } 427 } 428 }, CREATE_GLOBAL_ACC_CTXT); 429 430 nashornContext.initGlobal(newGlobal); 431 432 final int NON_ENUMERABLE_CONSTANT = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE; 433 // current ScriptContext exposed as "context" 434 // "context" is non-writable from script - but script engine still 435 // needs to set it and so save the context Property object 436 contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, ctxt); 437 // current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as 438 // NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property 439 // in the Global of a Context we just created - both the Context and the Global were just created and can not be 440 // seen from another thread outside of this constructor. 441 newGlobal.addOwnProperty("engine", NON_ENUMERABLE_CONSTANT, this); 442 // global script arguments with undefined value 443 newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED); 444 // file name default is null 445 newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null); 446 // evaluate engine.js initialization script this new global object 447 try { 448 evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal); 449 } catch (final ScriptException exp) { 450 throw new RuntimeException(exp); 451 } 452 return newGlobal; 453 } 454 455 // scripts should see "context" and "engine" as variables in the given global object 456 private void setContextVariables(final Global ctxtGlobal, final ScriptContext ctxt) { 457 // set "context" global variable via contextProperty - because this 458 // property is non-writable 459 contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false); 460 Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal); 461 if (args == null || args == UNDEFINED) { 462 args = ScriptRuntime.EMPTY_ARRAY; 463 } 464 // if no arguments passed, expose it 465 if (! (args instanceof ScriptObject)) { 466 args = ctxtGlobal.wrapAsObject(args); 467 ctxtGlobal.set("arguments", args, false); 468 } 469 } 470 471 private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException { 472 name.getClass(); // null check 473 474 Global invokeGlobal = null; 475 ScriptObjectMirror selfMirror = null; 476 if (selfObject instanceof ScriptObjectMirror) { 477 selfMirror = (ScriptObjectMirror)selfObject; 478 if (! isOfContext(selfMirror.getHomeGlobal(), nashornContext)) { 479 throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); 480 } 481 invokeGlobal = selfMirror.getHomeGlobal(); 482 } else if (selfObject instanceof ScriptObject) { 483 // invokeMethod called from script code - in which case we may get 'naked' ScriptObject 484 // Wrap it with oldGlobal to make a ScriptObjectMirror for the same. 485 final Global oldGlobal = Context.getGlobal(); 486 invokeGlobal = oldGlobal; 487 if (oldGlobal == null) { 488 throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); 489 } 490 491 if (! isOfContext(oldGlobal, nashornContext)) { 492 throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); 493 } 494 495 selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal); 496 } else if (selfObject == null) { 497 // selfObject is null => global function call 498 final Global ctxtGlobal = getNashornGlobalFrom(context); 499 invokeGlobal = ctxtGlobal; 500 selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(ctxtGlobal, ctxtGlobal); 501 } 502 503 if (selfMirror != null) { 504 try { 505 return ScriptObjectMirror.translateUndefined(selfMirror.callMember(name, args)); 506 } catch (final Exception e) { 507 final Throwable cause = e.getCause(); 508 if (cause instanceof NoSuchMethodException) { 509 throw (NoSuchMethodException)cause; 510 } 511 throwAsScriptException(e, invokeGlobal); 512 throw new AssertionError("should not reach here"); 513 } 514 } 515 516 // Non-script object passed as selfObject 517 throw new IllegalArgumentException(getMessage("interface.on.non.script.object")); 518 } 519 520 private Object evalImpl(final Source src, final ScriptContext ctxt) throws ScriptException { 521 return evalImpl(compileImpl(src, ctxt), ctxt); 522 } 523 524 private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException { 525 return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt)); 526 } 527 528 private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException { 529 if (script == null) { 530 return null; 531 } 532 final Global oldGlobal = Context.getGlobal(); 533 final boolean globalChanged = (oldGlobal != ctxtGlobal); 534 try { 535 if (globalChanged) { 536 Context.setGlobal(ctxtGlobal); 537 } 538 539 // set ScriptContext variables if ctxt is non-null 540 if (ctxt != null) { 541 setContextVariables(ctxtGlobal, ctxt); 542 } 543 return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); 544 } catch (final Exception e) { 545 throwAsScriptException(e, ctxtGlobal); 546 throw new AssertionError("should not reach here"); 547 } finally { 548 if (globalChanged) { 549 Context.setGlobal(oldGlobal); 550 } 551 } 552 } 553 554 private static void throwAsScriptException(final Exception e, final Global global) throws ScriptException { 555 if (e instanceof ScriptException) { 556 throw (ScriptException)e; 557 } else if (e instanceof NashornException) { 558 final NashornException ne = (NashornException)e; 559 final ScriptException se = new ScriptException( 560 ne.getMessage(), ne.getFileName(), 561 ne.getLineNumber(), ne.getColumnNumber()); 562 ne.initEcmaError(global); 563 se.initCause(e); 564 throw se; 565 } else if (e instanceof RuntimeException) { 566 throw (RuntimeException)e; 567 } else { 568 // wrap any other exception as ScriptException 569 throw new ScriptException(e); 570 } 571 } 572 573 private CompiledScript asCompiledScript(final Source source) throws ScriptException { 574 final ScriptFunction func = compileImpl(source, context); 575 return new CompiledScript() { 576 @Override 577 public Object eval(final ScriptContext ctxt) throws ScriptException { 578 final Global globalObject = getNashornGlobalFrom(ctxt); 579 // Are we running the script in the correct global? 580 if (func.getScope() == globalObject) { 581 return evalImpl(func, ctxt, globalObject); 582 } 583 // ScriptContext with a different global. Compile again! 584 // Note that we may still hit per-global compilation cache. 585 return evalImpl(compileImpl(source, ctxt), ctxt, globalObject); 586 } 587 @Override 588 public ScriptEngine getEngine() { 589 return NashornScriptEngine.this; 590 } 591 }; 592 } 593 594 private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException { 595 return compileImpl(source, getNashornGlobalFrom(ctxt)); 596 } 597 598 private ScriptFunction compileImpl(final Source source, final Global newGlobal) throws ScriptException { 599 final Global oldGlobal = Context.getGlobal(); 600 final boolean globalChanged = (oldGlobal != newGlobal); 601 try { 602 if (globalChanged) { 603 Context.setGlobal(newGlobal); 604 } 605 606 return nashornContext.compileScript(source, newGlobal); 607 } catch (final Exception e) { 608 throwAsScriptException(e, newGlobal); 609 throw new AssertionError("should not reach here"); 610 } finally { 611 if (globalChanged) { 612 Context.setGlobal(oldGlobal); 613 } 614 } 615 } 616 617 private static boolean isInterfaceImplemented(final Class<?> iface, final ScriptObject sobj) { 618 for (final Method method : iface.getMethods()) { 619 // ignore methods of java.lang.Object class 620 if (method.getDeclaringClass() == Object.class) { 621 continue; 622 } 623 624 // skip check for default methods - non-abstract, interface methods 625 if (! Modifier.isAbstract(method.getModifiers())) { 626 continue; 627 } 628 629 Object obj = sobj.get(method.getName()); 630 if (! (obj instanceof ScriptFunction)) { 631 return false; 632 } 633 } 634 return true; 635 } 636 637 private static boolean isOfContext(final Global global, final Context context) { 638 return global.isOfContext(context); 639 } 640 }