| /* |
| * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package jdk.nashorn.internal.runtime; |
| |
| import static jdk.nashorn.internal.lookup.Lookup.MH; |
| |
| import java.lang.invoke.MethodHandle; |
| import java.lang.invoke.MethodHandles; |
| import java.lang.invoke.MethodType; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.LinkedList; |
| |
| import jdk.nashorn.internal.codegen.Compiler; |
| import jdk.nashorn.internal.codegen.CompilerConstants; |
| import jdk.nashorn.internal.codegen.FunctionSignature; |
| import jdk.nashorn.internal.codegen.types.Type; |
| import jdk.nashorn.internal.ir.FunctionNode; |
| import jdk.nashorn.internal.ir.FunctionNode.CompilationState; |
| import jdk.nashorn.internal.parser.Token; |
| import jdk.nashorn.internal.parser.TokenType; |
| |
| /** |
| * This is a subclass that represents a script function that may be regenerated, |
| * for example with specialization based on call site types, or lazily generated. |
| * The common denominator is that it can get new invokers during its lifespan, |
| * unlike {@code FinalScriptFunctionData} |
| */ |
| public final class RecompilableScriptFunctionData extends ScriptFunctionData { |
| |
| /** FunctionNode with the code for this ScriptFunction */ |
| private volatile FunctionNode functionNode; |
| |
| /** Source from which FunctionNode was parsed. */ |
| private final Source source; |
| |
| /** Token of this function within the source. */ |
| private final long token; |
| |
| /** Allocator map from makeMap() */ |
| private final PropertyMap allocatorMap; |
| |
| /** Code installer used for all further recompilation/specialization of this ScriptFunction */ |
| private volatile CodeInstaller<ScriptEnvironment> installer; |
| |
| /** Name of class where allocator function resides */ |
| private final String allocatorClassName; |
| |
| /** lazily generated allocator */ |
| private MethodHandle allocator; |
| |
| private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); |
| |
| /** |
| * Used for specialization based on runtime arguments. Whenever we specialize on |
| * callsite parameter types at runtime, we need to use a parameter type guard to |
| * ensure that the specialized version of the script function continues to be |
| * applicable for a particular callsite * |
| */ |
| private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class, Object[].class); |
| |
| /** |
| * It is usually a good gamble whever we detect a runtime callsite with a double |
| * (or java.lang.Number instance) to specialize the parameter to an integer, if the |
| * parameter in question can be represented as one. The double typically only exists |
| * because the compiler doesn't know any better than "a number type" and conservatively |
| * picks doubles when it can't prove that an integer addition wouldn't overflow |
| */ |
| private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class); |
| |
| /** |
| * Constructor - public as scripts use it |
| * |
| * @param functionNode functionNode that represents this function code |
| * @param installer installer for code regeneration versions of this function |
| * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor |
| * @param allocatorMap allocator map to seed instances with, when constructing |
| */ |
| public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) { |
| super(functionNode.isAnonymous() ? |
| "" : |
| functionNode.getIdent().getName(), |
| functionNode.getParameters().size(), |
| functionNode.isStrict(), |
| false, |
| true); |
| |
| this.functionNode = functionNode; |
| this.source = functionNode.getSource(); |
| this.token = tokenFor(functionNode); |
| this.installer = installer; |
| this.allocatorClassName = allocatorClassName; |
| this.allocatorMap = allocatorMap; |
| } |
| |
| @Override |
| String toSource() { |
| if (source != null && token != 0) { |
| return source.getString(Token.descPosition(token), Token.descLength(token)); |
| } |
| |
| return "function " + (name == null ? "" : name) + "() { [native code] }"; |
| } |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb = new StringBuilder(); |
| |
| if (source != null) { |
| sb.append(source.getName()) |
| .append(':') |
| .append(source.getLine(Token.descPosition(token))) |
| .append(' '); |
| } |
| |
| return sb.toString() + super.toString(); |
| } |
| |
| private static long tokenFor(final FunctionNode fn) { |
| final int position = Token.descPosition(fn.getFirstToken()); |
| final int length = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken()); |
| |
| return Token.toDesc(TokenType.FUNCTION, position, length); |
| } |
| |
| @Override |
| ScriptObject allocate() { |
| try { |
| ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try |
| return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap); |
| } catch (final RuntimeException | Error e) { |
| throw e; |
| } catch (final Throwable t) { |
| throw new RuntimeException(t); |
| } |
| } |
| |
| private void ensureHasAllocator() throws ClassNotFoundException { |
| if (allocator == null && allocatorClassName != null) { |
| this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class)); |
| } |
| } |
| |
| @Override |
| protected void ensureCodeGenerated() { |
| if (!code.isEmpty()) { |
| return; // nothing to do, we have code, at least some. |
| } |
| |
| if (functionNode.isLazy()) { |
| Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'"); |
| final Compiler compiler = new Compiler(installer); |
| functionNode = compiler.compile(functionNode); |
| assert !functionNode.isLazy(); |
| compiler.install(functionNode); |
| |
| /* |
| * We don't need to update any flags - varArgs and needsCallee are instrincic |
| * in the function world we need to get a destination node from the compile instead |
| * and replace it with our function node. TODO |
| */ |
| } |
| |
| /* |
| * We can't get to this program point unless we have bytecode, either from |
| * eager compilation or from running a lazy compile on the lines above |
| */ |
| |
| assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode); |
| |
| // code exists - look it up and add it into the automatically sorted invoker list |
| addCode(functionNode); |
| |
| if (! functionNode.canSpecialize()) { |
| // allow GC to claim IR stuff that is not needed anymore |
| functionNode = null; |
| installer = null; |
| } |
| } |
| |
| private MethodHandle addCode(final FunctionNode fn) { |
| return addCode(fn, null, null, null); |
| } |
| |
| private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) { |
| final MethodType targetType = new FunctionSignature(fn).getMethodType(); |
| MethodHandle target = |
| MH.findStatic( |
| LOOKUP, |
| fn.getCompileUnit().getCode(), |
| fn.getName(), |
| targetType); |
| |
| /* |
| * For any integer argument. a double that is representable as an integer is OK. |
| * otherwise the guard would have failed. in that case introduce a filter that |
| * casts the double to an integer, which we know will preserve all precision. |
| */ |
| for (int i = 0; i < targetType.parameterCount(); i++) { |
| if (targetType.parameterType(i) == int.class) { |
| //representable as int |
| target = MH.filterArguments(target, i, ENSURE_INT); |
| } |
| } |
| |
| MethodHandle mh = target; |
| if (guard != null) { |
| mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback); |
| } |
| |
| final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh); |
| code.add(cf); |
| |
| return cf.getInvoker(); |
| } |
| |
| private static Type runtimeType(final Object arg) { |
| if (arg == null) { |
| return Type.OBJECT; |
| } |
| |
| final Class<?> clazz = arg.getClass(); |
| assert !clazz.isPrimitive() : "always boxed"; |
| if (clazz == Double.class) { |
| return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER; |
| } else if (clazz == Integer.class) { |
| return Type.INT; |
| } else if (clazz == Long.class) { |
| return Type.LONG; |
| } else if (clazz == String.class) { |
| return Type.STRING; |
| } |
| return Type.OBJECT; |
| } |
| |
| private static boolean canCoerce(final Object arg, final Type type) { |
| Type argType = runtimeType(arg); |
| if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) { |
| return true; |
| } |
| System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass()); |
| new Throwable().printStackTrace(); |
| return false; |
| } |
| |
| @SuppressWarnings("unused") |
| private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) { |
| final int length = args.length; |
| assert args.length >= paramTypes.length; |
| |
| //i==start, skip the this, callee params etc |
| int start = args.length - paramTypes.length; |
| for (int i = start; i < args.length; i++) { |
| final Object arg = args[i]; |
| if (!canCoerce(arg, paramTypes[i - start])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @SuppressWarnings("unused") |
| private static int ensureInt(final Object arg) { |
| if (arg instanceof Number) { |
| return ((Number)arg).intValue(); |
| } else if (arg instanceof Undefined) { |
| return 0; |
| } |
| throw new AssertionError(arg); |
| } |
| |
| /** |
| * Given the runtime callsite args, compute a method type that is equivalent to what |
| * was passed - this is typically a lot more specific that what the compiler has been |
| * able to deduce |
| * @param callSiteType callsite type for the compiled callsite target |
| * @param args runtime arguments to the compiled callsite target |
| * @return adjusted method type, narrowed as to conform to runtime callsite type instead |
| */ |
| private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) { |
| if (args == null) { |
| //for example bound, or otherwise runtime arguments to callsite unavailable, then |
| //do not change the type |
| return callSiteType; |
| } |
| final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()]; |
| final int start = args.length - callSiteType.parameterCount(); |
| for (int i = start; i < args.length; i++) { |
| paramTypes[i - start] = runtimeType(args[i]).getTypeClass(); |
| } |
| return MH.type(callSiteType.returnType(), paramTypes); |
| } |
| |
| private static ArrayList<Type> runtimeType(final MethodType mt) { |
| final ArrayList<Type> type = new ArrayList<>(); |
| for (int i = 0; i < mt.parameterCount(); i++) { |
| type.add(Type.typeFor(mt.parameterType(i))); |
| } |
| return type; |
| } |
| |
| @Override |
| MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) { |
| final MethodType runtimeType = runtimeType(callSiteType, args); |
| assert runtimeType.parameterCount() == callSiteType.parameterCount(); |
| |
| final MethodHandle mh = super.getBestInvoker(runtimeType, args); |
| |
| /* |
| * Not all functions can be specialized, for example, if we deemed memory |
| * footprint too large to store a parse snapshot, or if it is meaningless |
| * to do so, such as e.g. for runScript |
| */ |
| if (functionNode == null || !functionNode.canSpecialize()) { |
| return mh; |
| } |
| |
| /* |
| * Check if best invoker is equally specific or more specific than runtime |
| * type. In that case, we don't need further specialization, but can use |
| * whatever we have already. We know that it will match callSiteType, or it |
| * would not have been returned from getBestInvoker |
| */ |
| if (!code.isLessSpecificThan(runtimeType)) { |
| return mh; |
| } |
| |
| int i; |
| final FunctionNode snapshot = functionNode.getSnapshot(); |
| assert snapshot != null; |
| |
| /* |
| * Create a list of the arg types that the compiler knows about |
| * typically, the runtime args are a lot more specific, and we should aggressively |
| * try to use those whenever possible |
| * We WILL try to make an aggressive guess as possible, and add guards if needed. |
| * For example, if the compiler can deduce that we have a number type, but the runtime |
| * passes and int, we might still want to keep it an int, and the gamble to |
| * check that whatever is passed is int representable usually pays off |
| * If the compiler only knows that a parameter is an "Object", it is still worth |
| * it to try to specialize it by looking at the runtime arg. |
| */ |
| final LinkedList<Type> compileTimeArgs = new LinkedList<>(); |
| for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) { |
| compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i))); |
| } |
| |
| /* |
| * The classes known at compile time are a safe to generate as primitives without parameter guards |
| * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives |
| * IFF there are parameter guards |
| */ |
| MethodHandle guard = null; |
| final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType); |
| while (runtimeParamTypes.size() > functionNode.getParameters().size()) { |
| runtimeParamTypes.remove(0); |
| } |
| for (i = 0; i < compileTimeArgs.size(); i++) { |
| final Type rparam = Type.typeFor(runtimeType.parameterType(i)); |
| final Type cparam = compileTimeArgs.get(i); |
| |
| if (cparam.isObject() && !rparam.isObject()) { |
| //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive |
| if (guard == null) { |
| guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])); |
| } |
| } |
| } |
| |
| Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args)); |
| |
| assert snapshot != null; |
| assert snapshot != functionNode; |
| |
| final Compiler compiler = new Compiler(installer); |
| |
| final FunctionNode compiledSnapshot = compiler.compile( |
| snapshot.setHints( |
| null, |
| new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()])))); |
| |
| /* |
| * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you |
| * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication |
| * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is |
| * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until |
| * the "different types for one symbol in difference places" work is done |
| */ |
| compiler.install(compiledSnapshot); |
| |
| return addCode(compiledSnapshot, runtimeType, guard, mh); |
| } |
| |
| private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { |
| return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types)); |
| } |
| |
| } |
| |