1// Copyright 2022 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#include "src/builtins/builtins-utils-inl.h"
6#include "src/objects/js-struct-inl.h"
7
8namespace v8 {
9namespace internal {
10
11constexpr int kMaxJSStructFields = 999;
12
13#ifdef V8_ENABLE_WEBASSEMBLY
14#include "src/wasm/wasm-limits.h"
15static_assert(wasm::kV8MaxWasmStructFields == kMaxJSStructFields,
16              "Max number of fields should be the same for both JS and "
17              "WebAssembly structs");
18#endif  // V8_ENABLE_WEBASSEMBLY
19
20BUILTIN(SharedStructTypeConstructor) {
21  DCHECK(FLAG_shared_string_table);
22
23  HandleScope scope(isolate);
24  static const char method_name[] = "SharedStructType";
25  auto* factory = isolate->factory();
26
27  Handle<JSReceiver> field_names_arg;
28  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
29      isolate, field_names_arg,
30      Object::ToObject(isolate, args.atOrUndefined(isolate, 1), method_name));
31
32  // Treat field_names_arg as arraylike.
33  Handle<Object> raw_length_number;
34  ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
35      isolate, raw_length_number,
36      Object::GetLengthFromArrayLike(isolate, field_names_arg));
37  double num_properties_double = raw_length_number->Number();
38  if (num_properties_double < 0 || num_properties_double > kMaxJSStructFields) {
39    THROW_NEW_ERROR_RETURN_FAILURE(
40        isolate, NewRangeError(MessageTemplate::kStructFieldCountOutOfRange));
41  }
42  int num_properties = static_cast<int>(num_properties_double);
43
44  Handle<DescriptorArray> descriptors = factory->NewDescriptorArray(
45      num_properties, 0, AllocationType::kSharedOld);
46
47  // Build up the descriptor array.
48  for (int i = 0; i < num_properties; ++i) {
49    Handle<Object> raw_field_name;
50    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
51        isolate, raw_field_name,
52        JSReceiver::GetElement(isolate, field_names_arg, i));
53    Handle<Name> field_name;
54    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, field_name,
55                                       Object::ToName(isolate, raw_field_name));
56    field_name = factory->InternalizeName(field_name);
57
58    // Shared structs' fields need to be aligned, so make it all tagged.
59    PropertyDetails details(
60        PropertyKind::kData, SEALED, PropertyLocation::kField,
61        PropertyConstness::kMutable, Representation::Tagged(), i);
62    descriptors->Set(InternalIndex(i), *field_name,
63                     MaybeObject::FromObject(FieldType::Any()), details);
64  }
65  descriptors->Sort();
66
67  Handle<SharedFunctionInfo> info =
68      isolate->factory()->NewSharedFunctionInfoForBuiltin(
69          isolate->factory()->empty_string(), Builtin::kSharedStructConstructor,
70          FunctionKind::kNormalFunction);
71  info->set_internal_formal_parameter_count(JSParameterCount(0));
72  info->set_length(0);
73
74  Handle<JSFunction> constructor =
75      Factory::JSFunctionBuilder{isolate, info, isolate->native_context()}
76          .set_map(isolate->strict_function_map())
77          .Build();
78
79  int instance_size;
80  int in_object_properties;
81  JSFunction::CalculateInstanceSizeHelper(JS_SHARED_STRUCT_TYPE, false, 0,
82                                          num_properties, &instance_size,
83                                          &in_object_properties);
84  Handle<Map> instance_map = factory->NewMap(
85      JS_SHARED_STRUCT_TYPE, instance_size, TERMINAL_FAST_ELEMENTS_KIND,
86      in_object_properties, AllocationType::kSharedMap);
87
88  instance_map->InitializeDescriptors(isolate, *descriptors);
89  // Structs have fixed layout ahead of time, so there's no slack.
90  instance_map->SetInObjectUnusedPropertyFields(0);
91  instance_map->set_is_extensible(false);
92  JSFunction::SetInitialMap(isolate, constructor, instance_map,
93                            factory->null_value());
94
95  // The constructor is not a shared object, so the shared map should not point
96  // to it.
97  instance_map->set_constructor_or_back_pointer(*factory->null_value());
98
99  return *constructor;
100}
101
102BUILTIN(SharedStructConstructor) {
103  HandleScope scope(isolate);
104  auto* factory = isolate->factory();
105
106  Handle<JSObject> instance =
107      factory->NewJSObject(args.target(), AllocationType::kSharedOld);
108
109  Handle<Map> instance_map(instance->map(), isolate);
110  if (instance_map->HasOutOfObjectProperties()) {
111    int num_oob_fields =
112        instance_map->NumberOfFields(ConcurrencyMode::kSynchronous) -
113        instance_map->GetInObjectProperties();
114    Handle<PropertyArray> property_array =
115        factory->NewPropertyArray(num_oob_fields, AllocationType::kSharedOld);
116    instance->SetProperties(*property_array);
117  }
118
119  return *instance;
120}
121
122}  // namespace internal
123}  // namespace v8
124