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