[object] Set/Get JSFunction::prototype_or_initial_map atomically
Maps set on the JSFunction were done so in a non-atomic way, which meant that we were failing to have a synchronization point and the read/writes could be reordered. This started happening after a previous CL[1] moved some methods from relaxed to non-atomic, which triggered TSAN (see v8:11696). [1]: https://chromium-review.googlesource.com/c/v8/v8/+/2843359 Bug: v8:7790, v8:11696 Change-Id: I8472ff8b63d391376ee2f1dcf0a8b4fd7cecfcd1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2851893 Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org> Cr-Commit-Position: refs/heads/master@{#74357}
This commit is contained in:
parent
e75736aed4
commit
799fa7b0a8
@ -3614,7 +3614,8 @@ Handle<JSFunction> Factory::JSFunctionBuilder::BuildRaw(Handle<Code> code) {
|
||||
function.set_code(*code, kReleaseStore, mode);
|
||||
if (function.has_prototype_slot()) {
|
||||
function.set_prototype_or_initial_map(
|
||||
ReadOnlyRoots(isolate).the_hole_value(), SKIP_WRITE_BARRIER);
|
||||
ReadOnlyRoots(isolate).the_hole_value(), kReleaseStore,
|
||||
SKIP_WRITE_BARRIER);
|
||||
}
|
||||
|
||||
// Potentially body initialization.
|
||||
|
@ -1638,8 +1638,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
JSFunction::kSizeWithPrototype, 0, prototype,
|
||||
Builtins::kFunctionConstructor);
|
||||
// Function instances are sloppy by default.
|
||||
function_fun->set_prototype_or_initial_map(
|
||||
*isolate_->sloppy_function_map());
|
||||
function_fun->set_prototype_or_initial_map(*isolate_->sloppy_function_map(),
|
||||
kReleaseStore);
|
||||
function_fun->shared().DontAdaptArguments();
|
||||
function_fun->shared().set_length(1);
|
||||
InstallWithIntrinsicDefaultProto(isolate_, function_fun,
|
||||
@ -4168,7 +4168,7 @@ void Genesis::InitializeIteratorFunctions() {
|
||||
JSFunction::kSizeWithPrototype, 0, generator_function_prototype,
|
||||
Builtins::kGeneratorFunctionConstructor);
|
||||
generator_function_function->set_prototype_or_initial_map(
|
||||
native_context->generator_function_map());
|
||||
native_context->generator_function_map(), kReleaseStore);
|
||||
generator_function_function->shared().DontAdaptArguments();
|
||||
generator_function_function->shared().set_length(1);
|
||||
InstallWithIntrinsicDefaultProto(
|
||||
@ -4197,7 +4197,7 @@ void Genesis::InitializeIteratorFunctions() {
|
||||
JSFunction::kSizeWithPrototype, 0, async_generator_function_prototype,
|
||||
Builtins::kAsyncGeneratorFunctionConstructor);
|
||||
async_generator_function_function->set_prototype_or_initial_map(
|
||||
native_context->async_generator_function_map());
|
||||
native_context->async_generator_function_map(), kReleaseStore);
|
||||
async_generator_function_function->shared().DontAdaptArguments();
|
||||
async_generator_function_function->shared().set_length(1);
|
||||
InstallWithIntrinsicDefaultProto(
|
||||
@ -4298,7 +4298,7 @@ void Genesis::InitializeIteratorFunctions() {
|
||||
JSFunction::kSizeWithPrototype, 0, async_function_prototype,
|
||||
Builtins::kAsyncFunctionConstructor);
|
||||
async_function_constructor->set_prototype_or_initial_map(
|
||||
native_context->async_function_map());
|
||||
native_context->async_function_map(), kReleaseStore);
|
||||
async_function_constructor->shared().DontAdaptArguments();
|
||||
async_function_constructor->shared().set_length(1);
|
||||
native_context->set_async_function_constructor(*async_function_constructor);
|
||||
|
@ -206,27 +206,28 @@ void JSFunction::set_context(HeapObject value, WriteBarrierMode mode) {
|
||||
CONDITIONAL_WRITE_BARRIER(*this, kContextOffset, value, mode);
|
||||
}
|
||||
|
||||
ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map, HeapObject,
|
||||
kPrototypeOrInitialMapOffset, map().has_prototype_slot())
|
||||
RELEASE_ACQUIRE_ACCESSORS_CHECKED(JSFunction, prototype_or_initial_map,
|
||||
HeapObject, kPrototypeOrInitialMapOffset,
|
||||
map().has_prototype_slot())
|
||||
|
||||
DEF_GETTER(JSFunction, has_prototype_slot, bool) {
|
||||
return map(cage_base).has_prototype_slot();
|
||||
}
|
||||
|
||||
DEF_GETTER(JSFunction, initial_map, Map) {
|
||||
return Map::cast(prototype_or_initial_map(cage_base));
|
||||
return Map::cast(prototype_or_initial_map(cage_base, kAcquireLoad));
|
||||
}
|
||||
|
||||
DEF_GETTER(JSFunction, has_initial_map, bool) {
|
||||
DCHECK(has_prototype_slot(cage_base));
|
||||
return prototype_or_initial_map(cage_base).IsMap(cage_base);
|
||||
return prototype_or_initial_map(cage_base, kAcquireLoad).IsMap(cage_base);
|
||||
}
|
||||
|
||||
DEF_GETTER(JSFunction, has_instance_prototype, bool) {
|
||||
DCHECK(has_prototype_slot(cage_base));
|
||||
return has_initial_map(cage_base) ||
|
||||
!prototype_or_initial_map(cage_base).IsTheHole(
|
||||
GetReadOnlyRoots(cage_base));
|
||||
!prototype_or_initial_map(cage_base, kAcquireLoad)
|
||||
.IsTheHole(GetReadOnlyRoots(cage_base));
|
||||
}
|
||||
|
||||
DEF_GETTER(JSFunction, has_prototype, bool) {
|
||||
@ -251,7 +252,7 @@ DEF_GETTER(JSFunction, instance_prototype, HeapObject) {
|
||||
return initial_map(cage_base).prototype(cage_base);
|
||||
// When there is no initial map and the prototype is a JSReceiver, the
|
||||
// initial map field is used for the prototype field.
|
||||
return HeapObject::cast(prototype_or_initial_map(cage_base));
|
||||
return HeapObject::cast(prototype_or_initial_map(cage_base, kAcquireLoad));
|
||||
}
|
||||
|
||||
DEF_GETTER(JSFunction, prototype, Object) {
|
||||
|
@ -400,7 +400,7 @@ void SetInstancePrototype(Isolate* isolate, Handle<JSFunction> function,
|
||||
// Put the value in the initial map field until an initial map is needed.
|
||||
// At that point, a new initial map is created and the prototype is put
|
||||
// into the initial map where it belongs.
|
||||
function->set_prototype_or_initial_map(*value);
|
||||
function->set_prototype_or_initial_map(*value, kReleaseStore);
|
||||
} else {
|
||||
Handle<Map> new_map =
|
||||
Map::Copy(isolate, initial_map, "SetInstancePrototype");
|
||||
@ -425,7 +425,7 @@ void SetInstancePrototype(Isolate* isolate, Handle<JSFunction> function,
|
||||
// Put the value in the initial map field until an initial map is
|
||||
// needed. At that point, a new initial map is created and the
|
||||
// prototype is put into the initial map where it belongs.
|
||||
function->set_prototype_or_initial_map(*value);
|
||||
function->set_prototype_or_initial_map(*value, kReleaseStore);
|
||||
if (value->IsJSObject()) {
|
||||
// Optimize as prototype to detach it from its transition tree.
|
||||
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
|
||||
@ -488,7 +488,7 @@ void JSFunction::SetInitialMap(Isolate* isolate, Handle<JSFunction> function,
|
||||
Map::SetPrototype(isolate, map, prototype);
|
||||
}
|
||||
map->SetConstructor(*constructor);
|
||||
function->set_prototype_or_initial_map(*map);
|
||||
function->set_prototype_or_initial_map(*map, kReleaseStore);
|
||||
if (FLAG_log_maps) {
|
||||
LOG(isolate, MapEvent("InitialMap", Handle<Map>(), map, "",
|
||||
SharedFunctionInfo::DebugName(
|
||||
|
@ -55,7 +55,7 @@ class JSBoundFunction
|
||||
class JSFunction : public JSFunctionOrBoundFunction {
|
||||
public:
|
||||
// [prototype_or_initial_map]:
|
||||
DECL_ACCESSORS(prototype_or_initial_map, HeapObject)
|
||||
DECL_RELEASE_ACQUIRE_ACCESSORS(prototype_or_initial_map, HeapObject)
|
||||
|
||||
// [shared]: The information about the function that
|
||||
// can be shared by instances.
|
||||
|
@ -889,7 +889,7 @@ void V8HeapExplorer::ExtractJSObjectReferences(HeapEntry* entry,
|
||||
} else if (obj.IsJSFunction()) {
|
||||
JSFunction js_fun = JSFunction::cast(js_obj);
|
||||
if (js_fun.has_prototype_slot()) {
|
||||
Object proto_or_map = js_fun.prototype_or_initial_map();
|
||||
Object proto_or_map = js_fun.prototype_or_initial_map(kAcquireLoad);
|
||||
if (!proto_or_map.IsTheHole(isolate)) {
|
||||
if (!proto_or_map.IsMap()) {
|
||||
SetPropertyReference(entry, roots.prototype_string(), proto_or_map,
|
||||
|
@ -497,7 +497,7 @@ bool InitClassPrototype(Isolate* isolate,
|
||||
map = Map::CopyDropDescriptors(isolate, map);
|
||||
map->set_is_prototype_map(true);
|
||||
Map::SetPrototype(isolate, map, prototype_parent);
|
||||
constructor->set_prototype_or_initial_map(*prototype);
|
||||
constructor->set_prototype_or_initial_map(*prototype, kReleaseStore);
|
||||
map->SetConstructor(*constructor);
|
||||
Handle<FixedArray> computed_properties(
|
||||
class_boilerplate->instance_computed_properties(), isolate);
|
||||
|
@ -3070,7 +3070,8 @@ TEST(NewPromiseCapability) {
|
||||
|
||||
CHECK(result->promise().IsJSObject());
|
||||
Handle<JSObject> promise(JSObject::cast(result->promise()), isolate);
|
||||
CHECK_EQ(constructor_fn->prototype_or_initial_map(), promise->map());
|
||||
CHECK_EQ(constructor_fn->prototype_or_initial_map(kAcquireLoad),
|
||||
promise->map());
|
||||
CHECK(result->resolve().IsJSFunction());
|
||||
CHECK(result->reject().IsJSFunction());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user