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 /** method handle to scope getter for this ScriptFunction */ 70 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); 71 72 private static final MethodHandle IS_FUNCTION_MH = findOwnMH("isFunctionMH", boolean.class, Object.class, ScriptFunctionData.class); 73 74 private static final MethodHandle IS_NONSTRICT_FUNCTION = findOwnMH("isNonStrictFunction", boolean.class, Object.class, Object.class, ScriptFunctionData.class); 75 76 private static final MethodHandle ADD_ZEROTH_ELEMENT = findOwnMH("addZerothElement", Object[].class, Object[].class, Object.class); 77 78 /** The parent scope. */ 79 private final ScriptObject scope; 80 81 private final ScriptFunctionData data; 82 83 /** The property map used for newly allocated object when function is used as constructor. */ 84 protected PropertyMap allocatorMap; 85 86 /** 87 * Constructor 88 * 89 * @param name function name 90 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) 91 * @param map property map 92 * @param scope scope 93 * @param specs specialized version of this function - other method handles 94 * @param strict is this a strict mode function? 95 * @param builtin is this a built in function? 96 * @param isConstructor is this a constructor? 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 boolean strict, 105 final boolean builtin, 106 final boolean isConstructor) { 107 108 this(new FinalScriptFunctionData(name, methodHandle, specs, strict, builtin, isConstructor), map, scope); 109 } 110 111 /** 112 * Constructor 113 * 114 * @param data static function data 115 * @param map property map 116 * @param scope scope 117 */ 118 protected ScriptFunction( 119 final ScriptFunctionData data, 120 final PropertyMap map, 121 final ScriptObject scope) { 122 123 super(map); 124 125 if (Context.DEBUG) { 126 constructorCount++; 127 } 128 129 this.data = data; 130 this.scope = scope; 131 this.allocatorMap = data.getAllocatorMap(); 132 } 133 134 @Override 135 public String getClassName() { 136 return "Function"; 137 } 138 139 /** 140 * ECMA 15.3.5.3 [[HasInstance]] (V) 141 * Step 3 if "prototype" value is not an Object, throw TypeError 142 */ 143 @Override 144 public boolean isInstance(final ScriptObject instance) { 145 final Object basePrototype = getTargetFunction().getPrototype(); 146 if (!(basePrototype instanceof ScriptObject)) { 147 throw typeError("prototype.not.an.object", ScriptRuntime.safeToString(getTargetFunction()), ScriptRuntime.safeToString(basePrototype)); 148 } 149 150 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) { 151 if (proto == basePrototype) { 152 return true; 153 } 154 } 155 156 return false; 157 } 158 159 /** 160 * Returns the target function for this function. If the function was not created using 161 * {@link #makeBoundFunction(Object, Object[])}, its target function is itself. If it is bound, its target function 162 * is the target function of the function it was made from (therefore, the target function is always the final, 163 * unbound recipient of the calls). 164 * @return the target function for this function. 165 */ 166 protected ScriptFunction getTargetFunction() { 167 return this; 168 } 169 170 boolean isBoundFunction() { 171 return getTargetFunction() != this; 172 } 173 174 /** 175 * Set the arity of this ScriptFunction 176 * @param arity arity 177 */ 178 public final void setArity(final int arity) { 179 data.setArity(arity); 180 } 181 182 /** 183 * Is this a ECMAScript 'use strict' function? 184 * @return true if function is in strict mode 185 */ 186 public boolean isStrict() { 187 return data.isStrict(); 188 } 189 190 /** 191 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument 192 * according to ECMA 10.4.3. 193 * @return true if this argument must be an object 194 */ 195 public boolean needsWrappedThis() { 196 return data.needsWrappedThis(); 197 } 198 199 /** 200 * Execute this script function. 201 * @param self Target object. 202 * @param arguments Call arguments. 203 * @return ScriptFunction result. 204 * @throws Throwable if there is an exception/error with the invocation or thrown from it 205 */ 206 Object invoke(final Object self, final Object... arguments) throws Throwable { 207 if (Context.DEBUG) { 208 invokes++; 209 } 210 return data.invoke(this, self, arguments); 211 } 212 213 /** 214 * Execute this script function as a constructor. 215 * @param arguments Call arguments. 216 * @return Newly constructed result. 217 * @throws Throwable if there is an exception/error with the invocation or thrown from it 218 */ 219 Object construct(final Object... arguments) throws Throwable { 220 return data.construct(this, arguments); 221 } 222 223 /** 224 * Allocate function. Called from generated {@link ScriptObject} code 225 * for allocation as a factory method 226 * 227 * @return a new instance of the {@link ScriptObject} whose allocator this is 228 */ 229 @SuppressWarnings("unused") 230 private Object allocate() { 231 if (Context.DEBUG) { 232 allocations++; 233 } 234 assert !isBoundFunction(); // allocate never invoked on bound functions 235 236 final ScriptObject object = data.allocate(allocatorMap); 237 238 if (object != null) { 239 Object prototype = getPrototype(); 240 if (prototype instanceof ScriptObject) { 241 object.setInitialProto((ScriptObject)prototype); 242 } 243 244 if (object.getProto() == null) { 245 object.setInitialProto(getObjectPrototype()); 246 } 247 } 248 249 return object; 250 } 251 252 /** 253 * Return Object.prototype - used by "allocate" 254 * @return Object.prototype 255 */ 256 protected abstract ScriptObject getObjectPrototype(); 257 258 /** 259 * Creates a version of this function bound to a specific "self" and other arguments, as per 260 * {@code Function.prototype.bind} functionality in ECMAScript 5.1 section 15.3.4.5. 261 * @param self the self to bind to this function. Can be null (in which case, null is bound as this). 262 * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. 263 * @return a function with the specified self and parameters bound. 264 */ 265 protected ScriptFunction makeBoundFunction(final Object self, final Object[] args) { 266 return makeBoundFunction(data.makeBoundFunctionData(this, self, args)); 267 } 268 269 /** 270 * Create a version of this function as in {@link ScriptFunction#makeBoundFunction(Object, Object[])}, 271 * but using a {@link ScriptFunctionData} for the bound data. 272 * 273 * @param boundData ScriptFuntionData for the bound function 274 * @return a function with the bindings performed according to the given data 275 */ 276 protected abstract ScriptFunction makeBoundFunction(ScriptFunctionData boundData); 277 278 @Override 279 public final String safeToString() { 280 return toSource(); 281 } 282 283 @Override 284 public String toString() { 285 return data.toString(); 286 } 287 288 /** 289 * Get this function as a String containing its source code. If no source code 290 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]} 291 * @return string representation of this function's source 292 */ 293 public final String toSource() { 294 return data.toSource(); 295 } 296 297 /** 298 * Get the prototype object for this function 299 * @return prototype 300 */ 301 public abstract Object getPrototype(); 302 303 /** 304 * Set the prototype object for this function 305 * @param prototype new prototype object 306 */ 307 public abstract void setPrototype(Object prototype); 308 309 /** 310 * Create a function that invokes this function synchronized on {@code sync} or the self object 311 * of the invocation. 312 * @param sync the Object to synchronize on, or undefined 313 * @return synchronized function 314 */ 315 public abstract ScriptFunction makeSynchronizedFunction(Object sync); 316 317 /** 318 * Return the most appropriate invoke handle if there are specializations 319 * @param type most specific method type to look for invocation with 320 * @param args args for trampoline invocation 321 * @return invoke method handle 322 */ 323 private MethodHandle getBestInvoker(final MethodType type, final Object[] args) { 324 return data.getBestInvoker(type, args); 325 } 326 327 /** 328 * Return the most appropriate invoke handle if there are specializations 329 * @param type most specific method type to look for invocation with 330 * @return invoke method handle 331 */ 332 public MethodHandle getBestInvoker(final MethodType type) { 333 return getBestInvoker(type, null); 334 } 335 336 /** 337 * Return the invoke handle bound to a given ScriptObject self reference. 338 * If callee parameter is required result is rebound to this. 339 * 340 * @param self self reference 341 * @return bound invoke handle 342 */ 343 public final MethodHandle getBoundInvokeHandle(final Object self) { 344 return MH.bindTo(bindToCalleeIfNeeded(data.getGenericInvoker()), self); 345 } 346 347 /** 348 * Bind the method handle to this {@code ScriptFunction} instance if it needs a callee parameter. If this function's 349 * method handles don't have a callee parameter, the handle is returned unchanged. 350 * @param methodHandle the method handle to potentially bind to this function instance. 351 * @return the potentially bound method handle 352 */ 353 private MethodHandle bindToCalleeIfNeeded(final MethodHandle methodHandle) { 354 return ScriptFunctionData.needsCallee(methodHandle) ? MH.bindTo(methodHandle, this) : methodHandle; 355 356 } 357 358 /** 359 * Get the name for this function 360 * @return the name 361 */ 362 public final String getName() { 363 return data.getName(); 364 } 365 366 367 /** 368 * Get the scope for this function 369 * @return the scope 370 */ 371 public final ScriptObject getScope() { 372 return scope; 373 } 374 375 /** 376 * Prototype getter for this ScriptFunction - follows the naming convention 377 * used by Nasgen and the code generator 378 * 379 * @param self self reference 380 * @return self's prototype 381 */ 382 public static Object G$prototype(final Object self) { 383 return (self instanceof ScriptFunction) ? 384 ((ScriptFunction)self).getPrototype() : 385 UNDEFINED; 386 } 387 388 /** 389 * Prototype setter for this ScriptFunction - follows the naming convention 390 * used by Nasgen and the code generator 391 * 392 * @param self self reference 393 * @param prototype prototype to set 394 */ 395 public static void S$prototype(final Object self, final Object prototype) { 396 if (self instanceof ScriptFunction) { 397 ((ScriptFunction)self).setPrototype(prototype); 398 } 399 } 400 401 /** 402 * Length getter - ECMA 15.3.3.2: Function.length 403 * @param self self reference 404 * @return length 405 */ 406 public static int G$length(final Object self) { 407 if (self instanceof ScriptFunction) { 408 return ((ScriptFunction)self).data.getArity(); 409 } 410 411 return 0; 412 } 413 414 /** 415 * Name getter - ECMA Function.name 416 * @param self self refence 417 * @return the name, or undefined if none 418 */ 419 public static Object G$name(final Object self) { 420 if (self instanceof ScriptFunction) { 421 return ((ScriptFunction)self).getName(); 422 } 423 424 return UNDEFINED; 425 } 426 427 /** 428 * Get the prototype for this ScriptFunction 429 * @param constructor constructor 430 * @return prototype, or null if given constructor is not a ScriptFunction 431 */ 432 public static ScriptObject getPrototype(final Object constructor) { 433 if (constructor instanceof ScriptFunction) { 434 final Object proto = ((ScriptFunction)constructor).getPrototype(); 435 if (proto instanceof ScriptObject) { 436 return (ScriptObject)proto; 437 } 438 } 439 440 return null; 441 } 442 443 // These counters are updated only in debug mode. 444 private static int constructorCount; 445 private static int invokes; 446 private static int allocations; 447 448 /** 449 * @return the constructorCount 450 */ 451 public static int getConstructorCount() { 452 return constructorCount; 453 } 454 455 /** 456 * @return the invokes 457 */ 458 public static int getInvokes() { 459 return invokes; 460 } 461 462 /** 463 * @return the allocations 464 */ 465 public static int getAllocations() { 466 return allocations; 467 } 468 469 @Override 470 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) { 471 final MethodType type = desc.getMethodType(); 472 return new GuardedInvocation(pairArguments(data.getBestConstructor(type.changeParameterType(0, ScriptFunction.class), null), type), null, getFunctionGuard(this)); 473 } 474 475 @SuppressWarnings("unused") 476 private static Object wrapFilter(final Object obj) { 477 if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) { 478 return obj; 479 } 480 return ((GlobalObject)Context.getGlobalTrusted()).wrapAsObject(obj); 481 } 482 483 /** 484 * dyn:call call site signature: (callee, thiz, [args...]) 485 * generated method signature: (callee, thiz, [args...]) 486 * 487 * cases: 488 * (a) method has callee parameter 489 * (1) for local/scope calls, we just bind thiz and drop the second argument. 490 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures. 491 * (b) method doesn't have callee parameter (builtin functions) 492 * (3) for local/scope calls, bind thiz and drop both callee and thiz. 493 * (4) for normal this-calls, drop callee. 494 */ 495 @Override 496 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final LinkRequest request) { 497 final MethodType type = desc.getMethodType(); 498 499 if (request.isCallSiteUnstable()) { 500 // (this, callee, args...) => (this, callee, args[]) 501 final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class, 502 type.parameterCount() - 2); 503 504 // If call site is statically typed to take a ScriptFunction, we don't need a guard, otherwise we need a 505 // generic "is this a ScriptFunction?" guard. 506 return new GuardedInvocation(collector, ScriptFunction.class.isAssignableFrom(desc.getMethodType().parameterType(0)) 507 ? null : NashornGuards.getScriptFunctionGuard()); 508 } 509 510 MethodHandle boundHandle; 511 MethodHandle guard = null; 512 513 final boolean scopeCall = NashornCallSiteDescriptor.isScope(desc); 514 515 if (data.needsCallee()) { 516 final MethodHandle callHandle = getBestInvoker(type, request.getArguments()); 517 if (scopeCall) { 518 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined 519 // (callee, this, args...) => (callee, args...) 520 boundHandle = MH.insertArguments(callHandle, 1, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 521 // (callee, args...) => (callee, [this], args...) 522 boundHandle = MH.dropArguments(boundHandle, 1, Object.class); 523 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) { 535 // Make a handle that drops the passed "this" argument and substitutes either Global or Undefined 536 // (this, args...) => (args...) 537 boundHandle = MH.bindTo(callHandle, needsWrappedThis() ? Context.getGlobalTrusted() : ScriptRuntime.UNDEFINED); 538 // (args...) => ([callee], [this], args...) 539 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, 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