diff options
author | Michaël Zasso <targos@protonmail.com> | 2021-03-08 10:59:36 +0300 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2021-06-14 09:13:27 +0300 |
commit | 864fe9910bf0a09dfe5b9e1d0072718d17920338 (patch) | |
tree | 30e0fe365eb067a2611f7973fa2493fb4553b3f0 /deps | |
parent | 6111671d456f8287090b635da6df8cf72784011f (diff) |
deps: make V8 9.1 abi-compatible with 9.0
Revert "[api] Avoid handles for const API functions"
This reverts commit aee471b2ff5b1a9e622426454885b748d226535b.
Revert "[api] Remove deprecated [Shared]ArrayBuffer API"
This reverts commit 578f6be77fc5d8af975005c2baf918e7225abb62.
Revert "[Jobs]: Cleanup in v8 platform."
This reverts commit baf2b088dd9f585aa597459f30d71431171666e2.
Revert "Skip global registration of [Shared]ArrayBuffer backing stores"
This reverts commit fcdf35e6d70d51699ece063e25dc705e80673308.
PR-URL: https://github.com/nodejs/node/pull/38991
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Diffstat (limited to 'deps')
-rw-r--r-- | deps/v8/include/v8-platform.h | 18 | ||||
-rw-r--r-- | deps/v8/include/v8.h | 276 | ||||
-rw-r--r-- | deps/v8/src/api/api.cc | 529 | ||||
-rw-r--r-- | deps/v8/src/objects/backing-store.cc | 37 | ||||
-rw-r--r-- | deps/v8/src/objects/backing-store.h | 11 | ||||
-rw-r--r-- | deps/v8/src/objects/string-inl.h | 78 | ||||
-rw-r--r-- | deps/v8/src/objects/string.cc | 11 | ||||
-rw-r--r-- | deps/v8/src/objects/string.h | 92 | ||||
-rw-r--r-- | deps/v8/src/wasm/module-compiler.cc | 2 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-api-array-buffer.cc | 189 | ||||
-rw-r--r-- | deps/v8/test/cctest/test-typedarrays.cc | 24 |
11 files changed, 1029 insertions, 238 deletions
diff --git a/deps/v8/include/v8-platform.h b/deps/v8/include/v8-platform.h index fc9a357feb6..4c94d7fd392 100644 --- a/deps/v8/include/v8-platform.h +++ b/deps/v8/include/v8-platform.h @@ -181,8 +181,9 @@ class JobDelegate { /** * Returns true if the current task is called from the thread currently * running JobHandle::Join(). + * TODO(etiennep): Make pure virtual once custom embedders implement it. */ - virtual bool IsJoiningThread() const = 0; + virtual bool IsJoiningThread() const { return false; } }; /** @@ -219,14 +220,19 @@ class JobHandle { * Forces all existing workers to yield ASAP but doesn’t wait for them. * Warning, this is dangerous if the Job's callback is bound to or has access * to state which may be deleted after this call. + * TODO(etiennep): Cleanup once implemented by all embedders. */ - virtual void CancelAndDetach() = 0; + virtual void CancelAndDetach() { Cancel(); } /** * Returns true if there's any work pending or any worker running. */ virtual bool IsActive() = 0; + // TODO(etiennep): Clean up once all overrides are removed. + V8_DEPRECATED("Use !IsActive() instead.") + virtual bool IsCompleted() { return !IsActive(); } + /** * Returns true if associated with a Job and other methods may be called. * Returns false after Join() or Cancel() was called. This may return true @@ -234,6 +240,10 @@ class JobHandle { */ virtual bool IsValid() = 0; + // TODO(etiennep): Clean up once all overrides are removed. + V8_DEPRECATED("Use IsValid() instead.") + virtual bool IsRunning() { return IsValid(); } + /** * Returns true if job priority can be changed. */ @@ -262,6 +272,10 @@ class JobTask { * it must not call back any JobHandle methods. */ virtual size_t GetMaxConcurrency(size_t worker_count) const = 0; + + // TODO(1114823): Clean up once all overrides are removed. + V8_DEPRECATED("Use the version that takes |worker_count|.") + virtual size_t GetMaxConcurrency() const { return 0; } }; /** diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index ba87e57a1e2..840dd2c2258 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1495,7 +1495,7 @@ class V8_EXPORT UnboundScript { */ Local<Script> BindToCurrentContext(); - int GetId() const; + int GetId(); Local<Value> GetScriptName(); /** @@ -1705,7 +1705,7 @@ class V8_EXPORT Module : public Data { * * The module must be a SourceTextModule and must not have a kErrored status. */ - int ScriptId() const; + int ScriptId(); /** * Returns whether this module or any of its requested modules is async, @@ -3547,12 +3547,12 @@ class V8_EXPORT String : public Name { /** * Returns true if this string can be made external. */ - bool CanMakeExternal() const; + bool CanMakeExternal(); /** * Returns true if the strings values are equal. Same as JS ==/===. */ - bool StringEquals(Local<String> str) const; + bool StringEquals(Local<String> str); /** * Converts an object to a UTF-8-encoded character array. Useful if @@ -4162,7 +4162,7 @@ class V8_EXPORT Object : public Value { Maybe<bool> SetIntegrityLevel(Local<Context> context, IntegrityLevel level); /** Gets the number of internal fields for this Object. */ - int InternalFieldCount() const; + int InternalFieldCount(); /** Same as above, but works for PersistentBase. */ V8_INLINE static int InternalFieldCount( @@ -4272,10 +4272,10 @@ class V8_EXPORT Object : public Value { Local<Context> context, Local<Name> key); /** Tests for a named lookup interceptor.*/ - bool HasNamedLookupInterceptor() const; + bool HasNamedLookupInterceptor(); /** Tests for an index lookup interceptor.*/ - bool HasIndexedLookupInterceptor() const; + bool HasIndexedLookupInterceptor(); /** * Returns the identity hash for this object. The current implementation @@ -4315,12 +4315,12 @@ class V8_EXPORT Object : public Value { * ObjectTemplate::SetCallAsFunctionHandler method. * When an Object is callable this method returns true. */ - bool IsCallable() const; + bool IsCallable(); /** * True if this object is a constructor. */ - bool IsConstructor() const; + bool IsConstructor(); /** * True if this object can carry information relevant to the embedder in its @@ -4329,14 +4329,14 @@ class V8_EXPORT Object : public Value { * V8 automatically adds internal fields at compile time, such as e.g. * v8::ArrayBuffer. */ - bool IsApiWrapper() const; + bool IsApiWrapper(); /** * True if this object was created from an object template which was marked * as undetectable. See v8::ObjectTemplate::MarkAsUndetectable for more * information. */ - bool IsUndetectable() const; + bool IsUndetectable(); /** * Call an Object as a function if a callback is set by the @@ -4395,7 +4395,7 @@ class V8_EXPORT Object : public Value { * * See also: v8::ObjectTemplate::SetCodeLike */ - bool IsCodeLike(Isolate* isolate) const; + bool IsCodeLike(Isolate* isolate); private: Object(); @@ -4893,7 +4893,7 @@ class V8_EXPORT Promise : public Object { * Returns true if the promise has at least one derived promise, and * therefore resolve/reject handlers (including default handler). */ - bool HasHandler() const; + bool HasHandler(); /** * Returns the content of the [[PromiseResult]] field. The Promise must not @@ -5001,7 +5001,7 @@ class V8_EXPORT Proxy : public Object { public: Local<Value> GetTarget(); Local<Value> GetHandler(); - bool IsRevoked() const; + bool IsRevoked(); void Revoke(); /** @@ -5417,6 +5417,57 @@ class V8_EXPORT ArrayBuffer : public Object { }; /** + * The contents of an |ArrayBuffer|. Externalization of |ArrayBuffer| + * returns an instance of this class, populated, with a pointer to data + * and byte length. + * + * The Data pointer of ArrayBuffer::Contents must be freed using the provided + * deleter, which will call ArrayBuffer::Allocator::Free if the buffer + * was allocated with ArraryBuffer::Allocator::Allocate. + */ + class V8_EXPORT Contents { // NOLINT + public: + using DeleterCallback = void (*)(void* buffer, size_t length, void* info); + + Contents() + : data_(nullptr), + byte_length_(0), + allocation_base_(nullptr), + allocation_length_(0), + allocation_mode_(Allocator::AllocationMode::kNormal), + deleter_(nullptr), + deleter_data_(nullptr) {} + + void* AllocationBase() const { return allocation_base_; } + size_t AllocationLength() const { return allocation_length_; } + Allocator::AllocationMode AllocationMode() const { + return allocation_mode_; + } + + void* Data() const { return data_; } + size_t ByteLength() const { return byte_length_; } + DeleterCallback Deleter() const { return deleter_; } + void* DeleterData() const { return deleter_data_; } + + private: + Contents(void* data, size_t byte_length, void* allocation_base, + size_t allocation_length, + Allocator::AllocationMode allocation_mode, DeleterCallback deleter, + void* deleter_data); + + void* data_; + size_t byte_length_; + void* allocation_base_; + size_t allocation_length_; + Allocator::AllocationMode allocation_mode_; + DeleterCallback deleter_; + void* deleter_data_; + + friend class ArrayBuffer; + }; + + + /** * Data length in bytes. */ size_t ByteLength() const; @@ -5430,6 +5481,22 @@ class V8_EXPORT ArrayBuffer : public Object { static Local<ArrayBuffer> New(Isolate* isolate, size_t byte_length); /** + * Create a new ArrayBuffer over an existing memory block. + * The created array buffer is by default immediately in externalized state. + * In externalized state, the memory block will not be reclaimed when a + * created ArrayBuffer is garbage-collected. + * In internalized state, the memory block will be released using + * |Allocator::Free| once all ArrayBuffers referencing it are collected by + * the garbage collector. + */ + V8_DEPRECATED( + "Use the version that takes a BackingStore. " + "See http://crbug.com/v8/9908.") + static Local<ArrayBuffer> New( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized); + + /** * Create a new ArrayBuffer with an existing backing store. * The created array keeps a reference to the backing store until the array * is garbage collected. Note that the IsExternal bit does not affect this @@ -5468,6 +5535,15 @@ class V8_EXPORT ArrayBuffer : public Object { void* deleter_data); /** + * Returns true if ArrayBuffer is externalized, that is, does not + * own its memory block. + */ + V8_DEPRECATED( + "With v8::BackingStore externalized ArrayBuffers are " + "the same as ordinary ArrayBuffers. See http://crbug.com/v8/9908.") + bool IsExternal() const; + + /** * Returns true if this ArrayBuffer may be detached. */ bool IsDetachable() const; @@ -5481,10 +5557,46 @@ class V8_EXPORT ArrayBuffer : public Object { void Detach(); /** + * Make this ArrayBuffer external. The pointer to underlying memory block + * and byte length are returned as |Contents| structure. After ArrayBuffer + * had been externalized, it does no longer own the memory block. The caller + * should take steps to free memory when it is no longer needed. + * + * The Data pointer of ArrayBuffer::Contents must be freed using the provided + * deleter, which will call ArrayBuffer::Allocator::Free if the buffer + * was allocated with ArrayBuffer::Allocator::Allocate. + */ + V8_DEPRECATED("Use GetBackingStore or Detach. See http://crbug.com/v8/9908.") + Contents Externalize(); + + /** + * Marks this ArrayBuffer external given a witness that the embedder + * has fetched the backing store using the new GetBackingStore() function. + * + * With the new lifetime management of backing stores there is no need for + * externalizing, so this function exists only to make the transition easier. + */ + V8_DEPRECATED("This will be removed together with IsExternal.") + void Externalize(const std::shared_ptr<BackingStore>& backing_store); + + /** + * 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 gets garbage collected. + * + * The embedder should make sure to hold a strong reference to the + * ArrayBuffer while accessing this pointer. + */ + V8_DEPRECATED("Use GetBackingStore. See http://crbug.com/v8/9908.") + Contents GetContents(); + + /** * Get a shared pointer to the backing store of this array buffer. This * pointer coordinates the lifetime management of the internal storage * with any live ArrayBuffers on the heap, even across isolates. The embedder * should not attempt to manage lifetime of the storage through other means. + * + * This function replaces both Externalize() and GetContents(). */ std::shared_ptr<BackingStore> GetBackingStore(); @@ -5496,6 +5608,7 @@ class V8_EXPORT ArrayBuffer : public Object { private: ArrayBuffer(); static void CheckCast(Value* obj); + Contents GetContents(bool externalize); }; @@ -5789,6 +5902,57 @@ class V8_EXPORT DataView : public ArrayBufferView { 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 ArrayBuffer::Contents must be freed using the provided + * deleter, which will call ArrayBuffer::Allocator::Free if the buffer + * was allocated with ArraryBuffer::Allocator::Allocate. + */ + class V8_EXPORT Contents { // NOLINT + public: + using Allocator = v8::ArrayBuffer::Allocator; + using DeleterCallback = void (*)(void* buffer, size_t length, void* info); + + Contents() + : data_(nullptr), + byte_length_(0), + allocation_base_(nullptr), + allocation_length_(0), + allocation_mode_(Allocator::AllocationMode::kNormal), + deleter_(nullptr), + deleter_data_(nullptr) {} + + void* AllocationBase() const { return allocation_base_; } + size_t AllocationLength() const { return allocation_length_; } + Allocator::AllocationMode AllocationMode() const { + return allocation_mode_; + } + + void* Data() const { return data_; } + size_t ByteLength() const { return byte_length_; } + DeleterCallback Deleter() const { return deleter_; } + void* DeleterData() const { return deleter_data_; } + + private: + Contents(void* data, size_t byte_length, void* allocation_base, + size_t allocation_length, + Allocator::AllocationMode allocation_mode, DeleterCallback deleter, + void* deleter_data); + + void* data_; + size_t byte_length_; + void* allocation_base_; + size_t allocation_length_; + Allocator::AllocationMode allocation_mode_; + DeleterCallback deleter_; + void* deleter_data_; + + friend class SharedArrayBuffer; + }; + + /** * Data length in bytes. */ size_t ByteLength() const; @@ -5802,6 +5966,19 @@ class V8_EXPORT SharedArrayBuffer : public Object { static Local<SharedArrayBuffer> 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. + */ + V8_DEPRECATED( + "Use the version that takes a BackingStore. " + "See http://crbug.com/v8/9908.") + static Local<SharedArrayBuffer> New( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized); + + /** * Create a new SharedArrayBuffer with an existing backing store. * The created array keeps a reference to the backing store until the array * is garbage collected. Note that the IsExternal bit does not affect this @@ -5840,10 +6017,72 @@ class V8_EXPORT SharedArrayBuffer : public Object { void* deleter_data); /** + * Create a new SharedArrayBuffer over an existing memory block. Propagate + * flags to indicate whether the underlying buffer can be grown. + */ + V8_DEPRECATED( + "Use the version that takes a BackingStore. " + "See http://crbug.com/v8/9908.") + static Local<SharedArrayBuffer> New( + Isolate* isolate, const SharedArrayBuffer::Contents&, + ArrayBufferCreationMode mode = ArrayBufferCreationMode::kExternalized); + + /** + * Returns true if SharedArrayBuffer is externalized, that is, does not + * own its memory block. + */ + V8_DEPRECATED( + "With v8::BackingStore externalized SharedArrayBuffers are the same " + "as ordinary SharedArrayBuffers. See http://crbug.com/v8/9908.") + 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 externalized, it does no longer own 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. + * + */ + V8_DEPRECATED("Use GetBackingStore or Detach. See http://crbug.com/v8/9908.") + Contents Externalize(); + + /** + * Marks this SharedArrayBuffer external given a witness that the embedder + * has fetched the backing store using the new GetBackingStore() function. + * + * With the new lifetime management of backing stores there is no need for + * externalizing, so this function exists only to make the transition easier. + */ + V8_DEPRECATED("This will be removed together with IsExternal.") + void Externalize(const std::shared_ptr<BackingStore>& backing_store); + + /** + * 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. + */ + V8_DEPRECATED("Use GetBackingStore. See http://crbug.com/v8/9908.") + Contents GetContents(); + + /** * Get a shared pointer to the backing store of this array buffer. This * pointer coordinates the lifetime management of the internal storage * with any live ArrayBuffers on the heap, even across isolates. The embedder * should not attempt to manage lifetime of the storage through other means. + * + * This function replaces both Externalize() and GetContents(). */ std::shared_ptr<BackingStore> GetBackingStore(); @@ -5854,6 +6093,7 @@ class V8_EXPORT SharedArrayBuffer : public Object { private: SharedArrayBuffer(); static void CheckCast(Value* obj); + Contents GetContents(bool externalize); }; @@ -6953,7 +7193,7 @@ class V8_EXPORT ObjectTemplate : public Template { * Gets the number of internal fields for objects generated from * this template. */ - int InternalFieldCount() const; + int InternalFieldCount(); /** * Sets the number of internal fields for objects generated from @@ -6964,7 +7204,7 @@ class V8_EXPORT ObjectTemplate : public Template { /** * Returns true if the object will be an immutable prototype exotic object. */ - bool IsImmutableProto() const; + bool IsImmutableProto(); /** * Makes the ObjectTemplate for an immutable prototype exotic object, with an @@ -6982,7 +7222,7 @@ class V8_EXPORT ObjectTemplate : public Template { * Reference: https://github.com/tc39/proposal-dynamic-code-brand-checks */ void SetCodeLike(); - bool IsCodeLike() const; + bool IsCodeLike(); V8_INLINE static ObjectTemplate* Cast(Data* data); @@ -10547,7 +10787,7 @@ class V8_EXPORT Context : public Data { * Returns true if code generation from strings is allowed for the context. * For more details see AllowCodeGenerationFromStrings(bool) documentation. */ - bool IsCodeGenerationFromStringsAllowed() const; + bool IsCodeGenerationFromStringsAllowed(); /** * Sets the error description for the exception that is thrown when diff --git a/deps/v8/src/api/api.cc b/deps/v8/src/api/api.cc index b0849860421..01833cafc61 100644 --- a/deps/v8/src/api/api.cc +++ b/deps/v8/src/api/api.cc @@ -937,9 +937,11 @@ bool Data::IsModule() const { return Utils::OpenHandle(this)->IsModule(); } bool Data::IsValue() const { i::DisallowGarbageCollection no_gc; - i::Object self = *Utils::OpenHandle(this); - if (self.IsSmi()) return true; - i::HeapObject heap_object = i::HeapObject::cast(self); + i::Handle<i::Object> self = Utils::OpenHandle(this); + if (self->IsSmi()) { + return true; + } + i::HeapObject heap_object = i::HeapObject::cast(*self); DCHECK(!heap_object.IsTheHole()); if (heap_object.IsSymbol()) { return !i::Symbol::cast(heap_object).is_private(); @@ -1764,7 +1766,7 @@ void ObjectTemplate::SetCallAsFunctionHandler(FunctionCallback callback, i::FunctionTemplateInfo::SetInstanceCallHandler(isolate, cons, obj); } -int ObjectTemplate::InternalFieldCount() const { +int ObjectTemplate::InternalFieldCount() { return Utils::OpenHandle(this)->embedder_field_count(); } @@ -1785,7 +1787,7 @@ void ObjectTemplate::SetInternalFieldCount(int value) { Utils::OpenHandle(this)->set_embedder_field_count(value); } -bool ObjectTemplate::IsImmutableProto() const { +bool ObjectTemplate::IsImmutableProto() { return Utils::OpenHandle(this)->immutable_proto(); } @@ -1796,7 +1798,7 @@ void ObjectTemplate::SetImmutableProto() { self->set_immutable_proto(true); } -bool ObjectTemplate::IsCodeLike() const { +bool ObjectTemplate::IsCodeLike() { return Utils::OpenHandle(this)->code_like(); } @@ -1851,11 +1853,15 @@ Local<Script> UnboundScript::BindToCurrentContext() { return ToApiHandle<Script>(function); } -int UnboundScript::GetId() const { - auto function_info = i::SharedFunctionInfo::cast(*Utils::OpenHandle(this)); - i::Isolate* isolate = function_info.GetIsolate(); +int UnboundScript::GetId() { + auto function_info = + i::Handle<i::SharedFunctionInfo>::cast(Utils::OpenHandle(this)); + i::Isolate* isolate = function_info->GetIsolate(); LOG_API(isolate, UnboundScript, GetId); - return i::Script::cast(function_info.script()).id(); + i::HandleScope scope(isolate); + i::Handle<i::Script> script(i::Script::cast(function_info->script()), + isolate); + return script->id(); } int UnboundScript::GetLineNumber(int code_pos) { @@ -2066,10 +2072,13 @@ Local<Value> Module::GetException() const { } int Module::GetModuleRequestsLength() const { - i::Module self = *Utils::OpenHandle(this); - if (self.IsSyntheticModule()) return 0; - ASSERT_NO_SCRIPT_NO_EXCEPTION(self.GetIsolate()); - return i::SourceTextModule::cast(self).info().module_requests().length(); + i::Handle<i::Module> self = Utils::OpenHandle(this); + if (self->IsSyntheticModule()) return 0; + ASSERT_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate()); + return i::Handle<i::SourceTextModule>::cast(self) + ->info() + .module_requests() + .length(); } Local<String> Module::GetModuleRequest(int i) const { @@ -2170,22 +2179,22 @@ Local<UnboundModuleScript> Module::GetUnboundModuleScript() { isolate)); } -int Module::ScriptId() const { - i::Module self = *Utils::OpenHandle(this); - Utils::ApiCheck(self.IsSourceTextModule(), "v8::Module::ScriptId", +int Module::ScriptId() { + i::Handle<i::Module> self = Utils::OpenHandle(this); + Utils::ApiCheck(self->IsSourceTextModule(), "v8::Module::ScriptId", "v8::Module::ScriptId must be used on an SourceTextModule"); - ASSERT_NO_SCRIPT_NO_EXCEPTION(self.GetIsolate()); - return i::SourceTextModule::cast(self).GetScript().id(); + ASSERT_NO_SCRIPT_NO_EXCEPTION(self->GetIsolate()); + return i::Handle<i::SourceTextModule>::cast(self)->GetScript().id(); } bool Module::IsGraphAsync() const { Utils::ApiCheck( GetStatus() >= kInstantiated, "v8::Module::IsGraphAsync", "v8::Module::IsGraphAsync must be used on an instantiated module"); - i::Module self = *Utils::OpenHandle(this); - auto isolate = self.GetIsolate(); + i::Handle<i::Module> self = Utils::OpenHandle(this); + auto isolate = self->GetIsolate(); ASSERT_NO_SCRIPT_NO_EXCEPTION(isolate); - return self.IsGraphAsync(isolate); + return self->IsGraphAsync(isolate); } bool Module::IsSourceTextModule() const { @@ -3323,15 +3332,15 @@ bool Value::FullIsNull() const { } bool Value::IsTrue() const { - i::Object object = *Utils::OpenHandle(this); - if (object.IsSmi()) return false; - return object.IsTrue(); + i::Handle<i::Object> object = Utils::OpenHandle(this); + if (object->IsSmi()) return false; + return object->IsTrue(); } bool Value::IsFalse() const { - i::Object object = *Utils::OpenHandle(this); - if (object.IsSmi()) return false; - return object.IsFalse(); + i::Handle<i::Object> object = Utils::OpenHandle(this); + if (object->IsSmi()) return false; + return object->IsFalse(); } bool Value::IsFunction() const { return Utils::OpenHandle(this)->IsCallable(); } @@ -3351,9 +3360,8 @@ bool Value::IsSymbol() const { bool Value::IsArray() const { return Utils::OpenHandle(this)->IsJSArray(); } bool Value::IsArrayBuffer() const { - i::Object obj = *Utils::OpenHandle(this); - if (!obj.IsJSArrayBuffer()) return false; - return !i::JSArrayBuffer::cast(obj).is_shared(); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + return obj->IsJSArrayBuffer() && !i::JSArrayBuffer::cast(*obj).is_shared(); } bool Value::IsArrayBufferView() const { @@ -3380,9 +3388,8 @@ bool Value::IsDataView() const { } bool Value::IsSharedArrayBuffer() const { - i::Object obj = *Utils::OpenHandle(this); - if (!obj.IsJSArrayBuffer()) return false; - return i::JSArrayBuffer::cast(obj).is_shared(); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + return obj->IsJSArrayBuffer() && i::JSArrayBuffer::cast(*obj).is_shared(); } bool Value::IsObject() const { return Utils::OpenHandle(this)->IsJSReceiver(); } @@ -3423,23 +3430,23 @@ VALUE_IS_SPECIFIC_TYPE(WeakSet, JSWeakSet) bool Value::IsBoolean() const { return Utils::OpenHandle(this)->IsBoolean(); } bool Value::IsExternal() const { - i::Object obj = *Utils::OpenHandle(this); - if (!obj.IsHeapObject()) return false; - i::HeapObject heap_obj = i::HeapObject::cast(obj); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (!obj->IsHeapObject()) return false; + i::Handle<i::HeapObject> heap_obj = i::Handle<i::HeapObject>::cast(obj); // Check the instance type is JS_OBJECT (instance type of Externals) before // attempting to get the Isolate since that guarantees the object is writable // and GetIsolate will work. - if (heap_obj.map().instance_type() != i::JS_OBJECT_TYPE) return false; - i::Isolate* isolate = i::JSObject::cast(heap_obj).GetIsolate(); + if (heap_obj->map().instance_type() != i::JS_OBJECT_TYPE) return false; + i::Isolate* isolate = i::JSObject::cast(*heap_obj).GetIsolate(); ASSERT_NO_SCRIPT_NO_EXCEPTION(isolate); - return heap_obj.IsExternal(isolate); + return heap_obj->IsExternal(isolate); } bool Value::IsInt32() const { - i::Object obj = *Utils::OpenHandle(this); - if (obj.IsSmi()) return true; - if (obj.IsNumber()) { - return i::IsInt32Double(obj.Number()); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (obj->IsSmi()) return true; + if (obj->IsNumber()) { + return i::IsInt32Double(obj->Number()); } return false; } @@ -3465,18 +3472,18 @@ bool Value::IsRegExp() const { } bool Value::IsAsyncFunction() const { - i::Object obj = *Utils::OpenHandle(this); - if (!obj.IsJSFunction()) return false; - i::JSFunction func = i::JSFunction::cast(obj); - return i::IsAsyncFunction(func.shared().kind()); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (!obj->IsJSFunction()) return false; + i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(obj); + return i::IsAsyncFunction(func->shared().kind()); } bool Value::IsGeneratorFunction() const { - i::Object obj = *Utils::OpenHandle(this); - if (!obj.IsJSFunction()) return false; - i::JSFunction func = i::JSFunction::cast(obj); - ASSERT_NO_SCRIPT_NO_EXCEPTION(func.GetIsolate()); - return i::IsGeneratorFunction(func.shared().kind()); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (!obj->IsJSFunction()) return false; + i::Handle<i::JSFunction> func = i::Handle<i::JSFunction>::cast(obj); + ASSERT_NO_SCRIPT_NO_EXCEPTION(func->GetIsolate()); + return i::IsGeneratorFunction(func->shared().kind()); } bool Value::IsGeneratorObject() const { @@ -3797,6 +3804,7 @@ std::shared_ptr<v8::BackingStore> v8::ArrayBuffer::GetBackingStore() { backing_store = i::BackingStore::EmptyBackingStore(i::SharedFlag::kNotShared); } + i::GlobalBackingStoreRegistry::Register(backing_store); std::shared_ptr<i::BackingStoreBase> bs_base = backing_store; return std::static_pointer_cast<v8::BackingStore>(bs_base); } @@ -3807,6 +3815,7 @@ std::shared_ptr<v8::BackingStore> v8::SharedArrayBuffer::GetBackingStore() { if (!backing_store) { backing_store = i::BackingStore::EmptyBackingStore(i::SharedFlag::kShared); } + i::GlobalBackingStoreRegistry::Register(backing_store); std::shared_ptr<i::BackingStoreBase> bs_base = backing_store; return std::static_pointer_cast<v8::BackingStore>(bs_base); } @@ -4678,16 +4687,16 @@ Maybe<bool> v8::Object::HasRealNamedCallbackProperty(Local<Context> context, return result; } -bool v8::Object::HasNamedLookupInterceptor() const { - auto self = *Utils::OpenHandle(this); - if (self.IsJSObject()) return false; - return i::JSObject::cast(self).HasNamedInterceptor(); +bool v8::Object::HasNamedLookupInterceptor() { + auto self = Utils::OpenHandle(this); + return self->IsJSObject() && + i::Handle<i::JSObject>::cast(self)->HasNamedInterceptor(); } -bool v8::Object::HasIndexedLookupInterceptor() const { - auto self = *Utils::OpenHandle(this); - if (self.IsJSObject()) return false; - return i::JSObject::cast(self).HasIndexedInterceptor(); +bool v8::Object::HasIndexedLookupInterceptor() { + auto self = Utils::OpenHandle(this); + return self->IsJSObject() && + i::Handle<i::JSObject>::cast(self)->HasIndexedInterceptor(); } MaybeLocal<Value> v8::Object::GetRealNamedPropertyInPrototypeChain( @@ -4822,22 +4831,22 @@ int v8::Object::GetIdentityHash() { return self->GetOrCreateIdentityHash(isolate).value(); } -bool v8::Object::IsCallable() const { +bool v8::Object::IsCallable() { auto self = Utils::OpenHandle(this); return self->IsCallable(); } -bool v8::Object::IsConstructor() const { +bool v8::Object::IsConstructor() { auto self = Utils::OpenHandle(this); return self->IsConstructor(); } -bool v8::Object::IsApiWrapper() const { +bool v8::Object::IsApiWrapper() { auto self = i::Handle<i::JSObject>::cast(Utils::OpenHandle(this)); return self->IsApiWrapper(); } -bool v8::Object::IsUndetectable() const { +bool v8::Object::IsUndetectable() { auto self = i::Handle<i::JSObject>::cast(Utils::OpenHandle(this)); return self->IsUndetectable(); } @@ -5078,11 +5087,17 @@ int Function::GetScriptColumnNumber() const { } int Function::ScriptId() const { - i::JSReceiver self = *Utils::OpenHandle(this); - if (!self.IsJSFunction()) return v8::UnboundScript::kNoScriptId; - auto func = i::JSFunction::cast(self); - if (!func.shared().script().IsScript()) return v8::UnboundScript::kNoScriptId; - return i::Script::cast(func.shared().script()).id(); + auto self = Utils::OpenHandle(this); + if (!self->IsJSFunction()) { + return v8::UnboundScript::kNoScriptId; + } + auto func = i::Handle<i::JSFunction>::cast(self); + if (!func->shared().script().IsScript()) { + return v8::UnboundScript::kNoScriptId; + } + i::Handle<i::Script> script(i::Script::cast(func->shared().script()), + func->GetIsolate()); + return script->id(); } Local<v8::Value> Function::GetBoundFunction() const { @@ -5616,36 +5631,36 @@ bool Boolean::Value() const { } int64_t Integer::Value() const { - i::Object obj = *Utils::OpenHandle(this); - if (obj.IsSmi()) { - return i::Smi::ToInt(obj); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (obj->IsSmi()) { + return i::Smi::ToInt(*obj); } else { - return static_cast<int64_t>(obj.Number()); + return static_cast<int64_t>(obj->Number()); } } int32_t Int32::Value() const { - i::Object obj = *Utils::OpenHandle(this); - if (obj.IsSmi()) { - return i::Smi::ToInt(obj); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (obj->IsSmi()) { + return i::Smi::ToInt(*obj); } else { - return static_cast<int32_t>(obj.Number()); + return static_cast<int32_t>(obj->Number()); } } uint32_t Uint32::Value() const { - i::Object obj = *Utils::OpenHandle(this); - if (obj.IsSmi()) { - return i::Smi::ToInt(obj); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + if (obj->IsSmi()) { + return i::Smi::ToInt(*obj); } else { - return static_cast<uint32_t>(obj.Number()); + return static_cast<uint32_t>(obj->Number()); } } -int v8::Object::InternalFieldCount() const { - i::JSReceiver self = *Utils::OpenHandle(this); - if (!self.IsJSObject()) return 0; - return i::JSObject::cast(self).GetEmbedderFieldCount(); +int v8::Object::InternalFieldCount() { + i::Handle<i::JSReceiver> self = Utils::OpenHandle(this); + if (!self->IsJSObject()) return 0; + return i::Handle<i::JSObject>::cast(self)->GetEmbedderFieldCount(); } static bool InternalFieldOK(i::Handle<i::JSReceiver> obj, int index, @@ -6178,9 +6193,9 @@ void Context::AllowCodeGenerationFromStrings(bool allow) { : i::ReadOnlyRoots(isolate).false_value()); } -bool Context::IsCodeGenerationFromStringsAllowed() const { - i::Context context = *Utils::OpenHandle(this); - return !context.allow_code_gen_from_strings().IsFalse(context.GetIsolate()); +bool Context::IsCodeGenerationFromStringsAllowed() { + i::Handle<i::Context> context = Utils::OpenHandle(this); + return !context->allow_code_gen_from_strings().IsFalse(context->GetIsolate()); } void Context::SetErrorMessageForCodeGenerationFromStrings(Local<String> error) { @@ -6624,7 +6639,8 @@ bool v8::String::MakeExternal( return result; } -bool v8::String::CanMakeExternal() const { +bool v8::String::CanMakeExternal() { + i::DisallowGarbageCollection no_gc; i::String obj = *Utils::OpenHandle(this); if (obj.IsThinString()) { @@ -6639,7 +6655,7 @@ bool v8::String::CanMakeExternal() const { return !i::Heap::InYoungGeneration(obj); } -bool v8::String::StringEquals(Local<String> that) const { +bool v8::String::StringEquals(Local<String> that) { auto self = Utils::OpenHandle(this); auto other = Utils::OpenHandle(*that); return self->Equals(*other); @@ -6810,11 +6826,12 @@ Local<v8::Value> v8::BooleanObject::New(Isolate* isolate, bool value) { } bool v8::BooleanObject::ValueOf() const { - i::Object obj = *Utils::OpenHandle(this); - i::JSPrimitiveWrapper js_primitive_wrapper = i::JSPrimitiveWrapper::cast(obj); - i::Isolate* isolate = js_primitive_wrapper.GetIsolate(); + i::Handle<i::Object> obj = Utils::OpenHandle(this); + i::Handle<i::JSPrimitiveWrapper> js_primitive_wrapper = + i::Handle<i::JSPrimitiveWrapper>::cast(obj); + i::Isolate* isolate = js_primitive_wrapper->GetIsolate(); LOG_API(isolate, BooleanObject, BooleanValue); - return js_primitive_wrapper.value().IsTrue(isolate); + return js_primitive_wrapper->value().IsTrue(isolate); } Local<v8::Value> v8::StringObject::New(Isolate* v8_isolate, @@ -7341,13 +7358,16 @@ MaybeLocal<Promise> Promise::Then(Local<Context> context, RETURN_ESCAPED(Local<Promise>::Cast(Utils::ToLocal(result))); } -bool Promise::HasHandler() const { - i::JSReceiver promise = *Utils::OpenHandle(this); - i::Isolate* isolate = promise.GetIsolate(); +bool Promise::HasHandler() { + i::Handle<i::JSReceiver> promise = Utils::OpenHandle(this); + i::Isolate* isolate = promise->GetIsolate(); LOG_API(isolate, Promise, HasRejectHandler); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(isolate); - if (!promise.IsJSPromise()) return false; - return i::JSPromise::cast(promise).has_handler(); + if (promise->IsJSPromise()) { + i::Handle<i::JSPromise> js_promise = i::Handle<i::JSPromise>::cast(promise); + return js_promise->has_handler(); + } + return false; } Local<Value> Promise::Result() { @@ -7386,7 +7406,7 @@ Local<Value> Proxy::GetHandler() { return Utils::ToLocal(handler); } -bool Proxy::IsRevoked() const { +bool Proxy::IsRevoked() { i::Handle<i::JSProxy> self = Utils::OpenHandle(this); return self->IsRevoked(); } @@ -7513,17 +7533,169 @@ v8::ArrayBuffer::Allocator* v8::ArrayBuffer::Allocator::NewDefaultAllocator() { return new ArrayBufferAllocator(); } +bool v8::ArrayBuffer::IsExternal() const { + return Utils::OpenHandle(this)->is_external(); +} + bool v8::ArrayBuffer::IsDetachable() const { return Utils::OpenHandle(this)->is_detachable(); } namespace { +// The backing store deleter just deletes the indirection, which downrefs +// the shared pointer. It will get collected normally. +void BackingStoreDeleter(void* buffer, size_t length, void* info) { + std::shared_ptr<i::BackingStore>* bs_indirection = + reinterpret_cast<std::shared_ptr<i::BackingStore>*>(info); + if (bs_indirection) { + i::BackingStore* backing_store = bs_indirection->get(); + TRACE_BS("API:delete bs=%p mem=%p (length=%zu)\n", backing_store, + backing_store->buffer_start(), backing_store->byte_length()); + USE(backing_store); + } + delete bs_indirection; +} + +void* MakeDeleterData(std::shared_ptr<i::BackingStore> backing_store) { + if (!backing_store) return nullptr; + TRACE_BS("API:extern bs=%p mem=%p (length=%zu)\n", backing_store.get(), + backing_store->buffer_start(), backing_store->byte_length()); + return new std::shared_ptr<i::BackingStore>(backing_store); +} + +std::shared_ptr<i::BackingStore> LookupOrCreateBackingStore( + i::Isolate* i_isolate, void* data, size_t byte_length, i::SharedFlag shared, + ArrayBufferCreationMode mode) { + // "internalized" means that the storage was allocated by the + // ArrayBufferAllocator and thus should be freed upon destruction. + bool free_on_destruct = mode == ArrayBufferCreationMode::kInternalized; + + // Try to lookup a previously-registered backing store in the global + // registry. If found, use that instead of wrapping an embedder allocation. + std::shared_ptr<i::BackingStore> backing_store = + i::GlobalBackingStoreRegistry::Lookup(data, byte_length); + + if (backing_store) { + // Check invariants for a previously-found backing store. + + // 1. We cannot allow an embedder to first allocate a backing store that + // should not be freed upon destruct, and then allocate an alias that should + // destruct it. The other order is fine. + bool changing_destruct_mode = + free_on_destruct && !backing_store->free_on_destruct(); + Utils::ApiCheck( + !changing_destruct_mode, "v8_[Shared]ArrayBuffer_New", + "previous backing store found that should not be freed on destruct"); + + // 2. We cannot allow embedders to use the same backing store for both + // SharedArrayBuffers and regular ArrayBuffers. + bool changing_shared_flag = + (shared == i::SharedFlag::kShared) != backing_store->is_shared(); + Utils::ApiCheck( + !changing_shared_flag, "v8_[Shared]ArrayBuffer_New", + "previous backing store found that does not match shared flag"); + } else { + // No previous backing store found. + backing_store = i::BackingStore::WrapAllocation( + i_isolate, data, byte_length, shared, free_on_destruct); + + // The embedder already has a direct pointer to the buffer start, so + // globally register the backing store in case they come back with the + // same buffer start and the backing store is marked as free_on_destruct. + i::GlobalBackingStoreRegistry::Register(backing_store); + } + return backing_store; +} + std::shared_ptr<i::BackingStore> ToInternal( std::shared_ptr<i::BackingStoreBase> backing_store) { return std::static_pointer_cast<i::BackingStore>(backing_store); } } // namespace +v8::ArrayBuffer::Contents::Contents(void* data, size_t byte_length, + void* allocation_base, + size_t allocation_length, + Allocator::AllocationMode allocation_mode, + DeleterCallback deleter, void* deleter_data) + : data_(data), + byte_length_(byte_length), + allocation_base_(allocation_base), + allocation_length_(allocation_length), + allocation_mode_(allocation_mode), + deleter_(deleter), + deleter_data_(deleter_data) { + DCHECK_LE(allocation_base_, data_); + DCHECK_LE(byte_length_, allocation_length_); +} + +v8::ArrayBuffer::Contents v8::ArrayBuffer::Externalize() { + return GetContents(true); +} + +void v8::ArrayBuffer::Externalize( + const std::shared_ptr<BackingStore>& backing_store) { + i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this); + Utils::ApiCheck(!self->is_external(), "v8_ArrayBuffer_Externalize", + "ArrayBuffer already externalized"); + self->set_is_external(true); + DCHECK_EQ(self->backing_store(), backing_store->Data()); +} + +v8::ArrayBuffer::Contents v8::ArrayBuffer::GetContents() { + return GetContents(false); +} + +v8::ArrayBuffer::Contents v8::ArrayBuffer::GetContents(bool externalize) { + // TODO(titzer): reduce duplication between shared/unshared GetContents() + using BufferType = v8::ArrayBuffer; + + i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this); + + std::shared_ptr<i::BackingStore> backing_store = self->GetBackingStore(); + + void* deleter_data = nullptr; + if (externalize) { + Utils::ApiCheck(!self->is_external(), "v8_ArrayBuffer_Externalize", + "ArrayBuffer already externalized"); + self->set_is_external(true); + // When externalizing, upref the shared pointer to the backing store + // and store that as the deleter data. When the embedder calls the deleter + // callback, we will delete the additional (on-heap) shared_ptr. + deleter_data = MakeDeleterData(backing_store); + } + + if (!backing_store) { + // If the array buffer has zero length or was detached, return empty + // contents. + DCHECK_EQ(0, self->byte_length()); + BufferType::Contents contents( + nullptr, 0, nullptr, 0, + v8::ArrayBuffer::Allocator::AllocationMode::kNormal, + BackingStoreDeleter, deleter_data); + return contents; + } + + // Backing stores that given to the embedder might be passed back through + // the API using only the start of the buffer. We need to find such + // backing stores using global registration until the API is changed. + i::GlobalBackingStoreRegistry::Register(backing_store); + + auto allocation_mode = + backing_store->is_wasm_memory() + ? v8::ArrayBuffer::Allocator::AllocationMode::kReservation + : v8::ArrayBuffer::Allocator::AllocationMode::kNormal; + + BufferType::Contents contents(backing_store->buffer_start(), // -- + backing_store->byte_length(), // -- + backing_store->buffer_start(), // -- + backing_store->byte_length(), // -- + allocation_mode, // -- + BackingStoreDeleter, // -- + deleter_data); + return contents; +} + void v8::ArrayBuffer::Detach() { i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this); i::Isolate* isolate = obj->GetIsolate(); @@ -7557,6 +7729,27 @@ Local<ArrayBuffer> v8::ArrayBuffer::New(Isolate* isolate, size_t byte_length) { return Utils::ToLocal(array_buffer); } +Local<ArrayBuffer> v8::ArrayBuffer::New(Isolate* isolate, void* data, + size_t byte_length, + ArrayBufferCreationMode mode) { + // Embedders must guarantee that the external backing store is valid. + CHECK_IMPLIES(byte_length != 0, data != nullptr); + CHECK_LE(byte_length, i::JSArrayBuffer::kMaxByteLength); + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + LOG_API(i_isolate, ArrayBuffer, New); + ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); + + std::shared_ptr<i::BackingStore> backing_store = LookupOrCreateBackingStore( + i_isolate, data, byte_length, i::SharedFlag::kNotShared, mode); + + i::Handle<i::JSArrayBuffer> obj = + i_isolate->factory()->NewJSArrayBuffer(std::move(backing_store)); + if (mode == ArrayBufferCreationMode::kExternalized) { + obj->set_is_external(true); + } + return Utils::ToLocal(obj); +} + Local<ArrayBuffer> v8::ArrayBuffer::New( Isolate* isolate, std::shared_ptr<BackingStore> backing_store) { CHECK_IMPLIES(backing_store->ByteLength() != 0, @@ -7730,6 +7923,119 @@ Local<DataView> DataView::New(Local<SharedArrayBuffer> shared_array_buffer, return Utils::ToLocal(obj); } +namespace { +i::Handle<i::JSArrayBuffer> SetupSharedArrayBuffer( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode) { + CHECK(i::FLAG_harmony_sharedarraybuffer); + // Embedders must guarantee that the external backing store is valid. + CHECK(byte_length == 0 || data != nullptr); + i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); + LOG_API(i_isolate, SharedArrayBuffer, New); + ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); + + std::shared_ptr<i::BackingStore> backing_store = LookupOrCreateBackingStore( + i_isolate, data, byte_length, i::SharedFlag::kShared, mode); + + i::Handle<i::JSArrayBuffer> obj = + i_isolate->factory()->NewJSSharedArrayBuffer(std::move(backing_store)); + + if (mode == ArrayBufferCreationMode::kExternalized) { + obj->set_is_external(true); + } + return obj; +} + +} // namespace + +bool v8::SharedArrayBuffer::IsExternal() const { + return Utils::OpenHandle(this)->is_external(); +} + +v8::SharedArrayBuffer::Contents::Contents( + void* data, size_t byte_length, void* allocation_base, + size_t allocation_length, Allocator::AllocationMode allocation_mode, + DeleterCallback deleter, void* deleter_data) + : data_(data), + byte_length_(byte_length), + allocation_base_(allocation_base), + allocation_length_(allocation_length), + allocation_mode_(allocation_mode), + deleter_(deleter), + deleter_data_(deleter_data) { + DCHECK_LE(allocation_base_, data_); + DCHECK_LE(byte_length_, allocation_length_); +} + +v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::Externalize() { + return GetContents(true); +} + +void v8::SharedArrayBuffer::Externalize( + const std::shared_ptr<BackingStore>& backing_store) { + i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this); + Utils::ApiCheck(!self->is_external(), "v8_SharedArrayBuffer_Externalize", + "SharedArrayBuffer already externalized"); + self->set_is_external(true); + + DCHECK_EQ(self->backing_store(), backing_store->Data()); +} + +v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::GetContents() { + return GetContents(false); +} + +v8::SharedArrayBuffer::Contents v8::SharedArrayBuffer::GetContents( + bool externalize) { + // TODO(titzer): reduce duplication between shared/unshared GetContents() + using BufferType = v8::SharedArrayBuffer; + + i::Handle<i::JSArrayBuffer> self = Utils::OpenHandle(this); + + std::shared_ptr<i::BackingStore> backing_store = self->GetBackingStore(); + + void* deleter_data = nullptr; + if (externalize) { + Utils::ApiCheck(!self->is_external(), "v8_SharedArrayBuffer_Externalize", + "SharedArrayBuffer already externalized"); + self->set_is_external(true); + // When externalizing, upref the shared pointer to the backing store + // and store that as the deleter data. When the embedder calls the deleter + // callback, we will delete the additional (on-heap) shared_ptr. + deleter_data = MakeDeleterData(backing_store); + } + + if (!backing_store) { + // If the array buffer has zero length or was detached, return empty + // contents. + DCHECK_EQ(0, self->byte_length()); + BufferType::Contents contents( + nullptr, 0, nullptr, 0, + v8::ArrayBuffer::Allocator::AllocationMode::kNormal, + BackingStoreDeleter, deleter_data); + return contents; + } + + // Backing stores that given to the embedder might be passed back through + // the API using only the start of the buffer. We need to find such + // backing stores using global registration until the API is changed. + i::GlobalBackingStoreRegistry::Register(backing_store); + + auto allocation_mode = + backing_store->is_wasm_memory() + ? v8::ArrayBuffer::Allocator::AllocationMode::kReservation + : v8::ArrayBuffer::Allocator::AllocationMode::kNormal; + + BufferType::Contents contents(backing_store->buffer_start(), // -- + backing_store->byte_length(), // -- + backing_store->buffer_start(), // -- + backing_store->byte_length(), // -- + allocation_mode, // -- + BackingStoreDeleter, // -- + deleter_data); + return contents; +} + size_t v8::SharedArrayBuffer::ByteLength() const { i::Handle<i::JSArrayBuffer> obj = Utils::OpenHandle(this); return obj->byte_length(); @@ -7758,6 +8064,14 @@ Local<SharedArrayBuffer> v8::SharedArrayBuffer::New(Isolate* isolate, } Local<SharedArrayBuffer> v8::SharedArrayBuffer::New( + Isolate* isolate, void* data, size_t byte_length, + ArrayBufferCreationMode mode) { + i::Handle<i::JSArrayBuffer> buffer = + SetupSharedArrayBuffer(isolate, data, byte_length, mode); + return Utils::ToLocalShared(buffer); +} + +Local<SharedArrayBuffer> v8::SharedArrayBuffer::New( Isolate* isolate, std::shared_ptr<BackingStore> backing_store) { CHECK(i::FLAG_harmony_sharedarraybuffer); CHECK_IMPLIES(backing_store->ByteLength() != 0, @@ -7774,6 +8088,14 @@ Local<SharedArrayBuffer> v8::SharedArrayBuffer::New( return Utils::ToLocalShared(obj); } +Local<SharedArrayBuffer> v8::SharedArrayBuffer::New( + Isolate* isolate, const SharedArrayBuffer::Contents& contents, + ArrayBufferCreationMode mode) { + i::Handle<i::JSArrayBuffer> buffer = SetupSharedArrayBuffer( + isolate, contents.Data(), contents.ByteLength(), mode); + return Utils::ToLocalShared(buffer); +} + std::unique_ptr<v8::BackingStore> v8::SharedArrayBuffer::NewBackingStore( Isolate* isolate, size_t byte_length) { i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); @@ -9070,7 +9392,7 @@ void v8::Isolate::LocaleConfigurationChangeNotification() { #endif // V8_INTL_SUPPORT } -bool v8::Object::IsCodeLike(v8::Isolate* isolate) const { +bool v8::Object::IsCodeLike(v8::Isolate* isolate) { i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); LOG_API(i_isolate, Object, IsCodeLike); ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate); @@ -9121,21 +9443,18 @@ MicrotasksScope::~MicrotasksScope() { #endif } -// static void MicrotasksScope::PerformCheckpoint(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); auto* microtask_queue = isolate->default_microtask_queue(); microtask_queue->PerformCheckpoint(v8_isolate); } -// static int MicrotasksScope::GetCurrentDepth(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); auto* microtask_queue = isolate->default_microtask_queue(); return microtask_queue->GetMicrotasksScopeDepth(); } -// static bool MicrotasksScope::IsRunningMicrotasks(Isolate* v8_isolate) { i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); auto* microtask_queue = isolate->default_microtask_queue(); diff --git a/deps/v8/src/objects/backing-store.cc b/deps/v8/src/objects/backing-store.cc index 08288ef62c0..7931fbf13dd 100644 --- a/deps/v8/src/objects/backing-store.cc +++ b/deps/v8/src/objects/backing-store.cc @@ -685,8 +685,17 @@ inline GlobalBackingStoreRegistryImpl* impl() { void GlobalBackingStoreRegistry::Register( std::shared_ptr<BackingStore> backing_store) { if (!backing_store || !backing_store->buffer_start()) return; - // Only wasm memory backing stores need to be registered globally. - CHECK(backing_store->is_wasm_memory()); + + if (!backing_store->free_on_destruct()) { + // If the backing store buffer is managed by the embedder, + // then we don't have to guarantee that there is single unique + // BackingStore per buffer_start() because the destructor of + // of the BackingStore will be a no-op in that case. + + // All Wasm memory has to be registered. + CHECK(!backing_store->is_wasm_memory()); + return; + } base::MutexGuard scope_lock(&impl()->mutex_); if (backing_store->globally_registered_) return; @@ -702,8 +711,6 @@ void GlobalBackingStoreRegistry::Register( void GlobalBackingStoreRegistry::Unregister(BackingStore* backing_store) { if (!backing_store->globally_registered_) return; - CHECK(backing_store->is_wasm_memory()); - DCHECK_NOT_NULL(backing_store->buffer_start()); base::MutexGuard scope_lock(&impl()->mutex_); @@ -715,6 +722,26 @@ void GlobalBackingStoreRegistry::Unregister(BackingStore* backing_store) { backing_store->globally_registered_ = false; } +std::shared_ptr<BackingStore> GlobalBackingStoreRegistry::Lookup( + void* buffer_start, size_t length) { + base::MutexGuard scope_lock(&impl()->mutex_); + TRACE_BS("BS:lookup mem=%p (%zu bytes)\n", buffer_start, length); + const auto& result = impl()->map_.find(buffer_start); + if (result == impl()->map_.end()) { + return std::shared_ptr<BackingStore>(); + } + auto backing_store = result->second.lock(); + CHECK_EQ(buffer_start, backing_store->buffer_start()); + if (backing_store->is_wasm_memory()) { + // Grow calls to shared WebAssembly threads can be triggered from different + // workers, length equality cannot be guaranteed here. + CHECK_LE(length, backing_store->byte_length()); + } else { + CHECK_EQ(length, backing_store->byte_length()); + } + return backing_store; +} + void GlobalBackingStoreRegistry::Purge(Isolate* isolate) { // We need to keep a reference to all backing stores that are inspected // in the purging loop below. Otherwise, we might get a deadlock @@ -728,7 +755,7 @@ void GlobalBackingStoreRegistry::Purge(Isolate* isolate) { auto backing_store = entry.second.lock(); prevent_destruction_under_lock.emplace_back(backing_store); if (!backing_store) continue; // skip entries where weak ptr is null - CHECK(backing_store->is_wasm_memory()); + if (!backing_store->is_wasm_memory()) continue; // skip non-wasm memory if (!backing_store->is_shared()) continue; // skip non-shared memory SharedWasmMemoryData* shared_data = backing_store->get_shared_wasm_memory_data(); diff --git a/deps/v8/src/objects/backing-store.h b/deps/v8/src/objects/backing-store.h index eb879d5e8ad..4d20109676e 100644 --- a/deps/v8/src/objects/backing-store.h +++ b/deps/v8/src/objects/backing-store.h @@ -219,16 +219,21 @@ class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase { #endif // V8_ENABLE_WEBASSEMBLY }; -// A global, per-process mapping from buffer addresses to backing stores -// of wasm memory objects. +// A global, per-process mapping from buffer addresses to backing stores. +// This is generally only used for dealing with an embedder that has not +// migrated to the new API which should use proper pointers to manage +// backing stores. class GlobalBackingStoreRegistry { public: // Register a backing store in the global registry. A mapping from the // {buffer_start} to the backing store object will be added. The backing // store will automatically unregister itself upon destruction. - // Only wasm memory backing stores are supported. static void Register(std::shared_ptr<BackingStore> backing_store); + // Look up a backing store based on the {buffer_start} pointer. + static std::shared_ptr<BackingStore> Lookup(void* buffer_start, + size_t length); + private: friend class BackingStore; // Unregister a backing store in the global registry. diff --git a/deps/v8/src/objects/string-inl.h b/deps/v8/src/objects/string-inl.h index 912109b2e0a..5db17a0b6cd 100644 --- a/deps/v8/src/objects/string-inl.h +++ b/deps/v8/src/objects/string-inl.h @@ -134,51 +134,49 @@ StringShape::StringShape(InstanceType t) : type_(static_cast<uint32_t>(t)) { DCHECK_EQ(type_ & kIsNotStringMask, kStringTag); } -bool StringShape::IsInternalized() const { +bool StringShape::IsInternalized() { DCHECK(valid()); STATIC_ASSERT(kNotInternalizedTag != 0); return (type_ & (kIsNotStringMask | kIsNotInternalizedMask)) == (kStringTag | kInternalizedTag); } -bool StringShape::IsCons() const { +bool StringShape::IsCons() { return (type_ & kStringRepresentationMask) == kConsStringTag; } -bool StringShape::IsThin() const { +bool StringShape::IsThin() { return (type_ & kStringRepresentationMask) == kThinStringTag; } -bool StringShape::IsSliced() const { +bool StringShape::IsSliced() { return (type_ & kStringRepresentationMask) == kSlicedStringTag; } -bool StringShape::IsIndirect() const { +bool StringShape::IsIndirect() { return (type_ & kIsIndirectStringMask) == kIsIndirectStringTag; } -bool StringShape::IsExternal() const { +bool StringShape::IsExternal() { return (type_ & kStringRepresentationMask) == kExternalStringTag; } -bool StringShape::IsSequential() const { +bool StringShape::IsSequential() { return (type_ & kStringRepresentationMask) == kSeqStringTag; } -bool StringShape::IsUncachedExternal() const { +bool StringShape::IsUncachedExternal() { return (type_ & kUncachedExternalStringMask) == kUncachedExternalStringTag; } -StringRepresentationTag StringShape::representation_tag() const { +StringRepresentationTag StringShape::representation_tag() { uint32_t tag = (type_ & kStringRepresentationMask); return static_cast<StringRepresentationTag>(tag); } -uint32_t StringShape::encoding_tag() const { - return type_ & kStringEncodingMask; -} +uint32_t StringShape::encoding_tag() { return type_ & kStringEncodingMask; } -uint32_t StringShape::full_representation_tag() const { +uint32_t StringShape::full_representation_tag() { return (type_ & (kStringRepresentationMask | kStringEncodingMask)); } @@ -188,15 +186,15 @@ STATIC_ASSERT((kStringRepresentationMask | kStringEncodingMask) == STATIC_ASSERT(static_cast<uint32_t>(kStringEncodingMask) == Internals::kStringEncodingMask); -bool StringShape::IsSequentialOneByte() const { +bool StringShape::IsSequentialOneByte() { return full_representation_tag() == (kSeqStringTag | kOneByteStringTag); } -bool StringShape::IsSequentialTwoByte() const { +bool StringShape::IsSequentialTwoByte() { return full_representation_tag() == (kSeqStringTag | kTwoByteStringTag); } -bool StringShape::IsExternalOneByte() const { +bool StringShape::IsExternalOneByte() { return full_representation_tag() == (kExternalStringTag | kOneByteStringTag); } @@ -205,7 +203,7 @@ STATIC_ASSERT((kExternalStringTag | kOneByteStringTag) == STATIC_ASSERT(v8::String::ONE_BYTE_ENCODING == kOneByteStringTag); -bool StringShape::IsExternalTwoByte() const { +bool StringShape::IsExternalTwoByte() { return full_representation_tag() == (kExternalStringTag | kTwoByteStringTag); } @@ -301,7 +299,7 @@ bool String::IsOneByteRepresentationUnderneath(String string) { } } -uc32 FlatStringReader::Get(int index) const { +uc32 FlatStringReader::Get(int index) { if (is_one_byte_) { return Get<uint8_t>(index); } else { @@ -310,7 +308,7 @@ uc32 FlatStringReader::Get(int index) const { } template <typename Char> -Char FlatStringReader::Get(int index) const { +Char FlatStringReader::Get(int index) { DCHECK_EQ(is_one_byte_, sizeof(Char) == 1); DCHECK(0 <= index && index < length_); if (sizeof(Char) == 1) { @@ -436,7 +434,7 @@ class SeqSubStringKey final : public StringTableKey { using SeqOneByteSubStringKey = SeqSubStringKey<SeqOneByteString>; using SeqTwoByteSubStringKey = SeqSubStringKey<SeqTwoByteString>; -bool String::Equals(String other) const { +bool String::Equals(String other) { if (other == *this) return true; if (this->IsInternalizedString() && other.IsInternalizedString()) { return false; @@ -444,7 +442,6 @@ bool String::Equals(String other) const { return SlowEquals(other); } -// static bool String::Equals(Isolate* isolate, Handle<String> one, Handle<String> two) { if (one.is_identical_to(two)) return true; if (one->IsInternalizedString() && two->IsInternalizedString()) { @@ -578,7 +575,7 @@ bool String::IsConsStringEqualToImpl( bool String::IsOneByteEqualTo(Vector<const char> str) { return IsEqualTo(str); } template <typename Char> -const Char* String::GetChars(const DisallowGarbageCollection& no_gc) const { +const Char* String::GetChars(const DisallowGarbageCollection& no_gc) { DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this)); return StringShape(*this).IsExternal() ? CharTraits<Char>::ExternalString::cast(*this).GetChars() @@ -588,7 +585,7 @@ const Char* String::GetChars(const DisallowGarbageCollection& no_gc) const { template <typename Char> const Char* String::GetChars( const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const { + const SharedStringAccessGuardIfNeeded& access_guard) { return StringShape(*this).IsExternal() ? CharTraits<Char>::ExternalString::cast(*this).GetChars() : CharTraits<Char>::String::cast(*this).GetChars(no_gc, @@ -619,17 +616,17 @@ Handle<String> String::Flatten(LocalIsolate* isolate, Handle<String> string, return string; } -uint16_t String::Get(int index, Isolate* isolate) const { +uint16_t String::Get(int index, Isolate* isolate) { DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this)); return GetImpl(index); } -uint16_t String::Get(int index, LocalIsolate* local_isolate) const { +uint16_t String::Get(int index, LocalIsolate* local_isolate) { SharedStringAccessGuardIfNeeded scope(local_isolate); return GetImpl(index); } -uint16_t String::GetImpl(int index) const { +uint16_t String::GetImpl(int index) { DCHECK(index >= 0 && index < length()); class StringGetDispatcher : public AllStatic { @@ -658,12 +655,12 @@ void String::Set(int index, uint16_t value) { : SeqTwoByteString::cast(*this).SeqTwoByteStringSet(index, value); } -bool String::IsFlat() const { +bool String::IsFlat() { if (!StringShape(*this).IsCons()) return true; return ConsString::cast(*this).second().length() == 0; } -String String::GetUnderlying() const { +String String::GetUnderlying() { // Giving direct access to underlying string only makes sense if the // wrapping string is already flattened. DCHECK(this->IsFlat()); @@ -767,7 +764,7 @@ uint32_t String::ToValidIndex(Object number) { return index; } -uint8_t SeqOneByteString::Get(int index) const { +uint8_t SeqOneByteString::Get(int index) { DCHECK(index >= 0 && index < length()); return ReadField<byte>(kHeaderSize + index * kCharSize); } @@ -777,12 +774,11 @@ void SeqOneByteString::SeqOneByteStringSet(int index, uint16_t value) { WriteField<byte>(kHeaderSize + index * kCharSize, static_cast<byte>(value)); } -Address SeqOneByteString::GetCharsAddress() const { +Address SeqOneByteString::GetCharsAddress() { return field_address(kHeaderSize); } -uint8_t* SeqOneByteString::GetChars( - const DisallowGarbageCollection& no_gc) const { +uint8_t* SeqOneByteString::GetChars(const DisallowGarbageCollection& no_gc) { USE(no_gc); DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this)); return reinterpret_cast<uint8_t*>(GetCharsAddress()); @@ -790,17 +786,17 @@ uint8_t* SeqOneByteString::GetChars( uint8_t* SeqOneByteString::GetChars( const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const { + const SharedStringAccessGuardIfNeeded& access_guard) { USE(no_gc); USE(access_guard); return reinterpret_cast<uint8_t*>(GetCharsAddress()); } -Address SeqTwoByteString::GetCharsAddress() const { +Address SeqTwoByteString::GetCharsAddress() { return field_address(kHeaderSize); } -uc16* SeqTwoByteString::GetChars(const DisallowGarbageCollection& no_gc) const { +uc16* SeqTwoByteString::GetChars(const DisallowGarbageCollection& no_gc) { USE(no_gc); DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(*this)); return reinterpret_cast<uc16*>(GetCharsAddress()); @@ -808,13 +804,13 @@ uc16* SeqTwoByteString::GetChars(const DisallowGarbageCollection& no_gc) const { uc16* SeqTwoByteString::GetChars( const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const { + const SharedStringAccessGuardIfNeeded& access_guard) { USE(no_gc); USE(access_guard); return reinterpret_cast<uc16*>(GetCharsAddress()); } -uint16_t SeqTwoByteString::Get(int index) const { +uint16_t SeqTwoByteString::Get(int index) { DCHECK(index >= 0 && index < length()); return ReadField<uint16_t>(kHeaderSize + index * kShortSize); } @@ -941,7 +937,7 @@ void ExternalOneByteString::set_resource( if (resource != nullptr) update_data_cache(isolate); } -const uint8_t* ExternalOneByteString::GetChars() const { +const uint8_t* ExternalOneByteString::GetChars() { DisallowGarbageCollection no_gc; if (is_uncached()) { if (resource()->IsCacheable()) { @@ -963,7 +959,7 @@ const uint8_t* ExternalOneByteString::GetChars() const { return reinterpret_cast<const uint8_t*>(resource()->data()); } -uint8_t ExternalOneByteString::Get(int index) const { +uint8_t ExternalOneByteString::Get(int index) { DCHECK(index >= 0 && index < length()); return GetChars()[index]; } @@ -1006,7 +1002,7 @@ void ExternalTwoByteString::set_resource( if (resource != nullptr) update_data_cache(isolate); } -const uint16_t* ExternalTwoByteString::GetChars() const { +const uint16_t* ExternalTwoByteString::GetChars() { DisallowGarbageCollection no_gc; if (is_uncached()) { if (resource()->IsCacheable()) { @@ -1028,7 +1024,7 @@ const uint16_t* ExternalTwoByteString::GetChars() const { return resource()->data(); } -uint16_t ExternalTwoByteString::Get(int index) const { +uint16_t ExternalTwoByteString::Get(int index) { DCHECK(index >= 0 && index < length()); return GetChars()[index]; } diff --git a/deps/v8/src/objects/string.cc b/deps/v8/src/objects/string.cc index ffa1be3aa34..2397f31f9d5 100644 --- a/deps/v8/src/objects/string.cc +++ b/deps/v8/src/objects/string.cc @@ -774,7 +774,7 @@ template Handle<FixedArray> String::CalculateLineEnds(LocalIsolate* isolate, Handle<String> src, bool include_ending_line); -bool String::SlowEquals(String other) const { +bool String::SlowEquals(String other) { DisallowGarbageCollection no_gc; // Fast check: negative check with lengths. int len = length(); @@ -826,7 +826,6 @@ bool String::SlowEquals(String other) const { return comparator.Equals(*this, other); } -// static bool String::SlowEquals(Isolate* isolate, Handle<String> one, Handle<String> two) { // Fast check: negative check with lengths. @@ -1442,7 +1441,7 @@ void SeqTwoByteString::clear_padding() { SizeFor(length()) - data_size); } -uint16_t ConsString::Get(int index) const { +uint16_t ConsString::Get(int index) { DCHECK(index >= 0 && index < this->length()); // Check for a flattened cons string @@ -1471,11 +1470,9 @@ uint16_t ConsString::Get(int index) const { UNREACHABLE(); } -uint16_t ThinString::Get(int index) const { return actual().Get(index); } +uint16_t ThinString::Get(int index) { return actual().Get(index); } -uint16_t SlicedString::Get(int index) const { - return parent().Get(offset() + index); -} +uint16_t SlicedString::Get(int index) { return parent().Get(offset() + index); } int ExternalString::ExternalPayloadSize() const { int length_multiplier = IsTwoByteRepresentation() ? i::kShortSize : kCharSize; diff --git a/deps/v8/src/objects/string.h b/deps/v8/src/objects/string.h index b8d47b5551f..683dc34f12d 100644 --- a/deps/v8/src/objects/string.h +++ b/deps/v8/src/objects/string.h @@ -44,25 +44,25 @@ class StringShape { inline explicit StringShape(const String s); inline explicit StringShape(Map s); inline explicit StringShape(InstanceType t); - inline bool IsSequential() const; - inline bool IsExternal() const; - inline bool IsCons() const; - inline bool IsSliced() const; - inline bool IsThin() const; - inline bool IsIndirect() const; - inline bool IsUncachedExternal() const; - inline bool IsExternalOneByte() const; - inline bool IsExternalTwoByte() const; - inline bool IsSequentialOneByte() const; - inline bool IsSequentialTwoByte() const; - inline bool IsInternalized() const; - inline StringRepresentationTag representation_tag() const; - inline uint32_t encoding_tag() const; - inline uint32_t full_representation_tag() const; + inline bool IsSequential(); + inline bool IsExternal(); + inline bool IsCons(); + inline bool IsSliced(); + inline bool IsThin(); + inline bool IsIndirect(); + inline bool IsUncachedExternal(); + inline bool IsExternalOneByte(); + inline bool IsExternalTwoByte(); + inline bool IsSequentialOneByte(); + inline bool IsSequentialTwoByte(); + inline bool IsInternalized(); + inline StringRepresentationTag representation_tag(); + inline uint32_t encoding_tag(); + inline uint32_t full_representation_tag(); #ifdef DEBUG - inline uint32_t type() const { return type_; } + inline uint32_t type() { return type_; } inline void invalidate() { valid_ = false; } - inline bool valid() const { return valid_; } + inline bool valid() { return valid_; } #else inline void invalidate() {} #endif @@ -181,13 +181,13 @@ class String : public TorqueGeneratedString<String, Name> { // SharedStringAccessGuard is not needed (i.e. on the main thread or on // read-only strings). template <typename Char> - inline const Char* GetChars(const DisallowGarbageCollection& no_gc) const; + inline const Char* GetChars(const DisallowGarbageCollection& no_gc); // Get chars from sequential or external strings. template <typename Char> inline const Char* GetChars( const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const; + const SharedStringAccessGuardIfNeeded& access_guard); // Returns the address of the character at an offset into this string. // Requires: this->IsFlat() @@ -217,8 +217,8 @@ class String : public TorqueGeneratedString<String, Name> { // to this method are not efficient unless the string is flat. // If it is called from a background thread, the LocalIsolate version should // be used. - V8_INLINE uint16_t Get(int index, Isolate* isolate = nullptr) const; - V8_INLINE uint16_t Get(int index, LocalIsolate* local_isolate) const; + V8_INLINE uint16_t Get(int index, Isolate* isolate = nullptr); + V8_INLINE uint16_t Get(int index, LocalIsolate* local_isolate); // ES6 section 7.1.3.1 ToNumber Applied to the String Type static Handle<Object> ToNumber(Isolate* isolate, Handle<String> subject); @@ -253,7 +253,7 @@ class String : public TorqueGeneratedString<String, Name> { // Returns the parent of a sliced string or first part of a flat cons string. // Requires: StringShape(this).IsIndirect() && this->IsFlat() - inline String GetUnderlying() const; + inline String GetUnderlying(); // String relational comparison, implemented according to ES6 section 7.2.11 // Abstract Relational Comparison (step 5): The comparison of Strings uses a @@ -314,7 +314,7 @@ class String : public TorqueGeneratedString<String, Name> { int start_index = 0); // String equality operations. - inline bool Equals(String other) const; + inline bool Equals(String other); inline static bool Equals(Isolate* isolate, Handle<String> one, Handle<String> two); @@ -419,7 +419,7 @@ class String : public TorqueGeneratedString<String, Name> { DECL_PRINTER(String) DECL_VERIFIER(String) - inline bool IsFlat() const; + inline bool IsFlat(); // Max char codes. static const int32_t kMaxOneByteCharCode = unibrow::Latin1::kMaxChar; @@ -541,7 +541,7 @@ class String : public TorqueGeneratedString<String, Name> { friend class InternalizedStringKey; // Implementation of the Get() public methods. Do not use directly. - V8_INLINE uint16_t GetImpl(int index) const; + V8_INLINE uint16_t GetImpl(int index); // Implementation of the IsEqualTo() public methods. Do not use directly. template <EqualityType kEqType, typename Char> @@ -561,7 +561,7 @@ class String : public TorqueGeneratedString<String, Name> { // Slow case of String::Equals. This implementation works on any strings // but it is most efficient on strings that are almost flat. - V8_EXPORT_PRIVATE bool SlowEquals(String other) const; + V8_EXPORT_PRIVATE bool SlowEquals(String other); V8_EXPORT_PRIVATE static bool SlowEquals(Isolate* isolate, Handle<String> one, Handle<String> two); @@ -633,21 +633,20 @@ class SeqOneByteString using Char = uint8_t; // Dispatched behavior. - inline uint8_t Get(int index) const; + inline uint8_t Get(int index); inline void SeqOneByteStringSet(int index, uint16_t value); // Get the address of the characters in this string. - inline Address GetCharsAddress() const; + inline Address GetCharsAddress(); // Get a pointer to the characters of the string. May only be called when a // SharedStringAccessGuard is not needed (i.e. on the main thread or on // read-only strings). - inline uint8_t* GetChars(const DisallowGarbageCollection& no_gc) const; + inline uint8_t* GetChars(const DisallowGarbageCollection& no_gc); // Get a pointer to the characters of the string. - inline uint8_t* GetChars( - const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const; + inline uint8_t* GetChars(const DisallowGarbageCollection& no_gc, + const SharedStringAccessGuardIfNeeded& access_guard); // Clear uninitialized padding space. This ensures that the snapshot content // is deterministic. @@ -679,21 +678,20 @@ class SeqTwoByteString using Char = uint16_t; // Dispatched behavior. - inline uint16_t Get(int index) const; + inline uint16_t Get(int index); inline void SeqTwoByteStringSet(int index, uint16_t value); // Get the address of the characters in this string. - inline Address GetCharsAddress() const; + inline Address GetCharsAddress(); // Get a pointer to the characters of the string. May only be called when a // SharedStringAccessGuard is not needed (i.e. on the main thread or on // read-only strings). - inline uc16* GetChars(const DisallowGarbageCollection& no_gc) const; + inline uc16* GetChars(const DisallowGarbageCollection& no_gc); // Get a pointer to the characters of the string. - inline uc16* GetChars( - const DisallowGarbageCollection& no_gc, - const SharedStringAccessGuardIfNeeded& access_guard) const; + inline uc16* GetChars(const DisallowGarbageCollection& no_gc, + const SharedStringAccessGuardIfNeeded& access_guard); // Clear uninitialized padding space. This ensures that the snapshot content // is deterministic. @@ -736,7 +734,7 @@ class ConsString : public TorqueGeneratedConsString<ConsString, String> { inline Object unchecked_second(); // Dispatched behavior. - V8_EXPORT_PRIVATE uint16_t Get(int index) const; + V8_EXPORT_PRIVATE uint16_t Get(int index); // Minimum length for a cons string. static const int kMinLength = 13; @@ -759,7 +757,7 @@ class ThinString : public TorqueGeneratedThinString<ThinString, String> { public: DECL_GETTER(unchecked_actual, HeapObject) - V8_EXPORT_PRIVATE uint16_t Get(int index) const; + V8_EXPORT_PRIVATE uint16_t Get(int index); DECL_VERIFIER(ThinString) @@ -785,7 +783,7 @@ class SlicedString : public TorqueGeneratedSlicedString<SlicedString, String> { inline void set_parent(String parent, WriteBarrierMode mode = UPDATE_WRITE_BARRIER); // Dispatched behavior. - V8_EXPORT_PRIVATE uint16_t Get(int index) const; + V8_EXPORT_PRIVATE uint16_t Get(int index); // Minimum length for a sliced string. static const int kMinLength = 13; @@ -864,10 +862,10 @@ class ExternalOneByteString : public ExternalString { // which the pointer cache has to be refreshed. inline void update_data_cache(Isolate* isolate); - inline const uint8_t* GetChars() const; + inline const uint8_t* GetChars(); // Dispatched behavior. - inline uint8_t Get(int index) const; + inline uint8_t Get(int index); DECL_CAST(ExternalOneByteString) @@ -910,10 +908,10 @@ class ExternalTwoByteString : public ExternalString { // which the pointer cache has to be refreshed. inline void update_data_cache(Isolate* isolate); - inline const uint16_t* GetChars() const; + inline const uint16_t* GetChars(); // Dispatched behavior. - inline uint16_t Get(int index) const; + inline uint16_t Get(int index); // For regexp code. inline const uint16_t* ExternalTwoByteStringGetData(unsigned start); @@ -943,9 +941,9 @@ class V8_EXPORT_PRIVATE FlatStringReader : public Relocatable { public: FlatStringReader(Isolate* isolate, Handle<String> str); void PostGarbageCollection() override; - inline uc32 Get(int index) const; + inline uc32 Get(int index); template <typename Char> - inline Char Get(int index) const; + inline Char Get(int index); int length() { return length_; } private: diff --git a/deps/v8/src/wasm/module-compiler.cc b/deps/v8/src/wasm/module-compiler.cc index 3b1d8750bac..d45c7385251 100644 --- a/deps/v8/src/wasm/module-compiler.cc +++ b/deps/v8/src/wasm/module-compiler.cc @@ -3342,8 +3342,6 @@ void CompilationStateImpl::WaitForCompilationEvent( return done_->load(std::memory_order_relaxed); } - bool IsJoiningThread() const override { return true; } - void NotifyConcurrencyIncrease() override { UNIMPLEMENTED(); } uint8_t GetTaskId() override { return kMainTaskId; } diff --git a/deps/v8/test/cctest/test-api-array-buffer.cc b/deps/v8/test/cctest/test-api-array-buffer.cc index 9875098d1fb..86044edcde4 100644 --- a/deps/v8/test/cctest/test-api-array-buffer.cc +++ b/deps/v8/test/cctest/test-api-array-buffer.cc @@ -47,11 +47,31 @@ Local<TypedArray> CreateAndCheck(Local<v8::ArrayBuffer> ab, int byteOffset, std::shared_ptr<v8::BackingStore> Externalize(Local<v8::ArrayBuffer> ab) { std::shared_ptr<v8::BackingStore> backing_store = ab->GetBackingStore(); + // Keep the tests until the deprecated functions are removed. +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + ab->Externalize(backing_store); + CHECK(ab->IsExternal()); +#if __clang__ +#pragma clang diagnostic pop +#endif return backing_store; } std::shared_ptr<v8::BackingStore> Externalize(Local<v8::SharedArrayBuffer> ab) { std::shared_ptr<v8::BackingStore> backing_store = ab->GetBackingStore(); + // Keep the tests until the deprecated functions are removed. +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + ab->Externalize(backing_store); + CHECK(ab->IsExternal()); +#if __clang__ +#pragma clang diagnostic pop +#endif return backing_store; } @@ -130,6 +150,46 @@ THREADED_TEST(ArrayBuffer_JSInternalToExternal) { CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); } +THREADED_TEST(ArrayBuffer_External) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + i::ScopedVector<uint8_t> my_data(100); + memset(my_data.begin(), 0, 100); + // Keep the tests until the deprecated functions are removed. +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + Local<v8::ArrayBuffer> ab3 = + v8::ArrayBuffer::New(isolate, my_data.begin(), 100); + CheckInternalFieldsAreZero(ab3); + CHECK_EQ(100, ab3->ByteLength()); + CHECK(ab3->IsExternal()); +#if __clang__ +#pragma clang diagnostic pop +#endif + + CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust()); + + v8::Local<v8::Value> result = CompileRun("ab3.byteLength"); + CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); + + result = CompileRun( + "var u8_b = new Uint8Array(ab3);" + "u8_b[0] = 0xBB;" + "u8_b[1] = 0xCC;" + "u8_b.length"); + CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); + 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(env.local()).FromJust()); +} + THREADED_TEST(ArrayBuffer_DisableDetach) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); @@ -233,6 +293,37 @@ THREADED_TEST(ArrayBuffer_DetachingScript) { CheckDataViewIsDetached(dv); } +// TODO(v8:9380) the Contents data structure should be deprecated. +THREADED_TEST(ArrayBuffer_AllocationInformation) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + const size_t ab_size = 1024; + Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, ab_size); +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + v8::ArrayBuffer::Contents contents(ab->GetContents()); +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + + // Array buffers should have normal allocation mode. + CHECK_EQ(contents.AllocationMode(), + v8::ArrayBuffer::Allocator::AllocationMode::kNormal); + // The allocation must contain the buffer (normally they will be equal, but + // this is not required by the contract). + CHECK_NOT_NULL(contents.AllocationBase()); + const uintptr_t alloc = + reinterpret_cast<uintptr_t>(contents.AllocationBase()); + const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data()); + CHECK_LE(alloc, data); + CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength()); +} + THREADED_TEST(ArrayBuffer_ExternalizeEmpty) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); @@ -251,6 +342,7 @@ THREADED_TEST(ArrayBuffer_ExternalizeEmpty) { // marked as is_external or not. USE(u8a->Buffer()); + CHECK(ab->IsExternal()); CHECK_EQ(2, backing_store->ByteLength()); } @@ -289,6 +381,35 @@ THREADED_TEST(SharedArrayBuffer_ApiInternalToExternal) { CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); } +THREADED_TEST(ArrayBuffer_ExternalReused) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + i::ScopedVector<uint8_t> data(100); + Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(isolate, data.begin(), 100); + std::shared_ptr<v8::BackingStore> bs1 = ab1->GetBackingStore(); + ab1->Detach(); + Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, data.begin(), 100); + std::shared_ptr<v8::BackingStore> bs2 = ab2->GetBackingStore(); + CHECK_EQ(bs1->Data(), bs2->Data()); +} + +THREADED_TEST(SharedArrayBuffer_ExternalReused) { + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + i::ScopedVector<uint8_t> data(100); + Local<v8::SharedArrayBuffer> ab1 = + v8::SharedArrayBuffer::New(isolate, data.begin(), 100); + std::shared_ptr<v8::BackingStore> bs1 = ab1->GetBackingStore(); + Local<v8::SharedArrayBuffer> ab2 = + v8::SharedArrayBuffer::New(isolate, data.begin(), 100); + std::shared_ptr<v8::BackingStore> bs2 = ab2->GetBackingStore(); + CHECK_EQ(bs1->Data(), bs2->Data()); +} + THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { i::FLAG_harmony_sharedarraybuffer = true; LocalContext env; @@ -330,6 +451,64 @@ THREADED_TEST(SharedArrayBuffer_JSInternalToExternal) { CHECK_EQ(0xDD, result->Int32Value(env.local()).FromJust()); } +THREADED_TEST(SharedArrayBuffer_External) { + i::FLAG_harmony_sharedarraybuffer = true; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + i::ScopedVector<uint8_t> my_data(100); + memset(my_data.begin(), 0, 100); + Local<v8::SharedArrayBuffer> ab3 = + v8::SharedArrayBuffer::New(isolate, my_data.begin(), 100); + CheckInternalFieldsAreZero(ab3); + CHECK_EQ(100, static_cast<int>(ab3->ByteLength())); + CHECK(ab3->IsExternal()); + + CHECK(env->Global()->Set(env.local(), v8_str("ab3"), ab3).FromJust()); + + v8::Local<v8::Value> result = CompileRun("ab3.byteLength"); + CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); + + result = CompileRun( + "var u8_b = new Uint8Array(ab3);" + "u8_b[0] = 0xBB;" + "u8_b[1] = 0xCC;" + "u8_b.length"); + CHECK_EQ(100, result->Int32Value(env.local()).FromJust()); + 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(env.local()).FromJust()); +} + +// TODO(v8:9380) the Contents data structure should be deprecated. +THREADED_TEST(SharedArrayBuffer_AllocationInformation) { + i::FLAG_harmony_sharedarraybuffer = true; + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + const size_t ab_size = 1024; + Local<v8::SharedArrayBuffer> ab = + v8::SharedArrayBuffer::New(isolate, ab_size); + v8::SharedArrayBuffer::Contents contents(ab->GetContents()); + + // Array buffers should have normal allocation mode. + CHECK_EQ(contents.AllocationMode(), + v8::ArrayBuffer::Allocator::AllocationMode::kNormal); + // The allocation must contain the buffer (normally they will be equal, but + // this is not required by the contract). + CHECK_NOT_NULL(contents.AllocationBase()); + const uintptr_t alloc = + reinterpret_cast<uintptr_t>(contents.AllocationBase()); + const uintptr_t data = reinterpret_cast<uintptr_t>(contents.Data()); + CHECK_LE(alloc, data); + CHECK_LE(data + contents.ByteLength(), alloc + contents.AllocationLength()); +} + THREADED_TEST(SkipArrayBufferBackingStoreDuringGC) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); @@ -337,12 +516,9 @@ THREADED_TEST(SkipArrayBufferBackingStoreDuringGC) { // Make sure the pointer looks like a heap object uint8_t* store_ptr = reinterpret_cast<uint8_t*>(i::kHeapObjectTag); - auto backing_store = v8::ArrayBuffer::NewBackingStore( - store_ptr, 8, [](void*, size_t, void*) {}, nullptr); // Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store - Local<v8::ArrayBuffer> ab = - v8::ArrayBuffer::New(isolate, std::move(backing_store)); + Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8); // Should not crash CcTest::CollectGarbage(i::NEW_SPACE); // in survivor space now @@ -363,15 +539,12 @@ THREADED_TEST(SkipArrayBufferDuringScavenge) { Local<v8::Object> tmp = v8::Object::New(isolate); uint8_t* store_ptr = reinterpret_cast<uint8_t*>(*reinterpret_cast<uintptr_t*>(*tmp)); - auto backing_store = v8::ArrayBuffer::NewBackingStore( - store_ptr, 8, [](void*, size_t, void*) {}, nullptr); // Make `store_ptr` point to from space CcTest::CollectGarbage(i::NEW_SPACE); // Create ArrayBuffer with pointer-that-cannot-be-visited in the backing store - Local<v8::ArrayBuffer> ab = - v8::ArrayBuffer::New(isolate, std::move(backing_store)); + Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, store_ptr, 8); // Should not crash, // i.e. backing store pointer should not be treated as a heap object pointer diff --git a/deps/v8/test/cctest/test-typedarrays.cc b/deps/v8/test/cctest/test-typedarrays.cc index 0134befedd2..867d0f90b95 100644 --- a/deps/v8/test/cctest/test-typedarrays.cc +++ b/deps/v8/test/cctest/test-typedarrays.cc @@ -71,6 +71,30 @@ TEST(CopyContentsView) { TestArrayBufferViewContents(&env, true); } + +TEST(AllocateNotExternal) { + LocalContext env; + v8::HandleScope scope(env->GetIsolate()); + void* memory = reinterpret_cast<Isolate*>(env->GetIsolate()) + ->array_buffer_allocator() + ->Allocate(1024); + +// Keep the test until the functions are removed. +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated" +#endif + v8::Local<v8::ArrayBuffer> buffer = + v8::ArrayBuffer::New(env->GetIsolate(), memory, 1024, + v8::ArrayBufferCreationMode::kInternalized); + CHECK(!buffer->IsExternal()); +#if __clang__ +#pragma clang diagnostic pop +#endif + + CHECK_EQ(memory, buffer->GetBackingStore()->Data()); +} + void TestSpeciesProtector(char* code, bool invalidates_species_protector = true) { v8::Isolate::CreateParams create_params; |