NashornCallSiteDescriptor.java revision 1482:d35aa8beb997
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.nashorn.internal.ir.debug.NashornTextifier; 38import jdk.nashorn.internal.runtime.AccessControlContextFactory; 39import jdk.nashorn.internal.runtime.ScriptRuntime; 40 41/** 42 * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that 43 * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also 44 * we're storing flags in an additional primitive field. 45 */ 46public final class NashornCallSiteDescriptor extends CallSiteDescriptor { 47 /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a 48 * property access expression. */ 49 public static final int CALLSITE_SCOPE = 1 << 0; 50 /** Flags that the call site is in code that uses ECMAScript strict mode. */ 51 public static final int CALLSITE_STRICT = 1 << 1; 52 /** Flags that a property getter or setter call site references a scope variable that is located at a known distance 53 * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */ 54 public static final int CALLSITE_FAST_SCOPE = 1 << 2; 55 /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the 56 * descriptor, and in that case we have to throw an UnwarrantedOptimismException */ 57 public static final int CALLSITE_OPTIMISTIC = 1 << 3; 58 /** Is this really an apply that we try to call as a call? */ 59 public static final int CALLSITE_APPLY_TO_CALL = 1 << 4; 60 /** Does this a callsite for a variable declaration? */ 61 public static final int CALLSITE_DECLARE = 1 << 5; 62 63 /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit 64 * code where call sites have this flag set. */ 65 public static final int CALLSITE_PROFILE = 1 << 6; 66 /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where 67 * call sites have this flag set. */ 68 public static final int CALLSITE_TRACE = 1 << 7; 69 /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword 70 * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 71 public static final int CALLSITE_TRACE_MISSES = 1 << 8; 72 /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword 73 * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 74 public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9; 75 /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts 76 * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites 77 * have this flag set. */ 78 public static final int CALLSITE_TRACE_VALUES = 1 << 10; 79 80 //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious 81 //right now given the program points 82 83 /** 84 * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point). 85 * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program 86 * points we can have. 87 * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its 88 * trace/profile settings. 89 */ 90 public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11; 91 92 /** 93 * Maximum program point value. 21 bits should be enough for anyone 94 */ 95 public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1; 96 97 /** 98 * Flag mask to get the program point flags 99 */ 100 public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1; 101 102 private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals = 103 new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() { 104 @Override 105 protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) { 106 return new ConcurrentHashMap<>(); 107 } 108 }; 109 110 private static final AccessControlContext GET_LOOKUP_PERMISSION_CONTEXT = 111 AccessControlContextFactory.createAccessControlContext(CallSiteDescriptor.GET_LOOKUP_PERMISSION_NAME); 112 113 private final String operator; 114 private final String operand; 115 private final int flags; 116 117 /** 118 * Function used by {@link NashornTextifier} to represent call site flags in 119 * human readable form 120 * @param flags call site flags 121 * @return human readable form of this callsite descriptor 122 */ 123 public static String toString(final int flags) { 124 final StringBuilder sb = new StringBuilder(); 125 if ((flags & CALLSITE_SCOPE) != 0) { 126 if ((flags & CALLSITE_FAST_SCOPE) != 0) { 127 sb.append("fastscope "); 128 } else { 129 assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope"; 130 sb.append("scope "); 131 } 132 if ((flags & CALLSITE_DECLARE) != 0) { 133 sb.append("declare "); 134 } 135 } 136 if ((flags & CALLSITE_APPLY_TO_CALL) != 0) { 137 sb.append("apply2call "); 138 } 139 if ((flags & CALLSITE_STRICT) != 0) { 140 sb.append("strict "); 141 } 142 return sb.length() == 0 ? "" : " " + sb.toString().trim(); 143 } 144 145 /** 146 * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable 147 * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so). 148 * @param lookup the lookup describing the script 149 * @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}. 150 * @param methodType the method type at the call site 151 * @param flags Nashorn-specific call site flags 152 * @return a call site descriptor with the specified values. 153 */ 154 public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, 155 final MethodType methodType, final int flags) { 156 final String[] tokenizedName = CallSiteDescriptor.tokenizeName(name); 157 assert tokenizedName.length >= 2; 158 assert "dyn".equals(tokenizedName[0]); 159 assert tokenizedName[1] != null; 160 // TODO: see if we can move mangling/unmangling into Dynalink 161 return get(lookup, name, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null, 162 methodType, flags); 163 } 164 165 private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, final String operator, final String operand, final MethodType methodType, final int flags) { 166 final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, name, operator, operand, methodType, flags); 167 // Many of these call site descriptors are identical (e.g. every getter for a property color will be 168 // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them. 169 final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass()); 170 final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd); 171 return canonical != null ? canonical : csd; 172 } 173 174 private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String name, final String operator, final String operand, 175 final MethodType methodType, final int flags) { 176 super(lookup, name, methodType); 177 this.operator = operator; 178 this.operand = operand; 179 this.flags = flags; 180 } 181 182 @Override 183 public int getNameTokenCount() { 184 return operand == null ? 2 : 3; 185 } 186 187 @Override 188 public String getNameToken(final int i) { 189 switch(i) { 190 case 0: return "dyn"; 191 case 1: return operator; 192 case 2: 193 if(operand != null) { 194 return operand; 195 } 196 } 197 throw new IndexOutOfBoundsException(String.valueOf(i)); 198 } 199 200 static Lookup getLookupInternal(final CallSiteDescriptor csd) { 201 if (csd instanceof NashornCallSiteDescriptor) { 202 return ((NashornCallSiteDescriptor)csd).getLookupPrivileged(); 203 } 204 return AccessController.doPrivileged((PrivilegedAction<Lookup>)()->csd.getLookup(), GET_LOOKUP_PERMISSION_CONTEXT); 205 } 206 207 @Override 208 public boolean equals(final Object obj) { 209 return super.equals(obj) && flags == ((NashornCallSiteDescriptor)obj).flags; 210 } 211 212 @Override 213 public int hashCode() { 214 return super.hashCode() ^ flags; 215 } 216 217 /** 218 * Returns the operator (e.g. {@code "getProp"}) in this call site descriptor's name. Equivalent to 219 * {@code getNameToken(CallSiteDescriptor.OPERATOR)}. The returned operator can be composite. 220 * @return the operator in this call site descriptor's name. 221 */ 222 public String getOperator() { 223 return operator; 224 } 225 226 /** 227 * Returns the first operator in this call site descriptor's name. E.g. if this call site descriptor has a composite 228 * operation {@code "getProp|getMethod|getElem"}, it will return {@code "getProp"}. Nashorn - being a ECMAScript 229 * engine - does not distinguish between property, element, and method namespace; ECMAScript objects just have one 230 * single property namespace for all these, therefore it is largely irrelevant what the composite operation is 231 * structured like; if the first operation can't be satisfied, neither can the others. The first operation is 232 * however sometimes used to slightly alter the semantics; for example, a distinction between {@code "getProp"} and 233 * {@code "getMethod"} being the first operation can translate into whether {@code "__noSuchProperty__"} or 234 * {@code "__noSuchMethod__"} will be executed in case the property is not found. 235 * @return the first operator in this call site descriptor's name. 236 */ 237 public String getFirstOperator() { 238 final int delim = operator.indexOf(CallSiteDescriptor.OPERATOR_DELIMITER); 239 return delim == -1 ? operator : operator.substring(0, delim); 240 } 241 242 /** 243 * Returns the named operand in this descriptor's name. Equivalent to 244 * {@code getNameToken(CallSiteDescriptor.NAME_OPERAND)}. E.g. for operation {@code "dyn:getProp:color"}, returns 245 * {@code "color"}. For call sites without named operands (e.g. {@code "dyn:new"}) returns null. 246 * @return the named operand in this descriptor's name. 247 */ 248 public String getOperand() { 249 return operand; 250 } 251 252 /** 253 * If this is a dyn:call or dyn:new, this returns function description from callsite. 254 * Caller has to make sure this is a dyn:call or dyn:new call site. 255 * 256 * @return function description if available (or null) 257 */ 258 public String getFunctionDescription() { 259 assert getFirstOperator().equals("call") || getFirstOperator().equals("new"); 260 return getNameTokenCount() > 2? getNameToken(2) : null; 261 } 262 263 /** 264 * If this is a dyn:call or dyn:new, this returns function description from callsite. 265 * Caller has to make sure this is a dyn:call or dyn:new call site. 266 * 267 * @param desc call site descriptor 268 * @return function description if available (or null) 269 */ 270 public static String getFunctionDescription(final CallSiteDescriptor desc) { 271 return desc instanceof NashornCallSiteDescriptor ? 272 ((NashornCallSiteDescriptor)desc).getFunctionDescription() : null; 273 } 274 275 276 /** 277 * Returns the error message to be used when dyn:call or dyn:new is used on a non-function. 278 * 279 * @param obj object on which dyn:call or dyn:new is used 280 * @return error message 281 */ 282 public String getFunctionErrorMessage(final Object obj) { 283 final String funcDesc = getFunctionDescription(); 284 return funcDesc != null? funcDesc : ScriptRuntime.safeToString(obj); 285 } 286 287 /** 288 * Returns the error message to be used when dyn:call or dyn:new is used on a non-function. 289 * 290 * @param desc call site descriptor 291 * @param obj object on which dyn:call or dyn:new is used 292 * @return error message 293 */ 294 public static String getFunctionErrorMessage(final CallSiteDescriptor desc, final Object obj) { 295 return desc instanceof NashornCallSiteDescriptor ? 296 ((NashornCallSiteDescriptor)desc).getFunctionErrorMessage(obj) : 297 ScriptRuntime.safeToString(obj); 298 } 299 300 /** 301 * Returns the Nashorn-specific flags for this call site descriptor. 302 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 303 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 304 * generated outside of Nashorn. 305 * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site 306 * descriptor. 307 */ 308 public static int getFlags(final CallSiteDescriptor desc) { 309 return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0; 310 } 311 312 /** 313 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 314 * @param flag the tested flag 315 * @return true if the flag is set, false otherwise 316 */ 317 private boolean isFlag(final int flag) { 318 return (flags & flag) != 0; 319 } 320 321 /** 322 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 323 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 324 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 325 * generated outside of Nashorn. 326 * @param flag the tested flag 327 * @return true if the flag is set, false otherwise (it will be false if the descriptor is not a Nashorn call site 328 * descriptor). 329 */ 330 private static boolean isFlag(final CallSiteDescriptor desc, final int flag) { 331 return (getFlags(desc) & flag) != 0; 332 } 333 334 /** 335 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_SCOPE} flag set. 336 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 337 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 338 * generated outside of Nashorn. 339 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 340 */ 341 public static boolean isScope(final CallSiteDescriptor desc) { 342 return isFlag(desc, CALLSITE_SCOPE); 343 } 344 345 /** 346 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_FAST_SCOPE} flag set. 347 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 348 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 349 * generated outside of Nashorn. 350 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 351 */ 352 public static boolean isFastScope(final CallSiteDescriptor desc) { 353 return isFlag(desc, CALLSITE_FAST_SCOPE); 354 } 355 356 /** 357 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_STRICT} flag set. 358 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 359 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 360 * generated outside of Nashorn. 361 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 362 */ 363 public static boolean isStrict(final CallSiteDescriptor desc) { 364 return isFlag(desc, CALLSITE_STRICT); 365 } 366 367 /** 368 * Returns true if this is an apply call that we try to call as 369 * a "call" 370 * @param desc descriptor 371 * @return true if apply to call 372 */ 373 public static boolean isApplyToCall(final CallSiteDescriptor desc) { 374 return isFlag(desc, CALLSITE_APPLY_TO_CALL); 375 } 376 377 /** 378 * Is this an optimistic call site 379 * @param desc descriptor 380 * @return true if optimistic 381 */ 382 public static boolean isOptimistic(final CallSiteDescriptor desc) { 383 return isFlag(desc, CALLSITE_OPTIMISTIC); 384 } 385 386 /** 387 * Does this callsite contain a declaration for its target? 388 * @param desc descriptor 389 * @return true if contains declaration 390 */ 391 public static boolean isDeclaration(final CallSiteDescriptor desc) { 392 return isFlag(desc, CALLSITE_DECLARE); 393 } 394 395 /** 396 * Returns true if {@code flags} has the {@link #CALLSITE_STRICT} bit set. 397 * @param flags the flags 398 * @return true if the flag is set, false otherwise. 399 */ 400 public static boolean isStrictFlag(final int flags) { 401 return (flags & CALLSITE_STRICT) != 0; 402 } 403 404 /** 405 * Returns true if {@code flags} has the {@link #CALLSITE_SCOPE} bit set. 406 * @param flags the flags 407 * @return true if the flag is set, false otherwise. 408 */ 409 public static boolean isScopeFlag(final int flags) { 410 return (flags & CALLSITE_SCOPE) != 0; 411 } 412 413 /** 414 * Get a program point from a descriptor (must be optimistic) 415 * @param desc descriptor 416 * @return program point 417 */ 418 public static int getProgramPoint(final CallSiteDescriptor desc) { 419 assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc; 420 return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT; 421 } 422 423 boolean isProfile() { 424 return isFlag(CALLSITE_PROFILE); 425 } 426 427 boolean isTrace() { 428 return isFlag(CALLSITE_TRACE); 429 } 430 431 boolean isTraceMisses() { 432 return isFlag(CALLSITE_TRACE_MISSES); 433 } 434 435 boolean isTraceEnterExit() { 436 return isFlag(CALLSITE_TRACE_ENTEREXIT); 437 } 438 439 boolean isTraceObjects() { 440 return isFlag(CALLSITE_TRACE_VALUES); 441 } 442 443 boolean isOptimistic() { 444 return isFlag(CALLSITE_OPTIMISTIC); 445 } 446 447 @Override 448 public CallSiteDescriptor changeMethodTypeInternal(final MethodType newMethodType) { 449 return get(getLookupPrivileged(), getName(), operator, operand, newMethodType, flags); 450 } 451} 452