Bug 1054882 - Rollup patch. r=till, r=sstangl, r=jorendorff

This commit is contained in:
Lars T Hansen 2014-09-16 18:45:31 +02:00
parent faf0f8344b
commit ef7ed4ebde
56 changed files with 3131 additions and 923 deletions

View File

@ -75,6 +75,15 @@ var ecmaGlobals =
"RegExp",
"Set",
{name: "SharedArrayBuffer", nightly: true},
{name: "SharedInt8Array", nightly: true},
{name: "SharedUint8Array", nightly: true},
{name: "SharedUint8ClampedArray", nightly: true},
{name: "SharedInt16Array", nightly: true},
{name: "SharedUint16Array", nightly: true},
{name: "SharedInt32Array", nightly: true},
{name: "SharedUint32Array", nightly: true},
{name: "SharedFloat32Array", nightly: true},
{name: "SharedFloat64Array", nightly: true},
{name: "SIMD", nightly: true},
"StopIteration",
"String",

View File

@ -49,6 +49,15 @@ var ecmaGlobals =
"RegExp",
"Set",
{name: "SharedArrayBuffer", nightly: true},
{name: "SharedInt8Array", nightly: true},
{name: "SharedUint8Array", nightly: true},
{name: "SharedUint8ClampedArray", nightly: true},
{name: "SharedInt16Array", nightly: true},
{name: "SharedUint16Array", nightly: true},
{name: "SharedInt32Array", nightly: true},
{name: "SharedUint32Array", nightly: true},
{name: "SharedFloat32Array", nightly: true},
{name: "SharedFloat64Array", nightly: true},
{name: "SIMD", nightly: true},
"StopIteration",
"String",

View File

@ -532,8 +532,8 @@ Valueify(const JSClass *c)
*/
enum ESClassValue {
ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_Date,
ESClass_Set, ESClass_Map
ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
ESClass_Date, ESClass_Set, ESClass_Map
};
/*

View File

@ -37,10 +37,14 @@
#ifdef JS_ION_PERF
# include "jit/PerfSpewer.h"
#endif
#include "vm/ArrayBufferObject.h"
#include "vm/SharedArrayObject.h"
#include "vm/StringBuffer.h"
#include "jsobjinlines.h"
#include "vm/ArrayBufferObject-inl.h"
using namespace js;
using namespace js::jit;
@ -222,8 +226,11 @@ ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue global
if (!GetDataProperty(cx, globalVal, field, &v))
return false;
if (!IsTypedArrayConstructor(v, global.viewType()))
if (!IsTypedArrayConstructor(v, global.viewType()) &&
!IsSharedTypedArrayConstructor(v, global.viewType()))
{
return LinkFail(cx, "bad typed array constructor");
}
return true;
}
@ -417,7 +424,7 @@ ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalV
}
static bool
LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*> heap)
LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObjectMaybeShared*> heap)
{
uint32_t heapLength = heap->byteLength();
@ -462,8 +469,11 @@ LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObject*>
if (module.usesSignalHandlersForInterrupt() && !cx->canUseSignalHandlers())
return LinkFail(cx, "Code generated with signal handlers but signals are deactivated");
if (!ArrayBufferObject::prepareForAsmJS(cx, heap, module.usesSignalHandlersForOOB()))
return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
if (heap->is<ArrayBufferObject>()) {
Rooted<ArrayBufferObject *> abheap(cx, &heap->as<ArrayBufferObject>());
if (!ArrayBufferObject::prepareForAsmJS(cx, abheap, module.usesSignalHandlersForOOB()))
return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
}
module.initHeap(heap, cx);
return true;
@ -486,12 +496,12 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
if (args.length() > 2)
bufferVal = args[2];
Rooted<ArrayBufferObject*> heap(cx);
Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
if (module.hasArrayView()) {
if (!IsTypedArrayBuffer(bufferVal))
if (IsArrayBuffer(bufferVal) || IsSharedArrayBuffer(bufferVal))
heap = &AsAnyArrayBuffer(bufferVal);
else
return LinkFail(cx, "bad ArrayBuffer argument");
heap = &AsTypedArrayBuffer(bufferVal);
if (!LinkModuleToHeap(cx, module, heap))
return false;
}
@ -639,7 +649,10 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
// which is normally immutable except for the neuter operation that occurs
// when an ArrayBuffer is transfered. Throw an internal error if we're
// about to run with a neutered heap.
if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) {
if (module.maybeHeapBufferObject() &&
module.maybeHeapBufferObject()->is<ArrayBufferObject>() &&
module.maybeHeapBufferObject()->as<ArrayBufferObject>().isNeutered())
{
js_ReportOverRecursed(cx);
return false;
}

View File

@ -44,6 +44,7 @@
#include "jsobjinlines.h"
#include "frontend/ParseNode-inl.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
@ -742,7 +743,7 @@ AsmJSModule::staticallyLink(ExclusiveContext *cx)
}
void
AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx)
{
JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
JS_ASSERT(dynamicallyLinked_);
@ -786,7 +787,8 @@ AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
}
void
AsmJSModule::restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer,
AsmJSModule::restoreToInitialState(uint8_t *prevCode,
ArrayBufferObjectMaybeShared *maybePrevBuffer,
ExclusiveContext *cx)
{
#ifdef DEBUG

View File

@ -801,7 +801,7 @@ class AsmJSModule
uint8_t * code_;
uint8_t * interruptExit_;
StaticLinkData staticLinkData_;
HeapPtrArrayBufferObject maybeHeap_;
HeapPtrArrayBufferObjectMaybeShared maybeHeap_;
bool dynamicallyLinked_;
bool loadedFromCache_;
bool profilingEnabled_;
@ -1387,9 +1387,10 @@ class AsmJSModule
dynamicallyLinked_ = true;
JS_ASSERT(isDynamicallyLinked());
}
void initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx);
void initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext *cx);
bool clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const;
void restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer,
void restoreToInitialState(uint8_t *prevCode,
ArrayBufferObjectMaybeShared *maybePrevBuffer,
ExclusiveContext *cx);
/*************************************************************************/
@ -1407,7 +1408,7 @@ class AsmJSModule
JS_ASSERT(isDynamicallyLinked());
return heapDatum();
}
ArrayBufferObject *maybeHeapBufferObject() const {
ArrayBufferObjectMaybeShared *maybeHeapBufferObject() const {
JS_ASSERT(isDynamicallyLinked());
return maybeHeap_;
}

View File

@ -3515,21 +3515,21 @@ CheckNewArrayView(ModuleCompiler &m, PropertyName *varName, ParseNode *newExpr)
JSAtomState &names = m.cx()->names();
Scalar::Type type;
if (field == names.Int8Array)
if (field == names.Int8Array || field == names.SharedInt8Array)
type = Scalar::Int8;
else if (field == names.Uint8Array)
else if (field == names.Uint8Array || field == names.SharedUint8Array)
type = Scalar::Uint8;
else if (field == names.Int16Array)
else if (field == names.Int16Array || field == names.SharedInt16Array)
type = Scalar::Int16;
else if (field == names.Uint16Array)
else if (field == names.Uint16Array || field == names.SharedUint16Array)
type = Scalar::Uint16;
else if (field == names.Int32Array)
else if (field == names.Int32Array || field == names.SharedInt32Array)
type = Scalar::Int32;
else if (field == names.Uint32Array)
else if (field == names.Uint32Array || field == names.SharedUint32Array)
type = Scalar::Uint32;
else if (field == names.Float32Array)
else if (field == names.Float32Array || field == names.SharedFloat32Array)
type = Scalar::Float32;
else if (field == names.Float64Array)
else if (field == names.Float64Array || field == names.SharedFloat64Array)
type = Scalar::Float64;
else
return m.fail(ctorExpr, "could not match typed array name");

View File

@ -162,9 +162,11 @@ class Symbol;
namespace js {
class ArgumentsObject;
class ArrayBufferObjectMaybeShared;
class ArrayBufferObject;
class ArrayBufferViewObject;
class SharedArrayBufferObject;
class SharedTypedArrayObject;
class BaseShape;
class DebugScopeObject;
class GlobalObject;
@ -205,6 +207,7 @@ namespace gc {
template <typename T> struct MapTypeToTraceKind {};
template <> struct MapTypeToTraceKind<ArgumentsObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ArrayBufferObjectMaybeShared>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<ArrayBufferViewObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<BaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
template <> struct MapTypeToTraceKind<DebugScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
@ -225,6 +228,7 @@ template <> struct MapTypeToTraceKind<SavedFrame> { static const JSGCTrace
template <> struct MapTypeToTraceKind<ScopeObject> { static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<Shape> { static const JSGCTraceKind kind = JSTRACE_SHAPE; };
template <> struct MapTypeToTraceKind<SharedArrayBufferObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<SharedTypedArrayObject>{ static const JSGCTraceKind kind = JSTRACE_OBJECT; };
template <> struct MapTypeToTraceKind<UnownedBaseShape> { static const JSGCTraceKind kind = JSTRACE_BASE_SHAPE; };
template <> struct MapTypeToTraceKind<jit::JitCode> { static const JSGCTraceKind kind = JSTRACE_JITCODE; };
template <> struct MapTypeToTraceKind<types::TypeObject>{ static const JSGCTraceKind kind = JSTRACE_TYPE_OBJECT; };
@ -893,6 +897,7 @@ typedef RelocatablePtr<JSObject*> RelocatablePtrObject;
typedef RelocatablePtr<JSScript*> RelocatablePtrScript;
typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
typedef HeapPtr<JSAtom*> HeapPtrAtom;

View File

@ -627,8 +627,8 @@ DeclMarkerImpl(BaseShape, UnownedBaseShape)
DeclMarkerImpl(JitCode, jit::JitCode)
DeclMarkerImpl(Object, ArgumentsObject)
DeclMarkerImpl(Object, ArrayBufferObject)
DeclMarkerImpl(Object, ArrayBufferObjectMaybeShared)
DeclMarkerImpl(Object, ArrayBufferViewObject)
DeclMarkerImpl(Object, SharedArrayBufferObject)
DeclMarkerImpl(Object, DebugScopeObject)
DeclMarkerImpl(Object, GlobalObject)
DeclMarkerImpl(Object, JSObject)
@ -637,6 +637,8 @@ DeclMarkerImpl(Object, NestedScopeObject)
DeclMarkerImpl(Object, ObjectImpl)
DeclMarkerImpl(Object, SavedFrame)
DeclMarkerImpl(Object, ScopeObject)
DeclMarkerImpl(Object, SharedArrayBufferObject)
DeclMarkerImpl(Object, SharedTypedArrayObject)
DeclMarkerImpl(Script, JSScript)
DeclMarkerImpl(LazyScript, LazyScript)
DeclMarkerImpl(Shape, Shape)

View File

@ -107,8 +107,8 @@ DeclMarker(BaseShape, UnownedBaseShape)
DeclMarker(JitCode, jit::JitCode)
DeclMarker(Object, ArgumentsObject)
DeclMarker(Object, ArrayBufferObject)
DeclMarker(Object, ArrayBufferObjectMaybeShared)
DeclMarker(Object, ArrayBufferViewObject)
DeclMarker(Object, SharedArrayBufferObject)
DeclMarker(Object, DebugScopeObject)
DeclMarker(Object, GlobalObject)
DeclMarker(Object, JSObject)
@ -116,6 +116,8 @@ DeclMarker(Object, JSFunction)
DeclMarker(Object, NestedScopeObject)
DeclMarker(Object, SavedFrame)
DeclMarker(Object, ScopeObject)
DeclMarker(Object, SharedArrayBufferObject)
DeclMarker(Object, SharedTypedArrayObject)
DeclMarker(Script, JSScript)
DeclMarker(LazyScript, LazyScript)
DeclMarker(Shape, Shape)

View File

@ -1,10 +1,15 @@
// Don't assert.
// Don't assert on linking.
// Provide superficial functionality.
function $(stdlib, foreign, heap) {
"use asm";
var Float64ArrayView = new stdlib.Float64Array(heap);
function f() {}
var f64 = new stdlib.SharedFloat64Array(heap);
function f() { var v=0.0; v=+f64[0]; return +v; }
return f
}
if (typeof SharedArrayBuffer !== "undefined")
$(this, {}, new SharedArrayBuffer(4096));
if (this.SharedArrayBuffer && this.SharedFloat64Array) {
var heap = new SharedArrayBuffer(65536);
(new SharedFloat64Array(heap))[0] = 3.14159;
assertEq($(this, {}, heap)(), 3.14159);
}

View File

@ -2,9 +2,9 @@
function f() {
var x = new SharedArrayBuffer(0x1000);
var y = new Int32Array(x);
var y = new SharedInt32Array(x);
gc();
}
if (typeof SharedArrayBuffer !== "undefined")
if (this.SharedArrayBuffer && this.SharedInt32Array)
f();

View File

@ -2,10 +2,10 @@
function f() {
var x = new SharedArrayBuffer(0x1000);
var y = new Int32Array(x);
var z = new Int8Array(x);
var y = new SharedInt32Array(x);
var z = new SharedInt8Array(x);
gc();
}
if (typeof SharedArrayBuffer !== "undefined")
if (this.SharedArrayBuffer && this.SharedInt32Array && this.SharedInt8Array)
f();

View File

@ -0,0 +1,29 @@
// |jit-test| slow;
//
// This is for testing inlining behavior in the jits.
//
// For Baseline, run:
// $ IONFLAGS=bl-ic .../js --ion-off --baseline-eager inline-access.js
// Then inspect the output, there should be calls to "GetElem(TypedArray[Int32])",
// "GetProp(NativeObj/NativeGetter 0x...)", and "SetElem_TypedArray stub"
// for the read access, length access, and write access respectively, within f.
//
// For Ion, run:
// $ IONFLAGS=logs .../js --ion-offthread-compile=off inline-access.js
// Then postprocess with iongraph and verify (by inspecting MIR late in the pipeline)
// that it contains instructions like "typedarraylength", "loadtypedarrayelement",
// and "storetypedarrayelement".
if (!this.SharedInt32Array)
quit();
function f(ta) {
return (ta[2] = ta[0] + ta[1] + ta.length);
}
var v = new SharedInt32Array(1024);
var sum = 0;
var iter = 1000;
for ( var i=0 ; i < iter ; i++ )
sum += f(v);
assertEq(sum, v.length * iter);

View File

@ -2,11 +2,11 @@
function f() {
var x = new SharedArrayBuffer(4096);
var y = new Int32Array(x);
var y = new SharedInt32Array(x);
assertEq(y[0], 0);
assertEq(y[1], 0);
assertEq(y[1023], 0);
}
if (typeof SharedArrayBuffer !== "undefined")
if (this.SharedArrayBuffer && this.SharedInt32Array)
f();

View File

@ -24,6 +24,7 @@
#endif
#include "jit/VMFunctions.h"
#include "vm/Opcodes.h"
#include "vm/TypedArrayCommon.h"
#include "jsboolinlines.h"
#include "jsscriptinlines.h"
@ -3858,9 +3859,9 @@ static bool TryAttachNativeGetElemStub(JSContext *cx, HandleScript script, jsbyt
}
static bool
TypedArrayRequiresFloatingPoint(TypedArrayObject *tarr)
TypedArrayRequiresFloatingPoint(HandleObject obj)
{
uint32_t type = tarr->type();
uint32_t type = AnyTypedArrayType(obj);
return type == Scalar::Uint32 ||
type == Scalar::Float32 ||
type == Scalar::Float64;
@ -3931,7 +3932,7 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
if (obj->isNative()) {
// Check for NativeObject[int] dense accesses.
if (rhs.isInt32() && rhs.toInt32() >= 0 && !obj->is<TypedArrayObject>()) {
if (rhs.isInt32() && rhs.toInt32() >= 0 && !IsAnyTypedArray(obj.get())) {
JitSpew(JitSpew_BaselineIC, " Generating GetElem(Native[Int32] dense) stub");
ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
obj->lastProperty(), isCallElem);
@ -3953,7 +3954,7 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
}
// Check for TypedArray[int] => Number accesses.
if (obj->is<TypedArrayObject>() && rhs.isNumber() && res.isNumber() &&
if (IsAnyTypedArray(obj.get()) && rhs.isNumber() && res.isNumber() &&
!TypedArrayGetElemStubExists(stub, obj))
{
// Don't attach CALLELEM stubs for accesses on typed array expected to yield numbers.
@ -3962,15 +3963,16 @@ TryAttachGetElemStub(JSContext *cx, JSScript *script, jsbytecode *pc, ICGetElem_
return true;
#endif
TypedArrayObject *tarr = &obj->as<TypedArrayObject>();
if (!cx->runtime()->jitSupportsFloatingPoint &&
(TypedArrayRequiresFloatingPoint(tarr) || rhs.isDouble()))
(TypedArrayRequiresFloatingPoint(obj) || rhs.isDouble()))
{
return true;
}
JitSpew(JitSpew_BaselineIC, " Generating GetElem(TypedArray[Int32]) stub");
ICGetElem_TypedArray::Compiler compiler(cx, tarr->lastProperty(), tarr->type());
ICGetElem_TypedArray::Compiler compiler(cx,
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()));
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
return false;
@ -4612,11 +4614,11 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
Register key = masm.extractInt32(R1, ExtractTemp1);
// Bounds check.
masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), scratchReg);
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
// Load the elements vector.
masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), scratchReg);
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
// Load the value.
BaseIndex source(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
@ -4632,7 +4634,7 @@ ICGetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
}
//
// GetEelem_Arguments
// GetElem_Arguments
//
bool
ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
@ -5026,7 +5028,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
// Try to generate new stubs.
if (obj->isNative() &&
!obj->is<TypedArrayObject>() &&
!IsAnyTypedArray(obj.get()) &&
index.isInt32() && index.toInt32() >= 0 &&
!rhs.isMagic(JS_ELEMENTS_HOLE))
{
@ -5074,27 +5076,30 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub_
return true;
}
if (obj->is<TypedArrayObject>() && index.isNumber() && rhs.isNumber()) {
Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
if (IsAnyTypedArray(obj.get()) && index.isNumber() && rhs.isNumber()) {
if (!cx->runtime()->jitSupportsFloatingPoint &&
(TypedArrayRequiresFloatingPoint(tarr) || index.isDouble()))
(TypedArrayRequiresFloatingPoint(obj) || index.isDouble()))
{
return true;
}
uint32_t len = tarr->length();
uint32_t len = AnyTypedArrayLength(obj.get());
double idx = index.toNumber();
bool expectOutOfBounds = (idx < 0 || idx >= double(len));
if (!TypedArraySetElemStubExists(stub, tarr, expectOutOfBounds)) {
if (!TypedArraySetElemStubExists(stub, obj, expectOutOfBounds)) {
// Remove any existing TypedArraySetElemStub that doesn't handle out-of-bounds
if (expectOutOfBounds)
RemoveExistingTypedArraySetElemStub(cx, stub, tarr);
RemoveExistingTypedArraySetElemStub(cx, stub, obj);
JitSpew(JitSpew_BaselineIC,
" Generating SetElem_TypedArray stub (shape=%p, type=%u, oob=%s)",
tarr->lastProperty(), tarr->type(), expectOutOfBounds ? "yes" : "no");
ICSetElem_TypedArray::Compiler compiler(cx, tarr->lastProperty(), tarr->type(),
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()),
expectOutOfBounds ? "yes" : "no");
ICSetElem_TypedArray::Compiler compiler(cx,
AnyTypedArrayShape(obj.get()),
AnyTypedArrayType(obj.get()),
expectOutOfBounds);
ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
if (!typedArrayStub)
@ -5516,12 +5521,12 @@ ICSetElem_TypedArray::Compiler::generateStubCode(MacroAssembler &masm)
// Bounds check.
Label oobWrite;
masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), scratchReg);
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), scratchReg);
masm.branch32(Assembler::BelowOrEqual, scratchReg, key,
expectOutOfBounds_ ? &oobWrite : &failure);
// Load the elements vector.
masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), scratchReg);
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), scratchReg);
BaseIndex dest(scratchReg, key, ScaleFromElemWidth(Scalar::byteSize(type_)));
Address value(BaselineStackReg, ICStackValueOffset);

