Tue, 15 Oct 2013 17:37:47 +0200
8026367: Add a sync keyword to mozilla_compat
Reviewed-by: sundar, attila, lagergren
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 */
26 package jdk.nashorn.internal.runtime;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
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;
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;
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 {
55 /** FunctionNode with the code for this ScriptFunction */
56 private FunctionNode functionNode;
58 /** Source from which FunctionNode was parsed. */
59 private final Source source;
61 /** Token of this function within the source. */
62 private final long token;
64 /** Allocator map from makeMap() */
65 private final PropertyMap allocatorMap;
67 /** Code installer used for all further recompilation/specialization of this ScriptFunction */
68 private CodeInstaller<ScriptEnvironment> installer;
70 /** Name of class where allocator function resides */
71 private final String allocatorClassName;
73 /** lazily generated allocator */
74 private MethodHandle allocator;
76 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
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);
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);
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 functionNode.isStrict(),
107 false,
108 true);
110 this.functionNode = functionNode;
111 this.source = functionNode.getSource();
112 this.token = tokenFor(functionNode);
113 this.installer = installer;
114 this.allocatorClassName = allocatorClassName;
115 this.allocatorMap = allocatorMap;
116 }
118 @Override
119 String toSource() {
120 if (source != null && token != 0) {
121 return source.getString(Token.descPosition(token), Token.descLength(token));
122 }
124 return "function " + (name == null ? "" : name) + "() { [native code] }";
125 }
127 @Override
128 public String toString() {
129 final StringBuilder sb = new StringBuilder();
131 if (source != null) {
132 sb.append(source.getName())
133 .append(':')
134 .append(functionNode.getLineNumber())
135 .append(' ');
136 }
138 return sb.toString() + super.toString();
139 }
141 private static String functionName(final FunctionNode fn) {
142 if (fn.isAnonymous()) {
143 return "";
144 } else {
145 final FunctionNode.Kind kind = fn.getKind();
146 if (kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
147 final String name = NameCodec.decode(fn.getIdent().getName());
148 return name.substring(4); // 4 is "get " or "set "
149 } else {
150 return fn.getIdent().getName();
151 }
152 }
153 }
155 private static long tokenFor(final FunctionNode fn) {
156 final int position = Token.descPosition(fn.getFirstToken());
157 final int length = Token.descPosition(fn.getLastToken()) - position + Token.descLength(fn.getLastToken());
159 return Token.toDesc(TokenType.FUNCTION, position, length);
160 }
162 @Override
163 ScriptObject allocate() {
164 try {
165 ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
166 return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap);
167 } catch (final RuntimeException | Error e) {
168 throw e;
169 } catch (final Throwable t) {
170 throw new RuntimeException(t);
171 }
172 }
174 private void ensureHasAllocator() throws ClassNotFoundException {
175 if (allocator == null && allocatorClassName != null) {
176 this.allocator = MH.findStatic(LOOKUP, Context.forStructureClass(allocatorClassName), CompilerConstants.ALLOCATE.symbolName(), MH.type(ScriptObject.class, PropertyMap.class));
177 }
178 }
180 @Override
181 protected synchronized void ensureCodeGenerated() {
182 if (!code.isEmpty()) {
183 return; // nothing to do, we have code, at least some.
184 }
186 if (functionNode.isLazy()) {
187 Compiler.LOG.info("Trampoline hit: need to do lazy compilation of '", functionNode.getName(), "'");
188 final Compiler compiler = new Compiler(installer);
189 functionNode = compiler.compile(functionNode);
190 assert !functionNode.isLazy();
191 compiler.install(functionNode);
193 /*
194 * We don't need to update any flags - varArgs and needsCallee are instrincic
195 * in the function world we need to get a destination node from the compile instead
196 * and replace it with our function node. TODO
197 */
198 }
200 /*
201 * We can't get to this program point unless we have bytecode, either from
202 * eager compilation or from running a lazy compile on the lines above
203 */
205 assert functionNode.hasState(CompilationState.EMITTED) : functionNode.getName() + " " + functionNode.getState() + " " + Debug.id(functionNode);
207 // code exists - look it up and add it into the automatically sorted invoker list
208 addCode(functionNode);
210 if (! functionNode.canSpecialize()) {
211 // allow GC to claim IR stuff that is not needed anymore
212 functionNode = null;
213 installer = null;
214 }
215 }
217 private MethodHandle addCode(final FunctionNode fn) {
218 return addCode(fn, null, null, null);
219 }
221 private MethodHandle addCode(final FunctionNode fn, final MethodType runtimeType, final MethodHandle guard, final MethodHandle fallback) {
222 final MethodType targetType = new FunctionSignature(fn).getMethodType();
223 MethodHandle target =
224 MH.findStatic(
225 LOOKUP,
226 fn.getCompileUnit().getCode(),
227 fn.getName(),
228 targetType);
230 /*
231 * For any integer argument. a double that is representable as an integer is OK.
232 * otherwise the guard would have failed. in that case introduce a filter that
233 * casts the double to an integer, which we know will preserve all precision.
234 */
235 for (int i = 0; i < targetType.parameterCount(); i++) {
236 if (targetType.parameterType(i) == int.class) {
237 //representable as int
238 target = MH.filterArguments(target, i, ENSURE_INT);
239 }
240 }
242 MethodHandle mh = target;
243 if (guard != null) {
244 mh = MH.guardWithTest(MH.asCollector(guard, Object[].class, target.type().parameterCount()), MH.asType(target, fallback.type()), fallback);
245 }
247 final CompiledFunction cf = new CompiledFunction(runtimeType == null ? targetType : runtimeType, mh);
248 code.add(cf);
250 return cf.getInvoker();
251 }
253 private static Type runtimeType(final Object arg) {
254 if (arg == null) {
255 return Type.OBJECT;
256 }
258 final Class<?> clazz = arg.getClass();
259 assert !clazz.isPrimitive() : "always boxed";
260 if (clazz == Double.class) {
261 return JSType.isRepresentableAsInt((double)arg) ? Type.INT : Type.NUMBER;
262 } else if (clazz == Integer.class) {
263 return Type.INT;
264 } else if (clazz == Long.class) {
265 return Type.LONG;
266 } else if (clazz == String.class) {
267 return Type.STRING;
268 }
269 return Type.OBJECT;
270 }
272 private static boolean canCoerce(final Object arg, final Type type) {
273 Type argType = runtimeType(arg);
274 if (Type.widest(argType, type) == type || arg == ScriptRuntime.UNDEFINED) {
275 return true;
276 }
277 System.err.println(arg + " does not fit in "+ argType + " " + type + " " + arg.getClass());
278 new Throwable().printStackTrace();
279 return false;
280 }
282 @SuppressWarnings("unused")
283 private static boolean paramTypeGuard(final Type[] paramTypes, final Object... args) {
284 final int length = args.length;
285 assert args.length >= paramTypes.length;
287 //i==start, skip the this, callee params etc
288 int start = args.length - paramTypes.length;
289 for (int i = start; i < args.length; i++) {
290 final Object arg = args[i];
291 if (!canCoerce(arg, paramTypes[i - start])) {
292 return false;
293 }
294 }
295 return true;
296 }
298 @SuppressWarnings("unused")
299 private static int ensureInt(final Object arg) {
300 if (arg instanceof Number) {
301 return ((Number)arg).intValue();
302 } else if (arg instanceof Undefined) {
303 return 0;
304 }
305 throw new AssertionError(arg);
306 }
308 /**
309 * Given the runtime callsite args, compute a method type that is equivalent to what
310 * was passed - this is typically a lot more specific that what the compiler has been
311 * able to deduce
312 * @param callSiteType callsite type for the compiled callsite target
313 * @param args runtime arguments to the compiled callsite target
314 * @return adjusted method type, narrowed as to conform to runtime callsite type instead
315 */
316 private static MethodType runtimeType(final MethodType callSiteType, final Object[] args) {
317 if (args == null) {
318 //for example bound, or otherwise runtime arguments to callsite unavailable, then
319 //do not change the type
320 return callSiteType;
321 }
322 final Class<?>[] paramTypes = new Class<?>[callSiteType.parameterCount()];
323 final int start = args.length - callSiteType.parameterCount();
324 for (int i = start; i < args.length; i++) {
325 paramTypes[i - start] = runtimeType(args[i]).getTypeClass();
326 }
327 return MH.type(callSiteType.returnType(), paramTypes);
328 }
330 private static ArrayList<Type> runtimeType(final MethodType mt) {
331 final ArrayList<Type> type = new ArrayList<>();
332 for (int i = 0; i < mt.parameterCount(); i++) {
333 type.add(Type.typeFor(mt.parameterType(i)));
334 }
335 return type;
336 }
338 @Override
339 synchronized MethodHandle getBestInvoker(final MethodType callSiteType, final Object[] args) {
340 final MethodType runtimeType = runtimeType(callSiteType, args);
341 assert runtimeType.parameterCount() == callSiteType.parameterCount();
343 final MethodHandle mh = super.getBestInvoker(runtimeType, args);
345 /*
346 * Not all functions can be specialized, for example, if we deemed memory
347 * footprint too large to store a parse snapshot, or if it is meaningless
348 * to do so, such as e.g. for runScript
349 */
350 if (functionNode == null || !functionNode.canSpecialize()) {
351 return mh;
352 }
354 /*
355 * Check if best invoker is equally specific or more specific than runtime
356 * type. In that case, we don't need further specialization, but can use
357 * whatever we have already. We know that it will match callSiteType, or it
358 * would not have been returned from getBestInvoker
359 */
360 if (!code.isLessSpecificThan(runtimeType)) {
361 return mh;
362 }
364 int i;
365 final FunctionNode snapshot = functionNode.getSnapshot();
366 assert snapshot != null;
368 /*
369 * Create a list of the arg types that the compiler knows about
370 * typically, the runtime args are a lot more specific, and we should aggressively
371 * try to use those whenever possible
372 * We WILL try to make an aggressive guess as possible, and add guards if needed.
373 * For example, if the compiler can deduce that we have a number type, but the runtime
374 * passes and int, we might still want to keep it an int, and the gamble to
375 * check that whatever is passed is int representable usually pays off
376 * If the compiler only knows that a parameter is an "Object", it is still worth
377 * it to try to specialize it by looking at the runtime arg.
378 */
379 final LinkedList<Type> compileTimeArgs = new LinkedList<>();
380 for (i = callSiteType.parameterCount() - 1; i >= 0 && compileTimeArgs.size() < snapshot.getParameters().size(); i--) {
381 compileTimeArgs.addFirst(Type.typeFor(callSiteType.parameterType(i)));
382 }
384 /*
385 * The classes known at compile time are a safe to generate as primitives without parameter guards
386 * But the classes known at runtime (if more specific than compile time types) are safe to generate as primitives
387 * IFF there are parameter guards
388 */
389 MethodHandle guard = null;
390 final ArrayList<Type> runtimeParamTypes = runtimeType(runtimeType);
391 while (runtimeParamTypes.size() > functionNode.getParameters().size()) {
392 runtimeParamTypes.remove(0);
393 }
394 for (i = 0; i < compileTimeArgs.size(); i++) {
395 final Type rparam = Type.typeFor(runtimeType.parameterType(i));
396 final Type cparam = compileTimeArgs.get(i);
398 if (cparam.isObject() && !rparam.isObject()) {
399 //check that the runtime object is still coercible to the runtime type, because compiler can't prove it's always primitive
400 if (guard == null) {
401 guard = MH.insertArguments(PARAM_TYPE_GUARD, 0, (Object)runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]));
402 }
403 }
404 }
406 Compiler.LOG.info("Callsite specialized ", name, " runtimeType=", runtimeType, " parameters=", snapshot.getParameters(), " args=", Arrays.asList(args));
408 assert snapshot != null;
409 assert snapshot != functionNode;
411 final Compiler compiler = new Compiler(installer);
413 final FunctionNode compiledSnapshot = compiler.compile(
414 snapshot.setHints(
415 null,
416 new Compiler.Hints(runtimeParamTypes.toArray(new Type[runtimeParamTypes.size()]))));
418 /*
419 * No matter how narrow your types were, they can never be narrower than Attr during recompile made them. I.e. you
420 * can put an int into the function here, if you see it as a runtime type, but if the function uses a multiplication
421 * on it, it will still need to be a double. At least until we have overflow checks. Similarly, if an int is
422 * passed but it is used as a string, it makes no sense to make the parameter narrower than Object. At least until
423 * the "different types for one symbol in difference places" work is done
424 */
425 compiler.install(compiledSnapshot);
427 return addCode(compiledSnapshot, runtimeType, guard, mh);
428 }
430 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
431 return MH.findStatic(MethodHandles.lookup(), RecompilableScriptFunctionData.class, name, MH.type(rtype, types));
432 }
434 }