Reference.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/AST.h>
  7. #include <LibJS/Runtime/Error.h>
  8. #include <LibJS/Runtime/GlobalObject.h>
  9. #include <LibJS/Runtime/Reference.h>
  10. namespace JS {
  11. // 6.2.4.6 PutValue ( V, W ), https://tc39.es/ecma262/#sec-putvalue
  12. void Reference::put_value(GlobalObject& global_object, Value value)
  13. {
  14. auto& vm = global_object.vm();
  15. if (!is_valid_reference()) {
  16. vm.throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment);
  17. return;
  18. }
  19. if (is_unresolvable()) {
  20. if (m_strict) {
  21. throw_reference_error(global_object);
  22. return;
  23. }
  24. global_object.set(m_name, value, Object::ShouldThrowExceptions::No);
  25. return;
  26. }
  27. if (is_property_reference()) {
  28. auto* base_obj = m_base_value.to_object(global_object);
  29. if (!base_obj)
  30. return;
  31. auto succeeded_or_error = base_obj->internal_set(m_name, value, get_this_value());
  32. if (succeeded_or_error.is_error())
  33. return;
  34. auto succeeded = succeeded_or_error.release_value();
  35. if (!succeeded && m_strict) {
  36. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishSetProperty, m_name, m_base_value.to_string_without_side_effects());
  37. return;
  38. }
  39. return;
  40. }
  41. VERIFY(m_base_type == BaseType::Environment);
  42. VERIFY(m_base_environment);
  43. m_base_environment->set_mutable_binding(global_object, m_name.as_string(), value, m_strict);
  44. }
  45. void Reference::throw_reference_error(GlobalObject& global_object) const
  46. {
  47. auto& vm = global_object.vm();
  48. if (!m_name.is_valid())
  49. vm.throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
  50. else
  51. vm.throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, m_name.to_string_or_symbol().to_display_string());
  52. }
  53. // 6.2.4.5 GetValue ( V ), https://tc39.es/ecma262/#sec-getvalue
  54. Value Reference::get_value(GlobalObject& global_object) const
  55. {
  56. if (!is_valid_reference() || is_unresolvable()) {
  57. throw_reference_error(global_object);
  58. return {};
  59. }
  60. if (is_property_reference()) {
  61. auto* base_obj = m_base_value.to_object(global_object);
  62. if (!base_obj)
  63. return {};
  64. return TRY_OR_DISCARD(base_obj->get(m_name));
  65. }
  66. VERIFY(m_base_type == BaseType::Environment);
  67. VERIFY(m_base_environment);
  68. return m_base_environment->get_binding_value(global_object, m_name.as_string(), m_strict);
  69. }
  70. // 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
  71. bool Reference::delete_(GlobalObject& global_object)
  72. {
  73. // 13.5.1.2 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-delete-operator-runtime-semantics-evaluation
  74. // UnaryExpression : delete UnaryExpression
  75. // NOTE: The following steps have already been evaluated by the time we get here:
  76. // 1. Let ref be the result of evaluating UnaryExpression.
  77. // 2. ReturnIfAbrupt(ref).
  78. // 3. If ref is not a Reference Record, return true.
  79. // 4. If IsUnresolvableReference(ref) is true, then
  80. if (is_unresolvable()) {
  81. // a. Assert: ref.[[Strict]] is false.
  82. VERIFY(!m_strict);
  83. // b. Return true.
  84. return true;
  85. }
  86. auto& vm = global_object.vm();
  87. // 5. If IsPropertyReference(ref) is true, then
  88. if (is_property_reference()) {
  89. // a. Assert: ! IsPrivateReference(ref) is false.
  90. // FIXME: We don't have private references yet.
  91. // b. If IsSuperReference(ref) is true, throw a ReferenceError exception.
  92. if (is_super_reference()) {
  93. vm.throw_exception<ReferenceError>(global_object, ErrorType::UnsupportedDeleteSuperProperty);
  94. return {};
  95. }
  96. // c. Let baseObj be ! ToObject(ref.[[Base]]).
  97. auto* base_obj = m_base_value.to_object(global_object);
  98. VERIFY(base_obj);
  99. // d. Let deleteStatus be ? baseObj.[[Delete]](ref.[[ReferencedName]]).
  100. bool delete_status = TRY_OR_DISCARD(base_obj->internal_delete(m_name));
  101. // e. If deleteStatus is false and ref.[[Strict]] is true, throw a TypeError exception.
  102. if (!delete_status && m_strict) {
  103. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishDeleteProperty, m_name, m_base_value.to_string_without_side_effects());
  104. return {};
  105. }
  106. // f. Return deleteStatus.
  107. return delete_status;
  108. }
  109. // 6. Else,
  110. // a. Let base be ref.[[Base]].
  111. // b. Assert: base is an Environment Record.
  112. VERIFY(m_base_type == BaseType::Environment);
  113. // c. Return ? base.DeleteBinding(ref.[[ReferencedName]]).
  114. return m_base_environment->delete_binding(global_object, m_name.as_string());
  115. }
  116. String Reference::to_string() const
  117. {
  118. StringBuilder builder;
  119. builder.append("Reference { Base=");
  120. switch (m_base_type) {
  121. case BaseType::Unresolvable:
  122. builder.append("Unresolvable");
  123. break;
  124. case BaseType::Environment:
  125. builder.appendff("{}", base_environment().class_name());
  126. break;
  127. case BaseType::Value:
  128. if (m_base_value.is_empty())
  129. builder.append("<empty>");
  130. else
  131. builder.appendff("{}", m_base_value.to_string_without_side_effects());
  132. break;
  133. }
  134. builder.append(", ReferencedName=");
  135. if (!m_name.is_valid())
  136. builder.append("<invalid>");
  137. else if (m_name.is_symbol())
  138. builder.appendff("{}", m_name.as_symbol()->to_string());
  139. else
  140. builder.appendff("{}", m_name.to_string());
  141. builder.appendff(", Strict={}", m_strict);
  142. builder.appendff(", ThisValue=");
  143. if (m_this_value.is_empty())
  144. builder.append("<empty>");
  145. else
  146. builder.appendff("{}", m_this_value.to_string_without_side_effects());
  147. builder.append(" }");
  148. return builder.to_string();
  149. }
  150. }