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