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

Tue, 22 Jan 2013 14:14:37 +0100

author
hannesw
date
Tue, 22 Jan 2013 14:14:37 +0100
changeset 42
935dcec38e9a
parent 29
1d0307c2bb4c
child 44
e62dba3ce52b
permissions
-rw-r--r--

8006570: This-value for non-strict functions should be converted to object
Reviewed-by: jlaskey, lagergren, attila

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.runtime;
jlaskey@3 27
jlaskey@3 28 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
jlaskey@3 29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
jlaskey@3 30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
jlaskey@3 31 import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
jlaskey@3 32
jlaskey@3 33 import java.lang.invoke.MethodHandle;
jlaskey@3 34 import java.lang.invoke.MethodHandles;
jlaskey@3 35 import java.lang.invoke.MethodType;
jlaskey@3 36 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
jlaskey@3 37 import jdk.nashorn.internal.codegen.types.Type;
jlaskey@3 38 import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
jlaskey@3 39 import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
jlaskey@3 40 import jdk.nashorn.internal.parser.Token;
jlaskey@3 41 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
jlaskey@3 42 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
hannesw@42 43 import jdk.nashorn.internal.runtime.linker.NashornGuardedInvocation;
jlaskey@3 44 import jdk.nashorn.internal.runtime.linker.NashornGuards;
jlaskey@3 45 import jdk.nashorn.internal.runtime.options.Options;
jlaskey@3 46 import org.dynalang.dynalink.CallSiteDescriptor;
jlaskey@3 47 import org.dynalang.dynalink.linker.GuardedInvocation;
jlaskey@3 48
jlaskey@3 49 /**
jlaskey@3 50 * Runtime representation of a JavaScript function.
jlaskey@3 51 */
jlaskey@3 52 public abstract class ScriptFunction extends ScriptObject {
jlaskey@3 53
jlaskey@3 54 /** Method handle for prototype getter for this ScriptFunction */
jlaskey@3 55 public static final MethodHandle G$PROTOTYPE = findOwnMH("G$prototype", Object.class, Object.class);
jlaskey@3 56
jlaskey@3 57 /** Method handle for prototype setter for this ScriptFunction */
jlaskey@3 58 public static final MethodHandle S$PROTOTYPE = findOwnMH("S$prototype", void.class, Object.class, Object.class);
jlaskey@3 59
jlaskey@3 60 /** Method handle for length getter for this ScriptFunction */
jlaskey@3 61 public static final MethodHandle G$LENGTH = findOwnMH("G$length", int.class, Object.class);
jlaskey@3 62
jlaskey@3 63 /** Method handle for name getter for this ScriptFunction */
jlaskey@3 64 public static final MethodHandle G$NAME = findOwnMH("G$name", Object.class, Object.class);
jlaskey@3 65
jlaskey@3 66 /** Method handle for allocate function for this ScriptFunction */
jlaskey@3 67 public static final MethodHandle ALLOCATE = findOwnMH("allocate", Object.class);
jlaskey@3 68
jlaskey@3 69 private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class);
jlaskey@3 70
jlaskey@3 71 /** method handle to arity setter for this ScriptFunction */
jlaskey@3 72 public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class);
jlaskey@3 73 /** method handle to scope getter for this ScriptFunction */
jlaskey@3 74 public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class);
jlaskey@3 75
jlaskey@3 76 /** Should specialized function and specialized constructors for the builtin be used if available? */
jlaskey@3 77 private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable");
jlaskey@3 78
jlaskey@3 79 /** Name of function or null. */
jlaskey@3 80 private final String name;
jlaskey@3 81
jlaskey@3 82 /** Source of function. */
jlaskey@3 83 private final Source source;
jlaskey@3 84
jlaskey@3 85 /** Start position and length in source. */
jlaskey@3 86 private final long token;
jlaskey@3 87
jlaskey@3 88 /** Reference to code for this method. */
jlaskey@3 89 private final MethodHandle invokeHandle;
jlaskey@3 90
jlaskey@3 91 /** Reference to code for this method when called to create "new" object */
jlaskey@3 92 protected MethodHandle constructHandle;
jlaskey@3 93
jlaskey@3 94 /** Reference to constructor prototype. */
jlaskey@3 95 protected Object prototype;
jlaskey@3 96
jlaskey@3 97 /** Constructor to create a new instance. */
jlaskey@3 98 private MethodHandle allocator;
jlaskey@3 99
jlaskey@3 100 /** Map for new instance constructor. */
jlaskey@3 101 private PropertyMap allocatorMap;
jlaskey@3 102
jlaskey@3 103 /** The parent scope. */
jlaskey@3 104 private final ScriptObject scope;
jlaskey@3 105
jlaskey@3 106 /** Specializations - see @SpecializedFunction */
jlaskey@3 107 private MethodHandle[] invokeSpecializations;
jlaskey@3 108
jlaskey@3 109 /** Specializations - see @SpecializedFunction */
jlaskey@3 110 private MethodHandle[] constructSpecializations;
jlaskey@3 111
jlaskey@3 112 /** This field is either computed in constructor or set explicitly by calling setArity method. */
jlaskey@3 113 private int arity;
jlaskey@3 114
jlaskey@3 115 /**
jlaskey@3 116 * Constructor
jlaskey@3 117 *
jlaskey@3 118 * @param name function name
jlaskey@3 119 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
jlaskey@3 120 * @param map property map
jlaskey@3 121 * @param scope scope
jlaskey@3 122 * @param specs specialized version of this function - other method handles
jlaskey@3 123 */
jlaskey@3 124 protected ScriptFunction(
jlaskey@3 125 final String name,
jlaskey@3 126 final MethodHandle methodHandle,
jlaskey@3 127 final PropertyMap map,
jlaskey@3 128 final ScriptObject scope,
jlaskey@3 129 final MethodHandle[] specs) {
jlaskey@3 130 this(name, methodHandle, map, scope, null, 0, false, specs);
jlaskey@3 131 }
jlaskey@3 132
jlaskey@3 133 /**
jlaskey@3 134 * Constructor
jlaskey@3 135 *
jlaskey@3 136 * @param name function name
jlaskey@3 137 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
jlaskey@3 138 * @param map property map
jlaskey@3 139 * @param scope scope
jlaskey@3 140 * @param source the source
jlaskey@3 141 * @param token token
jlaskey@3 142 * @param allocator method handle to this function's allocator - see JO$ classes
jlaskey@3 143 * @param allocatorMap property map to be used for all constructors
jlaskey@3 144 * @param needsCallee does this method use the {@code callee} variable
jlaskey@3 145 * @param specs specialized version of this function - other method handles
jlaskey@3 146 */
jlaskey@3 147 protected ScriptFunction(
jlaskey@3 148 final String name,
jlaskey@3 149 final MethodHandle methodHandle,
jlaskey@3 150 final PropertyMap map,
jlaskey@3 151 final ScriptObject scope,
jlaskey@3 152 final Source source,
jlaskey@3 153 final long token,
jlaskey@3 154 final MethodHandle allocator,
jlaskey@3 155 final PropertyMap allocatorMap,
jlaskey@3 156 final boolean needsCallee,
jlaskey@3 157 final MethodHandle[] specs) {
jlaskey@3 158
jlaskey@3 159 this(name, methodHandle, map, scope, source, token, needsCallee, specs);
jlaskey@3 160
jlaskey@3 161 //this is the internal constructor
jlaskey@3 162
jlaskey@3 163 this.allocator = allocator;
jlaskey@3 164 this.allocatorMap = allocatorMap;
jlaskey@3 165 }
jlaskey@3 166
jlaskey@3 167 /**
jlaskey@3 168 * Constructor
jlaskey@3 169 *
jlaskey@3 170 * @param name function name
jlaskey@3 171 * @param methodHandle method handle to function (if specializations are present, assumed to be most generic)
jlaskey@3 172 * @param map property map
jlaskey@3 173 * @param scope scope
jlaskey@3 174 * @param source the source
jlaskey@3 175 * @param token token
jlaskey@3 176 * @param needsCallee does this method use the {@code callee} variable
jlaskey@3 177 * @param specs specialized version of this function - other method handles
jlaskey@3 178 */
jlaskey@3 179 protected ScriptFunction(
jlaskey@3 180 final String name,
jlaskey@3 181 final MethodHandle methodHandle,
jlaskey@3 182 final PropertyMap map,
jlaskey@3 183 final ScriptObject scope,
jlaskey@3 184 final Source source,
jlaskey@3 185 final long token,
jlaskey@3 186 final boolean needsCallee,
jlaskey@3 187 final MethodHandle[] specs) {
jlaskey@3 188
jlaskey@3 189 super(map);
jlaskey@3 190
jlaskey@3 191 if (Context.DEBUG) {
jlaskey@3 192 constructorCount++;
jlaskey@3 193 }
jlaskey@3 194
jlaskey@3 195 // needCallee => scope != null
jlaskey@3 196 assert !needsCallee || scope != null;
jlaskey@3 197 this.name = name;
jlaskey@3 198 this.source = source;
jlaskey@3 199 this.token = token;
jlaskey@3 200 this.scope = scope;
jlaskey@3 201
jlaskey@3 202 final MethodType type = methodHandle.type();
jlaskey@3 203 final int paramCount = type.parameterCount();
jlaskey@3 204 final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
jlaskey@3 205
jlaskey@3 206 final MethodHandle mh = MH.asType(methodHandle, adaptType(type, scope != null, isVarArg));
jlaskey@3 207
jlaskey@3 208 this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
jlaskey@3 209
jlaskey@3 210 if (scope != null) {
jlaskey@3 211 if (needsCallee) {
jlaskey@3 212 if (!isVarArg) {
jlaskey@3 213 this.arity--;
jlaskey@3 214 }
jlaskey@3 215 }
jlaskey@3 216 this.invokeHandle = mh;
jlaskey@3 217 this.constructHandle = mh;
jlaskey@3 218 } else if (isConstructor(mh)) {
jlaskey@3 219 if (!isVarArg) {
jlaskey@3 220 this.arity--; // drop the boolean flag for arity
jlaskey@3 221 }
jlaskey@3 222 /*
jlaskey@3 223 * We insert a boolean argument to tell if the method was invoked as
jlaskey@3 224 * constructor or not if the method handle's first argument is boolean.
jlaskey@3 225 */
jlaskey@3 226 this.invokeHandle = MH.insertArguments(mh, 0, false);
jlaskey@3 227 this.constructHandle = MH.insertArguments(mh, 0, true);
jlaskey@3 228
jlaskey@3 229 if (specs != null) {
jlaskey@3 230 this.invokeSpecializations = new MethodHandle[specs.length];
jlaskey@3 231 this.constructSpecializations = new MethodHandle[specs.length];
jlaskey@3 232 for (int i = 0; i < specs.length; i++) {
jlaskey@3 233 this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
jlaskey@3 234 this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true);
jlaskey@3 235 }
jlaskey@3 236 }
jlaskey@3 237 } else {
jlaskey@3 238 this.invokeHandle = mh;
jlaskey@3 239 this.constructHandle = mh;
jlaskey@3 240 this.invokeSpecializations = specs;
jlaskey@3 241 this.constructSpecializations = specs;
jlaskey@3 242 }
jlaskey@3 243 }
jlaskey@3 244
jlaskey@3 245 /**
jlaskey@3 246 * Takes a method type, and returns a (potentially different method type) that the method handles used by
jlaskey@3 247 * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and
jlaskey@3 248 * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will
jlaskey@3 249 * have all its parameters turned into {@code Object} as well, except for the following ones:
jlaskey@3 250 * <ul>
jlaskey@3 251 * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor
jlaskey@3 252 * invocation,</li>
jlaskey@3 253 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
jlaskey@3 254 * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be
jlaskey@3 255 * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li>
jlaskey@3 256 * @param type the original type
jlaskey@3 257 * @param hasCallee true if the function uses the callee argument
jlaskey@3 258 * @param isVarArg if the function is a vararg
jlaskey@3 259 * @return the new type, conforming to the rules above.
jlaskey@3 260 */
jlaskey@3 261 private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) {
jlaskey@3 262 // Generify broadly
jlaskey@3 263 MethodType newType = type.generic().changeReturnType(Object.class);
jlaskey@3 264 if(isVarArg) {
jlaskey@3 265 // Change back to vararg if we over-generified it
jlaskey@3 266 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
jlaskey@3 267 }
jlaskey@3 268 final boolean hasBoolean = type.parameterType(0) == boolean.class;
jlaskey@3 269 if(hasBoolean) {
jlaskey@3 270 // Restore the initial boolean argument
jlaskey@3 271 newType = newType.changeParameterType(0, boolean.class);
jlaskey@3 272 }
jlaskey@3 273 if(hasCallee) {
jlaskey@3 274 // Restore the ScriptFunction argument
jlaskey@3 275 newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class);
jlaskey@3 276 }
jlaskey@3 277 return newType;
jlaskey@3 278 }
jlaskey@3 279
jlaskey@3 280 @Override
jlaskey@3 281 public String getClassName() {
jlaskey@3 282 return "Function";
jlaskey@3 283 }
jlaskey@3 284
jlaskey@3 285 /**
jlaskey@3 286 * ECMA 15.3.5.3 [[HasInstance]] (V)
jlaskey@3 287 * Step 3 if "prototype" value is not an Object, throw TypeError
jlaskey@3 288 */
jlaskey@3 289 @Override
jlaskey@3 290 public boolean isInstance(final ScriptObject instance) {
jlaskey@3 291 if (!(prototype instanceof ScriptObject)) {
jlaskey@3 292 typeError(Context.getGlobal(), "prototype.not.an.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(prototype));
jlaskey@3 293 }
jlaskey@3 294
jlaskey@3 295 for (ScriptObject proto = instance.getProto(); proto != null; proto = proto.getProto()) {
jlaskey@3 296 if (proto == prototype) {
jlaskey@3 297 return true;
jlaskey@3 298 }
jlaskey@3 299 }
jlaskey@3 300
jlaskey@3 301 return false;
jlaskey@3 302 }
jlaskey@3 303
jlaskey@3 304 /**
jlaskey@3 305 * Get the arity of this ScriptFunction
jlaskey@3 306 * @return arity
jlaskey@3 307 */
jlaskey@3 308 public final int getArity() {
jlaskey@3 309 return arity;
jlaskey@3 310 }
jlaskey@3 311
jlaskey@3 312 /**
jlaskey@3 313 * Set the arity of this ScriptFunction
jlaskey@3 314 * @param arity arity
jlaskey@3 315 */
jlaskey@3 316 public final void setArity(final int arity) {
jlaskey@3 317 this.arity = arity;
jlaskey@3 318 }
jlaskey@3 319
jlaskey@3 320 /**
jlaskey@3 321 * Is this a ECMAScript 'use strict' function?
jlaskey@3 322 * @return true if function is in strict mode
jlaskey@3 323 */
jlaskey@3 324 public abstract boolean isStrict();
jlaskey@3 325
jlaskey@3 326 /**
jlaskey@3 327 * Is this a ECMAScript built-in function (like parseInt, Array.isArray) ?
jlaskey@3 328 * @return true if built-in
jlaskey@3 329 */
jlaskey@3 330 public abstract boolean isBuiltin();
jlaskey@3 331
jlaskey@3 332 /**
hannesw@42 333 * Is this a non-strict (not built-in) script function?
hannesw@42 334 * @return true if neither strict nor built-in
hannesw@42 335 */
hannesw@42 336 public boolean isNonStrictFunction() {
hannesw@42 337 return !isStrict() && !isBuiltin();
hannesw@42 338 }
hannesw@42 339
hannesw@42 340 /**
jlaskey@3 341 * Execute this script function.
jlaskey@3 342 * @param self Target object.
jlaskey@3 343 * @param arguments Call arguments.
jlaskey@3 344 * @return ScriptFunction result.
jlaskey@3 345 * @throws Throwable if there is an exception/error with the invocation or thrown from it
jlaskey@3 346 */
jlaskey@3 347 public Object invoke(final Object self, final Object... arguments) throws Throwable {
jlaskey@3 348 if (Context.DEBUG) {
jlaskey@3 349 invokes++;
jlaskey@3 350 }
jlaskey@3 351
jlaskey@3 352 final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
jlaskey@3 353
jlaskey@3 354 if (isVarArg(invokeHandle)) {
jlaskey@3 355 if (hasCalleeParameter()) {
jlaskey@3 356 return invokeHandle.invokeExact(self, this, args);
jlaskey@3 357 }
jlaskey@3 358 return invokeHandle.invokeExact(self, args);
jlaskey@3 359 }
jlaskey@3 360
jlaskey@3 361 final int paramCount = invokeHandle.type().parameterCount();
jlaskey@3 362 if (hasCalleeParameter()) {
jlaskey@3 363 switch (paramCount) {
jlaskey@3 364 case 2:
jlaskey@3 365 return invokeHandle.invokeExact(self, this);
jlaskey@3 366 case 3:
jlaskey@3 367 return invokeHandle.invokeExact(self, this, getArg(args, 0));
jlaskey@3 368 case 4:
jlaskey@3 369 return invokeHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
jlaskey@3 370 case 5:
jlaskey@3 371 return invokeHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
jlaskey@3 372 default:
jlaskey@3 373 return invokeHandle.invokeWithArguments(withArguments(self, this, paramCount, args));
jlaskey@3 374 }
jlaskey@3 375 }
jlaskey@3 376
jlaskey@3 377 switch (paramCount) {
jlaskey@3 378 case 1:
jlaskey@3 379 return invokeHandle.invokeExact(self);
jlaskey@3 380 case 2:
jlaskey@3 381 return invokeHandle.invokeExact(self, getArg(args, 0));
jlaskey@3 382 case 3:
jlaskey@3 383 return invokeHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
jlaskey@3 384 case 4:
jlaskey@3 385 return invokeHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
jlaskey@3 386 default:
jlaskey@3 387 return invokeHandle.invokeWithArguments(withArguments(self, null, paramCount, args));
jlaskey@3 388 }
jlaskey@3 389 }
jlaskey@3 390
jlaskey@3 391 private static Object getArg(final Object[] args, final int i) {
jlaskey@3 392 return i < args.length ? args[i] : UNDEFINED;
jlaskey@3 393 }
jlaskey@3 394
jlaskey@3 395 /**
jlaskey@3 396 * Construct new object using this constructor.
jlaskey@3 397 * @param self Target object.
jlaskey@3 398 * @param args Call arguments.
jlaskey@3 399 * @return ScriptFunction result.
jlaskey@3 400 * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it
jlaskey@3 401 */
jlaskey@3 402 public Object construct(final Object self, final Object... args) throws Throwable {
jlaskey@3 403 if (constructHandle == null) {
jlaskey@3 404 typeError(Context.getGlobal(), "not.a.constructor", ScriptRuntime.safeToString(this));
jlaskey@3 405 }
jlaskey@3 406
jlaskey@3 407 if (isVarArg(constructHandle)) {
jlaskey@3 408 if (hasCalleeParameter()) {
jlaskey@3 409 return constructHandle.invokeExact(self, this, args);
jlaskey@3 410 }
jlaskey@3 411 return constructHandle.invokeExact(self, args);
jlaskey@3 412 }
jlaskey@3 413
jlaskey@3 414 final int paramCount = constructHandle.type().parameterCount();
jlaskey@3 415 if (hasCalleeParameter()) {
jlaskey@3 416 switch (paramCount) {
jlaskey@3 417 case 2:
jlaskey@3 418 return constructHandle.invokeExact(self, this);
jlaskey@3 419 case 3:
jlaskey@3 420 return constructHandle.invokeExact(self, this, getArg(args, 0));
jlaskey@3 421 case 4:
jlaskey@3 422 return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1));
jlaskey@3 423 case 5:
jlaskey@3 424 return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2));
jlaskey@3 425 default:
jlaskey@3 426 return constructHandle.invokeWithArguments(withArguments(self, this, args));
jlaskey@3 427 }
jlaskey@3 428 }
jlaskey@3 429
jlaskey@3 430 switch(paramCount) {
jlaskey@3 431 case 1:
jlaskey@3 432 return constructHandle.invokeExact(self);
jlaskey@3 433 case 2:
jlaskey@3 434 return constructHandle.invokeExact(self, getArg(args, 0));
jlaskey@3 435 case 3:
jlaskey@3 436 return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1));
jlaskey@3 437 case 4:
jlaskey@3 438 return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2));
jlaskey@3 439 default:
jlaskey@3 440 return constructHandle.invokeWithArguments(withArguments(self, null, args));
jlaskey@3 441 }
jlaskey@3 442 }
jlaskey@3 443
jlaskey@3 444 private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) {
jlaskey@3 445 return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function
jlaskey@3 446 }
jlaskey@3 447
jlaskey@3 448 private static Object[] withArguments(final Object self, final ScriptFunction function, final int paramCount, final Object... args) {
jlaskey@3 449 final Object[] finalArgs = new Object[paramCount];
jlaskey@3 450
jlaskey@3 451 finalArgs[0] = self;
jlaskey@3 452 int nextArg = 1;
jlaskey@3 453 if (function != null) {
jlaskey@3 454 finalArgs[nextArg++] = function;
jlaskey@3 455 }
jlaskey@3 456
jlaskey@3 457 //don't add more args that there is paramcount in the handle (including self)
jlaskey@3 458 final int maxArgs = Math.min(args.length, paramCount - (function == null ? 1 : 2));
jlaskey@3 459 for (int i = 0; i < maxArgs;) {
jlaskey@3 460 finalArgs[nextArg++] = args[i++];
jlaskey@3 461 }
jlaskey@3 462
jlaskey@3 463 //if we have fewer params than paramcount, pad undefined
jlaskey@3 464 while (nextArg < paramCount) {
jlaskey@3 465 finalArgs[nextArg++] = UNDEFINED;
jlaskey@3 466 }
jlaskey@3 467
jlaskey@3 468 return finalArgs;
jlaskey@3 469 }
jlaskey@3 470
jlaskey@3 471 /**
jlaskey@3 472 * Allocate function. Called from generated {@link ScriptObject} code
jlaskey@3 473 * for allocation as a factory method
jlaskey@3 474 *
jlaskey@3 475 * @return a new instance of the {@link ScriptObject} whose allocator this is
jlaskey@3 476 */
jlaskey@3 477 public Object allocate() {
jlaskey@3 478 if (Context.DEBUG) {
jlaskey@3 479 allocations++;
jlaskey@3 480 }
jlaskey@3 481
jlaskey@3 482 if (getConstructHandle() == null) {
jlaskey@3 483 typeError(Context.getGlobal(), "not.a.constructor", ScriptRuntime.safeToString(this));
jlaskey@3 484 }
jlaskey@3 485
jlaskey@3 486 ScriptObject object = null;
jlaskey@3 487
jlaskey@3 488 if (allocator != null) {
jlaskey@3 489 try {
jlaskey@3 490 object = (ScriptObject)allocator.invokeExact(allocatorMap);
jlaskey@3 491 } catch (final RuntimeException | Error e) {
jlaskey@3 492 throw e;
jlaskey@3 493 } catch (final Throwable t) {
jlaskey@3 494 throw new RuntimeException(t);
jlaskey@3 495 }
jlaskey@3 496 }
jlaskey@3 497
jlaskey@3 498 if (object != null) {
jlaskey@3 499 if (prototype instanceof ScriptObject) {
jlaskey@3 500 object.setProto((ScriptObject)prototype);
jlaskey@3 501 }
jlaskey@3 502
jlaskey@3 503 if (object.getProto() == null) {
jlaskey@3 504 object.setProto(getObjectPrototype());
jlaskey@3 505 }
jlaskey@3 506 }
jlaskey@3 507
jlaskey@3 508 return object;
jlaskey@3 509 }
jlaskey@3 510
jlaskey@3 511 /**
jlaskey@3 512 * Return Object.prototype - used by "allocate"
jlaskey@3 513 * @return Object.prototype
jlaskey@3 514 */
jlaskey@3 515 protected abstract ScriptObject getObjectPrototype();
jlaskey@3 516
jlaskey@3 517 /**
jlaskey@3 518 * Creates a version of this function bound to a specific "self" and other argumentss
jlaskey@3 519 * @param self the self to bind the function to
jlaskey@3 520 * @param args other arguments (beside self) to bind the function to
jlaskey@3 521 * @return the bound function
jlaskey@3 522 */
jlaskey@3 523 public abstract ScriptFunction makeBoundFunction(Object self, Object[] args);
jlaskey@3 524
jlaskey@3 525 /**
jlaskey@3 526 * Test if a methodHandle refers to a constructor.
jlaskey@3 527 * @param methodHandle MethodHandle to test.
jlaskey@3 528 * @return True if method is a constructor.
jlaskey@3 529 */
jlaskey@3 530 private static boolean isConstructor(final MethodHandle methodHandle) {
jlaskey@3 531 return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
jlaskey@3 532 }
jlaskey@3 533
jlaskey@3 534 /**
jlaskey@3 535 * Test if a methodHandle refers to a variable argument method.
jlaskey@3 536 * @param methodHandle MethodHandle to test.
jlaskey@3 537 * @return True if variable arguments.
jlaskey@3 538 */
jlaskey@3 539 public boolean isVarArg(final MethodHandle methodHandle) {
jlaskey@3 540 return hasCalleeParameter()
jlaskey@3 541 ? methodHandle.type().parameterCount() == 3 && methodHandle.type().parameterType(2).isArray()
jlaskey@3 542 : methodHandle.type().parameterCount() == 2 && methodHandle.type().parameterType(1).isArray();
jlaskey@3 543 }
jlaskey@3 544
jlaskey@3 545 @Override
jlaskey@3 546 public final String safeToString() {
jlaskey@3 547 return toSource();
jlaskey@3 548 }
jlaskey@3 549
jlaskey@3 550 @Override
jlaskey@3 551 public String toString() {
jlaskey@3 552 final StringBuilder sb = new StringBuilder();
jlaskey@3 553
jlaskey@3 554 sb.append(super.toString())
jlaskey@3 555 .append(" [ ")
jlaskey@3 556 .append(invokeHandle)
jlaskey@3 557 .append(", ")
jlaskey@3 558 .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
jlaskey@3 559
jlaskey@3 560 if (source != null) {
jlaskey@3 561 sb.append(" @ ")
jlaskey@3 562 .append(source.getName())
jlaskey@3 563 .append(':')
jlaskey@3 564 .append(source.getLine(Token.descPosition(token)));
jlaskey@3 565 }
jlaskey@3 566 sb.append(" ]");
jlaskey@3 567
jlaskey@3 568 return sb.toString();
jlaskey@3 569 }
jlaskey@3 570
jlaskey@3 571 /**
jlaskey@3 572 * Get this function as a String containing its source code. If no source code
jlaskey@3 573 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
jlaskey@3 574 * @return string representation of this function's source
jlaskey@3 575 */
jlaskey@3 576 public final String toSource() {
jlaskey@3 577 if (source != null && token != 0) {
jlaskey@3 578 return source.getString(Token.descPosition(token), Token.descLength(token));
jlaskey@3 579 }
jlaskey@3 580
jlaskey@3 581 return "function " + (name == null ? "" : name) + "() { [native code] }";
jlaskey@3 582 }
jlaskey@3 583
jlaskey@3 584 /**
jlaskey@3 585 * Get the prototype object for this function
jlaskey@3 586 * @return prototype
jlaskey@3 587 */
jlaskey@3 588 public final Object getPrototype() {
jlaskey@3 589 return prototype;
jlaskey@3 590 }
jlaskey@3 591
jlaskey@3 592 /**
jlaskey@3 593 * Set the prototype object for this function
jlaskey@3 594 * @param prototype new prototype object
jlaskey@3 595 * @return the prototype parameter
jlaskey@3 596 */
jlaskey@3 597 public final Object setPrototype(final Object prototype) {
jlaskey@3 598 this.prototype = prototype;
jlaskey@3 599 return prototype;
jlaskey@3 600 }
jlaskey@3 601
jlaskey@3 602 private static int weigh(final MethodType t) {
jlaskey@3 603 int weight = Type.typeFor(t.returnType()).getWeight();
jlaskey@3 604 for (final Class<?> paramType : t.parameterArray()) {
jlaskey@3 605 final int pweight = Type.typeFor(paramType).getWeight();
jlaskey@3 606 weight += pweight;
jlaskey@3 607 }
jlaskey@3 608 return weight;
jlaskey@3 609 }
jlaskey@3 610
jlaskey@3 611 private static boolean typeCompatible(final MethodType desc, final MethodType spec) {
jlaskey@3 612 //spec must fit in desc
jlaskey@3 613 final Class<?>[] dparray = desc.parameterArray();
jlaskey@3 614 final Class<?>[] sparray = spec.parameterArray();
jlaskey@3 615
jlaskey@3 616 if (dparray.length != sparray.length) {
jlaskey@3 617 return false;
jlaskey@3 618 }
jlaskey@3 619
jlaskey@3 620 for (int i = 0; i < dparray.length; i++) {
jlaskey@3 621 final Type dp = Type.typeFor(dparray[i]);
jlaskey@3 622 final Type sp = Type.typeFor(sparray[i]);
jlaskey@3 623
jlaskey@3 624 if (dp.isBoolean()) {
jlaskey@3 625 return false; //don't specialize on booleans, we have the "true" vs int 1 ambiguity in resolution
jlaskey@3 626 }
jlaskey@3 627
jlaskey@3 628 //specialization arguments must be at least as wide as dp, if not wider
jlaskey@3 629 if (Type.widest(dp, sp) != sp) {
jlaskey@3 630 //e.g. specialization takes double and callsite says "object". reject.
jlaskey@3 631 //but if specialization says double and callsite says "int" or "long" or "double", that's fine
jlaskey@3 632 return false;
jlaskey@3 633 }
jlaskey@3 634 }
jlaskey@3 635
jlaskey@3 636 return true; // anything goes for return type, take the convenient one and it will be upcasted thru dynalink magic.
jlaskey@3 637 }
jlaskey@3 638
jlaskey@3 639 private MethodHandle candidateWithLowestWeight(final MethodType descType, final MethodHandle initialCandidate, final MethodHandle[] specs) {
jlaskey@3 640 if (DISABLE_SPECIALIZATION || specs == null) {
jlaskey@3 641 return initialCandidate;
jlaskey@3 642 }
jlaskey@3 643
jlaskey@3 644 int minimumWeight = Integer.MAX_VALUE;
jlaskey@3 645 MethodHandle candidate = initialCandidate;
jlaskey@3 646
jlaskey@3 647 for (final MethodHandle spec : specs) {
jlaskey@3 648 final MethodType specType = spec.type();
jlaskey@3 649
jlaskey@3 650 if (!typeCompatible(descType, specType)) {
jlaskey@3 651 continue;
jlaskey@3 652 }
jlaskey@3 653
jlaskey@3 654 //return type is ok. we want a wider or equal one for our callsite.
jlaskey@3 655 final int specWeight = weigh(specType);
jlaskey@3 656 if (specWeight < minimumWeight) {
jlaskey@3 657 candidate = spec;
jlaskey@3 658 minimumWeight = specWeight;
jlaskey@3 659 }
jlaskey@3 660 }
jlaskey@3 661
jlaskey@3 662 if (DISABLE_SPECIALIZATION && candidate != initialCandidate) {
jlaskey@3 663 Context.err("### Specializing builtin " + getName() + " -> " + candidate + "?");
jlaskey@3 664 }
jlaskey@3 665
jlaskey@3 666 return candidate;
jlaskey@3 667 }
jlaskey@3 668
jlaskey@3 669 /**
jlaskey@3 670 * Return the most appropriate invoke handle if there are specializations
jlaskey@3 671 * @param type most specific method type to look for invocation with
jlaskey@3 672 * @return invoke method handle
jlaskey@3 673 */
jlaskey@3 674 public final MethodHandle getBestSpecializedInvokeHandle(final MethodType type) {
jlaskey@3 675 return candidateWithLowestWeight(type, getInvokeHandle(), invokeSpecializations);
jlaskey@3 676 }
jlaskey@3 677
jlaskey@3 678 /**
jlaskey@3 679 * Get the invoke handle - the most generic (and if no specializations are in place, only) invocation
jlaskey@3 680 * method handle for this ScriptFunction
jlaskey@3 681 * @see SpecializedFunction
jlaskey@3 682 * @return invokeHandle
jlaskey@3 683 */
jlaskey@3 684 public final MethodHandle getInvokeHandle() {
jlaskey@3 685 return invokeHandle;
jlaskey@3 686 }
jlaskey@3 687
jlaskey@3 688 /**
jlaskey@3 689 * Return the invoke handle bound to a given ScriptObject self reference.
jlaskey@3 690 * If callee parameter is required result is rebound to this.
jlaskey@3 691 *
jlaskey@3 692 * @param self self reference
jlaskey@3 693 * @return bound invoke handle
jlaskey@3 694 */
jlaskey@3 695 public final MethodHandle getBoundInvokeHandle(final ScriptObject self) {
jlaskey@3 696 final MethodHandle bound = MH.bindTo(getInvokeHandle(), self);
jlaskey@3 697 return hasCalleeParameter() ? MH.bindTo(bound, this) : bound;
jlaskey@3 698 }
jlaskey@3 699
jlaskey@3 700 private boolean hasCalleeParameter() {
jlaskey@3 701 return scope != null;
jlaskey@3 702 }
jlaskey@3 703
jlaskey@3 704 /**
jlaskey@3 705 * Get the construct handle - the most generic (and if no specializations are in place, only) constructor
jlaskey@3 706 * method handle for this ScriptFunction
jlaskey@3 707 * @see SpecializedConstructor
jlaskey@3 708 * @param type type for wanted constructor
jlaskey@3 709 * @return construct handle
jlaskey@3 710 */
jlaskey@3 711 public final MethodHandle getConstructHandle(final MethodType type) {
jlaskey@3 712 return candidateWithLowestWeight(type, getConstructHandle(), constructSpecializations);
jlaskey@3 713 }
jlaskey@3 714
jlaskey@3 715 /**
jlaskey@3 716 * Get a method handle to the constructor for this function
jlaskey@3 717 * @return constructor handle
jlaskey@3 718 */
jlaskey@3 719 public final MethodHandle getConstructHandle() {
jlaskey@3 720 return constructHandle;
jlaskey@3 721 }
jlaskey@3 722
jlaskey@3 723 /**
jlaskey@3 724 * Set a method handle to the constructor for this function
jlaskey@3 725 * @param constructHandle constructor handle
jlaskey@3 726 */
jlaskey@3 727 public final void setConstructHandle(final MethodHandle constructHandle) {
jlaskey@3 728 this.constructHandle = constructHandle;
jlaskey@3 729 this.constructSpecializations = null;
jlaskey@3 730 }
jlaskey@3 731
jlaskey@3 732 /**
jlaskey@3 733 * Get the name for this function
jlaskey@3 734 * @return the name
jlaskey@3 735 */
jlaskey@3 736 public final String getName() {
jlaskey@3 737 return name;
jlaskey@3 738 }
jlaskey@3 739
jlaskey@3 740 /**
jlaskey@3 741 * Does this script function need to be compiled. This determined by
jlaskey@3 742 * null checking invokeHandle
jlaskey@3 743 *
jlaskey@3 744 * @return true if this needs compilation
jlaskey@3 745 */
jlaskey@3 746 public final boolean needsCompilation() {
jlaskey@3 747 return invokeHandle == null;
jlaskey@3 748 }
jlaskey@3 749
jlaskey@3 750 /**
jlaskey@3 751 * Get token for this function
jlaskey@3 752 * @return token
jlaskey@3 753 */
jlaskey@3 754 public final long getToken() {
jlaskey@3 755 return token;
jlaskey@3 756 }
jlaskey@3 757
jlaskey@3 758 /**
jlaskey@3 759 * Get the scope for this function
jlaskey@3 760 * @return the scope
jlaskey@3 761 */
jlaskey@3 762 public final ScriptObject getScope() {
jlaskey@3 763 return scope;
jlaskey@3 764 }
jlaskey@3 765
jlaskey@3 766 /**
jlaskey@3 767 * Prototype getter for this ScriptFunction - follows the naming convention
jlaskey@3 768 * used by Nasgen and the code generator
jlaskey@3 769 *
jlaskey@3 770 * @param self self reference
jlaskey@3 771 * @return self's prototype
jlaskey@3 772 */
jlaskey@3 773 public static Object G$prototype(final Object self) {
jlaskey@3 774 return (self instanceof ScriptFunction) ?
jlaskey@3 775 ((ScriptFunction)self).getPrototype() :
jlaskey@3 776 UNDEFINED;
jlaskey@3 777 }
jlaskey@3 778
jlaskey@3 779 /**
jlaskey@3 780 * Prototype setter for this ScriptFunction - follows the naming convention
jlaskey@3 781 * used by Nasgen and the code generator
jlaskey@3 782 *
jlaskey@3 783 * @param self self reference
jlaskey@3 784 * @param prototype prototype to set
jlaskey@3 785 */
jlaskey@3 786 public static void S$prototype(final Object self, final Object prototype) {
jlaskey@3 787 if (self instanceof ScriptFunction) {
jlaskey@3 788 ((ScriptFunction)self).setPrototype(prototype);
jlaskey@3 789 }
jlaskey@3 790 }
jlaskey@3 791
jlaskey@3 792 /**
jlaskey@3 793 * Length getter - ECMA 15.3.3.2: Function.length
jlaskey@3 794 * @param self self reference
jlaskey@3 795 * @return length
jlaskey@3 796 */
jlaskey@3 797 public static int G$length(final Object self) {
jlaskey@3 798 if (self instanceof ScriptFunction) {
jlaskey@3 799 return ((ScriptFunction)self).getArity();
jlaskey@3 800 }
jlaskey@3 801
jlaskey@3 802 return 0;
jlaskey@3 803 }
jlaskey@3 804
jlaskey@3 805 /**
jlaskey@3 806 * Name getter - ECMA Function.name
jlaskey@3 807 * @param self self refence
jlaskey@3 808 * @return the name, or undefined if none
jlaskey@3 809 */
jlaskey@3 810 public static Object G$name(final Object self) {
jlaskey@3 811 if (self instanceof ScriptFunction) {
jlaskey@3 812 return ((ScriptFunction)self).getName();
jlaskey@3 813 }
jlaskey@3 814
jlaskey@3 815 return UNDEFINED;
jlaskey@3 816 }
jlaskey@3 817
jlaskey@3 818 /**
jlaskey@3 819 * Get the prototype for this ScriptFunction
jlaskey@3 820 * @param constructor constructor
jlaskey@3 821 * @return prototype, or null if given constructor is not a ScriptFunction
jlaskey@3 822 */
jlaskey@3 823 public static ScriptObject getPrototype(final Object constructor) {
jlaskey@3 824 if (constructor instanceof ScriptFunction) {
jlaskey@3 825 final Object proto = ((ScriptFunction)constructor).getPrototype();
jlaskey@3 826 if (proto instanceof ScriptObject) {
jlaskey@3 827 return (ScriptObject)proto;
jlaskey@3 828 }
jlaskey@3 829 }
jlaskey@3 830
jlaskey@3 831 return null;
jlaskey@3 832 }
jlaskey@3 833
jlaskey@3 834 // These counters are updated only in debug mode.
jlaskey@3 835 private static int constructorCount;
jlaskey@3 836 private static int invokes;
jlaskey@3 837 private static int allocations;
jlaskey@3 838
jlaskey@3 839 /**
jlaskey@3 840 * @return the constructorCount
jlaskey@3 841 */
jlaskey@3 842 public static int getConstructorCount() {
jlaskey@3 843 return constructorCount;
jlaskey@3 844 }
jlaskey@3 845
jlaskey@3 846 /**
jlaskey@3 847 * @return the invokes
jlaskey@3 848 */
jlaskey@3 849 public static int getInvokes() {
jlaskey@3 850 return invokes;
jlaskey@3 851 }
jlaskey@3 852
jlaskey@3 853 /**
jlaskey@3 854 * @return the allocations
jlaskey@3 855 */
jlaskey@3 856 public static int getAllocations() {
jlaskey@3 857 return allocations;
jlaskey@3 858 }
jlaskey@3 859
jlaskey@3 860 @Override
jlaskey@3 861 protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
jlaskey@3 862 final MethodType type = desc.getMethodType();
jlaskey@3 863 MethodHandle constructor = getConstructHandle(type);
jlaskey@3 864
jlaskey@3 865 if (constructor == null) {
jlaskey@3 866 typeError(Context.getGlobal(), "not.a.constructor", ScriptRuntime.safeToString(this));
jlaskey@3 867 return null;
jlaskey@3 868 }
jlaskey@3 869
jlaskey@3 870 final MethodType ctorType = constructor.type();
jlaskey@3 871
jlaskey@3 872 // guard against primitive constructor return types
jlaskey@3 873 constructor = MH.asType(constructor, constructor.type().changeReturnType(Object.class));
jlaskey@3 874
jlaskey@3 875 // apply new filter
jlaskey@3 876 final Class<?>[] ctorArgs = ctorType.dropParameterTypes(0, 1).parameterArray(); // drop self
jlaskey@3 877 MethodHandle handle = MH.foldArguments(MH.dropArguments(NEWFILTER, 2, ctorArgs), constructor);
jlaskey@3 878
jlaskey@3 879 if (hasCalleeParameter()) {
jlaskey@3 880 final MethodHandle allocate = MH.bindTo(MethodHandles.exactInvoker(ALLOCATE.type()), ScriptFunction.ALLOCATE);
jlaskey@3 881 handle = MH.foldArguments(handle, allocate);
jlaskey@3 882 handle = MH.asType(handle, handle.type().changeParameterType(0, Object.class)); // ScriptFunction => Object
jlaskey@3 883 } else {
jlaskey@3 884 final MethodHandle allocate = MH.dropArguments(MH.bindTo(ScriptFunction.ALLOCATE, this), 0, Object.class);
jlaskey@3 885 handle = MH.filterArguments(handle, 0, allocate);
jlaskey@3 886 }
jlaskey@3 887
jlaskey@3 888 final MethodHandle filterIn = MH.asType(pairArguments(handle, type), type);
jlaskey@3 889 return new GuardedInvocation(filterIn, null, NashornGuards.getFunctionGuard(this));
jlaskey@3 890 }
jlaskey@3 891
jlaskey@3 892 @SuppressWarnings("unused")
jlaskey@3 893 private static Object newFilter(final Object result, final Object allocation) {
jlaskey@3 894 return result instanceof ScriptObject ? result : allocation;
jlaskey@3 895 }
jlaskey@3 896
jlaskey@3 897 /**
jlaskey@3 898 * dyn:call call site signature: (callee, thiz, [args...])
jlaskey@3 899 * generated method signature: (thiz, callee, [args...])
jlaskey@3 900 *
jlaskey@3 901 * cases:
jlaskey@3 902 * (a) method has callee parameter
jlaskey@3 903 * (1) for local/scope calls, we just bind thiz and drop the second argument.
jlaskey@3 904 * (2) for normal this-calls, we have to swap thiz and callee to get matching signatures.
jlaskey@3 905 * (b) method doesn't have callee parameter (builtin functions)
jlaskey@3 906 * (3) for local/scope calls, bind thiz and drop both callee and thiz.
jlaskey@3 907 * (4) for normal this-calls, drop callee.
jlaskey@3 908 */
jlaskey@3 909 @Override
jlaskey@3 910 protected GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final boolean megaMorphic) {
jlaskey@3 911 final MethodType type = desc.getMethodType();
jlaskey@3 912
jlaskey@3 913 if(megaMorphic) {
jlaskey@3 914 // (this, callee, args...) => (this, callee, args[])
jlaskey@3 915 final MethodHandle collector = MH.asCollector(ScriptRuntime.APPLY.methodHandle(), Object[].class,
jlaskey@3 916 type.parameterCount() - 2);
jlaskey@3 917
jlaskey@3 918 return new GuardedInvocation(collector,
jlaskey@3 919 desc.getMethodType().parameterType(0) == ScriptFunction.class ? null : NashornGuards.getScriptFunctionGuard());
jlaskey@3 920 }
jlaskey@3 921
jlaskey@3 922 MethodHandle boundHandle;
jlaskey@3 923 if (hasCalleeParameter()) {
jlaskey@3 924 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type);
jlaskey@3 925
jlaskey@3 926 if(NashornCallSiteDescriptor.isScope(desc)) {
jlaskey@3 927 // (this, callee, args...) => (callee, args...) => (callee, [this], args...)
hannesw@42 928 boundHandle = MH.bindTo(callHandle, isNonStrictFunction() ? Context.getGlobal(): ScriptRuntime.UNDEFINED);
jlaskey@3 929 boundHandle = MH.dropArguments(boundHandle, 1, Object.class);
jlaskey@3 930 } else {
jlaskey@3 931 // (this, callee, args...) permute => (callee, this, args...) which is what we get in
jlaskey@3 932 final MethodType oldType = callHandle.type();
jlaskey@3 933 final int[] reorder = new int[oldType.parameterCount()];
jlaskey@3 934 for (int i = 2; i < reorder.length; i++) {
jlaskey@3 935 reorder[i] = i;
jlaskey@3 936 }
jlaskey@3 937 reorder[0] = 1;
jlaskey@3 938 assert reorder[1] == 0;
jlaskey@3 939 final MethodType newType = oldType.changeParameterType(0, oldType.parameterType(1)).changeParameterType(1, oldType.parameterType(0));
jlaskey@3 940 boundHandle = MethodHandles.permuteArguments(callHandle, newType, reorder);
jlaskey@3 941 }
jlaskey@3 942 } else {
jlaskey@3 943 final MethodHandle callHandle = getBestSpecializedInvokeHandle(type.dropParameterTypes(0, 1));
jlaskey@3 944
jlaskey@3 945 if(NashornCallSiteDescriptor.isScope(desc)) {
hannesw@42 946 boundHandle = MH.bindTo(callHandle, isNonStrictFunction()? Context.getGlobal() : ScriptRuntime.UNDEFINED);
jlaskey@3 947 boundHandle = MH.dropArguments(boundHandle, 0, Object.class, Object.class);
jlaskey@3 948 } else {
jlaskey@3 949 boundHandle = MH.dropArguments(callHandle, 0, Object.class);
jlaskey@3 950 }
jlaskey@3 951 }
jlaskey@3 952
jlaskey@3 953 boundHandle = pairArguments(boundHandle, type);
hannesw@42 954 return new NashornGuardedInvocation(boundHandle, null, NashornGuards.getFunctionGuard(this), isNonStrictFunction());
jlaskey@3 955 }
jlaskey@3 956
jlaskey@3 957 /**
jlaskey@3 958 * Used for noSuchMethod/noSuchProperty and JSAdapter hooks.
jlaskey@3 959 *
jlaskey@3 960 * These don't want a callee parameter, so bind that. Name binding is optional.
jlaskey@3 961 */
jlaskey@3 962 MethodHandle getCallMethodHandle(final MethodType type, final String bindName) {
jlaskey@3 963 MethodHandle methodHandle = getBestSpecializedInvokeHandle(type);
jlaskey@3 964
jlaskey@3 965 if (bindName != null) {
jlaskey@3 966 if (hasCalleeParameter()) {
jlaskey@3 967 methodHandle = MH.insertArguments(methodHandle, 1, this, bindName);
jlaskey@3 968 } else {
jlaskey@3 969 methodHandle = MH.insertArguments(methodHandle, 1, bindName);
jlaskey@3 970 }
jlaskey@3 971 } else {
jlaskey@3 972 if (hasCalleeParameter()) {
jlaskey@3 973 methodHandle = MH.insertArguments(methodHandle, 1, this);
jlaskey@3 974 }
jlaskey@3 975 }
jlaskey@3 976
jlaskey@3 977 return pairArguments(methodHandle, type);
jlaskey@3 978 }
jlaskey@3 979
jlaskey@3 980 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
jlaskey@3 981 final Class<?> own = ScriptFunction.class;
jlaskey@3 982 final MethodType mt = MH.type(rtype, types);
jlaskey@3 983 try {
jlaskey@3 984 return MH.findStatic(MethodHandles.lookup(), own, name, mt);
jlaskey@3 985 } catch (final MethodHandleFactory.LookupException e) {
jlaskey@3 986 return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
jlaskey@3 987 }
jlaskey@3 988 }
jlaskey@3 989 }
jlaskey@3 990

mercurial