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