Thu, 24 May 2018 16:39:31 +0800
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 | } |