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

Print this page

        

*** 32,41 **** --- 32,42 ---- import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import jdk.nashorn.internal.codegen.CompilerConstants.Call; + import jdk.nashorn.internal.codegen.ScriptFunctionData; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.objects.annotations.SpecializedConstructor; import jdk.nashorn.internal.objects.annotations.SpecializedFunction; import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
*** 68,79 **** private static final MethodHandle NEWFILTER = findOwnMH("newFilter", Object.class, Object.class, Object.class); private static final MethodHandle WRAPFILTER = findOwnMH("wrapFilter", Object.class, Object.class); - /** method handle to arity setter for this ScriptFunction */ - public static final Call SET_ARITY = virtualCallNoLookup(ScriptFunction.class, "setArity", void.class, int.class); /** method handle to scope getter for this ScriptFunction */ public static final Call GET_SCOPE = virtualCallNoLookup(ScriptFunction.class, "getScope", ScriptObject.class); /** Should specialized function and specialized constructors for the builtin be used if available? */ private static final boolean DISABLE_SPECIALIZATION = Options.getBooleanProperty("nashorn.scriptfunction.specialization.disable"); --- 69,78 ----
*** 86,99 **** /** Start position and length in source. */ private final long token; /** Reference to code for this method. */ ! private final MethodHandle invokeHandle; /** Reference to code for this method when called to create "new" object */ ! protected MethodHandle constructHandle; /** Reference to constructor prototype. */ protected Object prototype; /** Constructor to create a new instance. */ --- 85,104 ---- /** Start position and length in source. */ private final long token; /** Reference to code for this method. */ ! private final MethodHandle invoker; /** Reference to code for this method when called to create "new" object */ ! protected MethodHandle constructor; ! ! /** Generic invoker to used in {@link #invoke(Object, Object...)}. */ ! private MethodHandle genericInvoker; ! ! /** Generic constructor used in {@link #construct(Object, Object...)}. */ ! private MethodHandle genericConstructor; /** Reference to constructor prototype. */ protected Object prototype; /** Constructor to create a new instance. */
*** 127,137 **** final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final MethodHandle[] specs) { ! this(name, methodHandle, map, scope, null, 0, needsCallee(methodHandle), specs); } /** * Heuristic to figure out if the method handle has a callee argument. If it's type is either * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has --- 132,142 ---- final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final MethodHandle[] specs) { ! this(name, methodHandle, map, scope, needsCallee(methodHandle), specs); } /** * Heuristic to figure out if the method handle has a callee argument. If it's type is either * {@code (boolean, Object, ScriptFunction, ...)} or {@code (Object, ScriptFunction, ...)}, then we'll assume it has
*** 154,305 **** } /** * Constructor * ! * @param name function name * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) * @param map property map * @param scope scope - * @param source the source - * @param token token * @param allocator method handle to this function's allocator - see JO$ classes - * @param allocatorMap property map to be used for all constructors - * @param needsCallee does this method use the {@code callee} variable - * @param specs specialized version of this function - other method handles */ protected ScriptFunction( ! final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, ! final Source source, ! final long token, ! final MethodHandle allocator, ! final PropertyMap allocatorMap, ! final boolean needsCallee, ! final MethodHandle[] specs) { ! this(name, methodHandle, map, scope, source, token, needsCallee, specs); ! //this is the internal constructor this.allocator = allocator; ! this.allocatorMap = allocatorMap; } /** * Constructor * * @param name function name * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) * @param map property map * @param scope scope - * @param source the source - * @param token token * @param needsCallee does this method use the {@code callee} variable * @param specs specialized version of this function - other method handles */ protected ScriptFunction( final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, - final Source source, - final long token, final boolean needsCallee, final MethodHandle[] specs) { super(map); if (Context.DEBUG) { constructorCount++; } this.name = name; ! this.source = source; ! this.token = token; this.scope = scope; if(needsCallee) { setHasCalleeParameter(); } final MethodType type = methodHandle.type(); final int paramCount = type.parameterCount(); final boolean isVarArg = type.parameterType(paramCount - 1).isArray(); - final MethodHandle mh = MH.asType(methodHandle, adaptType(type, needsCallee, isVarArg)); - this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity if (needsCallee && !isVarArg) { this.arity--; } ! if (scope != null) { ! this.invokeHandle = mh; ! this.constructHandle = mh; ! } else if (isConstructor(mh)) { if (!isVarArg) { this.arity--; // drop the boolean flag for arity } /* * We insert a boolean argument to tell if the method was invoked as * constructor or not if the method handle's first argument is boolean. */ ! this.invokeHandle = MH.insertArguments(mh, 0, false); ! this.constructHandle = MH.insertArguments(mh, 0, true); if (specs != null) { this.invokeSpecializations = new MethodHandle[specs.length]; this.constructSpecializations = new MethodHandle[specs.length]; for (int i = 0; i < specs.length; i++) { this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false); this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true); } } } else { ! this.invokeHandle = mh; ! this.constructHandle = mh; this.invokeSpecializations = specs; this.constructSpecializations = specs; } } /** ! * Takes a method type, and returns a (potentially different method type) that the method handles used by ! * ScriptFunction must conform to in order to be usable in {@link #invoke(Object, Object...)} and ! * {@link #construct(Object, Object...)}. The returned method type will be sure to return {@code Object}, and will ! * have all its parameters turned into {@code Object} as well, except for the following ones: * <ul> - * <li>an optional first {@code boolean} parameter, used for some functions to distinguish method and constructor - * invocation,</li> * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> ! * <li>the second (or, in presence of boolean parameter, third) argument, which is forced to be ! * {@link ScriptFunction}, in case the function receives itself (callee) as an argument</li> ! * @param type the original type ! * @param hasCallee true if the function uses the callee argument ! * @param isVarArg if the function is a vararg ! * @return the new type, conforming to the rules above. ! */ ! private static MethodType adaptType(final MethodType type, final boolean hasCallee, final boolean isVarArg) { ! // Generify broadly ! MethodType newType = type.generic().changeReturnType(Object.class); ! if(isVarArg) { ! // Change back to vararg if we over-generified it newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); } ! final boolean hasBoolean = type.parameterType(0) == boolean.class; ! if(hasBoolean) { ! // Restore the initial boolean argument ! newType = newType.changeParameterType(0, boolean.class); ! } ! if(hasCallee) { ! // Restore the ScriptFunction argument ! newType = newType.changeParameterType(hasBoolean ? 2 : 1, ScriptFunction.class); } ! return newType; } @Override public String getClassName() { return "Function"; --- 159,296 ---- } /** * Constructor * ! * @param data static function data * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) * @param map property map * @param scope scope * @param allocator method handle to this function's allocator - see JO$ classes */ protected ScriptFunction( ! final ScriptFunctionData data, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, ! final MethodHandle allocator) { ! ! super(map); ! ! if (Context.DEBUG) { ! constructorCount++; ! } ! this.name = data.getName(); ! this.source = data.getSource(); ! this.token = data.getToken(); ! this.arity = data.getArity(); ! this.scope = scope; ! if(data.needsCallee()) { ! setHasCalleeParameter(); ! } + this.invoker = methodHandle; + this.constructor = methodHandle; this.allocator = allocator; ! this.allocatorMap = data.getAllocatorMap(); } /** * Constructor * * @param name function name * @param methodHandle method handle to function (if specializations are present, assumed to be most generic) * @param map property map * @param scope scope * @param needsCallee does this method use the {@code callee} variable * @param specs specialized version of this function - other method handles */ protected ScriptFunction( final String name, final MethodHandle methodHandle, final PropertyMap map, final ScriptObject scope, final boolean needsCallee, final MethodHandle[] specs) { super(map); if (Context.DEBUG) { constructorCount++; } this.name = name; ! this.source = null; ! this.token = 0; this.scope = scope; if(needsCallee) { setHasCalleeParameter(); } final MethodType type = methodHandle.type(); final int paramCount = type.parameterCount(); final boolean isVarArg = type.parameterType(paramCount - 1).isArray(); this.arity = isVarArg ? -1 : paramCount - 1; //drop the self param for arity if (needsCallee && !isVarArg) { this.arity--; } ! if (isConstructor(methodHandle)) { if (!isVarArg) { this.arity--; // drop the boolean flag for arity } /* * We insert a boolean argument to tell if the method was invoked as * constructor or not if the method handle's first argument is boolean. */ ! this.invoker = MH.insertArguments(methodHandle, 0, false); ! this.constructor = MH.insertArguments(methodHandle, 0, true); if (specs != null) { this.invokeSpecializations = new MethodHandle[specs.length]; this.constructSpecializations = new MethodHandle[specs.length]; for (int i = 0; i < specs.length; i++) { this.invokeSpecializations[i] = MH.insertArguments(specs[i], 0, false); this.constructSpecializations[i] = MH.insertArguments(specs[i], 0, true); } } } else { ! this.invoker = methodHandle; ! this.constructor = methodHandle; this.invokeSpecializations = specs; this.constructSpecializations = specs; } } /** ! * Takes a method handle, and returns a potentially different method handle that can be used in ! * {@link #invoke(Object, Object...)} or {@link #construct(Object, Object...)}. The returned method handle ! * will be sure to return {@code Object}, and will have all its parameters turned into {@code Object} as well, ! * except for the following ones: * <ul> * <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li> ! * <li>the second argument, which is forced to be {@link ScriptFunction}, in case the function receives itself ! * (callee) as an argument</li> ! * </ul> ! * ! * @param handle the original method handle ! * @return the new handle, conforming to the rules above. ! */ ! private MethodHandle adaptMethodType(final MethodHandle handle) { ! final MethodType type = handle.type(); ! MethodType newType = type.generic(); ! if (isVarArg(handle)) { newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class); } ! if (hasCalleeParameter()) { ! newType = newType.changeParameterType(1, ScriptFunction.class); } ! return type.equals(newType) ? handle : handle.asType(newType); } @Override public String getClassName() { return "Function";
*** 370,416 **** public Object invoke(final Object self, final Object... arguments) throws Throwable { if (Context.DEBUG) { invokes++; } final Object selfObj = convertThisObject(self); final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; ! if (isVarArg(invokeHandle)) { if (hasCalleeParameter()) { ! return invokeHandle.invokeExact(selfObj, this, args); } ! return invokeHandle.invokeExact(selfObj, args); } ! final int paramCount = invokeHandle.type().parameterCount(); if (hasCalleeParameter()) { switch (paramCount) { case 2: ! return invokeHandle.invokeExact(selfObj, this); case 3: ! return invokeHandle.invokeExact(selfObj, this, getArg(args, 0)); case 4: ! return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1)); case 5: ! return invokeHandle.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return invokeHandle.invokeWithArguments(withArguments(selfObj, this, paramCount, args)); } } switch (paramCount) { case 1: ! return invokeHandle.invokeExact(selfObj); case 2: ! return invokeHandle.invokeExact(selfObj, getArg(args, 0)); case 3: ! return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); case 4: ! return invokeHandle.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return invokeHandle.invokeWithArguments(withArguments(selfObj, null, paramCount, args)); } } private static Object getArg(final Object[] args, final int i) { return i < args.length ? args[i] : UNDEFINED; --- 361,411 ---- public Object invoke(final Object self, final Object... arguments) throws Throwable { if (Context.DEBUG) { invokes++; } + if (genericInvoker == null) { + genericInvoker = adaptMethodType(invoker); + } + final Object selfObj = convertThisObject(self); final Object[] args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments; ! if (isVarArg(genericInvoker)) { if (hasCalleeParameter()) { ! return genericInvoker.invokeExact(selfObj, this, args); } ! return genericInvoker.invokeExact(selfObj, args); } ! final int paramCount = genericInvoker.type().parameterCount(); if (hasCalleeParameter()) { switch (paramCount) { case 2: ! return genericInvoker.invokeExact(selfObj, this); case 3: ! return genericInvoker.invokeExact(selfObj, this, getArg(args, 0)); case 4: ! return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1)); case 5: ! return genericInvoker.invokeExact(selfObj, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return genericInvoker.invokeWithArguments(withArguments(selfObj, this, paramCount, args)); } } switch (paramCount) { case 1: ! return genericInvoker.invokeExact(selfObj); case 2: ! return genericInvoker.invokeExact(selfObj, getArg(args, 0)); case 3: ! return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1)); case 4: ! return genericInvoker.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return genericInvoker.invokeWithArguments(withArguments(selfObj, null, paramCount, args)); } } private static Object getArg(final Object[] args, final int i) { return i < args.length ? args[i] : UNDEFINED;
*** 422,469 **** * @param args Call arguments. * @return ScriptFunction result. * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it */ public Object construct(final Object self, final Object... args) throws Throwable { ! if (constructHandle == null) { typeError("not.a.constructor", ScriptRuntime.safeToString(this)); } ! if (isVarArg(constructHandle)) { if (hasCalleeParameter()) { ! return constructHandle.invokeExact(self, this, args); } ! return constructHandle.invokeExact(self, args); } ! final int paramCount = constructHandle.type().parameterCount(); if (hasCalleeParameter()) { switch (paramCount) { case 2: ! return constructHandle.invokeExact(self, this); case 3: ! return constructHandle.invokeExact(self, this, getArg(args, 0)); case 4: ! return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1)); case 5: ! return constructHandle.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return constructHandle.invokeWithArguments(withArguments(self, this, args)); } } switch(paramCount) { case 1: ! return constructHandle.invokeExact(self); case 2: ! return constructHandle.invokeExact(self, getArg(args, 0)); case 3: ! return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1)); case 4: ! return constructHandle.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return constructHandle.invokeWithArguments(withArguments(self, null, args)); } } private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) { return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function --- 417,467 ---- * @param args Call arguments. * @return ScriptFunction result. * @throws Throwable if there is an exception/error with the constructor invocation or thrown from it */ public Object construct(final Object self, final Object... args) throws Throwable { ! if (constructor == null) { typeError("not.a.constructor", ScriptRuntime.safeToString(this)); } ! if (genericConstructor == null) { ! genericConstructor = adaptMethodType(constructor); ! } ! if (isVarArg(genericConstructor)) { if (hasCalleeParameter()) { ! return genericConstructor.invokeExact(self, this, args); } ! return genericConstructor.invokeExact(self, args); } ! final int paramCount = genericConstructor.type().parameterCount(); if (hasCalleeParameter()) { switch (paramCount) { case 2: ! return genericConstructor.invokeExact(self, this); case 3: ! return genericConstructor.invokeExact(self, this, getArg(args, 0)); case 4: ! return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1)); case 5: ! return genericConstructor.invokeExact(self, this, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return genericConstructor.invokeWithArguments(withArguments(self, this, args)); } } switch(paramCount) { case 1: ! return genericConstructor.invokeExact(self); case 2: ! return genericConstructor.invokeExact(self, getArg(args, 0)); case 3: ! return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1)); case 4: ! return genericConstructor.invokeExact(self, getArg(args, 0), getArg(args, 1), getArg(args, 2)); default: ! return genericConstructor.invokeWithArguments(withArguments(self, null, args)); } } private static Object[] withArguments(final Object self, final ScriptFunction function, final Object... args) { return withArguments(self, function, args.length + (function == null ? 1 : 2), args); // + 2 to include self and function
*** 575,585 **** public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(super.toString()) .append(" [ ") ! .append(invokeHandle) .append(", ") .append((name == null || name.isEmpty()) ? "<anonymous>" : name); if (source != null) { sb.append(" @ ") --- 573,583 ---- public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(super.toString()) .append(" [ ") ! .append(invoker) .append(", ") .append((name == null || name.isEmpty()) ? "<anonymous>" : name); if (source != null) { sb.append(" @ ")
*** 704,714 **** * method handle for this ScriptFunction * @see SpecializedFunction * @return invokeHandle */ public final MethodHandle getInvokeHandle() { ! return invokeHandle; } /** * Return the invoke handle bound to a given ScriptObject self reference. * If callee parameter is required result is rebound to this. --- 702,712 ---- * method handle for this ScriptFunction * @see SpecializedFunction * @return invokeHandle */ public final MethodHandle getInvokeHandle() { ! return invoker; } /** * Return the invoke handle bound to a given ScriptObject self reference. * If callee parameter is required result is rebound to this.
*** 738,756 **** /** * Get a method handle to the constructor for this function * @return constructor handle */ public final MethodHandle getConstructHandle() { ! return constructHandle; } /** * Set a method handle to the constructor for this function * @param constructHandle constructor handle */ public final void setConstructHandle(final MethodHandle constructHandle) { ! this.constructHandle = constructHandle; this.constructSpecializations = null; } /** * Get the name for this function --- 736,754 ---- /** * Get a method handle to the constructor for this function * @return constructor handle */ public final MethodHandle getConstructHandle() { ! return constructor; } /** * Set a method handle to the constructor for this function * @param constructHandle constructor handle */ public final void setConstructHandle(final MethodHandle constructHandle) { ! this.constructor = constructHandle; this.constructSpecializations = null; } /** * Get the name for this function
*** 765,775 **** * null checking invokeHandle * * @return true if this needs compilation */ public final boolean needsCompilation() { ! return invokeHandle == null; } /** * Get token for this function * @return token --- 763,773 ---- * null checking invokeHandle * * @return true if this needs compilation */ public final boolean needsCompilation() { ! return invoker == null; } /** * Get token for this function * @return token