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 
  26 package jdk.nashorn.internal.runtime.linker;
  27 
  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;
  37 import 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  */
  44 public 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 }
--- EOF ---