1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 
  30 import java.lang.invoke.MethodHandle;
  31 import java.lang.invoke.MethodHandles;
  32 import java.lang.invoke.MethodType;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.LinkedList;
  36 import jdk.internal.dynalink.support.NameCodec;
  37 
  38 import jdk.nashorn.internal.codegen.Compiler;
  39 import jdk.nashorn.internal.codegen.CompilerConstants;
  40 import jdk.nashorn.internal.codegen.FunctionSignature;
  41 import jdk.nashorn.internal.codegen.types.Type;
  42 import jdk.nashorn.internal.ir.FunctionNode;
  43 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
  44 import jdk.nashorn.internal.parser.Token;
  45 import jdk.nashorn.internal.parser.TokenType;
  46 
  47 /**
  48  * This is a subclass that represents a script function that may be regenerated,
  49  * for example with specialization based on call site types, or lazily generated.
  50  * The common denominator is that it can get new invokers during its lifespan,
  51  * unlike {@code FinalScriptFunctionData}
  52  */
  53 public final class RecompilableScriptFunctionData extends ScriptFunctionData {
  54 
  55     /** FunctionNode with the code for this ScriptFunction */
  56     private FunctionNode functionNode;
  57 
  58     /** Source from which FunctionNode was parsed. */
  59     private final Source source;
  60 
  61     /** Token of this function within the source. */
  62     private final long token;
  63 
  64     /** Allocator map from makeMap() */
  65     private final PropertyMap allocatorMap;
  66 
  67     /** Code installer used for all further recompilation/specialization of this ScriptFunction */
  68     private CodeInstaller<ScriptEnvironment> installer;
  69 
  70     /** Name of class where allocator function resides */
  71     private final String allocatorClassName;
  72 
  73     /** lazily generated allocator */
  74     private MethodHandle allocator;
  75 
  76     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
  77 
  78     /**
  79      * Used for specialization based on runtime arguments. Whenever we specialize on
  80      * callsite parameter types at runtime, we need to use a parameter type guard to
  81      * ensure that the specialized version of the script function continues to be
  82      * applicable for a particular callsite *
  83      */
  84     private static final MethodHandle PARAM_TYPE_GUARD = findOwnMH("paramTypeGuard", boolean.class, Type[].class,  Object[].class);
  85 
  86     /**
  87      * It is usually a good gamble whever we detect a runtime callsite with a double
  88      * (or java.lang.Number instance) to specialize the parameter to an integer, if the
  89      * parameter in question can be represented as one. The double typically only exists
  90      * because the compiler doesn't know any better than "a number type" and conservatively
  91      * picks doubles when it can't prove that an integer addition wouldn't overflow
  92      */
  93     private static final MethodHandle ENSURE_INT = findOwnMH("ensureInt", int.class, Object.class);
  94 
  95     /**
  96      * Constructor - public as scripts use it
  97      *
  98      * @param functionNode       functionNode that represents this function code
  99      * @param installer          installer for code regeneration versions of this function
 100      * @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
 101      * @param allocatorMap       allocator map to seed instances with, when constructing
 102      */
 103     public RecompilableScriptFunctionData(final FunctionNode functionNode, final CodeInstaller<ScriptEnvironment> installer, final String allocatorClassName, final PropertyMap allocatorMap) {
 104         super(functionName(functionNode),
 105               functionNode.getParameters().size(),
 106               getFlags(functionNode));
 107 
 108         this.functionNode       = functionNode;
 109         this.source             = functionNode.getSource();
 110         this.token              = tokenFor(functionNode);
 111         this.installer          = installer;
 112         this.allocatorClassName = allocatorClassName;
 113         this.allocatorMap       = allocatorMap;
 114     }
 115 
 116     @Override
 117     String toSource() {
 118         if (source != null && token != 0) {
 119             return source.getString(Token.descPosition(token), Token.descLength(token));
 120         }
 121 
 122         return "function " + (name == null ? "" : name) + "() { [native code] }";
 123     }
 124 
 125     @Override
 126     public String toString() {
 127         final StringBuilder sb = new StringBuilder();
 128 
 129         if (source != null) {
 130             sb.append(source.getName());
 131             if (functionNode != null) {
 132                 sb.append(':').append(functionNode.getLineNumber());
 133             }
 134             sb.append(' ');
 135         }
 136 
 137         return sb.toString() + super.toString();
 138     }
 139 
 140     private static String functionName(final FunctionNode fn) {
 141         if (fn.isAnonymous()) {
 142             return "";
 143         } else {
 144             final FunctionNode.Kind kind = fn.getKind();
 145             if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
 146                 final String name = NameCodec.decode(fn.getIdent().getName());
 147                 return name.substring(4); // 4 is "get " or "set "
 148             } else {
 149                 return fn.getIdent().getName();
 150             }
 151         }
 152     }
 153 
 154     private static long tokenFor(final FunctionNode fn) {
 155         final int  position   = Token.descPosition(fn.getFirstToken());
 156         final int  length     = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
 157 
 158         return Token.toDesc(TokenType.FUNCTION, position, length);
 159     }
 160 
 161     private static int getFlags(final FunctionNode functionNode) {
 162         int flags = IS_CONSTRUCTOR;
 163         if (functionNode.isStrict()) {
 164             flags |= IS_STRICT;
 165         }
 166         if (functionNode.needsCallee()) {
 167             flags |= NEEDS_CALLEE;
 168         }
 169         if (functionNode.usesThis() || functionNode.hasEval()) {
 170             flags |= USES_THIS;
 171         }
 172         return flags;
 173     }
 174 
 175     @Override
 176     ScriptObject allocate(final PropertyMap map) {
 177         try {
 178             ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
 179             return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
 180         } catch (final RuntimeException | Error e) {
 181             throw e;
 182         } catch (final Throwable t) {
 183             throw new RuntimeException(t);
 184         }
 185     }
 186 
 187     private void ensureHasAllocator() throws ClassNotFoundException {
 188         if (allocator == null && allocatorClassName != null) {
 189             this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
 190         }
 191     }
 192 
 193     @Override
 194     PropertyMap getAllocatorMap() {
 195         return allocatorMap;
 196     }
 197 
 198 
 199     @Override
 200     protected void ensureCompiled() {
 201         if (functionNode != null && functionNode.isLazy()) {
 202             Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
 203             final Compiler compiler = new Compiler(installer);
 204             functionNode = compiler.compile(functionNode);
 205             assert !functionNode.isLazy();
 206             compiler.install(functionNode);
 207             flags = getFlags(functionNode);
 208         }
 209     }
 210 
 211     @Override
 212     protected synchronized void ensureCodeGenerated() {
 213         if (!code.isEmpty()) {
 214             return; // nothing to do, we have code, at least some.
 215         }
 216 
 217         ensureCompiled();
 218 
 219         /*
 220          * We can't get to this program point unless we have bytecode, either from
 221          * eager compilation or from running a lazy compile on the lines above
 222          */
 223 
 224         assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
 225 
 226         // code exists - look it up and add it into the automatically sorted invoker list
 227         addCode(functionNode);
 228 
 229         if (! functionNode.canSpecialize()) {
 230             // allow GC to claim IR stuff that is not needed anymore
 231             functionNode = null;
 232             installer = null;
 233         }
 234     }
 235 
 236     private MethodHandle addCode(final FunctionNode fn) {
 237         return addCode(fn, null, null, null);
 238     }
 239 
 240     private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
 241         final MethodType targetType = new FunctionSignature(fn).getMethodType();
 242         MethodHandle target =
 243             MH.findStatic(
 244                     LOOKUP,
 245                     fn.getCompileUnit().getCode(),
 246                     fn.getName(),
 247                     targetType);
 248 
 249         /*
 250          * For any integer argument. a double that is representable as an integer is OK.
 251          * otherwise the guard would have failed. in that case introduce a filter that
 252          * casts the double to an integer, which we know will preserve all precision.
 253          */
 254         for (int i = 0; i < targetType.parameterCount(); i++) {
 255             if (targetType.parameterType(i) == int.class) {
 256                 //representable as int
 257                 target = MH.filterArguments(target, i, ENSURE_INT);
 258             }
 259         }
 260 
 261         MethodHandle mh = target;
 262         if (guard != null) {
 263             mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
 264         }
 265 
 266         final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh);
 267         code.add(cf);
 268 
 269         return cf.getInvoker();
 270     }
 271 
 272     private static Type runtimeType(final Object arg) {
 273         if (arg == null) {
 274             return Type.OBJECT;
 275         }
 276 
 277         final Class<?> clazz = arg.getClass();
 278         assert !clazz.isPrimitive() : "always boxed";
 279         if (clazz == Double.class) {
 280             return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER;
 281         } else if (clazz == Integer.class) {
 282             return Type.INT;
 283         } else if (clazz == Long.class) {
 284             return Type.LONG;
 285         } else if (clazz == String.class) {
 286             return Type.STRING;
 287         }
 288         return Type.OBJECT;
 289     }
 290 
 291     private static boolean canCoerce(final Object arg, final Type type) {
 292         Type argType = runtimeType(arg);
 293         if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) {
 294             return true;
 295         }
 296         System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass());
 297         new Throwable().printStackTrace();
 298         return false;
 299     }
 300 
 301     @SuppressWarnings("unused")
 302     private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) {
 303         final int length = args.length;
 304         assert args.length >= paramTypes.length;
 305 
 306         //i==start, skip the this, callee params etc
 307         int start = args.length - paramTypes.length;
 308         for (int i = start; i < args.length; i++) {
 309             final Object arg = args[i];
 310             if (!canCoerce(arg, paramTypes[i - start])) {
 311                 return false;
 312             }
 313         }
 314         return true;
 315     }
 316 
 317     @SuppressWarnings("unused")
 318     private static int ensureInt(final Object arg) {
 319         if (arg instanceof Number) {
 320             return ((Number)arg).intValue();
 321         } else if (arg instanceof Undefined) {
 322             return 0;
 323         }
 324         throw new AssertionError(arg);
 325     }
 326 
 327     /**
 328      * Given the runtime callsite args, compute a method type that is equivalent to what
 329      * was passed - this is typically a lot more specific that what the compiler has been
 330      * able to deduce
 331      * @param callSiteType callsite type for the compiled callsite target
 332      * @param args runtime arguments to the compiled callsite target
 333      * @return adjusted method type, narrowed as to conform to runtime callsite type instead
 334      */
 335     private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) {
 336         if (args == null) {
 337             //for example bound, or otherwise runtime arguments to callsite unavailable, then
 338             //do not change the type
 339             return callSiteType;
 340         }
 341         final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()];
 342         final int        start      = args.length - callSiteType.parameterCount();
 343         for (int i = start; i < args.length; i++) {
 344             paramTypes[i - start] = runtimeType(args[i]).getTypeClass();
 345         }
 346         return MH.type(callSiteType.returnType(), paramTypes);
 347     }
 348 
 349     private static ArrayList<Type> runtimeType(final MethodType mt) {
 350         final ArrayList<Type> type = new ArrayList<>();
 351         for (int i = 0; i < mt.parameterCount(); i++) {
 352             type.add(Type.typeFor(mt.parameterType(i)));
 353         }
 354         return type;
 355     }
 356 
 357     @Override
 358     synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
 359         final MethodType runtimeType = runtimeType(callSiteType, args);
 360         assert runtimeType.parameterCount() == callSiteType.parameterCount();
 361 
 362         final MethodHandle mh = super.getBestInvoker(runtimeType, args);
 363 
 364         /*
 365          * Not all functions can be specialized, for example, if we deemed memory
 366          * footprint too large to store a parse snapshot, or if it is meaningless
 367          * to do so, such as e.g. for runScript
 368          */
 369         if (functionNode == null || !functionNode.canSpecialize()) {
 370             return mh;
 371         }
 372 
 373         /*
 374          * Check if best invoker is equally specific or more specific than runtime
 375          * type. In that case, we don't need further specialization, but can use
 376          * whatever we have already. We know that it will match callSiteType, or it
 377          * would not have been returned from getBestInvoker
 378          */
 379         if (!code.isLessSpecificThan(runtimeType)) {
 380             return mh;
 381         }
 382 
 383         int i;
 384         final FunctionNode snapshot = functionNode.getSnapshot();
 385         assert snapshot != null;
 386 
 387         /*
 388          * Create a list of the arg types that the compiler knows about
 389          * typically, the runtime args are a lot more specific, and we should aggressively
 390          * try to use those whenever possible
 391          * We WILL try to make an aggressive guess as possible, and add guards if needed.
 392          * For example, if the compiler can deduce that we have a number type, but the runtime
 393          * passes and int, we might still want to keep it an int, and the gamble to
 394          * check that whatever is passed is int representable usually pays off
 395          * If the compiler only knows that a parameter is an "Object", it is still worth
 396          * it to try to specialize it by looking at the runtime arg.
 397          */
 398         final LinkedList<Type> compileTimeArgs = new LinkedList<>();
 399         for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
 400             compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i)));
 401         }
 402 
 403         /*
 404          * The classes known at compile time are a safe to generate as primitives without parameter guards
 405          * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives
 406          * IFF there are parameter guards
 407          */
 408         MethodHandle guard = null;
 409         final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType);
 410         while (runtimeParamTypes.size() > functionNode.getParameters().size()) {
 411             runtimeParamTypes.remove(0);
 412         }
 413         for (i = 0; i < compileTimeArgs.size(); i++) {
 414             final Type rparam = Type.typeFor(runtimeType.parameterType(i));
 415             final Type cparam = compileTimeArgs.get(i);
 416 
 417             if (cparam.isObject() && !rparam.isObject()) {
 418                 //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive
 419                 if (guard == null) {
 420                     guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]));
 421                 }
 422             }
 423         }
 424 
 425         Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args));
 426 
 427         assert snapshot != null;
 428         assert snapshot != functionNode;
 429 
 430         final Compiler compiler = new Compiler(installer);
 431 
 432         final FunctionNode compiledSnapshot = compiler.compile(
 433             snapshot.setHints(
 434                 null,
 435                 new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]))));
 436 
 437         /*
 438          * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you
 439          * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication
 440          * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is
 441          * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until
 442          * the "different types for one symbol in difference places" work is done
 443          */
 444         compiler.install(compiledSnapshot);
 445 
 446         return addCode(compiledSnapshot, runtimeType, guard, mh);
 447     }
 448 
 449     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 450         return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
 451     }
 452 
 453 }
 454