src/jdk/nashorn/internal/codegen/RuntimeCallSite.java

Mon, 11 Feb 2013 21:26:06 +0530

author
sundar
date
Mon, 11 Feb 2013 21:26:06 +0530
changeset 82
abea4ba28901
parent 66
bee7c8a45a04
child 100
3245e174fe3a
permissions
-rw-r--r--

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 }

mercurial