Serenity Operating System
at master 222 lines 9.4 kB view raw
1/* 2 * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibJS/Runtime/DeclarativeEnvironment.h> 8#include <LibJS/Runtime/Error.h> 9#include <LibJS/Runtime/GlobalObject.h> 10#include <LibJS/Runtime/Reference.h> 11 12namespace JS { 13 14// 6.2.4.6 PutValue ( V, W ), https://tc39.es/ecma262/#sec-putvalue 15ThrowCompletionOr<void> Reference::put_value(VM& vm, Value value) 16{ 17 // 1. ReturnIfAbrupt(V). 18 // 2. ReturnIfAbrupt(W). 19 20 // 3. If V is not a Reference Record, throw a ReferenceError exception. 21 if (!is_valid_reference()) 22 return vm.throw_completion<ReferenceError>(ErrorType::InvalidLeftHandAssignment); 23 24 // 4. If IsUnresolvableReference(V) is true, then 25 if (is_unresolvable()) { 26 // a. If V.[[Strict]] is true, throw a ReferenceError exception. 27 if (m_strict) 28 return throw_reference_error(vm); 29 30 // b. Let globalObj be GetGlobalObject(). 31 auto& global_object = vm.get_global_object(); 32 33 // c. Perform ? Set(globalObj, V.[[ReferencedName]], W, false). 34 TRY(global_object.set(m_name, value, Object::ShouldThrowExceptions::No)); 35 36 // Return unused. 37 return {}; 38 } 39 40 // 5. If IsPropertyReference(V) is true, then 41 if (is_property_reference()) { 42 // a. Let baseObj be ? ToObject(V.[[Base]]). 43 auto* base_obj = TRY(m_base_value.to_object(vm)); 44 45 // b. If IsPrivateReference(V) is true, then 46 if (is_private_reference()) { 47 // i. Return ? PrivateSet(baseObj, V.[[ReferencedName]], W). 48 return base_obj->private_set(m_private_name, value); 49 } 50 51 // c. Let succeeded be ? baseObj.[[Set]](V.[[ReferencedName]], W, GetThisValue(V)). 52 auto succeeded = TRY(base_obj->internal_set(m_name, value, get_this_value())); 53 54 // d. If succeeded is false and V.[[Strict]] is true, throw a TypeError exception. 55 if (!succeeded && m_strict) 56 return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishSetProperty, m_name, TRY_OR_THROW_OOM(vm, m_base_value.to_string_without_side_effects())); 57 58 // e. Return unused. 59 return {}; 60 } 61 62 // 6. Else, 63 // a. Let base be V.[[Base]]. 64 65 // b. Assert: base is an Environment Record. 66 VERIFY(m_base_type == BaseType::Environment); 67 VERIFY(m_base_environment); 68 69 // c. Return ? base.SetMutableBinding(V.[[ReferencedName]], W, V.[[Strict]]) (see 9.1). 70 if (m_environment_coordinate.has_value() && m_environment_coordinate->index != EnvironmentCoordinate::global_marker) 71 return static_cast<DeclarativeEnvironment*>(m_base_environment)->set_mutable_binding_direct(vm, m_environment_coordinate->index, value, m_strict); 72 else 73 return m_base_environment->set_mutable_binding(vm, m_name.as_string(), value, m_strict); 74} 75 76Completion Reference::throw_reference_error(VM& vm) const 77{ 78 if (!m_name.is_valid()) 79 return vm.throw_completion<ReferenceError>(ErrorType::ReferenceUnresolvable); 80 else 81 return vm.throw_completion<ReferenceError>(ErrorType::UnknownIdentifier, m_name.to_string_or_symbol().to_display_string()); 82} 83 84// 6.2.4.5 GetValue ( V ), https://tc39.es/ecma262/#sec-getvalue 85ThrowCompletionOr<Value> Reference::get_value(VM& vm) const 86{ 87 auto& realm = *vm.current_realm(); 88 89 // 1. ReturnIfAbrupt(V). 90 // 2. If V is not a Reference Record, return V. 91 92 // 3. If IsUnresolvableReference(V) is true, throw a ReferenceError exception. 93 if (!is_valid_reference() || is_unresolvable()) 94 return throw_reference_error(vm); 95 96 // 4. If IsPropertyReference(V) is true, then 97 if (is_property_reference()) { 98 // a. Let baseObj be ? ToObject(V.[[Base]]). 99 // NOTE: Deferred as an optimization; we might not actually need to create an object. 100 101 // b. If IsPrivateReference(V) is true, then 102 if (is_private_reference()) { 103 // FIXME: We need to be able to specify the receiver for this 104 // if we want to use it in error messages in future 105 // as things currently stand this does the "wrong thing" but 106 // the error is unobservable 107 108 auto* base_obj = TRY(m_base_value.to_object(vm)); 109 110 // i. Return ? PrivateGet(baseObj, V.[[ReferencedName]]). 111 return base_obj->private_get(m_private_name); 112 } 113 114 // OPTIMIZATION: For various primitives we can avoid actually creating a new object for them. 115 Object* base_obj = nullptr; 116 if (m_base_value.is_string()) { 117 auto string_value = TRY(m_base_value.as_string().get(vm, m_name)); 118 if (string_value.has_value()) 119 return *string_value; 120 base_obj = realm.intrinsics().string_prototype(); 121 } else if (m_base_value.is_number()) 122 base_obj = realm.intrinsics().number_prototype(); 123 else if (m_base_value.is_boolean()) 124 base_obj = realm.intrinsics().boolean_prototype(); 125 else 126 base_obj = TRY(m_base_value.to_object(vm)); 127 128 // c. Return ? baseObj.[[Get]](V.[[ReferencedName]], GetThisValue(V)). 129 return base_obj->internal_get(m_name, get_this_value()); 130 } 131 132 // 5. Else, 133 // a. Let base be V.[[Base]]. 134 135 // b. Assert: base is an Environment Record. 136 VERIFY(m_base_type == BaseType::Environment); 137 VERIFY(m_base_environment); 138 139 // c. Return ? base.GetBindingValue(V.[[ReferencedName]], V.[[Strict]]) (see 9.1). 140 if (m_environment_coordinate.has_value() && m_environment_coordinate->index != EnvironmentCoordinate::global_marker) 141 return static_cast<DeclarativeEnvironment*>(m_base_environment)->get_binding_value_direct(vm, m_environment_coordinate->index, m_strict); 142 return m_base_environment->get_binding_value(vm, m_name.as_string(), m_strict); 143} 144 145// 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation 146ThrowCompletionOr<bool> Reference::delete_(VM& vm) 147{ 148 // 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation 149 // UnaryExpression : delete UnaryExpression 150 151 // NOTE: The following steps have already been evaluated by the time we get here: 152 // 1. Let ref be the result of evaluating UnaryExpression. 153 // 2. ReturnIfAbrupt(ref). 154 // 3. If ref is not a Reference Record, return true. 155 156 // 4. If IsUnresolvableReference(ref) is true, then 157 if (is_unresolvable()) { 158 // a. Assert: ref.[[Strict]] is false. 159 VERIFY(!m_strict); 160 // b. Return true. 161 return true; 162 } 163 164 // 5. If IsPropertyReference(ref) is true, then 165 if (is_property_reference()) { 166 // a. Assert: IsPrivateReference(ref) is false. 167 VERIFY(!is_private_reference()); 168 169 // b. If IsSuperReference(ref) is true, throw a ReferenceError exception. 170 if (is_super_reference()) 171 return vm.throw_completion<ReferenceError>(ErrorType::UnsupportedDeleteSuperProperty); 172 173 // c. Let baseObj be ! ToObject(ref.[[Base]]). 174 auto* base_obj = MUST(m_base_value.to_object(vm)); 175 176 // d. Let deleteStatus be ? baseObj.[[Delete]](ref.[[ReferencedName]]). 177 bool delete_status = TRY(base_obj->internal_delete(m_name)); 178 179 // e. If deleteStatus is false and ref.[[Strict]] is true, throw a TypeError exception. 180 if (!delete_status && m_strict) 181 return vm.throw_completion<TypeError>(ErrorType::ReferenceNullishDeleteProperty, m_name, TRY_OR_THROW_OOM(vm, m_base_value.to_string_without_side_effects())); 182 183 // f. Return deleteStatus. 184 return delete_status; 185 } 186 187 // 6. Else, 188 // a. Let base be ref.[[Base]]. 189 // b. Assert: base is an Environment Record. 190 191 VERIFY(m_base_type == BaseType::Environment); 192 193 // c. Return ? base.DeleteBinding(ref.[[ReferencedName]]). 194 return m_base_environment->delete_binding(vm, m_name.as_string()); 195} 196 197// 6.2.4.8 InitializeReferencedBinding ( V, W ), https://tc39.es/ecma262/#sec-object.prototype.hasownproperty 198// 1.2.1.1 InitializeReferencedBinding ( V, W, hint ), https://tc39.es/proposal-explicit-resource-management/#sec-initializereferencedbinding 199ThrowCompletionOr<void> Reference::initialize_referenced_binding(VM& vm, Value value, Environment::InitializeBindingHint hint) const 200{ 201 VERIFY(!is_unresolvable()); 202 VERIFY(m_base_type == BaseType::Environment); 203 return m_base_environment->initialize_binding(vm, m_name.as_string(), value, hint); 204} 205 206// 6.2.4.9 MakePrivateReference ( baseValue, privateIdentifier ), https://tc39.es/ecma262/#sec-makeprivatereference 207Reference make_private_reference(VM& vm, Value base_value, DeprecatedFlyString const& private_identifier) 208{ 209 // 1. Let privEnv be the running execution context's PrivateEnvironment. 210 auto* private_environment = vm.running_execution_context().private_environment; 211 212 // 2. Assert: privEnv is not null. 213 VERIFY(private_environment); 214 215 // 3. Let privateName be ResolvePrivateIdentifier(privEnv, privateIdentifier). 216 auto private_name = private_environment->resolve_private_identifier(private_identifier); 217 218 // 4. Return the Reference Record { [[Base]]: baseValue, [[ReferencedName]]: privateName, [[Strict]]: true, [[ThisValue]]: empty }. 219 return Reference { base_value, private_name }; 220} 221 222}