NashornCallSiteDescriptor.java revision 953:221a84ef44c0
11573Srgrimes/*
21573Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
31573Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41573Srgrimes *
51573Srgrimes * This code is free software; you can redistribute it and/or modify it
61573Srgrimes * under the terms of the GNU General Public License version 2 only, as
71573Srgrimes * published by the Free Software Foundation.  Oracle designates this
81573Srgrimes * particular file as subject to the "Classpath" exception as provided
91573Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101573Srgrimes *
111573Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121573Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131573Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141573Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151573Srgrimes * accompanied this code).
161573Srgrimes *
171573Srgrimes * You should have received a copy of the GNU General Public License version
181573Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191573Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201573Srgrimes *
211573Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221573Srgrimes * or visit www.oracle.com if you need additional information or have any
231573Srgrimes * questions.
241573Srgrimes */
251573Srgrimes
261573Srgrimespackage jdk.nashorn.internal.runtime.linker;
271573Srgrimes
281573Srgrimesimport java.lang.invoke.MethodHandles;
291573Srgrimesimport java.lang.invoke.MethodHandles.Lookup;
301573Srgrimesimport java.lang.invoke.MethodType;
311573Srgrimesimport java.util.concurrent.ConcurrentHashMap;
321573Srgrimesimport java.util.concurrent.ConcurrentMap;
3392889Sobrienimport jdk.internal.dynalink.CallSiteDescriptor;
3492889Sobrienimport jdk.internal.dynalink.support.AbstractCallSiteDescriptor;
351573Srgrimesimport jdk.internal.dynalink.support.CallSiteDescriptorFactory;
36211276Sumeimport jdk.nashorn.internal.ir.debug.NashornTextifier;
371573Srgrimes
38158115Sume/**
39145279Sume * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that
40158115Sume * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also
41158115Sume * we're storing flags in an additional primitive field.
42158115Sume */
43158115Sumepublic final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
441573Srgrimes    /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
45158115Sume     * property access expression. */
46158115Sume    public static final int CALLSITE_SCOPE         = 1 << 0;
47158115Sume    /** Flags that the call site is in code that uses ECMAScript strict mode. */
48158115Sume    public static final int CALLSITE_STRICT        = 1 << 1;
49158115Sume    /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
50158115Sume     * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
51158115Sume    public static final int CALLSITE_FAST_SCOPE    = 1 << 2;
52158115Sume    /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
53158115Sume     * descriptor, and in that case we have to throw an UnwarrantedOptimismException */
54158115Sume    public static final int CALLSITE_OPTIMISTIC    = 1 << 3;
55158115Sume    /** Is this really an apply that we try to call as a call? */
56158115Sume    public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
57158115Sume
58145279Sume    /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
59157779Sume     * code where call sites have this flag set. */
60157779Sume    public static final int CALLSITE_PROFILE        = 1 << 5;
61145279Sume    /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
621573Srgrimes     * call sites have this flag set. */
63158115Sume    public static final int CALLSITE_TRACE          = 1 << 6;
64158115Sume    /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
65158115Sume     * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
66158115Sume    public static final int CALLSITE_TRACE_MISSES   = 1 << 7;
67158115Sume    /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
68158115Sume     * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
69158115Sume    public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 8;
70158115Sume    /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
71158115Sume     * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
72158115Sume     * have this flag set. */
73158115Sume    public static final int CALLSITE_TRACE_VALUES   = 1 << 9;
74158115Sume
75158115Sume    //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
76211276Sume    //right now given the program points
77158115Sume
78158115Sume    /**
79158115Sume     * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
80157779Sume     * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program
81157779Sume     * points we can have.
82158115Sume     * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
83145279Sume     * trace/profile settings.
84145279Sume     */
85157779Sume    public static final int CALLSITE_PROGRAM_POINT_SHIFT = 10;
86158115Sume
87211276Sume    /**
88158115Sume     * Maximum program point value. 22 bits should be enough for anyone
89158115Sume     */
90158115Sume    public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
91211276Sume
92211276Sume    /**
93158115Sume     * Flag mask to get the program point flags
94158115Sume     */
95158115Sume    public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1;
96158115Sume
97145279Sume    private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
98145279Sume            new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
99158115Sume        @Override
100158115Sume        protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) {
101158115Sume            return new ConcurrentHashMap<>();
102158115Sume        }
103158115Sume    };
104158115Sume
105158115Sume    private final MethodHandles.Lookup lookup;
106158115Sume    private final String operator;
107158115Sume    private final String operand;
108158115Sume    private final MethodType methodType;
109158115Sume    private final int flags;
110158115Sume
111158115Sume    /**
112158115Sume     * Function used by {@link NashornTextifier} to represent call site flags in
113158115Sume     * human readable form
114158115Sume     * @param flags call site flags
115158115Sume     * @return human readable form of this callsite descriptor
116158115Sume     */
117158115Sume    public static String toString(final int flags) {
118158115Sume        final StringBuilder sb = new StringBuilder();
119158115Sume        if ((flags & CALLSITE_SCOPE) != 0) {
120158115Sume            if ((flags & CALLSITE_FAST_SCOPE) != 0) {
121158115Sume                sb.append("fastscope ");
122158115Sume            } else {
123158115Sume                assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
124211276Sume                sb.append("scope ");
125211276Sume            }
126213453Sume        }
127211276Sume        if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
128211276Sume            sb.append("apply2call ");
129158115Sume        }
130158115Sume        if ((flags & CALLSITE_STRICT) != 0) {
1311573Srgrimes            sb.append("strict ");
132145279Sume        }
1331573Srgrimes        return sb.length() == 0 ? "" : " " + sb.toString().trim();
134145279Sume    }
135157779Sume
1361573Srgrimes    /**
137145279Sume     * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable
138145279Sume     * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so).
139157779Sume     * @param lookup the lookup describing the script
140157779Sume     * @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}.
141145279Sume     * @param methodType the method type at the call site
142157779Sume     * @param flags Nashorn-specific call site flags
1431573Srgrimes     * @return a call site descriptor with the specified values.
144     */
145    public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name,
146            final MethodType methodType, final int flags) {
147        final String[] tokenizedName = CallSiteDescriptorFactory.tokenizeName(name);
148        assert tokenizedName.length == 2 || tokenizedName.length == 3;
149        assert "dyn".equals(tokenizedName[0]);
150        assert tokenizedName[1] != null;
151        // TODO: see if we can move mangling/unmangling into Dynalink
152        return get(lookup, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null,
153                methodType, flags);
154    }
155
156    private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String operator, final String operand, final MethodType methodType, final int flags) {
157        final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operator, operand, methodType, flags);
158        // Many of these call site descriptors are identical (e.g. every getter for a property color will be
159        // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them.
160        final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass());
161        final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd);
162        return canonical != null ? canonical : csd;
163    }
164
165    private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String operator, final String operand,
166            final MethodType methodType, final int flags) {
167        this.lookup = lookup;
168        this.operator = operator;
169        this.operand = operand;
170        this.methodType = methodType;
171        this.flags = flags;
172    }
173
174    @Override
175    public int getNameTokenCount() {
176        return operand == null ? 2 : 3;
177    }
178
179    @Override
180    public String getNameToken(final int i) {
181        switch(i) {
182        case 0: return "dyn";
183        case 1: return operator;
184        case 2:
185            if(operand != null) {
186                return operand;
187            }
188            break;
189        default:
190            break;
191        }
192        throw new IndexOutOfBoundsException(String.valueOf(i));
193    }
194
195    @Override
196    public Lookup getLookup() {
197        return lookup;
198    }
199
200    @Override
201    public boolean equals(final CallSiteDescriptor csd) {
202        return super.equals(csd) && flags == getFlags(csd);
203    }
204
205    @Override
206    public MethodType getMethodType() {
207        return methodType;
208    }
209
210    /**
211     * Returns the operator (e.g. {@code "getProp"}) in this call site descriptor's name. Equivalent to
212     * {@code getNameToken(CallSiteDescriptor.OPERATOR)}. The returned operator can be composite.
213     * @return the operator in this call site descriptor's name.
214     */
215    public String getOperator() {
216        return operator;
217    }
218
219    /**
220     * Returns the first operator in this call site descriptor's name. E.g. if this call site descriptor has a composite
221     * operation {@code "getProp|getMethod|getElem"}, it will return {@code "getProp"}. Nashorn - being a ECMAScript
222     * engine - does not distinguish between property, element, and method namespace; ECMAScript objects just have one
223     * single property namespace for all these, therefore it is largely irrelevant what the composite operation is
224     * structured like; if the first operation can't be satisfied, neither can the others. The first operation is
225     * however sometimes used to slightly alter the semantics; for example, a distinction between {@code "getProp"} and
226     * {@code "getMethod"} being the first operation can translate into whether {@code "__noSuchProperty__"} or
227     * {@code "__noSuchMethod__"} will be executed in case the property is not found.
228     * @return the first operator in this call site descriptor's name.
229     */
230    public String getFirstOperator() {
231        final int delim = operator.indexOf(CallSiteDescriptor.OPERATOR_DELIMITER);
232        return delim == -1 ? operator : operator.substring(0, delim);
233    }
234
235    /**
236     * Returns the named operand in this descriptor's name. Equivalent to
237     * {@code getNameToken(CallSiteDescriptor.NAME_OPERAND)}. E.g. for operation {@code "dyn:getProp:color"}, returns
238     * {@code "color"}. For call sites without named operands (e.g. {@code "dyn:new"}) returns null.
239     * @return the named operand in this descriptor's name.
240     */
241    public String getOperand() {
242        return operand;
243    }
244
245    /**
246     * Returns the Nashorn-specific flags for this call site descriptor.
247     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
248     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
249     * generated outside of Nashorn.
250     * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site
251     * descriptor.
252     */
253    private static int getFlags(final CallSiteDescriptor desc) {
254        return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0;
255    }
256
257    /**
258     * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
259     * @param flag the tested flag
260     * @return true if the flag is set, false otherwise
261     */
262    private boolean isFlag(final int flag) {
263        return (flags & flag) != 0;
264    }
265
266    /**
267     * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
268     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
269     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
270     * generated outside of Nashorn.
271     * @param flag the tested flag
272     * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site
273     * descriptor).
274     */
275    private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
276        return (getFlags(desc) & flag) != 0;
277    }
278
279    /**
280     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_SCOPE} flag set.
281     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
282     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
283     * generated outside of Nashorn.
284     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
285     */
286    public static boolean isScope(final CallSiteDescriptor desc) {
287        return isFlag(desc, CALLSITE_SCOPE);
288    }
289
290    /**
291     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_FAST_SCOPE} flag set.
292     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
293     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
294     * generated outside of Nashorn.
295     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
296     */
297    public static boolean isFastScope(final CallSiteDescriptor desc) {
298        return isFlag(desc, CALLSITE_FAST_SCOPE);
299    }
300
301    /**
302     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_STRICT} flag set.
303     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
304     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
305     * generated outside of Nashorn.
306     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
307     */
308    public static boolean isStrict(final CallSiteDescriptor desc) {
309        return isFlag(desc, CALLSITE_STRICT);
310    }
311
312    /**
313     * Returns true if this is an apply call that we try to call as
314     * a "call"
315     * @param desc descriptor
316     * @return true if apply to call
317     */
318    public static boolean isApplyToCall(final CallSiteDescriptor desc) {
319        return isFlag(desc, CALLSITE_APPLY_TO_CALL);
320    }
321
322    /**
323     * Is this an optimistic call site
324     * @param desc descriptor
325     * @return true if optimistic
326     */
327    public static boolean isOptimistic(final CallSiteDescriptor desc) {
328        return isFlag(desc, CALLSITE_OPTIMISTIC);
329    }
330
331    /**
332     * Get a program point from a descriptor (must be optimistic)
333     * @param desc descriptor
334     * @return program point
335     */
336    public static int getProgramPoint(final CallSiteDescriptor desc) {
337        assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc;
338        return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT;
339    }
340
341    boolean isProfile() {
342        return isFlag(CALLSITE_PROFILE);
343    }
344
345    boolean isTrace() {
346        return isFlag(CALLSITE_TRACE);
347    }
348
349    boolean isTraceMisses() {
350        return isFlag(CALLSITE_TRACE_MISSES);
351    }
352
353    boolean isTraceEnterExit() {
354        return isFlag(CALLSITE_TRACE_ENTEREXIT);
355    }
356
357    boolean isTraceObjects() {
358        return isFlag(CALLSITE_TRACE_VALUES);
359    }
360
361    boolean isOptimistic() {
362        return isFlag(CALLSITE_OPTIMISTIC);
363    }
364
365    @Override
366    public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
367        return get(getLookup(), operator, operand, newMethodType, flags);
368    }
369
370}
371