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

Sat, 09 Feb 2013 16:58:48 +0100

author
attila
date
Sat, 09 Feb 2013 16:58:48 +0100
changeset 81
5ead5333fa59
parent 72
f6fae6de6f4f
child 82
abea4ba28901
permissions
-rw-r--r--

8006943: Fix order of function method arguments to be (callee, thisObject)
Reviewed-by: jlaskey, lagergren

hannesw@72 1 /*
hannesw@72 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
hannesw@72 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
hannesw@72 4 *
hannesw@72 5 * This code is free software; you can redistribute it and/or modify it
hannesw@72 6 * under the terms of the GNU General Public License version 2 only, as
hannesw@72 7 * published by the Free Software Foundation. Oracle designates this
hannesw@72 8 * particular file as subject to the "Classpath" exception as provided
hannesw@72 9 * by Oracle in the LICENSE file that accompanied this code.
hannesw@72 10 *
hannesw@72 11 * This code is distributed in the hope that it will be useful, but WITHOUT
hannesw@72 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
hannesw@72 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
hannesw@72 14 * version 2 for more details (a copy is included in the LICENSE file that
hannesw@72 15 * accompanied this code).
hannesw@72 16 *
hannesw@72 17 * You should have received a copy of the GNU General Public License version
hannesw@72 18 * 2 along with this work; if not, write to the Free Software Foundation,
hannesw@72 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
hannesw@72 20 *
hannesw@72 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
hannesw@72 22 * or visit www.oracle.com if you need additional information or have any
hannesw@72 23 * questions.
hannesw@72 24 */
hannesw@72 25
hannesw@72 26 package jdk.nashorn.internal.runtime;
hannesw@72 27
attila@81 28 import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
attila@81 29
attila@81 30 import java.lang.invoke.MethodHandle;
attila@81 31 import java.lang.invoke.MethodType;
hannesw@72 32 import jdk.nashorn.internal.ir.FunctionNode;
hannesw@72 33 import jdk.nashorn.internal.parser.Token;
hannesw@72 34 import jdk.nashorn.internal.parser.TokenType;
hannesw@72 35
hannesw@72 36 /**
hannesw@72 37 * A container for data needed to instantiate a specific {@link ScriptFunction} at runtime.
hannesw@72 38 * Instances of this class are created during codegen and stored in script classes'
hannesw@72 39 * constants array to reduce function instantiation overhead during runtime.
hannesw@72 40 */
hannesw@72 41 public class ScriptFunctionData {
hannesw@72 42
hannesw@72 43 // per-function object flags
hannesw@72 44 private static final int IS_STRICT = 0b0000_0001;
hannesw@72 45 private static final int IS_BUILTIN = 0b0000_0010;
hannesw@72 46 private static final int HAS_CALLEE = 0b0000_0100;
hannesw@72 47 private static final int IS_VARARGS = 0b0000_1000;
hannesw@72 48
hannesw@72 49 /** Name of the function or "" */
hannesw@72 50 private final String name;
hannesw@72 51 /** Source of this function, or null */
hannesw@72 52 private final Source source;
hannesw@72 53 /** Map for new instance constructor */
hannesw@72 54 private PropertyMap allocatorMap;
hannesw@72 55 /** Start position and length in source */
hannesw@72 56 private final long token;
hannesw@72 57 /** Number of expected arguments, either taken from FunctionNode or calculated from method handle signature*/
hannesw@72 58 private int arity;
hannesw@72 59 /** Does this function need a callee argument? */
hannesw@72 60 private final int flags;
hannesw@72 61
hannesw@72 62 /** Reference to code for this method. */
hannesw@72 63 private MethodHandle invoker;
hannesw@72 64 /** Reference to code for this method when called to create "new" object */
hannesw@72 65 private MethodHandle constructor;
hannesw@72 66 /** Constructor to create a new instance. */
hannesw@72 67 private MethodHandle allocator;
hannesw@72 68 /** Generic invoker to used in {@link ScriptFunction#invoke(Object, Object...)}. */
hannesw@72 69 private MethodHandle genericInvoker;
hannesw@72 70 /** Generic constructor used in {@link ScriptFunction#construct(Object, Object...)}. */
hannesw@72 71 private MethodHandle genericConstructor;
hannesw@72 72 /** Specializations - see @SpecializedFunction */
hannesw@72 73 private MethodHandle[] invokeSpecializations;
hannesw@72 74 /** Specializations - see @SpecializedFunction */
hannesw@72 75 private MethodHandle[] constructSpecializations;
hannesw@72 76
hannesw@72 77 /**
hannesw@72 78 * Constructor
hannesw@72 79 * @param fn the function node
hannesw@72 80 * @param allocatorMap the allocator property map
hannesw@72 81 */
hannesw@72 82 public ScriptFunctionData(final FunctionNode fn, final PropertyMap allocatorMap) {
hannesw@72 83
hannesw@72 84 final long firstToken = fn.getFirstToken();
hannesw@72 85 final long lastToken = fn.getLastToken();
hannesw@72 86 final int position = Token.descPosition(firstToken);
hannesw@72 87 final int length = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
hannesw@72 88
hannesw@72 89 this.name = fn.isAnonymous() ? "" : fn.getIdent().getName();
hannesw@72 90 this.source = fn.getSource();
hannesw@72 91 this.allocatorMap = allocatorMap;
hannesw@72 92 this.token = Token.toDesc(TokenType.FUNCTION, position, length);
hannesw@72 93 this.arity = fn.getParameters().size();
hannesw@72 94 this.flags = makeFlags(fn.needsCallee(), fn.isVarArg(), fn.isStrictMode(), false);
hannesw@72 95 }
hannesw@72 96
hannesw@72 97 /**
hannesw@72 98 * Constructor
hannesw@72 99 * @param name the function name
hannesw@72 100 * @param methodHandle the method handle
hannesw@72 101 * @param specs array of specialized method handles
hannesw@72 102 * @param strict strict flag
hannesw@72 103 * @param builtin builtin flag
hannesw@72 104 */
hannesw@72 105 public ScriptFunctionData(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
hannesw@72 106 this.name = name;
hannesw@72 107 this.source = null;
hannesw@72 108 this.token = 0;
hannesw@72 109
hannesw@72 110 final MethodType type = methodHandle.type();
hannesw@72 111 final int paramCount = type.parameterCount();
hannesw@72 112 final boolean isVarArg = type.parameterType(paramCount - 1).isArray();
hannesw@72 113 final boolean needsCallee = needsCallee(methodHandle);
hannesw@72 114
hannesw@72 115 this.flags = makeFlags(needsCallee, isVarArg, strict, builtin);
hannesw@72 116 this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity
hannesw@72 117
hannesw@72 118 if (needsCallee && !isVarArg) {
hannesw@72 119 this.arity--;
hannesw@72 120 }
hannesw@72 121
hannesw@72 122 if (isConstructor(methodHandle)) {
hannesw@72 123 if (!isVarArg) {
hannesw@72 124 this.arity--; // drop the boolean flag for arity
hannesw@72 125 }
hannesw@72 126 /*
hannesw@72 127 * We insert a boolean argument to tell if the method was invoked as
hannesw@72 128 * constructor or not if the method handle's first argument is boolean.
hannesw@72 129 */
hannesw@72 130 this.invoker = MH.insertArguments(methodHandle, 0, false);
attila@81 131 this.constructor = adaptConstructor(MH.insertArguments(methodHandle, 0, true));
hannesw@72 132
hannesw@72 133 if (specs != null) {
hannesw@72 134 this.invokeSpecializations = new MethodHandle[specs.length];
hannesw@72 135 this.constructSpecializations = new MethodHandle[specs.length];
hannesw@72 136 for (int i = 0; i < specs.length; i++) {
hannesw@72 137 this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false);
attila@81 138 this.constructSpecializations[i] = adaptConstructor(MH.insertArguments(specs[i], 0, true));
hannesw@72 139 }
hannesw@72 140 }
hannesw@72 141 } else {
hannesw@72 142 this.invoker = methodHandle;
attila@81 143 this.constructor = adaptConstructor(methodHandle);
hannesw@72 144 this.invokeSpecializations = specs;
hannesw@72 145 this.constructSpecializations = specs;
hannesw@72 146 }
hannesw@72 147 }
hannesw@72 148
hannesw@72 149 /**
hannesw@72 150 * Get the arity of the function.
hannesw@72 151 * @return the arity
hannesw@72 152 */
hannesw@72 153 public int getArity() {
hannesw@72 154 return arity;
hannesw@72 155 }
hannesw@72 156
hannesw@72 157 /**
hannesw@72 158 * Set the arity of the function.
hannesw@72 159 * @param arity the arity
hannesw@72 160 */
hannesw@72 161 public void setArity(int arity) {
hannesw@72 162 this.arity = arity;
hannesw@72 163 }
hannesw@72 164
hannesw@72 165 /**
hannesw@72 166 * Get the function name.
hannesw@72 167 * @return function name
hannesw@72 168 */
hannesw@72 169 public String getName() {
hannesw@72 170 return name;
hannesw@72 171 }
hannesw@72 172
hannesw@72 173 /**
hannesw@72 174 * Get the source of the function.
hannesw@72 175 * @return the source
hannesw@72 176 */
hannesw@72 177 public Source getSource() {
hannesw@72 178 return source;
hannesw@72 179 }
hannesw@72 180
hannesw@72 181 /**
hannesw@72 182 * Get this function as a String containing its source code. If no source code
hannesw@72 183 * exists in this ScriptFunction, its contents will be displayed as {@code [native code]}
hannesw@72 184 * @return string representation of this function's source
hannesw@72 185 */
hannesw@72 186 public String toSource() {
hannesw@72 187 if (source != null && token != 0) {
hannesw@72 188 return source.getString(Token.descPosition(token), Token.descLength(token));
hannesw@72 189 }
hannesw@72 190
hannesw@72 191 return "function " + (name == null ? "" : name) + "() { [native code] }";
hannesw@72 192 }
hannesw@72 193
hannesw@72 194 @Override
hannesw@72 195 public String toString() {
hannesw@72 196 final StringBuilder sb = new StringBuilder();
hannesw@72 197
hannesw@72 198 sb.append(super.toString())
hannesw@72 199 .append(" [ ")
hannesw@72 200 .append(invoker)
hannesw@72 201 .append(", ")
hannesw@72 202 .append((name == null || name.isEmpty()) ? "<anonymous>" : name);
hannesw@72 203
hannesw@72 204 if (source != null) {
hannesw@72 205 sb.append(" @ ")
hannesw@72 206 .append(source.getName())
hannesw@72 207 .append(':')
hannesw@72 208 .append(source.getLine(Token.descPosition(token)));
hannesw@72 209 }
hannesw@72 210 sb.append(" ]");
hannesw@72 211
hannesw@72 212 return sb.toString();
hannesw@72 213 }
hannesw@72 214
hannesw@72 215 /**
hannesw@72 216 * Get the allocator property map.
hannesw@72 217 * @return the allocator map
hannesw@72 218 */
hannesw@72 219 public PropertyMap getAllocatorMap() {
hannesw@72 220 return allocatorMap;
hannesw@72 221 }
hannesw@72 222
hannesw@72 223 /**
hannesw@72 224 * Get the function's parse token.
hannesw@72 225 * @return the token
hannesw@72 226 */
hannesw@72 227 public long getToken() {
hannesw@72 228 return token;
hannesw@72 229 }
hannesw@72 230
hannesw@72 231 /**
hannesw@72 232 * Returns true if the function needs a callee argument.
hannesw@72 233 * @return the needsCallee flag
hannesw@72 234 */
hannesw@72 235 public boolean needsCallee() {
hannesw@72 236 return (flags & HAS_CALLEE) != 0;
hannesw@72 237 }
hannesw@72 238
hannesw@72 239 /**
hannesw@72 240 * Returns true if this is a strict-mode function.
hannesw@72 241 * @return the strict flag
hannesw@72 242 */
hannesw@72 243 public boolean isStrict() {
hannesw@72 244 return (flags & IS_STRICT) != 0;
hannesw@72 245 }
hannesw@72 246
hannesw@72 247 /**
hannesw@72 248 * Returns true if this is a built-in function.
hannesw@72 249 * @return the built-in flag
hannesw@72 250 */
hannesw@72 251 public boolean isBuiltin() {
hannesw@72 252 return (flags & IS_BUILTIN) != 0;
hannesw@72 253 }
hannesw@72 254
hannesw@72 255 /**
hannesw@72 256 * Returns true if this is a var-arg function.
hannesw@72 257 * @return the var-arg flag
hannesw@72 258 */
hannesw@72 259 public boolean isVarArg() {
hannesw@72 260 return (flags & IS_VARARGS) != 0;
hannesw@72 261 }
hannesw@72 262
hannesw@72 263 /**
hannesw@72 264 * Returns true if this is a non-strict, non-built-in function that requires non-primitive this argument
hannesw@72 265 * according to ECMA 10.4.3.
hannesw@72 266 * @return true if this argument must be an object
hannesw@72 267 */
hannesw@72 268 public boolean needsWrappedThis() {
hannesw@72 269 return (flags & (IS_STRICT | IS_BUILTIN)) == 0;
hannesw@72 270 }
hannesw@72 271
hannesw@72 272 /**
hannesw@72 273 * Get the method handle used to invoke this function.
hannesw@72 274 * @return the invoke handle
hannesw@72 275 */
hannesw@72 276 public MethodHandle getInvoker() {
hannesw@72 277 return invoker;
hannesw@72 278 }
hannesw@72 279
hannesw@72 280 /**
hannesw@72 281 * Get the method handle used to invoke this function as a constructor.
hannesw@72 282 * @return the constructor handle
hannesw@72 283 */
hannesw@72 284 public MethodHandle getConstructor() {
hannesw@72 285 return constructor;
hannesw@72 286 }
hannesw@72 287
hannesw@72 288 /**
hannesw@72 289 * Set the constructor method handle.
hannesw@72 290 * @param constructor the constructor handle
hannesw@72 291 */
hannesw@72 292 public void setConstructor(MethodHandle constructor) {
hannesw@72 293 this.constructor = constructor;
hannesw@72 294 this.constructSpecializations = null;
hannesw@72 295 }
hannesw@72 296
hannesw@72 297 /**
hannesw@72 298 * Get the method handle used to allocate a new object for this constructor.
hannesw@72 299 * @return the allocator handle
hannesw@72 300 */
hannesw@72 301 public MethodHandle getAllocator() {
hannesw@72 302 return allocator;
hannesw@72 303 }
hannesw@72 304
hannesw@72 305 /**
hannesw@72 306 * Get an adapted version of the invoker handle that only uses {@code Object} as parameter and return types.
hannesw@72 307 * @return the generic invoke handle
hannesw@72 308 */
hannesw@72 309 public MethodHandle getGenericInvoker() {
hannesw@72 310 if (genericInvoker == null) {
hannesw@72 311 assert invoker != null : "invoker is null";
hannesw@72 312 genericInvoker = adaptMethodType(invoker);
hannesw@72 313 }
hannesw@72 314 return genericInvoker;
hannesw@72 315 }
hannesw@72 316
hannesw@72 317 /**
hannesw@72 318 * Get an adapted version of the constructor handle that only uses {@code Object} as parameter and return types.
hannesw@72 319 * @return the generic constructor handle
hannesw@72 320 */
hannesw@72 321 public MethodHandle getGenericConstructor() {
hannesw@72 322 if (genericConstructor == null) {
hannesw@72 323 assert constructor != null : "constructor is null";
hannesw@72 324 genericConstructor = adaptMethodType(constructor);
hannesw@72 325 }
hannesw@72 326 return genericConstructor;
hannesw@72 327 }
hannesw@72 328
hannesw@72 329 /**
hannesw@72 330 * Get the specialized invoke handles for this function.
hannesw@72 331 * @return array of specialized invoke handles
hannesw@72 332 */
hannesw@72 333 public MethodHandle[] getInvokeSpecializations() {
hannesw@72 334 return invokeSpecializations;
hannesw@72 335 }
hannesw@72 336
hannesw@72 337 /**
hannesw@72 338 * Get the specialized construct handles for this function.
hannesw@72 339 * @return array of specialized construct handles
hannesw@72 340 */
hannesw@72 341 public MethodHandle[] getConstructSpecializations() {
hannesw@72 342 return constructSpecializations;
hannesw@72 343 }
hannesw@72 344
hannesw@72 345 /**
hannesw@72 346 * Set the method handles for this function.
hannesw@72 347 * @param invoker the invoker handle
hannesw@72 348 * @param allocator the allocator handle
hannesw@72 349 */
hannesw@72 350 public void setMethodHandles(MethodHandle invoker, MethodHandle allocator) {
hannesw@72 351 // We can't make method handle fields final because they're not available during codegen
hannesw@72 352 // and they're set when first called, so we enforce set-once here.
hannesw@72 353 if (this.invoker == null) {
hannesw@72 354 this.invoker = invoker;
attila@81 355 this.constructor = adaptConstructor(invoker);
hannesw@72 356 this.allocator = allocator;
hannesw@72 357 }
hannesw@72 358 }
hannesw@72 359
hannesw@72 360 /**
hannesw@72 361 * Convert boolean flags to int.
hannesw@72 362 * @param needsCallee needs-callee flag
hannesw@72 363 * @param isVarArg var-arg flag
hannesw@72 364 * @param isStrict strict flag
hannesw@72 365 * @param isBuiltin builtin flag
hannesw@72 366 * @return int flags
hannesw@72 367 */
hannesw@72 368 private static int makeFlags(final boolean needsCallee, final boolean isVarArg, final boolean isStrict, final boolean isBuiltin) {
hannesw@72 369 int flags = 0;
hannesw@72 370 if (needsCallee) {
hannesw@72 371 flags |= HAS_CALLEE;
hannesw@72 372 }
hannesw@72 373 if (isVarArg) {
hannesw@72 374 flags |= IS_VARARGS;
hannesw@72 375 }
hannesw@72 376 if (isStrict) {
hannesw@72 377 flags |= IS_STRICT;
hannesw@72 378 }
hannesw@72 379 if (isBuiltin) {
hannesw@72 380 flags |= IS_BUILTIN;
hannesw@72 381 }
hannesw@72 382 return flags;
hannesw@72 383 }
hannesw@72 384
hannesw@72 385 /**
hannesw@72 386 * Test if a methodHandle refers to a constructor.
hannesw@72 387 * @param methodHandle MethodHandle to test.
hannesw@72 388 * @return True if method is a constructor.
hannesw@72 389 */
hannesw@72 390 private static boolean isConstructor(final MethodHandle methodHandle) {
hannesw@72 391 return methodHandle.type().parameterCount() >= 1 && methodHandle.type().parameterType(0) == boolean.class;
hannesw@72 392 }
hannesw@72 393
hannesw@72 394 /**
hannesw@72 395 * Heuristic to figure out if the method handle has a callee argument. If it's type is either
hannesw@72 396 * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
hannesw@72 397 * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
hannesw@72 398 * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
hannesw@72 399 * they also always receive a callee.
hannesw@72 400 * @param methodHandle the examined method handle
hannesw@72 401 * @return true if the method handle expects a callee, false otherwise
hannesw@72 402 */
hannesw@72 403 private static boolean needsCallee(MethodHandle methodHandle) {
hannesw@72 404 final MethodType type = methodHandle.type();
hannesw@72 405 final int len = type.parameterCount();
hannesw@72 406 if(len == 0) {
hannesw@72 407 return false;
hannesw@72 408 }
hannesw@72 409 if(type.parameterType(0) == boolean.class) {
attila@81 410 return len > 1 && type.parameterType(1) == ScriptFunction.class;
hannesw@72 411 }
attila@81 412 return type.parameterType(0) == ScriptFunction.class;
hannesw@72 413 }
hannesw@72 414
hannesw@72 415 /**
hannesw@72 416 * Takes a method handle, and returns a potentially different method handle that can be used in
hannesw@72 417 * {@link ScriptFunction#invoke(Object, Object...)} or {@link ScriptFunction#construct(Object, Object...)}.
hannesw@72 418 * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
hannesw@72 419 * {@code Object} as well, except for the following ones:
hannesw@72 420 * <ul>
hannesw@72 421 * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
hannesw@72 422 * <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
hannesw@72 423 * (callee) as an argument</li>
hannesw@72 424 * </ul>
hannesw@72 425 *
hannesw@72 426 * @param handle the original method handle
hannesw@72 427 * @return the new handle, conforming to the rules above.
hannesw@72 428 */
hannesw@72 429 private MethodHandle adaptMethodType(final MethodHandle handle) {
hannesw@72 430 final MethodType type = handle.type();
hannesw@72 431 MethodType newType = type.generic();
hannesw@72 432 if (isVarArg()) {
hannesw@72 433 newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
hannesw@72 434 }
hannesw@72 435 if (needsCallee()) {
attila@81 436 newType = newType.changeParameterType(0, ScriptFunction.class);
hannesw@72 437 }
hannesw@72 438 return type.equals(newType) ? handle : handle.asType(newType);
hannesw@72 439 }
hannesw@72 440
attila@81 441 /**
attila@81 442 * Adapts a method handle to conform to requirements of a constructor. Right now this consists of making sure its
attila@81 443 * return value is {@code Object}. We might consider moving the caller-this argument swap here too from
attila@81 444 * {@link ScriptFunction#findNewMethod(org.dynalang.dynalink.CallSiteDescriptor)}.
attila@81 445 * @param ctorHandle the constructor method handle
attila@81 446 * @return adapted constructor method handle
attila@81 447 */
attila@81 448 private static MethodHandle adaptConstructor(final MethodHandle ctorHandle) {
attila@81 449 return changeReturnTypeToObject(ctorHandle);
attila@81 450 }
attila@81 451
attila@81 452 /**
attila@81 453 * Adapts the method handle so its return type is {@code Object}. If the handle's return type is already
attila@81 454 * {@code Object}, the handle is returned unchanged.
attila@81 455 * @param mh the handle to adapt
attila@81 456 * @return the adapted handle
attila@81 457 */
attila@81 458 private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
attila@81 459 return MH.asType(mh, mh.type().changeReturnType(Object.class));
attila@81 460 }
hannesw@72 461 }

mercurial