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