View File

@ -18,6 +18,9 @@
#include "jit/BaselineRegisters.h"
namespace js {
class TypedArrayLayout;
namespace jit {
//

View File

@ -4357,7 +4357,7 @@ CodeGenerator::visitTypedArrayLength(LTypedArrayLength *lir)
{
Register obj = ToRegister(lir->object());
Register out = ToRegister(lir->output());
masm.unboxInt32(Address(obj, TypedArrayObject::lengthOffset()), out);
masm.unboxInt32(Address(obj, TypedArrayLayout::lengthOffset()), out);
return true;
}
@ -4366,7 +4366,7 @@ CodeGenerator::visitTypedArrayElements(LTypedArrayElements *lir)
{
Register obj = ToRegister(lir->object());
Register out = ToRegister(lir->output());
masm.loadPtr(Address(obj, TypedArrayObject::dataOffset()), out);
masm.loadPtr(Address(obj, TypedArrayLayout::dataOffset()), out);
return true;
}
@ -8200,7 +8200,7 @@ CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir)
// Load the length.
Register scratch = out.scratchReg();
Int32Key key = ToInt32Key(lir->index());
masm.unboxInt32(Address(object, TypedArrayObject::lengthOffset()), scratch);
masm.unboxInt32(Address(object, TypedArrayLayout::lengthOffset()), scratch);
// Load undefined unless length > key.
Label inbounds, done;
@ -8210,7 +8210,7 @@ CodeGenerator::visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir)
// Load the elements vector.
masm.bind(&inbounds);
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), scratch);
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), scratch);
Scalar::Type arrayType = lir->mir()->arrayType();
int width = Scalar::byteSize(arrayType);

View File

@ -7414,7 +7414,7 @@ IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *
JS_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsTypedArray(obj, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
return true;
if (!LIRGenerator::allowStaticTypedArrayAccesses())
@ -7430,14 +7430,12 @@ IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *
if (!tarrObj)
return true;
TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj);
if (tarrType->unknownProperties())
return true;
// LoadTypedArrayElementStatic currently treats uint32 arrays as int32.
Scalar::Type viewType = tarr->type();
Scalar::Type viewType = AnyTypedArrayType(tarrObj);
if (viewType == Scalar::Uint32)
return true;
@ -7446,12 +7444,14 @@ IonBuilder::getElemTryTypedStatic(bool *emitted, MDefinition *obj, MDefinition *
return true;
// Emit LoadTypedArrayElementStatic.
tarrType->watchStateChangeForTypedArrayData(constraints());
if (tarrObj->is<TypedArrayObject>())
tarrType->watchStateChangeForTypedArrayData(constraints());
obj->setImplicitlyUsedUnchecked();
index->setImplicitlyUsedUnchecked();
MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(alloc(), tarr, ptr);
MLoadTypedArrayElementStatic *load = MLoadTypedArrayElementStatic::New(alloc(), tarrObj, ptr);
current->add(load);
current->push(load);
@ -7480,7 +7480,7 @@ IonBuilder::getElemTryTypedArray(bool *emitted, MDefinition *obj, MDefinition *i
JS_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsTypedArray(obj, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(obj, index, &arrayType))
return true;
// Emit typed getelem variant.
@ -7804,8 +7804,8 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
MOZ_ASSERT((index != nullptr) == (elements != nullptr));
if (obj->isConstant() && obj->toConstant()->value().isObject()) {
TypedArrayObject *tarr = &obj->toConstant()->value().toObject().as<TypedArrayObject>();
void *data = tarr->viewData();
JSObject *tarr = &obj->toConstant()->value().toObject();
void *data = AnyTypedArrayViewData(tarr);
// Bug 979449 - Optimistically embed the elements and use TI to
// invalidate if we move them.
#ifdef JSGC_GENERATIONAL
@ -7814,15 +7814,16 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
bool isTenured = true;
#endif
if (isTenured && tarr->hasSingletonType()) {
// The 'data' pointer can change in rare circumstances
// The 'data' pointer of TypedArrayObject can change in rare circumstances
// (ArrayBufferObject::changeContents).
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
if (!tarrType->unknownProperties()) {
tarrType->watchStateChangeForTypedArrayData(constraints());
if (tarr->is<TypedArrayObject>())
tarrType->watchStateChangeForTypedArrayData(constraints());
obj->setImplicitlyUsedUnchecked();
int32_t len = AssertedCast<int32_t>(tarr->length());
int32_t len = AssertedCast<int32_t>(AnyTypedArrayLength(tarr));
*length = MConstant::New(alloc(), Int32Value(len));
current->add(*length);
@ -8116,7 +8117,7 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object,
JS_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsTypedArray(object, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
return true;
if (!LIRGenerator::allowStaticTypedArrayAccesses())
@ -8131,24 +8132,24 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object,
if (!tarrObj)
return true;
TypedArrayObject *tarr = &tarrObj->as<TypedArrayObject>();
#ifdef JSGC_GENERATIONAL
if (tarr->runtimeFromMainThread()->gc.nursery.isInside(tarr->viewData()))
if (tarrObj->runtimeFromMainThread()->gc.nursery.isInside(AnyTypedArrayViewData(tarrObj)))
return true;
#endif
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarr);
types::TypeObjectKey *tarrType = types::TypeObjectKey::get(tarrObj);
if (tarrType->unknownProperties())
return true;
Scalar::Type viewType = tarr->type();
Scalar::Type viewType = AnyTypedArrayType(tarrObj);
MDefinition *ptr = convertShiftToMaskForStaticTypedArray(index, viewType);
if (!ptr)
return true;
// Emit StoreTypedArrayElementStatic.
tarrType->watchStateChangeForTypedArrayData(constraints());
if (tarrObj->is<TypedArrayObject>())
tarrType->watchStateChangeForTypedArrayData(constraints());
object->setImplicitlyUsedUnchecked();
index->setImplicitlyUsedUnchecked();
@ -8160,7 +8161,7 @@ IonBuilder::setElemTryTypedStatic(bool *emitted, MDefinition *object,
current->add(toWrite->toInstruction());
}
MInstruction *store = MStoreTypedArrayElementStatic::New(alloc(), tarr, ptr, toWrite);
MInstruction *store = MStoreTypedArrayElementStatic::New(alloc(), tarrObj, ptr, toWrite);
current->add(store);
current->push(value);
@ -8178,7 +8179,7 @@ IonBuilder::setElemTryTypedArray(bool *emitted, MDefinition *object,
JS_ASSERT(*emitted == false);
Scalar::Type arrayType;
if (!ElementAccessIsTypedArray(object, index, &arrayType))
if (!ElementAccessIsAnyTypedArray(object, index, &arrayType))
return true;
// Emit typed setelem variant.
@ -8729,7 +8730,7 @@ IonBuilder::objectsHaveCommonPrototype(types::TemporaryTypeSet *types, PropertyN
// Look for a getter/setter on the class itself which may need
// to be called. Ignore the getGeneric hook for typed arrays, it
// only handles integers and forwards names to the prototype.
if (isGetter && clasp->ops.getGeneric && !IsTypedArrayClass(clasp))
if (isGetter && clasp->ops.getGeneric && !IsAnyTypedArrayClass(clasp))
return false;
if (!isGetter && clasp->ops.setGeneric)
return false;

View File

@ -35,6 +35,7 @@ using namespace js::jit;
using mozilla::tl::FloorLog2;
typedef Rooted<TypedArrayObject *> RootedTypedArrayObject;
typedef Rooted<SharedTypedArrayObject *> RootedSharedTypedArrayObject;
void
CodeLocationJump::repoint(JitCode *code, MacroAssembler *masm)
@ -1094,12 +1095,13 @@ GenerateArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher
return true;
}
// In this case, the code for TypedArray and SharedTypedArray is not the same,
// because the code embeds pointers to the respective class arrays. Code that
// caches the stub code must distinguish between the two cases.
static void
GenerateTypedArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
JSObject *obj, Register object, TypedOrValueRegister output)
const TypedArrayLayout &layout, Register object, TypedOrValueRegister output)
{
JS_ASSERT(obj->is<TypedArrayObject>());
Label failures;
Register tmpReg;
@ -1113,14 +1115,14 @@ GenerateTypedArrayLength(JSContext *cx, MacroAssembler &masm, IonCache::StubAtta
// Implement the negated version of JSObject::isTypedArray predicate.
masm.loadObjClass(object, tmpReg);
masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(&TypedArrayObject::classes[0]),
masm.branchPtr(Assembler::Below, tmpReg, ImmPtr(layout.addressOfFirstClass()),
&failures);
masm.branchPtr(Assembler::AboveOrEqual, tmpReg,
ImmPtr(&TypedArrayObject::classes[Scalar::TypeMax]),
ImmPtr(layout.addressOfMaxClass()),
&failures);
// Load length.
masm.loadTypedOrValue(Address(object, TypedArrayObject::lengthOffset()), output);
masm.loadTypedOrValue(Address(object, TypedArrayLayout::lengthOffset()), output);
/* Success. */
attacher.jumpRejoin(masm);
@ -1287,13 +1289,13 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript
JS_ASSERT(canAttachStub());
JS_ASSERT(!*emitted);
if (!obj->is<TypedArrayObject>())
if (!IsAnyTypedArray(obj))
return true;
if (cx->names().length != name)
return true;
if (hasTypedArrayLengthStub())
if (hasAnyTypedArrayLengthStub(obj))
return true;
if (output().type() != MIRType_Value && output().type() != MIRType_Int32) {
@ -1309,10 +1311,9 @@ GetPropertyIC::tryAttachTypedArrayLength(JSContext *cx, HandleScript outerScript
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
GenerateTypedArrayLength(cx, masm, attacher, AnyTypedArrayLayout(obj), object(), output());
JS_ASSERT(!hasTypedArrayLengthStub_);
hasTypedArrayLengthStub_ = true;
setHasTypedArrayLengthStub(obj);
return linkAndAttachStub(cx, masm, attacher, ion, "typed array length");
}
@ -1788,6 +1789,7 @@ GetPropertyIC::reset()
{
RepatchIonCache::reset();
hasTypedArrayLengthStub_ = false;
hasSharedTypedArrayLengthStub_ = false;
hasStrictArgumentsLengthStub_ = false;
hasNormalArgumentsLengthStub_ = false;
hasGenericProxyStub_ = false;
@ -1837,6 +1839,7 @@ GetPropertyParIC::reset()
{
ParallelIonCache::reset();
hasTypedArrayLengthStub_ = false;
hasSharedTypedArrayLengthStub_ = false;
}
bool
@ -1867,10 +1870,9 @@ GetPropertyParIC::attachTypedArrayLength(LockedJSContext &cx, IonScript *ion, Ha
{
MacroAssembler masm(cx, ion);
DispatchStubPrepender attacher(*this);
GenerateTypedArrayLength(cx, masm, attacher, obj, object(), output());
GenerateTypedArrayLength(cx, masm, attacher, AnyTypedArrayLayout(obj), object(), output());
JS_ASSERT(!hasTypedArrayLengthStub_);
hasTypedArrayLengthStub_ = true;
setHasTypedArrayLengthStub(obj);
return linkAndAttachStub(cx, masm, attacher, ion, "parallel typed array length");
}
@ -1927,8 +1929,8 @@ GetPropertyParIC::update(ForkJoinContext *cx, size_t cacheIndex,
}
}
if (!attachedStub && !cache.hasTypedArrayLengthStub() &&
obj->is<TypedArrayObject>() && cx->names().length == cache.name() &&
if (!attachedStub && !cache.hasAnyTypedArrayLengthStub(obj) &&
IsAnyTypedArray(obj) && cx->names().length == cache.name() &&
(cache.output().type() == MIRType_Value || cache.output().type() == MIRType_Int32))
{
if (!cache.attachTypedArrayLength(ncx, ion, obj))
@ -3189,7 +3191,7 @@ GetElementIC::attachDenseElement(JSContext *cx, HandleScript outerScript, IonScr
GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
TypedOrValueRegister output)
{
if (!obj->is<TypedArrayObject>())
if (!IsAnyTypedArray(obj))
return false;
if (!idval.isInt32() && !idval.isString())
@ -3208,12 +3210,12 @@ GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
if (index == UINT32_MAX)
return false;
}
if (index >= obj->as<TypedArrayObject>().length())
if (index >= AnyTypedArrayLength(obj))
return false;
// The output register is not yet specialized as a float register, the only
// way to accept float typed arrays for now is to return a Value type.
uint32_t arrayType = obj->as<TypedArrayObject>().type();
uint32_t arrayType = AnyTypedArrayType(obj);
if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
return output.hasValue();
@ -3222,7 +3224,7 @@ GetElementIC::canAttachTypedArrayElement(JSObject *obj, const Value &idval,
static void
GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
HandleTypedArrayObject tarr, const Value &idval, Register object,
HandleObject tarr, const Value &idval, Register object,
ConstantOrRegister index, TypedOrValueRegister output,
bool allowDoubleResult)
{
@ -3231,10 +3233,10 @@ GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
Label failures;
// The array type is the object within the table of typed array classes.
Scalar::Type arrayType = tarr->type();
Scalar::Type arrayType = AnyTypedArrayType(tarr);
// Guard on the shape.
Shape *shape = tarr->lastProperty();
Shape *shape = AnyTypedArrayShape(tarr);
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
// Decide to what type index the stub should be optimized
@ -3291,7 +3293,7 @@ GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
}
// Guard on the initialized length.
Address length(object, TypedArrayObject::lengthOffset());
Address length(object, TypedArrayLayout::lengthOffset());
masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
// Save the object register on the stack in case of failure.
@ -3300,7 +3302,7 @@ GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
masm.push(object);
// Load elements vector.
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elementReg);
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
// Load the value. We use an invalid register because the destination
// register is necessary a non double register.
@ -3326,7 +3328,7 @@ GenerateGetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
bool
GetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleTypedArrayObject tarr, const Value &idval)
HandleObject tarr, const Value &idval)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
@ -3498,8 +3500,7 @@ GetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
attachedStub = true;
}
if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr, idval))
if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
return false;
attachedStub = true;
}
@ -3569,7 +3570,7 @@ static bool
IsTypedArrayElementSetInlineable(JSObject *obj, const Value &idval, const Value &value)
{
// Don't bother attaching stubs for assigning strings and objects.
return obj->is<TypedArrayObject>() && idval.isInt32() &&
return IsAnyTypedArray(obj) && idval.isInt32() &&
!value.isString() && !value.isObject();
}
@ -3743,7 +3744,7 @@ SetElementIC::attachDenseElement(JSContext *cx, HandleScript outerScript, IonScr
static bool
GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::StubAttacher &attacher,
HandleTypedArrayObject tarr, Register object,
HandleObject tarr, Register object,
ValueOperand indexVal, ConstantOrRegister value,
Register tempUnbox, Register temp, FloatRegister tempDouble,
FloatRegister tempFloat32)
@ -3751,7 +3752,7 @@ GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
Label failures, done, popObjectAndFail;
// Guard on the shape.
Shape *shape = tarr->lastProperty();
Shape *shape = AnyTypedArrayShape(tarr);
if (!shape)
return false;
masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
@ -3761,16 +3762,16 @@ GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
Register index = masm.extractInt32(indexVal, tempUnbox);
// Guard on the length.
Address length(object, TypedArrayObject::lengthOffset());
Address length(object, TypedArrayLayout::lengthOffset());
masm.unboxInt32(length, temp);
masm.branch32(Assembler::BelowOrEqual, temp, index, &done);
// Load the elements vector.
Register elements = temp;
masm.loadPtr(Address(object, TypedArrayObject::dataOffset()), elements);
masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elements);
// Set the value.
Scalar::Type arrayType = tarr->type();
Scalar::Type arrayType = AnyTypedArrayType(tarr);
int width = Scalar::byteSize(arrayType);
BaseIndex target(elements, index, ScaleFromElemWidth(width));
@ -3824,7 +3825,7 @@ GenerateSetTypedArrayElement(JSContext *cx, MacroAssembler &masm, IonCache::Stub
bool
SetElementIC::attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleTypedArrayObject tarr)
HandleObject tarr)
{
MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
RepatchStubAppender attacher(*this);
@ -3854,8 +3855,7 @@ SetElementIC::update(JSContext *cx, size_t cacheIndex, HandleObject obj,
attachedStub = true;
}
if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(cx, outerScript, ion, tarr))
if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj))
return false;
}
}
@ -3895,7 +3895,7 @@ SetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleO
bool
SetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
HandleTypedArrayObject tarr)
HandleObject tarr)
{
MacroAssembler masm(cx, ion);
DispatchStubPrepender attacher(*this);
@ -3937,8 +3937,7 @@ SetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
attachedStub = true;
}
if (!attachedStub && IsTypedArrayElementSetInlineable(obj, idval, value)) {
RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(ncx, ion, tarr))
if (!cache.attachTypedArrayElement(ncx, ion, obj))
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
}
}
@ -3980,7 +3979,7 @@ GetElementParIC::attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleO
bool
GetElementParIC::attachTypedArrayElement(LockedJSContext &cx, IonScript *ion,
HandleTypedArrayObject tarr, const Value &idval)
HandleObject tarr, const Value &idval)
{
MacroAssembler masm(cx, ion);
DispatchStubPrepender attacher(*this);
@ -4048,8 +4047,7 @@ GetElementParIC::update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj
if (!attachedStub &&
GetElementIC::canAttachTypedArrayElement(obj, idval, cache.output()))
{
RootedTypedArrayObject tarr(cx, &obj->as<TypedArrayObject>());
if (!cache.attachTypedArrayElement(ncx, ion, tarr, idval))
if (!cache.attachTypedArrayElement(ncx, ion, obj, idval))
return cx->setPendingAbortFatal(ParallelBailoutOutOfMemory);
attachedStub = true;
}

View File

@ -14,13 +14,11 @@
#endif
#include "jit/Registers.h"
#include "jit/shared/Assembler-shared.h"
#include "vm/TypedArrayCommon.h"
namespace js {
class LockedJSContext;
class TypedArrayObject;
typedef Handle<TypedArrayObject *> HandleTypedArrayObject;
namespace jit {
@ -560,6 +558,7 @@ class GetPropertyIC : public RepatchIonCache
bool monitoredResult_ : 1;
bool hasTypedArrayLengthStub_ : 1;
bool hasSharedTypedArrayLengthStub_ : 1;
bool hasStrictArgumentsLengthStub_ : 1;
bool hasNormalArgumentsLengthStub_ : 1;
bool hasGenericProxyStub_ : 1;
@ -577,6 +576,7 @@ class GetPropertyIC : public RepatchIonCache
numLocations_(0),
monitoredResult_(monitoredResult),
hasTypedArrayLengthStub_(false),
hasSharedTypedArrayLengthStub_(false),
hasStrictArgumentsLengthStub_(false),
hasNormalArgumentsLengthStub_(false),
hasGenericProxyStub_(false)
@ -599,8 +599,8 @@ class GetPropertyIC : public RepatchIonCache
bool monitoredResult() const {
return monitoredResult_;
}
bool hasTypedArrayLengthStub() const {
return hasTypedArrayLengthStub_;
bool hasAnyTypedArrayLengthStub(HandleObject obj) const {
return obj->is<TypedArrayObject>() ? hasTypedArrayLengthStub_ : hasSharedTypedArrayLengthStub_;
}
bool hasArgumentsLengthStub(bool strict) const {
return strict ? hasStrictArgumentsLengthStub_ : hasNormalArgumentsLengthStub_;
@ -609,6 +609,16 @@ class GetPropertyIC : public RepatchIonCache
return hasGenericProxyStub_;
}
void setHasTypedArrayLengthStub(HandleObject obj) {
if (obj->is<TypedArrayObject>()) {
JS_ASSERT(!hasTypedArrayLengthStub_);
hasTypedArrayLengthStub_ = true;
} else {
JS_ASSERT(!hasSharedTypedArrayLengthStub_);
hasSharedTypedArrayLengthStub_ = true;
}
}
void setLocationInfo(size_t locationsIndex, size_t numLocations) {
JS_ASSERT(idempotent());
JS_ASSERT(!numLocations_);
@ -836,7 +846,7 @@ class GetElementIC : public RepatchIonCache
HandleObject obj, const Value &idval);
bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleTypedArrayObject tarr, const Value &idval);
HandleObject tarr, const Value &idval);
bool attachArgumentsElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleObject obj);
@ -934,7 +944,7 @@ class SetElementIC : public RepatchIonCache
HandleObject obj, const Value &idval);
bool attachTypedArrayElement(JSContext *cx, HandleScript outerScript, IonScript *ion,
HandleTypedArrayObject tarr);
HandleObject tarr);
static bool
update(JSContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
@ -1095,13 +1105,15 @@ class GetPropertyParIC : public ParallelIonCache
PropertyName *name_;
TypedOrValueRegister output_;
bool hasTypedArrayLengthStub_ : 1;
bool hasSharedTypedArrayLengthStub_ : 1;
public:
GetPropertyParIC(Register object, PropertyName *name, TypedOrValueRegister output)
: object_(object),
name_(name),
output_(output),
hasTypedArrayLengthStub_(false)
hasTypedArrayLengthStub_(false),
hasSharedTypedArrayLengthStub_(false)
{
}
@ -1124,8 +1136,18 @@ class GetPropertyParIC : public ParallelIonCache
TypedOrValueRegister output() const {
return output_;
}
bool hasTypedArrayLengthStub() const {
return hasTypedArrayLengthStub_;
bool hasAnyTypedArrayLengthStub(HandleObject obj) const {
return obj->is<TypedArrayObject>() ? hasTypedArrayLengthStub_ : hasSharedTypedArrayLengthStub_;
}
void setHasTypedArrayLengthStub(HandleObject obj) {
if (obj->is<TypedArrayObject>()) {
JS_ASSERT(!hasTypedArrayLengthStub_);
hasTypedArrayLengthStub_ = true;
} else {
JS_ASSERT(!hasSharedTypedArrayLengthStub_);
hasSharedTypedArrayLengthStub_ = true;
}
}
// CanAttachNativeGetProp Helpers
@ -1198,7 +1220,7 @@ class GetElementParIC : public ParallelIonCache
HandlePropertyName name, HandleObject holder, HandleShape shape);
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
const Value &idval);
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleTypedArrayObject tarr,
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleObject tarr,
const Value &idval);
static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj, HandleValue idval,
@ -1326,7 +1348,7 @@ class SetElementParIC : public ParallelIonCache
bool attachDenseElement(LockedJSContext &cx, IonScript *ion, HandleObject obj,
const Value &idval);
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleTypedArrayObject tarr);
bool attachTypedArrayElement(LockedJSContext &cx, IonScript *ion, HandleObject tarr);
static bool update(ForkJoinContext *cx, size_t cacheIndex, HandleObject obj,
HandleValue idval, HandleValue value);

