Mon, 22 Sep 2014 13:28:28 +0200
8047764: Indexed or polymorphic set on global affects Object.prototype
Reviewed-by: lagergren, attila
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 */
26 package jdk.nashorn.internal.runtime.linker;
28 import java.lang.invoke.MethodHandles;
29 import java.lang.invoke.MethodHandles.Lookup;
30 import java.lang.invoke.MethodType;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ConcurrentMap;
33 import jdk.internal.dynalink.CallSiteDescriptor;
34 import jdk.internal.dynalink.support.AbstractCallSiteDescriptor;
35 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
36 import jdk.nashorn.internal.ir.debug.NashornTextifier;
38 /**
39 * Nashorn-specific implementation of Dynalink's {@link CallSiteDescriptor}. The reason we have our own subclass is that
40 * we can have a more compact representation, as we know that we're always only using {@code "dyn:*"} operations; also
41 * we're storing flags in an additional primitive field.
42 */
43 public final class NashornCallSiteDescriptor extends AbstractCallSiteDescriptor {
44 /** Flags that the call site references a scope variable (it's an identifier reference or a var declaration, not a
45 * property access expression. */
46 public static final int CALLSITE_SCOPE = 1 << 0;
47 /** Flags that the call site is in code that uses ECMAScript strict mode. */
48 public static final int CALLSITE_STRICT = 1 << 1;
49 /** Flags that a property getter or setter call site references a scope variable that is located at a known distance
50 * in the scope chain. Such getters and setters can often be linked more optimally using these assumptions. */
51 public static final int CALLSITE_FAST_SCOPE = 1 << 2;
52 /** Flags that a callsite type is optimistic, i.e. we might get back a wider return value than encoded in the
53 * descriptor, and in that case we have to throw an UnwarrantedOptimismException */
54 public static final int CALLSITE_OPTIMISTIC = 1 << 3;
55 /** Is this really an apply that we try to call as a call? */
56 public static final int CALLSITE_APPLY_TO_CALL = 1 << 4;
57 /** Does this a callsite for a variable declaration? */
58 public static final int CALLSITE_DECLARE = 1 << 5;
60 /** Flags that the call site is profiled; Contexts that have {@code "profile.callsites"} boolean property set emit
61 * code where call sites have this flag set. */
62 public static final int CALLSITE_PROFILE = 1 << 6;
63 /** Flags that the call site is traced; Contexts that have {@code "trace.callsites"} property set emit code where
64 * call sites have this flag set. */
65 public static final int CALLSITE_TRACE = 1 << 7;
66 /** Flags that the call site linkage miss (and thus, relinking) is traced; Contexts that have the keyword
67 * {@code "miss"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
68 public static final int CALLSITE_TRACE_MISSES = 1 << 8;
69 /** Flags that entry/exit to/from the method linked at call site are traced; Contexts that have the keyword
70 * {@code "enterexit"} in their {@code "trace.callsites"} property emit code where call sites have this flag set. */
71 public static final int CALLSITE_TRACE_ENTEREXIT = 1 << 9;
72 /** Flags that values passed as arguments to and returned from the method linked at call site are traced; Contexts
73 * that have the keyword {@code "values"} in their {@code "trace.callsites"} property emit code where call sites
74 * have this flag set. */
75 public static final int CALLSITE_TRACE_VALUES = 1 << 10;
77 //we could have more tracing flags here, for example CALLSITE_TRACE_SCOPE, but bits are a bit precious
78 //right now given the program points
80 /**
81 * Number of bits the program point is shifted to the left in the flags (lowest bit containing a program point).
82 * Always one larger than the largest flag shift. Note that introducing a new flag halves the number of program
83 * points we can have.
84 * TODO: rethink if we need the various profile/trace flags or the linker can use the Context instead to query its
85 * trace/profile settings.
86 */
87 public static final int CALLSITE_PROGRAM_POINT_SHIFT = 11;
89 /**
90 * Maximum program point value. 21 bits should be enough for anyone
91 */
92 public static final int MAX_PROGRAM_POINT_VALUE = (1 << 32 - CALLSITE_PROGRAM_POINT_SHIFT) - 1;
94 /**
95 * Flag mask to get the program point flags
96 */
97 public static final int FLAGS_MASK = (1 << CALLSITE_PROGRAM_POINT_SHIFT) - 1;
99 private static final ClassValue<ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor>> canonicals =
100 new ClassValue<ConcurrentMap<NashornCallSiteDescriptor,NashornCallSiteDescriptor>>() {
101 @Override
102 protected ConcurrentMap<NashornCallSiteDescriptor, NashornCallSiteDescriptor> computeValue(final Class<?> type) {
103 return new ConcurrentHashMap<>();
104 }
105 };
107 private final MethodHandles.Lookup lookup;
108 private final String operator;
109 private final String operand;
110 private final MethodType methodType;
111 private final int flags;
113 /**
114 * Function used by {@link NashornTextifier} to represent call site flags in
115 * human readable form
116 * @param flags call site flags
117 * @return human readable form of this callsite descriptor
118 */
119 public static String toString(final int flags) {
120 final StringBuilder sb = new StringBuilder();
121 if ((flags & CALLSITE_SCOPE) != 0) {
122 if ((flags & CALLSITE_FAST_SCOPE) != 0) {
123 sb.append("fastscope ");
124 } else {
125 assert (flags & CALLSITE_FAST_SCOPE) == 0 : "can't be fastscope without scope";
126 sb.append("scope ");
127 }
128 if ((flags & CALLSITE_DECLARE) != 0) {
129 sb.append("declare ");
130 }
131 }
132 if ((flags & CALLSITE_APPLY_TO_CALL) != 0) {
133 sb.append("apply2call ");
134 }
135 if ((flags & CALLSITE_STRICT) != 0) {
136 sb.append("strict ");
137 }
138 return sb.length() == 0 ? "" : " " + sb.toString().trim();
139 }
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 = CallSiteDescriptorFactory.tokenizeName(name);
153 assert tokenizedName.length == 2 || tokenizedName.length == 3;
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 }
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 }
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 }
179 @Override
180 public int getNameTokenCount() {
181 return operand == null ? 2 : 3;
182 }
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 }
200 @Override
201 public Lookup getLookup() {
202 return lookup;
203 }
205 @Override
206 public boolean equals(final CallSiteDescriptor csd) {
207 return super.equals(csd) && flags == getFlags(csd);
208 }
210 @Override
211 public MethodType getMethodType() {
212 return methodType;
213 }
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 }
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 }
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 }
250 /**
251 * Returns the Nashorn-specific flags for this call site descriptor.
252 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
253 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
254 * generated outside of Nashorn.
255 * @return the Nashorn-specific flags for the call site, or 0 if the passed descriptor is not a Nashorn call site
256 * descriptor.
257 */
258 public static int getFlags(final CallSiteDescriptor desc) {
259 return desc instanceof NashornCallSiteDescriptor ? ((NashornCallSiteDescriptor)desc).flags : 0;
260 }
262 /**
263 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
264 * @param flag the tested flag
265 * @return true if the flag is set, false otherwise
266 */
267 private boolean isFlag(final int flag) {
268 return (flags & flag) != 0;
269 }
271 /**
272 * Returns true if this descriptor has the specified flag set, see {@code CALLSITE_*} constants in this class.
273 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
274 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
275 * generated outside of Nashorn.
276 * @param flag the tested flag
277 * @return true if the flag is set, false otherwise (it will be false if the decriptor is not a Nashorn call site
278 * descriptor).
279 */
280 private static boolean isFlag(final CallSiteDescriptor desc, final int flag) {
281 return (getFlags(desc) & flag) != 0;
282 }
284 /**
285 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_SCOPE} flag set.
286 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
287 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
288 * generated outside of Nashorn.
289 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
290 */
291 public static boolean isScope(final CallSiteDescriptor desc) {
292 return isFlag(desc, CALLSITE_SCOPE);
293 }
295 /**
296 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_FAST_SCOPE} flag set.
297 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
298 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
299 * generated outside of Nashorn.
300 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
301 */
302 public static boolean isFastScope(final CallSiteDescriptor desc) {
303 return isFlag(desc, CALLSITE_FAST_SCOPE);
304 }
306 /**
307 * Returns true if this descriptor is a Nashorn call site descriptor and has the {@link #CALLSITE_STRICT} flag set.
308 * @param desc the descriptor. It can be any kind of a call site descriptor, not necessarily a
309 * {@code NashornCallSiteDescriptor}. This allows for graceful interoperability when linking Nashorn with code
310 * generated outside of Nashorn.
311 * @return true if the descriptor is a Nashorn call site descriptor, and the flag is set, false otherwise.
312 */
313 public static boolean isStrict(final CallSiteDescriptor desc) {
314 return isFlag(desc, CALLSITE_STRICT);
315 }
317 /**
318 * Returns true if this is an apply call that we try to call as
319 * a "call"
320 * @param desc descriptor
321 * @return true if apply to call
322 */
323 public static boolean isApplyToCall(final CallSiteDescriptor desc) {
324 return isFlag(desc, CALLSITE_APPLY_TO_CALL);
325 }
327 /**
328 * Is this an optimistic call site
329 * @param desc descriptor
330 * @return true if optimistic
331 */
332 public static boolean isOptimistic(final CallSiteDescriptor desc) {
333 return isFlag(desc, CALLSITE_OPTIMISTIC);
334 }
336 /**
337 * Does this callsite contain a declaration for its target?
338 * @param desc descriptor
339 * @return true if contains declaration
340 */
341 public static boolean isDeclaration(final CallSiteDescriptor desc) {
342 return isFlag(desc, CALLSITE_DECLARE);
343 }
345 /**
346 * Returns true if {@code flags} has the {@link #CALLSITE_STRICT} bit set.
347 * @param flags the flags
348 * @return true if the flag is set, false otherwise.
349 */
350 public static boolean isStrictFlag(final int flags) {
351 return (flags & CALLSITE_STRICT) != 0;
352 }
354 /**
355 * Returns true if {@code flags} has the {@link #CALLSITE_SCOPE} bit set.
356 * @param flags the flags
357 * @return true if the flag is set, false otherwise.
358 */
359 public static boolean isScopeFlag(final int flags) {
360 return (flags & CALLSITE_SCOPE) != 0;
361 }
363 /**
364 * Get a program point from a descriptor (must be optimistic)
365 * @param desc descriptor
366 * @return program point
367 */
368 public static int getProgramPoint(final CallSiteDescriptor desc) {
369 assert isOptimistic(desc) : "program point requested from non-optimistic descriptor " + desc;
370 return getFlags(desc) >> CALLSITE_PROGRAM_POINT_SHIFT;
371 }
373 boolean isProfile() {
374 return isFlag(CALLSITE_PROFILE);
375 }
377 boolean isTrace() {
378 return isFlag(CALLSITE_TRACE);
379 }
381 boolean isTraceMisses() {
382 return isFlag(CALLSITE_TRACE_MISSES);
383 }
385 boolean isTraceEnterExit() {
386 return isFlag(CALLSITE_TRACE_ENTEREXIT);
387 }
389 boolean isTraceObjects() {
390 return isFlag(CALLSITE_TRACE_VALUES);
391 }
393 boolean isOptimistic() {
394 return isFlag(CALLSITE_OPTIMISTIC);
395 }
397 @Override
398 public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
399 return get(getLookup(), operator, operand, newMethodType, flags);
400 }
402 }