/src/serenity/Userland/Libraries/LibJS/Runtime/FunctionObject.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/TypeCasts.h> |
9 | | #include <LibJS/Runtime/Completion.h> |
10 | | #include <LibJS/Runtime/FunctionObject.h> |
11 | | #include <LibJS/Runtime/NativeFunction.h> |
12 | | #include <LibJS/Runtime/VM.h> |
13 | | |
14 | | namespace JS { |
15 | | |
16 | | FunctionObject::FunctionObject(Realm& realm, Object* prototype, MayInterfereWithIndexedPropertyAccess may_interfere_with_indexed_property_access) |
17 | 0 | : Object(realm, prototype, may_interfere_with_indexed_property_access) |
18 | 0 | { |
19 | 0 | } |
20 | | |
21 | | FunctionObject::FunctionObject(Object& prototype, MayInterfereWithIndexedPropertyAccess may_interfere_with_indexed_property_access) |
22 | 0 | : Object(ConstructWithPrototypeTag::Tag, prototype, may_interfere_with_indexed_property_access) |
23 | 0 | { |
24 | 0 | } |
25 | | |
26 | | // 10.2.9 SetFunctionName ( F, name [ , prefix ] ), https://tc39.es/ecma262/#sec-setfunctionname |
27 | | void FunctionObject::set_function_name(Variant<PropertyKey, PrivateName> const& name_arg, Optional<StringView> const& prefix) |
28 | 0 | { |
29 | 0 | auto& vm = this->vm(); |
30 | | |
31 | | // 1. Assert: F is an extensible object that does not have a "name" own property. |
32 | 0 | VERIFY(m_is_extensible); |
33 | 0 | VERIFY(!storage_has(vm.names.name)); |
34 | | |
35 | 0 | ByteString name; |
36 | | |
37 | | // 2. If Type(name) is Symbol, then |
38 | 0 | if (auto const* property_key = name_arg.get_pointer<PropertyKey>(); property_key && property_key->is_symbol()) { |
39 | | // a. Let description be name's [[Description]] value. |
40 | 0 | auto const& description = property_key->as_symbol()->description(); |
41 | | |
42 | | // b. If description is undefined, set name to the empty String. |
43 | 0 | if (!description.has_value()) |
44 | 0 | name = ByteString::empty(); |
45 | | // c. Else, set name to the string-concatenation of "[", description, and "]". |
46 | 0 | else |
47 | 0 | name = ByteString::formatted("[{}]", *description); |
48 | 0 | } |
49 | | // 3. Else if name is a Private Name, then |
50 | 0 | else if (auto const* private_name = name_arg.get_pointer<PrivateName>()) { |
51 | | // a. Set name to name.[[Description]]. |
52 | 0 | name = private_name->description; |
53 | 0 | } |
54 | | // NOTE: This is necessary as we use a different parameter name. |
55 | 0 | else { |
56 | 0 | name = name_arg.get<PropertyKey>().to_string(); |
57 | 0 | } |
58 | | |
59 | | // 4. If F has an [[InitialName]] internal slot, then |
60 | 0 | if (is<NativeFunction>(this)) { |
61 | | // a. Set F.[[InitialName]] to name. |
62 | 0 | static_cast<NativeFunction&>(*this).set_initial_name({}, name); |
63 | 0 | } |
64 | | |
65 | | // 5. If prefix is present, then |
66 | 0 | if (prefix.has_value()) { |
67 | | // a. Set name to the string-concatenation of prefix, the code unit 0x0020 (SPACE), and name. |
68 | 0 | name = ByteString::formatted("{} {}", *prefix, name); |
69 | | |
70 | | // b. If F has an [[InitialName]] internal slot, then |
71 | 0 | if (is<NativeFunction>(this)) { |
72 | | // i. Optionally, set F.[[InitialName]] to name. |
73 | 0 | static_cast<NativeFunction&>(*this).set_initial_name({}, name); |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | // 6. Perform ! DefinePropertyOrThrow(F, "name", PropertyDescriptor { [[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). |
78 | 0 | MUST(define_property_or_throw(vm.names.name, PropertyDescriptor { .value = PrimitiveString::create(vm, move(name)), .writable = false, .enumerable = false, .configurable = true })); |
79 | | |
80 | | // 7. Return unused. |
81 | 0 | } |
82 | | |
83 | | // 10.2.10 SetFunctionLength ( F, length ), https://tc39.es/ecma262/#sec-setfunctionlength |
84 | | void FunctionObject::set_function_length(double length) |
85 | 0 | { |
86 | 0 | auto& vm = this->vm(); |
87 | | |
88 | | // "length (a non-negative integer or +∞)" |
89 | 0 | VERIFY(trunc(length) == length || __builtin_isinf_sign(length) == 1); |
90 | | |
91 | | // 1. Assert: F is an extensible object that does not have a "length" own property. |
92 | 0 | VERIFY(m_is_extensible); |
93 | 0 | VERIFY(!storage_has(vm.names.length)); |
94 | | |
95 | | // 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). |
96 | 0 | MUST(define_property_or_throw(vm.names.length, PropertyDescriptor { .value = Value { length }, .writable = false, .enumerable = false, .configurable = true })); |
97 | | |
98 | | // 3. Return unused. |
99 | 0 | } |
100 | | |
101 | | } |