1 : /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
2 : /* vim: set ts=40 sw=4 et tw=99: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla WebGL impl
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation
20 : * Portions created by the Initial Developer are Copyright (C) 2009
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Vladimir Vukicevic <vladimir@pobox.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include <string.h>
41 :
42 : #include "mozilla/Util.h"
43 :
44 : #include "jstypes.h"
45 : #include "jsutil.h"
46 : #include "jshash.h"
47 : #include "jsprf.h"
48 : #include "jsapi.h"
49 : #include "jsarray.h"
50 : #include "jsatom.h"
51 : #include "jsbool.h"
52 : #include "jscntxt.h"
53 : #include "jsversion.h"
54 : #include "jsgc.h"
55 : #include "jsgcmark.h"
56 : #include "jsinterp.h"
57 : #include "jslock.h"
58 : #include "jsnum.h"
59 : #include "jsobj.h"
60 : #include "jstypedarray.h"
61 :
62 : #include "vm/GlobalObject.h"
63 :
64 : #include "jsatominlines.h"
65 : #include "jsinferinlines.h"
66 : #include "jsobjinlines.h"
67 : #include "jstypedarrayinlines.h"
68 :
69 : #include "vm/MethodGuard-inl.h"
70 :
71 : using namespace mozilla;
72 : using namespace js;
73 : using namespace js::gc;
74 : using namespace js::types;
75 :
76 : /*
77 : * Allocate array buffers with the maximum number of fixed slots marked as
78 : * reserved, so that the fixed slots may be used for the buffer's contents.
79 : * The last fixed slot is kept for the object's private data.
80 : */
81 : static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1;
82 :
83 : static bool
84 5050 : ValueIsLength(JSContext *cx, const Value &v, uint32_t *len)
85 : {
86 5050 : if (v.isInt32()) {
87 4368 : int32_t i = v.toInt32();
88 4368 : if (i < 0)
89 0 : return false;
90 4368 : *len = i;
91 4368 : return true;
92 : }
93 :
94 682 : if (v.isDouble()) {
95 0 : double d = v.toDouble();
96 0 : if (JSDOUBLE_IS_NaN(d))
97 0 : return false;
98 :
99 0 : uint32_t length = uint32_t(d);
100 0 : if (d != double(length))
101 0 : return false;
102 :
103 0 : *len = length;
104 0 : return true;
105 : }
106 :
107 682 : return false;
108 : }
109 :
110 : /*
111 : * Convert |v| to an array index for an array of length |length| per
112 : * the Typed Array Specification section 7.0, |subarray|. If successful,
113 : * the output value is in the range [0, length).
114 : */
115 : static bool
116 324 : ToClampedIndex(JSContext *cx, const Value &v, int32_t length, int32_t *out)
117 : {
118 324 : if (!ToInt32(cx, v, out))
119 0 : return false;
120 324 : if (*out < 0) {
121 126 : *out += length;
122 126 : if (*out < 0)
123 36 : *out = 0;
124 198 : } else if (*out > length) {
125 27 : *out = length;
126 : }
127 324 : return true;
128 : }
129 :
130 : /*
131 : * ArrayBuffer
132 : *
133 : * This class holds the underlying raw buffer that the TypedArray classes
134 : * access. It can be created explicitly and passed to a TypedArray, or
135 : * can be created implicitly by constructing a TypedArray with a size.
136 : */
137 :
138 : /**
139 : * Walks up the prototype chain to find the actual ArrayBuffer data.
140 : * This MAY return NULL. Callers should always use js_IsArrayBuffer()
141 : * first.
142 : */
143 : JSObject *
144 633 : ArrayBuffer::getArrayBuffer(JSObject *obj)
145 : {
146 1266 : while (obj && !js_IsArrayBuffer(obj))
147 0 : obj = obj->getProto();
148 633 : return obj;
149 : }
150 :
151 : JSBool
152 0 : ArrayBuffer::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
153 : {
154 0 : JSObject *arrayBuffer = getArrayBuffer(obj);
155 0 : if (!arrayBuffer) {
156 0 : vp->setInt32(0);
157 0 : return true;
158 : }
159 0 : vp->setInt32(int32_t(arrayBuffer->arrayBufferByteLength()));
160 0 : return true;
161 : }
162 :
163 : JSBool
164 207 : ArrayBuffer::fun_slice(JSContext *cx, unsigned argc, Value *vp)
165 : {
166 207 : CallArgs args = CallArgsFromVp(argc, vp);
167 :
168 : bool ok;
169 207 : JSObject *obj = NonGenericMethodGuard(cx, args, fun_slice, &ArrayBufferClass, &ok);
170 207 : if (!obj)
171 0 : return ok;
172 :
173 207 : JSObject *arrayBuffer = getArrayBuffer(obj);
174 207 : if (!arrayBuffer)
175 0 : return true;
176 :
177 : // these are the default values
178 207 : int32_t length = int32_t(arrayBuffer->arrayBufferByteLength());
179 207 : int32_t begin = 0, end = length;
180 :
181 207 : if (args.length() > 0) {
182 207 : if (!ToClampedIndex(cx, args[0], length, &begin))
183 0 : return false;
184 :
185 207 : if (args.length() > 1) {
186 99 : if (!ToClampedIndex(cx, args[1], length, &end))
187 0 : return false;
188 : }
189 : }
190 :
191 207 : if (begin > end)
192 18 : begin = end;
193 :
194 207 : JSObject *nobj = createSlice(cx, arrayBuffer, begin, end);
195 207 : if (!nobj)
196 0 : return false;
197 207 : args.rval().setObject(*nobj);
198 207 : return true;
199 : }
200 :
201 : /*
202 : * new ArrayBuffer(byteLength)
203 : */
204 : JSBool
205 155 : ArrayBuffer::class_constructor(JSContext *cx, unsigned argc, Value *vp)
206 : {
207 155 : int32_t nbytes = 0;
208 155 : if (argc > 0 && !ToInt32(cx, vp[2], &nbytes))
209 0 : return false;
210 :
211 155 : JSObject *bufobj = create(cx, nbytes);
212 155 : if (!bufobj)
213 9 : return false;
214 146 : vp->setObject(*bufobj);
215 146 : return true;
216 : }
217 :
218 : bool
219 5134 : JSObject::allocateArrayBufferSlots(JSContext *cx, uint32_t size, uint8_t *contents)
220 : {
221 : /*
222 : * ArrayBuffer objects delegate added properties to another JSObject, so
223 : * their internal layout can use the object's fixed slots for storage.
224 : * Set up the object to look like an array with an elements header.
225 : */
226 5134 : JS_ASSERT(isArrayBuffer() && !hasDynamicSlots() && !hasDynamicElements());
227 :
228 5134 : size_t usableSlots = ARRAYBUFFER_RESERVED_SLOTS - ObjectElements::VALUES_PER_HEADER;
229 :
230 5134 : if (size > sizeof(Value) * usableSlots) {
231 228 : ObjectElements *newheader = (ObjectElements *)cx->calloc_(size + sizeof(ObjectElements));
232 228 : if (!newheader)
233 9 : return false;
234 219 : elements = newheader->elements();
235 219 : if (contents)
236 0 : memcpy(elements, contents, size);
237 : } else {
238 4906 : elements = fixedElements();
239 4906 : if (contents)
240 207 : memcpy(elements, contents, size);
241 : else
242 4699 : memset(elements, 0, size);
243 : }
244 :
245 5125 : ObjectElements *header = getElementsHeader();
246 :
247 : /*
248 : * Note that |bytes| may not be a multiple of |sizeof(Value)|, so
249 : * |capacity * sizeof(Value)| may underestimate the size by up to
250 : * |sizeof(Value) - 1| bytes.
251 : */
252 5125 : header->capacity = size / sizeof(Value);
253 5125 : header->initializedLength = 0;
254 5125 : header->length = size;
255 5125 : header->unused = 0;
256 :
257 5125 : return true;
258 : }
259 :
260 : static JSObject *
261 225 : DelegateObject(JSContext *cx, JSObject *obj)
262 : {
263 225 : if (!obj->getPrivate()) {
264 27 : JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL);
265 27 : obj->setPrivate(delegate);
266 27 : return delegate;
267 : }
268 198 : return static_cast<JSObject*>(obj->getPrivate());
269 : }
270 :
271 : JSObject *
272 5134 : ArrayBuffer::create(JSContext *cx, int32_t nbytes, uint8_t *contents)
273 : {
274 5134 : JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::slowClass);
275 5134 : if (!obj)
276 0 : return NULL;
277 5134 : JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT16);
278 :
279 5134 : if (nbytes < 0) {
280 : /*
281 : * We're just not going to support arrays that are bigger than what will fit
282 : * as an integer value; if someone actually ever complains (validly), then we
283 : * can fix.
284 : */
285 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_ARRAY_LENGTH);
286 0 : return NULL;
287 : }
288 :
289 5134 : JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
290 :
291 : js::Shape *empty = EmptyShape::getInitialShape(cx, &ArrayBufferClass,
292 : obj->getProto(), obj->getParent(),
293 5134 : gc::FINALIZE_OBJECT16);
294 5134 : if (!empty)
295 0 : return NULL;
296 5134 : obj->setLastPropertyInfallible(empty);
297 :
298 : /*
299 : * The first 8 bytes hold the length.
300 : * The rest of it is a flat data store for the array buffer.
301 : */
302 5134 : if (!obj->allocateArrayBufferSlots(cx, nbytes, contents))
303 9 : return NULL;
304 :
305 5125 : return obj;
306 : }
307 :
308 : JSObject *
309 207 : ArrayBuffer::createSlice(JSContext *cx, JSObject *arrayBuffer, uint32_t begin, uint32_t end)
310 : {
311 207 : JS_ASSERT(arrayBuffer->isArrayBuffer());
312 207 : JS_ASSERT(begin <= arrayBuffer->arrayBufferByteLength());
313 207 : JS_ASSERT(end <= arrayBuffer->arrayBufferByteLength());
314 :
315 207 : JS_ASSERT(begin <= end);
316 207 : uint32_t length = end - begin;
317 :
318 207 : return create(cx, length, arrayBuffer->arrayBufferDataOffset() + begin);
319 : }
320 :
321 0 : ArrayBuffer::~ArrayBuffer()
322 : {
323 0 : }
324 :
325 : void
326 92 : ArrayBuffer::obj_trace(JSTracer *trc, JSObject *obj)
327 : {
328 : /*
329 : * If this object changes, it will get marked via the private data barrier,
330 : * so it's safe to leave it Unbarriered.
331 : */
332 92 : JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
333 92 : if (delegate) {
334 30 : MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
335 30 : obj->setPrivateUnbarriered(delegate);
336 : }
337 92 : }
338 :
339 : static JSProperty * const PROPERTY_FOUND = reinterpret_cast<JSProperty *>(1);
340 :
341 : JSBool
342 0 : ArrayBuffer::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id,
343 : JSObject **objp, JSProperty **propp)
344 : {
345 0 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
346 0 : *propp = PROPERTY_FOUND;
347 0 : *objp = getArrayBuffer(obj);
348 0 : return true;
349 : }
350 :
351 0 : JSObject *delegate = DelegateObject(cx, obj);
352 0 : if (!delegate)
353 0 : return false;
354 :
355 0 : JSBool delegateResult = delegate->lookupGeneric(cx, id, objp, propp);
356 :
357 : /* If false, there was an error, so propagate it.
358 : * Otherwise, if propp is non-null, the property
359 : * was found. Otherwise it was not
360 : * found so look in the prototype chain.
361 : */
362 0 : if (!delegateResult)
363 0 : return false;
364 :
365 0 : if (*propp != NULL) {
366 0 : if (*objp == delegate)
367 0 : *objp = obj;
368 0 : return true;
369 : }
370 :
371 0 : JSObject *proto = obj->getProto();
372 0 : if (!proto) {
373 0 : *objp = NULL;
374 0 : *propp = NULL;
375 0 : return true;
376 : }
377 :
378 0 : return proto->lookupGeneric(cx, id, objp, propp);
379 : }
380 :
381 : JSBool
382 0 : ArrayBuffer::obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name,
383 : JSObject **objp, JSProperty **propp)
384 : {
385 0 : return obj_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
386 : }
387 :
388 : JSBool
389 0 : ArrayBuffer::obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index,
390 : JSObject **objp, JSProperty **propp)
391 : {
392 0 : JSObject *delegate = DelegateObject(cx, obj);
393 0 : if (!delegate)
394 0 : return false;
395 :
396 : /*
397 : * If false, there was an error, so propagate it.
398 : * Otherwise, if propp is non-null, the property
399 : * was found. Otherwise it was not
400 : * found so look in the prototype chain.
401 : */
402 0 : if (!delegate->lookupElement(cx, index, objp, propp))
403 0 : return false;
404 :
405 0 : if (*propp != NULL) {
406 0 : if (*objp == delegate)
407 0 : *objp = obj;
408 0 : return true;
409 : }
410 :
411 0 : if (JSObject *proto = obj->getProto())
412 0 : return proto->lookupElement(cx, index, objp, propp);
413 :
414 0 : *objp = NULL;
415 0 : *propp = NULL;
416 0 : return true;
417 : }
418 :
419 : JSBool
420 0 : ArrayBuffer::obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid,
421 : JSObject **objp, JSProperty **propp)
422 : {
423 0 : return obj_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
424 : }
425 :
426 : JSBool
427 0 : ArrayBuffer::obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v,
428 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
429 : {
430 0 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
431 0 : return true;
432 :
433 0 : JSObject *delegate = DelegateObject(cx, obj);
434 0 : if (!delegate)
435 0 : return false;
436 0 : return js_DefineProperty(cx, delegate, id, v, getter, setter, attrs);
437 : }
438 :
439 : JSBool
440 0 : ArrayBuffer::obj_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v,
441 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
442 : {
443 0 : return obj_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs);
444 : }
445 :
446 : JSBool
447 0 : ArrayBuffer::obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v,
448 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
449 : {
450 0 : JSObject *delegate = DelegateObject(cx, obj);
451 0 : if (!delegate)
452 0 : return false;
453 0 : return js_DefineElement(cx, delegate, index, v, getter, setter, attrs);
454 : }
455 :
456 : JSBool
457 0 : ArrayBuffer::obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v,
458 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
459 : {
460 0 : return obj_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs);
461 : }
462 :
463 : JSBool
464 424 : ArrayBuffer::obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
465 : {
466 424 : obj = getArrayBuffer(obj);
467 424 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
468 208 : vp->setInt32(obj->arrayBufferByteLength());
469 208 : return true;
470 : }
471 :
472 216 : JSObject *delegate = DelegateObject(cx, obj);
473 216 : if (!delegate)
474 0 : return false;
475 216 : return js_GetProperty(cx, delegate, receiver, id, vp);
476 : }
477 :
478 : JSBool
479 0 : ArrayBuffer::obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name,
480 : Value *vp)
481 : {
482 0 : obj = getArrayBuffer(obj);
483 0 : if (name == cx->runtime->atomState.byteLengthAtom) {
484 0 : vp->setInt32(obj->arrayBufferByteLength());
485 0 : return true;
486 : }
487 :
488 0 : JSObject *delegate = DelegateObject(cx, obj);
489 0 : if (!delegate)
490 0 : return false;
491 0 : return js_GetProperty(cx, delegate, receiver, ATOM_TO_JSID(name), vp);
492 : }
493 :
494 : JSBool
495 0 : ArrayBuffer::obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
496 : {
497 0 : JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj));
498 0 : if (!delegate)
499 0 : return false;
500 0 : return js_GetElement(cx, delegate, receiver, index, vp);
501 : }
502 :
503 : JSBool
504 0 : ArrayBuffer::obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver,
505 : uint32_t index, Value *vp, bool *present)
506 : {
507 0 : JSObject *delegate = DelegateObject(cx, getArrayBuffer(obj));
508 0 : if (!delegate)
509 0 : return false;
510 0 : return delegate->getElementIfPresent(cx, receiver, index, vp, present);
511 : }
512 :
513 : JSBool
514 0 : ArrayBuffer::obj_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
515 : {
516 0 : return obj_getGeneric(cx, obj, receiver, SPECIALID_TO_JSID(sid), vp);
517 : }
518 :
519 : JSBool
520 9 : ArrayBuffer::obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
521 : {
522 9 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
523 0 : return true;
524 :
525 9 : if (JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)) {
526 : // setting __proto__ = null
527 : // effectively removes the prototype chain.
528 : // any attempt to set __proto__ on native
529 : // objects after setting them to null makes
530 : // __proto__ just a plain property.
531 : // the following code simulates this behaviour on arrays.
532 : //
533 : // we first attempt to set the prototype on
534 : // the delegate which is a native object
535 : // so that existing code handles the case
536 : // of treating it as special or plain.
537 : // if the delegate's prototype has now changed
538 : // then we change our prototype too.
539 : //
540 : // otherwise __proto__ was a plain property
541 : // and we don't modify our prototype chain
542 : // since obj_getProperty will fetch it as a plain
543 : // property from the delegate.
544 9 : JSObject *delegate = DelegateObject(cx, obj);
545 9 : if (!delegate)
546 0 : return false;
547 :
548 9 : JSObject *oldDelegateProto = delegate->getProto();
549 :
550 9 : if (!js_SetPropertyHelper(cx, delegate, id, 0, vp, strict))
551 0 : return false;
552 :
553 9 : if (delegate->getProto() != oldDelegateProto) {
554 : // actual __proto__ was set and not a plain property called
555 : // __proto__
556 9 : if (!obj->isExtensible()) {
557 9 : obj->reportNotExtensible(cx);
558 9 : return false;
559 : }
560 0 : if (!SetProto(cx, obj, vp->toObjectOrNull(), true)) {
561 : // this can be caused for example by setting x.__proto__ = x
562 : // restore delegate prototype chain
563 0 : SetProto(cx, delegate, oldDelegateProto, true);
564 0 : return false;
565 : }
566 : }
567 0 : return true;
568 : }
569 :
570 0 : JSObject *delegate = DelegateObject(cx, obj);
571 0 : if (!delegate)
572 0 : return false;
573 :
574 0 : return js_SetPropertyHelper(cx, delegate, id, 0, vp, strict);
575 : }
576 :
577 : JSBool
578 0 : ArrayBuffer::obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
579 : {
580 0 : return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
581 : }
582 :
583 : JSBool
584 0 : ArrayBuffer::obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
585 : {
586 0 : JSObject *delegate = DelegateObject(cx, obj);
587 0 : if (!delegate)
588 0 : return false;
589 :
590 0 : return js_SetElementHelper(cx, delegate, index, 0, vp, strict);
591 : }
592 :
593 : JSBool
594 0 : ArrayBuffer::obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
595 : {
596 0 : return obj_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
597 : }
598 :
599 : JSBool
600 0 : ArrayBuffer::obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
601 : {
602 0 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
603 0 : *attrsp = JSPROP_PERMANENT | JSPROP_READONLY;
604 0 : return true;
605 : }
606 :
607 0 : JSObject *delegate = DelegateObject(cx, obj);
608 0 : if (!delegate)
609 0 : return false;
610 0 : return js_GetAttributes(cx, delegate, id, attrsp);
611 : }
612 :
613 : JSBool
614 0 : ArrayBuffer::obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
615 : {
616 0 : return obj_getGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
617 : }
618 :
619 : JSBool
620 0 : ArrayBuffer::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
621 : {
622 0 : JSObject *delegate = DelegateObject(cx, obj);
623 0 : if (!delegate)
624 0 : return false;
625 0 : return js_GetElementAttributes(cx, delegate, index, attrsp);
626 : }
627 :
628 : JSBool
629 0 : ArrayBuffer::obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
630 : {
631 0 : return obj_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
632 : }
633 :
634 : JSBool
635 0 : ArrayBuffer::obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
636 : {
637 0 : if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom)) {
638 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
639 0 : JSMSG_CANT_SET_ARRAY_ATTRS);
640 0 : return false;
641 : }
642 :
643 0 : JSObject *delegate = DelegateObject(cx, obj);
644 0 : if (!delegate)
645 0 : return false;
646 0 : return js_SetAttributes(cx, delegate, id, attrsp);
647 : }
648 :
649 : JSBool
650 0 : ArrayBuffer::obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
651 : {
652 0 : return obj_setGenericAttributes(cx, obj, ATOM_TO_JSID(name), attrsp);
653 : }
654 :
655 : JSBool
656 0 : ArrayBuffer::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
657 : {
658 0 : JSObject *delegate = DelegateObject(cx, obj);
659 0 : if (!delegate)
660 0 : return false;
661 0 : return js_SetElementAttributes(cx, delegate, index, attrsp);
662 : }
663 :
664 : JSBool
665 0 : ArrayBuffer::obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
666 : {
667 0 : return obj_setGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
668 : }
669 :
670 : JSBool
671 0 : ArrayBuffer::obj_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
672 : {
673 0 : if (name == cx->runtime->atomState.byteLengthAtom) {
674 0 : rval->setBoolean(false);
675 0 : return true;
676 : }
677 :
678 0 : JSObject *delegate = DelegateObject(cx, obj);
679 0 : if (!delegate)
680 0 : return false;
681 0 : return js_DeleteProperty(cx, delegate, name, rval, strict);
682 : }
683 :
684 : JSBool
685 0 : ArrayBuffer::obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
686 : {
687 0 : JSObject *delegate = DelegateObject(cx, obj);
688 0 : if (!delegate)
689 0 : return false;
690 0 : return js_DeleteElement(cx, delegate, index, rval, strict);
691 : }
692 :
693 : JSBool
694 0 : ArrayBuffer::obj_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
695 : {
696 0 : JSObject *delegate = DelegateObject(cx, obj);
697 0 : if (!delegate)
698 0 : return false;
699 0 : return js_DeleteSpecial(cx, delegate, sid, rval, strict);
700 : }
701 :
702 : JSBool
703 18 : ArrayBuffer::obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
704 : Value *statep, jsid *idp)
705 : {
706 18 : statep->setNull();
707 18 : return true;
708 : }
709 :
710 : JSType
711 0 : ArrayBuffer::obj_typeOf(JSContext *cx, JSObject *obj)
712 : {
713 0 : return JSTYPE_OBJECT;
714 : }
715 :
716 : /*
717 : * TypedArray
718 : *
719 : * The non-templated base class for the specific typed implementations.
720 : * This class holds all the member variables that are used by
721 : * the subclasses.
722 : */
723 :
724 : JSObject *
725 38766649 : TypedArray::getTypedArray(JSObject *obj)
726 : {
727 77533298 : while (!js_IsTypedArray(obj))
728 0 : obj = obj->getProto();
729 38766649 : return obj;
730 : }
731 :
732 : inline bool
733 24645 : TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, uint32_t *ip)
734 : {
735 : uint32_t index;
736 24645 : if (js_IdIsIndex(id, &index) && index < getLength(obj)) {
737 22436 : if (ip)
738 22436 : *ip = index;
739 22436 : return true;
740 : }
741 :
742 2209 : return false;
743 : }
744 :
745 : typedef Value (* TypedArrayPropertyGetter)(JSObject *tarray);
746 :
747 : template <TypedArrayPropertyGetter Get>
748 : class TypedArrayGetter {
749 : public:
750 42 : static inline bool get(JSContext *cx, JSObject *obj, jsid id, Value *vp) {
751 9 : do {
752 42 : if (js_IsTypedArray(obj)) {
753 33 : JSObject *tarray = TypedArray::getTypedArray(obj);
754 33 : if (tarray)
755 33 : *vp = Get(tarray);
756 33 : return true;
757 : }
758 : } while ((obj = obj->getProto()) != NULL);
759 0 : return true;
760 : }
761 : };
762 :
763 : /*
764 : * For now (until slots directly hold data)
765 : * slots data element points to the JSObject representing the ArrayBuffer.
766 : */
767 : inline Value
768 24 : getBufferValue(JSObject *tarray)
769 : {
770 24 : JSObject *buffer = TypedArray::getBuffer(tarray);
771 24 : return ObjectValue(*buffer);
772 : }
773 :
774 : JSBool
775 24 : TypedArray::prop_getBuffer(JSContext *cx, JSObject *obj, jsid id, Value *vp)
776 : {
777 24 : return TypedArrayGetter<getBufferValue>::get(cx, obj, id, vp);
778 : }
779 :
780 : inline Value
781 0 : getByteOffsetValue(JSObject *tarray)
782 : {
783 0 : return Int32Value(TypedArray::getByteOffset(tarray));
784 : }
785 :
786 : JSBool
787 0 : TypedArray::prop_getByteOffset(JSContext *cx, JSObject *obj, jsid id, Value *vp)
788 : {
789 0 : return TypedArrayGetter<getByteOffsetValue>::get(cx, obj, id, vp);
790 : }
791 :
792 : inline Value
793 0 : getByteLengthValue(JSObject *tarray)
794 : {
795 0 : return Int32Value(TypedArray::getByteLength(tarray));
796 : }
797 :
798 : JSBool
799 0 : TypedArray::prop_getByteLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
800 : {
801 0 : return TypedArrayGetter<getByteLengthValue>::get(cx, obj, id, vp);
802 : }
803 :
804 : inline Value
805 9 : getLengthValue(JSObject *tarray)
806 : {
807 9 : return Int32Value(TypedArray::getLength(tarray));
808 : }
809 :
810 : JSBool
811 9 : TypedArray::prop_getLength(JSContext *cx, JSObject *obj, jsid id, Value *vp)
812 : {
813 9 : return TypedArrayGetter<getLengthValue>::get(cx, obj, id, vp);
814 : }
815 :
816 : JSBool
817 22 : TypedArray::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id,
818 : JSObject **objp, JSProperty **propp)
819 : {
820 22 : JSObject *tarray = getTypedArray(obj);
821 22 : JS_ASSERT(tarray);
822 :
823 22 : if (isArrayIndex(cx, tarray, id)) {
824 0 : *propp = PROPERTY_FOUND;
825 0 : *objp = obj;
826 0 : return true;
827 : }
828 :
829 22 : JSObject *proto = obj->getProto();
830 22 : if (!proto) {
831 0 : *objp = NULL;
832 0 : *propp = NULL;
833 0 : return true;
834 : }
835 :
836 22 : return proto->lookupGeneric(cx, id, objp, propp);
837 : }
838 :
839 : JSBool
840 0 : TypedArray::obj_lookupProperty(JSContext *cx, JSObject *obj, PropertyName *name,
841 : JSObject **objp, JSProperty **propp)
842 : {
843 0 : return obj_lookupGeneric(cx, obj, ATOM_TO_JSID(name), objp, propp);
844 : }
845 :
846 : JSBool
847 0 : TypedArray::obj_lookupElement(JSContext *cx, JSObject *obj, uint32_t index,
848 : JSObject **objp, JSProperty **propp)
849 : {
850 0 : JSObject *tarray = getTypedArray(obj);
851 0 : JS_ASSERT(tarray);
852 :
853 0 : if (index < getLength(tarray)) {
854 0 : *propp = PROPERTY_FOUND;
855 0 : *objp = obj;
856 0 : return true;
857 : }
858 :
859 0 : if (JSObject *proto = obj->getProto())
860 0 : return proto->lookupElement(cx, index, objp, propp);
861 :
862 0 : *objp = NULL;
863 0 : *propp = NULL;
864 0 : return true;
865 : }
866 :
867 : JSBool
868 0 : TypedArray::obj_lookupSpecial(JSContext *cx, JSObject *obj, SpecialId sid,
869 : JSObject **objp, JSProperty **propp)
870 : {
871 0 : return obj_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
872 : }
873 :
874 : JSBool
875 0 : TypedArray::obj_getGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
876 : {
877 0 : *attrsp = (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
878 : ? JSPROP_PERMANENT | JSPROP_READONLY
879 0 : : JSPROP_PERMANENT | JSPROP_ENUMERATE;
880 0 : return true;
881 : }
882 :
883 : JSBool
884 0 : TypedArray::obj_getPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
885 : {
886 0 : *attrsp = JSPROP_PERMANENT | JSPROP_ENUMERATE;
887 0 : return true;
888 : }
889 :
890 : JSBool
891 0 : TypedArray::obj_getElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
892 : {
893 0 : *attrsp = JSPROP_PERMANENT | JSPROP_ENUMERATE;
894 0 : return true;
895 : }
896 :
897 : JSBool
898 0 : TypedArray::obj_getSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
899 : {
900 0 : return obj_getGenericAttributes(cx, obj, SPECIALID_TO_JSID(sid), attrsp);
901 : }
902 :
903 : JSBool
904 0 : TypedArray::obj_setGenericAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
905 : {
906 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
907 0 : return false;
908 : }
909 :
910 : JSBool
911 0 : TypedArray::obj_setPropertyAttributes(JSContext *cx, JSObject *obj, PropertyName *name, unsigned *attrsp)
912 : {
913 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
914 0 : return false;
915 : }
916 :
917 : JSBool
918 0 : TypedArray::obj_setElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
919 : {
920 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
921 0 : return false;
922 : }
923 :
924 : JSBool
925 0 : TypedArray::obj_setSpecialAttributes(JSContext *cx, JSObject *obj, SpecialId sid, unsigned *attrsp)
926 : {
927 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_SET_ARRAY_ATTRS);
928 0 : return false;
929 : }
930 :
931 : /* static */ int
932 3926 : TypedArray::lengthOffset()
933 : {
934 3926 : return JSObject::getFixedSlotOffset(FIELD_LENGTH);
935 : }
936 :
937 : /* static */ int
938 3258 : TypedArray::dataOffset()
939 : {
940 3258 : return JSObject::getPrivateDataOffset(NUM_FIXED_SLOTS);
941 : }
942 :
943 : /* Helper clamped uint8_t type */
944 :
945 : int32_t JS_FASTCALL
946 992 : js_TypedArray_uint8_clamp_double(const double x)
947 : {
948 : // Not < so that NaN coerces to 0
949 992 : if (!(x >= 0))
950 308 : return 0;
951 :
952 684 : if (x > 255)
953 246 : return 255;
954 :
955 438 : double toTruncate = x + 0.5;
956 438 : uint8_t y = uint8_t(toTruncate);
957 :
958 : /*
959 : * now val is rounded to nearest, ties rounded up. We want
960 : * rounded to nearest ties to even, so check whether we had a
961 : * tie.
962 : */
963 438 : if (y == toTruncate) {
964 : /*
965 : * It was a tie (since adding 0.5 gave us the exact integer
966 : * we want). Since we rounded up, we either already have an
967 : * even number or we have an odd number but the number we
968 : * want is one less. So just unconditionally masking out the
969 : * ones bit should do the trick to get us the value we
970 : * want.
971 : */
972 104 : return (y & ~1);
973 : }
974 :
975 334 : return y;
976 : }
977 :
978 : struct uint8_clamped {
979 : uint8_t val;
980 :
981 : uint8_clamped() { }
982 1288 : uint8_clamped(const uint8_clamped& other) : val(other.val) { }
983 :
984 : // invoke our assignment helpers for constructor conversion
985 0 : uint8_clamped(uint8_t x) { *this = x; }
986 0 : uint8_clamped(uint16_t x) { *this = x; }
987 0 : uint8_clamped(uint32_t x) { *this = x; }
988 0 : uint8_clamped(int8_t x) { *this = x; }
989 0 : uint8_clamped(int16_t x) { *this = x; }
990 773 : uint8_clamped(int32_t x) { *this = x; }
991 887 : uint8_clamped(double x) { *this = x; }
992 :
993 1660 : inline uint8_clamped& operator= (const uint8_clamped& x) {
994 1660 : val = x.val;
995 1660 : return *this;
996 : }
997 :
998 0 : inline uint8_clamped& operator= (uint8_t x) {
999 0 : val = x;
1000 0 : return *this;
1001 : }
1002 :
1003 0 : inline uint8_clamped& operator= (uint16_t x) {
1004 0 : val = (x > 255) ? 255 : uint8_t(x);
1005 0 : return *this;
1006 : }
1007 :
1008 0 : inline uint8_clamped& operator= (uint32_t x) {
1009 0 : val = (x > 255) ? 255 : uint8_t(x);
1010 0 : return *this;
1011 : }
1012 :
1013 0 : inline uint8_clamped& operator= (int8_t x) {
1014 0 : val = (x >= 0) ? uint8_t(x) : 0;
1015 0 : return *this;
1016 : }
1017 :
1018 0 : inline uint8_clamped& operator= (int16_t x) {
1019 : val = (x >= 0)
1020 : ? ((x < 255)
1021 : ? uint8_t(x)
1022 : : 255)
1023 0 : : 0;
1024 0 : return *this;
1025 : }
1026 :
1027 773 : inline uint8_clamped& operator= (int32_t x) {
1028 : val = (x >= 0)
1029 : ? ((x < 255)
1030 : ? uint8_t(x)
1031 : : 255)
1032 773 : : 0;
1033 773 : return *this;
1034 : }
1035 :
1036 887 : inline uint8_clamped& operator= (const double x) {
1037 887 : val = uint8_t(js_TypedArray_uint8_clamp_double(x));
1038 887 : return *this;
1039 : }
1040 :
1041 1288 : inline operator uint8_t() const {
1042 1288 : return val;
1043 : }
1044 : };
1045 :
1046 : /* Make sure the compiler isn't doing some funky stuff */
1047 : JS_STATIC_ASSERT(sizeof(uint8_clamped) == 1);
1048 :
1049 : template<typename NativeType> static inline const int TypeIDOfType();
1050 7590 : template<> inline const int TypeIDOfType<int8_t>() { return TypedArray::TYPE_INT8; }
1051 5726 : template<> inline const int TypeIDOfType<uint8_t>() { return TypedArray::TYPE_UINT8; }
1052 5750 : template<> inline const int TypeIDOfType<int16_t>() { return TypedArray::TYPE_INT16; }
1053 4576 : template<> inline const int TypeIDOfType<uint16_t>() { return TypedArray::TYPE_UINT16; }
1054 6150 : template<> inline const int TypeIDOfType<int32_t>() { return TypedArray::TYPE_INT32; }
1055 4861 : template<> inline const int TypeIDOfType<uint32_t>() { return TypedArray::TYPE_UINT32; }
1056 12581 : template<> inline const int TypeIDOfType<float>() { return TypedArray::TYPE_FLOAT32; }
1057 5591 : template<> inline const int TypeIDOfType<double>() { return TypedArray::TYPE_FLOAT64; }
1058 5503 : template<> inline const int TypeIDOfType<uint8_clamped>() { return TypedArray::TYPE_UINT8_CLAMPED; }
1059 :
1060 3889 : template<typename NativeType> static inline const bool TypeIsUnsigned() { return false; }
1061 798 : template<> inline const bool TypeIsUnsigned<uint8_t>() { return true; }
1062 966 : template<> inline const bool TypeIsUnsigned<uint16_t>() { return true; }
1063 1217 : template<> inline const bool TypeIsUnsigned<uint32_t>() { return true; }
1064 :
1065 24915 : template<typename NativeType> static inline const bool TypeIsFloatingPoint() { return false; }
1066 854 : template<> inline const bool TypeIsFloatingPoint<float>() { return true; }
1067 19079 : template<> inline const bool TypeIsFloatingPoint<double>() { return true; }
1068 :
1069 : template<typename NativeType> static inline const bool ElementTypeMayBeDouble() { return false; }
1070 : template<> inline const bool ElementTypeMayBeDouble<uint32_t>() { return true; }
1071 : template<> inline const bool ElementTypeMayBeDouble<float>() { return true; }
1072 : template<> inline const bool ElementTypeMayBeDouble<double>() { return true; }
1073 :
1074 : template<typename NativeType> class TypedArrayTemplate;
1075 :
1076 : template<typename NativeType>
1077 : class TypedArrayTemplate
1078 : : public TypedArray
1079 : {
1080 : public:
1081 : typedef NativeType ThisType;
1082 : typedef TypedArrayTemplate<NativeType> ThisTypeArray;
1083 58328 : static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
1084 6870 : static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
1085 44768 : static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
1086 : static const bool ArrayElementTypeMayBeDouble() { return ElementTypeMayBeDouble<NativeType>(); }
1087 :
1088 : static const size_t BYTES_PER_ELEMENT = sizeof(ThisType);
1089 :
1090 28902 : static inline Class *slowClass()
1091 : {
1092 28902 : return &TypedArray::slowClasses[ArrayTypeID()];
1093 : }
1094 :
1095 19722 : static inline Class *fastClass()
1096 : {
1097 19722 : return &TypedArray::fastClasses[ArrayTypeID()];
1098 : }
1099 :
1100 : static void
1101 45 : obj_trace(JSTracer *trc, JSObject *obj)
1102 : {
1103 45 : MarkSlot(trc, &obj->getFixedSlotRef(FIELD_BUFFER), "typedarray.buffer");
1104 45 : }
1105 :
1106 : static JSBool
1107 897 : obj_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name,
1108 : Value *vp)
1109 : {
1110 897 : JSObject *tarray = getTypedArray(obj);
1111 :
1112 897 : if (name == cx->runtime->atomState.lengthAtom) {
1113 172 : vp->setNumber(getLength(tarray));
1114 172 : return true;
1115 : }
1116 :
1117 725 : JSObject *proto = obj->getProto();
1118 725 : if (!proto) {
1119 0 : vp->setUndefined();
1120 0 : return true;
1121 : }
1122 :
1123 725 : return proto->getProperty(cx, receiver, name, vp);
1124 : }
1125 :
1126 : static JSBool
1127 29054 : obj_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
1128 : {
1129 29054 : JSObject *tarray = getTypedArray(obj);
1130 :
1131 29054 : if (index < getLength(tarray)) {
1132 23843 : copyIndexToValue(cx, tarray, index, vp);
1133 23843 : return true;
1134 : }
1135 :
1136 5211 : JSObject *proto = obj->getProto();
1137 5211 : if (!proto) {
1138 0 : vp->setUndefined();
1139 0 : return true;
1140 : }
1141 :
1142 5211 : return proto->getElement(cx, receiver, index, vp);
1143 : }
1144 :
1145 : static JSBool
1146 0 : obj_getSpecial(JSContext *cx, JSObject *obj, JSObject *receiver, SpecialId sid, Value *vp)
1147 : {
1148 0 : JSObject *proto = obj->getProto();
1149 0 : if (!proto) {
1150 0 : vp->setUndefined();
1151 0 : return true;
1152 : }
1153 :
1154 0 : return proto->getSpecial(cx, receiver, sid, vp);
1155 : }
1156 :
1157 : static JSBool
1158 5067 : obj_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
1159 : {
1160 5067 : Value idval = IdToValue(id);
1161 :
1162 : uint32_t index;
1163 5067 : if (IsDefinitelyIndex(idval, &index))
1164 4170 : return obj_getElement(cx, obj, receiver, index, vp);
1165 :
1166 897 : SpecialId sid;
1167 897 : if (ValueIsSpecial(obj, &idval, &sid, cx))
1168 0 : return obj_getSpecial(cx, obj, receiver, sid, vp);
1169 :
1170 : JSAtom *atom;
1171 897 : if (!js_ValueToAtom(cx, idval, &atom))
1172 0 : return false;
1173 :
1174 897 : if (atom->isIndex(&index))
1175 0 : return obj_getElement(cx, obj, receiver, index, vp);
1176 :
1177 897 : return obj_getProperty(cx, obj, receiver, atom->asPropertyName(), vp);
1178 : }
1179 :
1180 : static JSBool
1181 8 : obj_getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp, bool *present)
1182 : {
1183 : // Fast-path the common case of index < length
1184 8 : JSObject *tarray = getTypedArray(obj);
1185 :
1186 8 : if (index < getLength(tarray)) {
1187 : // this inline function is specialized for each type
1188 8 : copyIndexToValue(cx, tarray, index, vp);
1189 8 : *present = true;
1190 8 : return true;
1191 : }
1192 :
1193 0 : JSObject *proto = obj->getProto();
1194 0 : if (!proto) {
1195 0 : vp->setUndefined();
1196 0 : return true;
1197 : }
1198 :
1199 0 : return proto->getElementIfPresent(cx, receiver, index, vp, present);
1200 : }
1201 :
1202 : static bool
1203 22436 : setElementTail(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp, JSBool strict)
1204 : {
1205 22436 : JS_ASSERT(tarray);
1206 22436 : JS_ASSERT(index < getLength(tarray));
1207 :
1208 22436 : if (vp->isInt32()) {
1209 13838 : setIndex(tarray, index, NativeType(vp->toInt32()));
1210 13838 : return true;
1211 : }
1212 :
1213 : double d;
1214 8598 : if (vp->isDouble()) {
1215 3161 : d = vp->toDouble();
1216 5437 : } else if (vp->isNull()) {
1217 0 : d = 0.0;
1218 5437 : } else if (vp->isPrimitive()) {
1219 3102 : JS_ASSERT(vp->isString() || vp->isUndefined() || vp->isBoolean());
1220 3102 : if (vp->isString()) {
1221 1774 : JS_ALWAYS_TRUE(ToNumber(cx, *vp, &d));
1222 1328 : } else if (vp->isUndefined()) {
1223 95 : d = js_NaN;
1224 : } else {
1225 1233 : d = double(vp->toBoolean());
1226 : }
1227 : } else {
1228 : // non-primitive assignments become NaN or 0 (for float/int arrays)
1229 2335 : d = js_NaN;
1230 : }
1231 :
1232 : // If the array is an integer array, we only handle up to
1233 : // 32-bit ints from this point on. if we want to handle
1234 : // 64-bit ints, we'll need some changes.
1235 :
1236 : // Assign based on characteristics of the destination type
1237 8598 : if (ArrayTypeIsFloatingPoint()) {
1238 1728 : setIndex(tarray, index, NativeType(d));
1239 6870 : } else if (ArrayTypeIsUnsigned()) {
1240 0 : JS_ASSERT(sizeof(NativeType) <= 4);
1241 2981 : uint32_t n = js_DoubleToECMAUint32(d);
1242 2981 : setIndex(tarray, index, NativeType(n));
1243 3889 : } else if (ArrayTypeID() == TypedArray::TYPE_UINT8_CLAMPED) {
1244 : // The uint8_clamped type has a special rounding converter
1245 : // for doubles.
1246 887 : setIndex(tarray, index, NativeType(d));
1247 : } else {
1248 0 : JS_ASSERT(sizeof(NativeType) <= 4);
1249 3002 : int32_t n = js_DoubleToECMAInt32(d);
1250 3002 : setIndex(tarray, index, NativeType(n));
1251 : }
1252 :
1253 8598 : return true;
1254 : }
1255 :
1256 : static JSBool
1257 24623 : obj_setGeneric(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
1258 : {
1259 24623 : JSObject *tarray = getTypedArray(obj);
1260 24623 : JS_ASSERT(tarray);
1261 :
1262 24623 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
1263 0 : vp->setNumber(getLength(tarray));
1264 0 : return true;
1265 : }
1266 :
1267 : uint32_t index;
1268 : // We can't just chain to js_SetPropertyHelper, because we're not a normal object.
1269 24623 : if (!isArrayIndex(cx, tarray, id, &index)) {
1270 : // Silent ignore is better than an exception here, because
1271 : // at some point we may want to support other properties on
1272 : // these objects. This is especially true when these arrays
1273 : // are used to implement HTML Canvas 2D's PixelArray objects,
1274 : // which used to be plain old arrays.
1275 2187 : vp->setUndefined();
1276 2187 : return true;
1277 : }
1278 :
1279 22436 : return setElementTail(cx, tarray, index, vp, strict);
1280 : }
1281 :
1282 : static JSBool
1283 0 : obj_setProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *vp, JSBool strict)
1284 : {
1285 0 : return obj_setGeneric(cx, obj, ATOM_TO_JSID(name), vp, strict);
1286 : }
1287 :
1288 : static JSBool
1289 0 : obj_setElement(JSContext *cx, JSObject *obj, uint32_t index, Value *vp, JSBool strict)
1290 : {
1291 0 : JSObject *tarray = getTypedArray(obj);
1292 0 : JS_ASSERT(tarray);
1293 :
1294 0 : if (index >= getLength(tarray)) {
1295 : // Silent ignore is better than an exception here, because
1296 : // at some point we may want to support other properties on
1297 : // these objects. This is especially true when these arrays
1298 : // are used to implement HTML Canvas 2D's PixelArray objects,
1299 : // which used to be plain old arrays.
1300 0 : vp->setUndefined();
1301 0 : return true;
1302 : }
1303 :
1304 0 : return setElementTail(cx, tarray, index, vp, strict);
1305 : }
1306 :
1307 : static JSBool
1308 0 : obj_setSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *vp, JSBool strict)
1309 : {
1310 0 : return obj_setGeneric(cx, obj, SPECIALID_TO_JSID(sid), vp, strict);
1311 : }
1312 :
1313 : static JSBool
1314 0 : obj_defineGeneric(JSContext *cx, JSObject *obj, jsid id, const Value *v,
1315 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1316 : {
1317 0 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom))
1318 0 : return true;
1319 :
1320 0 : Value tmp = *v;
1321 0 : return obj_setGeneric(cx, obj, id, &tmp, false);
1322 : }
1323 :
1324 : static JSBool
1325 0 : obj_defineProperty(JSContext *cx, JSObject *obj, PropertyName *name, const Value *v,
1326 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1327 : {
1328 0 : return obj_defineGeneric(cx, obj, ATOM_TO_JSID(name), v, getter, setter, attrs);
1329 : }
1330 :
1331 : static JSBool
1332 0 : obj_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *v,
1333 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1334 : {
1335 0 : Value tmp = *v;
1336 0 : return obj_setElement(cx, obj, index, &tmp, false);
1337 : }
1338 :
1339 : static JSBool
1340 0 : obj_defineSpecial(JSContext *cx, JSObject *obj, SpecialId sid, const Value *v,
1341 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1342 : {
1343 0 : return obj_defineGeneric(cx, obj, SPECIALID_TO_JSID(sid), v, getter, setter, attrs);
1344 : }
1345 :
1346 : static JSBool
1347 0 : obj_deleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
1348 : {
1349 0 : if (name == cx->runtime->atomState.lengthAtom) {
1350 0 : rval->setBoolean(false);
1351 0 : return true;
1352 : }
1353 :
1354 0 : rval->setBoolean(true);
1355 0 : return true;
1356 : }
1357 :
1358 : static JSBool
1359 0 : obj_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
1360 : {
1361 0 : JSObject *tarray = TypedArray::getTypedArray(obj);
1362 0 : JS_ASSERT(tarray);
1363 :
1364 0 : if (index < getLength(tarray)) {
1365 0 : rval->setBoolean(false);
1366 0 : return true;
1367 : }
1368 :
1369 0 : rval->setBoolean(true);
1370 0 : return true;
1371 : }
1372 :
1373 : static JSBool
1374 0 : obj_deleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
1375 : {
1376 0 : rval->setBoolean(true);
1377 0 : return true;
1378 : }
1379 :
1380 : static JSBool
1381 38702339 : obj_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1382 : Value *statep, jsid *idp)
1383 : {
1384 38702339 : JSObject *tarray = getTypedArray(obj);
1385 38702339 : JS_ASSERT(tarray);
1386 :
1387 : /*
1388 : * Iteration is "length" (if JSENUMERATE_INIT_ALL), then [0, length).
1389 : * *statep is JSVAL_TRUE if enumerating "length" and
1390 : * JSVAL_TO_INT(index) when enumerating index.
1391 : */
1392 38702339 : switch (enum_op) {
1393 : case JSENUMERATE_INIT_ALL:
1394 0 : statep->setBoolean(true);
1395 0 : if (idp)
1396 0 : *idp = ::INT_TO_JSID(getLength(tarray) + 1);
1397 0 : break;
1398 :
1399 : case JSENUMERATE_INIT:
1400 24 : statep->setInt32(0);
1401 24 : if (idp)
1402 0 : *idp = ::INT_TO_JSID(getLength(tarray));
1403 24 : break;
1404 :
1405 : case JSENUMERATE_NEXT:
1406 38702315 : if (statep->isTrue()) {
1407 0 : *idp = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
1408 0 : statep->setInt32(0);
1409 : } else {
1410 38702315 : uint32_t index = statep->toInt32();
1411 38702315 : if (index < getLength(tarray)) {
1412 38702291 : *idp = ::INT_TO_JSID(index);
1413 38702291 : statep->setInt32(index + 1);
1414 : } else {
1415 24 : JS_ASSERT(index == getLength(tarray));
1416 24 : statep->setNull();
1417 : }
1418 : }
1419 38702315 : break;
1420 :
1421 : case JSENUMERATE_DESTROY:
1422 0 : statep->setNull();
1423 0 : break;
1424 : }
1425 :
1426 38702339 : return true;
1427 : }
1428 :
1429 : static JSType
1430 4 : obj_typeOf(JSContext *cx, JSObject *obj)
1431 : {
1432 4 : return JSTYPE_OBJECT;
1433 : }
1434 :
1435 : static JSObject *
1436 5815 : createTypedArray(JSContext *cx, JSObject *bufobj, uint32_t byteOffset, uint32_t len)
1437 : {
1438 5815 : JS_ASSERT(bufobj->isArrayBuffer());
1439 5815 : JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
1440 5815 : if (!obj)
1441 0 : return NULL;
1442 5815 : JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8);
1443 :
1444 : /*
1445 : * Specialize the type of the object on the current scripted location,
1446 : * and mark the type as definitely a typed array.
1447 : */
1448 5815 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(slowClass());
1449 5815 : types::TypeObject *type = types::GetTypeCallerInitObject(cx, key);
1450 5815 : if (!type)
1451 0 : return NULL;
1452 5815 : obj->setType(type);
1453 :
1454 5815 : obj->setSlot(FIELD_TYPE, Int32Value(ArrayTypeID()));
1455 5815 : obj->setSlot(FIELD_BUFFER, ObjectValue(*bufobj));
1456 :
1457 : /*
1458 : * N.B. The base of the array's data is stored in the object's
1459 : * private data rather than a slot, to avoid alignment restrictions
1460 : * on private Values.
1461 : */
1462 5815 : obj->setPrivate(bufobj->arrayBufferDataOffset() + byteOffset);
1463 :
1464 5815 : obj->setSlot(FIELD_LENGTH, Int32Value(len));
1465 5815 : obj->setSlot(FIELD_BYTEOFFSET, Int32Value(byteOffset));
1466 5815 : obj->setSlot(FIELD_BYTELENGTH, Int32Value(len * sizeof(NativeType)));
1467 :
1468 11630 : DebugOnly<uint32_t> bufferByteLength = getBuffer(obj)->arrayBufferByteLength();
1469 5815 : JS_ASSERT(bufferByteLength - getByteOffset(obj) >= getByteLength(obj));
1470 5815 : JS_ASSERT(getByteOffset(obj) <= bufferByteLength);
1471 5815 : JS_ASSERT(getBuffer(obj)->arrayBufferDataOffset() <= getDataOffset(obj));
1472 5815 : JS_ASSERT(getDataOffset(obj) <= offsetData(obj, bufferByteLength));
1473 :
1474 5815 : JS_ASSERT(obj->getClass() == slowClass());
1475 :
1476 : js::Shape *empty = EmptyShape::getInitialShape(cx, fastClass(),
1477 : obj->getProto(), obj->getParent(),
1478 : gc::FINALIZE_OBJECT8,
1479 5815 : BaseShape::NOT_EXTENSIBLE);
1480 5815 : if (!empty)
1481 0 : return NULL;
1482 5815 : obj->setLastPropertyInfallible(empty);
1483 :
1484 5815 : JS_ASSERT(obj->numFixedSlots() == NUM_FIXED_SLOTS);
1485 :
1486 5815 : return obj;
1487 : }
1488 :
1489 : /*
1490 : * new [Type]Array(length)
1491 : * new [Type]Array(otherTypedArray)
1492 : * new [Type]Array(JSArray)
1493 : * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length)
1494 : */
1495 : static JSBool
1496 5266 : class_constructor(JSContext *cx, unsigned argc, Value *vp)
1497 : {
1498 : /* N.B. this is a constructor for slowClass, not fastClass! */
1499 5266 : JSObject *obj = create(cx, argc, JS_ARGV(cx, vp));
1500 5266 : if (!obj)
1501 0 : return false;
1502 5266 : vp->setObject(*obj);
1503 5266 : return true;
1504 : }
1505 :
1506 : static JSObject *
1507 5320 : create(JSContext *cx, unsigned argc, Value *argv)
1508 : {
1509 : /* N.B. there may not be an argv[-2]/argv[-1]. */
1510 :
1511 : /* () or (number) */
1512 5320 : uint32_t len = 0;
1513 5320 : if (argc == 0 || ValueIsLength(cx, argv[0], &len)) {
1514 4638 : JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
1515 4638 : if (!bufobj)
1516 0 : return NULL;
1517 :
1518 4638 : return createTypedArray(cx, bufobj, 0, len);
1519 : }
1520 :
1521 : /* (not an object) */
1522 682 : if (!argv[0].isObject()) {
1523 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1524 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1525 0 : return NULL;
1526 : }
1527 :
1528 682 : JSObject *dataObj = &argv[0].toObject();
1529 :
1530 : /* (typedArray) */
1531 682 : if (js_IsTypedArray(dataObj)) {
1532 0 : JSObject *otherTypedArray = getTypedArray(dataObj);
1533 0 : JS_ASSERT(otherTypedArray);
1534 :
1535 0 : uint32_t len = getLength(otherTypedArray);
1536 0 : JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
1537 0 : if (!bufobj)
1538 0 : return NULL;
1539 :
1540 0 : JSObject *obj = createTypedArray(cx, bufobj, 0, len);
1541 0 : if (!obj || !copyFromTypedArray(cx, obj, otherTypedArray, 0))
1542 0 : return NULL;
1543 0 : return obj;
1544 : }
1545 :
1546 : /* (obj, byteOffset, length). */
1547 682 : int32_t byteOffset = -1;
1548 682 : int32_t length = -1;
1549 :
1550 682 : if (argc > 1) {
1551 101 : if (!ToInt32(cx, argv[1], &byteOffset))
1552 0 : return NULL;
1553 101 : if (byteOffset < 0) {
1554 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1555 : JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1");
1556 0 : return NULL;
1557 : }
1558 :
1559 101 : if (argc > 2) {
1560 101 : if (!ToInt32(cx, argv[2], &length))
1561 0 : return NULL;
1562 101 : if (length < 0) {
1563 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1564 : JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
1565 0 : return NULL;
1566 : }
1567 : }
1568 : }
1569 :
1570 : /* (obj, byteOffset, length) */
1571 682 : return createTypedArrayWithOffsetLength(cx, dataObj, byteOffset, length);
1572 : }
1573 :
1574 : /* subarray(start[, end]) */
1575 : static JSBool
1576 1629 : fun_subarray(JSContext *cx, unsigned argc, Value *vp)
1577 : {
1578 1629 : CallArgs args = CallArgsFromVp(argc, vp);
1579 :
1580 : bool ok;
1581 1629 : JSObject *obj = NonGenericMethodGuard(cx, args, fun_subarray, fastClass(), &ok);
1582 1629 : if (!obj)
1583 1134 : return ok;
1584 :
1585 495 : JSObject *tarray = getTypedArray(obj);
1586 495 : if (!tarray)
1587 0 : return true;
1588 :
1589 : // these are the default values
1590 495 : int32_t begin = 0, end = getLength(tarray);
1591 495 : int32_t length = int32_t(getLength(tarray));
1592 :
1593 495 : if (args.length() > 0) {
1594 9 : if (!ToClampedIndex(cx, args[0], length, &begin))
1595 0 : return false;
1596 :
1597 9 : if (args.length() > 1) {
1598 9 : if (!ToClampedIndex(cx, args[1], length, &end))
1599 0 : return false;
1600 : }
1601 : }
1602 :
1603 495 : if (begin > end)
1604 0 : begin = end;
1605 :
1606 495 : JSObject *nobj = createSubarray(cx, tarray, begin, end);
1607 495 : if (!nobj)
1608 0 : return false;
1609 495 : args.rval().setObject(*nobj);
1610 495 : return true;
1611 : }
1612 :
1613 : /* set(array[, offset]) */
1614 : static JSBool
1615 821 : fun_set(JSContext *cx, unsigned argc, Value *vp)
1616 : {
1617 821 : CallArgs args = CallArgsFromVp(argc, vp);
1618 :
1619 : bool ok;
1620 821 : JSObject *obj = NonGenericMethodGuard(cx, args, fun_set, fastClass(), &ok);
1621 821 : if (!obj)
1622 567 : return ok;
1623 :
1624 254 : JSObject *tarray = getTypedArray(obj);
1625 254 : if (!tarray)
1626 0 : return true;
1627 :
1628 : // these are the default values
1629 254 : int32_t off = 0;
1630 :
1631 254 : if (args.length() > 1) {
1632 9 : if (!ToInt32(cx, args[1], &off))
1633 0 : return false;
1634 :
1635 9 : if (off < 0 || uint32_t(off) > getLength(tarray)) {
1636 : // the given offset is bogus
1637 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1638 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1639 0 : return false;
1640 : }
1641 : }
1642 :
1643 254 : uint32_t offset(off);
1644 :
1645 : // first arg must be either a typed array or a JS array
1646 254 : if (args.length() == 0 || !args[0].isObject()) {
1647 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1648 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1649 0 : return false;
1650 : }
1651 :
1652 254 : JSObject *arg0 = args[0].toObjectOrNull();
1653 254 : if (js_IsTypedArray(arg0)) {
1654 9 : JSObject *src = TypedArray::getTypedArray(arg0);
1655 9 : if (!src ||
1656 : getLength(src) > getLength(tarray) - offset)
1657 : {
1658 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1659 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1660 0 : return false;
1661 : }
1662 :
1663 9 : if (!copyFromTypedArray(cx, obj, src, offset))
1664 0 : return false;
1665 : } else {
1666 : uint32_t len;
1667 245 : if (!js_GetLengthProperty(cx, arg0, &len))
1668 0 : return false;
1669 :
1670 : // avoid overflow; we know that offset <= length
1671 245 : if (len > getLength(tarray) - offset) {
1672 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1673 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1674 0 : return false;
1675 : }
1676 :
1677 245 : if (!copyFromArray(cx, obj, arg0, len, offset))
1678 0 : return false;
1679 : }
1680 :
1681 254 : args.rval().setUndefined();
1682 254 : return true;
1683 : }
1684 :
1685 : public:
1686 : static JSObject *
1687 682 : createTypedArrayWithOffsetLength(JSContext *cx, JSObject *other,
1688 : int32_t byteOffsetInt, int32_t lengthInt)
1689 : {
1690 682 : JS_ASSERT(!js_IsTypedArray(other));
1691 :
1692 : /* Handle creation from an ArrayBuffer not ArrayBuffer.prototype. */
1693 682 : if (other->isArrayBuffer()) {
1694 550 : uint32_t boffset = (byteOffsetInt < 0) ? 0 : uint32_t(byteOffsetInt);
1695 :
1696 550 : if (boffset > other->arrayBufferByteLength() || boffset % sizeof(NativeType) != 0) {
1697 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1698 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1699 0 : return NULL; // invalid byteOffset
1700 : }
1701 :
1702 : uint32_t len;
1703 550 : if (lengthInt < 0) {
1704 449 : len = (other->arrayBufferByteLength() - boffset) / sizeof(NativeType);
1705 449 : if (len * sizeof(NativeType) != (other->arrayBufferByteLength() - boffset)) {
1706 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1707 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1708 0 : return NULL; // given byte array doesn't map exactly to sizeof(NativeType)*N
1709 : }
1710 : } else {
1711 101 : len = (uint32_t) lengthInt;
1712 : }
1713 :
1714 : // Go slowly and check for overflow.
1715 550 : uint32_t arrayByteLength = len*sizeof(NativeType);
1716 550 : if (uint32_t(len) >= INT32_MAX / sizeof(NativeType) ||
1717 : uint32_t(boffset) >= INT32_MAX - arrayByteLength)
1718 : {
1719 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1720 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1721 0 : return NULL; // overflow occurred along the way when calculating boffset+len*sizeof(NativeType)
1722 : }
1723 :
1724 550 : if (arrayByteLength + boffset > other->arrayBufferByteLength()) {
1725 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1726 : JSMSG_TYPED_ARRAY_BAD_ARGS);
1727 0 : return NULL; // boffset+len is too big for the arraybuffer
1728 : }
1729 :
1730 550 : return createTypedArray(cx, other, boffset, len);
1731 : }
1732 :
1733 : /*
1734 : * Otherwise create a new typed array and copy len properties from the
1735 : * object.
1736 : */
1737 : uint32_t len;
1738 132 : if (!js_GetLengthProperty(cx, other, &len))
1739 0 : return NULL;
1740 :
1741 132 : JSObject *bufobj = createBufferWithSizeAndCount(cx, len);
1742 132 : if (!bufobj)
1743 0 : return NULL;
1744 :
1745 132 : JSObject *obj = createTypedArray(cx, bufobj, 0, len);
1746 132 : if (!obj || !copyFromArray(cx, obj, other, len))
1747 0 : return NULL;
1748 132 : return obj;
1749 : }
1750 :
1751 : static const NativeType
1752 23851 : getIndex(JSObject *obj, uint32_t index)
1753 : {
1754 23851 : return *(static_cast<const NativeType*>(getDataOffset(obj)) + index);
1755 : }
1756 :
1757 : static void
1758 22436 : setIndex(JSObject *obj, uint32_t index, NativeType val)
1759 : {
1760 22436 : *(static_cast<NativeType*>(getDataOffset(obj)) + index) = val;
1761 22436 : }
1762 :
1763 12179 : static void copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp);
1764 :
1765 : static JSObject *
1766 495 : createSubarray(JSContext *cx, JSObject *tarray, uint32_t begin, uint32_t end)
1767 : {
1768 495 : JS_ASSERT(tarray);
1769 :
1770 : JS_ASSERT(0 <= begin);
1771 495 : JS_ASSERT(begin <= getLength(tarray));
1772 : JS_ASSERT(0 <= end);
1773 495 : JS_ASSERT(end <= getLength(tarray));
1774 :
1775 495 : JSObject *bufobj = getBuffer(tarray);
1776 495 : JS_ASSERT(bufobj);
1777 :
1778 495 : JS_ASSERT(begin <= end);
1779 495 : uint32_t length = end - begin;
1780 :
1781 495 : JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
1782 495 : JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= getByteOffset(tarray));
1783 495 : uint32_t byteOffset = getByteOffset(tarray) + begin * sizeof(NativeType);
1784 :
1785 495 : return createTypedArray(cx, bufobj, byteOffset, length);
1786 : }
1787 :
1788 : protected:
1789 : static NativeType
1790 80 : nativeFromDouble(double d)
1791 : {
1792 80 : if (!ArrayTypeIsFloatingPoint() && JS_UNLIKELY(JSDOUBLE_IS_NaN(d)))
1793 0 : return NativeType(int32_t(0));
1794 80 : if (TypeIsFloatingPoint<NativeType>())
1795 80 : return NativeType(d);
1796 0 : if (TypeIsUnsigned<NativeType>())
1797 0 : return NativeType(js_DoubleToECMAUint32(d));
1798 0 : return NativeType(js_DoubleToECMAInt32(d));
1799 : }
1800 :
1801 : static NativeType
1802 36342 : nativeFromValue(JSContext *cx, const Value &v)
1803 : {
1804 36342 : if (v.isInt32())
1805 172 : return NativeType(v.toInt32());
1806 :
1807 36170 : if (v.isDouble())
1808 80 : return nativeFromDouble(v.toDouble());
1809 :
1810 : /*
1811 : * The condition guarantees that holes and undefined values
1812 : * are treated identically.
1813 : */
1814 36090 : if (v.isPrimitive() && !v.isMagic() && !v.isUndefined()) {
1815 : double dval;
1816 0 : JS_ALWAYS_TRUE(ToNumber(cx, v, &dval));
1817 0 : return nativeFromDouble(dval);
1818 : }
1819 :
1820 : return ArrayTypeIsFloatingPoint()
1821 : ? NativeType(js_NaN)
1822 36090 : : NativeType(int32_t(0));
1823 : }
1824 :
1825 : static bool
1826 377 : copyFromArray(JSContext *cx, JSObject *thisTypedArrayObj,
1827 : JSObject *ar, uint32_t len, uint32_t offset = 0)
1828 : {
1829 377 : thisTypedArrayObj = getTypedArray(thisTypedArrayObj);
1830 377 : JS_ASSERT(thisTypedArrayObj);
1831 :
1832 377 : JS_ASSERT(offset <= getLength(thisTypedArrayObj));
1833 377 : JS_ASSERT(len <= getLength(thisTypedArrayObj) - offset);
1834 377 : NativeType *dest = static_cast<NativeType*>(getDataOffset(thisTypedArrayObj)) + offset;
1835 :
1836 377 : if (ar->isDenseArray() && ar->getDenseArrayInitializedLength() >= len) {
1837 250 : JS_ASSERT(ar->getArrayLength() == len);
1838 :
1839 250 : const Value *src = ar->getDenseArrayElements();
1840 :
1841 : /*
1842 : * It is valid to skip the hole check here because nativeFromValue
1843 : * treats a hole as undefined.
1844 : */
1845 572 : for (unsigned i = 0; i < len; ++i)
1846 322 : *dest++ = nativeFromValue(cx, *src++);
1847 : } else {
1848 : Value v;
1849 :
1850 36147 : for (unsigned i = 0; i < len; ++i) {
1851 36020 : if (!ar->getElement(cx, i, &v))
1852 0 : return false;
1853 36020 : *dest++ = nativeFromValue(cx, v);
1854 : }
1855 : }
1856 :
1857 377 : return true;
1858 : }
1859 :
1860 : static bool
1861 9 : copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, uint32_t offset)
1862 : {
1863 9 : thisTypedArrayObj = getTypedArray(thisTypedArrayObj);
1864 9 : JS_ASSERT(thisTypedArrayObj);
1865 :
1866 9 : JS_ASSERT(offset <= getLength(thisTypedArrayObj));
1867 9 : JS_ASSERT(getLength(tarray) <= getLength(thisTypedArrayObj) - offset);
1868 9 : if (getBuffer(tarray) == getBuffer(thisTypedArrayObj))
1869 0 : return copyFromWithOverlap(cx, thisTypedArrayObj, tarray, offset);
1870 :
1871 9 : NativeType *dest = static_cast<NativeType*>((void*)getDataOffset(thisTypedArrayObj)) + offset;
1872 :
1873 9 : if (getType(tarray) == getType(thisTypedArrayObj)) {
1874 9 : js_memcpy(dest, getDataOffset(tarray), getByteLength(tarray));
1875 9 : return true;
1876 : }
1877 :
1878 0 : unsigned srclen = getLength(tarray);
1879 0 : switch (getType(tarray)) {
1880 : case TypedArray::TYPE_INT8: {
1881 0 : int8_t *src = static_cast<int8_t*>(getDataOffset(tarray));
1882 0 : for (unsigned i = 0; i < srclen; ++i)
1883 0 : *dest++ = NativeType(*src++);
1884 0 : break;
1885 : }
1886 : case TypedArray::TYPE_UINT8:
1887 : case TypedArray::TYPE_UINT8_CLAMPED: {
1888 0 : uint8_t *src = static_cast<uint8_t*>(getDataOffset(tarray));
1889 0 : for (unsigned i = 0; i < srclen; ++i)
1890 0 : *dest++ = NativeType(*src++);
1891 0 : break;
1892 : }
1893 : case TypedArray::TYPE_INT16: {
1894 0 : int16_t *src = static_cast<int16_t*>(getDataOffset(tarray));
1895 0 : for (unsigned i = 0; i < srclen; ++i)
1896 0 : *dest++ = NativeType(*src++);
1897 0 : break;
1898 : }
1899 : case TypedArray::TYPE_UINT16: {
1900 0 : uint16_t *src = static_cast<uint16_t*>(getDataOffset(tarray));
1901 0 : for (unsigned i = 0; i < srclen; ++i)
1902 0 : *dest++ = NativeType(*src++);
1903 0 : break;
1904 : }
1905 : case TypedArray::TYPE_INT32: {
1906 0 : int32_t *src = static_cast<int32_t*>(getDataOffset(tarray));
1907 0 : for (unsigned i = 0; i < srclen; ++i)
1908 0 : *dest++ = NativeType(*src++);
1909 0 : break;
1910 : }
1911 : case TypedArray::TYPE_UINT32: {
1912 0 : uint32_t *src = static_cast<uint32_t*>(getDataOffset(tarray));
1913 0 : for (unsigned i = 0; i < srclen; ++i)
1914 0 : *dest++ = NativeType(*src++);
1915 0 : break;
1916 : }
1917 : case TypedArray::TYPE_FLOAT32: {
1918 0 : float *src = static_cast<float*>(getDataOffset(tarray));
1919 0 : for (unsigned i = 0; i < srclen; ++i)
1920 0 : *dest++ = NativeType(*src++);
1921 0 : break;
1922 : }
1923 : case TypedArray::TYPE_FLOAT64: {
1924 0 : double *src = static_cast<double*>(getDataOffset(tarray));
1925 0 : for (unsigned i = 0; i < srclen; ++i)
1926 0 : *dest++ = NativeType(*src++);
1927 0 : break;
1928 : }
1929 : default:
1930 0 : JS_NOT_REACHED("copyFrom with a TypedArray of unknown type");
1931 : break;
1932 : }
1933 :
1934 0 : return true;
1935 : }
1936 :
1937 : static bool
1938 0 : copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, uint32_t offset)
1939 : {
1940 0 : JS_ASSERT(offset <= getLength(self));
1941 :
1942 0 : NativeType *dest = static_cast<NativeType*>(getDataOffset(self)) + offset;
1943 :
1944 0 : if (getType(tarray) == getType(self)) {
1945 0 : memmove(dest, getDataOffset(tarray), getByteLength(tarray));
1946 0 : return true;
1947 : }
1948 :
1949 : // We have to make a copy of the source array here, since
1950 : // there's overlap, and we have to convert types.
1951 0 : void *srcbuf = cx->malloc_(getByteLength(tarray));
1952 0 : if (!srcbuf)
1953 0 : return false;
1954 0 : js_memcpy(srcbuf, getDataOffset(tarray), getByteLength(tarray));
1955 :
1956 0 : switch (getType(tarray)) {
1957 : case TypedArray::TYPE_INT8: {
1958 0 : int8_t *src = (int8_t*) srcbuf;
1959 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1960 0 : *dest++ = NativeType(*src++);
1961 0 : break;
1962 : }
1963 : case TypedArray::TYPE_UINT8:
1964 : case TypedArray::TYPE_UINT8_CLAMPED: {
1965 0 : uint8_t *src = (uint8_t*) srcbuf;
1966 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1967 0 : *dest++ = NativeType(*src++);
1968 0 : break;
1969 : }
1970 : case TypedArray::TYPE_INT16: {
1971 0 : int16_t *src = (int16_t*) srcbuf;
1972 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1973 0 : *dest++ = NativeType(*src++);
1974 0 : break;
1975 : }
1976 : case TypedArray::TYPE_UINT16: {
1977 0 : uint16_t *src = (uint16_t*) srcbuf;
1978 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1979 0 : *dest++ = NativeType(*src++);
1980 0 : break;
1981 : }
1982 : case TypedArray::TYPE_INT32: {
1983 0 : int32_t *src = (int32_t*) srcbuf;
1984 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1985 0 : *dest++ = NativeType(*src++);
1986 0 : break;
1987 : }
1988 : case TypedArray::TYPE_UINT32: {
1989 0 : uint32_t *src = (uint32_t*) srcbuf;
1990 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1991 0 : *dest++ = NativeType(*src++);
1992 0 : break;
1993 : }
1994 : case TypedArray::TYPE_FLOAT32: {
1995 0 : float *src = (float*) srcbuf;
1996 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
1997 0 : *dest++ = NativeType(*src++);
1998 0 : break;
1999 : }
2000 : case TypedArray::TYPE_FLOAT64: {
2001 0 : double *src = (double*) srcbuf;
2002 0 : for (unsigned i = 0; i < getLength(tarray); ++i)
2003 0 : *dest++ = NativeType(*src++);
2004 0 : break;
2005 : }
2006 : default:
2007 0 : JS_NOT_REACHED("copyFromWithOverlap with a TypedArray of unknown type");
2008 : break;
2009 : }
2010 :
2011 0 : UnwantedForeground::free_(srcbuf);
2012 0 : return true;
2013 : }
2014 :
2015 : static void *
2016 5815 : offsetData(JSObject *obj, uint32_t offs) {
2017 5815 : return (void*)(((uint8_t*)getDataOffset(obj)) + offs);
2018 : }
2019 :
2020 : static JSObject *
2021 4770 : createBufferWithSizeAndCount(JSContext *cx, uint32_t count)
2022 : {
2023 4770 : size_t size = sizeof(NativeType);
2024 4770 : if (size != 0 && count >= INT32_MAX / size) {
2025 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2026 : JSMSG_NEED_DIET, "size and count");
2027 0 : return NULL;
2028 : }
2029 :
2030 4770 : int32_t bytelen = size * count;
2031 4770 : return ArrayBuffer::create(cx, bytelen);
2032 : }
2033 : };
2034 :
2035 : class Int8Array : public TypedArrayTemplate<int8_t> {
2036 : public:
2037 : enum { ACTUAL_TYPE = TYPE_INT8 };
2038 : static const JSProtoKey key = JSProto_Int8Array;
2039 : static JSFunctionSpec jsfuncs[];
2040 : };
2041 : class Uint8Array : public TypedArrayTemplate<uint8_t> {
2042 : public:
2043 : enum { ACTUAL_TYPE = TYPE_UINT8 };
2044 : static const JSProtoKey key = JSProto_Uint8Array;
2045 : static JSFunctionSpec jsfuncs[];
2046 : };
2047 : class Int16Array : public TypedArrayTemplate<int16_t> {
2048 : public:
2049 : enum { ACTUAL_TYPE = TYPE_INT16 };
2050 : static const JSProtoKey key = JSProto_Int16Array;
2051 : static JSFunctionSpec jsfuncs[];
2052 : };
2053 : class Uint16Array : public TypedArrayTemplate<uint16_t> {
2054 : public:
2055 : enum { ACTUAL_TYPE = TYPE_UINT16 };
2056 : static const JSProtoKey key = JSProto_Uint16Array;
2057 : static JSFunctionSpec jsfuncs[];
2058 : };
2059 : class Int32Array : public TypedArrayTemplate<int32_t> {
2060 : public:
2061 : enum { ACTUAL_TYPE = TYPE_INT32 };
2062 : static const JSProtoKey key = JSProto_Int32Array;
2063 : static JSFunctionSpec jsfuncs[];
2064 : };
2065 : class Uint32Array : public TypedArrayTemplate<uint32_t> {
2066 : public:
2067 : enum { ACTUAL_TYPE = TYPE_UINT32 };
2068 : static const JSProtoKey key = JSProto_Uint32Array;
2069 : static JSFunctionSpec jsfuncs[];
2070 : };
2071 : class Float32Array : public TypedArrayTemplate<float> {
2072 : public:
2073 : enum { ACTUAL_TYPE = TYPE_FLOAT32 };
2074 : static const JSProtoKey key = JSProto_Float32Array;
2075 : static JSFunctionSpec jsfuncs[];
2076 : };
2077 : class Float64Array : public TypedArrayTemplate<double> {
2078 : public:
2079 : enum { ACTUAL_TYPE = TYPE_FLOAT64 };
2080 : static const JSProtoKey key = JSProto_Float64Array;
2081 : static JSFunctionSpec jsfuncs[];
2082 : };
2083 : class Uint8ClampedArray : public TypedArrayTemplate<uint8_clamped> {
2084 : public:
2085 : enum { ACTUAL_TYPE = TYPE_UINT8_CLAMPED };
2086 : static const JSProtoKey key = JSProto_Uint8ClampedArray;
2087 : static JSFunctionSpec jsfuncs[];
2088 : };
2089 :
2090 : // this default implementation is only valid for integer types
2091 : // less than 32-bits in size.
2092 : template<typename NativeType>
2093 : void
2094 : TypedArrayTemplate<NativeType>::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp)
2095 : {
2096 : JS_STATIC_ASSERT(sizeof(NativeType) < 4);
2097 :
2098 12179 : vp->setInt32(getIndex(tarray, index));
2099 12179 : }
2100 :
2101 : // and we need to specialize for 32-bit integers and floats
2102 : template<>
2103 : void
2104 1777 : TypedArrayTemplate<int32_t>::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp)
2105 : {
2106 1777 : int32_t val = getIndex(tarray, index);
2107 1777 : vp->setInt32(val);
2108 1777 : }
2109 :
2110 : template<>
2111 : void
2112 1379 : TypedArrayTemplate<uint32_t>::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp)
2113 : {
2114 1379 : uint32_t val = getIndex(tarray, index);
2115 1379 : vp->setNumber(val);
2116 1379 : }
2117 :
2118 : template<>
2119 : void
2120 6963 : TypedArrayTemplate<float>::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp)
2121 : {
2122 6963 : float val = getIndex(tarray, index);
2123 6963 : double dval = val;
2124 :
2125 : /*
2126 : * Doubles in typed arrays could be typed-punned arrays of integers. This
2127 : * could allow user code to break the engine-wide invariant that only
2128 : * canonical nans are stored into jsvals, which means user code could
2129 : * confuse the engine into interpreting a double-typed jsval as an
2130 : * object-typed jsval.
2131 : *
2132 : * This could be removed for platforms/compilers known to convert a 32-bit
2133 : * non-canonical nan to a 64-bit canonical nan.
2134 : */
2135 6963 : if (JS_UNLIKELY(JSDOUBLE_IS_NaN(dval)))
2136 433 : dval = js_NaN;
2137 :
2138 6963 : vp->setDouble(dval);
2139 6963 : }
2140 :
2141 : template<>
2142 : void
2143 1553 : TypedArrayTemplate<double>::copyIndexToValue(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp)
2144 : {
2145 1553 : double val = getIndex(tarray, index);
2146 :
2147 : /*
2148 : * Doubles in typed arrays could be typed-punned arrays of integers. This
2149 : * could allow user code to break the engine-wide invariant that only
2150 : * canonical nans are stored into jsvals, which means user code could
2151 : * confuse the engine into interpreting a double-typed jsval as an
2152 : * object-typed jsval.
2153 : */
2154 1553 : if (JS_UNLIKELY(JSDOUBLE_IS_NaN(val)))
2155 421 : val = js_NaN;
2156 :
2157 1553 : vp->setDouble(val);
2158 1553 : }
2159 :
2160 : /***
2161 : *** JS impl
2162 : ***/
2163 :
2164 : /*
2165 : * ArrayBuffer (base)
2166 : */
2167 :
2168 : Class ArrayBuffer::slowClass = {
2169 : "ArrayBuffer",
2170 : JSCLASS_HAS_PRIVATE |
2171 : JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
2172 : JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
2173 : JS_PropertyStub, /* addProperty */
2174 : JS_PropertyStub, /* delProperty */
2175 : JS_PropertyStub, /* getProperty */
2176 : JS_StrictPropertyStub, /* setProperty */
2177 : JS_EnumerateStub,
2178 : JS_ResolveStub,
2179 : JS_ConvertStub,
2180 : JS_FinalizeStub
2181 : };
2182 :
2183 : Class js::ArrayBufferClass = {
2184 : "ArrayBuffer",
2185 : JSCLASS_HAS_PRIVATE |
2186 : JSCLASS_IMPLEMENTS_BARRIERS |
2187 : Class::NON_NATIVE |
2188 : JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
2189 : JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
2190 : JS_PropertyStub, /* addProperty */
2191 : JS_PropertyStub, /* delProperty */
2192 : JS_PropertyStub, /* getProperty */
2193 : JS_StrictPropertyStub, /* setProperty */
2194 : JS_EnumerateStub,
2195 : JS_ResolveStub,
2196 : JS_ConvertStub,
2197 : NULL, /* finalize */
2198 : NULL, /* checkAccess */
2199 : NULL, /* call */
2200 : NULL, /* construct */
2201 : NULL, /* hasInstance */
2202 : ArrayBuffer::obj_trace,
2203 : JS_NULL_CLASS_EXT,
2204 : {
2205 : ArrayBuffer::obj_lookupGeneric,
2206 : ArrayBuffer::obj_lookupProperty,
2207 : ArrayBuffer::obj_lookupElement,
2208 : ArrayBuffer::obj_lookupSpecial,
2209 : ArrayBuffer::obj_defineGeneric,
2210 : ArrayBuffer::obj_defineProperty,
2211 : ArrayBuffer::obj_defineElement,
2212 : ArrayBuffer::obj_defineSpecial,
2213 : ArrayBuffer::obj_getGeneric,
2214 : ArrayBuffer::obj_getProperty,
2215 : ArrayBuffer::obj_getElement,
2216 : ArrayBuffer::obj_getElementIfPresent,
2217 : ArrayBuffer::obj_getSpecial,
2218 : ArrayBuffer::obj_setGeneric,
2219 : ArrayBuffer::obj_setProperty,
2220 : ArrayBuffer::obj_setElement,
2221 : ArrayBuffer::obj_setSpecial,
2222 : ArrayBuffer::obj_getGenericAttributes,
2223 : ArrayBuffer::obj_getPropertyAttributes,
2224 : ArrayBuffer::obj_getElementAttributes,
2225 : ArrayBuffer::obj_getSpecialAttributes,
2226 : ArrayBuffer::obj_setGenericAttributes,
2227 : ArrayBuffer::obj_setPropertyAttributes,
2228 : ArrayBuffer::obj_setElementAttributes,
2229 : ArrayBuffer::obj_setSpecialAttributes,
2230 : ArrayBuffer::obj_deleteProperty,
2231 : ArrayBuffer::obj_deleteElement,
2232 : ArrayBuffer::obj_deleteSpecial,
2233 : ArrayBuffer::obj_enumerate,
2234 : ArrayBuffer::obj_typeOf,
2235 : NULL, /* thisObject */
2236 : NULL, /* clear */
2237 : }
2238 : };
2239 :
2240 : JSPropertySpec ArrayBuffer::jsprops[] = {
2241 : { "byteLength",
2242 : -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
2243 : ArrayBuffer::prop_getByteLength, JS_StrictPropertyStub },
2244 : {0,0,0,0,0}
2245 : };
2246 :
2247 : JSFunctionSpec ArrayBuffer::jsfuncs[] = {
2248 : JS_FN("slice", ArrayBuffer::fun_slice, 2, JSFUN_GENERIC_NATIVE),
2249 : JS_FS_END
2250 : };
2251 :
2252 : /*
2253 : * shared TypedArray
2254 : */
2255 :
2256 : JSPropertySpec TypedArray::jsprops[] = {
2257 : { js_length_str,
2258 : -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
2259 : TypedArray::prop_getLength, JS_StrictPropertyStub },
2260 : { "byteLength",
2261 : -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
2262 : TypedArray::prop_getByteLength, JS_StrictPropertyStub },
2263 : { "byteOffset",
2264 : -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
2265 : TypedArray::prop_getByteOffset, JS_StrictPropertyStub },
2266 : { "buffer",
2267 : -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
2268 : TypedArray::prop_getBuffer, JS_StrictPropertyStub },
2269 : {0,0,0,0,0}
2270 : };
2271 :
2272 : /*
2273 : * TypedArray boilerplate
2274 : */
2275 :
2276 : #define IMPL_TYPED_ARRAY_STATICS(_typedArray) \
2277 : JSFunctionSpec _typedArray::jsfuncs[] = { \
2278 : JS_FN("subarray", _typedArray::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
2279 : JS_FN("set", _typedArray::fun_set, 2, JSFUN_GENERIC_NATIVE), \
2280 : JS_FS_END \
2281 : }
2282 :
2283 : #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray) \
2284 : { \
2285 : #_typedArray, \
2286 : JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
2287 : JSCLASS_HAS_PRIVATE | \
2288 : JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \
2289 : JS_PropertyStub, /* addProperty */ \
2290 : JS_PropertyStub, /* delProperty */ \
2291 : JS_PropertyStub, /* getProperty */ \
2292 : JS_StrictPropertyStub, /* setProperty */ \
2293 : JS_EnumerateStub, \
2294 : JS_ResolveStub, \
2295 : JS_ConvertStub, \
2296 : JS_FinalizeStub \
2297 : }
2298 :
2299 : #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \
2300 : { \
2301 : #_typedArray, \
2302 : JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
2303 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
2304 : JSCLASS_FOR_OF_ITERATION | \
2305 : Class::NON_NATIVE, \
2306 : JS_PropertyStub, /* addProperty */ \
2307 : JS_PropertyStub, /* delProperty */ \
2308 : JS_PropertyStub, /* getProperty */ \
2309 : JS_StrictPropertyStub, /* setProperty */ \
2310 : JS_EnumerateStub, \
2311 : JS_ResolveStub, \
2312 : JS_ConvertStub, \
2313 : NULL, /* finalize */ \
2314 : NULL, /* checkAccess */ \
2315 : NULL, /* call */ \
2316 : NULL, /* construct */ \
2317 : NULL, /* hasInstance */ \
2318 : _typedArray::obj_trace, /* trace */ \
2319 : { \
2320 : NULL, /* equality */ \
2321 : NULL, /* outerObject */ \
2322 : NULL, /* innerObject */ \
2323 : JS_ElementIteratorStub, \
2324 : NULL, /* unused */ \
2325 : false, /* isWrappedNative */ \
2326 : }, \
2327 : { \
2328 : _typedArray::obj_lookupGeneric, \
2329 : _typedArray::obj_lookupProperty, \
2330 : _typedArray::obj_lookupElement, \
2331 : _typedArray::obj_lookupSpecial, \
2332 : _typedArray::obj_defineGeneric, \
2333 : _typedArray::obj_defineProperty, \
2334 : _typedArray::obj_defineElement, \
2335 : _typedArray::obj_defineSpecial, \
2336 : _typedArray::obj_getGeneric, \
2337 : _typedArray::obj_getProperty, \
2338 : _typedArray::obj_getElement, \
2339 : _typedArray::obj_getElementIfPresent, \
2340 : _typedArray::obj_getSpecial, \
2341 : _typedArray::obj_setGeneric, \
2342 : _typedArray::obj_setProperty, \
2343 : _typedArray::obj_setElement, \
2344 : _typedArray::obj_setSpecial, \
2345 : _typedArray::obj_getGenericAttributes, \
2346 : _typedArray::obj_getPropertyAttributes, \
2347 : _typedArray::obj_getElementAttributes, \
2348 : _typedArray::obj_getSpecialAttributes, \
2349 : _typedArray::obj_setGenericAttributes, \
2350 : _typedArray::obj_setPropertyAttributes, \
2351 : _typedArray::obj_setElementAttributes, \
2352 : _typedArray::obj_setSpecialAttributes, \
2353 : _typedArray::obj_deleteProperty, \
2354 : _typedArray::obj_deleteElement, \
2355 : _typedArray::obj_deleteSpecial, \
2356 : _typedArray::obj_enumerate, \
2357 : _typedArray::obj_typeOf, \
2358 : NULL, /* thisObject */ \
2359 : NULL, /* clear */ \
2360 : } \
2361 : }
2362 :
2363 : template<class ArrayType>
2364 : static inline JSObject *
2365 11457 : InitTypedArrayClass(JSContext *cx, GlobalObject *global)
2366 : {
2367 11457 : JSObject *proto = global->createBlankPrototype(cx, ArrayType::slowClass());
2368 11457 : if (!proto)
2369 0 : return NULL;
2370 :
2371 : JSFunction *ctor =
2372 : global->createConstructor(cx, ArrayType::class_constructor, ArrayType::fastClass(),
2373 11457 : cx->runtime->atomState.classAtoms[ArrayType::key], 3);
2374 11457 : if (!ctor)
2375 0 : return NULL;
2376 :
2377 11457 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
2378 0 : return NULL;
2379 :
2380 11457 : if (!ctor->defineProperty(cx, cx->runtime->atomState.BYTES_PER_ELEMENTAtom,
2381 : Int32Value(ArrayType::BYTES_PER_ELEMENT),
2382 : JS_PropertyStub, JS_StrictPropertyStub,
2383 : JSPROP_PERMANENT | JSPROP_READONLY) ||
2384 : !proto->defineProperty(cx, cx->runtime->atomState.BYTES_PER_ELEMENTAtom,
2385 : Int32Value(ArrayType::BYTES_PER_ELEMENT),
2386 : JS_PropertyStub, JS_StrictPropertyStub,
2387 : JSPROP_PERMANENT | JSPROP_READONLY))
2388 : {
2389 0 : return NULL;
2390 : }
2391 :
2392 11457 : if (!DefinePropertiesAndBrand(cx, proto, ArrayType::jsprops, ArrayType::jsfuncs))
2393 0 : return NULL;
2394 :
2395 11457 : if (!DefineConstructorAndPrototype(cx, global, ArrayType::key, ctor, proto))
2396 0 : return NULL;
2397 :
2398 11457 : return proto;
2399 : }
2400 :
2401 : IMPL_TYPED_ARRAY_STATICS(Int8Array);
2402 : IMPL_TYPED_ARRAY_STATICS(Uint8Array);
2403 : IMPL_TYPED_ARRAY_STATICS(Int16Array);
2404 : IMPL_TYPED_ARRAY_STATICS(Uint16Array);
2405 : IMPL_TYPED_ARRAY_STATICS(Int32Array);
2406 : IMPL_TYPED_ARRAY_STATICS(Uint32Array);
2407 : IMPL_TYPED_ARRAY_STATICS(Float32Array);
2408 : IMPL_TYPED_ARRAY_STATICS(Float64Array);
2409 : IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray);
2410 :
2411 : Class TypedArray::fastClasses[TYPE_MAX] = {
2412 : IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array),
2413 : IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array),
2414 : IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array),
2415 : IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array),
2416 : IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array),
2417 : IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array),
2418 : IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array),
2419 : IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array),
2420 : IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray)
2421 : };
2422 :
2423 : Class TypedArray::slowClasses[TYPE_MAX] = {
2424 : IMPL_TYPED_ARRAY_SLOW_CLASS(Int8Array),
2425 : IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8Array),
2426 : IMPL_TYPED_ARRAY_SLOW_CLASS(Int16Array),
2427 : IMPL_TYPED_ARRAY_SLOW_CLASS(Uint16Array),
2428 : IMPL_TYPED_ARRAY_SLOW_CLASS(Int32Array),
2429 : IMPL_TYPED_ARRAY_SLOW_CLASS(Uint32Array),
2430 : IMPL_TYPED_ARRAY_SLOW_CLASS(Float32Array),
2431 : IMPL_TYPED_ARRAY_SLOW_CLASS(Float64Array),
2432 : IMPL_TYPED_ARRAY_SLOW_CLASS(Uint8ClampedArray)
2433 : };
2434 :
2435 : static JSObject *
2436 1273 : InitArrayBufferClass(JSContext *cx, GlobalObject *global)
2437 : {
2438 1273 : JSObject *arrayBufferProto = global->createBlankPrototype(cx, &ArrayBuffer::slowClass);
2439 1273 : if (!arrayBufferProto)
2440 0 : return NULL;
2441 :
2442 : JSFunction *ctor =
2443 : global->createConstructor(cx, ArrayBuffer::class_constructor, &ArrayBufferClass,
2444 1273 : CLASS_ATOM(cx, ArrayBuffer), 1);
2445 1273 : if (!ctor)
2446 0 : return NULL;
2447 :
2448 1273 : if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto))
2449 0 : return NULL;
2450 :
2451 1273 : if (!DefinePropertiesAndBrand(cx, arrayBufferProto, ArrayBuffer::jsprops, ArrayBuffer::jsfuncs))
2452 0 : return NULL;
2453 :
2454 1273 : if (!DefineConstructorAndPrototype(cx, global, JSProto_ArrayBuffer, ctor, arrayBufferProto))
2455 0 : return NULL;
2456 :
2457 1273 : return arrayBufferProto;
2458 : }
2459 :
2460 : JS_FRIEND_API(JSObject *)
2461 2625 : js_InitTypedArrayClasses(JSContext *cx, JSObject *obj)
2462 : {
2463 2625 : JS_ASSERT(obj->isNative());
2464 :
2465 2625 : GlobalObject *global = &obj->asGlobal();
2466 :
2467 : /* Idempotency required: we initialize several things, possibly lazily. */
2468 : JSObject *stop;
2469 2625 : if (!js_GetClassObject(cx, global, JSProto_ArrayBuffer, &stop))
2470 0 : return NULL;
2471 2625 : if (stop)
2472 1352 : return stop;
2473 :
2474 11457 : if (!InitTypedArrayClass<Int8Array>(cx, global) ||
2475 1273 : !InitTypedArrayClass<Uint8Array>(cx, global) ||
2476 1273 : !InitTypedArrayClass<Int16Array>(cx, global) ||
2477 1273 : !InitTypedArrayClass<Uint16Array>(cx, global) ||
2478 1273 : !InitTypedArrayClass<Int32Array>(cx, global) ||
2479 1273 : !InitTypedArrayClass<Uint32Array>(cx, global) ||
2480 1273 : !InitTypedArrayClass<Float32Array>(cx, global) ||
2481 1273 : !InitTypedArrayClass<Float64Array>(cx, global) ||
2482 1273 : !InitTypedArrayClass<Uint8ClampedArray>(cx, global))
2483 : {
2484 0 : return NULL;
2485 : }
2486 :
2487 1273 : return InitArrayBufferClass(cx, global);
2488 : }
2489 :
2490 : JS_FRIEND_API(JSBool)
2491 649 : js_IsArrayBuffer(JSObject *obj)
2492 : {
2493 649 : JS_ASSERT(obj);
2494 649 : return obj->isArrayBuffer();
2495 : }
2496 :
2497 : JS_FRIEND_API(JSBool)
2498 0 : JS_IsArrayBufferObject(JSObject *obj)
2499 : {
2500 0 : return js_IsArrayBuffer(obj);
2501 : }
2502 :
2503 : namespace js {
2504 :
2505 : bool
2506 58920966 : IsFastTypedArrayClass(const Class *clasp)
2507 : {
2508 : return &TypedArray::fastClasses[0] <= clasp &&
2509 58920966 : clasp < &TypedArray::fastClasses[TypedArray::TYPE_MAX];
2510 : }
2511 :
2512 : } // namespace js
2513 :
2514 : uint32_t
2515 0 : JS_GetArrayBufferByteLength(JSObject *obj)
2516 : {
2517 0 : return obj->arrayBufferByteLength();
2518 : }
2519 :
2520 : uint8_t *
2521 2 : JS_GetArrayBufferData(JSObject *obj)
2522 : {
2523 2 : return obj->arrayBufferDataOffset();
2524 : }
2525 :
2526 : JS_FRIEND_API(JSBool)
2527 58920966 : js_IsTypedArray(JSObject *obj)
2528 : {
2529 58920966 : JS_ASSERT(obj);
2530 58920966 : Class *clasp = obj->getClass();
2531 58920966 : return IsFastTypedArrayClass(clasp);
2532 : }
2533 :
2534 : JS_FRIEND_API(JSObject *)
2535 2 : js_CreateArrayBuffer(JSContext *cx, uint32_t nbytes)
2536 : {
2537 2 : return ArrayBuffer::create(cx, nbytes);
2538 : }
2539 :
2540 : JS_FRIEND_API(JSObject *)
2541 0 : JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
2542 : {
2543 0 : return js_CreateArrayBuffer(cx, nbytes);
2544 : }
2545 :
2546 : static inline JSObject *
2547 54 : TypedArrayConstruct(JSContext *cx, int atype, unsigned argc, Value *argv)
2548 : {
2549 54 : switch (atype) {
2550 : case TypedArray::TYPE_INT8:
2551 0 : return Int8Array::create(cx, argc, argv);
2552 :
2553 : case TypedArray::TYPE_UINT8:
2554 54 : return Uint8Array::create(cx, argc, argv);
2555 :
2556 : case TypedArray::TYPE_INT16:
2557 0 : return Int16Array::create(cx, argc, argv);
2558 :
2559 : case TypedArray::TYPE_UINT16:
2560 0 : return Uint16Array::create(cx, argc, argv);
2561 :
2562 : case TypedArray::TYPE_INT32:
2563 0 : return Int32Array::create(cx, argc, argv);
2564 :
2565 : case TypedArray::TYPE_UINT32:
2566 0 : return Uint32Array::create(cx, argc, argv);
2567 :
2568 : case TypedArray::TYPE_FLOAT32:
2569 0 : return Float32Array::create(cx, argc, argv);
2570 :
2571 : case TypedArray::TYPE_FLOAT64:
2572 0 : return Float64Array::create(cx, argc, argv);
2573 :
2574 : case TypedArray::TYPE_UINT8_CLAMPED:
2575 0 : return Uint8ClampedArray::create(cx, argc, argv);
2576 :
2577 : default:
2578 0 : JS_NOT_REACHED("shouldn't have gotten here");
2579 : return NULL;
2580 : }
2581 : }
2582 :
2583 : JS_FRIEND_API(JSObject *)
2584 54 : js_CreateTypedArray(JSContext *cx, int atype, uint32_t nelements)
2585 : {
2586 54 : JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
2587 :
2588 54 : Value nelems = Int32Value(nelements);
2589 54 : return TypedArrayConstruct(cx, atype, 1, &nelems);
2590 : }
2591 :
2592 : JS_FRIEND_API(JSObject *)
2593 0 : js_CreateTypedArrayWithArray(JSContext *cx, int atype, JSObject *arrayArg)
2594 : {
2595 0 : JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
2596 :
2597 0 : Value arrval = ObjectValue(*arrayArg);
2598 0 : return TypedArrayConstruct(cx, atype, 1, &arrval);
2599 : }
2600 :
2601 : JS_FRIEND_API(JSObject *)
2602 0 : js_CreateTypedArrayWithBuffer(JSContext *cx, int atype, JSObject *bufArg,
2603 : int byteoffset, int length)
2604 : {
2605 0 : JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX);
2606 0 : JS_ASSERT(bufArg && js_IsArrayBuffer(bufArg));
2607 0 : JS_ASSERT_IF(byteoffset < 0, length < 0);
2608 :
2609 : Value vals[4];
2610 :
2611 0 : int argc = 1;
2612 0 : vals[0].setObject(*bufArg);
2613 :
2614 0 : if (byteoffset >= 0) {
2615 0 : vals[argc].setInt32(byteoffset);
2616 0 : argc++;
2617 : }
2618 :
2619 0 : if (length >= 0) {
2620 0 : vals[argc].setInt32(length);
2621 0 : argc++;
2622 : }
2623 :
2624 0 : AutoArrayRooter tvr(cx, ArrayLength(vals), vals);
2625 0 : return TypedArrayConstruct(cx, atype, argc, &vals[0]);
2626 : }
2627 :
2628 : uint32_t
2629 13 : JS_GetTypedArrayLength(JSObject *obj)
2630 : {
2631 13 : return obj->getSlot(TypedArray::FIELD_LENGTH).toInt32();
2632 : }
2633 :
2634 : uint32_t
2635 0 : JS_GetTypedArrayByteOffset(JSObject *obj)
2636 : {
2637 0 : return obj->getSlot(TypedArray::FIELD_BYTEOFFSET).toInt32();
2638 : }
2639 :
2640 : uint32_t
2641 0 : JS_GetTypedArrayByteLength(JSObject *obj)
2642 : {
2643 0 : return obj->getSlot(TypedArray::FIELD_BYTELENGTH).toInt32();
2644 : }
2645 :
2646 : uint32_t
2647 11 : JS_GetTypedArrayType(JSObject *obj)
2648 : {
2649 11 : return obj->getSlot(TypedArray::FIELD_TYPE).toInt32();
2650 : }
2651 :
2652 : JSObject *
2653 0 : JS_GetTypedArrayBuffer(JSObject *obj)
2654 : {
2655 0 : return (JSObject *) obj->getSlot(TypedArray::FIELD_BUFFER).toPrivate();
2656 : }
2657 :
2658 : void *
2659 9 : JS_GetTypedArrayData(JSObject *obj)
2660 : {
2661 9 : return TypedArray::getDataOffset(obj);
2662 : }
|