src/jdk/nashorn/internal/runtime/ScriptFunctionData.java

Thu, 24 May 2018 16:39:31 +0800

author
aoqi
date
Thu, 24 May 2018 16:39:31 +0800
changeset 1959
61ffdd1b89f2
parent 1647
fa7dce1af94e
parent 1490
d85f981c8cf8
permissions
-rw-r--r--

Merge

aoqi@0 1 /*
aoqi@0 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
aoqi@0 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0 4 *
aoqi@0 5 * This code is free software; you can redistribute it and/or modify it
aoqi@0 6 * under the terms of the GNU General Public License version 2 only, as
aoqi@0 7 * published by the Free Software Foundation. Oracle designates this
aoqi@0 8 * particular file as subject to the "Classpath" exception as provided
aoqi@0 9 * by Oracle in the LICENSE file that accompanied this code.
aoqi@0 10 *
aoqi@0 11 * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0 14 * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0 15 * accompanied this code).
aoqi@0 16 *
aoqi@0 17 * You should have received a copy of the GNU General Public License version
aoqi@0 18 * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0 20 *
aoqi@0 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0 22 * or visit www.oracle.com if you need additional information or have any
aoqi@0 23 * questions.
aoqi@0 24 */
aoqi@0 25
aoqi@0 26 package jdk.nashorn.internal.runtime;
aoqi@0 27
aoqi@0 28 import static jdk.nashorn.internal.lookup.Lookup.MH;
aoqi@0 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
aoqi@0 30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
attila@1251 31
attila@963 32 import java.io.IOException;
attila@963 33 import java.io.ObjectInputStream;
aoqi@0 34 import java.io.Serializable;
aoqi@0 35 import java.lang.invoke.MethodHandle;
aoqi@0 36 import java.lang.invoke.MethodHandles;
aoqi@0 37 import java.lang.invoke.MethodType;
lagergren@1028 38 import java.util.Collection;
attila@963 39 import java.util.LinkedList;
attila@963 40 import java.util.List;
attila@963 41 import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
attila@963 42
aoqi@0 43
aoqi@0 44 /**
aoqi@0 45 * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
aoqi@0 46 * Instances of this class are created during codegen and stored in script classes'
aoqi@0 47 * constants array to reduce function instantiation overhead during runtime.
aoqi@0 48 */
aoqi@0 49 public abstract class ScriptFunctionData implements Serializable {
attila@963 50 static final int MAX_ARITY = LinkerCallSite.ARGLIMIT;
attila@963 51 static {
attila@963 52 // Assert it fits in a byte, as that's what we store it in. It's just a size optimization though, so if needed
attila@963 53 // "byte arity" field can be widened.
attila@963 54 assert MAX_ARITY < 256;
attila@963 55 }
aoqi@0 56
attila@963 57 /** Name of the function or "" for anonymous functions */
aoqi@0 58 protected final String name;
aoqi@0 59
attila@963 60 /**
attila@963 61 * A list of code versions of a function sorted in ascending order of generic descriptors.
attila@963 62 */
attila@963 63 protected transient LinkedList<CompiledFunction> code = new LinkedList<>();
aoqi@0 64
aoqi@0 65 /** Function flags */
aoqi@0 66 protected int flags;
aoqi@0 67
attila@963 68 // Parameter arity of the function, corresponding to "f.length". E.g. "function f(a, b, c) { ... }" arity is 3, and
attila@963 69 // some built-in ECMAScript functions have their arity declared by the specification. Note that regardless of this
attila@963 70 // value, the function might still be capable of receiving variable number of arguments, see isVariableArity.
aoqi@0 71 private int arity;
aoqi@0 72
attila@963 73 /**
attila@963 74 * A pair of method handles used for generic invoker and constructor. Field is volatile as it can be initialized by
attila@963 75 * multiple threads concurrently, but we still tolerate a race condition in it as all values stored into it are
attila@963 76 * idempotent.
attila@963 77 */
attila@963 78 private volatile transient GenericInvokers genericInvokers;
attila@963 79
aoqi@0 80 private static final MethodHandle BIND_VAR_ARGS = findOwnMH("bindVarArgs", Object[].class, Object[].class, Object[].class);
aoqi@0 81
aoqi@0 82 /** Is this a strict mode function? */
hannesw@1396 83 public static final int IS_STRICT = 1 << 0;
aoqi@0 84 /** Is this a built-in function? */
hannesw@1396 85 public static final int IS_BUILTIN = 1 << 1;
aoqi@0 86 /** Is this a constructor function? */
hannesw@1396 87 public static final int IS_CONSTRUCTOR = 1 << 2;
aoqi@0 88 /** Does this function expect a callee argument? */
hannesw@1396 89 public static final int NEEDS_CALLEE = 1 << 3;
aoqi@0 90 /** Does this function make use of the this-object argument? */
hannesw@1396 91 public static final int USES_THIS = 1 << 4;
attila@963 92 /** Is this a variable arity function? */
hannesw@1396 93 public static final int IS_VARIABLE_ARITY = 1 << 5;
hannesw@1396 94 /** Is this a object literal property getter or setter? */
hannesw@1396 95 public static final int IS_PROPERTY_ACCESSOR = 1 << 6;
aoqi@0 96
aoqi@0 97 /** Flag for strict or built-in functions */
aoqi@0 98 public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
aoqi@0 99 /** Flag for built-in constructors */
aoqi@0 100 public static final int IS_BUILTIN_CONSTRUCTOR = IS_BUILTIN | IS_CONSTRUCTOR;
aoqi@0 101
aoqi@0 102 private static final long serialVersionUID = 4252901245508769114L;
aoqi@0 103
aoqi@0 104 /**
aoqi@0 105 * Constructor
aoqi@0 106 *
attila@963 107 * @param name script function name
attila@963 108 * @param arity arity
attila@963 109 * @param flags the function flags
aoqi@0 110 */
aoqi@0 111 ScriptFunctionData(final String name, final int arity, final int flags) {
aoqi@0 112 this.name = name;
aoqi@0 113 this.flags = flags;
attila@963 114 setArity(arity);
aoqi@0 115 }
aoqi@0 116
aoqi@0 117 final int getArity() {
aoqi@0 118 return arity;
aoqi@0 119 }
aoqi@0 120
attila@963 121 final boolean isVariableArity() {
attila@963 122 return (flags & IS_VARIABLE_ARITY) != 0;
attila@963 123 }
attila@963 124
hannesw@1396 125 final boolean isPropertyAccessor() {
hannesw@1396 126 return (flags & IS_PROPERTY_ACCESSOR) != 0;
hannesw@1396 127 }
hannesw@1396 128
aoqi@0 129 /**
aoqi@0 130 * Used from e.g. Native*$Constructors as an explicit call. TODO - make arity immutable and final
aoqi@0 131 * @param arity new arity
aoqi@0 132 */
aoqi@0 133 void setArity(final int arity) {
attila@963 134 if(arity < 0 || arity > MAX_ARITY) {
attila@963 135 throw new IllegalArgumentException(String.valueOf(arity));
attila@963 136 }
aoqi@0 137 this.arity = arity;
aoqi@0 138 }
aoqi@0 139
aoqi@0 140 CompiledFunction bind(final CompiledFunction originalInv, final ScriptFunction fn, final Object self, final Object[] args) {
attila@963 141 final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args);
aoqi@0 142
aoqi@0 143 if (isConstructor()) {
lagergren@1028 144 return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args), null);
aoqi@0 145 }
aoqi@0 146
attila@963 147 return new CompiledFunction(boundInvoker);
aoqi@0 148 }
aoqi@0 149
aoqi@0 150 /**
aoqi@0 151 * Is this a ScriptFunction generated with strict semantics?
aoqi@0 152 * @return true if strict, false otherwise
aoqi@0 153 */
sundar@1529 154 public final boolean isStrict() {
aoqi@0 155 return (flags & IS_STRICT) != 0;
aoqi@0 156 }
aoqi@0 157
attila@963 158 /**
attila@963 159 * Return the complete internal function name for this
attila@963 160 * data, not anonymous or similar. May be identical
attila@963 161 * @return internal function name
attila@963 162 */
attila@963 163 protected String getFunctionName() {
attila@963 164 return getName();
attila@963 165 }
attila@963 166
sundar@1529 167 final boolean isBuiltin() {
aoqi@0 168 return (flags & IS_BUILTIN) != 0;
aoqi@0 169 }
aoqi@0 170
sundar@1529 171 final boolean isConstructor() {
aoqi@0 172 return (flags & IS_CONSTRUCTOR) != 0;
aoqi@0 173 }
aoqi@0 174
attila@963 175 abstract boolean needsCallee();
aoqi@0 176
aoqi@0 177 /**
aoqi@0 178 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
aoqi@0 179 * according to ECMA 10.4.3.
aoqi@0 180 * @return true if this argument must be an object
aoqi@0 181 */
sundar@1529 182 final boolean needsWrappedThis() {
aoqi@0 183 return (flags & USES_THIS) != 0 && (flags & IS_STRICT_OR_BUILTIN) == 0;
aoqi@0 184 }
aoqi@0 185
aoqi@0 186 String toSource() {
aoqi@0 187 return "function " + (name == null ? "" : name) + "() { [native code] }";
aoqi@0 188 }
aoqi@0 189
aoqi@0 190 String getName() {
aoqi@0 191 return name;
aoqi@0 192 }
aoqi@0 193
aoqi@0 194 /**
aoqi@0 195 * Get this function as a String containing its source code. If no source code
aoqi@0 196 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
aoqi@0 197 *
aoqi@0 198 * @return string representation of this function
aoqi@0 199 */
aoqi@0 200 @Override
aoqi@0 201 public String toString() {
attila@963 202 return name.isEmpty() ? "<anonymous>" : name;
attila@963 203 }
attila@963 204
attila@963 205 /**
attila@963 206 * Verbose description of data
attila@963 207 * @return verbose description
attila@963 208 */
attila@963 209 public String toStringVerbose() {
aoqi@0 210 final StringBuilder sb = new StringBuilder();
aoqi@0 211
aoqi@0 212 sb.append("name='").
aoqi@0 213 append(name.isEmpty() ? "<anonymous>" : name).
aoqi@0 214 append("' ").
aoqi@0 215 append(code.size()).
aoqi@0 216 append(" invokers=").
aoqi@0 217 append(code);
aoqi@0 218
aoqi@0 219 return sb.toString();
aoqi@0 220 }
aoqi@0 221
aoqi@0 222 /**
aoqi@0 223 * Pick the best invoker, i.e. the one version of this method with as narrow and specific
aoqi@0 224 * types as possible. If the call site arguments are objects, but boxed primitives we can
aoqi@0 225 * also try to get a primitive version of the method and do an unboxing filter, but then
aoqi@0 226 * we need to insert a guard that checks the argument is really always a boxed primitive
aoqi@0 227 * and not suddenly a "real" object
aoqi@0 228 *
aoqi@0 229 * @param callSiteType callsite type
attila@963 230 * @return compiled function object representing the best invoker.
aoqi@0 231 */
lagergren@1028 232 final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
lagergren@1028 233 return getBestInvoker(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS);
aoqi@0 234 }
aoqi@0 235
lagergren@1028 236 final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
lagergren@1028 237 final CompiledFunction cf = getBest(callSiteType, runtimeScope, forbidden);
attila@963 238 assert cf != null;
attila@963 239 return cf;
aoqi@0 240 }
aoqi@0 241
lagergren@1028 242 final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
aoqi@0 243 if (!isConstructor()) {
aoqi@0 244 throw typeError("not.a.constructor", toSource());
aoqi@0 245 }
attila@963 246 // Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
lagergren@1028 247 final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope, forbidden);
attila@963 248 return cf;
aoqi@0 249 }
aoqi@0 250
aoqi@0 251 /**
aoqi@0 252 * If we can have lazy code generation, this is a hook to ensure that the code has been compiled.
attila@963 253 * This does not guarantee the code been installed in this {@code ScriptFunctionData} instance
aoqi@0 254 */
aoqi@0 255 protected void ensureCompiled() {
aoqi@0 256 //empty
aoqi@0 257 }
aoqi@0 258
aoqi@0 259 /**
aoqi@0 260 * Return a generic Object/Object invoker for this method. It will ensure code
aoqi@0 261 * is generated, get the most generic of all versions of this function and adapt it
aoqi@0 262 * to Objects.
aoqi@0 263 *
attila@963 264 * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
attila@963 265 * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
attila@963 266 * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
aoqi@0 267 * @return generic invoker of this script function
aoqi@0 268 */
attila@963 269 final MethodHandle getGenericInvoker(final ScriptObject runtimeScope) {
attila@963 270 // This method has race conditions both on genericsInvoker and genericsInvoker.invoker, but even if invoked
attila@963 271 // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this
attila@963 272 // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it.
attila@963 273 final GenericInvokers lgenericInvokers = ensureGenericInvokers();
attila@963 274 MethodHandle invoker = lgenericInvokers.invoker;
attila@963 275 if(invoker == null) {
attila@963 276 lgenericInvokers.invoker = invoker = createGenericInvoker(runtimeScope);
attila@963 277 }
attila@963 278 return invoker;
aoqi@0 279 }
aoqi@0 280
attila@963 281 private MethodHandle createGenericInvoker(final ScriptObject runtimeScope) {
attila@963 282 return makeGenericMethod(getGeneric(runtimeScope).createComposableInvoker());
aoqi@0 283 }
aoqi@0 284
attila@963 285 final MethodHandle getGenericConstructor(final ScriptObject runtimeScope) {
attila@963 286 // This method has race conditions both on genericsInvoker and genericsInvoker.constructor, but even if invoked
attila@963 287 // concurrently, they'll create idempotent results, so it doesn't matter. We could alternatively implement this
attila@963 288 // using java.util.concurrent.AtomicReferenceFieldUpdater, but it's hardly worth it.
attila@963 289 final GenericInvokers lgenericInvokers = ensureGenericInvokers();
attila@963 290 MethodHandle constructor = lgenericInvokers.constructor;
attila@963 291 if(constructor == null) {
attila@963 292 lgenericInvokers.constructor = constructor = createGenericConstructor(runtimeScope);
attila@963 293 }
attila@963 294 return constructor;
aoqi@0 295 }
aoqi@0 296
attila@963 297 private MethodHandle createGenericConstructor(final ScriptObject runtimeScope) {
attila@963 298 return makeGenericMethod(getGeneric(runtimeScope).createComposableConstructor());
attila@963 299 }
attila@963 300
attila@963 301 private GenericInvokers ensureGenericInvokers() {
attila@963 302 GenericInvokers lgenericInvokers = genericInvokers;
attila@963 303 if(lgenericInvokers == null) {
attila@963 304 genericInvokers = lgenericInvokers = new GenericInvokers();
attila@963 305 }
attila@963 306 return lgenericInvokers;
attila@963 307 }
attila@963 308
attila@963 309 private static MethodType widen(final MethodType cftype) {
attila@963 310 final Class<?>[] paramTypes = new Class<?>[cftype.parameterCount()];
attila@963 311 for (int i = 0; i < cftype.parameterCount(); i++) {
attila@963 312 paramTypes[i] = cftype.parameterType(i).isPrimitive() ? cftype.parameterType(i) : Object.class;
attila@963 313 }
attila@963 314 return MH.type(cftype.returnType(), paramTypes);
attila@963 315 }
attila@963 316
attila@963 317 /**
attila@963 318 * Used to find an apply to call version that fits this callsite.
attila@963 319 * We cannot just, as in the normal matcher case, return e.g. (Object, Object, int)
attila@963 320 * for (Object, Object, int, int, int) or we will destroy the semantics and get
sundar@1530 321 * a function that, when padded with undefined values, behaves differently
attila@963 322 * @param type actual call site type
attila@963 323 * @return apply to call that perfectly fits this callsite or null if none found
attila@963 324 */
attila@963 325 CompiledFunction lookupExactApplyToCall(final MethodType type) {
attila@963 326 for (final CompiledFunction cf : code) {
attila@963 327 if (!cf.isApplyToCall()) {
attila@963 328 continue;
attila@963 329 }
attila@963 330
attila@963 331 final MethodType cftype = cf.type();
attila@963 332 if (cftype.parameterCount() != type.parameterCount()) {
attila@963 333 continue;
attila@963 334 }
attila@963 335
attila@963 336 if (widen(cftype).equals(widen(type))) {
attila@963 337 return cf;
attila@963 338 }
attila@963 339 }
attila@963 340
attila@963 341 return null;
attila@963 342 }
attila@963 343
attila@963 344 CompiledFunction pickFunction(final MethodType callSiteType, final boolean canPickVarArg) {
attila@963 345 for (final CompiledFunction candidate : code) {
attila@963 346 if (candidate.matchesCallSite(callSiteType, canPickVarArg)) {
attila@963 347 return candidate;
attila@963 348 }
attila@963 349 }
attila@963 350 return null;
attila@963 351 }
attila@963 352
attila@963 353 /**
attila@963 354 * Returns the best function for the specified call site type.
attila@963 355 * @param callSiteType The call site type. Call site types are expected to have the form
attila@963 356 * {@code (callee, this[, args...])}.
attila@963 357 * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
attila@963 358 * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
attila@963 359 * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
sundar@1647 360 * @param linkLogicOkay is a CompiledFunction with a LinkLogic acceptable?
attila@963 361 * @return the best function for the specified call site type.
attila@963 362 */
sundar@1647 363 abstract CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden, final boolean linkLogicOkay);
attila@963 364
sundar@1647 365 /**
sundar@1647 366 * Returns the best function for the specified call site type.
sundar@1647 367 * @param callSiteType The call site type. Call site types are expected to have the form
sundar@1647 368 * {@code (callee, this[, args...])}.
sundar@1647 369 * @param runtimeScope the runtime scope. It can be used to evaluate types of scoped variables to guide the
sundar@1647 370 * optimistic compilation, should the call to this method trigger code compilation. Can be null if current runtime
sundar@1647 371 * scope is not known, but that might cause compilation of code that will need more deoptimization passes.
sundar@1647 372 * @return the best function for the specified call site type.
sundar@1647 373 */
sundar@1647 374 final CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
sundar@1647 375 return getBest(callSiteType, runtimeScope, forbidden, true);
attila@963 376 }
attila@963 377
hannesw@1506 378 boolean isValidCallSite(final MethodType callSiteType) {
hannesw@1506 379 return callSiteType.parameterCount() >= 2 && // Must have at least (callee, this)
hannesw@1506 380 callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class); // Callee must be assignable from script function
hannesw@1396 381 }
attila@963 382
attila@963 383 CompiledFunction getGeneric(final ScriptObject runtimeScope) {
sundar@1647 384 return getBest(getGenericType(), runtimeScope, CompiledFunction.NO_FUNCTIONS, false);
attila@963 385 }
attila@963 386
attila@963 387 /**
attila@963 388 * Get a method type for a generic invoker.
attila@963 389 * @return the method type for the generic invoker
attila@963 390 */
attila@963 391 abstract MethodType getGenericType();
attila@963 392
aoqi@0 393 /**
aoqi@0 394 * Allocates an object using this function's allocator.
aoqi@0 395 *
aoqi@0 396 * @param map the property map for the allocated object.
aoqi@0 397 * @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
aoqi@0 398 */
aoqi@0 399 ScriptObject allocate(final PropertyMap map) {
aoqi@0 400 return null;
aoqi@0 401 }
aoqi@0 402
aoqi@0 403 /**
aoqi@0 404 * Get the property map to use for objects allocated by this function.
aoqi@0 405 *
hannesw@1548 406 * @param prototype the prototype of the allocated object
aoqi@0 407 * @return the property map for allocated objects.
aoqi@0 408 */
hannesw@1548 409 PropertyMap getAllocatorMap(final ScriptObject prototype) {
aoqi@0 410 return null;
aoqi@0 411 }
aoqi@0 412
aoqi@0 413 /**
aoqi@0 414 * This method is used to create the immutable portion of a bound function.
sundar@1527 415 * See {@link ScriptFunction#createBound(Object, Object[])}
aoqi@0 416 *
aoqi@0 417 * @param fn the original function being bound
aoqi@0 418 * @param self this reference to bind. Can be null.
aoqi@0 419 * @param args additional arguments to bind. Can be null.
aoqi@0 420 */
aoqi@0 421 ScriptFunctionData makeBoundFunctionData(final ScriptFunction fn, final Object self, final Object[] args) {
aoqi@0 422 final Object[] allArgs = args == null ? ScriptRuntime.EMPTY_ARRAY : args;
aoqi@0 423 final int length = args == null ? 0 : args.length;
aoqi@0 424 // Clear the callee and this flags
aoqi@0 425 final int boundFlags = flags & ~NEEDS_CALLEE & ~USES_THIS;
aoqi@0 426
attila@963 427 final List<CompiledFunction> boundList = new LinkedList<>();
attila@963 428 final ScriptObject runtimeScope = fn.getScope();
lagergren@1028 429 final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope), null);
attila@963 430 boundList.add(bind(bindTarget, fn, self, allArgs));
aoqi@0 431
attila@963 432 return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
aoqi@0 433 }
aoqi@0 434
aoqi@0 435 /**
aoqi@0 436 * Convert this argument for non-strict functions according to ES 10.4.3
aoqi@0 437 *
aoqi@0 438 * @param thiz the this argument
aoqi@0 439 *
aoqi@0 440 * @return the converted this object
aoqi@0 441 */
aoqi@0 442 private Object convertThisObject(final Object thiz) {
attila@963 443 return needsWrappedThis() ? wrapThis(thiz) : thiz;
attila@963 444 }
attila@963 445
attila@963 446 static Object wrapThis(final Object thiz) {
attila@963 447 if (!(thiz instanceof ScriptObject)) {
aoqi@0 448 if (JSType.nullOrUndefined(thiz)) {
aoqi@0 449 return Context.getGlobal();
aoqi@0 450 }
aoqi@0 451
aoqi@0 452 if (isPrimitiveThis(thiz)) {
aoqi@0 453 return Context.getGlobal().wrapAsObject(thiz);
aoqi@0 454 }
aoqi@0 455 }
aoqi@0 456
aoqi@0 457 return thiz;
aoqi@0 458 }
aoqi@0 459
aoqi@0 460 static boolean isPrimitiveThis(final Object obj) {
attila@1251 461 return JSType.isString(obj) || obj instanceof Number || obj instanceof Boolean;
aoqi@0 462 }
aoqi@0 463
aoqi@0 464 /**
aoqi@0 465 * Creates an invoker method handle for a bound function.
aoqi@0 466 *
aoqi@0 467 * @param targetFn the function being bound
aoqi@0 468 * @param originalInvoker an original invoker method handle for the function. This can be its generic invoker or
aoqi@0 469 * any of its specializations.
aoqi@0 470 * @param self the "this" value being bound
aoqi@0 471 * @param args additional arguments being bound
aoqi@0 472 *
aoqi@0 473 * @return a bound invoker method handle that will bind the self value and the specified arguments. The resulting
aoqi@0 474 * invoker never needs a callee; if the original invoker needed it, it will be bound to {@code fn}. The resulting
aoqi@0 475 * invoker still takes an initial {@code this} parameter, but it is always dropped and the bound {@code self} passed
aoqi@0 476 * to the original invoker on invocation.
aoqi@0 477 */
aoqi@0 478 private MethodHandle bindInvokeHandle(final MethodHandle originalInvoker, final ScriptFunction targetFn, final Object self, final Object[] args) {
aoqi@0 479 // Is the target already bound? If it is, we won't bother binding either callee or self as they're already bound
aoqi@0 480 // in the target and will be ignored anyway.
aoqi@0 481 final boolean isTargetBound = targetFn.isBoundFunction();
aoqi@0 482
aoqi@0 483 final boolean needsCallee = needsCallee(originalInvoker);
aoqi@0 484 assert needsCallee == needsCallee() : "callee contract violation 2";
aoqi@0 485 assert !(isTargetBound && needsCallee); // already bound functions don't need a callee
aoqi@0 486
aoqi@0 487 final Object boundSelf = isTargetBound ? null : convertThisObject(self);
aoqi@0 488 final MethodHandle boundInvoker;
aoqi@0 489
aoqi@0 490 if (isVarArg(originalInvoker)) {
aoqi@0 491 // First, bind callee and this without arguments
aoqi@0 492 final MethodHandle noArgBoundInvoker;
aoqi@0 493
aoqi@0 494 if (isTargetBound) {
aoqi@0 495 // Don't bind either callee or this
aoqi@0 496 noArgBoundInvoker = originalInvoker;
aoqi@0 497 } else if (needsCallee) {
aoqi@0 498 // Bind callee and this
aoqi@0 499 noArgBoundInvoker = MH.insertArguments(originalInvoker, 0, targetFn, boundSelf);
aoqi@0 500 } else {
aoqi@0 501 // Only bind this
aoqi@0 502 noArgBoundInvoker = MH.bindTo(originalInvoker, boundSelf);
aoqi@0 503 }
aoqi@0 504 // Now bind arguments
aoqi@0 505 if (args.length > 0) {
aoqi@0 506 boundInvoker = varArgBinder(noArgBoundInvoker, args);
aoqi@0 507 } else {
aoqi@0 508 boundInvoker = noArgBoundInvoker;
aoqi@0 509 }
aoqi@0 510 } else {
aoqi@0 511 // If target is already bound, insert additional bound arguments after "this" argument, at position 1.
aoqi@0 512 final int argInsertPos = isTargetBound ? 1 : 0;
attila@963 513 final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : needsCallee ? 2 : 1))];
aoqi@0 514 int next = 0;
aoqi@0 515 if (!isTargetBound) {
aoqi@0 516 if (needsCallee) {
aoqi@0 517 boundArgs[next++] = targetFn;
aoqi@0 518 }
aoqi@0 519 boundArgs[next++] = boundSelf;
aoqi@0 520 }
aoqi@0 521 // If more bound args were specified than the function can take, we'll just drop those.
aoqi@0 522 System.arraycopy(args, 0, boundArgs, next, boundArgs.length - next);
aoqi@0 523 // If target is already bound, insert additional bound arguments after "this" argument, at position 1;
aoqi@0 524 // "this" will get dropped anyway by the target invoker. We previously asserted that already bound functions
aoqi@0 525 // don't take a callee parameter, so we can know that the signature is (this[, args...]) therefore args
aoqi@0 526 // start at position 1. If the function is not bound, we start inserting arguments at position 0.
aoqi@0 527 boundInvoker = MH.insertArguments(originalInvoker, argInsertPos, boundArgs);
aoqi@0 528 }
aoqi@0 529
aoqi@0 530 if (isTargetBound) {
aoqi@0 531 return boundInvoker;
aoqi@0 532 }
aoqi@0 533
aoqi@0 534 // If the target is not already bound, add a dropArguments that'll throw away the passed this
aoqi@0 535 return MH.dropArguments(boundInvoker, 0, Object.class);
aoqi@0 536 }
aoqi@0 537
aoqi@0 538 /**
aoqi@0 539 * Creates a constructor method handle for a bound function using the passed constructor handle.
aoqi@0 540 *
aoqi@0 541 * @param originalConstructor the constructor handle to bind. It must be a composed constructor.
aoqi@0 542 * @param fn the function being bound
aoqi@0 543 * @param args arguments being bound
aoqi@0 544 *
aoqi@0 545 * @return a bound constructor method handle that will bind the specified arguments. The resulting constructor never
aoqi@0 546 * needs a callee; if the original constructor needed it, it will be bound to {@code fn}. The resulting constructor
aoqi@0 547 * still takes an initial {@code this} parameter and passes it to the underlying original constructor. Finally, if
aoqi@0 548 * this script function data object has no constructor handle, null is returned.
aoqi@0 549 */
aoqi@0 550 private static MethodHandle bindConstructHandle(final MethodHandle originalConstructor, final ScriptFunction fn, final Object[] args) {
aoqi@0 551 assert originalConstructor != null;
aoqi@0 552
aoqi@0 553 // If target function is already bound, don't bother binding the callee.
aoqi@0 554 final MethodHandle calleeBoundConstructor = fn.isBoundFunction() ? originalConstructor :
aoqi@0 555 MH.dropArguments(MH.bindTo(originalConstructor, fn), 0, ScriptFunction.class);
aoqi@0 556
aoqi@0 557 if (args.length == 0) {
aoqi@0 558 return calleeBoundConstructor;
aoqi@0 559 }
aoqi@0 560
aoqi@0 561 if (isVarArg(calleeBoundConstructor)) {
aoqi@0 562 return varArgBinder(calleeBoundConstructor, args);
aoqi@0 563 }
aoqi@0 564
aoqi@0 565 final Object[] boundArgs;
aoqi@0 566
aoqi@0 567 final int maxArgCount = calleeBoundConstructor.type().parameterCount() - 1;
aoqi@0 568 if (args.length <= maxArgCount) {
aoqi@0 569 boundArgs = args;
aoqi@0 570 } else {
aoqi@0 571 boundArgs = new Object[maxArgCount];
aoqi@0 572 System.arraycopy(args, 0, boundArgs, 0, maxArgCount);
aoqi@0 573 }
aoqi@0 574
aoqi@0 575 return MH.insertArguments(calleeBoundConstructor, 1, boundArgs);
aoqi@0 576 }
aoqi@0 577
aoqi@0 578 /**
attila@963 579 * Takes a method handle, and returns a potentially different method handle that can be used in
attila@963 580 * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
attila@963 581 * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
attila@963 582 * {@code Object} as well, except for the following ones:
attila@963 583 * <ul>
attila@963 584 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
attila@963 585 * <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
attila@963 586 * (callee) as an argument.</li>
attila@963 587 * </ul>
attila@963 588 *
attila@963 589 * @param mh the original method handle
attila@963 590 *
attila@963 591 * @return the new handle, conforming to the rules above.
attila@963 592 */
attila@963 593 private static MethodHandle makeGenericMethod(final MethodHandle mh) {
attila@963 594 final MethodType type = mh.type();
attila@963 595 final MethodType newType = makeGenericType(type);
attila@963 596 return type.equals(newType) ? mh : mh.asType(newType);
attila@963 597 }
attila@963 598
attila@963 599 private static MethodType makeGenericType(final MethodType type) {
attila@963 600 MethodType newType = type.generic();
attila@963 601 if (isVarArg(type)) {
attila@963 602 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
attila@963 603 }
attila@963 604 if (needsCallee(type)) {
attila@963 605 newType = newType.changeParameterType(0, ScriptFunction.class);
attila@963 606 }
attila@963 607 return newType;
attila@963 608 }
attila@963 609
attila@963 610 /**
aoqi@0 611 * Execute this script function.
aoqi@0 612 *
aoqi@0 613 * @param self Target object.
aoqi@0 614 * @param arguments Call arguments.
aoqi@0 615 * @return ScriptFunction result.
aoqi@0 616 *
aoqi@0 617 * @throws Throwable if there is an exception/error with the invocation or thrown from it
aoqi@0 618 */
aoqi@0 619 Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
attila@963 620 final MethodHandle mh = getGenericInvoker(fn.getScope());
attila@963 621 final Object selfObj = convertThisObject(self);
attila@963 622 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
aoqi@0 623
aoqi@0 624 DebuggerSupport.notifyInvoke(mh);
aoqi@0 625
aoqi@0 626 if (isVarArg(mh)) {
aoqi@0 627 if (needsCallee(mh)) {
aoqi@0 628 return mh.invokeExact(fn, selfObj, args);
aoqi@0 629 }
aoqi@0 630 return mh.invokeExact(selfObj, args);
aoqi@0 631 }
aoqi@0 632
aoqi@0 633 final int paramCount = mh.type().parameterCount();
aoqi@0 634 if (needsCallee(mh)) {
aoqi@0 635 switch (paramCount) {
aoqi@0 636 case 2:
aoqi@0 637 return mh.invokeExact(fn, selfObj);
aoqi@0 638 case 3:
aoqi@0 639 return mh.invokeExact(fn, selfObj, getArg(args, 0));
aoqi@0 640 case 4:
aoqi@0 641 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
aoqi@0 642 case 5:
aoqi@0 643 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
aoqi@0 644 case 6:
aoqi@0 645 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
aoqi@0 646 case 7:
aoqi@0 647 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
aoqi@0 648 case 8:
aoqi@0 649 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
aoqi@0 650 default:
aoqi@0 651 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
aoqi@0 652 }
aoqi@0 653 }
aoqi@0 654
aoqi@0 655 switch (paramCount) {
aoqi@0 656 case 1:
aoqi@0 657 return mh.invokeExact(selfObj);
aoqi@0 658 case 2:
aoqi@0 659 return mh.invokeExact(selfObj, getArg(args, 0));
aoqi@0 660 case 3:
aoqi@0 661 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
aoqi@0 662 case 4:
aoqi@0 663 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
aoqi@0 664 case 5:
aoqi@0 665 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
aoqi@0 666 case 6:
aoqi@0 667 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
aoqi@0 668 case 7:
aoqi@0 669 return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
aoqi@0 670 default:
aoqi@0 671 return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
aoqi@0 672 }
aoqi@0 673 }
aoqi@0 674
aoqi@0 675 Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
attila@963 676 final MethodHandle mh = getGenericConstructor(fn.getScope());
aoqi@0 677 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
aoqi@0 678
aoqi@0 679 DebuggerSupport.notifyInvoke(mh);
aoqi@0 680
aoqi@0 681 if (isVarArg(mh)) {
aoqi@0 682 if (needsCallee(mh)) {
aoqi@0 683 return mh.invokeExact(fn, args);
aoqi@0 684 }
aoqi@0 685 return mh.invokeExact(args);
aoqi@0 686 }
aoqi@0 687
aoqi@0 688 final int paramCount = mh.type().parameterCount();
aoqi@0 689 if (needsCallee(mh)) {
aoqi@0 690 switch (paramCount) {
aoqi@0 691 case 1:
aoqi@0 692 return mh.invokeExact(fn);
aoqi@0 693 case 2:
aoqi@0 694 return mh.invokeExact(fn, getArg(args, 0));
aoqi@0 695 case 3:
aoqi@0 696 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
aoqi@0 697 case 4:
aoqi@0 698 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
aoqi@0 699 case 5:
aoqi@0 700 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
aoqi@0 701 case 6:
aoqi@0 702 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
aoqi@0 703 case 7:
aoqi@0 704 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
aoqi@0 705 default:
aoqi@0 706 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
aoqi@0 707 }
aoqi@0 708 }
aoqi@0 709
aoqi@0 710 switch (paramCount) {
aoqi@0 711 case 0:
aoqi@0 712 return mh.invokeExact();
aoqi@0 713 case 1:
aoqi@0 714 return mh.invokeExact(getArg(args, 0));
aoqi@0 715 case 2:
aoqi@0 716 return mh.invokeExact(getArg(args, 0), getArg(args, 1));
aoqi@0 717 case 3:
aoqi@0 718 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
aoqi@0 719 case 4:
aoqi@0 720 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
aoqi@0 721 case 5:
aoqi@0 722 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
aoqi@0 723 case 6:
aoqi@0 724 return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
aoqi@0 725 default:
aoqi@0 726 return mh.invokeWithArguments(withArguments(null, paramCount, args));
aoqi@0 727 }
aoqi@0 728 }
aoqi@0 729
aoqi@0 730 private static Object getArg(final Object[] args, final int i) {
aoqi@0 731 return i < args.length ? args[i] : UNDEFINED;
aoqi@0 732 }
aoqi@0 733
aoqi@0 734 private static Object[] withArguments(final ScriptFunction fn, final int argCount, final Object[] args) {
aoqi@0 735 final Object[] finalArgs = new Object[argCount];
aoqi@0 736
aoqi@0 737 int nextArg = 0;
aoqi@0 738 if (fn != null) {
aoqi@0 739 //needs callee
aoqi@0 740 finalArgs[nextArg++] = fn;
aoqi@0 741 }
aoqi@0 742
aoqi@0 743 // Don't add more args that there is argCount in the handle (including self and callee).
aoqi@0 744 for (int i = 0; i < args.length && nextArg < argCount;) {
aoqi@0 745 finalArgs[nextArg++] = args[i++];
aoqi@0 746 }
aoqi@0 747
aoqi@0 748 // If we have fewer args than argCount, pad with undefined.
aoqi@0 749 while (nextArg < argCount) {
aoqi@0 750 finalArgs[nextArg++] = UNDEFINED;
aoqi@0 751 }
aoqi@0 752
aoqi@0 753 return finalArgs;
aoqi@0 754 }
aoqi@0 755
aoqi@0 756 private static Object[] withArguments(final ScriptFunction fn, final Object self, final int argCount, final Object[] args) {
aoqi@0 757 final Object[] finalArgs = new Object[argCount];
aoqi@0 758
aoqi@0 759 int nextArg = 0;
aoqi@0 760 if (fn != null) {
aoqi@0 761 //needs callee
aoqi@0 762 finalArgs[nextArg++] = fn;
aoqi@0 763 }
aoqi@0 764 finalArgs[nextArg++] = self;
aoqi@0 765
aoqi@0 766 // Don't add more args that there is argCount in the handle (including self and callee).
aoqi@0 767 for (int i = 0; i < args.length && nextArg < argCount;) {
aoqi@0 768 finalArgs[nextArg++] = args[i++];
aoqi@0 769 }
aoqi@0 770
aoqi@0 771 // If we have fewer args than argCount, pad with undefined.
aoqi@0 772 while (nextArg < argCount) {
aoqi@0 773 finalArgs[nextArg++] = UNDEFINED;
aoqi@0 774 }
aoqi@0 775
aoqi@0 776 return finalArgs;
aoqi@0 777 }
aoqi@0 778 /**
aoqi@0 779 * Takes a variable-arity method and binds a variable number of arguments in it. The returned method will filter the
aoqi@0 780 * vararg array and pass a different array that prepends the bound arguments in front of the arguments passed on
aoqi@0 781 * invocation
aoqi@0 782 *
aoqi@0 783 * @param mh the handle
aoqi@0 784 * @param args the bound arguments
aoqi@0 785 *
aoqi@0 786 * @return the bound method handle
aoqi@0 787 */
aoqi@0 788 private static MethodHandle varArgBinder(final MethodHandle mh, final Object[] args) {
aoqi@0 789 assert args != null;
aoqi@0 790 assert args.length > 0;
aoqi@0 791 return MH.filterArguments(mh, mh.type().parameterCount() - 1, MH.bindTo(BIND_VAR_ARGS, args));
aoqi@0 792 }
aoqi@0 793
aoqi@0 794 /**
aoqi@0 795 * Heuristic to figure out if the method handle has a callee argument. If it's type is
aoqi@0 796 * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
aoqi@0 797 * the constructor above is not passed this information, and can't just blindly assume it's false
aoqi@0 798 * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
aoqi@0 799 * they also always receive a callee).
aoqi@0 800 *
aoqi@0 801 * @param mh the examined method handle
aoqi@0 802 *
aoqi@0 803 * @return true if the method handle expects a callee, false otherwise
aoqi@0 804 */
aoqi@0 805 protected static boolean needsCallee(final MethodHandle mh) {
attila@963 806 return needsCallee(mh.type());
attila@963 807 }
attila@963 808
attila@963 809 static boolean needsCallee(final MethodType type) {
attila@963 810 final int length = type.parameterCount();
attila@963 811
attila@963 812 if (length == 0) {
attila@963 813 return false;
attila@963 814 }
attila@963 815
attila@963 816 final Class<?> param0 = type.parameterType(0);
attila@963 817 return param0 == ScriptFunction.class || param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class;
aoqi@0 818 }
aoqi@0 819
aoqi@0 820 /**
aoqi@0 821 * Check if a javascript function methodhandle is a vararg handle
aoqi@0 822 *
aoqi@0 823 * @param mh method handle to check
aoqi@0 824 *
aoqi@0 825 * @return true if vararg
aoqi@0 826 */
aoqi@0 827 protected static boolean isVarArg(final MethodHandle mh) {
attila@963 828 return isVarArg(mh.type());
attila@963 829 }
attila@963 830
attila@963 831 static boolean isVarArg(final MethodType type) {
aoqi@0 832 return type.parameterType(type.parameterCount() - 1).isArray();
aoqi@0 833 }
aoqi@0 834
attila@963 835 /**
attila@963 836 * Is this ScriptFunction declared in a dynamic context
attila@963 837 * @return true if in dynamic context, false if not or irrelevant
attila@963 838 */
attila@963 839 public boolean inDynamicContext() {
attila@963 840 return false;
attila@963 841 }
attila@963 842
aoqi@0 843 @SuppressWarnings("unused")
aoqi@0 844 private static Object[] bindVarArgs(final Object[] array1, final Object[] array2) {
aoqi@0 845 if (array2 == null) {
aoqi@0 846 // Must clone it, as we can't allow the receiving method to alter the array
aoqi@0 847 return array1.clone();
aoqi@0 848 }
aoqi@0 849
aoqi@0 850 final int l2 = array2.length;
aoqi@0 851 if (l2 == 0) {
aoqi@0 852 return array1.clone();
aoqi@0 853 }
aoqi@0 854
aoqi@0 855 final int l1 = array1.length;
aoqi@0 856 final Object[] concat = new Object[l1 + l2];
aoqi@0 857 System.arraycopy(array1, 0, concat, 0, l1);
aoqi@0 858 System.arraycopy(array2, 0, concat, l1, l2);
aoqi@0 859
aoqi@0 860 return concat;
aoqi@0 861 }
aoqi@0 862
aoqi@0 863 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
aoqi@0 864 return MH.findStatic(MethodHandles.lookup(), ScriptFunctionData.class, name, MH.type(rtype, types));
aoqi@0 865 }
attila@963 866
attila@963 867 /**
attila@963 868 * This class is used to hold the generic invoker and generic constructor pair. It is structured in this way since
attila@963 869 * most functions will never use them, so this way ScriptFunctionData only pays storage cost for one null reference
attila@963 870 * to the GenericInvokers object, instead of two null references for the two method handles.
attila@963 871 */
attila@963 872 private static final class GenericInvokers {
attila@963 873 volatile MethodHandle invoker;
attila@963 874 volatile MethodHandle constructor;
attila@963 875 }
attila@963 876
attila@963 877 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
attila@963 878 in.defaultReadObject();
attila@963 879 code = new LinkedList<>();
attila@963 880 }
aoqi@0 881 }

mercurial