View File

@ -230,12 +230,21 @@ IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
// typed array prototype, and make sure we are accessing the right one
// for the type of the instance object.
if (thisTypes) {
Scalar::Type type = thisTypes->getTypedArrayType();
Scalar::Type type;
type = thisTypes->getTypedArrayType();
if (type != Scalar::TypeMax && TypedArrayObject::isOriginalLengthGetter(type, native)) {
MInstruction *length = addTypedArrayLength(callInfo.thisArg());
current->push(length);
return InliningStatus_Inlined;
}
type = thisTypes->getSharedTypedArrayType();
if (type != Scalar::TypeMax && SharedTypedArrayObject::isOriginalLengthGetter(type, native)) {
MInstruction *length = addTypedArrayLength(callInfo.thisArg());
current->push(length);
return InliningStatus_Inlined;
}
}
return InliningStatus_NotInlined;
@ -1494,7 +1503,7 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
// barriers and on typed arrays and on typed object arrays.
Scalar::Type arrayType;
if ((!isDenseNative || writeNeedsBarrier) &&
!ElementAccessIsTypedArray(obj, id, &arrayType) &&
!ElementAccessIsAnyTypedArray(obj, id, &arrayType) &&
!elementAccessIsTypedObjectArrayOfScalarType(obj, id, &arrayType))
{
return InliningStatus_NotInlined;
@ -1523,7 +1532,7 @@ IonBuilder::inlineUnsafePutElements(CallInfo &callInfo)
}
Scalar::Type arrayType;
if (ElementAccessIsTypedArray(obj, id, &arrayType)) {
if (ElementAccessIsAnyTypedArray(obj, id, &arrayType)) {
if (!inlineUnsafeSetTypedArrayElement(callInfo, base, arrayType))
return InliningStatus_Error;
continue;

View File

@ -3354,19 +3354,19 @@ InlinePropertyTable::buildTypeSetForFunction(JSFunction *func) const
void *
MLoadTypedArrayElementStatic::base() const
{
return typedArray_->viewData();
return AnyTypedArrayViewData(someTypedArray_);
}
size_t
MLoadTypedArrayElementStatic::length() const
{
return typedArray_->byteLength();
return AnyTypedArrayByteLength(someTypedArray_);
}
void *
MStoreTypedArrayElementStatic::base() const
{
return typedArray_->viewData();
return AnyTypedArrayViewData(someTypedArray_);
}
bool
@ -3381,7 +3381,7 @@ MGetElementCache::allowDoubleResult() const
size_t
MStoreTypedArrayElementStatic::length() const
{
return typedArray_->byteLength();
return AnyTypedArrayByteLength(someTypedArray_);
}
bool
@ -3567,12 +3567,12 @@ jit::ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id)
// Typed arrays are native classes but do not have dense elements.
const Class *clasp = types->getKnownClass();
return clasp && clasp->isNative() && !IsTypedArrayClass(clasp);
return clasp && clasp->isNative() && !IsAnyTypedArrayClass(clasp);
}
bool
jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType)
jit::ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType)
{
if (obj->mightBeType(MIRType_String))
return false;
@ -3585,6 +3585,9 @@ jit::ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
return false;
*arrayType = types->getTypedArrayType();
if (*arrayType != Scalar::TypeMax)
return true;
*arrayType = types->getSharedTypedArrayType();
return *arrayType != Scalar::TypeMax;
}
@ -4033,7 +4036,7 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai
// TI doesn't track TypedArray objects and should never insert a type
// barrier for them.
if (!name && IsTypedArrayClass(object->clasp()))
if (!name && IsAnyTypedArrayClass(object->clasp()))
continue;
jsid id = name ? NameToId(name) : JSID_VOID;
@ -4065,7 +4068,7 @@ jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstrai
types::TypeObjectKey *object = types->getObject(i);
if (!object || object->unknownProperties())
continue;
if (!name && IsTypedArrayClass(object->clasp()))
if (!name && IsAnyTypedArrayClass(object->clasp()))
continue;
jsid id = name ? NameToId(name) : JSID_VOID;

View File

@ -23,7 +23,7 @@
#include "jit/TypedObjectPrediction.h"
#include "jit/TypePolicy.h"
#include "vm/ScopeObject.h"
#include "vm/TypedArrayObject.h"
#include "vm/TypedArrayCommon.h"
namespace js {
@ -7950,10 +7950,10 @@ class MLoadTypedArrayElementStatic
: public MUnaryInstruction,
public ConvertToInt32Policy<0>
{
MLoadTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr)
: MUnaryInstruction(ptr), typedArray_(typedArray), fallible_(true)
MLoadTypedArrayElementStatic(JSObject *someTypedArray, MDefinition *ptr)
: MUnaryInstruction(ptr), someTypedArray_(someTypedArray), fallible_(true)
{
int type = typedArray_->type();
int type = viewType();
if (type == Scalar::Float32)
setResultType(MIRType_Float32);
else if (type == Scalar::Float64)
@ -7962,20 +7962,20 @@ class MLoadTypedArrayElementStatic
setResultType(MIRType_Int32);
}
AlwaysTenured<TypedArrayObject*> typedArray_;
AlwaysTenured<JSObject*> someTypedArray_;
bool fallible_;
public:
INSTRUCTION_HEADER(LoadTypedArrayElementStatic);
static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray,
static MLoadTypedArrayElementStatic *New(TempAllocator &alloc, JSObject *someTypedArray,
MDefinition *ptr)
{
return new(alloc) MLoadTypedArrayElementStatic(typedArray, ptr);
return new(alloc) MLoadTypedArrayElementStatic(someTypedArray, ptr);
}
Scalar::Type viewType() const {
return typedArray_->type();
return AnyTypedArrayType(someTypedArray_);
}
void *base() const;
size_t length() const;
@ -7999,7 +7999,7 @@ class MLoadTypedArrayElementStatic
void computeRange(TempAllocator &alloc);
bool truncate(TruncateKind kind);
bool canProduceFloat32() const { return typedArray_->type() == Scalar::Float32; }
bool canProduceFloat32() const { return viewType() == Scalar::Float32; }
};
class MStoreTypedArrayElement
@ -8147,19 +8147,19 @@ class MStoreTypedArrayElementStatic :
public MBinaryInstruction
, public StoreTypedArrayElementStaticPolicy
{
MStoreTypedArrayElementStatic(TypedArrayObject *typedArray, MDefinition *ptr, MDefinition *v)
: MBinaryInstruction(ptr, v), typedArray_(typedArray)
MStoreTypedArrayElementStatic(JSObject *someTypedArray, MDefinition *ptr, MDefinition *v)
: MBinaryInstruction(ptr, v), someTypedArray_(someTypedArray)
{}
AlwaysTenured<TypedArrayObject*> typedArray_;
AlwaysTenured<JSObject*> someTypedArray_;
public:
INSTRUCTION_HEADER(StoreTypedArrayElementStatic);
static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, TypedArrayObject *typedArray,
static MStoreTypedArrayElementStatic *New(TempAllocator &alloc, JSObject *someTypedArray,
MDefinition *ptr, MDefinition *v)
{
return new(alloc) MStoreTypedArrayElementStatic(typedArray, ptr, v);
return new(alloc) MStoreTypedArrayElementStatic(someTypedArray, ptr, v);
}
TypePolicy *typePolicy() {
@ -8167,7 +8167,7 @@ class MStoreTypedArrayElementStatic :
}
Scalar::Type viewType() const {
return typedArray_->type();
return AnyTypedArrayType(someTypedArray_);
}
bool isFloatArray() const {
return viewType() == Scalar::Float32 ||
@ -8185,7 +8185,7 @@ class MStoreTypedArrayElementStatic :
TruncateKind operandTruncateKind(size_t index) const;
bool canConsumeFloat32(MUse *use) const {
return use == getUseFor(1) && typedArray_->type() == Scalar::Float32;
return use == getUseFor(1) && viewType() == Scalar::Float32;
}
};
@ -11823,8 +11823,8 @@ MControlInstruction *MDefinition::toControlInstruction() {
// Helper functions used to decide how to build MIR.
bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType);
bool ElementAccessIsAnyTypedArray(MDefinition *obj, MDefinition *id,
Scalar::Type *arrayType);
bool ElementAccessIsPacked(types::CompilerConstraintList *constraints, MDefinition *obj);
bool ElementAccessMightBeCopyOnWrite(types::CompilerConstraintList *constraints, MDefinition *obj);
bool ElementAccessHasExtraIndexedProperty(types::CompilerConstraintList *constraints,

View File

@ -15,6 +15,7 @@
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "vm/NumericConversions.h"
#include "vm/TypedArrayCommon.h"
#include "jsopcodeinlines.h"
@ -1489,9 +1490,9 @@ MLoadTypedArrayElementStatic::computeRange(TempAllocator &alloc)
{
// We don't currently use MLoadTypedArrayElementStatic for uint32, so we
// don't have to worry about it returning a value outside our type.
JS_ASSERT(typedArray_->type() != Scalar::Uint32);
JS_ASSERT(AnyTypedArrayType(someTypedArray_) != Scalar::Uint32);
setRange(GetTypedArrayRange(alloc, typedArray_->type()));
setRange(GetTypedArrayRange(alloc, AnyTypedArrayType(someTypedArray_)));
}
void

View File

@ -351,6 +351,7 @@ MSG_DEF(JSMSG_SC_BAD_SERIALIZED_DATA, 1, JSEXN_INTERNALERR, "bad serialized str
MSG_DEF(JSMSG_SC_DUP_TRANSFERABLE, 0, JSEXN_TYPEERR, "duplicate transferable for structured clone")
MSG_DEF(JSMSG_SC_NOT_TRANSFERABLE, 0, JSEXN_TYPEERR, "invalid transferable array for structured clone")
MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE, 0, JSEXN_TYPEERR, "unsupported type for structured data")
MSG_DEF(JSMSG_SC_SHMEM_MUST_TRANSFER, 0, JSEXN_TYPEERR, "SharedArrayBuffer must be explicitly transfered during structured cloning")
// Debugger
MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
@ -421,9 +422,20 @@ MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG, 0, JSEXN_ERR, "Type is too large to alloc
// Typed array
MSG_DEF(JSMSG_BAD_INDEX, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_ERR, "invalid arguments")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_OBJECT, 0, JSEXN_TYPEERR, "invalid object argument")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 0, JSEXN_ERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_ERR, "argument {0} must be >= 0")
// Shared array buffer
MSG_DEF(JSMSG_SHARED_ARRAY_BAD_OBJECT, 0, JSEXN_TYPEERR, "invalid object argument")
MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH, 0, JSEXN_RANGEERR, "length argument out of range")
// Shared typed array
MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT, 0, JSEXN_TYPEERR, "invalid object argument")
MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_ARGS, 0, JSEXN_RANGEERR, "bad combination of offset, length, and element size")
MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_ARG_RANGE, 1, JSEXN_RANGEERR, "argument {0} out of range")
MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_LENGTH, 0, JSEXN_TYPEERR, "length argument must not be an object")
// Parallel array
MSG_DEF(JSMSG_PAR_ARRAY_BAD_ARG, 0, JSEXN_RANGEERR, "invalid parallel method argument")
MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 1, JSEXN_ERR, "target for index {0} is not an integer")

View File

