mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-13 03:24:26 +00:00
Bug 1054882 - Rollup patch. r=till, r=sstangl, r=jorendorff
This commit is contained in:
parent
faf0f8344b
commit
ef7ed4ebde
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_;
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
29
js/src/jit-test/tests/sharedbuf/inline-access.js
Normal file
29
js/src/jit-test/tests/sharedbuf/inline-access.js
Normal 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);
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include "jit/BaselineRegisters.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
class TypedArrayLayout;
|
||||
|
||||
namespace jit {
|
||||
|
||||
//
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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))
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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 ||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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>();
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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',
|
||||
|
240
js/src/tests/js1_8_5/extensions/sharedtypedarray.js
Normal file
240
js/src/tests/js1_8_5/extensions/sharedtypedarray.js
Normal 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');
|
@ -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).
|
||||
|
46
js/src/vm/ArrayBufferObject-inl.h
Normal file
46
js/src/vm/ArrayBufferObject-inl.h
Normal 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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
1027
js/src/vm/SharedTypedArrayObject.cpp
Normal file
1027
js/src/vm/SharedTypedArrayObject.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
js/src/vm/SharedTypedArrayObject.h
Normal file
122
js/src/vm/SharedTypedArrayObject.h
Normal 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
|
@ -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());
|
||||
|
657
js/src/vm/TypedArrayCommon.h
Normal file
657
js/src/vm/TypedArrayCommon.h
Normal 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
|
@ -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)
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user