Sat, 09 Feb 2013 16:58:48 +0100
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 | } |