NashornCallSiteDescriptor.java revision 953:221a84ef44c0
11573Srgrimes/* 21573Srgrimes * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 31573Srgrimes * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 41573Srgrimes * 51573Srgrimes * This code is free software; you can redistribute it and/or modify it 61573Srgrimes * under the terms of the GNU General Public License version 2 only, as 71573Srgrimes * published by the Free Software Foundation. Oracle designates this 81573Srgrimes * particular file as subject to the "Classpath" exception as provided 91573Srgrimes * by Oracle in the LICENSE file that accompanied this code. 101573Srgrimes * 111573Srgrimes * This code is distributed in the hope that it will be useful, but WITHOUT 121573Srgrimes * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 131573Srgrimes * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 141573Srgrimes * version 2 for more details (a copy is included in the LICENSE file that 151573Srgrimes * accompanied this code). 161573Srgrimes * 171573Srgrimes * You should have received a copy of the GNU General Public License version 181573Srgrimes * 2 along with this work; if not, write to the Free Software Foundation, 191573Srgrimes * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 201573Srgrimes * 211573Srgrimes * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 221573Srgrimes * or visit www.oracle.com if you need additional information or have any 231573Srgrimes * questions. 241573Srgrimes */ 251573Srgrimes 261573Srgrimespackage jdk.nashorn.internal.runtime.linker; 271573Srgrimes 281573Srgrimesimport java.lang.invoke.MethodHandles; 291573Srgrimesimport java.lang.invoke.MethodHandles.Lookup; 301573Srgrimesimport java.lang.invoke.MethodType; 311573Srgrimesimport java.util.concurrent.ConcurrentHashMap; 321573Srgrimesimport java.util.concurrent.ConcurrentMap; 3392889Sobrienimport jdk.internal.dynalink.CallSiteDescriptor; 3492889Sobrienimport jdk.internal.dynalink.support.AbstractCallSiteDescriptor; 351573Srgrimesimport jdk.internal.dynalink.support.CallSiteDescriptorFactory; 36211276Sumeimport jdk.nashorn.internal.ir.debug.NashornTextifier; 371573Srgrimes 38158115Sume/** 39145279Sume * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that 40158115Sume * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also 41158115Sume * we're storing flags in an additional primitive field. 42158115Sume */ 43158115Sumepublic final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor { 441573Srgrimes /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a 45158115Sume * property access expression. */ 46158115Sume public static final int CALLSITE_SCOPE = 1 << 0; 47158115Sume /** Flags that the call site is in code that uses ECMAScript strict mode. */ 48158115Sume public static final int CALLSITE_STRICT = 1 << 1; 49158115Sume /** Flags that a property getter or setter call site references a scope variable that is located at a known distance 50158115Sume * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */ 51158115Sume public static final int CALLSITE_FAST_SCOPE = 1 << 2; 52158115Sume /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the 53158115Sume * descriptor, and in that case we have to throw an UnwarrantedOptimismException */ 54158115Sume public static final int CALLSITE_OPTIMISTIC = 1 << 3; 55158115Sume /** Is this really an apply that we try to call as a call? */ 56158115Sume public static final int CALLSITE_APPLY_TO_CALL = 1 << 4; 57158115Sume 58145279Sume /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit 59157779Sume * code where call sites have this flag set. */ 60157779Sume public static final int CALLSITE_PROFILE = 1 << 5; 61145279Sume /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where 621573Srgrimes * call sites have this flag set. */ 63158115Sume public static final int CALLSITE_TRACE = 1 << 6; 64158115Sume /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword 65158115Sume * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 66158115Sume public static final int CALLSITE_TRACE_MISSES = 1 << 7; 67158115Sume /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword 68158115Sume * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */ 69158115Sume public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 8; 70158115Sume /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts 71158115Sume * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites 72158115Sume * have this flag set. */ 73158115Sume public static final int CALLSITE_TRACE_VALUES = 1 << 9; 74158115Sume 75158115Sume //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious 76211276Sume //right now given the program points 77158115Sume 78158115Sume /** 79158115Sume * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point). 80157779Sume * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program 81157779Sume * points we can have. 82158115Sume * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its 83145279Sume * trace/profile settings. 84145279Sume */ 85157779Sume public static final int CALLSITE_PROGRAM_POINT_SHIFT = 10; 86158115Sume 87211276Sume /** 88158115Sume * Maximum program point value. 22 bits should be enough for anyone 89158115Sume */ 90158115Sume public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1; 91211276Sume 92211276Sume /** 93158115Sume * Flag mask to get the program point flags 94158115Sume */ 95158115Sume public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1; 96158115Sume 97145279Sume private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals = 98145279Sume new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() { 99158115Sume @Override 100158115Sume protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) { 101158115Sume return new ConcurrentHashMap<>(); 102158115Sume } 103158115Sume }; 104158115Sume 105158115Sume private final MethodHandles.Lookup lookup; 106158115Sume private final String operator; 107158115Sume private final String operand; 108158115Sume private final MethodType methodType; 109158115Sume private final int flags; 110158115Sume 111158115Sume /** 112158115Sume * Function used by {@link NashornTextifier} to represent call site flags in 113158115Sume * human readable form 114158115Sume * @param flags call site flags 115158115Sume * @return human readable form of this callsite descriptor 116158115Sume */ 117158115Sume public static String toString(final int flags) { 118158115Sume final StringBuilder sb = new StringBuilder(); 119158115Sume if ((flags & CALLSITE_SCOPE) != 0) { 120158115Sume if ((flags & CALLSITE_FAST_SCOPE) != 0) { 121158115Sume sb.append("fastscope "); 122158115Sume } else { 123158115Sume assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope"; 124211276Sume sb.append("scope "); 125211276Sume } 126213453Sume } 127211276Sume if ((flags & CALLSITE_APPLY_TO_CALL) != 0) { 128211276Sume sb.append("apply2call "); 129158115Sume } 130158115Sume if ((flags & CALLSITE_STRICT) != 0) { 1311573Srgrimes sb.append("strict "); 132145279Sume } 1331573Srgrimes return sb.length() == 0 ? "" : " " + sb.toString().trim(); 134145279Sume } 135157779Sume 1361573Srgrimes /** 137145279Sume * Retrieves a Nashorn call site descriptor with the specified values. Since call site descriptors are immutable 138145279Sume * this method is at liberty to retrieve canonicalized instances (although it is not guaranteed it will do so). 139157779Sume * @param lookup the lookup describing the script 140157779Sume * @param name the name at the call site, e.g. {@code "dyn:getProp|getElem|getMethod:color"}. 141145279Sume * @param methodType the method type at the call site 142157779Sume * @param flags Nashorn-specific call site flags 1431573Srgrimes * @return a call site descriptor with the specified values. 144 */ 145 public static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String name, 146 final MethodType methodType, final int flags) { 147 final String[] tokenizedName = CallSiteDescriptorFactory.tokenizeName(name); 148 assert tokenizedName.length == 2 || tokenizedName.length == 3; 149 assert "dyn".equals(tokenizedName[0]); 150 assert tokenizedName[1] != null; 151 // TODO: see if we can move mangling/unmangling into Dynalink 152 return get(lookup, tokenizedName[1], tokenizedName.length == 3 ? tokenizedName[2].intern() : null, 153 methodType, flags); 154 } 155 156 private static NashornCallSiteDescriptor get(final MethodHandles.Lookup lookup, final String operator, final String operand, final MethodType methodType, final int flags) { 157 final NashornCallSiteDescriptor csd = new NashornCallSiteDescriptor(lookup, operator, operand, methodType, flags); 158 // Many of these call site descriptors are identical (e.g. every getter for a property color will be 159 // "dyn:getProp:color(Object)Object", so it makes sense canonicalizing them. 160 final ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> classCanonicals = canonicals.get(lookup.lookupClass()); 161 final NashornCallSiteDescriptor canonical = classCanonicals.putIfAbsent(csd, csd); 162 return canonical != null ? canonical : csd; 163 } 164 165 private NashornCallSiteDescriptor(final MethodHandles.Lookup lookup, final String operator, final String operand, 166 final MethodType methodType, final int flags) { 167 this.lookup = lookup; 168 this.operator = operator; 169 this.operand = operand; 170 this.methodType = methodType; 171 this.flags = flags; 172 } 173 174 @Override 175 public int getNameTokenCount() { 176 return operand == null ? 2 : 3; 177 } 178 179 @Override 180 public String getNameToken(final int i) { 181 switch(i) { 182 case 0: return "dyn"; 183 case 1: return operator; 184 case 2: 185 if(operand != null) { 186 return operand; 187 } 188 break; 189 default: 190 break; 191 } 192 throw new IndexOutOfBoundsException(String.valueOf(i)); 193 } 194 195 @Override 196 public Lookup getLookup() { 197 return lookup; 198 } 199 200 @Override 201 public boolean equals(final CallSiteDescriptor csd) { 202 return super.equals(csd) && flags == getFlags(csd); 203 } 204 205 @Override 206 public MethodType getMethodType() { 207 return methodType; 208 } 209 210 /** 211 * Returns the operator (e.g. {@code "getProp"}) in this call site descriptor's name. Equivalent to 212 * {@code getNameToken(CallSiteDescriptor.OPERATOR)}. The returned operator can be composite. 213 * @return the operator in this call site descriptor's name. 214 */ 215 public String getOperator() { 216 return operator; 217 } 218 219 /** 220 * Returns the first operator in this call site descriptor's name. E.g. if this call site descriptor has a composite 221 * operation {@code "getProp|getMethod|getElem"}, it will return {@code "getProp"}. Nashorn - being a ECMAScript 222 * engine - does not distinguish between property, element, and method namespace; ECMAScript objects just have one 223 * single property namespace for all these, therefore it is largely irrelevant what the composite operation is 224 * structured like; if the first operation can't be satisfied, neither can the others. The first operation is 225 * however sometimes used to slightly alter the semantics; for example, a distinction between {@code "getProp"} and 226 * {@code "getMethod"} being the first operation can translate into whether {@code "__noSuchProperty__"} or 227 * {@code "__noSuchMethod__"} will be executed in case the property is not found. 228 * @return the first operator in this call site descriptor's name. 229 */ 230 public String getFirstOperator() { 231 final int delim = operator.indexOf(CallSiteDescriptor.OPERATOR_DELIMITER); 232 return delim == -1 ? operator : operator.substring(0, delim); 233 } 234 235 /** 236 * Returns the named operand in this descriptor's name. Equivalent to 237 * {@code getNameToken(CallSiteDescriptor.NAME_OPERAND)}. E.g. for operation {@code "dyn:getProp:color"}, returns 238 * {@code "color"}. For call sites without named operands (e.g. {@code "dyn:new"}) returns null. 239 * @return the named operand in this descriptor's name. 240 */ 241 public String getOperand() { 242 return operand; 243 } 244 245 /** 246 * Returns the Nashorn-specific flags for this call site descriptor. 247 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 248 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 249 * generated outside of Nashorn. 250 * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site 251 * descriptor. 252 */ 253 private static int getFlags(final CallSiteDescriptor desc) { 254 return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0; 255 } 256 257 /** 258 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 259 * @param flag the tested flag 260 * @return true if the flag is set, false otherwise 261 */ 262 private boolean isFlag(final int flag) { 263 return (flags & flag) != 0; 264 } 265 266 /** 267 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class. 268 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 269 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 270 * generated outside of Nashorn. 271 * @param flag the tested flag 272 * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site 273 * descriptor). 274 */ 275 private static boolean isFlag(final CallSiteDescriptor desc, final int flag) { 276 return (getFlags(desc) & flag) != 0; 277 } 278 279 /** 280 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_SCOPE} flag set. 281 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 282 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 283 * generated outside of Nashorn. 284 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 285 */ 286 public static boolean isScope(final CallSiteDescriptor desc) { 287 return isFlag(desc, CALLSITE_SCOPE); 288 } 289 290 /** 291 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_FAST_SCOPE} flag set. 292 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 293 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 294 * generated outside of Nashorn. 295 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 296 */ 297 public static boolean isFastScope(final CallSiteDescriptor desc) { 298 return isFlag(desc, CALLSITE_FAST_SCOPE); 299 } 300 301 /** 302 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_STRICT} flag set. 303 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a 304 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code 305 * generated outside of Nashorn. 306 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise. 307 */ 308 public static boolean isStrict(final CallSiteDescriptor desc) { 309 return isFlag(desc, CALLSITE_STRICT); 310 } 311 312 /** 313 * Returns true if this is an apply call that we try to call as 314 * a "call" 315 * @param desc descriptor 316 * @return true if apply to call 317 */ 318 public static boolean isApplyToCall(final CallSiteDescriptor desc) { 319 return isFlag(desc, CALLSITE_APPLY_TO_CALL); 320 } 321 322 /** 323 * Is this an optimistic call site 324 * @param desc descriptor 325 * @return true if optimistic 326 */ 327 public static boolean isOptimistic(final CallSiteDescriptor desc) { 328 return isFlag(desc, CALLSITE_OPTIMISTIC); 329 } 330 331 /** 332 * Get a program point from a descriptor (must be optimistic) 333 * @param desc descriptor 334 * @return program point 335 */ 336 public static int getProgramPoint(final CallSiteDescriptor desc) { 337 assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc; 338 return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT; 339 } 340 341 boolean isProfile() { 342 return isFlag(CALLSITE_PROFILE); 343 } 344 345 boolean isTrace() { 346 return isFlag(CALLSITE_TRACE); 347 } 348 349 boolean isTraceMisses() { 350 return isFlag(CALLSITE_TRACE_MISSES); 351 } 352 353 boolean isTraceEnterExit() { 354 return isFlag(CALLSITE_TRACE_ENTEREXIT); 355 } 356 357 boolean isTraceObjects() { 358 return isFlag(CALLSITE_TRACE_VALUES); 359 } 360 361 boolean isOptimistic() { 362 return isFlag(CALLSITE_OPTIMISTIC); 363 } 364 365 @Override 366 public CallSiteDescriptor changeMethodType(final MethodType newMethodType) { 367 return get(getLookup(), operator, operand, newMethodType, flags); 368 } 369 370} 371