Mon, 11 Feb 2013 21:26:06 +0530
8007915: Nashorn IR, codegen, parser packages and Context instance should be inaccessible to user code
Reviewed-by: lagergren, jlaskey, attila
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.codegen;
28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
29 import static jdk.nashorn.internal.codegen.types.Type.BOOLEAN;
30 import static jdk.nashorn.internal.codegen.types.Type.INT;
31 import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
33 import java.lang.invoke.CallSite;
34 import java.lang.invoke.MethodHandle;
35 import java.lang.invoke.MethodHandles;
36 import java.lang.invoke.MethodType;
37 import java.lang.invoke.MutableCallSite;
38 import java.util.HashMap;
39 import java.util.Map;
40 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
41 import jdk.nashorn.internal.codegen.types.Type;
42 import jdk.nashorn.internal.ir.RuntimeNode;
43 import jdk.nashorn.internal.ir.RuntimeNode.Request;
44 import jdk.nashorn.internal.runtime.ScriptRuntime;
45 import jdk.nashorn.internal.runtime.linker.Bootstrap;
46 import jdk.nashorn.internal.runtime.linker.Lookup;
47 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
49 /**
50 * Optimistic call site that assumes its Object arguments to be of a boxed type.
51 * Gradually reverts to wider boxed types if the assumption for the RuntimeNode
52 * is proven wrong. Finally reverts to the generic ScriptRuntime method.
53 *
54 * This is used from the CodeGenerator when we have a runtime node, but 1 or more
55 * primitive arguments. This class generated appropriate specializations, for example
56 * {@code Object a === int b} is a good idea to specialize to {@code ((Integer)a).intValue() == b}
57 * surrounded by catch blocks that will try less narrow specializations
58 */
59 public final class RuntimeCallSite extends MutableCallSite {
60 static final Call BOOTSTRAP = staticCallNoLookup(Bootstrap.class, "runtimeBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
62 private static final MethodHandle NEXT = findOwnMH("next", MethodHandle.class);
64 private final RuntimeNode.Request request;
66 private String name;
68 /**
69 * A specialized runtime node, i.e. on where we know at least one more specific type than object
70 */
71 static final class SpecializedRuntimeNode {
72 private static final char REQUEST_SEPARATOR = ':';
74 private final RuntimeNode.Request request;
76 private final Type[] parameterTypes;
78 private final Type returnType;
80 /**
81 * Constructor.
82 *
83 * @param request runtime node request to specialize
84 * @param parameterTypes parameter types of the call site
85 * @param returnType return type of the call site
86 */
87 SpecializedRuntimeNode(final RuntimeNode.Request request, final Type[] parameterTypes, final Type returnType) {
88 this.request = request;
89 this.parameterTypes = parameterTypes;
90 this.returnType = returnType;
91 }
93 /**
94 * The first type to try to use for this genrated runtime node
95 *
96 * @return a type
97 */
98 public Type firstTypeGuess() {
99 Type widest = Type.UNKNOWN;
100 for (final Type type : parameterTypes) {
101 if (type.isObject()) {
102 continue;
103 }
104 widest = Type.widest(type, widest);
105 }
106 widest = Type.widest(widest, firstTypeGuessForObject(request));
108 return widest;
109 }
111 private static Type firstTypeGuessForObject(final Request request) {
112 switch (request) {
113 case ADD:
114 return INT;
115 default:
116 return BOOLEAN;
117 }
118 }
120 Request getRequest() {
121 return request;
122 }
124 Type[] getParameterTypes() {
125 return parameterTypes;
126 }
128 Type getReturnType() {
129 return returnType;
130 }
132 private static char descFor(final Type type) {
133 if (type.isObject()) {
134 return 'O';
135 }
136 return type.getDescriptor().charAt(0);
137 }
139 @Override
140 public boolean equals(final Object other) {
141 if (other instanceof SpecializedRuntimeNode) {
142 final SpecializedRuntimeNode otherNode = (SpecializedRuntimeNode)other;
144 if (!otherNode.getReturnType().equals(getReturnType())) {
145 return false;
146 }
148 if (getParameterTypes().length != otherNode.getParameterTypes().length) {
149 return false;
150 }
152 for (int i = 0; i < getParameterTypes().length; i++) {
153 if (!Type.areEquivalent(getParameterTypes()[i], otherNode.getParameterTypes()[i])) {
154 return false;
155 }
156 }
158 return otherNode.getRequest().equals(getRequest());
159 }
161 return false;
162 }
164 @Override
165 public int hashCode() {
166 int hashCode = getRequest().toString().hashCode();
167 hashCode ^= getReturnType().hashCode();
168 for (final Type type : getParameterTypes()) {
169 hashCode ^= type.hashCode();
170 }
171 return hashCode;
172 }
174 @Override
175 public String toString() {
176 final StringBuilder sb = new StringBuilder();
177 sb.append(getRequest().toString());
178 sb.append(REQUEST_SEPARATOR);
179 sb.append(descFor(getReturnType()));
181 for (final Type type : getParameterTypes()) {
182 sb.append(descFor(type));
183 }
185 return sb.toString();
186 }
188 String getName(final Type extraType) {
189 return toString() + "_" + descFor(extraType);
190 }
192 String getInitialName() {
193 return getName(firstTypeGuess());
194 }
195 }
198 /**
199 * Constructor
200 *
201 * @param type method type for call site
202 * @param name name of runtime call
203 */
204 public RuntimeCallSite(final MethodType type, final String name) {
205 super(type);
206 this.name = name;
207 this.request = Request.valueOf(name.substring(0, name.indexOf(SpecializedRuntimeNode.REQUEST_SEPARATOR)));
208 setTarget(makeMethod(name));
209 }
211 private String nextName(final String requestName) {
212 if (requestName.equals(request.toString())) {
213 return null;
214 }
216 final char[] c = requestName.toCharArray();
217 final int last = c.length - 1;
219 if (c[last - 1] != '_') {
220 return null;
221 }
223 switch (c[last]) {
224 case 'Z':
225 c[last] = 'I';
226 break;
227 case 'I':
228 c[last] = 'J';
229 break;
230 case 'J':
231 c[last] = 'D';
232 break;
233 case 'D':
234 default:
235 return request.toString();
236 }
238 return new String(c);
239 }
241 private boolean isSpecialized(final String requestName) {
242 return nextName(requestName) != null;
243 }
245 private MethodHandle makeMethod(final String requestName) {
246 MethodHandle mh;
248 if (isSpecialized(requestName)) {
249 final Class<?> boxedType;
250 final Class<?> primitiveType;
252 switch (requestName.charAt(requestName.length() - 1)) {
253 case 'Z':
254 boxedType = Boolean.class;
255 primitiveType = int.class;
256 break;
257 case 'I':
258 boxedType = Integer.class;
259 primitiveType = int.class;
260 break;
261 case 'J':
262 boxedType = Long.class;
263 primitiveType = long.class;
264 break;
265 case 'D':
266 boxedType = Number.class;
267 primitiveType = double.class;
268 break;
269 default:
270 throw new RuntimeException("should not reach here");
271 }
273 final boolean isStrictCmp = (request == Request.EQ_STRICT || request == Request.NE_STRICT);
275 if (isStrictCmp &&
276 (boxedType != Boolean.class &&
277 (type().parameterType(0) == boolean.class ||
278 type().parameterType(1) == boolean.class))) {
279 // number and boolean are never strictly equal, e.g. 0 !== false
280 mh = MH.dropArguments(MH.constant(boolean.class, request == Request.NE_STRICT), 0, type().parameterArray());
281 } else {
282 mh = METHODS.get(request.name().replace("_STRICT", "") + primitiveType.getSimpleName());
283 // unbox objects
285 for (int i = 0; i < type().parameterCount(); i++) {
286 if (!type().parameterType(i).isPrimitive()) {
287 mh = MH.filterArguments(mh, i, UNBOX.get(boxedType));
288 }
289 }
291 mh = Lookup.filterReturnType(mh, type().returnType());
292 mh = MH.explicitCastArguments(mh, type());
293 }
295 final MethodHandle fallback = MH.foldArguments(MethodHandles.exactInvoker(type()), MH.bindTo(NEXT, this));
297 MethodHandle guard;
298 if (type().parameterType(0).isPrimitive()) {
299 guard = MH.insertArguments(
300 MH.dropArguments(CHECKCAST, 1, type().parameterType(0)), 0, boxedType);
301 } else if (type().parameterType(1).isPrimitive()) {
302 guard = MH.insertArguments(
303 MH.dropArguments(CHECKCAST, 2, type().parameterType(1)), 0, boxedType);
304 } else {
305 assert !type().parameterType(0).isPrimitive() && !type().parameterType(1).isPrimitive();
306 guard = MH.insertArguments(CHECKCAST2, 0, boxedType);
307 }
309 if (request == Request.ADD && boxedType == Integer.class) {
310 // int add needs additional overflow check
311 MethodHandle addcheck = ADDCHECK;
312 for (int i = 0; i < type().parameterCount(); i++) {
313 if (!type().parameterType(i).isPrimitive()) {
314 addcheck = MH.filterArguments(addcheck, i, UNBOX.get(boxedType));
315 }
316 }
317 addcheck = MH.explicitCastArguments(addcheck, type().changeReturnType(boolean.class));
318 guard = MH.guardWithTest(upcastGuard(guard), addcheck,
319 MH.dropArguments(MH.constant(boolean.class, false), 0, type().parameterArray()));
320 }
322 return MH.guardWithTest(upcastGuard(guard), mh, fallback);
323 }
325 // generic fallback
326 return MH.explicitCastArguments(Lookup.filterReturnType(GENERIC_METHODS.get(request.name()), type().returnType()), type());
327 }
329 private MethodHandle upcastGuard(final MethodHandle guard) {
330 return MH.asType(guard, type().changeReturnType(boolean.class));
331 }
333 /**
334 * This is public just so that the generated specialization code can
335 * use it to get the next wider typed method
336 *
337 * Do not call directly
338 *
339 * @return next wider specialization method for this RuntimeCallSite
340 */
341 public MethodHandle next() {
342 this.name = nextName(name);
343 final MethodHandle next = makeMethod(name);
344 setTarget(next);
345 return next;
346 }
348 @Override
349 public String toString() {
350 return super.toString() + " " + name;
351 }
353 /** Method cache */
354 private static final Map<String, MethodHandle> METHODS;
356 /** Generic method cache */
357 private static final Map<String, MethodHandle> GENERIC_METHODS;
359 /** Unbox cache */
360 private static final Map<Class<?>, MethodHandle> UNBOX;
362 private static final MethodHandle CHECKCAST = findOwnMH("checkcast", boolean.class, Class.class, Object.class);
363 private static final MethodHandle CHECKCAST2 = findOwnMH("checkcast", boolean.class, Class.class, Object.class, Object.class);
364 private static final MethodHandle ADDCHECK = findOwnMH("ADDcheck", boolean.class, int.class, int.class);
366 /**
367 * Build maps of correct boxing operations
368 */
369 static {
370 UNBOX = new HashMap<>();
371 UNBOX.put(Boolean.class, findOwnMH("unboxZ", int.class, Object.class));
372 UNBOX.put(Integer.class, findOwnMH("unboxI", int.class, Object.class));
373 UNBOX.put(Long.class, findOwnMH("unboxJ", long.class, Object.class));
374 UNBOX.put(Number.class, findOwnMH("unboxD", double.class, Object.class));
376 METHODS = new HashMap<>();
378 for (final Request req : Request.values()) {
379 if (req.canSpecialize()) {
380 if (req.name().endsWith("_STRICT")) {
381 continue;
382 }
384 final boolean isCmp = Request.isComparison(req);
386 METHODS.put(req.name() + "int", findOwnMH(req.name(), (isCmp ? boolean.class : int.class), int.class, int.class));
387 METHODS.put(req.name() + "long", findOwnMH(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
388 METHODS.put(req.name() + "double", findOwnMH(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
389 }
390 }
392 GENERIC_METHODS = new HashMap<>();
393 for (final Request req : Request.values()) {
394 if (req.canSpecialize()) {
395 GENERIC_METHODS.put(req.name(), MH.findStatic(MethodHandles.lookup(), ScriptRuntime.class, req.name(),
396 MH.type(req.getReturnType().getTypeClass(), Object.class, Object.class)));
397 }
398 }
399 }
401 /**
402 * Specialized version of != operator for two int arguments. Do not call directly.
403 * @param a int
404 * @param b int
405 * @return a != b
406 */
407 public static boolean NE(final int a, final int b) {
408 return a != b;
409 }
411 /**
412 * Specialized version of != operator for two double arguments. Do not call directly.
413 * @param a double
414 * @param b double
415 * @return a != b
416 */
417 public static boolean NE(final double a, final double b) {
418 return a != b;
419 }
421 /**
422 * Specialized version of != operator for two long arguments. Do not call directly.
423 * @param a long
424 * @param b long
425 * @return a != b
426 */
427 public static boolean NE(final long a, final long b) {
428 return a != b;
429 }
431 /**
432 * Specialized version of == operator for two int arguments. Do not call directly.
433 * @param a int
434 * @param b int
435 * @return a == b
436 */
437 public static boolean EQ(final int a, final int b) {
438 return a == b;
439 }
441 /**
442 * Specialized version of == operator for two double arguments. Do not call directly.
443 * @param a double
444 * @param b double
445 * @return a == b
446 */
447 public static boolean EQ(final double a, final double b) {
448 return a == b;
449 }
451 /**
452 * Specialized version of == operator for two long arguments. Do not call directly.
453 * @param a long
454 * @param b long
455 * @return a == b
456 */
457 public static boolean EQ(final long a, final long b) {
458 return a == b;
459 }
461 /**
462 * Specialized version of < operator for two int arguments. Do not call directly.
463 * @param a int
464 * @param b int
465 * @return a < b
466 */
467 public static boolean LT(final int a, final int b) {
468 return a < b;
469 }
471 /**
472 * Specialized version of < operator for two double arguments. Do not call directly.
473 * @param a double
474 * @param b double
475 * @return a < b
476 */
477 public static boolean LT(final double a, final double b) {
478 return a < b;
479 }
481 /**
482 * Specialized version of < operator for two long arguments. Do not call directly.
483 * @param a long
484 * @param b long
485 * @return a < b
486 */
487 public static boolean LT(final long a, final long b) {
488 return a < b;
489 }
491 /**
492 * Specialized version of <= operator for two int arguments. Do not call directly.
493 * @param a int
494 * @param b int
495 * @return a <= b
496 */
497 public static boolean LE(final int a, final int b) {
498 return a <= b;
499 }
501 /**
502 * Specialized version of <= operator for two double arguments. Do not call directly.
503 * @param a double
504 * @param b double
505 * @return a <= b
506 */
507 public static boolean LE(final double a, final double b) {
508 return a <= b;
509 }
511 /**
512 * Specialized version of <= operator for two long arguments. Do not call directly.
513 * @param a long
514 * @param b long
515 * @return a <= b
516 */
517 public static boolean LE(final long a, final long b) {
518 return a <= b;
519 }
521 /**
522 * Specialized version of > operator for two int arguments. Do not call directly.
523 * @param a int
524 * @param b int
525 * @return a > b
526 */
527 public static boolean GT(final int a, final int b) {
528 return a > b;
529 }
531 /**
532 * Specialized version of > operator for two double arguments. Do not call directly.
533 * @param a double
534 * @param b double
535 * @return a > b
536 */
537 public static boolean GT(final double a, final double b) {
538 return a > b;
539 }
541 /**
542 * Specialized version of > operator for two long arguments. Do not call directly.
543 * @param a long
544 * @param b long
545 * @return a > b
546 */
547 public static boolean GT(final long a, final long b) {
548 return a > b;
549 }
551 /**
552 * Specialized version of >= operator for two int arguments. Do not call directly.
553 * @param a int
554 * @param b int
555 * @return a >= b
556 */
557 public static boolean GE(final int a, final int b) {
558 return a >= b;
559 }
561 /**
562 * Specialized version of >= operator for two double arguments. Do not call directly.
563 * @param a double
564 * @param b double
565 * @return a >= b
566 */
567 public static boolean GE(final double a, final double b) {
568 return a >= b;
569 }
571 /**
572 * Specialized version of >= operator for two long arguments. Do not call directly.
573 * @param a long
574 * @param b long
575 * @return a >= b
576 */
577 public static boolean GE(final long a, final long b) {
578 return a >= b;
579 }
581 /**
582 * Specialized version of + operator for two int arguments. Do not call directly.
583 * @param a int
584 * @param b int
585 * @return a + b
586 */
587 public static int ADD(final int a, final int b) {
588 return a + b;
589 }
591 /**
592 * Specialized version of + operator for two long arguments. Do not call directly.
593 * @param a long
594 * @param b long
595 * @return a + b
596 */
597 public static long ADD(final long a, final long b) {
598 return a + b;
599 }
601 /**
602 * Specialized version of + operator for two double arguments. Do not call directly.
603 * @param a double
604 * @param b double
605 * @return a + b
606 */
607 public static double ADD(final double a, final double b) {
608 return a + b;
609 }
611 /**
612 * Check that ints are addition compatible, i.e. their sum is equal to the sum
613 * of them cast to long. Otherwise the addition will overflow. Do not call directly.
614 *
615 * @param a int
616 * @param b int
617 *
618 * @return true if addition does not overflow
619 */
620 public static boolean ADDcheck(final int a, final int b) {
621 return (a + b == (long)a + (long)b);
622 }
624 /**
625 * Checkcast used for specialized ops. Do not call directly
626 *
627 * @param type to to check against
628 * @param obj object to check for type
629 *
630 * @return true if type check holds
631 */
632 public static boolean checkcast(final Class<?> type, final Object obj) {
633 return type.isInstance(obj);
634 }
636 /**
637 * Checkcast used for specialized ops. Do not call directly
638 *
639 * @param type type to check against
640 * @param objA first object to check against type
641 * @param objB second object to check against type
642 *
643 * @return true if type check holds for both objects
644 */
645 public static boolean checkcast(final Class<?> type, final Object objA, final Object objB) {
646 return type.isInstance(objA) && type.isInstance(objB);
647 }
649 /**
650 * Unbox a java.lang.Boolean. Do not call directly
651 * @param obj object to cast to int and unbox
652 * @return an int value for the boolean, 1 is true, 0 is false
653 */
654 public static int unboxZ(final Object obj) {
655 return (boolean)obj ? 1 : 0;
656 }
658 /**
659 * Unbox a java.lang.Integer. Do not call directly
660 * @param obj object to cast to int and unbox
661 * @return an int
662 */
663 public static int unboxI(final Object obj) {
664 return (int)obj;
665 }
667 /**
668 * Unbox a java.lang.Long. Do not call directly
669 * @param obj object to cast to long and unbox
670 * @return a long
671 */
672 public static long unboxJ(final Object obj) {
673 return (long)obj;
674 }
676 /**
677 * Unbox a java.lang.Number. Do not call directly
678 * @param obj object to cast to Number and unbox
679 * @return a double
680 */
681 public static double unboxD(final Object obj) {
682 return ((Number)obj).doubleValue();
683 }
685 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
686 try {
687 return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
688 } catch (final MethodHandleFactory.LookupException e) {
689 return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
690 }
691 }
693 }