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.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  29 import static jdk.nashorn.internal.lookup.Lookup.MH;
  30 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  31 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  32 
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.invoke.MethodType;
  36 import jdk.internal.dynalink.CallSiteDescriptor;
  37 import jdk.internal.dynalink.linker.GuardedInvocation;
  38 import jdk.internal.dynalink.linker.LinkRequest;
  39 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  40 import jdk.nashorn.internal.lookup.MethodHandleFactory;
  41 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  42 import jdk.nashorn.internal.runtime.linker.NashornGuards;
  43 
  44 /**
  45  * Runtime representation of a JavaScript function.
  46  */
  47 public abstract class ScriptFunction extends ScriptObject {
  48 
  49     /** Method handle for prototype getter for this ScriptFunction */
  50     public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
  51 
  52     /** Method handle for prototype setter for this ScriptFunction */
  53     public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
  54 
  55     /** Method handle for length getter for this ScriptFunction */
  56     public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
  57 
  58     /** Method handle for name getter for this ScriptFunction */
  59     public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
  60 
  61     /** Method handle used for implementing sync() in mozilla_compat */
  62     public static final MethodHandle INVOKE_SYNC = findOwnMH("invokeSync", Object.class, ScriptFunction.class, Object.class, Object.class, Object[].class);
  63 
  64     /** Method handle for allocate function for this ScriptFunction */
  65     static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
  66 
  67     private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class);
  68 
  69     private static final MethodHandle GLOBALFILTER = findOwnMH("globalFilter", Object.class, Object.class);
  70 
  71     /** method handle to scope getter for this ScriptFunction */
  72     public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
  73 
  74     private static final MethodHandle IS_FUNCTION_MH  = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class);
  75 
  76     private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class);
  77 
  78     private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH("addZerothElement", Object[].class, Object[].class, Object.class);
  79 
  80     /** The parent scope. */
  81     private final ScriptObject scope;
  82 
  83     private final ScriptFunctionData data;
  84 
  85     /** The property map used for newly allocated object when function is used as constructor. */
  86     protected PropertyMap allocatorMap;
  87 
  88     /**
  89      * Constructor
  90      *
  91      * @param name          function name
  92      * @param methodHandle  method handle to function (if specializations are present, assumed to be most generic)
  93      * @param map           property map
  94      * @param scope         scope
  95      * @param specs         specialized version of this function - other method handles
  96      * @param flags         {@link ScriptFunctionData} flags
  97      */
  98     protected ScriptFunction(
  99             final String name,
 100             final MethodHandle methodHandle,
 101             final PropertyMap map,
 102             final ScriptObject scope,
 103             final MethodHandle[] specs,
 104             final int flags) {
 105 
 106         this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
 107     }
 108 
 109     /**
 110      * Constructor
 111      *
 112      * @param data          static function data
 113      * @param map           property map
 114      * @param scope         scope
 115      */
 116     protected ScriptFunction(
 117             final ScriptFunctionData data,
 118             final PropertyMap map,
 119             final ScriptObject scope) {
 120 
 121         super(map);
 122 
 123         if (Context.DEBUG) {
 124             constructorCount++;
 125         }
 126 
 127         this.data  = data;
 128         this.scope = scope;
 129         this.allocatorMap = data.getAllocatorMap();
 130     }
 131 
 132     @Override
 133     public String getClassName() {
 134         return "Function";
 135     }
 136 
 137     /**
 138      * ECMA 15.3.5.3 [[HasInstance]] (V)
 139      * Step 3 if "prototype" value is not an Object, throw TypeError
 140      */
 141     @Override
 142     public boolean isInstance(final ScriptObject instance) {
 143         final Object basePrototype = getTargetFunction().getPrototype();
 144         if (!(basePrototype instanceof ScriptObject)) {
 145             throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype));
 146         }
 147 
 148         for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
 149             if (proto == basePrototype) {
 150                 return true;
 151             }
 152         }
 153 
 154         return false;
 155     }
 156 
 157     /**
 158      * Returns the target function for this function. If the function was not created using
 159      * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function
 160      * is the target function of the function it was made from (therefore, the target function is always the final,
 161      * unbound recipient of the calls).
 162      * @return the target function for this function.
 163      */
 164     protected ScriptFunction getTargetFunction() {
 165         return this;
 166     }
 167 
 168     boolean isBoundFunction() {
 169         return getTargetFunction() != this;
 170     }
 171 
 172     /**
 173      * Set the arity of this ScriptFunction
 174      * @param arity arity
 175      */
 176     public final void setArity(final int arity) {
 177         data.setArity(arity);
 178     }
 179 
 180     /**
 181      * Is this a ECMAScript 'use strict' function?
 182      * @return true if function is in strict mode
 183      */
 184     public boolean isStrict() {
 185         return data.isStrict();
 186     }
 187 
 188     /**
 189      * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
 190      * according to ECMA 10.4.3.
 191      * @return true if this argument must be an object
 192      */
 193     public boolean needsWrappedThis() {
 194         return data.needsWrappedThis();
 195     }
 196 
 197     /**
 198      * Execute this script function.
 199      * @param self  Target object.
 200      * @param arguments  Call arguments.
 201      * @return ScriptFunction result.
 202      * @throws Throwable if there is an exception/error with the invocation or thrown from it
 203      */
 204     Object invoke(final Object self, final Object... arguments) throws Throwable {
 205         if (Context.DEBUG) {
 206             invokes++;
 207         }
 208         return data.invoke(this, self, arguments);
 209     }
 210 
 211     /**
 212      * Execute this script function as a constructor.
 213      * @param arguments  Call arguments.
 214      * @return Newly constructed result.
 215      * @throws Throwable if there is an exception/error with the invocation or thrown from it
 216      */
 217     Object construct(final Object... arguments) throws Throwable {
 218         return data.construct(this, arguments);
 219     }
 220 
 221     /**
 222      * Allocate function. Called from generated {@link ScriptObject} code
 223      * for allocation as a factory method
 224      *
 225      * @return a new instance of the {@link ScriptObject} whose allocator this is
 226      */
 227     @SuppressWarnings("unused")
 228     private Object allocate() {
 229         if (Context.DEBUG) {
 230             allocations++;
 231         }
 232         assert !isBoundFunction(); // allocate never invoked on bound functions
 233 
 234         final ScriptObject object = data.allocate(allocatorMap);
 235 
 236         if (object != null) {
 237             Object prototype = getPrototype();
 238             if (prototype instanceof ScriptObject) {
 239                 object.setInitialProto((ScriptObject)prototype);
 240             }
 241 
 242             if (object.getProto() == null) {
 243                 object.setInitialProto(getObjectPrototype());
 244             }
 245         }
 246 
 247         return object;
 248     }
 249 
 250     /**
 251      * Return Object.prototype - used by "allocate"
 252      * @return Object.prototype
 253      */
 254     protected abstract ScriptObject getObjectPrototype();
 255 
 256     /**
 257      * Creates a version of this function bound to a specific "self" and other arguments, as per
 258      * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5.
 259      * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
 260      * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
 261      * @return a function with the specified self and parameters bound.
 262      */
 263     protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
 264         return makeBoundFunction(data.makeBoundFunctionData(this, self, args));
 265     }
 266 
 267     /**
 268      * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])},
 269      * but using a {@link ScriptFunctionData} for the bound data.
 270      *
 271      * @param boundData ScriptFuntionData for the bound function
 272      * @return a function with the bindings performed according to the given data
 273      */
 274     protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData);
 275 
 276     @Override
 277     public final String safeToString() {
 278         return toSource();
 279     }
 280 
 281     @Override
 282     public String toString() {
 283         return data.toString();
 284     }
 285 
 286     /**
 287      * Get this function as a String containing its source code. If no source code
 288      * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
 289      * @return string representation of this function's source
 290      */
 291     public final String toSource() {
 292         return data.toSource();
 293     }
 294 
 295     /**
 296      * Get the prototype object for this function
 297      * @return prototype
 298      */
 299     public abstract Object getPrototype();
 300 
 301     /**
 302      * Set the prototype object for this function
 303      * @param prototype new prototype object
 304      */
 305     public abstract void setPrototype(Object prototype);
 306 
 307     /**
 308      * Create a function that invokes this function synchronized on {@code sync} or the self object
 309      * of the invocation.
 310      * @param sync the Object to synchronize on, or undefined
 311      * @return synchronized function
 312      */
 313     public abstract ScriptFunction makeSynchronizedFunction(Object sync);
 314 
 315     /**
 316      * Return the most appropriate invoke handle if there are specializations
 317      * @param type most specific method type to look for invocation with
 318      * @param args args for trampoline invocation
 319      * @return invoke method handle
 320      */
 321     private MethodHandle getBestInvoker(final MethodType type, final Object[] args) {
 322         return data.getBestInvoker(type, args);
 323     }
 324 
 325     /**
 326      * Return the most appropriate invoke handle if there are specializations
 327      * @param type most specific method type to look for invocation with
 328      * @return invoke method handle
 329      */
 330     public MethodHandle getBestInvoker(final MethodType type) {
 331         return getBestInvoker(type, null);
 332     }
 333 
 334     /**
 335      * Return the invoke handle bound to a given ScriptObject self reference.
 336      * If callee parameter is required result is rebound to this.
 337      *
 338      * @param self self reference
 339      * @return bound invoke handle
 340      */
 341     public final MethodHandle getBoundInvokeHandle(final Object self) {
 342         return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self);
 343     }
 344 
 345     /**
 346      * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's
 347      * method handles don't have a callee parameter, the handle is returned unchanged.
 348      * @param methodHandle the method handle to potentially bind to this function instance.
 349      * @return the potentially bound method handle
 350      */
 351     private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) {
 352         return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle;
 353 
 354     }
 355 
 356     /**
 357      * Get the name for this function
 358      * @return the name
 359      */
 360     public final String getName() {
 361         return data.getName();
 362     }
 363 
 364 
 365     /**
 366      * Get the scope for this function
 367      * @return the scope
 368      */
 369     public final ScriptObject getScope() {
 370         return scope;
 371     }
 372 
 373     /**
 374      * Prototype getter for this ScriptFunction - follows the naming convention
 375      * used by Nasgen and the code generator
 376      *
 377      * @param self  self reference
 378      * @return self's prototype
 379      */
 380     public static Object G$prototype(final Object self) {
 381         return (self instanceof ScriptFunction) ?
 382             ((ScriptFunction)self).getPrototype() :
 383             UNDEFINED;
 384     }
 385 
 386     /**
 387      * Prototype setter for this ScriptFunction - follows the naming convention
 388      * used by Nasgen and the code generator
 389      *
 390      * @param self  self reference
 391      * @param prototype prototype to set
 392      */
 393     public static void S$prototype(final Object self, final Object prototype) {
 394         if (self instanceof ScriptFunction) {
 395             ((ScriptFunction)self).setPrototype(prototype);
 396         }
 397     }
 398 
 399     /**
 400      * Length getter - ECMA 15.3.3.2: Function.length
 401      * @param self self reference
 402      * @return length
 403      */
 404     public static int G$length(final Object self) {
 405         if (self instanceof ScriptFunction) {
 406             return ((ScriptFunction)self).data.getArity();
 407         }
 408 
 409         return 0;
 410     }
 411 
 412     /**
 413      * Name getter - ECMA Function.name
 414      * @param self self refence
 415      * @return the name, or undefined if none
 416      */
 417     public static Object G$name(final Object self) {
 418         if (self instanceof ScriptFunction) {
 419             return ((ScriptFunction)self).getName();
 420         }
 421 
 422         return UNDEFINED;
 423     }
 424 
 425     /**
 426      * Get the prototype for this ScriptFunction
 427      * @param constructor constructor
 428      * @return prototype, or null if given constructor is not a ScriptFunction
 429      */
 430     public static ScriptObject getPrototype(final Object constructor) {
 431         if (constructor instanceof ScriptFunction) {
 432             final Object proto = ((ScriptFunction)constructor).getPrototype();
 433             if (proto instanceof ScriptObject) {
 434                 return (ScriptObject)proto;
 435             }
 436         }
 437 
 438         return null;
 439     }
 440 
 441     // These counters are updated only in debug mode.
 442     private static int constructorCount;
 443     private static int invokes;
 444     private static int allocations;
 445 
 446     /**
 447      * @return the constructorCount
 448      */
 449     public static int getConstructorCount() {
 450         return constructorCount;
 451     }
 452 
 453     /**
 454      * @return the invokes
 455      */
 456     public static int getInvokes() {
 457         return invokes;
 458     }
 459 
 460     /**
 461      * @return the allocations
 462      */
 463     public static int getAllocations() {
 464         return allocations;
 465     }
 466 
 467     @Override
 468     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
 469         final MethodType type = desc.getMethodType();
 470         return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this));
 471     }
 472 
 473     @SuppressWarnings("unused")
 474     private static Object wrapFilter(final Object obj) {
 475         if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
 476             return obj;
 477         }
 478         return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj);
 479     }
 480 
 481 
 482     @SuppressWarnings("unused")
 483     private static Object globalFilter(final Object object) {
 484         // replace whatever we get with the current global object
 485         return Context.getGlobalTrusted();
 486     }
 487 
 488     /**
 489      * dyn:call call site signature: (callee, thiz, [args...])
 490      * generated method signature:   (callee, thiz, [args...])
 491      *
 492      * cases:
 493      * (a) method has callee parameter
 494      *   (1) for local/scope calls, we just bind thiz and drop the second argument.
 495      *   (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
 496      * (b) method doesn't have callee parameter (builtin functions)
 497      *   (3) for local/scope calls, bind thiz and drop both callee and thiz.
 498      *   (4) for normal this-calls, drop callee.
 499      */
 500     @Override
 501     protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 502         final MethodType type = desc.getMethodType();
 503         final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc);
 504 
 505         if (request.isCallSiteUnstable()) {
 506             // (callee, this, args...) => (callee, this, args[])
 507             final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, type.parameterCount() - 2);
 508 
 509             // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a
 510             // generic "is this a ScriptFunction?" guard.
 511             return new GuardedInvocation(collector, ScriptFunction.class.isAssignableFrom(desc.getMethodType().parameterType(0))
 512                     ? null : NashornGuards.getScriptFunctionGuard());
 513         }
 514 
 515         MethodHandle boundHandle;
 516         MethodHandle guard = null;
 517 
 518         if (data.needsCallee()) {
 519             final MethodHandle callHandle = getBestInvoker(type, request.getArguments());
 520             if (scopeCall && needsWrappedThis()) {
 521                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
 522                 // (callee, this, args...) => (callee, [this], args...)
 523                 boundHandle = MH.filterArguments(callHandle, 1, GLOBALFILTER);
 524             } else {
 525                 // It's already (callee, this, args...), just what we need
 526                 boundHandle = callHandle;
 527             }
 528         } else {
 529             final MethodHandle callHandle = getBestInvoker(type.dropParameterTypes(0, 1), request.getArguments());
 530             if (data.isBuiltin() && "extend".equals(data.getName())) {
 531                 // NOTE: the only built-in named "extend" is NativeJava.extend. As a special-case we're binding the
 532                 // current lookup as its "this" so it can do security-sensitive creation of adapter classes.
 533                 boundHandle = MH.dropArguments(MH.bindTo(callHandle, desc.getLookup()), 0, Object.class, Object.class);
 534             } else if (scopeCall && needsWrappedThis()) {
 535                 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined
 536                 // (this, args...) => ([this], args...)
 537                 boundHandle = MH.filterArguments(callHandle, 0, GLOBALFILTER);
 538                 // ([this], args...) => ([callee], [this], args...)
 539                 boundHandle = MH.dropArguments(boundHandle, 0, Object.class);
 540             } else {
 541                 // (this, args...) => ([callee], this, args...)
 542                 boundHandle = MH.dropArguments(callHandle, 0, Object.class);
 543             }
 544         }
 545 
 546         // For non-strict functions, check whether this-object is primitive type.
 547         // If so add a to-object-wrapper argument filter.
 548         // Else install a guard that will trigger a relink when the argument becomes primitive.
 549         if (!scopeCall && needsWrappedThis()) {
 550             if (ScriptFunctionData.isPrimitiveThis(request.getArguments()[1])) {
 551                 boundHandle = MH.filterArguments(boundHandle, 1, WRAPFILTER);
 552             } else {
 553                 guard = getNonStrictFunctionGuard(this);
 554             }
 555         }
 556 
 557         boundHandle = pairArguments(boundHandle, type);
 558 
 559         return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this) : guard);
 560    }
 561 
 562     /**
 563      * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
 564      *
 565      * These don't want a callee parameter, so bind that. Name binding is optional.
 566      */
 567     MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
 568         return pairArguments(bindToNameIfNeeded(bindToCalleeIfNeeded(getBestInvoker(type, null)), bindName), type);
 569     }
 570 
 571     private static MethodHandle bindToNameIfNeeded(final MethodHandle methodHandle, final String bindName) {
 572         if (bindName == null) {
 573             return methodHandle;
 574         }
 575 
 576         // if it is vararg method, we need to extend argument array with
 577         // a new zeroth element that is set to bindName value.
 578         final MethodType methodType = methodHandle.type();
 579         final int parameterCount = methodType.parameterCount();
 580         final boolean isVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
 581 
 582         if (isVarArg) {
 583             return MH.filterArguments(methodHandle, 1, MH.insertArguments(ADD_ZEROTH_ELEMENT, 1, bindName));
 584         }
 585         return MH.insertArguments(methodHandle, 1, bindName);
 586     }
 587 
 588     /**
 589      * Get the guard that checks if a {@link ScriptFunction} is equal to
 590      * a known ScriptFunction, using reference comparison
 591      *
 592      * @param function The ScriptFunction to check against. This will be bound to the guard method handle
 593      *
 594      * @return method handle for guard
 595      */
 596     private static MethodHandle getFunctionGuard(final ScriptFunction function) {
 597         assert function.data != null;
 598         return MH.insertArguments(IS_FUNCTION_MH, 1, function.data);
 599     }
 600 
 601     /**
 602      * Get a guard that checks if a {@link ScriptFunction} is equal to
 603      * a known ScriptFunction using reference comparison, and whether the type of
 604      * the second argument (this-object) is not a JavaScript primitive type.
 605      *
 606      * @param function The ScriptFunction to check against. This will be bound to the guard method handle
 607      *
 608      * @return method handle for guard
 609      */
 610     private static MethodHandle getNonStrictFunctionGuard(final ScriptFunction function) {
 611         assert function.data != null;
 612         return MH.insertArguments(IS_NONSTRICT_FUNCTION, 2, function.data);
 613     }
 614 
 615     @SuppressWarnings("unused")
 616     private static boolean isFunctionMH(final Object self, final ScriptFunctionData data) {
 617         return self instanceof ScriptFunction && ((ScriptFunction)self).data == data;
 618     }
 619 
 620     @SuppressWarnings("unused")
 621     private static boolean isNonStrictFunction(final Object self, final Object arg, final ScriptFunctionData data) {
 622         return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
 623     }
 624 
 625     @SuppressWarnings("unused")
 626     private static Object[] addZerothElement(final Object[] args, final Object value) {
 627         // extends input array with by adding new zeroth element
 628         final Object[] src = (args == null)? ScriptRuntime.EMPTY_ARRAY : args;
 629         final Object[] result = new Object[src.length + 1];
 630         System.arraycopy(src, 0, result, 1, src.length);
 631         result[0] = value;
 632         return result;
 633     }
 634 
 635     @SuppressWarnings("unused")
 636     private static Object invokeSync(final ScriptFunction func, final Object sync, final Object self, final Object... args)
 637             throws Throwable {
 638         final Object syncObj = sync == UNDEFINED ? self : sync;
 639         synchronized (syncObj) {
 640             return func.invoke(self, args);
 641         }
 642     }
 643 
 644     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 645         final Class<?>   own = ScriptFunction.class;
 646         final MethodType mt  = MH.type(rtype, types);
 647         try {
 648             return MH.findStatic(MethodHandles.lookup(), own, name, mt);
 649         } catch (final MethodHandleFactory.LookupException e) {
 650             return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
 651         }
 652     }
 653 }
 654