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