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