@ -77,11 +77,10 @@
#include "vm/Runtime.h"
#include "vm/SavedStacks.h"
#include "vm/Shape.h"
#include "vm/SharedArrayObject.h"
#include "vm/StopIterationObject.h"
#include "vm/StringBuffer.h"
#include "vm/Symbol.h"
#include "vm/TypedArrayObject.h"
#include "vm/TypedArrayCommon.h"
#include "vm/WeakMapObject.h"
#include "vm/WrapperObject.h"
#include "vm/Xdr.h"
@ -2740,7 +2739,7 @@ JS_AlreadyHasOwnPropertyById(JSContext *cx, HandleObject obj, HandleId id, bool
return true;
}
if (obj->is<TypedArrayObject>() && index < obj->as<TypedArrayObject>().length()) {
if (IsAnyTypedArray(obj) && index < AnyTypedArrayLength(obj)) {
*foundp = true;
return true;
}

View File

@ -32,6 +32,7 @@
#include "vm/NumericConversions.h"
#include "vm/Shape.h"
#include "vm/StringBuffer.h"
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
@ -848,7 +849,7 @@ js::ObjectMayHaveExtraIndexedProperties(JSObject *obj)
return true;
if (obj->getDenseInitializedLength() > 0)
return true;
if (obj->is<TypedArrayObject>())
if (IsAnyTypedArray(obj))
return true;
}
@ -2073,7 +2074,7 @@ js::array_push(JSContext *cx, unsigned argc, Value *vp)
/* Fast path for native objects with dense elements. */
do {
if (!obj->isNative() || obj->is<TypedArrayObject>())
if (!obj->isNative() || IsAnyTypedArray(obj.get()))
break;
if (obj->is<ArrayObject>() && !obj->as<ArrayObject>().lengthIsWritable())

View File

@ -1477,7 +1477,6 @@ extern JS_FRIEND_API(JSObject *)
JS_NewUint8Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewUint8ClampedArray(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewInt16Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
@ -1491,6 +1490,25 @@ JS_NewFloat32Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewFloat64Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt8Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint8Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint8ClampedArray(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt16Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint16Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt32Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint32Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedFloat32Array(JSContext *cx, uint32_t nelements);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedFloat64Array(JSContext *cx, uint32_t nelements);
/*
* Create a new typed array and copy in values from the given object. The
* object is used as if it were an array; that is, the new array (if
@ -1552,6 +1570,37 @@ extern JS_FRIEND_API(JSObject *)
JS_NewFloat64ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, int32_t length);
// As for the above, passing length==(uint32_t)-1 signifies "up to the
// end of the buffer".
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint8ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint8ClampedArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint16ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedInt32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedUint32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedFloat32ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
extern JS_FRIEND_API(JSObject *)
JS_NewSharedFloat64ArrayWithBuffer(JSContext *cx, JS::HandleObject arrayBuffer,
uint32_t byteOffset, uint32_t length);
/*
* Create a new ArrayBuffer with the given byte length.
*/
@ -1567,6 +1616,12 @@ JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes);
extern JS_FRIEND_API(bool)
JS_IsTypedArrayObject(JSObject *obj);
/*
* Ditto for JS_GetSharedTypedArray* APIs.
*/
extern JS_FRIEND_API(bool)
JS_IsSharedTypedArrayObject(JSObject *obj);
/*
* Check whether obj supports JS_GetArrayBufferView* APIs. Note that this may
* return false if a security wrapper is encountered that denies the
@ -1600,6 +1655,25 @@ JS_IsFloat32Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsFloat64Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedInt8Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedUint8Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedUint8ClampedArray(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedInt16Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedUint16Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedInt32Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedUint32Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedFloat32Array(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedFloat64Array(JSObject *obj);
/*
* Test for specific typed array types (ArrayBufferView subtypes) and return
* the unwrapped object if so, else nullptr. Never throws.
@ -1632,6 +1706,25 @@ UnwrapArrayBuffer(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapArrayBufferView(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedInt8Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedUint8Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedUint8ClampedArray(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedInt16Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedUint16Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedInt32Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedUint32Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedFloat32Array(JSObject *obj);
extern JS_FRIEND_API(JSObject *)
UnwrapSharedFloat64Array(JSObject *obj);
namespace detail {
extern JS_FRIEND_DATA(const Class* const) Int8ArrayClassPtr;
@ -1644,6 +1737,16 @@ extern JS_FRIEND_DATA(const Class* const) Uint32ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) Float32ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) Float64ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedInt8ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedUint8ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedUint8ClampedArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedInt16ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedUint16ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedInt32ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedUint32ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedFloat32ArrayClassPtr;
extern JS_FRIEND_DATA(const Class* const) SharedFloat64ArrayClassPtr;
const size_t TypedArrayLengthSlot = 1;
} // namespace detail
@ -1734,6 +1837,9 @@ JS_GetArrayBufferViewType(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsArrayBufferObject(JSObject *obj);
extern JS_FRIEND_API(bool)
JS_IsSharedArrayBufferObject(JSObject *obj);
/*
* Return the available byte length of an array buffer.
*

View File

@ -2035,6 +2035,16 @@ TemporaryTypeSet::getTypedArrayType()
return Scalar::TypeMax;
}
Scalar::Type
TemporaryTypeSet::getSharedTypedArrayType()
{
const Class *clasp = getKnownClass();
if (clasp && IsSharedTypedArrayClass(clasp))
return (Scalar::Type) (clasp - &SharedTypedArrayObject::classes[0]);
return Scalar::TypeMax;
}
bool
TemporaryTypeSet::isDOMClass()
{
@ -2275,8 +2285,12 @@ types::UseNewTypeForInitializer(JSScript *script, jsbytecode *pc, JSProtoKey key
if (script->functionNonDelazifying() && !script->treatAsRunOnce())
return GenericObject;
if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
if (key != JSProto_Object &&
!(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray) &&
!(key >= JSProto_SharedInt8Array && key <= JSProto_SharedUint8ClampedArray))
{
return GenericObject;
}
/*
* All loops in the script will have a JSTRY_ITER or JSTRY_LOOP try note
@ -2317,7 +2331,7 @@ ClassCanHaveExtraProperties(const Class *clasp)
return clasp->resolve != JS_ResolveStub
|| clasp->ops.lookupGeneric
|| clasp->ops.getGeneric
|| IsTypedArrayClass(clasp);
|| IsAnyTypedArrayClass(clasp);
}
static inline bool
@ -2357,7 +2371,7 @@ types::TypeCanHaveExtraIndexedProperties(CompilerConstraintList *constraints,
// Note: typed arrays have indexed properties not accounted for by type
// information, though these are all in bounds and will be accounted for
// by JIT paths.
if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsTypedArrayClass(clasp)))
if (!clasp || (ClassCanHaveExtraProperties(clasp) && !IsAnyTypedArrayClass(clasp)))
return true;
if (types->hasObjectFlags(constraints, types::OBJECT_FLAG_SPARSE_INDEXES))

View File

@ -759,9 +759,12 @@ class TemporaryTypeSet : public TypeSet
/* Get the prototype shared by all objects in this set, or nullptr. */
JSObject *getCommonPrototype();
/* Get the typed array type of all objects in this set, or TypedArrayObject::TYPE_MAX. */
/* Get the typed array type of all objects in this set, or Scalar::TypeMax. */
Scalar::Type getTypedArrayType();
/* Get the shared typed array type of all objects in this set, or Scalar::TypeMax. */
Scalar::Type getSharedTypedArrayType();
/* Whether all objects have JSCLASS_IS_DOMJSCLASS set. */
bool isDOMClass();

View File

@ -18,6 +18,7 @@
#include "vm/BooleanObject.h"
#include "vm/NumberObject.h"
#include "vm/SharedArrayObject.h"
#include "vm/SharedTypedArrayObject.h"
#include "vm/StringObject.h"
#include "vm/TypedArrayObject.h"
@ -316,6 +317,17 @@ GetClassForProtoKey(JSProtoKey key)
case JSProto_Uint8ClampedArray:
return &TypedArrayObject::classes[key - JSProto_Int8Array];
case JSProto_SharedInt8Array:
case JSProto_SharedUint8Array:
case JSProto_SharedInt16Array:
case JSProto_SharedUint16Array:
case JSProto_SharedInt32Array:
case JSProto_SharedUint32Array:
case JSProto_SharedFloat32Array:
case JSProto_SharedFloat64Array:
case JSProto_SharedUint8ClampedArray:
return &SharedTypedArrayObject::classes[key - JSProto_SharedInt8Array];
case JSProto_ArrayBuffer:
return &ArrayBufferObject::class_;

View File

@ -30,6 +30,7 @@
#include "vm/Interpreter.h"
#include "vm/Shape.h"
#include "vm/StopIterationObject.h"
#include "vm/TypedArrayCommon.h"
#include "jsinferinlines.h"
#include "jsobjinlines.h"
@ -148,9 +149,9 @@ EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSe
}
}
/* Collect any typed array elements from this object. */
if (pobj->is<TypedArrayObject>()) {
size_t len = pobj->as<TypedArrayObject>().length();
/* Collect any typed array or shared typed array elements from this object. */
if (IsAnyTypedArray(pobj)) {
size_t len = AnyTypedArrayLength(pobj);
for (size_t i = 0; i < len; i++) {
if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
return false;
@ -711,7 +712,7 @@ js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleVa
do {
if (!pobj->isNative() ||
!pobj->hasEmptyElements() ||
pobj->is<TypedArrayObject>() ||
IsAnyTypedArray(pobj) ||
pobj->hasUncacheableProto() ||
pobj->getOps()->enumerate ||
pobj->getClass()->enumerate != JS_EnumerateStub ||

View File

@ -1699,6 +1699,42 @@ js::ToUint16Slow(JSContext *cx, const HandleValue v, uint16_t *out)
return true;
}
template<typename T>
bool
js::ToLengthClamped(T *cx, HandleValue v, uint32_t *out, bool *overflow)
{
if (v.isInt32()) {
int32_t i = v.toInt32();
*out = i < 0 ? 0 : i;
return true;
}
double d;
if (v.isDouble()) {
d = v.toDouble();
} else {
if (!ToNumber(cx, v, &d)) {
*overflow = false;
return false;
}
}
d = ToInteger(d);
if (d <= 0.0) {
*out = 0;
return true;
}
if (d >= (double)0xFFFFFFFEU) {
*overflow = true;
return false;
}
*out = (uint32_t)d;
return true;
}
template bool
js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
template bool
js::ToLengthClamped<ExclusiveContext>(ExclusiveContext*, HandleValue, uint32_t*, bool*);
template <typename CharT>
bool
js_strtod(ThreadSafeContext *cx, const CharT *begin, const CharT *end, const CharT **dEnd,

View File

@ -250,6 +250,15 @@ ToInteger(JSContext *cx, HandleValue v, double *dp)
return true;
}
/* ES6 7.1.15 ToLength, but clamped to the [0,2^32-2] range. If the
* return value is false then *overflow will be true iff the value was
* not clampable to uint32_t range.
*
* For JSContext and ExclusiveContext.
*/
template<typename T>
bool ToLengthClamped(T *cx, HandleValue v, uint32_t *out, bool *overflow);
inline bool
SafeAdd(int32_t one, int32_t two, int32_t *res)
{

View File

@ -51,6 +51,7 @@
#include "vm/ProxyObject.h"
#include "vm/RegExpStaticsObject.h"
#include "vm/Shape.h"
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
#include "jsboolinlines.h"
@ -1278,7 +1279,7 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
/* preventExtensions must sparsify dense objects, so we can assign to holes without checks. */
JS_ASSERT_IF(obj->isNative(), obj->getDenseCapacity() == 0);
if (obj->isNative() && !obj->inDictionaryMode() && !obj->is<TypedArrayObject>()) {
if (obj->isNative() && !obj->inDictionaryMode() && !IsAnyTypedArray(obj)) {
/*
* Seal/freeze non-dictionary objects by constructing a new shape
* hierarchy mirroring the original one, which can be shared if many
@ -1368,14 +1369,14 @@ JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it,
return true;
}
if (obj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
if (it == SEAL) {
// Typed arrays are always sealed.
*resultp = true;
} else {
// Typed arrays cannot be frozen, but an empty typed array is
// trivially frozen.
*resultp = (obj->as<TypedArrayObject>().length() == 0);
*resultp = (AnyTypedArrayLength(obj) == 0);
}
return true;
}
@ -4206,7 +4207,7 @@ DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType
setter == JS_StrictPropertyStub &&
attrs == JSPROP_ENUMERATE &&
(!obj->isIndexed() || !obj->nativeContainsPure(id)) &&
!obj->is<TypedArrayObject>())
!IsAnyTypedArray(obj))
{
uint32_t index = JSID_TO_INT(id);
bool definesPast;
@ -4257,7 +4258,7 @@ DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType
}
// Don't define new indexed properties on typed arrays.
if (obj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index))
return true;
@ -4339,7 +4340,7 @@ js::DefineNativeProperty(ExclusiveContext *cx, HandleObject obj, HandleId id, Ha
* vice versa, finish the job via obj->changeProperty.
*/
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (obj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
/* Ignore getter/setter properties added to typed arrays. */
return true;
}
@ -4553,10 +4554,10 @@ LookupOwnPropertyInline(ExclusiveContext *cx,
// Check for a typed array element. Integer lookups always finish here
// so that integer properties on the prototype are ignored even for out
// of bounds accesses.
if (obj->template is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
if (index < obj->template as<TypedArrayObject>().length()) {
if (index < AnyTypedArrayLength(obj)) {
objp.set(obj);
MarkDenseOrTypedArrayElementFound<allowGC>(propp);
} else {
@ -5162,10 +5163,10 @@ LookupPropertyPureInline(JSObject *obj, jsid id, JSObject **objp, Shape **propp)
return true;
}
if (current->is<TypedArrayObject>()) {
if (IsAnyTypedArray(current)) {
uint64_t index;
if (IsTypedArrayIndex(id, &index)) {
if (index < obj->as<TypedArrayObject>().length()) {
if (index < AnyTypedArrayLength(obj)) {
*objp = current;
MarkDenseOrTypedArrayElementFound<NoGC>(propp);
} else {
@ -5267,8 +5268,8 @@ js::GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp)
return true;
}
if (obj->is<TypedArrayObject>()) {
vp->setNumber(obj->as<TypedArrayObject>().length());
if (IsAnyTypedArray(obj)) {
vp->setNumber(AnyTypedArrayLength(obj));
return true;
}
}
@ -5560,7 +5561,7 @@ baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg
if (IsImplicitDenseOrTypedArrayElement(shape)) {
uint32_t index = JSID_TO_INT(id);
if (obj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
double d;
if (mode == ParallelExecution) {
// Bail if converting the value might invoke user-defined
@ -5577,9 +5578,13 @@ baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg
// Silently do nothing for out-of-bounds sets, for consistency with
// current behavior. (ES6 currently says to throw for this in
// strict mode code, so we may eventually need to change.)
TypedArrayObject &tarray = obj->as<TypedArrayObject>();
if (index < tarray.length())
TypedArrayObject::setElement(tarray, index, d);
uint32_t len = AnyTypedArrayLength(obj);
if (index < len) {
if (obj->is<TypedArrayObject>())
TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
else
SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
}
return true;
}
@ -5702,7 +5707,7 @@ baseops::SetAttributes(JSContext *cx, HandleObject obj, HandleId id, unsigned *a
if (!shape)
return true;
if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
if (nobj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(nobj.get())) {
if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
return true;
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
@ -5741,7 +5746,7 @@ baseops::DeleteGeneric(JSContext *cx, HandleObject obj, HandleId id, bool *succe
cx->runtime()->gc.poke();
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (obj->is<TypedArrayObject>()) {
if (IsAnyTypedArray(obj)) {
// Don't delete elements from typed arrays.
*succeeded = false;
return true;
@ -5802,7 +5807,7 @@ js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::Hand
bool
baseops::Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable)
{
if (!obj->isNative() || obj->is<TypedArrayObject>()) {
if (!obj->isNative() || IsAnyTypedArray(obj)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
obj->getClass()->name);
return false;
@ -6514,8 +6519,10 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassIn
info->objectsMallocHeapMisc += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf);
} else if (is<PropertyIteratorObject>()) {
info->objectsMallocHeapMisc += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf);
} else if (is<ArrayBufferObject>() || is<SharedArrayBufferObject>()) {
} else if (is<ArrayBufferObject>()) {
ArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
} else if (is<SharedArrayBufferObject>()) {
SharedArrayBufferObject::addSizeOfExcludingThis(this, mallocSizeOf, info);
} else if (is<AsmJSModuleObject>()) {
as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &info->objectsNonHeapCodeAsmJS,
&info->objectsMallocHeapMisc);

View File

@ -17,6 +17,7 @@
#include "vm/Probes.h"
#include "vm/ScopeObject.h"
#include "vm/StringObject.h"
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
#include "jscompartmentinlines.h"
@ -342,6 +343,8 @@ JSObject::getDenseOrTypedArrayElement(uint32_t idx)
{
if (is<js::TypedArrayObject>())
return as<js::TypedArrayObject>().getElement(idx);
if (is<js::SharedTypedArrayObject>())
return as<js::SharedTypedArrayObject>().getElement(idx);
return getDenseElement(idx);
}
@ -1123,8 +1126,8 @@ ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
case ESClass_String: return obj->is<StringObject>();
case ESClass_Boolean: return obj->is<BooleanObject>();
case ESClass_RegExp: return obj->is<RegExpObject>();
case ESClass_ArrayBuffer:
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
case ESClass_SharedArrayBuffer: return obj->is<SharedArrayBufferObject>();
case ESClass_Date: return obj->is<DateObject>();
case ESClass_Set: return obj->is<SetObject>();
case ESClass_Map: return obj->is<MapObject>();

View File

@ -35,6 +35,7 @@
#define CLASP(name) (&name##Class)
#define OCLASP(name) (&name##Object::class_)
#define TYPED_ARRAY_CLASP(type) (&TypedArrayObject::classes[Scalar::type])
#define SHARED_TYPED_ARRAY_CLASP(type) (&SharedTypedArrayObject::classes[Scalar::type])
#ifdef ENABLE_PARALLEL_JS
#define IF_PJS(real,imaginary) real
@ -110,6 +111,15 @@ IF_BDATA(real,imaginary)(TypedObject, 39, js_InitTypedObjectModule
imaginary(GeneratorFunction, 40, js_InitIteratorClasses, dummy) \
IF_BDATA(real,imaginary)(SIMD, 41, js_InitSIMDClass, OCLASP(SIMD)) \
real(WeakSet, 42, js_InitWeakSetClass, OCLASP(WeakSet)) \
IF_SAB(real,imaginary)(SharedInt8Array, 43, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Int8)) \
IF_SAB(real,imaginary)(SharedUint8Array, 44, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint8)) \
IF_SAB(real,imaginary)(SharedInt16Array, 45, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Int16)) \
IF_SAB(real,imaginary)(SharedUint16Array, 46, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint16)) \
IF_SAB(real,imaginary)(SharedInt32Array, 47, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Int32)) \
IF_SAB(real,imaginary)(SharedUint32Array, 48, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint32)) \
IF_SAB(real,imaginary)(SharedFloat32Array, 49, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Float32)) \
IF_SAB(real,imaginary)(SharedFloat64Array, 50, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Float64)) \
IF_SAB(real,imaginary)(SharedUint8ClampedArray, 51, js_InitViaClassSpec, SHARED_TYPED_ARRAY_CLASP(Uint8Clamped)) \
#define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)

View File

@ -268,6 +268,7 @@ UNIFIED_SOURCES += [
'vm/SelfHosting.cpp',
'vm/Shape.cpp',
'vm/SharedArrayObject.cpp',
'vm/SharedTypedArrayObject.cpp',
'vm/SPSProfiler.cpp',
'vm/Stack.cpp',
'vm/String.cpp',

View File

@ -0,0 +1,240 @@
// |reftest| skip-if(!xulRuntime.shell)
/* -*- Mode: js2; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/
*/
// Minimal test cases that obey the (current, but undesirable)
// requirement that the buffer size should be acceptable to asm.js -
// at least 4K AND a power of 2 OR a multiple of 16MB. Also, on
// 64-bit a SharedArrayBuffer is very expensive under these rules - a
// 4GB area is reserved for it. So don't go allocating a ton of them.
//
// These tests cannot test that sharing works across workers. There
// are or will be tests, in dom/workers, that do that.
//
// Structured cloning is not tested here (there are no APIs).
var b;
function testSharedArrayBuffer() {
b = new SharedArrayBuffer("4096"); // Test string conversion, too
assertEq(b instanceof SharedArrayBuffer, true);
assertEq(b.byteLength, 4096);
assertEq(!!SharedArrayBuffer.isView, true);
b.fnord = "Hi there";
assertEq(b.fnord, "Hi there");
SharedArrayBuffer.prototype.abracadabra = "no wishing for wishes!";
assertEq(b.abracadabra, "no wishing for wishes!");
// can "convert" a buffer (really works as an assertion)
assertEq(SharedArrayBuffer(b), b);
// can't convert any other object
assertThrowsInstanceOf(() => SharedArrayBuffer({}), TypeError);
// asm.js limitation - may go away
assertThrowsInstanceOf(() => new SharedArrayBuffer(12), Error);
// byteLength can be invoked as per normal, indirectly
assertEq(Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype,"byteLength").get.call(b), 4096);
// however byteLength is not generic
assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype,"byteLength").get.call({}), TypeError);
}
function testSharedTypedArray() {
assertEq(SharedInt8Array.prototype.BYTES_PER_ELEMENT, 1);
assertEq(SharedUint8Array.prototype.BYTES_PER_ELEMENT, 1);
assertEq(SharedUint8ClampedArray.prototype.BYTES_PER_ELEMENT, 1);
assertEq(SharedInt16Array.prototype.BYTES_PER_ELEMENT, 2);
assertEq(SharedUint16Array.prototype.BYTES_PER_ELEMENT, 2);
assertEq(SharedInt32Array.prototype.BYTES_PER_ELEMENT, 4);
assertEq(SharedUint32Array.prototype.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat32Array.prototype.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat32Array.prototype.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat64Array.prototype.BYTES_PER_ELEMENT, 8);
assertEq(SharedFloat64Array.prototype.BYTES_PER_ELEMENT, 8);
assertEq(SharedInt8Array.BYTES_PER_ELEMENT, 1);
assertEq(SharedUint8Array.BYTES_PER_ELEMENT, 1);
assertEq(SharedUint8ClampedArray.BYTES_PER_ELEMENT, 1);
assertEq(SharedInt16Array.BYTES_PER_ELEMENT, 2);
assertEq(SharedUint16Array.BYTES_PER_ELEMENT, 2);
assertEq(SharedInt32Array.BYTES_PER_ELEMENT, 4);
assertEq(SharedUint32Array.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat32Array.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat32Array.BYTES_PER_ELEMENT, 4);
assertEq(SharedFloat64Array.BYTES_PER_ELEMENT, 8);
assertEq(SharedFloat64Array.BYTES_PER_ELEMENT, 8);
// Distinct prototypes for distinct types
SharedInt8Array.prototype.hello = "hi there";
assertEq(SharedUint8Array.prototype.hello, undefined);
var x1 = new SharedInt8Array(b);
var x2 = new SharedInt32Array(b);
assertEq(SharedArrayBuffer.isView(x1), true);
assertEq(SharedArrayBuffer.isView(x2), true);
assertEq(SharedArrayBuffer.isView({}), false);
assertEq(SharedArrayBuffer.isView(new Int32Array(10)), false);
assertEq(x1.buffer, b);
assertEq(x2.buffer, b);
assertEq(Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call(x1), x1.buffer);
assertEq(Object.getOwnPropertyDescriptor(SharedInt32Array.prototype,"buffer").get.call(x2), x2.buffer);
// Not generic
assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call({}), TypeError);
// Not even to other shared typed arrays
assertThrowsInstanceOf(() => Object.getOwnPropertyDescriptor(SharedInt8Array.prototype,"buffer").get.call(x2), TypeError);
assertEq(x1.byteLength, b.byteLength);
assertEq(x2.byteLength, b.byteLength);
assertEq(x1.byteOffset, 0);
assertEq(x2.byteOffset, 0);
assertEq(x1.length, b.byteLength);
assertEq(x2.length, b.byteLength / 4);
// Conversions that should work
assertEq(SharedInt8Array(x1), x1);
assertEq(SharedInt32Array(x2), x2);
var x3 = new SharedInt8Array(b, 20);
assertEq(x3.length, b.byteLength - 20);
assertEq(x3.byteOffset, 20);
var x3 = new SharedInt32Array(b, 20, 10);
assertEq(x3.length, 10);
assertEq(x3.byteOffset, 20);
// Mismatched type
assertThrowsInstanceOf(() => SharedInt8Array(x2), TypeError);
// Unconvertable object
assertThrowsInstanceOf(() => SharedInt8Array({}), TypeError);
// negative start
assertThrowsInstanceOf(() => new SharedInt8Array(b, -7), RangeError);
// not congruent mod element size
assertThrowsInstanceOf(() => new SharedInt32Array(b, 3), RangeError);
// start out of range
assertThrowsInstanceOf(() => new SharedInt32Array(b, 4104), RangeError);
// end out of range
assertThrowsInstanceOf(() => new SharedInt32Array(b, 4092, 2), RangeError);
var b2 = new SharedInt32Array(1024); // Should create a new buffer
assertEq(b2.length, 1024);
assertEq(b2.byteLength, 4096);
assertEq(b2.byteOffset, 0);
assertEq(b2.buffer != b, true);
var b3 = new SharedInt32Array("1024"); // Should create a new buffer
assertEq(b3.length, 1024);
assertEq(b3.byteLength, 4096);
assertEq(b3.byteOffset, 0);
assertEq(b3.buffer != b, true);
// bad length
assertThrowsInstanceOf(() => new SharedInt32Array({}), TypeError);
// Views alias the storage
x2[0] = -1;
assertEq(x1[0], -1);
x1[0] = 1;
x1[1] = 1;
x1[2] = 1;
x1[3] = 1;
assertEq(x2[0], 0x01010101);
assertEq(x2[5], 0);
x3[0] = -1;
assertEq(x2[5], -1);
// Out-of-range accesses yield undefined
assertEq(x2[1023], 0);
assertEq(x2[1024], undefined);
assertEq(x2[2047], undefined);
}
function testSharedTypedArrayMethods() {
var v = new SharedInt32Array(b);
for ( var i=0 ; i < v.length ; i++ )
v[i] = i;
// Rudimentary tests - they don't test any boundary conditions
// subarray
var w = v.subarray(10, 20);
assertEq(w.length, 10);
for ( var i=0 ; i < w.length ; i++ )
assertEq(w[i], v[i+10]);
for ( var i=0 ; i < w.length ; i++ )
w[i] = -w[i];
for ( var i=0 ; i < w.length ; i++ )
assertEq(w[i], v[i+10]);
// copyWithin
for ( var i=0 ; i < v.length ; i++ )
v[i] = i;
v.copyWithin(10, 20, 30);
for ( var i=0 ; i < 10 ; i++ )
assertEq(v[i], i);
for ( var i=10 ; i < 20 ; i++ )
assertEq(v[i], v[i+10]);
// set
for ( var i=0 ; i < v.length ; i++ )
v[i] = i;
v.set([-1,-2,-3,-4,-5,-6,-7,-8,-9,-10], 5);
for ( var i=5 ; i < 15 ; i++ )
assertEq(v[i], -i+4);
// In the following two cases the two arrays will reference the same buffer,
// so there will be an overlapping copy.
//
// Case 1: Read from lower indices than are written
v.set(v.subarray(0, 7), 1);
assertEq(v[0], 0);
assertEq(v[1], 0);
assertEq(v[2], 1);
assertEq(v[3], 2);
assertEq(v[4], 3);
assertEq(v[5], 4);
assertEq(v[6], -1);
assertEq(v[7], -2);
assertEq(v[8], -4);
assertEq(v[9], -5);
// Case 2: Read from higher indices than are written
v.set(v.subarray(1, 5), 0);
assertEq(v[0], 0);
assertEq(v[1], 1);
assertEq(v[2], 2);
assertEq(v[3], 3);
assertEq(v[4], 3);
assertEq(v[5], 4);
assertEq(v[6], -1);
assertEq(v[7], -2);
assertEq(v[8], -4);
assertEq(v[9], -5);
}
testSharedArrayBuffer();
testSharedTypedArray();
testSharedTypedArrayMethods();
reportCompare(0, 0, 'ok');

View File

@ -4,6 +4,25 @@
* http://creativecommons.org/licenses/publicdomain/
*/
// Note, copied from elsewhere
if (typeof assertThrowsInstanceOf === 'undefined') {
var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) {
var fullmsg;
try {
f();
} catch (exc) {
if (exc instanceof ctor)
return;
fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc;
}
if (fullmsg === undefined)
fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
if (msg !== undefined)
fullmsg += " - " + msg;
throw new Error(fullmsg);
};
}
// NOTE: This only turns on 1.8.5 in shell builds. The browser requires the
// futzing in js/src/tests/browser.js (which only turns on 1.8, the most
// the browser supports).

View File

@ -0,0 +1,46 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_ArrayBufferObject_inl_h
#define vm_ArrayBufferObject_inl_h
/* Utilities and common inline code for ArrayBufferObject and SharedArrayBufferObject */
#include "vm/ArrayBufferObject.h"
#include "js/Value.h"
#include "vm/SharedArrayObject.h"
namespace js {
inline uint32_t
AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared *buf)
{
if (buf->is<ArrayBufferObject>())
return buf->as<ArrayBufferObject>().byteLength();
return buf->as<SharedArrayBufferObject>().byteLength();
}
inline uint8_t *
AnyArrayBufferDataPointer(const ArrayBufferObjectMaybeShared *buf)
{
if (buf->is<ArrayBufferObject>())
return buf->as<ArrayBufferObject>().dataPointer();
return buf->as<SharedArrayBufferObject>().dataPointer();
}
inline ArrayBufferObjectMaybeShared &
AsAnyArrayBuffer(HandleValue val)
{
if (val.toObject().is<ArrayBufferObject>())
return val.toObject().as<ArrayBufferObject>();
return val.toObject().as<SharedArrayBufferObject>();
}
} // namespace js
#endif // vm_ArrayBufferObject_inl_h

View File

@ -42,7 +42,6 @@
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/NumericConversions.h"
#include "vm/SharedArrayObject.h"
#include "vm/WrapperObject.h"
#include "jsatominlines.h"
@ -145,29 +144,25 @@ const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
bool
js::IsArrayBuffer(HandleValue v)
{
return v.isObject() &&
(v.toObject().is<ArrayBufferObject>() ||
v.toObject().is<SharedArrayBufferObject>());
return v.isObject() && v.toObject().is<ArrayBufferObject>();
}
bool
js::IsArrayBuffer(HandleObject obj)
{
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
return obj->is<ArrayBufferObject>();
}
bool
js::IsArrayBuffer(JSObject *obj)
{
return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
return obj->is<ArrayBufferObject>();
}
ArrayBufferObject &
js::AsArrayBuffer(HandleObject obj)
{
JS_ASSERT(IsArrayBuffer(obj));
if (obj->is<SharedArrayBufferObject>())
return obj->as<SharedArrayBufferObject>();
return obj->as<ArrayBufferObject>();
}
@ -175,8 +170,6 @@ ArrayBufferObject &
js::AsArrayBuffer(JSObject *obj)
{
JS_ASSERT(IsArrayBuffer(obj));
if (obj->is<SharedArrayBufferObject>())
return obj->as<SharedArrayBufferObject>();
return obj->as<ArrayBufferObject>();
}
@ -307,9 +300,6 @@ ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead)
bool
ArrayBufferObject::canNeuter(JSContext *cx)
{
if (isSharedArrayBuffer())
return false;
if (isAsmJSArrayBuffer()) {
if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this))
return false;
@ -363,7 +353,6 @@ void
ArrayBufferObject::setNewOwnedData(FreeOp* fop, BufferContents newContents)
{
JS_ASSERT(!isAsmJSArrayBuffer());
JS_ASSERT(!isSharedArrayBuffer());
if (ownsData()) {
JS_ASSERT(newContents.data() != dataPointer());
@ -405,9 +394,6 @@ ArrayBufferObject::prepareForAsmJSNoSignals(JSContext *cx, Handle<ArrayBufferObj
if (buffer->isAsmJSArrayBuffer())
return true;
if (buffer->isSharedArrayBuffer())
return true;
if (!ensureNonInline(cx, buffer))
return false;
@ -434,10 +420,6 @@ ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buf
if (buffer->isAsmJSArrayBuffer())
return true;
// SharedArrayBuffers are already created with AsmJS support in mind.
if (buffer->isSharedArrayBuffer())
return true;
// Get the entire reserved region (with all pages inaccessible).
void *data;
# ifdef XP_WIN
@ -528,7 +510,6 @@ ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
bool
ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
{
JS_ASSERT(!buffer.isSharedArrayBuffer());
AsmJSActivation *act = cx->mainThread().asmJSActivationStack();
for (; act; act = act->prevAsmJS()) {
if (act->module().maybeHeapBufferObject() == &buffer)
@ -579,8 +560,6 @@ ArrayBufferObject::addView(ArrayBufferViewObject *view)
uint8_t *
ArrayBufferObject::dataPointer() const
{
if (isSharedArrayBuffer())
return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate());
}
@ -601,7 +580,6 @@ ArrayBufferObject::releaseData(FreeOp *fop)
void
ArrayBufferObject::setDataPointer(BufferContents contents, OwnsState ownsData)
{
MOZ_ASSERT_IF(!is<SharedArrayBufferObject>(), contents.data());
setSlot(DATA_SLOT, PrivateValue(contents.data()));
setOwnsData(ownsData);
setFlags((flags() & ~KIND_MASK) | contents.kind());
@ -754,7 +732,6 @@ ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp
ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
{
if (!buffer->ownsData()) {
MOZ_ASSERT(!buffer->isSharedArrayBuffer());
BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
if (!contents)
return false;

View File

@ -22,7 +22,8 @@ class ArrayBufferViewObject;
// is as follows.
//
// - JSObject
// - ArrayBufferObject
// - ArrayBufferObjectMaybeShared
// - ArrayBufferObject
// - SharedArrayBufferObject
// - ArrayBufferViewObject
// - DataViewObject
@ -32,12 +33,50 @@ class ArrayBufferViewObject;
// - Uint8ArrayObject
// - ...
// - TypedObject (declared in builtin/TypedObject.h)
// - SharedTypedArrayObject (declared in vm/SharedTypedArrayObject.h)
// - SharedTypedArrayObjectTemplate
// - SharedInt8ArrayObject
// - SharedUint8ArrayObject
// - ...
//
// Note that |TypedArrayObjectTemplate| is just an implementation
// detail that makes implementing its various subclasses easier.
// Note that |TypedArrayObjectTemplate| and |SharedTypedArrayObjectTemplate| are
// just implementation details that make implementing their various subclasses easier.
//
// ArrayBufferObject and SharedArrayBufferObject are unrelated data types:
// the racy memory of the latter cannot substitute for the non-racy memory of
// the former; the non-racy memory of the former cannot be used with the atomics;
// the former can be neutered and the latter not; and they have different
// method suites. Hence they have been separated completely.
//
// Most APIs will only accept ArrayBufferObject. ArrayBufferObjectMaybeShared exists
// as a join point to allow APIs that can take or use either, notably AsmJS.
//
// As ArrayBufferObject and SharedArrayBufferObject are separated, so are the
// TypedArray hierarchies below the two. However, the TypedArrays have the
// same layout (see TypedArrayObject.h), so there is little code duplication.
typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
class ArrayBufferObjectMaybeShared;
uint32_t AnyArrayBufferByteLength(const ArrayBufferObjectMaybeShared *buf);
uint8_t *AnyArrayBufferDataPointer(const ArrayBufferObjectMaybeShared *buf);
ArrayBufferObjectMaybeShared &AsAnyArrayBuffer(HandleValue val);
class ArrayBufferObjectMaybeShared : public JSObject
{
public:
uint32_t byteLength() {
return AnyArrayBufferByteLength(this);
}
uint8_t *dataPointer() {
return AnyArrayBufferDataPointer(this);
}
};
/*
* ArrayBufferObject
*
@ -46,8 +85,11 @@ typedef Vector<ArrayBufferObject *, 0, SystemAllocPolicy> ArrayBufferVector;
* access. It can be created explicitly and passed to an ArrayBufferViewObject
* subclass, or can be created implicitly by constructing a TypedArrayObject
* with a size.
*
* ArrayBufferObject (or really the underlying memory) /is not racy/: the
* memory is private to a single worker.
*/
class ArrayBufferObject : public JSObject
class ArrayBufferObject : public ArrayBufferObjectMaybeShared
{
static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
static bool fun_slice_impl(JSContext *cx, CallArgs args);
@ -67,9 +109,8 @@ class ArrayBufferObject : public JSObject
enum BufferKind {
PLAIN_BUFFER = 0, // malloced or inline data
ASMJS_BUFFER = 0x1,
SHARED_BUFFER = 0x2,
MAPPED_BUFFER = 0x4,
KIND_MASK = ASMJS_BUFFER | SHARED_BUFFER | MAPPED_BUFFER
MAPPED_BUFFER = 0x2,
KIND_MASK = ASMJS_BUFFER | MAPPED_BUFFER
};
protected:
@ -223,7 +264,6 @@ class ArrayBufferObject : public JSObject
BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
bool isAsmJSArrayBuffer() const { return flags() & ASMJS_BUFFER; }
bool isSharedArrayBuffer() const { return flags() & SHARED_BUFFER; }
bool isMappedArrayBuffer() const { return flags() & MAPPED_BUFFER; }
bool isNeutered() const { return flags() & NEUTERED_BUFFER; }
@ -269,7 +309,6 @@ class ArrayBufferObject : public JSObject
}
void setIsAsmJSArrayBuffer() { setFlags(flags() | ASMJS_BUFFER); }
void setIsSharedArrayBuffer() { setFlags(flags() | SHARED_BUFFER); }
void setIsMappedArrayBuffer() { setFlags(flags() | MAPPED_BUFFER); }
void setIsNeutered() { setFlags(flags() | NEUTERED_BUFFER); }
@ -343,8 +382,8 @@ InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *bu
{
/*
* N.B. The base of the array's data is stored in the object's
* private data rather than a slot to avoid alignment restrictions
* on private Values.
* private data rather than a slot to avoid the restriction that
* private Values that are pointers must have the low bits clear.
*/
MOZ_ASSERT(buffer->dataPointer() != nullptr);
obj->initPrivate(buffer->dataPointer() + byteOffset);
@ -353,8 +392,7 @@ InitArrayBufferViewDataPointer(ArrayBufferViewObject *obj, ArrayBufferObject *bu
}
/*
* Tests for either ArrayBufferObject or SharedArrayBufferObject.
* For specific class testing, use e.g., obj->is<ArrayBufferObject>().
* Tests for ArrayBufferObject, like obj->is<ArrayBufferObject>().
*/
bool IsArrayBuffer(HandleValue v);
bool IsArrayBuffer(HandleObject obj);

View File

@ -36,9 +36,10 @@ inline bool
ClassCanHaveFixedData(const Class *clasp)
{
// Normally, the number of fixed slots given an object is the maximum
// permitted for its size class. For array buffers and typed arrays we only
// use enough to cover the class reserved slots, so that the remaining
// space in the object's allocation is available for the buffer's data.
// permitted for its size class. For array buffers and non-shared typed
// arrays we only use enough to cover the class reserved slots, so that
// the remaining space in the object's allocation is available for the
// buffer's data.
return clasp == &ArrayBufferObject::class_ || IsTypedArrayClass(clasp);
}

View File

@ -9,6 +9,7 @@
#include "gc/Marking.h"
#include "js/Value.h"
#include "vm/Debugger.h"
#include "vm/TypedArrayCommon.h"
#include "jsobjinlines.h"
#include "vm/Shape-inl.h"
@ -78,7 +79,7 @@ bool
ObjectImpl::canHaveNonEmptyElements()
{
JSObject *obj = static_cast<JSObject *>(this);
return isNative() && !obj->is<TypedArrayObject>();
return isNative() && !IsAnyTypedArray(obj);
}
#endif // DEBUG

View File

@ -24,6 +24,7 @@
#include "vm/ForkJoin.h"
#include "vm/Interpreter.h"
#include "vm/String.h"
#include "vm/TypedArrayCommon.h"
#include "jsfuninlines.h"
#include "jsscriptinlines.h"
@ -469,9 +470,9 @@ js::intrinsic_UnsafePutElements(JSContext *cx, unsigned argc, Value *vp)
RootedObject arrobj(cx, &args[arri].toObject());
uint32_t idx = args[idxi].toInt32();
if (arrobj->is<TypedArrayObject>() || arrobj->is<TypedObject>()) {
JS_ASSERT(!arrobj->is<TypedArrayObject>() || idx < arrobj->as<TypedArrayObject>().length());
JS_ASSERT(!arrobj->is<TypedObject>() || idx < uint32_t(arrobj->as<TypedObject>().length()));
if (IsAnyTypedArray(arrobj.get()) || arrobj->is<TypedObject>()) {
JS_ASSERT_IF(IsAnyTypedArray(arrobj.get()), idx < AnyTypedArrayLength(arrobj.get()));
JS_ASSERT_IF(arrobj->is<TypedObject>(), idx < uint32_t(arrobj->as<TypedObject>().length()));
RootedValue tmp(cx, args[elemi]);
// XXX: Always non-strict.
if (!JSObject::setElement(cx, arrobj, arrobj, idx, &tmp, false))

View File

@ -15,7 +15,7 @@
#include "vm/Interpreter.h"
#include "vm/ScopeObject.h"
#include "vm/TypedArrayObject.h"
#include "vm/TypedArrayCommon.h"
#include "jsatominlines.h"
#include "jscntxtinlines.h"
@ -212,7 +212,7 @@ GetShapeAttributes(JSObject *obj, Shape *shape)
JS_ASSERT(obj->isNative());
if (IsImplicitDenseOrTypedArrayElement(shape)) {
if (obj->is<TypedArrayObject>())
if (IsAnyTypedArray(obj))
return JSPROP_ENUMERATE | JSPROP_PERMANENT;
return JSPROP_ENUMERATE;
}

View File

@ -11,10 +11,11 @@
#ifdef XP_WIN
# include "jswin.h"
#else
#endif
#include "jswrapper.h"
#ifndef XP_WIN
# include <sys/mman.h>
#endif
#ifdef MOZ_VALGRIND
# include <valgrind/memcheck.h>
#endif
@ -22,16 +23,10 @@
#include "mozilla/Atomics.h"
#include "asmjs/AsmJSValidate.h"
#include "vm/TypedArrayCommon.h"
using namespace js;
using mozilla::IsNaN;
using mozilla::PodCopy;
/*
* SharedArrayRawBuffer
*/
static inline void *
MapMemory(size_t length, bool commit)
{
@ -84,7 +79,11 @@ static_assert(sizeof(SharedArrayRawBuffer) < AsmJSPageSize, "Page size not big e
SharedArrayRawBuffer *
SharedArrayRawBuffer::New(uint32_t length)
{
// Enforced by SharedArrayBufferObject constructor.
// The value (uint32_t)-1 is used as a signal in various places,
// so guard against it on principle.
JS_ASSERT(length != (uint32_t)-1);
// Enforced by SharedArrayBufferObject::New(cx, length).
JS_ASSERT(IsValidAsmJSHeapLength(length));
#ifdef JS_CODEGEN_X64
@ -148,14 +147,15 @@ SharedArrayRawBuffer::dropReference()
}
}
/*
* SharedArrayBufferObject
*/
bool
js::IsSharedArrayBuffer(HandleValue v)
{
return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
}
const JSFunctionSpec SharedArrayBufferObject::jsfuncs[] = {
/* Nothing yet */
JS_FS_END
};
const JSFunctionSpec SharedArrayBufferObject::jsstaticfuncs[] = {
JS_FN("isView", SharedArrayBufferObject::fun_isView, 1, 0),
JS_FS_END
};
MOZ_ALWAYS_INLINE bool
SharedArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
@ -172,27 +172,45 @@ SharedArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *v
return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
}
bool
SharedArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setBoolean(args.get(0).isObject() &&
JS_IsSharedTypedArrayObject(&args.get(0).toObject()));
return true;
}
bool
SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
{
int32_t length = 0;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() > 0 && !ToInt32(cx, args[0], &length))
return false;
if (length < 0) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
if (!args.isConstructing()) {
if (args.hasDefined(0) && IsObjectWithClass(args[0], ESClass_SharedArrayBuffer, cx)) {
args.rval().set(args[0]);
return true;
}
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_OBJECT);
return false;
}
JSObject *bufobj = New(cx, uint32_t(length));
uint32_t length;
bool overflow;
if (!ToLengthClamped(cx, args[0], &length, &overflow)) {
if (overflow)
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH);
return false;
}
JSObject *bufobj = New(cx, length);
if (!bufobj)
return false;
args.rval().setObject(*bufobj);
return true;
}
JSObject *
SharedArrayBufferObject *
SharedArrayBufferObject::New(JSContext *cx, uint32_t length)
{
if (!IsValidAsmJSHeapLength(length)) {
@ -210,7 +228,7 @@ SharedArrayBufferObject::New(JSContext *cx, uint32_t length)
return New(cx, buffer);
}
JSObject *
SharedArrayBufferObject *
SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
{
Rooted<SharedArrayBufferObject*> obj(cx, NewBuiltinClassInstance<SharedArrayBufferObject>(cx));
@ -219,10 +237,7 @@ SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
JS_ASSERT(obj->getClass() == &class_);
obj->initialize(buffer->byteLength(), BufferContents::createUnowned(nullptr), DoesntOwnData);
obj->acceptRawBuffer(buffer);
obj->setIsSharedArrayBuffer();
return obj;
}
@ -230,34 +245,21 @@ SharedArrayBufferObject::New(JSContext *cx, SharedArrayRawBuffer *buffer)
void
SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer *buffer)
{
setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, PrivateValue(buffer));
setReservedSlot(RAWBUF_SLOT, PrivateValue(buffer));
}
void
SharedArrayBufferObject::dropRawBuffer()
{
setReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT, UndefinedValue());
setReservedSlot(RAWBUF_SLOT, UndefinedValue());
}
SharedArrayRawBuffer *
SharedArrayBufferObject::rawBufferObject() const
{
// RAWBUF_SLOT must be populated via acceptRawBuffer(),
// and the raw buffer must not have been dropped.
Value v = getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
return (SharedArrayRawBuffer *)v.toPrivate();
}
uint8_t *
SharedArrayBufferObject::dataPointer() const
{
return rawBufferObject()->dataPointer();
}
uint32_t
SharedArrayBufferObject::byteLength() const
{
return rawBufferObject()->byteLength();
Value v = getReservedSlot(RAWBUF_SLOT);
JS_ASSERT(!v.isUndefined());
return reinterpret_cast<SharedArrayRawBuffer *>(v.toPrivate());
}
void
@ -267,16 +269,19 @@ SharedArrayBufferObject::Finalize(FreeOp *fop, JSObject *obj)
// Detect the case of failure during SharedArrayBufferObject creation,
// which causes a SharedArrayRawBuffer to never be attached.
Value v = buf.getReservedSlot(SharedArrayBufferObject::RAWBUF_SLOT);
Value v = buf.getReservedSlot(RAWBUF_SLOT);
if (!v.isUndefined()) {
buf.rawBufferObject()->dropReference();
buf.dropRawBuffer();
}
}
/*
* SharedArrayBufferObject
*/
/* static */ void
SharedArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo *info)
{
info->objectsNonHeapElementsMapped += obj->as<SharedArrayBufferObject>().byteLength();
}
const Class SharedArrayBufferObject::protoClass = {
"SharedArrayBufferPrototype",
@ -306,7 +311,7 @@ const Class SharedArrayBufferObject::class_ = {
nullptr, /* call */
nullptr, /* hasInstance */
nullptr, /* construct */
ArrayBufferObject::obj_trace,
nullptr, /* trace */
JS_NULL_CLASS_SPEC,
JS_NULL_CLASS_EXT
};
@ -325,6 +330,9 @@ js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
if (!ctor)
return nullptr;
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
return nullptr;
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
@ -335,14 +343,41 @@ js_InitSharedArrayBufferClass(JSContext *cx, HandleObject obj)
if (!getter)
return nullptr;
RootedValue value(cx, UndefinedValue());
if (!DefineNativeProperty(cx, proto, byteLengthId, value,
if (!DefineNativeProperty(cx, proto, byteLengthId, UndefinedHandleValue,
JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs))
{
return nullptr;
}
if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_SharedArrayBuffer, ctor, proto))
if (!JS_DefineFunctions(cx, ctor, SharedArrayBufferObject::jsstaticfuncs))
return nullptr;
if (!JS_DefineFunctions(cx, proto, SharedArrayBufferObject::jsfuncs))
return nullptr;
return proto;
}
bool
js::IsSharedArrayBuffer(HandleValue v)
{
return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
}
bool
js::IsSharedArrayBuffer(HandleObject o)
{
return o->is<SharedArrayBufferObject>();
}
SharedArrayBufferObject &
js::AsSharedArrayBuffer(HandleObject obj)
{
JS_ASSERT(IsSharedArrayBuffer(obj));
return obj->as<SharedArrayBufferObject>();
}
JS_FRIEND_API(bool)
JS_IsSharedArrayBufferObject(JSObject *obj)
{
obj = CheckedUnwrap(obj);
return obj ? obj->is<ArrayBufferObject>() : false;
}

View File

@ -30,6 +30,14 @@ namespace js {
* |<------ sizeof ------>|<- length ->|
*
* | waste | SharedArrayRawBuffer | data array | waste |
*
* Observe that if we want to map the data array on a specific address, such
* as absolute zero (bug 1056027), then the SharedArrayRawBuffer cannot be
* prefixed to the data array, it has to be a separate object, also in
* shared memory. (That would get rid of ~4KB of waste, as well.) Very little
* else would have to change throughout the engine, the SARB would point to
* the data array using a constant pointer, instead of computing its
* address.
*/
class SharedArrayRawBuffer
{
@ -62,44 +70,83 @@ class SharedArrayRawBuffer
/*
* SharedArrayBufferObject
*
* When transferred to a WebWorker, the buffer is not neutered on the parent side,
* and both child and parent reference the same buffer.
* When transferred to a WebWorker, the buffer is not neutered on the
* parent side, and both child and parent reference the same buffer.
*
* The underlying memory is memory-mapped and reference counted
* (across workers and/or processes). The SharedArrayBuffer object
* has a finalizer that decrements the refcount, the last one to leave
* (globally) unmaps the memory. The sender ups the refcount before
* transmitting the memory to another worker.
*
* SharedArrayBufferObject (or really the underlying memory) /is
* racy/: more than one worker can access the memory at the same time.
*
* A SharedTypedArrayObject (a view) references a SharedArrayBuffer
* and keeps it alive. The SharedArrayBuffer does /not/ reference its
* views, nor do the views reference each other in any way.
*/
class SharedArrayBufferObject : public ArrayBufferObject
class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
{
static bool byteLengthGetterImpl(JSContext *cx, CallArgs args);
public:
// RAWBUF_SLOT holds a pointer (as "private" data) to the
// SharedArrayRawBuffer object, which is manually managed storage.
static const uint8_t RAWBUF_SLOT = 0;
static const uint8_t RESERVED_SLOTS = 1;
static const Class class_;
static const Class protoClass;
static const JSFunctionSpec jsfuncs[];
static const JSFunctionSpec jsstaticfuncs[];
// Slot used for storing a pointer to the SharedArrayRawBuffer.
static const uint8_t RAWBUF_SLOT = ArrayBufferObject::RESERVED_SLOTS;
static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
static const uint8_t RESERVED_SLOTS = ArrayBufferObject::RESERVED_SLOTS + 1;
static bool fun_isView(JSContext *cx, unsigned argc, Value *vp);
static bool class_constructor(JSContext *cx, unsigned argc, Value *vp);
// Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
static JSObject *New(JSContext *cx, uint32_t length);
static SharedArrayBufferObject *New(JSContext *cx, uint32_t length);
// Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
static JSObject *New(JSContext *cx, SharedArrayRawBuffer *buffer);
static bool byteLengthGetter(JSContext *cx, unsigned argc, Value *vp);
static SharedArrayBufferObject *New(JSContext *cx, SharedArrayRawBuffer *buffer);
static void Finalize(FreeOp *fop, JSObject *obj);
void acceptRawBuffer(SharedArrayRawBuffer *buffer);
void dropRawBuffer();
static void addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf,
JS::ClassInfo *info);
SharedArrayRawBuffer *rawBufferObject() const;
uint8_t *dataPointer() const;
uint32_t byteLength() const;
// Invariant: This method does not cause GC and can be called
// without anchoring the object it is called on.
void *globalID() const {
// The buffer address is good enough as an ID provided the memory is not shared
// between processes or, if it is, it is mapped to the same address in every
// process. (At the moment, shared memory cannot be shared between processes.)
return dataPointer();
}
uint32_t byteLength() const {
return rawBufferObject()->byteLength();
}
uint8_t *dataPointer() const {
return rawBufferObject()->dataPointer();
}
private:
void acceptRawBuffer(SharedArrayRawBuffer *buffer);
void dropRawBuffer();
};
bool
IsSharedArrayBuffer(HandleValue v);
bool IsSharedArrayBuffer(HandleValue v);
bool IsSharedArrayBuffer(HandleObject o);
SharedArrayBufferObject &AsSharedArrayBuffer(HandleObject o);
} // namespace js

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_SharedTypedArrayObject_h
#define vm_SharedTypedArrayObject_h
#include "jsobj.h"
#include "builtin/TypedObjectConstants.h"
#include "gc/Barrier.h"
#include "js/Class.h"
#include "vm/ArrayBufferObject.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayObject.h"
typedef struct JSProperty JSProperty;
namespace js {
// Note that the representation of a SharedTypedArrayObject is the
// same as the representation of a TypedArrayObject, see comments in
// TypedArrayObject.h.
class SharedTypedArrayObject : public JSObject
{
protected:
static const size_t TYPE_SLOT = TypedArrayLayout::TYPE_SLOT;
static const size_t BUFFER_SLOT = TypedArrayLayout::BUFFER_SLOT;
static const size_t BYTEOFFSET_SLOT = TypedArrayLayout::BYTEOFFSET_SLOT;
static const size_t LENGTH_SLOT = TypedArrayLayout::LENGTH_SLOT;
static const size_t RESERVED_SLOTS = TypedArrayLayout::RESERVED_SLOTS;
static const size_t DATA_SLOT = TypedArrayLayout::DATA_SLOT;
public:
static const Class classes[Scalar::TypeMax];
static const Class protoClasses[Scalar::TypeMax];
static SharedArrayBufferObject *bufferObject(JSContext *cx, Handle<SharedTypedArrayObject *> obj);
static Value bufferValue(SharedTypedArrayObject *tarr) {
return tarr->getFixedSlot(BUFFER_SLOT);
}
static Value byteOffsetValue(SharedTypedArrayObject *tarr) {
return tarr->getFixedSlot(BYTEOFFSET_SLOT);
}
static Value byteLengthValue(SharedTypedArrayObject *tarr) {
int32_t size = Scalar::byteSize(tarr->type());
return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * size);
}
static Value lengthValue(SharedTypedArrayObject *tarr) {
return tarr->getFixedSlot(LENGTH_SLOT);
}
static void setElement(SharedTypedArrayObject &obj, uint32_t index, double d);
static bool isOriginalLengthGetter(Scalar::Type type, Native native);
SharedArrayBufferObject *buffer() const;
Scalar::Type type() const {
return (Scalar::Type) getFixedSlot(TYPE_SLOT).toInt32();
}
void *viewData() const {
return getPrivate(DATA_SLOT);
}
uint32_t byteOffset() const {
return byteOffsetValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
}
uint32_t byteLength() const {
return byteLengthValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
}
uint32_t length() const {
return lengthValue(const_cast<SharedTypedArrayObject*>(this)).toInt32();
}
Value getElement(uint32_t index);
/*
* Byte length above which created typed arrays and data views will have
* singleton types regardless of the context in which they are created.
*/
static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
private:
static TypedArrayLayout layout_;
public:
static const TypedArrayLayout &layout() {
return layout_;
}
};
inline bool
IsSharedTypedArrayClass(const Class *clasp)
{
return &SharedTypedArrayObject::classes[0] <= clasp &&
clasp < &SharedTypedArrayObject::classes[Scalar::TypeMax];
}
inline bool
IsSharedTypedArrayProtoClass(const Class *clasp)
{
return &SharedTypedArrayObject::protoClasses[0] <= clasp &&
clasp < &SharedTypedArrayObject::protoClasses[Scalar::TypeMax];
}
bool
IsSharedTypedArrayConstructor(HandleValue v, uint32_t type);
} // namespace js
template <>
inline bool
JSObject::is<js::SharedTypedArrayObject>() const
{
return js::IsSharedTypedArrayClass(getClass());
}
#endif // vm_SharedTypedArrayObject_h

View File

@ -77,6 +77,7 @@ enum StructuredDataType MOZ_ENUM_TYPE(uint32_t) {
SCTAG_TYPED_ARRAY_OBJECT,
SCTAG_MAP_OBJECT,
SCTAG_SET_OBJECT,
SCTAG_SHARED_TYPED_ARRAY_OBJECT,
SCTAG_END_OF_KEYS,
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
@ -226,6 +227,7 @@ struct JSStructuredCloneReader {
bool checkDouble(double d);
bool readTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp,
bool v1Read = false);
bool readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
bool readArrayBuffer(uint32_t nbytes, MutableHandleValue vp);
bool readV1ArrayBuffer(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp);
bool startRead(MutableHandleValue vp);
@ -278,6 +280,8 @@ struct JSStructuredCloneWriter {
bool writeString(uint32_t tag, JSString *str);
bool writeArrayBuffer(HandleObject obj);
bool writeTypedArray(HandleObject obj);
bool writeSharedArrayBuffer(HandleObject obj);
bool writeSharedTypedArray(HandleObject obj);
bool startObject(HandleObject obj, bool *backref);
bool startWrite(HandleValue v);
bool traverseObject(HandleObject obj);
@ -880,6 +884,33 @@ JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj)
out.writeBytes(buffer.dataPointer(), buffer.byteLength());
}
bool
JSStructuredCloneWriter::writeSharedArrayBuffer(HandleObject obj)
{
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_SC_SHMEM_MUST_TRANSFER);
return false;
}
bool
JSStructuredCloneWriter::writeSharedTypedArray(HandleObject obj)
{
Rooted<SharedTypedArrayObject*> tarr(context(), &CheckedUnwrap(obj)->as<SharedTypedArrayObject>());
JSAutoCompartment ac(context(), tarr);
if (!out.writePair(SCTAG_SHARED_TYPED_ARRAY_OBJECT, tarr->length()))
return false;
uint64_t type = tarr->type();
if (!out.write(type))
return false;
// Write out the SharedArrayBuffer tag and contents.
RootedValue val(context(), SharedTypedArrayObject::bufferValue(tarr));
if (!startWrite(val))
return false;
return out.write(tarr->byteOffset());
}
bool
JSStructuredCloneWriter::startObject(HandleObject obj, bool *backref)
{
@ -1026,6 +1057,10 @@ JSStructuredCloneWriter::startWrite(HandleValue v)
return writeTypedArray(obj);
} else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
return writeArrayBuffer(obj);
} else if (JS_IsSharedTypedArrayObject(obj)) {
return writeSharedTypedArray(obj);
} else if (JS_IsSharedArrayBufferObject(obj)) {
return writeSharedArrayBuffer(obj);
} else if (ObjectClassIs(obj, ESClass_Object, context())) {
return traverseObject(obj);
} else if (ObjectClassIs(obj, ESClass_Array, context())) {
@ -1123,32 +1158,31 @@ JSStructuredCloneWriter::transferOwnership()
if (ObjectClassIs(obj, ESClass_ArrayBuffer, context())) {
// The current setup of the array buffer inheritance hierarchy doesn't
// lend itself well to generic manipulation via proxies.
Rooted<ArrayBufferObject*> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
if (arrayBuffer->isSharedArrayBuffer()) {
SharedArrayRawBuffer *rawbuf = arrayBuffer->as<SharedArrayBufferObject>().rawBufferObject();
Rooted<ArrayBufferObject *> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
size_t nbytes = arrayBuffer->byteLength();
ArrayBufferObject::BufferContents bufContents =
ArrayBufferObject::stealContents(context(), arrayBuffer);
if (!bufContents)
return false; // Destructor will clean up the already-transferred data.
content = bufContents.data();
tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
if (bufContents.kind() & ArrayBufferObject::MAPPED_BUFFER)
ownership = JS::SCTAG_TMO_MAPPED_DATA;
else
ownership = JS::SCTAG_TMO_ALLOC_DATA;
extraData = nbytes;
} else if (ObjectClassIs(obj, ESClass_SharedArrayBuffer, context())) {
Rooted<SharedArrayBufferObject *> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
SharedArrayRawBuffer *rawbuf = sharedArrayBuffer->rawBufferObject();
// Avoids a race condition where the parent thread frees the buffer
// before the child has accepted the transferable.
rawbuf->addReference();
// Avoids a race condition where the parent thread frees the buffer
// before the child has accepted the transferable.
rawbuf->addReference();
tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
ownership = JS::SCTAG_TMO_SHARED_BUFFER;
content = rawbuf;
extraData = 0;
} else {
size_t nbytes = arrayBuffer->byteLength();
ArrayBufferObject::BufferContents bufContents =
ArrayBufferObject::stealContents(context(), arrayBuffer);
if (!bufContents)
return false; // Destructor will clean up the already-transferred data
content = bufContents.data();
tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
if (bufContents.kind() & ArrayBufferObject::MAPPED_BUFFER)
ownership = JS::SCTAG_TMO_MAPPED_DATA;
else
ownership = JS::SCTAG_TMO_ALLOC_DATA;
extraData = nbytes;
}
tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
ownership = JS::SCTAG_TMO_SHARED_BUFFER;
content = rawbuf;
extraData = 0;
} else {
if (!callbacks || !callbacks->writeTransfer)
return reportErrorTransferable();
@ -1378,6 +1412,74 @@ JSStructuredCloneReader::readTypedArray(uint32_t arrayType, uint32_t nelems, Mut
return true;
}
bool
JSStructuredCloneReader::readSharedTypedArray(uint32_t arrayType, uint32_t nelems, MutableHandleValue vp)
{
if (arrayType > Scalar::Uint8Clamped) {
JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr,
JSMSG_SC_BAD_SERIALIZED_DATA, "unhandled typed array element type");
return false;
}
// Push a placeholder onto the allObjs list to stand in for the typed array.
uint32_t placeholderIndex = allObjs.length();
Value dummy = UndefinedValue();
if (!allObjs.append(dummy))
return false;
// Read the ArrayBuffer object and its contents (but no properties).
RootedValue v(context());
uint32_t byteOffset;
if (!startRead(&v))
return false;
uint64_t n;
if (!in.read(&n))
return false;
byteOffset = n;
RootedObject buffer(context(), &v.toObject());
RootedObject obj(context());
switch (arrayType) {
case Scalar::Int8:
obj = JS_NewSharedInt8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Uint8:
obj = JS_NewSharedUint8ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Int16:
obj = JS_NewSharedInt16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Uint16:
obj = JS_NewSharedUint16ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Int32:
obj = JS_NewSharedInt32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Uint32:
obj = JS_NewSharedUint32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Float32:
obj = JS_NewSharedFloat32ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Float64:
obj = JS_NewSharedFloat64ArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
case Scalar::Uint8Clamped:
obj = JS_NewSharedUint8ClampedArrayWithBuffer(context(), buffer, byteOffset, nelems);
break;
default:
MOZ_CRASH("Can't happen: arrayType range checked above");
}
if (!obj)
return false;
vp.setObject(*obj);
allObjs[placeholderIndex].set(vp);
return true;
}
bool
JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, MutableHandleValue vp)
{
@ -1564,12 +1666,21 @@ JSStructuredCloneReader::startRead(MutableHandleValue vp)
return false;
break;
case SCTAG_TYPED_ARRAY_OBJECT:
// readTypedArray adds the array to allObjs
case SCTAG_TYPED_ARRAY_OBJECT: {
// readTypedArray adds the array to allObjs.
uint64_t arrayType;
if (!in.read(&arrayType))
return false;
return readTypedArray(arrayType, data, vp);
}
case SCTAG_SHARED_TYPED_ARRAY_OBJECT: {
// readSharedTypedArray adds the array to allObjs.
uint64_t arrayType;
if (!in.read(&arrayType))
return false;
return readSharedTypedArray(arrayType, data, vp);
}
case SCTAG_MAP_OBJECT: {
JSObject *obj = MapObject::create(context());

View File

@ -0,0 +1,657 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_TypedArrayCommon_h
#define vm_TypedArrayCommon_h
/* Utilities and common inline code for TypedArray and SharedTypedArray */
#include "mozilla/FloatingPoint.h"
#include "js/Value.h"
#include "vm/SharedTypedArrayObject.h"
#include "vm/TypedArrayObject.h"
namespace js {
// Definitions below are shared between TypedArrayObject and
// SharedTypedArrayObject.
// ValueIsLength happens not to be according to ES6, which mandates
// the use of ToLength, which in turn includes ToNumber, ToInteger,
// and clamping. ValueIsLength is used in the current TypedArray code
// but will disappear when that code is made spec-compliant.
inline bool
ValueIsLength(const Value &v, uint32_t *len)
{
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i < 0)
return false;
*len = i;
return true;
}
if (v.isDouble()) {
double d = v.toDouble();
if (mozilla::IsNaN(d))
return false;
uint32_t length = uint32_t(d);
if (d != double(length))
return false;
*len = length;
return true;
}
return false;
}
template<typename NativeType> static inline Scalar::Type TypeIDOfType();
template<> inline Scalar::Type TypeIDOfType<int8_t>() { return Scalar::Int8; }
template<> inline Scalar::Type TypeIDOfType<uint8_t>() { return Scalar::Uint8; }
template<> inline Scalar::Type TypeIDOfType<int16_t>() { return Scalar::Int16; }
template<> inline Scalar::Type TypeIDOfType<uint16_t>() { return Scalar::Uint16; }
template<> inline Scalar::Type TypeIDOfType<int32_t>() { return Scalar::Int32; }
template<> inline Scalar::Type TypeIDOfType<uint32_t>() { return Scalar::Uint32; }
template<> inline Scalar::Type TypeIDOfType<float>() { return Scalar::Float32; }
template<> inline Scalar::Type TypeIDOfType<double>() { return Scalar::Float64; }
template<> inline Scalar::Type TypeIDOfType<uint8_clamped>() { return Scalar::Uint8Clamped; }
inline bool
IsAnyTypedArray(HandleObject obj)
{
return obj->is<TypedArrayObject>() || obj->is<SharedTypedArrayObject>();
}
inline bool
IsAnyTypedArray(const JSObject *obj)
{
return obj->is<TypedArrayObject>() || obj->is<SharedTypedArrayObject>();
}
inline uint32_t
AnyTypedArrayLength(HandleObject obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().length();
return obj->as<SharedTypedArrayObject>().length();
}
inline uint32_t
AnyTypedArrayLength(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().length();
return obj->as<SharedTypedArrayObject>().length();
}
inline Scalar::Type
AnyTypedArrayType(HandleObject obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().type();
return obj->as<SharedTypedArrayObject>().type();
}
inline Scalar::Type
AnyTypedArrayType(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().type();
return obj->as<SharedTypedArrayObject>().type();
}
inline Shape*
AnyTypedArrayShape(HandleObject obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().lastProperty();
return obj->as<SharedTypedArrayObject>().lastProperty();
}
inline Shape*
AnyTypedArrayShape(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().lastProperty();
return obj->as<SharedTypedArrayObject>().lastProperty();
}
inline const TypedArrayLayout&
AnyTypedArrayLayout(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().layout();
return obj->as<SharedTypedArrayObject>().layout();
}
inline void *
AnyTypedArrayViewData(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().viewData();
return obj->as<SharedTypedArrayObject>().viewData();
}
inline uint32_t
AnyTypedArrayByteLength(const JSObject *obj)
{
if (obj->is<TypedArrayObject>())
return obj->as<TypedArrayObject>().byteLength();
return obj->as<SharedTypedArrayObject>().byteLength();
}
inline bool
IsAnyTypedArrayClass(const Class *clasp)
{
return IsTypedArrayClass(clasp) || IsSharedTypedArrayClass(clasp);
}
// The subarray() method on TypedArray and SharedTypedArray
template<typename Adapter>
class TypedArraySubarrayTemplate
{
typedef typename Adapter::ElementType NativeType;
typedef typename Adapter::TypedArrayObjectType ArrayType;
typedef typename Adapter::ArrayBufferObjectType BufferType;
public:
static JSObject *
createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
{
Rooted<ArrayType*> tarray(cx, &tarrayArg->as<ArrayType>());
if (begin > tarray->length() || end > tarray->length() || begin > end) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
return nullptr;
}
if (!Adapter::ensureHasBuffer(cx, tarray))
return nullptr;
Rooted<BufferType *> bufobj(cx, tarray->buffer());
JS_ASSERT(bufobj);
uint32_t length = end - begin;
JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
uint32_t arrayByteOffset = tarray->byteOffset();
JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
return Adapter::ThisTypedArrayObjectType::makeInstance(cx, bufobj, byteOffset, length);
}
static bool
fun_subarray_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
Rooted<ArrayType *> tarray(cx, &args.thisv().toObject().as<ArrayType>());
// these are the default values
uint32_t length = tarray->length();
uint32_t begin = 0, end = length;
if (args.length() > 0 && !ToClampedIndex(cx, args[0], length, &begin))
return false;
if (args.length() > 1 && !ToClampedIndex(cx, args[1], length, &end))
return false;
if (begin > end)
begin = end;
JSObject *nobj = createSubarray(cx, tarray, begin, end);
if (!nobj)
return false;
args.rval().setObject(*nobj);
return true;
}
};
// The copyWithin() method on TypedArray and SharedTypedArray
template<typename Adapter>
class TypedArrayCopyWithinTemplate
{
typedef typename Adapter::ElementType NativeType;
typedef typename Adapter::TypedArrayObjectType ArrayType;
public:
/* copyWithin(target, start[, end]) */
// ES6 draft rev 26, 22.2.3.5
static bool
fun_copyWithin_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
// Steps 1-2.
Rooted<ArrayType*> obj(cx, &args.thisv().toObject().as<ArrayType>());
// Steps 3-4.
uint32_t len = obj->length();
// Steps 6-8.
uint32_t to;
if (!ToClampedIndex(cx, args.get(0), len, &to))
return false;
// Steps 9-11.
uint32_t from;
if (!ToClampedIndex(cx, args.get(1), len, &from))
return false;
// Steps 12-14.
uint32_t final;
if (args.get(2).isUndefined()) {
final = len;
} else {
if (!ToClampedIndex(cx, args.get(2), len, &final))
return false;
}
// Steps 15-18.
// If |final - from < 0|, then |count| will be less than 0, so step 18
// never loops. Exit early so |count| can use a non-negative type.
// Also exit early if elements are being moved to their pre-existing
// location.
if (final < from || to == from) {
args.rval().setObject(*obj);
return true;
}
uint32_t count = Min(final - from, len - to);
uint32_t lengthDuringMove = obj->length(); // beware ToClampedIndex
MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
MOZ_ASSERT(from <= INT32_MAX, "size limited to 2**31");
MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
if (from + count > lengthDuringMove || to + count > lengthDuringMove) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
const size_t ElementSize = sizeof(NativeType);
MOZ_ASSERT(to <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
MOZ_ASSERT(from <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
MOZ_ASSERT(count <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
uint32_t byteDest = to * ElementSize;
uint32_t byteSrc = from * ElementSize;
uint32_t byteSize = count * ElementSize;
#ifdef DEBUG
uint32_t viewByteLength = obj->byteLength();
JS_ASSERT(byteDest <= viewByteLength);
JS_ASSERT(byteSrc <= viewByteLength);
JS_ASSERT(byteDest + byteSize <= viewByteLength);
JS_ASSERT(byteSrc + byteSize <= viewByteLength);
// Should not overflow because size is limited to 2^31
JS_ASSERT(byteDest + byteSize >= byteDest);
JS_ASSERT(byteSrc + byteSize >= byteSrc);
#endif
uint8_t *data = static_cast<uint8_t*>(obj->viewData());
mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize);
// Step 19.
args.rval().set(args.thisv());
return true;
}
};
// The set() method on TypedArray and SharedTypedArray
template<class Adapter>
class TypedArraySetTemplate
{
typedef typename Adapter::ElementType NativeType;
typedef typename Adapter::TypedArrayObjectType ArrayType;
public:
/* set(array[, offset]) */
static bool
fun_set_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(Adapter::ThisTypedArrayObjectType::IsThisClass(args.thisv()));
Rooted<ArrayType*> tarray(cx, &args.thisv().toObject().as<ArrayType>());
// first arg must be either a typed array or a JS array
if (args.length() == 0 || !args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
int32_t offset = 0;
if (args.length() > 1) {
if (!ToInt32(cx, args[1], &offset))
return false;
if (offset < 0 || uint32_t(offset) > tarray->length()) {
// the given offset is bogus
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
return false;
}
}
if (!args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
RootedObject arg0(cx, args[0].toObjectOrNull());
if (IsAnyTypedArray(arg0.get())) {
if (AnyTypedArrayLength(arg0.get()) > tarray->length() - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
if (!copyFromTypedArray(cx, tarray, arg0, offset))
return false;
} else {
uint32_t len;
if (!GetLengthProperty(cx, arg0, &len))
return false;
if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
if (!copyFromArray(cx, tarray, arg0, len, offset))
return false;
}
args.rval().setUndefined();
return true;
}
static bool
copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
HandleObject source, uint32_t len, uint32_t offset = 0)
{
Rooted<ArrayType*> thisTypedArray(cx, &thisTypedArrayObj->as<ArrayType>());
JS_ASSERT(offset <= thisTypedArray->length());
JS_ASSERT(len <= thisTypedArray->length() - offset);
if (source->is<TypedArrayObject>())
return copyFromTypedArray(cx, thisTypedArray, source, offset);
uint32_t i = 0;
if (source->isNative()) {
// Attempt fast-path infallible conversion of dense elements up to
// the first potentially side-effectful lookup or conversion.
uint32_t bound = Min(source->getDenseInitializedLength(), len);
NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
const Value *srcValues = source->getDenseElements();
for (; i < bound; i++) {
// Note: holes don't convert infallibly.
if (!canConvertInfallibly(srcValues[i]))
break;
dest[i] = infallibleValueToNative(srcValues[i]);
}
if (i == len)
return true;
}
// Convert and copy any remaining elements generically.
RootedValue v(cx);
for (; i < len; i++) {
if (!JSObject::getElement(cx, source, source, i, &v))
return false;
NativeType n;
if (!valueToNative(cx, v, &n))
return false;
len = Min(len, thisTypedArray->length());
if (i >= len)
break;
// Compute every iteration in case getElement acts wacky.
void *data = thisTypedArray->viewData();
static_cast<NativeType*>(data)[offset + i] = n;
}
return true;
}
static bool
copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj,
uint32_t offset)
{
Rooted<ArrayType *> thisTypedArray(cx, &thisTypedArrayObj->as<ArrayType>());
Rooted<ArrayType *> tarray(cx, &tarrayObj->as<ArrayType>());
JS_ASSERT(offset <= thisTypedArray->length());
JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset);
// Object equality is not good enough for SharedArrayBufferObject.
if (Adapter::sameBuffer(tarray->buffer(), thisTypedArray->buffer()))
return copyFromWithOverlap(cx, thisTypedArray, tarray, offset);
NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
if (tarray->type() == thisTypedArray->type()) {
js_memcpy(dest, tarray->viewData(), tarray->byteLength());
return true;
}
unsigned srclen = tarray->length();
switch (tarray->type()) {
case Scalar::Int8: {
int8_t *src = static_cast<int8_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t *src = static_cast<uint8_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int16: {
int16_t *src = static_cast<int16_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint16: {
uint16_t *src = static_cast<uint16_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int32: {
int32_t *src = static_cast<int32_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint32: {
uint32_t *src = static_cast<uint32_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float32: {
float *src = static_cast<float*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float64: {
double *src = static_cast<double*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
default:
MOZ_CRASH("copyFrom with a TypedArrayObject of unknown type");
}
return true;
}
static bool
copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset)
{
ArrayType *self = &selfObj->as<ArrayType>();
ArrayType *tarray = &tarrayObj->as<ArrayType>();
JS_ASSERT(offset <= self->length());
NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset;
uint32_t byteLength = tarray->byteLength();
if (tarray->type() == self->type()) {
memmove(dest, tarray->viewData(), byteLength);
return true;
}
// We have to make a copy of the source array here, since
// there's overlap, and we have to convert types.
uint8_t *srcbuf = self->zone()->template pod_malloc<uint8_t>(byteLength);
if (!srcbuf)
return false;
js_memcpy(srcbuf, tarray->viewData(), byteLength);
uint32_t len = tarray->length();
switch (tarray->type()) {
case Scalar::Int8: {
int8_t *src = (int8_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t *src = (uint8_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int16: {
int16_t *src = (int16_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint16: {
uint16_t *src = (uint16_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int32: {
int32_t *src = (int32_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint32: {
uint32_t *src = (uint32_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float32: {
float *src = (float*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float64: {
double *src = (double*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
default:
MOZ_CRASH("copyFromWithOverlap with a TypedArrayObject of unknown type");
}
js_free(srcbuf);
return true;
}
static bool
canConvertInfallibly(const Value &v)
{
return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
}
static NativeType
infallibleValueToNative(const Value &v)
{
if (v.isInt32())
return NativeType(v.toInt32());
if (v.isDouble())
return doubleToNative(v.toDouble());
if (v.isBoolean())
return NativeType(v.toBoolean());
if (v.isNull())
return NativeType(0);
MOZ_ASSERT(v.isUndefined());
return TypeIsFloatingPoint<NativeType>() ? NativeType(JS::GenericNaN()) : NativeType(0);
}
static bool
valueToNative(JSContext *cx, const Value &v, NativeType *result)
{
MOZ_ASSERT(!v.isMagic());
if (MOZ_LIKELY(canConvertInfallibly(v))) {
*result = infallibleValueToNative(v);
return true;
}
double d;
MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol());
if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
return false;
*result = doubleToNative(d);
return true;
}
static NativeType
doubleToNative(double d)
{
if (TypeIsFloatingPoint<NativeType>()) {
#ifdef JS_MORE_DETERMINISTIC
// The JS spec doesn't distinguish among different NaN values, and
// it deliberately doesn't specify the bit pattern written to a
// typed array when NaN is written into it. This bit-pattern
// inconsistency could confuse deterministic testing, so always
// canonicalize NaN values in more-deterministic builds.
d = JS::CanonicalizeNaN(d);
#endif
return NativeType(d);
}
if (MOZ_UNLIKELY(mozilla::IsNaN(d)))
return NativeType(0);
if (TypeIsUnsigned<NativeType>())
return NativeType(ToUint32(d));
return NativeType(ToInt32(d));
}
};
} // namespace js
#endif // vm_TypedArrayCommon_h

View File

@ -28,13 +28,14 @@
#endif
#include "jswrapper.h"
#include "builtin/TypedObjectConstants.h"
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "vm/ArrayBufferObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/NumericConversions.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayCommon.h"
#include "vm/WrapperObject.h"
#include "jsatominlines.h"
@ -47,7 +48,6 @@ using namespace js;
using namespace js::gc;
using namespace js::types;
using mozilla::DebugOnly;
using mozilla::IsNaN;
using mozilla::NegativeInfinity;
using mozilla::PodCopy;
@ -55,33 +55,6 @@ using mozilla::PositiveInfinity;
using JS::CanonicalizeNaN;
using JS::GenericNaN;
static bool
ValueIsLength(const Value &v, uint32_t *len)
{
if (v.isInt32()) {
int32_t i = v.toInt32();
if (i < 0)
return false;
*len = i;
return true;
}
if (v.isDouble()) {
double d = v.toDouble();
if (IsNaN(d))
return false;
uint32_t length = uint32_t(d);
if (d != double(length))
return false;
*len = length;
return true;
}
return false;
}
/*
* TypedArrayObject
*
@ -90,6 +63,32 @@ ValueIsLength(const Value &v, uint32_t *len)
* the subclasses.
*/
TypedArrayLayout TypedArrayObject::layout_(false, // shared
true, // neuterable
&TypedArrayObject::classes[0],
&TypedArrayObject::classes[Scalar::TypeMax]);
TypedArrayLayout::TypedArrayLayout(bool isShared, bool isNeuterable, const Class *firstClass,
const Class *maxClass)
: isShared_(isShared)
, isNeuterable_(isNeuterable)
, firstClass_(firstClass)
, maxClass_(maxClass)
{
}
/* static */ int
TypedArrayLayout::lengthOffset()
{
return JSObject::getFixedSlotOffset(LENGTH_SLOT);
}
/* static */ int
TypedArrayLayout::dataOffset()
{
return JSObject::getPrivateDataOffset(DATA_SLOT);
}
void
TypedArrayObject::neuter(void *newData)
{
@ -98,12 +97,6 @@ TypedArrayObject::neuter(void *newData)
setPrivate(newData);
}
ArrayBufferObject *
TypedArrayObject::sharedBuffer() const
{
return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>();
}
/* static */ bool
TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray)
{
@ -123,18 +116,6 @@ TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarr
return true;
}
/* static */ int
TypedArrayObject::lengthOffset()
{
return JSObject::getFixedSlotOffset(LENGTH_SLOT);
}
/* static */ int
TypedArrayObject::dataOffset()
{
return JSObject::getPrivateDataOffset(DATA_SLOT);
}
/* static */ void
TypedArrayObject::ObjectMoved(JSObject *obj, const JSObject *old)
{
@ -180,23 +161,19 @@ js::ClampDoubleToUint8(const double x)
return y;
}
template<typename NativeType> static inline Scalar::Type TypeIDOfType();
template<> inline Scalar::Type TypeIDOfType<int8_t>() { return Scalar::Int8; }
template<> inline Scalar::Type TypeIDOfType<uint8_t>() { return Scalar::Uint8; }
template<> inline Scalar::Type TypeIDOfType<int16_t>() { return Scalar::Int16; }
template<> inline Scalar::Type TypeIDOfType<uint16_t>() { return Scalar::Uint16; }
template<> inline Scalar::Type TypeIDOfType<int32_t>() { return Scalar::Int32; }
template<> inline Scalar::Type TypeIDOfType<uint32_t>() { return Scalar::Uint32; }
template<> inline Scalar::Type TypeIDOfType<float>() { return Scalar::Float32; }
template<> inline Scalar::Type TypeIDOfType<double>() { return Scalar::Float64; }
template<> inline Scalar::Type TypeIDOfType<uint8_clamped>() { return Scalar::Uint8Clamped; }
template<typename ElementType>
static inline JSObject *
NewArray(JSContext *cx, uint32_t nelements);
namespace {
// Note, this template can probably be merged in part with the one in
// SharedTypedArrayObject.cpp once our implementation of
// TypedArrayObject is closer to ES6: at the moment, our
// implementation does not process construction arguments in
// standards-compliant ways, at least, and a larger rewrite around the
// prototype hierarchy is also coming.
template<typename NativeType>
class TypedArrayObjectTemplate : public TypedArrayObject
{
@ -209,6 +186,23 @@ class TypedArrayObjectTemplate : public TypedArrayObject
static const size_t BYTES_PER_ELEMENT = sizeof(ThisType);
class TypedArrayObjectAdapter
{
public:
typedef TypedArrayObject TypedArrayObjectType;
typedef ArrayBufferObject ArrayBufferObjectType;
typedef NativeType ElementType;
typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObjectType;
static bool ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray) {
return TypedArrayObject::ensureHasBuffer(cx, tarray);
}
static bool sameBuffer(ArrayBufferObject *a, ArrayBufferObject *b) {
return a == b;
}
};
static inline const Class *protoClass()
{
return &TypedArrayObject::protoClasses[ArrayTypeID()];
@ -393,14 +387,15 @@ class TypedArrayObjectTemplate : public TypedArrayObject
/*
* (typedArray)
* (sharedTypedArray)
* (type[] array)
*
* Otherwise create a new typed array and copy elements 0..len-1
* properties from the object, treating it as some sort of array.
* Note that offset and length will be ignored
* Note that offset and length will be ignored. Note that a
* shared array's values are copied here.
*/
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() &&
!UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>())
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>())
{
return fromArray(cx, dataObj);
}
@ -496,127 +491,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
attrs);
}
/* subarray(start[, end]) */
static bool
fun_subarray_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsThisClass(args.thisv()));
Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
// these are the default values
uint32_t length = tarray->length();
uint32_t begin = 0, end = length;
if (args.length() > 0) {
if (!ToClampedIndex(cx, args[0], length, &begin))
return false;
if (args.length() > 1) {
if (!ToClampedIndex(cx, args[1], length, &end))
return false;
}
}
if (begin > end)
begin = end;
JSObject *nobj = createSubarray(cx, tarray, begin, end);
if (!nobj)
return false;
args.rval().setObject(*nobj);
return true;
}
static bool
fun_subarray(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
ThisTypedArrayObject::fun_subarray_impl>(cx, args);
}
/* copyWithin(target, start[, end]) */
// ES6 draft rev 26, 22.2.3.5
static bool
fun_copyWithin_impl(JSContext *cx, CallArgs args)
{
MOZ_ASSERT(IsThisClass(args.thisv()));
// Steps 1-2.
Rooted<TypedArrayObject*> obj(cx, &args.thisv().toObject().as<TypedArrayObject>());
// Steps 3-4.
uint32_t len = obj->length();
// Steps 6-8.
uint32_t to;
if (!ToClampedIndex(cx, args.get(0), len, &to))
return false;
// Steps 9-11.
uint32_t from;
if (!ToClampedIndex(cx, args.get(1), len, &from))
return false;
// Steps 12-14.
uint32_t final;
if (args.get(2).isUndefined()) {
final = len;
} else {
if (!ToClampedIndex(cx, args.get(2), len, &final))
return false;
}
// Steps 15-18.
// If |final - from < 0|, then |count| will be less than 0, so step 18
// never loops. Exit early so |count| can use a non-negative type.
// Also exit early if elements are being moved to their pre-existing
// location.
if (final < from || to == from) {
args.rval().setObject(*obj);
return true;
}
uint32_t count = Min(final - from, len - to);
uint32_t lengthDuringMove = obj->length(); // beware ToClampedIndex
MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
MOZ_ASSERT(from <= INT32_MAX, "size limited to 2**31");
MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
if (from + count > lengthDuringMove || to + count > lengthDuringMove) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
DebugOnly<const size_t> ElementSize = sizeof(NativeType);
MOZ_ASSERT(to <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
MOZ_ASSERT(from <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
MOZ_ASSERT(count <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
uint32_t byteDest = to * sizeof(NativeType);
uint32_t byteSrc = from * sizeof(NativeType);
uint32_t byteSize = count * sizeof(NativeType);
#ifdef DEBUG
uint32_t viewByteLength = obj->byteLength();
JS_ASSERT(byteDest <= viewByteLength);
JS_ASSERT(byteSrc <= viewByteLength);
JS_ASSERT(byteDest + byteSize <= viewByteLength);
JS_ASSERT(byteSrc + byteSize <= viewByteLength);
// Should not overflow because size is limited to 2^31
JS_ASSERT(byteDest + byteSize >= byteDest);
JS_ASSERT(byteSrc + byteSize >= byteSrc);
#endif
uint8_t *data = static_cast<uint8_t*>(obj->viewData());
mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize);
// Step 19.
args.rval().set(args.thisv());
return true;
TypedArraySubarrayTemplate<TypedArrayObjectAdapter>::fun_subarray_impl>(cx, args);
}
static bool
@ -624,65 +504,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
ThisTypedArrayObject::fun_copyWithin_impl>(cx, args);
}
/* set(array[, offset]) */
static bool
fun_set_impl(JSContext *cx, CallArgs args)
{
JS_ASSERT(IsThisClass(args.thisv()));
Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
// first arg must be either a typed array or a JS array
if (args.length() == 0 || !args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
int32_t offset = 0;
if (args.length() > 1) {
if (!ToInt32(cx, args[1], &offset))
return false;
if (offset < 0 || uint32_t(offset) > tarray->length()) {
// the given offset is bogus
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
return false;
}
}
if (!args[0].isObject()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
return false;
}
RootedObject arg0(cx, args[0].toObjectOrNull());
if (arg0->is<TypedArrayObject>()) {
if (arg0->as<TypedArrayObject>().length() > tarray->length() - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
if (!copyFromTypedArray(cx, tarray, arg0, offset))
return false;
} else {
uint32_t len;
if (!GetLengthProperty(cx, arg0, &len))
return false;
if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
return false;
}
if (!copyFromArray(cx, tarray, arg0, len, offset))
return false;
}
args.rval().setUndefined();
return true;
TypedArrayCopyWithinTemplate<TypedArrayObjectAdapter>::fun_copyWithin_impl>(cx, args);
}
static bool
@ -690,7 +512,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
ThisTypedArrayObject::fun_set_impl>(cx, args);
TypedArraySetTemplate<TypedArrayObjectAdapter>::fun_set_impl>(cx, args);
}
public:
@ -847,7 +669,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
return nullptr;
RootedObject obj(cx, makeInstance(cx, buffer, 0, len));
if (!obj || !copyFromArray(cx, obj, other, len))
if (!obj || !TypedArraySetTemplate<TypedArrayObjectAdapter>::copyFromArray(cx, obj, other, len))
return nullptr;
return obj;
}
@ -868,304 +690,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject
}
static Value getIndexValue(JSObject *tarray, uint32_t index);
static JSObject *
createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end)
{
Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>());
if (begin > tarray->length() || end > tarray->length() || begin > end) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
return nullptr;
}
if (!ensureHasBuffer(cx, tarray))
return nullptr;
Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer());
JS_ASSERT(bufobj);
uint32_t length = end - begin;
JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType));
uint32_t arrayByteOffset = tarray->byteOffset();
JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset);
uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType);
return makeInstance(cx, bufobj, byteOffset, length);
}
protected:
static NativeType
doubleToNative(double d)
{
if (TypeIsFloatingPoint<NativeType>()) {
#ifdef JS_MORE_DETERMINISTIC
// The JS spec doesn't distinguish among different NaN values, and
// it deliberately doesn't specify the bit pattern written to a
// typed array when NaN is written into it. This bit-pattern
// inconsistency could confuse deterministic testing, so always
// canonicalize NaN values in more-deterministic builds.
d = CanonicalizeNaN(d);
#endif
return NativeType(d);
}
if (MOZ_UNLIKELY(IsNaN(d)))
return NativeType(0);
if (TypeIsUnsigned<NativeType>())
return NativeType(ToUint32(d));
return NativeType(ToInt32(d));
}
static bool
canConvertInfallibly(const Value &v)
{
return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined();
}
static NativeType
infallibleValueToNative(const Value &v)
{
if (v.isInt32())
return NativeType(v.toInt32());
if (v.isDouble())
return doubleToNative(v.toDouble());
if (v.isBoolean())
return NativeType(v.toBoolean());
if (v.isNull())
return NativeType(0);
MOZ_ASSERT(v.isUndefined());
return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0);
}
static bool
valueToNative(JSContext *cx, const Value &v, NativeType *result)
{
MOZ_ASSERT(!v.isMagic());
if (MOZ_LIKELY(canConvertInfallibly(v))) {
*result = infallibleValueToNative(v);
return true;
}
double d;
MOZ_ASSERT(v.isString() || v.isObject() || v.isSymbol());
if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d)))
return false;
*result = doubleToNative(d);
return true;
}
static bool
copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj,
HandleObject source, uint32_t len, uint32_t offset = 0)
{
Rooted<TypedArrayObject*> thisTypedArray(cx, &thisTypedArrayObj->as<TypedArrayObject>());
JS_ASSERT(offset <= thisTypedArray->length());
JS_ASSERT(len <= thisTypedArray->length() - offset);
if (source->is<TypedArrayObject>())
return copyFromTypedArray(cx, thisTypedArray, source, offset);
uint32_t i = 0;
if (source->isNative()) {
// Attempt fast-path infallible conversion of dense elements up to
// the first potentially side-effectful lookup or conversion.
uint32_t bound = Min(source->getDenseInitializedLength(), len);
NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
const Value *srcValues = source->getDenseElements();
for (; i < bound; i++) {
// Note: holes don't convert infallibly.
if (!canConvertInfallibly(srcValues[i]))
break;
dest[i] = infallibleValueToNative(srcValues[i]);
}
if (i == len)
return true;
}
// Convert and copy any remaining elements generically.
RootedValue v(cx);
for (; i < len; i++) {
if (!JSObject::getElement(cx, source, source, i, &v))
return false;
NativeType n;
if (!valueToNative(cx, v, &n))
return false;
len = Min(len, thisTypedArray->length());
if (i >= len)
break;
// Compute every iteration in case getElement acts wacky.
void *data = thisTypedArray->viewData();
static_cast<NativeType*>(data)[offset + i] = n;
}
return true;
}
static bool
copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj,
uint32_t offset)
{
TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as<TypedArrayObject>();
TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
JS_ASSERT(offset <= thisTypedArray->length());
JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset);
if (tarray->buffer() == thisTypedArray->buffer())
return copyFromWithOverlap(cx, thisTypedArray, tarray, offset);
NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset;
if (tarray->type() == thisTypedArray->type()) {
js_memcpy(dest, tarray->viewData(), tarray->byteLength());
return true;
}
unsigned srclen = tarray->length();
switch (tarray->type()) {
case Scalar::Int8: {
int8_t *src = static_cast<int8_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t *src = static_cast<uint8_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int16: {
int16_t *src = static_cast<int16_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint16: {
uint16_t *src = static_cast<uint16_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int32: {
int32_t *src = static_cast<int32_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint32: {
uint32_t *src = static_cast<uint32_t*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float32: {
float *src = static_cast<float*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float64: {
double *src = static_cast<double*>(tarray->viewData());
for (unsigned i = 0; i < srclen; ++i)
*dest++ = NativeType(*src++);
break;
}
default:
MOZ_CRASH("copyFrom with a TypedArrayObject of unknown type");
}
return true;
}
static bool
copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset)
{
TypedArrayObject *self = &selfObj->as<TypedArrayObject>();
TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>();
JS_ASSERT(offset <= self->length());
NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset;
uint32_t byteLength = tarray->byteLength();
if (tarray->type() == self->type()) {
memmove(dest, tarray->viewData(), byteLength);
return true;
}
// We have to make a copy of the source array here, since
// there's overlap, and we have to convert types.
uint8_t *srcbuf = self->zone()->pod_malloc<uint8_t>(byteLength);
if (!srcbuf)
return false;
js_memcpy(srcbuf, tarray->viewData(), byteLength);
uint32_t len = tarray->length();
switch (tarray->type()) {
case Scalar::Int8: {
int8_t *src = (int8_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t *src = (uint8_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int16: {
int16_t *src = (int16_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint16: {
uint16_t *src = (uint16_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Int32: {
int32_t *src = (int32_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Uint32: {
uint32_t *src = (uint32_t*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float32: {
float *src = (float*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
case Scalar::Float64: {
double *src = (double*) srcbuf;
for (unsigned i = 0; i < len; ++i)
*dest++ = NativeType(*src++);
break;
}
default:
MOZ_CRASH("copyFromWithOverlap with a TypedArrayObject of unknown type");
}
js_free(srcbuf);
return true;
}
};
class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> {
@ -2552,23 +2076,6 @@ js::IsTypedArrayConstructor(HandleValue v, uint32_t type)
MOZ_CRASH("unexpected typed array type");
}
bool
js::IsTypedArrayBuffer(HandleValue v)
{
return v.isObject() &&
(v.toObject().is<ArrayBufferObject>() ||
v.toObject().is<SharedArrayBufferObject>());
}
ArrayBufferObject &
js::AsTypedArrayBuffer(HandleValue v)
{
JS_ASSERT(IsTypedArrayBuffer(v));
if (v.toObject().is<ArrayBufferObject>())
return v.toObject().as<ArrayBufferObject>();
return v.toObject().as<SharedArrayBufferObject>();
}
template <typename CharT>
bool
js::StringIsTypedArrayIndex(const CharT *s, size_t length, uint64_t *indexp)

View File

@ -25,14 +25,64 @@ namespace js {
* the subclasses.
*/
/*
* TypedArrayObject and SharedTypedArrayObject are unrelated types in
* both C++ and JS, and that is deliberate to avoid one substituting
* for the other. However, they share a fixed representation and have
* some variable attributes, all of which are encapsulated in the
* TypedArrayLayout class. The sharing avoids a lot of pointless
* duplication in the JITs: one code path can be used, with occasional
* decision points based on the attributes.
*/
class TypedArrayLayout
{
const bool isShared_;
const bool isNeuterable_;
const Class *firstClass_;
const Class *maxClass_;
public:
TypedArrayLayout(bool isShared, bool isNeuterable, const Class *firstClass, const Class *maxClass);
// Slot containing length of the view in number of typed elements.
static const size_t LENGTH_SLOT = JS_BUFVIEW_SLOT_LENGTH;
// The type value.
static const size_t TYPE_SLOT = JS_TYPEDARR_SLOT_TYPE;
// Underlying (Shared)ArrayBufferObject.
static const size_t BUFFER_SLOT = JS_BUFVIEW_SLOT_OWNER;
// Offset of view within underlying (Shared)ArrayBufferObject.
static const size_t BYTEOFFSET_SLOT = JS_BUFVIEW_SLOT_BYTEOFFSET;
static const size_t RESERVED_SLOTS = JS_TYPEDARR_SLOTS;
// The raw pointer to the buffer memory, the "private" value.
//
// This offset is exposed for performance reasons - so that it
// need not be looked up on accesses.
static const size_t DATA_SLOT = JS_TYPEDARR_SLOT_DATA;
static int lengthOffset();
static int dataOffset();
bool isSharedMemory() const { return isShared_; }
bool isNeuterable() const { return isNeuterable_; }
const Class *addressOfFirstClass() const { return firstClass_; }
const Class *addressOfMaxClass() const { return maxClass_; }
};
class TypedArrayObject : public ArrayBufferViewObject
{
protected:
// Typed array properties stored in slots, beyond those shared by all
// ArrayBufferViews.
static const size_t TYPE_SLOT = JS_TYPEDARR_SLOT_TYPE;
static const size_t RESERVED_SLOTS = JS_TYPEDARR_SLOTS;
static const size_t DATA_SLOT = JS_TYPEDARR_SLOT_DATA;
static const size_t TYPE_SLOT = TypedArrayLayout::TYPE_SLOT;
static const size_t DATA_SLOT = TypedArrayLayout::DATA_SLOT;
static const size_t RESERVED_SLOTS = TypedArrayLayout::RESERVED_SLOTS;
static_assert(js::detail::TypedArrayLengthSlot == LENGTH_SLOT,
"bad inlined constant in jsfriendapi.h");
@ -79,7 +129,6 @@ class TypedArrayObject : public ArrayBufferViewObject
static bool
ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray);
ArrayBufferObject *sharedBuffer() const;
bool hasBuffer() const {
return bufferValue(const_cast<TypedArrayObject*>(this)).isObject();
}
@ -87,9 +136,7 @@ class TypedArrayObject : public ArrayBufferViewObject
JSObject *obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
if (!obj)
return nullptr;
if (obj->is<ArrayBufferObject>())
return &obj->as<ArrayBufferObject>();
return sharedBuffer();
return &obj->as<ArrayBufferObject>();
}
uint32_t byteOffset() const {
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
@ -121,11 +168,16 @@ class TypedArrayObject : public ArrayBufferViewObject
*/
static const uint32_t SINGLETON_TYPE_BYTE_LENGTH = 1024 * 1024 * 10;
static int lengthOffset();
static int dataOffset();
static bool isOriginalLengthGetter(Scalar::Type type, Native native);
private:
static TypedArrayLayout layout_;
public:
static const TypedArrayLayout &layout() {
return layout_;
}
static void ObjectMoved(JSObject *obj, const JSObject *old);
};
@ -146,12 +198,6 @@ IsTypedArrayProtoClass(const Class *clasp)
bool
IsTypedArrayConstructor(HandleValue v, uint32_t type);
bool
IsTypedArrayBuffer(HandleValue v);
ArrayBufferObject &
AsTypedArrayBuffer(HandleValue v);
// Return value is whether the string is some integer. If the string is an
// integer which is not representable as a uint64_t, the return value is true
// and the resulting index is UINT64_MAX.