blob: b8d43229742b06d2f5367225c0cf94b5a52f6519 [file] [log] [blame]
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <unordered_set>
#include "src/builtins/builtins-utils-inl.h"
#include "src/objects/js-struct-inl.h"
#include "src/objects/property-details.h"
namespace v8 {
namespace internal {
constexpr int kMaxJSStructFields = 999;
// Note: For Wasm structs, we currently allow 2000 fields, because there was
// specific demand for that. Ideally we'd have the same limit, but JS structs
// rely on DescriptorArrays and are hence limited to 1020 fields at most.
static_assert(kMaxJSStructFields <= kMaxNumberOfDescriptors);
namespace {
struct NameHandleHasher {
size_t operator()(IndirectHandle<Name> name) const { return name->hash(); }
};
struct UniqueNameHandleEqual {
bool operator()(IndirectHandle<Name> x, IndirectHandle<Name> y) const {
DCHECK(IsUniqueName(*x));
DCHECK(IsUniqueName(*y));
return *x == *y;
}
};
using UniqueNameHandleSet =
std::unordered_set<IndirectHandle<Name>, NameHandleHasher,
UniqueNameHandleEqual>;
} // namespace
BUILTIN(SharedSpaceJSObjectHasInstance) {
HandleScope scope(isolate);
DirectHandle<Object> constructor = args.receiver();
if (!IsJSFunction(*constructor)) {
return *isolate->factory()->false_value();
}
bool result;
MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
AlwaysSharedSpaceJSObject::HasInstance(isolate,
Cast<JSFunction>(constructor),
args.atOrUndefined(isolate, 1)));
return *isolate->factory()->ToBoolean(result);
}
namespace {
Maybe<bool> CollectFieldsAndElements(Isolate* isolate,
DirectHandle<JSReceiver> property_names,
int num_properties,
DirectHandleVector<Name>& field_names,
std::set<uint32_t>& element_names) {
Handle<Object> raw_property_name;
Handle<Name> property_name;
UniqueNameHandleSet field_names_set;
for (int i = 0; i < num_properties; i++) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, raw_property_name,
JSReceiver::GetElement(isolate, property_names, i), Nothing<bool>());
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, property_name,
Object::ToName(isolate, raw_property_name),
Nothing<bool>());
bool is_duplicate;
size_t index;
if (!property_name->AsIntegerIndex(&index) ||
index > JSObject::kMaxElementIndex) {
property_name = isolate->factory()->InternalizeName(property_name);
// TODO(v8:12547): Support Symbols?
if (IsSymbol(*property_name)) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate, NewTypeError(MessageTemplate::kSymbolToString),
Nothing<bool>());
}
is_duplicate = !field_names_set.insert(property_name).second;
// Keep the field names in the original order.
if (!is_duplicate) field_names.push_back(property_name);
} else {
is_duplicate = !element_names.insert(static_cast<uint32_t>(index)).second;
}
if (is_duplicate) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewTypeError(MessageTemplate::kDuplicateTemplateProperty,
property_name),
Nothing<bool>());
}
}
return Just(true);
}
} // namespace
BUILTIN(SharedStructTypeConstructor) {
DCHECK(v8_flags.shared_string_table);
HandleScope scope(isolate);
auto* factory = isolate->factory();
DirectHandle<Map> instance_map;
{
// Step 1: Collect the struct's property names and create the instance map.
DirectHandle<JSReceiver> property_names_arg;
if (!IsJSReceiver(*args.atOrUndefined(isolate, 1))) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kArgumentIsNonObject,
factory->NewStringFromAsciiChecked("property names")));
}
property_names_arg = args.at<JSReceiver>(1);
// Treat property_names_arg as arraylike.
DirectHandle<Object> raw_length_number;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, raw_length_number,
Object::GetLengthFromArrayLike(isolate, property_names_arg));
double num_properties_double = Object::NumberValue(*raw_length_number);
if (num_properties_double < 0 ||
num_properties_double > kMaxJSStructFields) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kStructFieldCountOutOfRange));
}
int num_properties = static_cast<int>(num_properties_double);
DirectHandleVector<Name> field_names(isolate);
std::set<uint32_t> element_names;
if (num_properties != 0) {
MAYBE_RETURN(
CollectFieldsAndElements(isolate, property_names_arg, num_properties,
field_names, element_names),
ReadOnlyRoots(isolate).exception());
}
if (IsUndefined(*args.atOrUndefined(isolate, 2), isolate)) {
// Create a new instance map if this type isn't registered.
instance_map = JSSharedStruct::CreateInstanceMap(
isolate, base::VectorOf(field_names), element_names, {});
} else {
// Otherwise, get the canonical map.
if (!IsString(*args.atOrUndefined(isolate, 2))) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kArgumentIsNonString,
factory->NewStringFromAsciiChecked(
"type registry key")));
}
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, instance_map,
isolate->shared_struct_type_registry()->Register(
isolate, args.at<String>(2), base::VectorOf(field_names),
element_names));
}
}
// Step 2: Creat the JSFunction constructor. This is always created anew,
// regardless of whether the type is registered.
DirectHandle<SharedFunctionInfo> info =
isolate->factory()->NewSharedFunctionInfoForBuiltin(
isolate->factory()->empty_string(), Builtin::kSharedStructConstructor,
0, kAdapt);
DirectHandle<JSFunction> constructor =
Factory::JSFunctionBuilder{isolate, info, isolate->native_context()}
.set_map(isolate->strict_function_with_readonly_prototype_map())
.Build();
constructor->set_prototype_or_initial_map(*instance_map, kReleaseStore);
JSObject::AddProperty(
isolate, constructor, factory->has_instance_symbol(),
direct_handle(
isolate->native_context()->shared_space_js_object_has_instance(),
isolate),
ALL_ATTRIBUTES_MASK);
return *constructor;
}
BUILTIN(SharedStructConstructor) {
HandleScope scope(isolate);
DirectHandle<JSFunction> constructor(args.target());
DirectHandle<Map> instance_map(constructor->initial_map(), isolate);
return *isolate->factory()->NewJSSharedStruct(
args.target(),
JSSharedStruct::GetElementsTemplate(isolate, *instance_map));
}
BUILTIN(SharedStructTypeIsSharedStruct) {
HandleScope scope(isolate);
return isolate->heap()->ToBoolean(
IsJSSharedStruct(*args.atOrUndefined(isolate, 1)));
}
BUILTIN(AtomicsMutexIsMutex) {
HandleScope scope(isolate);
return isolate->heap()->ToBoolean(
IsJSAtomicsMutex(*args.atOrUndefined(isolate, 1)));
}
BUILTIN(AtomicsConditionIsCondition) {
HandleScope scope(isolate);
return isolate->heap()->ToBoolean(
IsJSAtomicsCondition(*args.atOrUndefined(isolate, 1)));
}
} // namespace internal
} // namespace v8