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