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