src/jdk/nashorn/internal/runtime/linker/NashornCallSiteDescriptor.java

Mon, 22 Sep 2014 13:28:28 +0200

author
hannesw
date
Mon, 22 Sep 2014 13:28:28 +0200
changeset 1020
9ee8fd4a7266
parent 991
b7a2db4de254
child 1205
4112748288bb
child 1512
152cfeee5001
permissions
-rw-r--r--

8047764: Indexed or polymorphic set on global affects Object.prototype
Reviewed-by: lagergren, 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.runtime.linker;
    28 import java.lang.invoke.MethodHandles;
    29 import java.lang.invoke.MethodHandles.Lookup;
    30 import java.lang.invoke.MethodType;
    31 import java.util.concurrent.ConcurrentHashMap;
    32 import java.util.concurrent.ConcurrentMap;
    33 import jdk.internal.dynalink.CallSiteDescriptor;
    34 import jdk.internal.dynalink.support.AbstractCallSiteDescriptor;
    35 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    36 import jdk.nashorn.internal.ir.debug.NashornTextifier;
    38 /**
    39  * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that
    40  * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also
    41  * we're storing flags in an additional primitive field.
    42  */
    43 public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
    44     /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
    45      * property access expression. */
    46     public static final int CALLSITE_SCOPE         = 1 << 0;
    47     /** Flags that the call site is in code that uses ECMAScript strict mode. */
    48     public static final int CALLSITE_STRICT        = 1 << 1;
    49     /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
    50      * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
    51     public static final int CALLSITE_FAST_SCOPE    = 1 << 2;
    52     /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
    53      * descriptor, and in that case we have to throw an UnwarrantedOptimismException */
    54     public static final int CALLSITE_OPTIMISTIC    = 1 << 3;
    55     /** Is this really an apply that we try to call as a call? */
    56     public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
    57     /** Does this a callsite for a variable declaration? */
    58     public static final int CALLSITE_DECLARE       = 1 << 5;
    60     /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
    61      * code where call sites have this flag set. */
    62     public static final int CALLSITE_PROFILE         = 1 << 6;
    63     /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
    64      * call sites have this flag set. */
    65     public static final int CALLSITE_TRACE           = 1 << 7;
    66     /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
    67      * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
    68     public static final int CALLSITE_TRACE_MISSES    = 1 << 8;
    69     /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
    70      * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
    71     public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9;
    72     /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
    73      * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
    74      * have this flag set. */
    75     public static final int CALLSITE_TRACE_VALUES    = 1 << 10;
    77     //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
    78     //right now given the program points
    80     /**
    81      * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
    82      * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program
    83      * points we can have.
    84      * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
    85      * trace/profile settings.
    86      */
    87     public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11;
    89     /**
    90      * Maximum program point value. 21 bits should be enough for anyone
    91      */
    92     public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
    94     /**
    95      * Flag mask to get the program point flags
    96      */
    97     public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1;
    99     private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
   100             new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
   101         @Override
   102         protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) {
   103             return new ConcurrentHashMap<>();
   104         }
   105     };
   107     private final MethodHandles.Lookup lookup;
   108     private final String operator;
   109     private final String operand;
   110     private final MethodType methodType;
   111     private final int flags;
   113     /**
   114      * Function used by {@link NashornTextifier} to represent call site flags in
   115      * human readable form
   116      * @param flags call site flags
   117      * @return human readable form of this callsite descriptor
   118      */
   119     public static String toString(final int flags) {
   120         final StringBuilder sb = new StringBuilder();
   121         if ((flags & CALLSITE_SCOPE) != 0) {
   122             if ((flags & CALLSITE_FAST_SCOPE) != 0) {
   123                 sb.append("fastscope ");
   124             } else {
   125                 assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
   126                 sb.append("scope ");
   127             }
   128             if ((flags & CALLSITE_DECLARE) != 0) {
   129                 sb.append("declare ");
   130             }
   131         }
   132         if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
   133             sb.append("apply2call ");
   134         }
   135         if ((flags & CALLSITE_STRICT) != 0) {
   136             sb.append("strict ");
   137         }
   138         return sb.length() == 0 ? "" : " " + sb.toString().trim();
   139     }
   141     /**
   142      * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable
   143      * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so).
   144      * @param lookup the lookup describing the script
   145      * @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}.
   146      * @param methodType the method type at the call site
   147      * @param flags Nashorn-specific call site flags
   148      * @return a call site descriptor with the specified values.
   149      */
   150     public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name,
   151             final MethodType methodType, final int flags) {
   152         final String[] tokenizedName = CallSiteDescriptorFactory.tokenizeName(name);
   153         assert tokenizedName.length == 2 || tokenizedName.length == 3;
   154         assert "dyn".equals(tokenizedName[0]);
   155         assert tokenizedName[1] != null;
   156         // TODO: see if we can move mangling/unmangling into Dynalink
   157         return get(lookup, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null,
   158                 methodType, flags);
   159     }
   161     private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String operator, final String operand, final MethodType methodType, final int flags) {
   162         final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operator, operand, methodType, flags);
   163         // Many of these call site descriptors are identical (e.g. every getter for a property color will be
   164         // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them.
   165         final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass());
   166         final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd);
   167         return canonical != null ? canonical : csd;
   168     }
   170     private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String operator, final String operand,
   171             final MethodType methodType, final int flags) {
   172         this.lookup = lookup;
   173         this.operator = operator;
   174         this.operand = operand;
   175         this.methodType = methodType;
   176         this.flags = flags;
   177     }
   179     @Override
   180     public int getNameTokenCount() {
   181         return operand == null ? 2 : 3;
   182     }
   184     @Override
   185     public String getNameToken(final int i) {
   186         switch(i) {
   187         case 0: return "dyn";
   188         case 1: return operator;
   189         case 2:
   190             if(operand != null) {
   191                 return operand;
   192             }
   193             break;
   194         default:
   195             break;
   196         }
   197         throw new IndexOutOfBoundsException(String.valueOf(i));
   198     }
   200     @Override
   201     public Lookup getLookup() {
   202         return lookup;
   203     }
   205     @Override
   206     public boolean equals(final CallSiteDescriptor csd) {
   207         return super.equals(csd) && flags == getFlags(csd);
   208     }
   210     @Override
   211     public MethodType getMethodType() {
   212         return methodType;
   213     }
   215     /**
   216      * Returns the operator (e.g. {@code "getProp"}) in this call site descriptor's name. Equivalent to
   217      * {@code getNameToken(CallSiteDescriptor.OPERATOR)}. The returned operator can be composite.
   218      * @return the operator in this call site descriptor's name.
   219      */
   220     public String getOperator() {
   221         return operator;
   222     }
   224     /**
   225      * Returns the first operator in this call site descriptor's name. E.g. if this call site descriptor has a composite
   226      * operation {@code "getProp|getMethod|getElem"}, it will return {@code "getProp"}. Nashorn - being a ECMAScript
   227      * engine - does not distinguish between property, element, and method namespace; ECMAScript objects just have one
   228      * single property namespace for all these, therefore it is largely irrelevant what the composite operation is
   229      * structured like; if the first operation can't be satisfied, neither can the others. The first operation is
   230      * however sometimes used to slightly alter the semantics; for example, a distinction between {@code "getProp"} and
   231      * {@code "getMethod"} being the first operation can translate into whether {@code "__noSuchProperty__"} or
   232      * {@code "__noSuchMethod__"} will be executed in case the property is not found.
   233      * @return the first operator in this call site descriptor's name.
   234      */
   235     public String getFirstOperator() {
   236         final int delim = operator.indexOf(CallSiteDescriptor.OPERATOR_DELIMITER);
   237         return delim == -1 ? operator : operator.substring(0, delim);
   238     }
   240     /**
   241      * Returns the named operand in this descriptor's name. Equivalent to
   242      * {@code getNameToken(CallSiteDescriptor.NAME_OPERAND)}. E.g. for operation {@code "dyn:getProp:color"}, returns
   243      * {@code "color"}. For call sites without named operands (e.g. {@code "dyn:new"}) returns null.
   244      * @return the named operand in this descriptor's name.
   245      */
   246     public String getOperand() {
   247         return operand;
   248     }
   250     /**
   251      * Returns the Nashorn-specific flags for this call site descriptor.
   252      * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
   253      * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
   254      * generated outside of Nashorn.
   255      * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site
   256      * descriptor.
   257      */
   258     public static int getFlags(final CallSiteDescriptor desc) {
   259         return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0;
   260     }
   262     /**
   263      * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
   264      * @param flag the tested flag
   265      * @return true if the flag is set, false otherwise
   266      */
   267     private boolean isFlag(final int flag) {
   268         return (flags & flag) != 0;
   269     }
   271     /**
   272      * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
   273      * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
   274      * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
   275      * generated outside of Nashorn.
   276      * @param flag the tested flag
   277      * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site
   278      * descriptor).
   279      */
   280     private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
   281         return (getFlags(desc) & flag) != 0;
   282     }
   284     /**
   285      * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_SCOPE} flag set.
   286      * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
   287      * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
   288      * generated outside of Nashorn.
   289      * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
   290      */
   291     public static boolean isScope(final CallSiteDescriptor desc) {
   292         return isFlag(desc, CALLSITE_SCOPE);
   293     }
   295     /**
   296      * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_FAST_SCOPE} flag set.
   297      * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
   298      * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
   299      * generated outside of Nashorn.
   300      * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
   301      */
   302     public static boolean isFastScope(final CallSiteDescriptor desc) {
   303         return isFlag(desc, CALLSITE_FAST_SCOPE);
   304     }
   306     /**
   307      * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_STRICT} flag set.
   308      * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
   309      * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
   310      * generated outside of Nashorn.
   311      * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
   312      */
   313     public static boolean isStrict(final CallSiteDescriptor desc) {
   314         return isFlag(desc, CALLSITE_STRICT);
   315     }
   317     /**
   318      * Returns true if this is an apply call that we try to call as
   319      * a "call"
   320      * @param desc descriptor
   321      * @return true if apply to call
   322      */
   323     public static boolean isApplyToCall(final CallSiteDescriptor desc) {
   324         return isFlag(desc, CALLSITE_APPLY_TO_CALL);
   325     }
   327     /**
   328      * Is this an optimistic call site
   329      * @param desc descriptor
   330      * @return true if optimistic
   331      */
   332     public static boolean isOptimistic(final CallSiteDescriptor desc) {
   333         return isFlag(desc, CALLSITE_OPTIMISTIC);
   334     }
   336     /**
   337      * Does this callsite contain a declaration for its target?
   338      * @param desc descriptor
   339      * @return true if contains declaration
   340      */
   341     public static boolean isDeclaration(final CallSiteDescriptor desc) {
   342         return isFlag(desc, CALLSITE_DECLARE);
   343     }
   345     /**
   346      * Returns true if {@code flags} has the {@link  #CALLSITE_STRICT} bit set.
   347      * @param flags the flags
   348      * @return true if the flag is set, false otherwise.
   349      */
   350     public static boolean isStrictFlag(final int flags) {
   351         return (flags & CALLSITE_STRICT) != 0;
   352     }
   354     /**
   355      * Returns true if {@code flags} has the {@link  #CALLSITE_SCOPE} bit set.
   356      * @param flags the flags
   357      * @return true if the flag is set, false otherwise.
   358      */
   359     public static boolean isScopeFlag(final int flags) {
   360         return (flags & CALLSITE_SCOPE) != 0;
   361     }
   363     /**
   364      * Get a program point from a descriptor (must be optimistic)
   365      * @param desc descriptor
   366      * @return program point
   367      */
   368     public static int getProgramPoint(final CallSiteDescriptor desc) {
   369         assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc;
   370         return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT;
   371     }
   373     boolean isProfile() {
   374         return isFlag(CALLSITE_PROFILE);
   375     }
   377     boolean isTrace() {
   378         return isFlag(CALLSITE_TRACE);
   379     }
   381     boolean isTraceMisses() {
   382         return isFlag(CALLSITE_TRACE_MISSES);
   383     }
   385     boolean isTraceEnterExit() {
   386         return isFlag(CALLSITE_TRACE_ENTEREXIT);
   387     }
   389     boolean isTraceObjects() {
   390         return isFlag(CALLSITE_TRACE_VALUES);
   391     }
   393     boolean isOptimistic() {
   394         return isFlag(CALLSITE_OPTIMISTIC);
   395     }
   397     @Override
   398     public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
   399         return get(getLookup(), operator, operand, newMethodType, flags);
   400     }
   402 }

mercurial