Skip to content

Commit 5495678

Browse files
mlippautzV8 LUCI CQ
authored andcommitted
[api] Expose new ways to wrap C++ objects with JS wrapper objects
JS wrapper objects are generally constructed via ObjectTemplate. Such objects can now be constructed with 0 embedder fields and use the newly introduced Wrap/Unwrap methods to set and get the corresponding C++ pointers. Internally, these will use a CppHeapPointer field and dedicated external pointer table. Bug: chromium:328117814 Change-Id: I9cb79a86ccdca8d185eaf79efffb16051dd75dc0 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5401858 Reviewed-by: Samuel Groß <[email protected]> Reviewed-by: Toon Verwaest <[email protected]> Commit-Queue: Michael Lippautz <[email protected]> Cr-Commit-Position: refs/heads/main@{#93129}
1 parent 636b17b commit 5495678

File tree

11 files changed

+292
-27
lines changed

11 files changed

+292
-27
lines changed

BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ filegroup(
653653
"include/v8-promise.h",
654654
"include/v8-proxy.h",
655655
"include/v8-regexp.h",
656+
"include/v8-sandbox.h",
656657
"include/v8-script.h",
657658
"include/v8-snapshot.h",
658659
"include/v8-statistics.h",

BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3081,6 +3081,7 @@ v8_header_set("v8_headers") {
30813081
"include/v8-promise.h",
30823082
"include/v8-proxy.h",
30833083
"include/v8-regexp.h",
3084+
"include/v8-sandbox.h",
30843085
"include/v8-script.h",
30853086
"include/v8-snapshot.h",
30863087
"include/v8-statistics.h",

include/v8-object.h

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
#ifndef INCLUDE_V8_OBJECT_H_
66
#define INCLUDE_V8_OBJECT_H_
77

8+
#include "v8-internal.h" // NOLINT(build/include_directory)
89
#include "v8-local-handle.h" // NOLINT(build/include_directory)
910
#include "v8-maybe.h" // NOLINT(build/include_directory)
1011
#include "v8-persistent-handle.h" // NOLINT(build/include_directory)
1112
#include "v8-primitive.h" // NOLINT(build/include_directory)
13+
#include "v8-sandbox.h" // NOLINT(build/include_directory)
1214
#include "v8-traced-handle.h" // NOLINT(build/include_directory)
1315
#include "v8-value.h" // NOLINT(build/include_directory)
1416
#include "v8config.h" // NOLINT(build/include_directory)
@@ -528,7 +530,50 @@ class V8_EXPORT Object : public Value {
528530
void* values[]);
529531

530532
/**
531-
* HasOwnProperty() is like JavaScript's Object.prototype.hasOwnProperty().
533+
* Unwraps a JS wrapper object.
534+
*
535+
* \param tag The tag for retrieving the wrappable instance. Must match the
536+
* tag that has been used for a previous `Wrap()` operation.
537+
* \param isolate The Isolate for the `wrapper` object.
538+
* \param wrapper The JS wrapper object that should be unwrapped.
539+
* \returns the C++ wrappable instance, or nullptr if the JS object has never
540+
* been wrapped.
541+
*/
542+
template <CppHeapPointerTag tag, typename T = void>
543+
static V8_INLINE T* Unwrap(v8::Isolate* isolate,
544+
const v8::Local<v8::Object>& wrapper);
545+
template <CppHeapPointerTag tag, typename T = void>
546+
static V8_INLINE T* Unwrap(v8::Isolate* isolate,
547+
const PersistentBase<Object>& wrapper);
548+
template <CppHeapPointerTag tag, typename T = void>
549+
static V8_INLINE T* Unwrap(v8::Isolate* isolate,
550+
const BasicTracedReference<Object>& wrapper);
551+
552+
/**
553+
* Wraps a JS wrapper with a C++ instance.
554+
*
555+
* \param tag The pointer tag that should be used for storing this object.
556+
* Future `Unwrap()` operations must provide a matching tag.
557+
* \param isolate The Isolate for the `wrapper` object.
558+
* \param wrapper The JS wrapper object.
559+
* \param wrappable The C++ object instance that is wrapped by the JS object.
560+
*/
561+
template <CppHeapPointerTag tag>
562+
static V8_INLINE void Wrap(v8::Isolate* isolate,
563+
const v8::Local<v8::Object>& wrapper,
564+
void* wrappable);
565+
template <CppHeapPointerTag tag>
566+
static V8_INLINE void Wrap(v8::Isolate* isolate,
567+
const PersistentBase<Object>& wrapper,
568+
void* wrappable);
569+
template <CppHeapPointerTag tag>
570+
static V8_INLINE void Wrap(v8::Isolate* isolate,
571+
const BasicTracedReference<Object>& wrapper,
572+
void* wrappable);
573+
574+
/**
575+
* HasOwnProperty() is like JavaScript's
576+
* Object.prototype.hasOwnProperty().
532577
*
533578
* See also v8::Object::Has() and v8::Object::HasRealNamedProperty().
534579
*/
@@ -731,6 +776,11 @@ class V8_EXPORT Object : public Value {
731776
bool IsCodeLike(Isolate* isolate) const;
732777

733778
private:
779+
static void* Unwrap(v8::Isolate* isolate, internal::Address wrapper_obj,
780+
CppHeapPointerTag tag);
781+
static void Wrap(v8::Isolate* isolate, internal::Address wrapper_obj,
782+
CppHeapPointerTag tag, void* wrappable);
783+
734784
Object();
735785
static void CheckCast(Value* obj);
736786
Local<Data> SlowGetInternalField(int index);
@@ -810,6 +860,73 @@ void* Object::GetAlignedPointerFromInternalField(int index) {
810860
return SlowGetAlignedPointerFromInternalField(index);
811861
}
812862

863+
// static
864+
template <CppHeapPointerTag tag, typename T>
865+
T* Object::Unwrap(v8::Isolate* isolate, const v8::Local<v8::Object>& wrapper) {
866+
auto obj = internal::ValueHelper::ValueAsAddress(*wrapper);
867+
#if !defined(V8_ENABLE_CHECKS)
868+
return internal::ReadCppHeapPointerField<tag, T>(
869+
isolate, obj, internal::Internals::kJSObjectHeaderSize);
870+
#else // defined(V8_ENABLE_CHECKS)
871+
return reinterpret_cast<T*>(Unwrap(isolate, obj, tag));
872+
#endif // defined(V8_ENABLE_CHECKS)
873+
}
874+
875+
// static
876+
template <CppHeapPointerTag tag, typename T>
877+
T* Object::Unwrap(v8::Isolate* isolate, const PersistentBase<Object>& wrapper) {
878+
auto obj =
879+
internal::ValueHelper::ValueAsAddress(wrapper.template value<Object>());
880+
#if !defined(V8_ENABLE_CHECKS)
881+
return internal::ReadCppHeapPointerField<tag, T>(
882+
isolate, obj, internal::Internals::kJSObjectHeaderSize);
883+
#else // defined(V8_ENABLE_CHECKS)
884+
885+
return reinterpret_cast<T*>(Unwrap(isolate, obj, tag));
886+
#endif // defined(V8_ENABLE_CHECKS)
887+
}
888+
889+
// static
890+
template <CppHeapPointerTag tag, typename T>
891+
T* Object::Unwrap(v8::Isolate* isolate,
892+
const BasicTracedReference<Object>& wrapper) {
893+
auto obj =
894+
internal::ValueHelper::ValueAsAddress(wrapper.template value<Object>());
895+
#if !defined(V8_ENABLE_CHECKS)
896+
return internal::ReadCppHeapPointerField<tag, T>(
897+
isolate, obj, internal::Internals::kJSObjectHeaderSize);
898+
#else // defined(V8_ENABLE_CHECKS)
899+
return reinterpret_cast<T*>(Unwrap(isolate, obj, tag));
900+
#endif // defined(V8_ENABLE_CHECKS)
901+
}
902+
903+
// static
904+
template <CppHeapPointerTag tag>
905+
void Object::Wrap(v8::Isolate* isolate, const v8::Local<v8::Object>& wrapper,
906+
void* wrappable) {
907+
auto obj = internal::ValueHelper::ValueAsAddress(*wrapper);
908+
Wrap(isolate, obj, tag, wrappable);
909+
}
910+
911+
// static
912+
template <CppHeapPointerTag tag>
913+
void Object::Wrap(v8::Isolate* isolate, const PersistentBase<Object>& wrapper,
914+
void* wrappable) {
915+
auto obj =
916+
internal::ValueHelper::ValueAsAddress(wrapper.template value<Object>());
917+
Wrap(isolate, obj, tag, wrappable);
918+
}
919+
920+
// static
921+
template <CppHeapPointerTag tag>
922+
void Object::Wrap(v8::Isolate* isolate,
923+
const BasicTracedReference<Object>& wrapper,
924+
void* wrappable) {
925+
auto obj =
926+
internal::ValueHelper::ValueAsAddress(wrapper.template value<Object>());
927+
Wrap(isolate, obj, tag, wrappable);
928+
}
929+
813930
Private* Private::Cast(Data* data) {
814931
#ifdef V8_ENABLE_CHECKS
815932
CheckCast(data);

include/v8-sandbox.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2024 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef INCLUDE_V8_SANDBOX_H_
6+
#define INCLUDE_V8_SANDBOX_H_
7+
8+
#include <cstdint>
9+
10+
#include "v8-internal.h" // NOLINT(build/include_directory)
11+
#include "v8config.h" // NOLINT(build/include_directory)
12+
13+
namespace v8 {
14+
15+
/**
16+
* A pointer tag used for wrapping and unwrapping `CppHeap` pointers as used
17+
* with JS API wrapper objects that rely on `v8::Object::Wrap()` and
18+
* `v8::Object::Unwrap()`.
19+
*/
20+
enum class CppHeapPointerTag : uint64_t {
21+
kDefaultTag = internal::ExternalPointerTag::kExternalObjectValueTag,
22+
};
23+
24+
namespace internal {
25+
26+
#ifdef V8_COMPRESS_POINTERS
27+
V8_INLINE static Address* GetCppHeapPointerTableBase(v8::Isolate* isolate) {
28+
Address addr = reinterpret_cast<Address>(isolate) +
29+
Internals::kIsolateCppHeapPointerTableAddressOffset +
30+
Internals::kExternalPointerTableBasePointerOffset;
31+
return *reinterpret_cast<Address**>(addr);
32+
}
33+
#endif // V8_COMPRESS_POINTERS
34+
35+
template <CppHeapPointerTag tag, typename T>
36+
V8_INLINE static T* ReadCppHeapPointerField(v8::Isolate* isolate,
37+
Address heap_object_ptr,
38+
int offset) {
39+
#ifdef V8_COMPRESS_POINTERS
40+
static_assert(tag != static_cast<CppHeapPointerTag>(kExternalPointerNullTag));
41+
// See src/sandbox/external-pointer-table-inl.h. Logic duplicated here so
42+
// it can be inlined and doesn't require an additional call.
43+
const CppHeapPointerHandle handle =
44+
Internals::ReadRawField<CppHeapPointerHandle>(heap_object_ptr, offset);
45+
if (handle == 0) {
46+
return reinterpret_cast<T*>(kNullAddress);
47+
}
48+
const uint32_t index = handle >> kExternalPointerIndexShift;
49+
const Address* table = GetCppHeapPointerTableBase(isolate);
50+
const std::atomic<Address>* ptr =
51+
reinterpret_cast<const std::atomic<Address>*>(&table[index]);
52+
Address entry = std::atomic_load_explicit(ptr, std::memory_order_relaxed);
53+
return reinterpret_cast<T*>(entry & ~static_cast<uint64_t>(tag));
54+
#else // !V8_COMPRESS_POINTERS
55+
return reinterpret_cast<T*>(
56+
Internals::ReadRawField<Address>(heap_object_ptr, offset));
57+
#endif // !V8_COMPRESS_POINTERS
58+
}
59+
60+
} // namespace internal
61+
} // namespace v8
62+
63+
#endif // INCLUDE_V8_SANDBOX_H_

src/api/api.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6298,6 +6298,24 @@ void v8::Object::SetAlignedPointerInInternalFields(int argc, int indices[],
62986298
values);
62996299
}
63006300

6301+
// static
6302+
void* v8::Object::Unwrap(v8::Isolate* isolate, i::Address wrapper_obj,
6303+
CppHeapPointerTag tag) {
6304+
return i::JSApiWrapper(i::JSObject::cast(i::Tagged<i::Object>(
6305+
reinterpret_cast<i::Address>(wrapper_obj))))
6306+
.GetCppHeapWrappable(reinterpret_cast<i::Isolate*>(isolate),
6307+
static_cast<i::ExternalPointerTag>(tag));
6308+
}
6309+
6310+
// static
6311+
void v8::Object::Wrap(v8::Isolate* isolate, i::Address wrapper_obj,
6312+
CppHeapPointerTag tag, void* wrappable) {
6313+
return i::JSApiWrapper(i::JSObject::cast(i::Tagged<i::Object>(
6314+
reinterpret_cast<i::Address>(wrapper_obj))))
6315+
.SetCppHeapWrappable(reinterpret_cast<i::Isolate*>(isolate), wrappable,
6316+
static_cast<i::ExternalPointerTag>(tag));
6317+
}
6318+
63016319
// --- E n v i r o n m e n t ---
63026320

63036321
void v8::V8::InitializePlatform(Platform* platform) {

src/objects/heap-object.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ class HeapObject : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> {
311311
template <ExternalPointerTag tag>
312312
inline Address TryReadCppHeapPointerField(
313313
size_t offset, IsolateForPointerCompression isolate) const;
314+
inline Address TryReadCppHeapPointerField(
315+
size_t offset, IsolateForPointerCompression isolate,
316+
ExternalPointerTag tag) const;
314317
template <ExternalPointerTag tag>
315318
inline void WriteExternalPointerField(size_t offset,
316319
IsolateForSandbox isolate,
@@ -326,6 +329,9 @@ class HeapObject : public TaggedImpl<HeapObjectReferenceType::STRONG, Address> {
326329
template <ExternalPointerTag tag>
327330
inline void WriteLazilyInitializedCppHeapPointerField(
328331
size_t offset, IsolateForPointerCompression isolate, Address value);
332+
inline void WriteLazilyInitializedCppHeapPointerField(
333+
size_t offset, IsolateForPointerCompression isolate, Address value,
334+
ExternalPointerTag tag);
329335

330336
//
331337
// Indirect pointers.

src/objects/js-objects-inl.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,12 @@ void* JSApiWrapper::GetCppHeapWrappable(
632632
kCppHeapWrappableOffset, isolate));
633633
}
634634

635+
void* JSApiWrapper::GetCppHeapWrappable(IsolateForPointerCompression isolate,
636+
ExternalPointerTag tag) const {
637+
return reinterpret_cast<void*>(object_->TryReadCppHeapPointerField(
638+
kCppHeapWrappableOffset, isolate, tag));
639+
}
640+
635641
template <ExternalPointerTag tag>
636642
void JSApiWrapper::SetCppHeapWrappable(IsolateForPointerCompression isolate,
637643
void* instance) {
@@ -646,6 +652,19 @@ void JSApiWrapper::SetCppHeapWrappable(IsolateForPointerCompression isolate,
646652
WriteBarrier::MarkingFromCppHeapWrappable(object_, instance);
647653
}
648654

655+
void JSApiWrapper::SetCppHeapWrappable(IsolateForPointerCompression isolate,
656+
void* instance, ExternalPointerTag tag) {
657+
if (instance == nullptr) {
658+
object_->ResetLazilyInitializedCppHeapPointerField(
659+
JSAPIObjectWithEmbedderSlots::kCppHeapWrappableOffset);
660+
return;
661+
}
662+
object_->WriteLazilyInitializedCppHeapPointerField(
663+
JSAPIObjectWithEmbedderSlots::kCppHeapWrappableOffset, isolate,
664+
reinterpret_cast<Address>(instance), tag);
665+
WriteBarrier::MarkingFromCppHeapWrappable(object_, instance);
666+
}
667+
649668
bool JSMessageObject::DidEnsureSourcePositionsAvailable() const {
650669
return shared_info() == Smi::zero();
651670
}

src/objects/js-objects.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,9 +1028,13 @@ class JSApiWrapper {
10281028
template <ExternalPointerTag tag>
10291029
V8_INLINE void SetCppHeapWrappable(IsolateForPointerCompression isolate,
10301030
void*);
1031+
V8_INLINE void SetCppHeapWrappable(IsolateForPointerCompression isolate,
1032+
void*, ExternalPointerTag tag);
10311033
template <ExternalPointerTag tag>
10321034
V8_INLINE void* GetCppHeapWrappable(
10331035
IsolateForPointerCompression isolate) const;
1036+
V8_INLINE void* GetCppHeapWrappable(IsolateForPointerCompression isolate,
1037+
ExternalPointerTag tag) const;
10341038

10351039
private:
10361040
static_assert(JSAPIObjectWithEmbedderSlots::kCppHeapWrappableOffset ==

src/objects/objects-inl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,12 @@ Address HeapObject::TryReadCppHeapPointerField(
792792
return i::TryReadCppHeapPointerField<tag>(field_address(offset), isolate);
793793
}
794794

795+
Address HeapObject::TryReadCppHeapPointerField(
796+
size_t offset, IsolateForPointerCompression isolate,
797+
ExternalPointerTag tag) const {
798+
return i::TryReadCppHeapPointerField(field_address(offset), isolate, tag);
799+
}
800+
795801
template <ExternalPointerTag tag>
796802
void HeapObject::WriteExternalPointerField(size_t offset,
797803
IsolateForSandbox isolate,
@@ -821,6 +827,13 @@ void HeapObject::WriteLazilyInitializedCppHeapPointerField(
821827
isolate, value);
822828
}
823829

830+
void HeapObject::WriteLazilyInitializedCppHeapPointerField(
831+
size_t offset, IsolateForPointerCompression isolate, Address value,
832+
ExternalPointerTag tag) {
833+
i::WriteLazilyInitializedCppHeapPointerField(field_address(offset), isolate,
834+
value, tag);
835+
}
836+
824837
void HeapObject::InitSelfIndirectPointerField(size_t offset,
825838
IsolateForSandbox isolate) {
826839
DCHECK(IsExposedTrustedObject(*this));

0 commit comments

Comments
 (0)