NashornCallSiteDescriptor.java revision 1494:c7ef0fb26eff
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.security.AccessControlContext; 32import java.security.AccessController; 33import java.security.PrivilegedAction; 34import java.util.concurrent.ConcurrentHashMap; 35import java.util.concurrent.ConcurrentMap; 36import jdk.internal.dynalink.CallSiteDescriptor; 37import jdk.internal.dynalink.CompositeOperation; 38import jdk.internal.dynalink.NamedOperation; 39import jdk.internal.dynalink.Operation; 40import jdk.internal.dynalink.StandardOperation; 41import jdk.nashorn.internal.ir.debug.NashornTextifier; 42import jdk.nashorn.internal.runtime.AccessControlContextFactory; 43import jdk.nashorn.internal.runtime.ScriptRuntime; 44 45/** 46 * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. 47 * The reason we have our own subclass is that we're storing flags in an 48 * additional primitive field. The class also exposes some useful utilities in 49 * form of static methods. 50 */ 51public final class NashornCallSiteDescriptor extends CallSiteDescriptor { 52 // Lowest three bits describe the operation 53 /** Property getter operation {@code obj.prop} */ 54 public static final int GET_PROPERTY = 0; 55 /** Element getter operation {@code obj[index]} */ 56 public static final int GET_ELEMENT = 1; 57 /** Property getter operation, subsequently invoked {@code obj.prop()} */ 58 public static final int GET_METHOD_PROPERTY = 2; 59 /** Element getter operation, subsequently invoked {@code obj[index]()} */ 60 public static final int GET_METHOD_ELEMENT = 3; 61 /** Property setter operation {@code obj.prop = value} */ 62 public static final int SET_PROPERTY = 4; 63 /** Element setter operation {@code obj[index] = value} */ 64 public static final int SET_ELEMENT = 5; 65 /** Call operation {@code fn(args...)} */ 66 public static final int CALL = 6; 67 /** New operation {@code new Constructor(args...)} */ 68 public static final int NEW = 7; 69 70 private static final int OPERATION_MASK = 7; 71 72 // Correspond to the operation indices above. 73 private static final Operation[] OPERATIONS = new Operation[] { 74 new CompositeOperation(StandardOperation.GET_PROPERTY, StandardOperation.GET_ELEMENT, StandardOperation.GET_METHOD), 75 new CompositeOperation(StandardOperation.GET_ELEMENT, StandardOperation.GET_PROPERTY, StandardOperation.GET_METHOD), 76 new CompositeOperation(StandardOperation.GET_METHOD, StandardOperation.GET_PROPERTY, StandardOperation.GET_ELEMENT), 77 new CompositeOperation(StandardOperation.GET_METHOD, StandardOperation.GET_ELEMENT, StandardOperation.GET_PROPERTY), 78 new CompositeOperation(StandardOperation.SET_PROPERTY, StandardOperation.SET_ELEMENT), 79 new CompositeOperation(StandardOperation.SET_ELEMENT, StandardOperation.SET_PROPERTY), 80 StandardOperation.CALL, 81 StandardOperation.NEW 82 }; 83 84 /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a 85 * property access expression. */ 86 public static final int CALLSITE_SCOPE = 1 << 3; 87 /** Flags that the call site is in code that uses ECMAScript strict mode. */ 88 public static final int CALLSITE_STRICT = 1 << 4; 89 /** Flags that a property getter or setter call site references a scope variable that is located at a known distance 90 * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */ 91 public static final int CALLSITE_FAST_SCOPE = 1 << 5; 92 /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the 93 * descriptor, and in that case we have to throw an UnwarrantedOptimismException */ 94 public static final int CALLSITE_OPTIMISTIC = 1 << 6; 95 /** Is this really an apply that we try to call as a call? */ 96 public static final int CALLSITE_APPLY_TO_CALL = 1 << 7; 97 /** Does this a callsite for a variable declaration? */ 98 public static final int CALLSITE_DECLARE = 1 << 8; 99 100 /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit 101 * code where call sites have this flag set. */ 102 public static final int CALLSITE_PROFILE = 1 << 9; 103 /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where 104 * call sites have this flag set. */ 105 public static final int CALLSITE_TRACE = 1 << 10; 106 /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword 107 * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 108 public static final int CALLSITE_TRACE_MISSES = 1 << 11; 109 /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword 110 * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 111 public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 12; 112 /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts 113 * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites 114 * have this flag set. */ 115 public static final int CALLSITE_TRACE_VALUES = 1 << 13; 116 117 //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious 118 //right now given the program points 119 120 /** 121 * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point). 122 * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program 123 * points we can have. 124 * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its 125 * trace/profile settings. 126 */ 127 public static final int CALLSITE_PROGRAM_POINT_SHIFT = 14; 128 129 /** 130 * Maximum program point value. We have 18 bits left over after flags, and 131 * it should be plenty. Program points are local to a single function. Every 132 * function maps to a single JVM bytecode method that can have at most 65535 133 * bytes. (Large functions are synthetically split into smaller functions.) 134 * A single invokedynamic is 5 bytes; even if a method consists of only 135 * invokedynamic instructions that leaves us with at most 65535/5 = 13107 136 * program points for the largest single method; those can be expressed on 137 * 14 bits. It is true that numbering of program points is independent of 138 * bytecode representation, but if a function would need more than ~14 bits 139 * for the program points, then it is reasonable to presume splitter 140 * would've split it into several smaller functions already. 141 */ 142 public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1; 143 144 /** 145 * Flag mask to get the program point flags 146 */ 147 public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1; 148 149 private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals = 150 new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() { 151 @Override 152 protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) { 153 return new ConcurrentHashMap<>(); 154 } 155 }; 156 157 private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT = 158 AccessControlContextFactory.createAccessControlContext(CallSiteDescriptor.GET_LOOKUP_PERMISSION_NAME); 159 160 private final int flags; 161 162 /** 163 * Function used by {@link NashornTextifier} to represent call site flags in 164 * human readable form 165 * @param flags call site flags 166 * @return human readable form of this callsite descriptor 167 */ 168 public static String toString(final int flags) { 169 final StringBuilder sb = new StringBuilder(); 170 if ((flags & CALLSITE_SCOPE) != 0) { 171 if ((flags & CALLSITE_FAST_SCOPE) != 0) { 172 sb.append("fastscope "); 173 } else { 174 assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope"; 175 sb.append("scope "); 176 } 177 if ((flags & CALLSITE_DECLARE) != 0) { 178 sb.append("declare "); 179 } 180 } 181 if ((flags & CALLSITE_APPLY_TO_CALL) != 0) { 182 sb.append("apply2call "); 183 } 184 if ((flags & CALLSITE_STRICT) != 0) { 185 sb.append("strict "); 186 } 187 return sb.length() == 0 ? "" : " " + sb.toString().trim(); 188 } 189 190 /** 191 * Given call site flags, returns the operation name encoded in them. 192 * @param flags flags 193 * @return the operation name 194 */ 195 public static String getOperationName(final int flags) { 196 switch(flags & OPERATION_MASK) { 197 case 0: return "GET_PROPERTY"; 198 case 1: return "GET_ELEMENT"; 199 case 2: return "GET_METHOD_PROPERTY"; 200 case 3: return "GET_METHOD_ELEMENT"; 201 case 4: return "SET_PROPERTY"; 202 case 5: return "SET_ELEMENT"; 203 case 6: return "CALL"; 204 case 7: return "NEW"; 205 default: throw new AssertionError(); 206 } 207 } 208 209 /** 210 * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable 211 * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so). 212 * @param lookup the lookup describing the script 213 * @param name the name at the call site. Can not be null, but it can be empty. 214 * @param methodType the method type at the call site 215 * @param flags Nashorn-specific call site flags 216 * @return a call site descriptor with the specified values. 217 */ 218 public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, 219 final MethodType methodType, final int flags) { 220 final Operation baseOp = OPERATIONS[flags & OPERATION_MASK]; 221 final String decodedName = NameCodec.decode(name); 222 // TODO: introduce caching of NamedOperation 223 final Operation op = decodedName.isEmpty() ? baseOp : new NamedOperation(baseOp, decodedName); 224 return get(lookup, op, methodType, flags); 225 } 226 227 private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final Operation operation, final MethodType methodType, final int flags) { 228 final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operation, methodType, flags); 229 // Many of these call site descriptors are identical (e.g. every getter for a property color will be 230 // "GET_PROPERTY:color(Object)Object", so it makes sense canonicalizing them. 231 final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass()); 232 final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd); 233 return canonical != null ? canonical : csd; 234 } 235 236 private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final Operation operation, final MethodType methodType, final int flags) { 237 super(lookup, operation, methodType); 238 this.flags = flags; 239 } 240 241 static Lookup getLookupInternal(final CallSiteDescriptor csd) { 242 if (csd instanceof NashornCallSiteDescriptor) { 243 return ((NashornCallSiteDescriptor)csd).getLookupPrivileged(); 244 } 245 return AccessController.doPrivileged((PrivilegedAction<Lookup>)()->csd.getLookup(), GET_LOOKUP_PERMISSION_CONTEXT); 246 } 247 248 @Override 249 public boolean equals(final Object obj) { 250 return super.equals(obj) && flags == ((NashornCallSiteDescriptor)obj).flags; 251 } 252 253 @Override 254 public int hashCode() { 255 return super.hashCode() ^ flags; 256 } 257 258 /** 259 * Returns the named operand in this descriptor's operation. Equivalent to 260 * {@code ((NamedOperation)getOperation()).getName().toString()} for call 261 * sites with a named operand. For call sites without named operands returns null. 262 * @return the named operand in this descriptor's operation. 263 */ 264 public String getOperand() { 265 return getOperand(this); 266 } 267 268 /** 269 * Returns the named operand in the passed descriptor's operation. 270 * Equivalent to 271 * {@code ((NamedOperation)desc.getOperation()).getName().toString()} for 272 * descriptors with a named operand. For descriptors without named operands 273 * returns null. 274 * @param desc the call site descriptors 275 * @return the named operand in this descriptor's operation. 276 */ 277 public static String getOperand(final CallSiteDescriptor desc) { 278 final Operation operation = desc.getOperation(); 279 return operation instanceof NamedOperation ? ((NamedOperation)operation).getName().toString() : null; 280 } 281 282 /** 283 * Returns the first operation in this call site descriptor's potentially 284 * composite operation. E.g. if this call site descriptor has a composite 285 * operation {@code GET_PROPERTY|GET_METHOD|GET_ELEM}, it will return 286 * {@code GET_PROPERTY}. Nashorn - being a ECMAScript engine - does not 287 * distinguish between property, element, and method namespace; ECMAScript 288 * objects just have one single property namespace for all these, therefore 289 * it is largely irrelevant what the composite operation is structured like; 290 * if the first operation can't be satisfied, neither can the others. The 291 * first operation is however sometimes used to slightly alter the 292 * semantics; for example, a distinction between {@code GET_PROPERTY} and 293 * {@code GET_METHOD} being the first operation can translate into whether 294 * {@code "__noSuchProperty__"} or {@code "__noSuchMethod__"} will be 295 * executed in case the property is not found. Note that if a call site 296 * descriptor comes from outside of Nashorn, its class will be different, 297 * and there is no guarantee about the way it composes its operations. For 298 * that reason, for potentially foreign call site descriptors you should use 299 * {@link #getFirstStandardOperation(CallSiteDescriptor)} instead. 300 * @return the first operation in this call site descriptor. Note this will 301 * always be a {@code StandardOperation} as Nashorn internally only uses 302 * standard operations. 303 */ 304 public StandardOperation getFirstOperation() { 305 final Operation base = NamedOperation.getBaseOperation(getOperation()); 306 if (base instanceof CompositeOperation) { 307 return (StandardOperation)((CompositeOperation)base).getOperation(0); 308 } 309 return (StandardOperation)base; 310 } 311 312 /** 313 * Returns the first standard operation in the (potentially composite) 314 * operation of the passed call site descriptor. 315 * @param desc the call site descriptor. 316 * @return Returns the first standard operation in the (potentially 317 * composite) operation of the passed call site descriptor. Can return null 318 * if the call site contains no standard operations. 319 */ 320 public static StandardOperation getFirstStandardOperation(final CallSiteDescriptor desc) { 321 final Operation base = NamedOperation.getBaseOperation(desc.getOperation()); 322 if (base instanceof StandardOperation) { 323 return (StandardOperation)base; 324 } else if (base instanceof CompositeOperation) { 325 final CompositeOperation cop = (CompositeOperation)base; 326 for(int i = 0; i < cop.getOperationCount(); ++i) { 327 final Operation op = cop.getOperation(i); 328 if (op instanceof StandardOperation) { 329 return (StandardOperation)op; 330 } 331 } 332 } 333 return null; 334 } 335 336 /** 337 * Returns true if the passed call site descriptor's operation contains (or 338 * is) the specified standard operation. 339 * @param desc the call site descriptor. 340 * @param operation the operation whose presence is tested. 341 * @return Returns true if the call site descriptor's operation contains (or 342 * is) the specified standard operation. 343 */ 344 public static boolean contains(final CallSiteDescriptor desc, final StandardOperation operation) { 345 return CompositeOperation.contains(NamedOperation.getBaseOperation(desc.getOperation()), operation); 346 } 347 348 /** 349 * Returns the error message to be used when CALL or NEW is used on a non-function. 350 * 351 * @param obj object on which CALL or NEW is used 352 * @return error message 353 */ 354 public String getFunctionErrorMessage(final Object obj) { 355 final String funcDesc = getOperand(); 356 return funcDesc != null? funcDesc : ScriptRuntime.safeToString(obj); 357 } 358 359 /** 360 * Returns the error message to be used when CALL or NEW is used on a non-function. 361 * 362 * @param desc call site descriptor 363 * @param obj object on which CALL or NEW is used 364 * @return error message 365 */ 366 public static String getFunctionErrorMessage(final CallSiteDescriptor desc, final Object obj) { 367 return desc instanceof NashornCallSiteDescriptor ? 368 ((NashornCallSiteDescriptor)desc).getFunctionErrorMessage(obj) : 369 ScriptRuntime.safeToString(obj); 370 } 371 372 /** 373 * Returns the Nashorn-specific flags for this call site descriptor. 374 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 375 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 376 * generated outside of Nashorn. 377 * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site 378 * descriptor. 379 */ 380 public static int getFlags(final CallSiteDescriptor desc) { 381 return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0; 382 } 383 384 /** 385 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 386 * @param flag the tested flag 387 * @return true if the flag is set, false otherwise 388 */ 389 private boolean isFlag(final int flag) { 390 return (flags & flag) != 0; 391 } 392 393 /** 394 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 395 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 396 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 397 * generated outside of Nashorn. 398 * @param flag the tested flag 399 * @return true if the flag is set, false otherwise (it will be false if the descriptor is not a Nashorn call site 400 * descriptor). 401 */ 402 private static boolean isFlag(final CallSiteDescriptor desc, final int flag) { 403 return (getFlags(desc) & flag) != 0; 404 } 405 406 /** 407 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_SCOPE} flag set. 408 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 409 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 410 * generated outside of Nashorn. 411 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 412 */ 413 public static boolean isScope(final CallSiteDescriptor desc) { 414 return isFlag(desc, CALLSITE_SCOPE); 415 } 416 417 /** 418 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_FAST_SCOPE} flag set. 419 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 420 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 421 * generated outside of Nashorn. 422 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 423 */ 424 public static boolean isFastScope(final CallSiteDescriptor desc) { 425 return isFlag(desc, CALLSITE_FAST_SCOPE); 426 } 427 428 /** 429 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_STRICT} flag set. 430 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 431 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 432 * generated outside of Nashorn. 433 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 434 */ 435 public static boolean isStrict(final CallSiteDescriptor desc) { 436 return isFlag(desc, CALLSITE_STRICT); 437 } 438 439 /** 440 * Returns true if this is an apply call that we try to call as 441 * a "call" 442 * @param desc descriptor 443 * @return true if apply to call 444 */ 445 public static boolean isApplyToCall(final CallSiteDescriptor desc) { 446 return isFlag(desc, CALLSITE_APPLY_TO_CALL); 447 } 448 449 /** 450 * Is this an optimistic call site 451 * @param desc descriptor 452 * @return true if optimistic 453 */ 454 public static boolean isOptimistic(final CallSiteDescriptor desc) { 455 return isFlag(desc, CALLSITE_OPTIMISTIC); 456 } 457 458 /** 459 * Does this callsite contain a declaration for its target? 460 * @param desc descriptor 461 * @return true if contains declaration 462 */ 463 public static boolean isDeclaration(final CallSiteDescriptor desc) { 464 return isFlag(desc, CALLSITE_DECLARE); 465 } 466 467 /** 468 * Returns true if {@code flags} has the {@link #CALLSITE_STRICT} bit set. 469 * @param flags the flags 470 * @return true if the flag is set, false otherwise. 471 */ 472 public static boolean isStrictFlag(final int flags) { 473 return (flags & CALLSITE_STRICT) != 0; 474 } 475 476 /** 477 * Returns true if {@code flags} has the {@link #CALLSITE_SCOPE} bit set. 478 * @param flags the flags 479 * @return true if the flag is set, false otherwise. 480 */ 481 public static boolean isScopeFlag(final int flags) { 482 return (flags & CALLSITE_SCOPE) != 0; 483 } 484 485 /** 486 * Get a program point from a descriptor (must be optimistic) 487 * @param desc descriptor 488 * @return program point 489 */ 490 public static int getProgramPoint(final CallSiteDescriptor desc) { 491 assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc; 492 return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT; 493 } 494 495 boolean isProfile() { 496 return isFlag(CALLSITE_PROFILE); 497 } 498 499 boolean isTrace() { 500 return isFlag(CALLSITE_TRACE); 501 } 502 503 boolean isTraceMisses() { 504 return isFlag(CALLSITE_TRACE_MISSES); 505 } 506 507 boolean isTraceEnterExit() { 508 return isFlag(CALLSITE_TRACE_ENTEREXIT); 509 } 510 511 boolean isTraceObjects() { 512 return isFlag(CALLSITE_TRACE_VALUES); 513 } 514 515 boolean isOptimistic() { 516 return isFlag(CALLSITE_OPTIMISTIC); 517 } 518 519 @Override 520 public CallSiteDescriptor changeMethodTypeInternal(final MethodType newMethodType) { 521 return get(getLookupPrivileged(), getOperation(), newMethodType, flags); 522 } 523} 524