Fri, 16 Aug 2013 18:51:53 +0200
8023017: SUB missing for widest op == number for BinaryNode
Reviewed-by: sundar, jlaskey
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.objects;
28 import static jdk.nashorn.internal.lookup.Lookup.MH;
29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.invoke.MethodHandles;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.BitSet;
37 import jdk.nashorn.internal.runtime.AccessorProperty;
38 import jdk.nashorn.internal.runtime.Property;
39 import jdk.nashorn.internal.runtime.PropertyDescriptor;
40 import jdk.nashorn.internal.runtime.PropertyMap;
41 import jdk.nashorn.internal.runtime.ScriptFunction;
42 import jdk.nashorn.internal.runtime.ScriptObject;
43 import jdk.nashorn.internal.runtime.ScriptRuntime;
44 import jdk.nashorn.internal.runtime.arrays.ArrayData;
45 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
47 /**
48 * ECMA 10.6 Arguments Object.
49 *
50 * Arguments object used for non-strict mode functions. For strict mode, we use
51 * a different implementation (@see NativeStrictArguments). In non-strict mode,
52 * named argument access and index argument access (arguments[i]) are linked.
53 * Modifications reflect on each other access -- till arguments indexed element
54 * is deleted. After delete, there is no link between named access and indexed
55 * access for that deleted index alone.
56 */
57 public final class NativeArguments extends ScriptObject {
59 private static final MethodHandle G$LENGTH = findOwnMH("G$length", Object.class, Object.class);
60 private static final MethodHandle S$LENGTH = findOwnMH("S$length", void.class, Object.class, Object.class);
61 private static final MethodHandle G$CALLEE = findOwnMH("G$callee", Object.class, Object.class);
62 private static final MethodHandle S$CALLEE = findOwnMH("S$callee", void.class, Object.class, Object.class);
64 private static final PropertyMap map$;
66 static {
67 final ArrayList<Property> properties = new ArrayList<>(2);
68 properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH));
69 properties.add(AccessorProperty.create("callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE));
70 map$ = PropertyMap.newMap(properties).setIsShared();
71 }
73 static PropertyMap getInitialMap() {
74 return map$;
75 }
77 private Object length;
78 private Object callee;
79 private final int numMapped;
80 private final int numParams;
82 // These are lazily initialized when delete is invoked on a mapped arg or an unmapped argument is set.
83 private ArrayData unmappedArgs;
84 private BitSet deleted;
86 NativeArguments(final Object[] arguments, final Object callee, final int numParams, final ScriptObject proto, final PropertyMap map) {
87 super(proto, map);
88 setIsArguments();
89 setArray(ArrayData.allocate(arguments));
90 this.length = arguments.length;
91 this.callee = callee;
92 this.numMapped = Math.min(numParams, arguments.length);
93 this.numParams = numParams;
94 }
96 @Override
97 public String getClassName() {
98 return "Arguments";
99 }
101 /**
102 * getArgument is used for named argument access.
103 */
104 @Override
105 public Object getArgument(final int key) {
106 assert key >= 0 && key < numParams : "invalid argument index";
107 return isMapped(key) ? getArray().getObject(key) : getUnmappedArg(key);
108 }
110 /**
111 * setArgument is used for named argument set.
112 */
113 @Override
114 public void setArgument(final int key, final Object value) {
115 assert key >= 0 && key < numParams : "invalid argument index";
116 if (isMapped(key)) {
117 setArray(getArray().set(key, value, false));
118 } else {
119 setUnmappedArg(key, value);
120 }
121 }
123 @Override
124 public boolean delete(final int key, final boolean strict) {
125 final int index = ArrayIndex.getArrayIndex(key);
126 return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
127 }
129 @Override
130 public boolean delete(final long key, final boolean strict) {
131 final int index = ArrayIndex.getArrayIndex(key);
132 return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
133 }
135 @Override
136 public boolean delete(final double key, final boolean strict) {
137 final int index = ArrayIndex.getArrayIndex(key);
138 return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
139 }
141 @Override
142 public boolean delete(final Object key, final boolean strict) {
143 final int index = ArrayIndex.getArrayIndex(key);
144 return isMapped(index) ? deleteMapped(index, strict) : super.delete(key, strict);
145 }
147 /**
148 * ECMA 15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw ) as specialized in
149 * ECMA 10.6 for Arguments object.
150 */
151 @Override
152 public boolean defineOwnProperty(final String key, final Object propertyDesc, final boolean reject) {
153 final int index = ArrayIndex.getArrayIndex(key);
154 if (index >= 0) {
155 final boolean isMapped = isMapped(index);
156 final Object oldValue = isMapped ? getArray().getObject(index) : null;
158 if (!super.defineOwnProperty(key, propertyDesc, false)) {
159 if (reject) {
160 throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
161 }
162 return false;
163 }
165 if (isMapped) {
166 // When mapped argument is redefined, if new descriptor is accessor property
167 // or data-non-writable property, we have to "unmap" (unlink).
168 final PropertyDescriptor desc = toPropertyDescriptor(Global.instance(), propertyDesc);
169 if (desc.type() == PropertyDescriptor.ACCESSOR) {
170 setDeleted(index, oldValue);
171 } else if (desc.has(PropertyDescriptor.WRITABLE) && !desc.isWritable()) {
172 // delete and set value from new descriptor if it has one, otherwise use old value
173 setDeleted(index, desc.has(PropertyDescriptor.VALUE) ? desc.getValue() : oldValue);
174 } else if (desc.has(PropertyDescriptor.VALUE)) {
175 setArray(getArray().set(index, desc.getValue(), false));
176 }
177 }
179 return true;
180 }
182 return super.defineOwnProperty(key, propertyDesc, reject);
183 }
185 // Internals below this point
187 // We track deletions using a bit set (delete arguments[index])
188 private boolean isDeleted(final int index) {
189 return deleted != null && deleted.get(index);
190 }
192 private void setDeleted(final int index, final Object unmappedValue) {
193 if (deleted == null) {
194 deleted = new BitSet(numMapped);
195 }
196 deleted.set(index, true);
197 setUnmappedArg(index, unmappedValue);
198 }
200 private boolean deleteMapped(final int index, final boolean strict) {
201 final Object value = getArray().getObject(index);
202 final boolean success = super.delete(index, strict);
203 if (success) {
204 setDeleted(index, value);
205 }
206 return success;
207 }
209 private Object getUnmappedArg(final int key) {
210 assert key >= 0 && key < numParams;
211 return unmappedArgs == null ? UNDEFINED : unmappedArgs.getObject(key);
212 }
214 private void setUnmappedArg(final int key, final Object value) {
215 assert key >= 0 && key < numParams;
216 if (unmappedArgs == null) {
217 /*
218 * Declared number of parameters may be more or less than the actual passed
219 * runtime arguments count. We need to truncate or extend with undefined values.
220 *
221 * Example:
222 *
223 * // less declared params
224 * (function (x) { print(arguments); })(20, 44);
225 *
226 * // more declared params
227 * (function (x, y) { print(arguments); })(3);
228 */
229 final Object[] newValues = new Object[numParams];
230 System.arraycopy(getArray().asObjectArray(), 0, newValues, 0, numMapped);
231 if (numMapped < numParams) {
232 Arrays.fill(newValues, numMapped, numParams, UNDEFINED);
233 }
234 this.unmappedArgs = ArrayData.allocate(newValues);
235 }
236 // Set value of argument
237 unmappedArgs = unmappedArgs.set(key, value, false);
238 }
240 /**
241 * Are arguments[index] and corresponding named parameter linked?
242 *
243 * In non-strict mode, arguments[index] and corresponding named param are "linked" or "mapped"
244 * if the argument is provided by the caller. Modifications are tacked b/w each other - until
245 * (delete arguments[index]) is used. Once deleted, the corresponding arg is no longer 'mapped'.
246 * Please note that delete can happen only through the arguments array - named param can not
247 * be deleted. (delete is one-way).
248 */
249 private boolean isMapped(final int index) {
250 // in mapped named args and not marked as "deleted"
251 return index >= 0 && index < numMapped && !isDeleted(index);
252 }
254 /**
255 * Factory to create correct Arguments object based on strict mode.
256 *
257 * @param arguments the actual arguments array passed
258 * @param callee the callee function that uses arguments object
259 * @param numParams the number of declared (named) function parameters
260 * @return Arguments Object
261 */
262 public static ScriptObject allocate(final Object[] arguments, final ScriptFunction callee, final int numParams) {
263 // Strict functions won't always have a callee for arguments, and will pass null instead.
264 final boolean isStrict = callee == null || callee.isStrict();
265 final Global global = Global.instance();
266 final ScriptObject proto = global.getObjectPrototype();
267 if (isStrict) {
268 return new NativeStrictArguments(arguments, numParams, proto, global.getStrictArgumentsMap());
269 }
270 return new NativeArguments(arguments, callee, numParams, proto, global.getArgumentsMap());
271 }
273 /**
274 * Length getter
275 * @param self self reference
276 * @return length property value
277 */
278 public static Object G$length(final Object self) {
279 if (self instanceof NativeArguments) {
280 return ((NativeArguments)self).getArgumentsLength();
281 }
283 return 0;
284 }
286 /**
287 * Length setter
288 * @param self self reference
289 * @param value value for length property
290 */
291 public static void S$length(final Object self, final Object value) {
292 if (self instanceof NativeArguments) {
293 ((NativeArguments)self).setArgumentsLength(value);
294 }
295 }
297 /**
298 * Callee getter
299 * @param self self reference
300 * @return value for callee property
301 */
302 public static Object G$callee(final Object self) {
303 if (self instanceof NativeArguments) {
304 return ((NativeArguments)self).getCallee();
305 }
306 return UNDEFINED;
307 }
309 /**
310 * Callee setter
311 * @param self self reference
312 * @param value value for callee property
313 */
314 public static void S$callee(final Object self, final Object value) {
315 if (self instanceof NativeArguments) {
316 ((NativeArguments)self).setCallee(value);
317 }
318 }
320 @Override
321 public Object getLength() {
322 return length;
323 }
325 private Object getArgumentsLength() {
326 return length;
327 }
329 private void setArgumentsLength(final Object length) {
330 this.length = length;
331 }
333 private Object getCallee() {
334 return callee;
335 }
337 private void setCallee(final Object callee) {
338 this.callee = callee;
339 }
341 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
342 return MH.findStatic(MethodHandles.lookup(), NativeArguments.class, name, MH.type(rtype, types));
343 }
344 }