NashornCallSiteDescriptor.java revision 1470:04ed602df062
11553Srgrimes/*
21553Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
31553Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
41553Srgrimes *
51553Srgrimes * This code is free software; you can redistribute it and/or modify it
61553Srgrimes * under the terms of the GNU General Public License version 2 only, as
71553Srgrimes * published by the Free Software Foundation.  Oracle designates this
81553Srgrimes * particular file as subject to the "Classpath" exception as provided
91553Srgrimes * by Oracle in the LICENSE file that accompanied this code.
101553Srgrimes *
111553Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT
121553Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131553Srgrimes * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
141553Srgrimes * version 2 for more details (a copy is included in the LICENSE file that
151553Srgrimes * accompanied this code).
161553Srgrimes *
171553Srgrimes * You should have received a copy of the GNU General Public License version
181553Srgrimes * 2 along with this work; if not, write to the Free Software Foundation,
191553Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
201553Srgrimes *
211553Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
221553Srgrimes * or visit www.oracle.com if you need additional information or have any
231553Srgrimes * questions.
241553Srgrimes */
251553Srgrimes
261553Srgrimespackage jdk.nashorn.internal.runtime.linker;
271553Srgrimes
281553Srgrimesimport java.lang.invoke.MethodHandles;
291553Srgrimesimport java.lang.invoke.MethodHandles.Lookup;
301553Srgrimesimport java.lang.invoke.MethodType;
311553Srgrimesimport java.util.concurrent.ConcurrentHashMap;
321553Srgrimesimport java.util.concurrent.ConcurrentMap;
331553Srgrimesimport jdk.internal.dynalink.CallSiteDescriptor;
341553Srgrimesimport jdk.internal.dynalink.support.AbstractCallSiteDescriptor;
351553Srgrimesimport jdk.nashorn.internal.ir.debug.NashornTextifier;
361553Srgrimesimport jdk.nashorn.internal.runtime.ScriptRuntime;
371553Srgrimes
381553Srgrimes/**
391553Srgrimes * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that
401553Srgrimes * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also
411553Srgrimes * we're storing flags in an additional primitive field.
421553Srgrimes */
431553Srgrimespublic final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
441553Srgrimes    /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
451553Srgrimes     * property access expression. */
461553Srgrimes    public static final int CALLSITE_SCOPE         = 1 << 0;
471553Srgrimes    /** Flags that the call site is in code that uses ECMAScript strict mode. */
481553Srgrimes    public static final int CALLSITE_STRICT        = 1 << 1;
491553Srgrimes    /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
501553Srgrimes     * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
511553Srgrimes    public static final int CALLSITE_FAST_SCOPE    = 1 << 2;
521553Srgrimes    /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
531553Srgrimes     * descriptor, and in that case we have to throw an UnwarrantedOptimismException */
541553Srgrimes    public static final int CALLSITE_OPTIMISTIC    = 1 << 3;
551553Srgrimes    /** Is this really an apply that we try to call as a call? */
561553Srgrimes    public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
571553Srgrimes    /** Does this a callsite for a variable declaration? */
581553Srgrimes    public static final int CALLSITE_DECLARE       = 1 << 5;
591553Srgrimes
601553Srgrimes    /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
611553Srgrimes     * code where call sites have this flag set. */
621553Srgrimes    public static final int CALLSITE_PROFILE         = 1 << 6;
631553Srgrimes    /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
641553Srgrimes     * call sites have this flag set. */
652916Swollman    public static final int CALLSITE_TRACE           = 1 << 7;
661553Srgrimes    /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
671553Srgrimes     * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
681553Srgrimes    public static final int CALLSITE_TRACE_MISSES    = 1 << 8;
692916Swollman    /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
702916Swollman     * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
711553Srgrimes    public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9;
721553Srgrimes    /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
731553Srgrimes     * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
742916Swollman     * have this flag set. */
751553Srgrimes    public static final int CALLSITE_TRACE_VALUES    = 1 << 10;
761553Srgrimes
771553Srgrimes    //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
782916Swollman    //right now given the program points
791553Srgrimes
801553Srgrimes    /**
811553Srgrimes     * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
821553Srgrimes     * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program
831553Srgrimes     * points we can have.
841553Srgrimes     * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
851553Srgrimes     * trace/profile settings.
861553Srgrimes     */
871553Srgrimes    public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11;
881553Srgrimes
891553Srgrimes    /**
901553Srgrimes     * Maximum program point value. 21 bits should be enough for anyone
911553Srgrimes     */
922916Swollman    public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
931553Srgrimes
941553Srgrimes    /**
951553Srgrimes     * Flag mask to get the program point flags
961553Srgrimes     */
971553Srgrimes    public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1;
981553Srgrimes
991553Srgrimes    private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
1001553Srgrimes            new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
1012916Swollman        @Override
1022916Swollman        protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) {
1031553Srgrimes            return new ConcurrentHashMap<>();
1041553Srgrimes        }
1052916Swollman    };
1061553Srgrimes
1072916Swollman    private final MethodHandles.Lookup lookup;
1081553Srgrimes    private final String operator;
1091553Srgrimes    private final String operand;
1102916Swollman    private final MethodType methodType;
1111553Srgrimes    private final int flags;
1122916Swollman
1131553Srgrimes    /**
1142916Swollman     * Function used by {@link NashornTextifier} to represent call site flags in
1152916Swollman     * human readable form
1161553Srgrimes     * @param flags call site flags
1172916Swollman     * @return human readable form of this callsite descriptor
1182916Swollman     */
1191553Srgrimes    public static String toString(final int flags) {
1201553Srgrimes        final StringBuilder sb = new StringBuilder();
1211553Srgrimes        if ((flags & CALLSITE_SCOPE) != 0) {
1221553Srgrimes            if ((flags & CALLSITE_FAST_SCOPE) != 0) {
1231553Srgrimes                sb.append("fastscope ");
1241553Srgrimes            } else {
1251553Srgrimes                assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
1261553Srgrimes                sb.append("scope ");
1271553Srgrimes            }
1281553Srgrimes            if ((flags & CALLSITE_DECLARE) != 0) {
1291553Srgrimes                sb.append("declare ");
1301553Srgrimes            }
1311553Srgrimes        }
1322916Swollman        if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
1331553Srgrimes            sb.append("apply2call ");
1341553Srgrimes        }
1351553Srgrimes        if ((flags & CALLSITE_STRICT) != 0) {
1361553Srgrimes            sb.append("strict ");
1371553Srgrimes        }
1381553Srgrimes        return sb.length() == 0 ? "" : " " + sb.toString().trim();
1391553Srgrimes    }
140
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 = CallSiteDescriptor.tokenizeName(name);
153        assert tokenizedName.length >= 2;
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    }
160
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    }
169
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    }
178
179    @Override
180    public int getNameTokenCount() {
181        return operand == null ? 2 : 3;
182    }
183
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    }
199
200    @Override
201    public Lookup getLookup() {
202        return lookup;
203    }
204
205    @Override
206    public boolean equals(final CallSiteDescriptor csd) {
207        return super.equals(csd) && flags == getFlags(csd);
208    }
209
210    @Override
211    public MethodType getMethodType() {
212        return methodType;
213    }
214
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    }
223
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    }
239
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    }
249
250    /**
251     * If this is a dyn:call or dyn:new, this returns function description from callsite.
252     * Caller has to make sure this is a dyn:call or dyn:new call site.
253     *
254     * @return function description if available (or null)
255     */
256    public String getFunctionDescription() {
257        assert getFirstOperator().equals("call") || getFirstOperator().equals("new");
258        return getNameTokenCount() > 2? getNameToken(2) : null;
259    }
260
261    /**
262     * If this is a dyn:call or dyn:new, this returns function description from callsite.
263     * Caller has to make sure this is a dyn:call or dyn:new call site.
264     *
265     * @param desc call site descriptor
266     * @return function description if available (or null)
267     */
268    public static String getFunctionDescription(final CallSiteDescriptor desc) {
269        return desc instanceof NashornCallSiteDescriptor ?
270                ((NashornCallSiteDescriptor)desc).getFunctionDescription() : null;
271    }
272
273
274    /**
275     * Returns the error message to be used when dyn:call or dyn:new is used on a non-function.
276     *
277     * @param obj object on which dyn:call or dyn:new is used
278     * @return error message
279     */
280    public String getFunctionErrorMessage(final Object obj) {
281        final String funcDesc = getFunctionDescription();
282        return funcDesc != null? funcDesc : ScriptRuntime.safeToString(obj);
283    }
284
285    /**
286     * Returns the error message to be used when dyn:call or dyn:new is used on a non-function.
287     *
288     * @param desc call site descriptor
289     * @param obj object on which dyn:call or dyn:new is used
290     * @return error message
291     */
292    public static String getFunctionErrorMessage(final CallSiteDescriptor desc, final Object obj) {
293        return desc instanceof NashornCallSiteDescriptor ?
294                ((NashornCallSiteDescriptor)desc).getFunctionErrorMessage(obj) :
295                ScriptRuntime.safeToString(obj);
296    }
297
298    /**
299     * Returns the Nashorn-specific flags for this call site descriptor.
300     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
301     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
302     * generated outside of Nashorn.
303     * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site
304     * descriptor.
305     */
306    public static int getFlags(final CallSiteDescriptor desc) {
307        return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0;
308    }
309
310    /**
311     * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
312     * @param flag the tested flag
313     * @return true if the flag is set, false otherwise
314     */
315    private boolean isFlag(final int flag) {
316        return (flags & flag) != 0;
317    }
318
319    /**
320     * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
321     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
322     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
323     * generated outside of Nashorn.
324     * @param flag the tested flag
325     * @return true if the flag is set, false otherwise (it will be false if the descriptor is not a Nashorn call site
326     * descriptor).
327     */
328    private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
329        return (getFlags(desc) & flag) != 0;
330    }
331
332    /**
333     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_SCOPE} flag set.
334     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
335     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
336     * generated outside of Nashorn.
337     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
338     */
339    public static boolean isScope(final CallSiteDescriptor desc) {
340        return isFlag(desc, CALLSITE_SCOPE);
341    }
342
343    /**
344     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_FAST_SCOPE} flag set.
345     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
346     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
347     * generated outside of Nashorn.
348     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
349     */
350    public static boolean isFastScope(final CallSiteDescriptor desc) {
351        return isFlag(desc, CALLSITE_FAST_SCOPE);
352    }
353
354    /**
355     * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link  #CALLSITE_STRICT} flag set.
356     * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
357     * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
358     * generated outside of Nashorn.
359     * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
360     */
361    public static boolean isStrict(final CallSiteDescriptor desc) {
362        return isFlag(desc, CALLSITE_STRICT);
363    }
364
365    /**
366     * Returns true if this is an apply call that we try to call as
367     * a "call"
368     * @param desc descriptor
369     * @return true if apply to call
370     */
371    public static boolean isApplyToCall(final CallSiteDescriptor desc) {
372        return isFlag(desc, CALLSITE_APPLY_TO_CALL);
373    }
374
375    /**
376     * Is this an optimistic call site
377     * @param desc descriptor
378     * @return true if optimistic
379     */
380    public static boolean isOptimistic(final CallSiteDescriptor desc) {
381        return isFlag(desc, CALLSITE_OPTIMISTIC);
382    }
383
384    /**
385     * Does this callsite contain a declaration for its target?
386     * @param desc descriptor
387     * @return true if contains declaration
388     */
389    public static boolean isDeclaration(final CallSiteDescriptor desc) {
390        return isFlag(desc, CALLSITE_DECLARE);
391    }
392
393    /**
394     * Returns true if {@code flags} has the {@link  #CALLSITE_STRICT} bit set.
395     * @param flags the flags
396     * @return true if the flag is set, false otherwise.
397     */
398    public static boolean isStrictFlag(final int flags) {
399        return (flags & CALLSITE_STRICT) != 0;
400    }
401
402    /**
403     * Returns true if {@code flags} has the {@link  #CALLSITE_SCOPE} bit set.
404     * @param flags the flags
405     * @return true if the flag is set, false otherwise.
406     */
407    public static boolean isScopeFlag(final int flags) {
408        return (flags & CALLSITE_SCOPE) != 0;
409    }
410
411    /**
412     * Get a program point from a descriptor (must be optimistic)
413     * @param desc descriptor
414     * @return program point
415     */
416    public static int getProgramPoint(final CallSiteDescriptor desc) {
417        assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc;
418        return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT;
419    }
420
421    boolean isProfile() {
422        return isFlag(CALLSITE_PROFILE);
423    }
424
425    boolean isTrace() {
426        return isFlag(CALLSITE_TRACE);
427    }
428
429    boolean isTraceMisses() {
430        return isFlag(CALLSITE_TRACE_MISSES);
431    }
432
433    boolean isTraceEnterExit() {
434        return isFlag(CALLSITE_TRACE_ENTEREXIT);
435    }
436
437    boolean isTraceObjects() {
438        return isFlag(CALLSITE_TRACE_VALUES);
439    }
440
441    boolean isOptimistic() {
442        return isFlag(CALLSITE_OPTIMISTIC);
443    }
444
445    @Override
446    public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
447        return get(getLookup(), operator, operand, newMethodType, flags);
448    }
449
450}
451