diff --git a/BUILD.gn b/BUILD.gn index edd8bc6c01..1b7e4d5f98 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -276,7 +276,8 @@ action("js2c_experimental") { "src/harmony-regexp.js", "src/harmony-reflect.js", "src/harmony-spread.js", - "src/harmony-object.js" + "src/harmony-object.js", + "src/harmony-sharedarraybuffer.js" ] outputs = [ diff --git a/include/v8.h b/include/v8.h index 483699cbda..39064c2020 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1920,6 +1920,13 @@ class V8_EXPORT Value : public Data { */ bool IsDataView() const; + /** + * Returns true if this value is a SharedArrayBuffer. + * This is an experimental feature. + */ + bool IsSharedArrayBuffer() const; + + V8_WARN_UNUSED_RESULT MaybeLocal ToBoolean( Local context) const; V8_WARN_UNUSED_RESULT MaybeLocal ToNumber( @@ -3344,7 +3351,7 @@ class V8_EXPORT ArrayBuffer : public Object { ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized); /** - * Returns true if ArrayBuffer is extrenalized, that is, does not + * Returns true if ArrayBuffer is externalized, that is, does not * own its memory block. */ bool IsExternal() const; @@ -3630,6 +3637,105 @@ class V8_EXPORT DataView : public ArrayBufferView { }; +/** + * An instance of the built-in SharedArrayBuffer constructor. + * This API is experimental and may change significantly. + */ +class V8_EXPORT SharedArrayBuffer : public Object { + public: + /** + * The contents of an |SharedArrayBuffer|. Externalization of + * |SharedArrayBuffer| returns an instance of this class, populated, with a + * pointer to data and byte length. + * + * The Data pointer of SharedArrayBuffer::Contents is always allocated with + * |ArrayBuffer::Allocator::Allocate| by the allocator specified in + * v8::Isolate::CreateParams::array_buffer_allocator. + * + * This API is experimental and may change significantly. + */ + class V8_EXPORT Contents { // NOLINT + public: + Contents() : data_(NULL), byte_length_(0) {} + + void* Data() const { return data_; } + size_t ByteLength() const { return byte_length_; } + + private: + void* data_; + size_t byte_length_; + + friend class SharedArrayBuffer; + }; + + + /** + * Data length in bytes. + */ + size_t ByteLength() const; + + /** + * Create a new SharedArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created SharedArrayBuffer and + * will be deallocated when it is garbage-collected, + * unless the object is externalized. + */ + static Local New(Isolate* isolate, size_t byte_length); + + /** + * Create a new SharedArrayBuffer over an existing memory block. The created + * array buffer is immediately in externalized state unless otherwise + * specified. The memory block will not be reclaimed when a created + * SharedArrayBuffer is garbage-collected. + */ + static Local New( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized); + + /** + * Returns true if SharedArrayBuffer is externalized, that is, does not + * own its memory block. + */ + bool IsExternal() const; + + /** + * Make this SharedArrayBuffer external. The pointer to underlying memory + * block and byte length are returned as |Contents| structure. After + * SharedArrayBuffer had been etxrenalized, it does no longer owns the memory + * block. The caller should take steps to free memory when it is no longer + * needed. + * + * The memory block is guaranteed to be allocated with |Allocator::Allocate| + * by the allocator specified in + * v8::Isolate::CreateParams::array_buffer_allocator. + * + */ + Contents Externalize(); + + /** + * Get a pointer to the ArrayBuffer's underlying memory block without + * externalizing it. If the ArrayBuffer is not externalized, this pointer + * will become invalid as soon as the ArrayBuffer became garbage collected. + * + * The embedder should make sure to hold a strong reference to the + * ArrayBuffer while accessing this pointer. + * + * The memory block is guaranteed to be allocated with |Allocator::Allocate| + * by the allocator specified in + * v8::Isolate::CreateParams::array_buffer_allocator. + */ + Contents GetContents(); + + V8_INLINE static SharedArrayBuffer* Cast(Value* obj); + + static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + + private: + SharedArrayBuffer(); + static void CheckCast(Value* obj); +}; + + /** * An instance of the built-in Date constructor (ECMA-262, 15.9). */ @@ -7817,6 +7923,14 @@ DataView* DataView::Cast(v8::Value* value) { } +SharedArrayBuffer* SharedArrayBuffer::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + + Function* Function::Cast(v8::Value* value) { #ifdef V8_ENABLE_CHECKS CheckCast(value); diff --git a/src/api.cc b/src/api.cc index c7126d742c..036659ee56 100644 --- a/src/api.cc +++ b/src/api.cc @@ -2679,7 +2679,8 @@ bool Value::IsArray() const { bool Value::IsArrayBuffer() const { - return Utils::OpenHandle(this)->IsJSArrayBuffer(); + i::Handle obj = Utils::OpenHandle(this); + return obj->IsJSArrayBuffer() && !i::JSArrayBuffer::cast(*obj)->is_shared(); } @@ -2700,6 +2701,7 @@ bool Value::IsTypedArray() const { i::JSTypedArray::cast(*obj)->type() == i::kExternal##Type##Array; \ } + TYPED_ARRAYS(VALUE_IS_TYPED_ARRAY) #undef VALUE_IS_TYPED_ARRAY @@ -2710,6 +2712,12 @@ bool Value::IsDataView() const { } +bool Value::IsSharedArrayBuffer() const { + i::Handle obj = Utils::OpenHandle(this); + return obj->IsJSArrayBuffer() && i::JSArrayBuffer::cast(*obj)->is_shared(); +} + + bool Value::IsObject() const { return Utils::OpenHandle(this)->IsJSObject(); } @@ -3086,9 +3094,9 @@ void v8::Promise::Resolver::CheckCast(Value* that) { void v8::ArrayBuffer::CheckCast(Value* that) { i::Handle obj = Utils::OpenHandle(that); - Utils::ApiCheck(obj->IsJSArrayBuffer(), - "v8::ArrayBuffer::Cast()", - "Could not convert to ArrayBuffer"); + Utils::ApiCheck( + obj->IsJSArrayBuffer() && !i::JSArrayBuffer::cast(*obj)->is_shared(), + "v8::ArrayBuffer::Cast()", "Could not convert to ArrayBuffer"); } @@ -3131,6 +3139,15 @@ void v8::DataView::CheckCast(Value* that) { } +void v8::SharedArrayBuffer::CheckCast(Value* that) { + i::Handle obj = Utils::OpenHandle(that); + Utils::ApiCheck( + obj->IsJSArrayBuffer() && i::JSArrayBuffer::cast(*obj)->is_shared(), + "v8::SharedArrayBuffer::Cast()", + "Could not convert to SharedArrayBuffer"); +} + + void v8::Date::CheckCast(v8::Value* that) { i::Handle obj = Utils::OpenHandle(that); i::Isolate* isolate = NULL; @@ -6305,7 +6322,7 @@ Local v8::ArrayBuffer::New(Isolate* isolate, size_t byte_length) { LOG_API(i_isolate, "v8::ArrayBuffer::New(size_t)"); ENTER_V8(i_isolate); i::Handle obj = - i_isolate->factory()->NewJSArrayBuffer(); + i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length); return Utils::ToLocal(obj); } @@ -6318,7 +6335,7 @@ Local v8::ArrayBuffer::New(Isolate* isolate, void* data, LOG_API(i_isolate, "v8::ArrayBuffer::New(void*, size_t)"); ENTER_V8(i_isolate); i::Handle obj = - i_isolate->factory()->NewJSArrayBuffer(); + i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kNotShared); i::Runtime::SetupArrayBuffer(i_isolate, obj, mode == ArrayBufferCreationMode::kExternalized, data, byte_length); @@ -6424,6 +6441,66 @@ Local DataView::New(Handle array_buffer, } +bool v8::SharedArrayBuffer::IsExternal() const { + return Utils::OpenHandle(this)->is_external(); +} + + +v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::Externalize() { + i::Handle self = Utils::OpenHandle(this); + i::Isolate* isolate = self->GetIsolate(); + Utils::ApiCheck(!self->is_external(), "v8::SharedArrayBuffer::Externalize", + "SharedArrayBuffer already externalized"); + self->set_is_external(true); + isolate->heap()->UnregisterArrayBuffer(self->backing_store()); + return GetContents(); +} + + +v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::GetContents() { + i::Handle self = Utils::OpenHandle(this); + size_t byte_length = static_cast(self->byte_length()->Number()); + Contents contents; + contents.data_ = self->backing_store(); + contents.byte_length_ = byte_length; + return contents; +} + + +size_t v8::SharedArrayBuffer::ByteLength() const { + i::Handle obj = Utils::OpenHandle(this); + return static_cast(obj->byte_length()->Number()); +} + + +Local v8::SharedArrayBuffer::New(Isolate* isolate, + size_t byte_length) { + i::Isolate* i_isolate = reinterpret_cast(isolate); + LOG_API(i_isolate, "v8::SharedArrayBuffer::New(size_t)"); + ENTER_V8(i_isolate); + i::Handle obj = + i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kShared); + i::Runtime::SetupArrayBufferAllocatingData(i_isolate, obj, byte_length, true, + i::SharedFlag::kShared); + return Utils::ToLocalShared(obj); +} + + +Local v8::SharedArrayBuffer::New( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode) { + i::Isolate* i_isolate = reinterpret_cast(isolate); + LOG_API(i_isolate, "v8::SharedArrayBuffer::New(void*, size_t)"); + ENTER_V8(i_isolate); + i::Handle obj = + i_isolate->factory()->NewJSArrayBuffer(i::SharedFlag::kShared); + i::Runtime::SetupArrayBuffer(i_isolate, obj, + mode == ArrayBufferCreationMode::kExternalized, + data, byte_length, i::SharedFlag::kShared); + return Utils::ToLocalShared(obj); +} + + Local v8::Symbol::New(Isolate* isolate, Local name) { i::Isolate* i_isolate = reinterpret_cast(isolate); LOG_API(i_isolate, "Symbol::New()"); diff --git a/src/api.h b/src/api.h index 38bb920036..886b903d35 100644 --- a/src/api.h +++ b/src/api.h @@ -159,6 +159,7 @@ class RegisteredExtension { V(Float32Array, JSTypedArray) \ V(Float64Array, JSTypedArray) \ V(DataView, JSDataView) \ + V(SharedArrayBuffer, JSArrayBuffer) \ V(Name, Name) \ V(String, String) \ V(Symbol, Symbol) \ @@ -230,6 +231,9 @@ class Utils { static inline Local ToLocalFloat64Array( v8::internal::Handle obj); + static inline Local ToLocalShared( + v8::internal::Handle obj); + static inline Local MessageToLocal( v8::internal::Handle obj); static inline Local PromiseToLocal( @@ -360,6 +364,7 @@ MAKE_TO_LOCAL(ToLocal, JSArrayBuffer, ArrayBuffer) MAKE_TO_LOCAL(ToLocal, JSArrayBufferView, ArrayBufferView) MAKE_TO_LOCAL(ToLocal, JSDataView, DataView) MAKE_TO_LOCAL(ToLocal, JSTypedArray, TypedArray) +MAKE_TO_LOCAL(ToLocalShared, JSArrayBuffer, SharedArrayBuffer) TYPED_ARRAYS(MAKE_TO_LOCAL_TYPED_ARRAY) diff --git a/src/arraybuffer.js b/src/arraybuffer.js index eebf2f7bc9..99611f6376 100644 --- a/src/arraybuffer.js +++ b/src/arraybuffer.js @@ -28,7 +28,7 @@ utils.Import(function(from) { function ArrayBufferConstructor(length) { // length = 1 if (%_IsConstructCall()) { var byteLength = $toPositiveInteger(length, kInvalidArrayBufferLength); - %ArrayBufferInitialize(this, byteLength); + %ArrayBufferInitialize(this, byteLength, kNotShared); } else { throw MakeTypeError(kConstructorNotFunction, "ArrayBuffer"); } diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 786e42be47..143802a49d 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1753,6 +1753,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spreadcalls) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_destructuring) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_object) EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_spread_arrays) +EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_sharedarraybuffer) void Genesis::InstallNativeFunctions_harmony_proxies() { @@ -1846,6 +1847,20 @@ void Genesis::InitializeGlobal_harmony_tostring() { } +void Genesis::InitializeGlobal_harmony_sharedarraybuffer() { + if (!FLAG_harmony_sharedarraybuffer) return; + + Handle global( + JSGlobalObject::cast(native_context()->global_object())); + + Handle shared_array_buffer_fun = InstallFunction( + global, "SharedArrayBuffer", JS_ARRAY_BUFFER_TYPE, + JSArrayBuffer::kSizeWithInternalFields, + isolate()->initial_object_prototype(), Builtins::kIllegal); + native_context()->set_shared_array_buffer_fun(*shared_array_buffer_fun); +} + + Handle Genesis::InstallInternalArray(Handle target, const char* name, ElementsKind elements_kind) { @@ -2410,6 +2425,8 @@ bool Genesis::InstallExperimentalNatives() { static const char* harmony_object_natives[] = {"native harmony-object.js", NULL}; static const char* harmony_spread_arrays_natives[] = {nullptr}; + static const char* harmony_sharedarraybuffer_natives[] = { + "native harmony-sharedarraybuffer.js", NULL}; for (int i = ExperimentalNatives::GetDebuggerCount(); i < ExperimentalNatives::GetBuiltinsCount(); i++) { diff --git a/src/contexts.h b/src/contexts.h index d86ac419ce..106dd54a41 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -103,6 +103,7 @@ enum BindingFlags { V(TO_LENGTH_FUN_INDEX, JSFunction, to_length_fun) \ V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \ V(ARRAY_BUFFER_FUN_INDEX, JSFunction, array_buffer_fun) \ + V(SHARED_ARRAY_BUFFER_FUN_INDEX, JSFunction, shared_array_buffer_fun) \ V(ARRAY_BUFFER_MAP_INDEX, Map, array_buffer_map) \ V(UINT8_ARRAY_FUN_INDEX, JSFunction, uint8_array_fun) \ V(INT8_ARRAY_FUN_INDEX, JSFunction, int8_array_fun) \ @@ -381,6 +382,7 @@ class Context: public FixedArray { FLOAT64_ARRAY_EXTERNAL_MAP_INDEX, UINT8_CLAMPED_ARRAY_EXTERNAL_MAP_INDEX, DATA_VIEW_FUN_INDEX, + SHARED_ARRAY_BUFFER_FUN_INDEX, MESSAGE_LISTENERS_INDEX, MAKE_MESSAGE_FUN_INDEX, GET_STACK_TRACE_LINE_INDEX, diff --git a/src/factory.cc b/src/factory.cc index e25e6c2c35..59ab64dbe5 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1717,9 +1717,11 @@ Handle Factory::NewJSGeneratorObject( } -Handle Factory::NewJSArrayBuffer() { +Handle Factory::NewJSArrayBuffer(SharedFlag shared) { Handle array_buffer_fun( - isolate()->native_context()->array_buffer_fun()); + shared == SharedFlag::kShared + ? isolate()->native_context()->shared_array_buffer_fun() + : isolate()->native_context()->array_buffer_fun()); CALL_HEAP_FUNCTION( isolate(), isolate()->heap()->AllocateJSObject(*array_buffer_fun), @@ -1934,7 +1936,8 @@ Handle Factory::NewJSTypedArray(ElementsKind elements_kind, obj->set_length(*length_object); Handle buffer = isolate()->factory()->NewJSArrayBuffer(); - Runtime::SetupArrayBuffer(isolate(), buffer, true, NULL, byte_length); + Runtime::SetupArrayBuffer(isolate(), buffer, true, NULL, byte_length, + SharedFlag::kNotShared); obj->set_buffer(*buffer); Handle elements = isolate()->factory()->NewFixedTypedArray( diff --git a/src/factory.h b/src/factory.h index 8eaa216ee8..4702cc75be 100644 --- a/src/factory.h +++ b/src/factory.h @@ -444,7 +444,8 @@ class Factory final { Handle NewJSGeneratorObject(Handle function); - Handle NewJSArrayBuffer(); + Handle NewJSArrayBuffer( + SharedFlag shared = SharedFlag::kNotShared); Handle NewJSTypedArray(ExternalArrayType type); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 4232e39971..02be345fba 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -193,7 +193,8 @@ DEFINE_IMPLICATION(es_staging, harmony) V(harmony_unicode_regexps, "harmony unicode regexps") \ V(harmony_reflect, "harmony Reflect API") \ V(harmony_destructuring, "harmony destructuring") \ - V(harmony_spread_arrays, "harmony spread in array literals") + V(harmony_spread_arrays, "harmony spread in array literals") \ + V(harmony_sharedarraybuffer, "harmony sharedarraybuffer") // Features that are complete (but still behind --harmony/es-staging flag). #define HARMONY_STAGED(V) \ diff --git a/src/harmony-sharedarraybuffer.js b/src/harmony-sharedarraybuffer.js new file mode 100644 index 0000000000..9843e79a28 --- /dev/null +++ b/src/harmony-sharedarraybuffer.js @@ -0,0 +1,56 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +(function(global, shared, exports) { + +"use strict"; + +%CheckIsBootstrapping(); + +var GlobalSharedArrayBuffer = global.SharedArrayBuffer; +var GlobalObject = global.Object; + +// ------------------------------------------------------------------- + +function SharedArrayBufferConstructor(length) { // length = 1 + if (%_IsConstructCall()) { + var byteLength = $toPositiveInteger(length, kInvalidArrayBufferLength); + %ArrayBufferInitialize(this, byteLength, kShared); + } else { + throw MakeTypeError(kConstructorNotFunction, "SharedArrayBuffer"); + } +} + +function SharedArrayBufferGetByteLen() { + if (!IS_SHAREDARRAYBUFFER(this)) { + throw MakeTypeError(kIncompatibleMethodReceiver, + 'SharedArrayBuffer.prototype.byteLength', this); + } + return %_ArrayBufferGetByteLength(this); +} + +function SharedArrayBufferIsViewJS(obj) { + return %ArrayBufferIsView(obj); +} + + +// Set up the SharedArrayBuffer constructor function. +%SetCode(GlobalSharedArrayBuffer, SharedArrayBufferConstructor); +%FunctionSetPrototype(GlobalSharedArrayBuffer, new GlobalObject()); + +// Set up the constructor property on the SharedArrayBuffer prototype object. +%AddNamedProperty(GlobalSharedArrayBuffer.prototype, "constructor", + GlobalSharedArrayBuffer, DONT_ENUM); + +%AddNamedProperty(GlobalSharedArrayBuffer.prototype, + symbolToStringTag, "SharedArrayBuffer", DONT_ENUM | READ_ONLY); + +$installGetter(GlobalSharedArrayBuffer.prototype, "byteLength", + SharedArrayBufferGetByteLen); + +$installFunctions(GlobalSharedArrayBuffer, DONT_ENUM, [ + "isView", SharedArrayBufferIsViewJS +]); + +}) diff --git a/src/macros.py b/src/macros.py index 9c166f6a71..2b950bff57 100644 --- a/src/macros.py +++ b/src/macros.py @@ -112,6 +112,7 @@ macro IS_ARGUMENTS(arg) = (%_ClassOf(arg) === 'Arguments'); macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global'); macro IS_ARRAYBUFFER(arg) = (%_ClassOf(arg) === 'ArrayBuffer'); macro IS_DATAVIEW(arg) = (%_ClassOf(arg) === 'DataView'); +macro IS_SHAREDARRAYBUFFER(arg) = (%_ClassOf(arg) === 'SharedArrayBuffer'); macro IS_GENERATOR(arg) = (%_ClassOf(arg) === 'Generator'); macro IS_SET_ITERATOR(arg) = (%_ClassOf(arg) === 'Set Iterator'); macro IS_MAP_ITERATOR(arg) = (%_ClassOf(arg) === 'Map Iterator'); @@ -308,3 +309,7 @@ define NOT_FOUND = -1; define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0); macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function)); macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (DEBUG_IS_STEPPING(function)) %DebugPrepareStepInIfStepping(function); + +# SharedFlag equivalents +define kNotShared = false; +define kShared = true; diff --git a/src/objects-inl.h b/src/objects-inl.h index a5b6811cb5..91a8766637 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -6483,6 +6483,14 @@ void JSArrayBuffer::set_was_neutered(bool value) { } +bool JSArrayBuffer::is_shared() { return IsShared::decode(bit_field()); } + + +void JSArrayBuffer::set_is_shared(bool value) { + set_bit_field(IsShared::update(bit_field(), value)); +} + + Object* JSArrayBufferView::byte_offset() const { if (WasNeutered()) return Smi::FromInt(0); return Object::cast(READ_FIELD(this, kByteOffsetOffset)); diff --git a/src/objects.h b/src/objects.h index 5d0cc044ae..ce7f7853bb 100644 --- a/src/objects.h +++ b/src/objects.h @@ -10276,6 +10276,10 @@ class JSWeakSet: public JSWeakCollection { }; +// Whether a JSArrayBuffer is a SharedArrayBuffer or not. +enum class SharedFlag { kNotShared, kShared }; + + class JSArrayBuffer: public JSObject { public: // [backing_store]: backing memory for this array @@ -10296,6 +10300,9 @@ class JSArrayBuffer: public JSObject { inline bool was_neutered(); inline void set_was_neutered(bool value); + inline bool is_shared(); + inline void set_is_shared(bool value); + DECLARE_CAST(JSArrayBuffer) void Neuter(); @@ -10320,6 +10327,7 @@ class JSArrayBuffer: public JSObject { class IsExternal : public BitField {}; class IsNeuterable : public BitField {}; class WasNeutered : public BitField {}; + class IsShared : public BitField {}; private: DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBuffer); diff --git a/src/runtime/runtime-typedarray.cc b/src/runtime/runtime-typedarray.cc index ba676b54fd..a92a642bbc 100644 --- a/src/runtime/runtime-typedarray.cc +++ b/src/runtime/runtime-typedarray.cc @@ -16,7 +16,7 @@ namespace internal { void Runtime::SetupArrayBuffer(Isolate* isolate, Handle array_buffer, bool is_external, void* data, - size_t allocated_length) { + size_t allocated_length, SharedFlag shared) { DCHECK(array_buffer->GetInternalFieldCount() == v8::ArrayBuffer::kInternalFieldCount); for (int i = 0; i < v8::ArrayBuffer::kInternalFieldCount; i++) { @@ -25,7 +25,8 @@ void Runtime::SetupArrayBuffer(Isolate* isolate, array_buffer->set_backing_store(data); array_buffer->set_bit_field(0); array_buffer->set_is_external(is_external); - array_buffer->set_is_neuterable(true); + array_buffer->set_is_neuterable(shared == SharedFlag::kNotShared); + array_buffer->set_is_shared(shared == SharedFlag::kShared); Handle byte_length = isolate->factory()->NewNumberFromSize(allocated_length); @@ -41,7 +42,8 @@ void Runtime::SetupArrayBuffer(Isolate* isolate, bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate, Handle array_buffer, size_t allocated_length, - bool initialize) { + bool initialize, + SharedFlag shared) { void* data; CHECK(isolate->array_buffer_allocator() != NULL); // Prevent creating array buffers when serializing. @@ -58,7 +60,8 @@ bool Runtime::SetupArrayBufferAllocatingData(Isolate* isolate, data = NULL; } - SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length); + SetupArrayBuffer(isolate, array_buffer, false, data, allocated_length, + shared); return true; } @@ -70,9 +73,10 @@ void Runtime::NeuterArrayBuffer(Handle array_buffer) { RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) { HandleScope scope(isolate); - DCHECK(args.length() == 2); + DCHECK(args.length() == 3); CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, holder, 0); CONVERT_NUMBER_ARG_HANDLE_CHECKED(byteLength, 1); + CONVERT_BOOLEAN_ARG_CHECKED(is_shared, 2); if (!holder->byte_length()->IsUndefined()) { // ArrayBuffer is already initialized; probably a fuzz test. return *holder; @@ -82,8 +86,9 @@ RUNTIME_FUNCTION(Runtime_ArrayBufferInitialize) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength)); } - if (!Runtime::SetupArrayBufferAllocatingData(isolate, holder, - allocated_length)) { + if (!Runtime::SetupArrayBufferAllocatingData( + isolate, holder, allocated_length, true, + is_shared ? SharedFlag::kShared : SharedFlag::kNotShared)) { THROW_NEW_ERROR_RETURN_FAILURE( isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength)); } @@ -138,6 +143,8 @@ RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) { CHECK(Smi::FromInt(0) == array_buffer->byte_length()); return isolate->heap()->undefined_value(); } + // Shared array buffers should never be neutered. + DCHECK(!array_buffer->is_shared()); DCHECK(!array_buffer->is_external()); void* backing_store = array_buffer->backing_store(); size_t byte_length = NumberToSize(isolate, array_buffer->byte_length()); @@ -240,7 +247,8 @@ RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) { DCHECK(IsExternalArrayElementsKind(holder->map()->elements_kind())); } else { Handle buffer = isolate->factory()->NewJSArrayBuffer(); - Runtime::SetupArrayBuffer(isolate, buffer, true, NULL, byte_length); + Runtime::SetupArrayBuffer(isolate, buffer, true, NULL, byte_length, + SharedFlag::kNotShared); holder->set_buffer(*buffer); Handle elements = isolate->factory()->NewFixedTypedArray(static_cast(length), diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 192e342d9b..797f3d0793 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -633,7 +633,7 @@ namespace internal { #define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \ - F(ArrayBufferInitialize, 2, 1) \ + F(ArrayBufferInitialize, 3, 1) \ F(ArrayBufferGetByteLength, 1, 1) \ F(ArrayBufferSliceImpl, 3, 1) \ F(ArrayBufferIsView, 1, 1) \ @@ -815,12 +815,13 @@ class Runtime : public AllStatic { static void SetupArrayBuffer(Isolate* isolate, Handle array_buffer, bool is_external, void* data, - size_t allocated_length); + size_t allocated_length, + SharedFlag shared = SharedFlag::kNotShared); - static bool SetupArrayBufferAllocatingData(Isolate* isolate, - Handle array_buffer, - size_t allocated_length, - bool initialize = true); + static bool SetupArrayBufferAllocatingData( + Isolate* isolate, Handle array_buffer, + size_t allocated_length, bool initialize = true, + SharedFlag shared = SharedFlag::kNotShared); static void NeuterArrayBuffer(Handle array_buffer); diff --git a/src/typedarray.js b/src/typedarray.js index 28d073aa2e..7c08fce555 100644 --- a/src/typedarray.js +++ b/src/typedarray.js @@ -127,7 +127,7 @@ function NAMEConstructByArrayLike(obj, arrayLike) { function NAMEConstructor(arg1, arg2, arg3) { if (%_IsConstructCall()) { - if (IS_ARRAYBUFFER(arg1)) { + if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) { NAMEConstructByArrayBuffer(this, arg1, arg2, arg3); } else if (IS_NUMBER(arg1) || IS_STRING(arg1) || IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) { @@ -347,6 +347,7 @@ TYPED_ARRAYS(SETUP_TYPED_ARRAY) function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3 if (%_IsConstructCall()) { + // TODO(binji): support SharedArrayBuffers? if (!IS_ARRAYBUFFER(buffer)) throw MakeTypeError(kDataViewNotArrayBuffer); if (!IS_UNDEFINED(byteOffset)) { byteOffset = $toPositiveInteger(byteOffset, kInvalidDataViewOffset); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index e6c83849ae..f7f920769b 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2846,6 +2846,136 @@ THREADED_TEST(ArrayBuffer_NeuteringScript) { } +class ScopedSharedArrayBufferContents { + public: + explicit ScopedSharedArrayBufferContents( + const v8::SharedArrayBuffer::Contents& contents) + : contents_(contents) {} + ~ScopedSharedArrayBufferContents() { free(contents_.Data()); } + void* Data() const { return contents_.Data(); } + size_t ByteLength() const { return contents_.ByteLength(); } + + private: + const v8::SharedArrayBuffer::Contents contents_; +}; + + +THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) { + i::FLAG_harmony_sharedarraybuffer = true; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + Local ab = v8::SharedArrayBuffer::New(isolate, 1024); + CheckInternalFieldsAreZero(ab); + CHECK_EQ(1024, static_cast(ab->ByteLength())); + CHECK(!ab->IsExternal()); + CcTest::heap()->CollectAllGarbage(); + + ScopedSharedArrayBufferContents ab_contents(ab->Externalize()); + CHECK(ab->IsExternal()); + + CHECK_EQ(1024, static_cast(ab_contents.ByteLength())); + uint8_t* data = static_cast(ab_contents.Data()); + DCHECK(data != NULL); + env->Global()->Set(v8_str("ab"), ab); + + v8::Handle result = CompileRun("ab.byteLength"); + CHECK_EQ(1024, result->Int32Value()); + + result = CompileRun( + "var u8 = new Uint8Array(ab);" + "u8[0] = 0xFF;" + "u8[1] = 0xAA;" + "u8.length"); + CHECK_EQ(1024, result->Int32Value()); + CHECK_EQ(0xFF, data[0]); + CHECK_EQ(0xAA, data[1]); + data[0] = 0xCC; + data[1] = 0x11; + result = CompileRun("u8[0] + u8[1]"); + CHECK_EQ(0xDD, result->Int32Value()); +} + + +THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { + i::FLAG_harmony_sharedarraybuffer = true; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + + v8::Local result = CompileRun( + "var ab1 = new SharedArrayBuffer(2);" + "var u8_a = new Uint8Array(ab1);" + "u8_a[0] = 0xAA;" + "u8_a[1] = 0xFF; u8_a.buffer"); + Local ab1 = Local::Cast(result); + CheckInternalFieldsAreZero(ab1); + CHECK_EQ(2, static_cast(ab1->ByteLength())); + CHECK(!ab1->IsExternal()); + ScopedSharedArrayBufferContents ab1_contents(ab1->Externalize()); + CHECK(ab1->IsExternal()); + + result = CompileRun("ab1.byteLength"); + CHECK_EQ(2, result->Int32Value()); + result = CompileRun("u8_a[0]"); + CHECK_EQ(0xAA, result->Int32Value()); + result = CompileRun("u8_a[1]"); + CHECK_EQ(0xFF, result->Int32Value()); + result = CompileRun( + "var u8_b = new Uint8Array(ab1);" + "u8_b[0] = 0xBB;" + "u8_a[0]"); + CHECK_EQ(0xBB, result->Int32Value()); + result = CompileRun("u8_b[1]"); + CHECK_EQ(0xFF, result->Int32Value()); + + CHECK_EQ(2, static_cast(ab1_contents.ByteLength())); + uint8_t* ab1_data = static_cast(ab1_contents.Data()); + CHECK_EQ(0xBB, ab1_data[0]); + CHECK_EQ(0xFF, ab1_data[1]); + ab1_data[0] = 0xCC; + ab1_data[1] = 0x11; + result = CompileRun("u8_a[0] + u8_a[1]"); + CHECK_EQ(0xDD, result->Int32Value()); +} + + +THREADED_TEST(SharedArrayBuffer_External) { + i::FLAG_harmony_sharedarraybuffer = true; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + i::ScopedVector my_data(100); + memset(my_data.start(), 0, 100); + Local ab3 = + v8::SharedArrayBuffer::New(isolate, my_data.start(), 100); + CheckInternalFieldsAreZero(ab3); + CHECK_EQ(100, static_cast(ab3->ByteLength())); + CHECK(ab3->IsExternal()); + + env->Global()->Set(v8_str("ab3"), ab3); + + v8::Handle result = CompileRun("ab3.byteLength"); + CHECK_EQ(100, result->Int32Value()); + + result = CompileRun( + "var u8_b = new Uint8Array(ab3);" + "u8_b[0] = 0xBB;" + "u8_b[1] = 0xCC;" + "u8_b.length"); + CHECK_EQ(100, result->Int32Value()); + CHECK_EQ(0xBB, my_data[0]); + CHECK_EQ(0xCC, my_data[1]); + my_data[0] = 0xCC; + my_data[1] = 0x11; + result = CompileRun("u8_b[0] + u8_b[1]"); + CHECK_EQ(0xDD, result->Int32Value()); +} + + THREADED_TEST(HiddenProperties) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); diff --git a/test/mjsunit/harmony/sharedarraybuffer.js b/test/mjsunit/harmony/sharedarraybuffer.js new file mode 100644 index 0000000000..0a2f2850f2 --- /dev/null +++ b/test/mjsunit/harmony/sharedarraybuffer.js @@ -0,0 +1,576 @@ +// Copyright 2015 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Flags: --harmony-sharedarraybuffer + +// SharedArrayBuffer + +function TestByteLength(param, expectedByteLength) { + var sab = new SharedArrayBuffer(param); + assertSame(expectedByteLength, sab.byteLength); +} + +function TestArrayBufferCreation() { + TestByteLength(1, 1); + TestByteLength(256, 256); + TestByteLength(2.567, 2); + + TestByteLength("abc", 0); + + TestByteLength(0, 0); + + assertThrows(function() { new SharedArrayBuffer(-10); }, RangeError); + assertThrows(function() { new SharedArrayBuffer(-2.567); }, RangeError); + +/* TODO[dslomov]: Reenable the test + assertThrows(function() { + var ab1 = new SharedArrayBuffer(0xFFFFFFFFFFFF) + }, RangeError); +*/ + + var sab = new SharedArrayBuffer(); + assertSame(0, sab.byteLength); + assertEquals("[object SharedArrayBuffer]", + Object.prototype.toString.call(sab)); +} + +TestArrayBufferCreation(); + +function TestByteLengthNotWritable() { + var sab = new SharedArrayBuffer(1024); + assertSame(1024, sab.byteLength); + + assertThrows(function() { "use strict"; sab.byteLength = 42; }, TypeError); +} + +TestByteLengthNotWritable(); + +function TestArrayBufferNoSlice() { + var sab = new SharedArrayBuffer(10); + assertEquals(undefined, sab.slice); +} + +TestArrayBufferNoSlice(); + +// Typed arrays using SharedArrayBuffers + +// TODO(binji): how many of these tests are necessary if there are no new +// TypedArray types? + +function MakeSharedTypedArray(constr, numElements) { + var sab = new SharedArrayBuffer(constr.BYTES_PER_ELEMENT * numElements); + return new constr(sab); +} + +function TestTypedArray(constr, elementSize, typicalElement) { + assertSame(elementSize, constr.BYTES_PER_ELEMENT); + + var sab = new SharedArrayBuffer(256*elementSize); + + var a0 = new constr(30); + assertEquals("[object " + constr.name + "]", + Object.prototype.toString.call(a0)); + + // TODO(binji): Should this return false here? It is a view, but it doesn't + // view a SharedArrayBuffer... + assertTrue(SharedArrayBuffer.isView(a0)); + assertSame(elementSize, a0.BYTES_PER_ELEMENT); + assertSame(30, a0.length); + assertSame(30*elementSize, a0.byteLength); + assertSame(0, a0.byteOffset); + assertSame(30*elementSize, a0.buffer.byteLength); + + var aOverBufferLen0 = new constr(sab, 128*elementSize, 0); + assertSame(sab, aOverBufferLen0.buffer); + assertSame(elementSize, aOverBufferLen0.BYTES_PER_ELEMENT); + assertSame(0, aOverBufferLen0.length); + assertSame(0, aOverBufferLen0.byteLength); + assertSame(128*elementSize, aOverBufferLen0.byteOffset); + + var a1 = new constr(sab, 128*elementSize, 128); + assertSame(sab, a1.buffer); + assertSame(elementSize, a1.BYTES_PER_ELEMENT); + assertSame(128, a1.length); + assertSame(128*elementSize, a1.byteLength); + assertSame(128*elementSize, a1.byteOffset); + + + var a2 = new constr(sab, 64*elementSize, 128); + assertSame(sab, a2.buffer); + assertSame(elementSize, a2.BYTES_PER_ELEMENT); + assertSame(128, a2.length); + assertSame(128*elementSize, a2.byteLength); + assertSame(64*elementSize, a2.byteOffset); + + var a3 = new constr(sab, 192*elementSize); + assertSame(sab, a3.buffer); + assertSame(64, a3.length); + assertSame(64*elementSize, a3.byteLength); + assertSame(192*elementSize, a3.byteOffset); + + var a4 = new constr(sab); + assertSame(sab, a4.buffer); + assertSame(256, a4.length); + assertSame(256*elementSize, a4.byteLength); + assertSame(0, a4.byteOffset); + + + var i; + for (i = 0; i < 128; i++) { + a1[i] = typicalElement; + } + + for (i = 0; i < 128; i++) { + assertSame(typicalElement, a1[i]); + } + + for (i = 0; i < 64; i++) { + assertSame(0, a2[i]); + } + + for (i = 64; i < 128; i++) { + assertSame(typicalElement, a2[i]); + } + + for (i = 0; i < 64; i++) { + assertSame(typicalElement, a3[i]); + } + + for (i = 0; i < 128; i++) { + assertSame(0, a4[i]); + } + + for (i = 128; i < 256; i++) { + assertSame(typicalElement, a4[i]); + } + + var aAtTheEnd = new constr(sab, 256*elementSize); + assertSame(elementSize, aAtTheEnd.BYTES_PER_ELEMENT); + assertSame(0, aAtTheEnd.length); + assertSame(0, aAtTheEnd.byteLength); + assertSame(256*elementSize, aAtTheEnd.byteOffset); + + assertThrows(function () { new constr(sab, 257*elementSize); }, RangeError); + assertThrows( + function () { new constr(sab, 128*elementSize, 192); }, + RangeError); + + if (elementSize !== 1) { + assertThrows(function() { new constr(sab, 128*elementSize - 1, 10); }, + RangeError); + var unalignedArrayBuffer = new SharedArrayBuffer(10*elementSize + 1); + var goodArray = new constr(unalignedArrayBuffer, 0, 10); + assertSame(10, goodArray.length); + assertSame(10*elementSize, goodArray.byteLength); + assertThrows(function() { new constr(unalignedArrayBuffer)}, RangeError); + assertThrows(function() { new constr(unalignedArrayBuffer, 5*elementSize)}, + RangeError); + } + + var abLen0 = new SharedArrayBuffer(0); + var aOverAbLen0 = new constr(abLen0); + assertSame(abLen0, aOverAbLen0.buffer); + assertSame(elementSize, aOverAbLen0.BYTES_PER_ELEMENT); + assertSame(0, aOverAbLen0.length); + assertSame(0, aOverAbLen0.byteLength); + assertSame(0, aOverAbLen0.byteOffset); + + var a = new constr(sab, 64*elementSize, 128); + assertEquals("[object " + constr.name + "]", + Object.prototype.toString.call(a)); + var desc = Object.getOwnPropertyDescriptor( + constr.prototype, Symbol.toStringTag); + assertTrue(desc.configurable); + assertFalse(desc.enumerable); + assertFalse(!!desc.writable); + assertFalse(!!desc.set); + assertEquals("function", typeof desc.get); +} + +TestTypedArray(Uint8Array, 1, 0xFF); +TestTypedArray(Int8Array, 1, -0x7F); +TestTypedArray(Uint16Array, 2, 0xFFFF); +TestTypedArray(Int16Array, 2, -0x7FFF); +TestTypedArray(Uint32Array, 4, 0xFFFFFFFF); +TestTypedArray(Int32Array, 4, -0x7FFFFFFF); +TestTypedArray(Float32Array, 4, 0.5); +TestTypedArray(Float64Array, 8, 0.5); +TestTypedArray(Uint8ClampedArray, 1, 0xFF); + + +function SubarrayTestCase(constructor, item, expectedResultLen, + expectedStartIndex, initialLen, start, end) { + var a = MakeSharedTypedArray(constructor, initialLen); + var s = a.subarray(start, end); + assertSame(constructor, s.constructor); + assertSame(expectedResultLen, s.length); + if (s.length > 0) { + s[0] = item; + assertSame(item, a[expectedStartIndex]); + } +} + +function TestSubArray(constructor, item) { + SubarrayTestCase(constructor, item, 512, 512, 1024, 512, 1024); + SubarrayTestCase(constructor, item, 512, 512, 1024, 512); + + SubarrayTestCase(constructor, item, 0, undefined, 0, 1, 20); + SubarrayTestCase(constructor, item, 100, 0, 100, 0, 100); + SubarrayTestCase(constructor, item, 100, 0, 100, 0, 1000); + SubarrayTestCase(constructor, item, 0, undefined, 100, 5, 1); + + SubarrayTestCase(constructor, item, 1, 89, 100, -11, -10); + SubarrayTestCase(constructor, item, 9, 90, 100, -10, 99); + SubarrayTestCase(constructor, item, 0, undefined, 100, -10, 80); + SubarrayTestCase(constructor, item, 10,80, 100, 80, -10); + + SubarrayTestCase(constructor, item, 10,90, 100, 90, "100"); + SubarrayTestCase(constructor, item, 10,90, 100, "90", "100"); + + SubarrayTestCase(constructor, item, 0, undefined, 100, 90, "abc"); + SubarrayTestCase(constructor, item, 10,0, 100, "abc", 10); + + SubarrayTestCase(constructor, item, 10,0, 100, 0.96, 10.96); + SubarrayTestCase(constructor, item, 10,0, 100, 0.96, 10.01); + SubarrayTestCase(constructor, item, 10,0, 100, 0.01, 10.01); + SubarrayTestCase(constructor, item, 10,0, 100, 0.01, 10.96); + + + SubarrayTestCase(constructor, item, 10,90, 100, 90); + SubarrayTestCase(constructor, item, 10,90, 100, -10); +} + +TestSubArray(Uint8Array, 0xFF); +TestSubArray(Int8Array, -0x7F); +TestSubArray(Uint16Array, 0xFFFF); +TestSubArray(Int16Array, -0x7FFF); +TestSubArray(Uint32Array, 0xFFFFFFFF); +TestSubArray(Int32Array, -0x7FFFFFFF); +TestSubArray(Float32Array, 0.5); +TestSubArray(Float64Array, 0.5); +TestSubArray(Uint8ClampedArray, 0xFF); + +function TestTypedArrayOutOfRange(constructor, value, result) { + var a = MakeSharedTypedArray(constructor, 1); + a[0] = value; + assertSame(result, a[0]); +} + +TestTypedArrayOutOfRange(Uint8Array, 0x1FA, 0xFA); +TestTypedArrayOutOfRange(Uint8Array, -1, 0xFF); + +TestTypedArrayOutOfRange(Int8Array, 0x1FA, 0x7A - 0x80); + +TestTypedArrayOutOfRange(Uint16Array, 0x1FFFA, 0xFFFA); +TestTypedArrayOutOfRange(Uint16Array, -1, 0xFFFF); +TestTypedArrayOutOfRange(Int16Array, 0x1FFFA, 0x7FFA - 0x8000); + +TestTypedArrayOutOfRange(Uint32Array, 0x1FFFFFFFA, 0xFFFFFFFA); +TestTypedArrayOutOfRange(Uint32Array, -1, 0xFFFFFFFF); +TestTypedArrayOutOfRange(Int32Array, 0x1FFFFFFFA, 0x7FFFFFFA - 0x80000000); + +TestTypedArrayOutOfRange(Uint8ClampedArray, 0x1FA, 0xFF); +TestTypedArrayOutOfRange(Uint8ClampedArray, -1, 0); + +var typedArrayConstructors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Uint8ClampedArray, + Float32Array, + Float64Array]; + +function TestPropertyTypeChecks(constructor) { + function CheckProperty(name) { + var d = Object.getOwnPropertyDescriptor(constructor.prototype, name); + var o = {}; + assertThrows(function() {d.get.call(o);}, TypeError); + for (var i = 0; i < typedArrayConstructors.length; i++) { + var ctor = typedArrayConstructors[i]; + var a = MakeSharedTypedArray(ctor, 10); + if (ctor === constructor) { + d.get.call(a); // shouldn't throw + } else { + assertThrows(function() {d.get.call(a);}, TypeError); + } + } + } + + CheckProperty("buffer"); + CheckProperty("byteOffset"); + CheckProperty("byteLength"); + CheckProperty("length"); +} + +for(i = 0; i < typedArrayConstructors.length; i++) { + TestPropertyTypeChecks(typedArrayConstructors[i]); +} + +function TestTypedArraySet() { + // Test array.set in different combinations. + + function assertArrayPrefix(expected, array) { + for (var i = 0; i < expected.length; ++i) { + assertEquals(expected[i], array[i]); + } + } + + // SharedTypedArrays don't allow initialization via array-like + function initializeFromArray(constructor, array) { + var buffer = MakeSharedTypedArray(constructor, array.length); + for (var i = 0; i < array.length; ++i) { + buffer[i] = array[i]; + } + return buffer; + } + + var a11 = initializeFromArray(Int16Array, [1, 2, 3, 4, 0, -1]) + var a12 = MakeSharedTypedArray(Uint16Array, 15); + a12.set(a11, 3) + assertArrayPrefix([0, 0, 0, 1, 2, 3, 4, 0, 0xffff, 0, 0], a12) + assertThrows(function(){ a11.set(a12) }) + + var a21 = [1, undefined, 10, NaN, 0, -1, {valueOf: function() {return 3}}] + var a22 = MakeSharedTypedArray(Int32Array, 12) + a22.set(a21, 2) + assertArrayPrefix([0, 0, 1, 0, 10, 0, 0, -1, 3, 0], a22) + + var a31 = initializeFromArray(Float32Array, [2, 4, 6, 8, 11, NaN, 1/0, -3]) + var a32 = a31.subarray(2, 6) + a31.set(a32, 4) + assertArrayPrefix([2, 4, 6, 8, 6, 8, 11, NaN], a31) + assertArrayPrefix([6, 8, 6, 8], a32) + + var a4 = initializeFromArray(Uint8ClampedArray, [3,2,5,6]) + a4.set(a4) + assertArrayPrefix([3, 2, 5, 6], a4) + + // Cases with overlapping backing store but different element sizes. + var b = new SharedArrayBuffer(4) + var a5 = new Int16Array(b) + var a50 = new Int8Array(b) + var a51 = new Int8Array(b, 0, 2) + var a52 = new Int8Array(b, 1, 2) + var a53 = new Int8Array(b, 2, 2) + + a5.set([0x5050, 0x0a0a]) + assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50) + assertArrayPrefix([0x50, 0x50], a51) + assertArrayPrefix([0x50, 0x0a], a52) + assertArrayPrefix([0x0a, 0x0a], a53) + + a50.set([0x50, 0x50, 0x0a, 0x0a]) + a51.set(a5) + assertArrayPrefix([0x50, 0x0a, 0x0a, 0x0a], a50) + + a50.set([0x50, 0x50, 0x0a, 0x0a]) + a52.set(a5) + assertArrayPrefix([0x50, 0x50, 0x0a, 0x0a], a50) + + a50.set([0x50, 0x50, 0x0a, 0x0a]) + a53.set(a5) + assertArrayPrefix([0x50, 0x50, 0x50, 0x0a], a50) + + a50.set([0x50, 0x51, 0x0a, 0x0b]) + a5.set(a51) + assertArrayPrefix([0x0050, 0x0051], a5) + + a50.set([0x50, 0x51, 0x0a, 0x0b]) + a5.set(a52) + assertArrayPrefix([0x0051, 0x000a], a5) + + a50.set([0x50, 0x51, 0x0a, 0x0b]) + a5.set(a53) + assertArrayPrefix([0x000a, 0x000b], a5) + + // Mixed types of same size. + var a61 = initializeFromArray(Float32Array, [1.2, 12.3]) + var a62 = MakeSharedTypedArray(Int32Array, 2) + a62.set(a61) + assertArrayPrefix([1, 12], a62) + a61.set(a62) + assertArrayPrefix([1, 12], a61) + + // Invalid source + var a = MakeSharedTypedArray(Uint16Array, 50); + var expected = []; + for (i = 0; i < 50; i++) { + a[i] = i; + expected.push(i); + } + a.set({}); + assertArrayPrefix(expected, a); + assertThrows(function() { a.set.call({}) }, TypeError); + assertThrows(function() { a.set.call([]) }, TypeError); + + assertThrows(function() { a.set(0); }, TypeError); + assertThrows(function() { a.set(0, 1); }, TypeError); +} + +TestTypedArraySet(); + +function TestTypedArraysWithIllegalIndices() { + var a = MakeSharedTypedArray(Int32Array, 100); + + a[-10] = 10; + assertEquals(undefined, a[-10]); + a["-10"] = 10; + assertEquals(undefined, a["-10"]); + + var s = " -10"; + a[s] = 10; + assertEquals(10, a[s]); + var s1 = " -10 "; + a[s] = 10; + assertEquals(10, a[s]); + + a["-1e2"] = 10; + assertEquals(10, a["-1e2"]); + assertEquals(undefined, a[-1e2]); + + a["-0"] = 256; + var s2 = " -0"; + a[s2] = 255; + assertEquals(undefined, a["-0"]); + assertEquals(255, a[s2]); + assertEquals(0, a[-0]); + + /* Chromium bug: 424619 + * a[-Infinity] = 50; + * assertEquals(undefined, a[-Infinity]); + */ + a[1.5] = 10; + assertEquals(undefined, a[1.5]); + var nan = Math.sqrt(-1); + a[nan] = 5; + assertEquals(undefined, a[nan]); + + var x = 0; + var y = -0; + assertEquals(Infinity, 1/x); + assertEquals(-Infinity, 1/y); + a[x] = 5; + a[y] = 27; + assertEquals(27, a[x]); + assertEquals(27, a[y]); +} + +TestTypedArraysWithIllegalIndices(); + +function TestTypedArraysWithIllegalIndicesStrict() { + 'use strict'; + var a = MakeSharedTypedArray(Int32Array, 100); + + a[-10] = 10; + assertEquals(undefined, a[-10]); + a["-10"] = 10; + assertEquals(undefined, a["-10"]); + + var s = " -10"; + a[s] = 10; + assertEquals(10, a[s]); + var s1 = " -10 "; + a[s] = 10; + assertEquals(10, a[s]); + + a["-1e2"] = 10; + assertEquals(10, a["-1e2"]); + assertEquals(undefined, a[-1e2]); + + a["-0"] = 256; + var s2 = " -0"; + a[s2] = 255; + assertEquals(undefined, a["-0"]); + assertEquals(255, a[s2]); + assertEquals(0, a[-0]); + + /* Chromium bug: 424619 + * a[-Infinity] = 50; + * assertEquals(undefined, a[-Infinity]); + */ + a[1.5] = 10; + assertEquals(undefined, a[1.5]); + var nan = Math.sqrt(-1); + a[nan] = 5; + assertEquals(undefined, a[nan]); + + var x = 0; + var y = -0; + assertEquals(Infinity, 1/x); + assertEquals(-Infinity, 1/y); + a[x] = 5; + a[y] = 27; + assertEquals(27, a[x]); + assertEquals(27, a[y]); +} + +TestTypedArraysWithIllegalIndicesStrict(); + +// General tests for properties + +// Test property attribute [[Enumerable]] +function TestEnumerable(func, obj) { + function props(x) { + var array = []; + for (var p in x) array.push(p); + return array.sort(); + } + assertArrayEquals([], props(func)); + assertArrayEquals([], props(func.prototype)); + if (obj) + assertArrayEquals([], props(obj)); +} +TestEnumerable(ArrayBuffer, new SharedArrayBuffer()); +for(i = 0; i < typedArrayConstructors.length; i++) { + TestEnumerable(typedArrayConstructors[i]); +} + +// Test arbitrary properties on ArrayBuffer +function TestArbitrary(m) { + function TestProperty(map, property, value) { + map[property] = value; + assertEquals(value, map[property]); + } + for (var i = 0; i < 20; i++) { + TestProperty(m, 'key' + i, 'val' + i); + TestProperty(m, 'foo' + i, 'bar' + i); + } +} +TestArbitrary(new SharedArrayBuffer(256)); +for(i = 0; i < typedArrayConstructors.length; i++) { + TestArbitrary(MakeSharedTypedArray(typedArrayConstructors[i], 10)); +} + +// Test direct constructor call +assertThrows(function() { SharedArrayBuffer(); }, TypeError); +for(i = 0; i < typedArrayConstructors.length; i++) { + assertThrows(function(i) { typedArrayConstructors[i](); }.bind(this, i), + TypeError); +} diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 29eb40945c..b3f3c53d47 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -1749,7 +1749,8 @@ '../../src/harmony-regexp.js', '../../src/harmony-reflect.js', '../../src/harmony-spread.js', - '../../src/harmony-object.js' + '../../src/harmony-object.js', + '../../src/harmony-sharedarraybuffer.js', ], 'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin', 'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',