Reference.cpp 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/Error.h>
  7. #include <LibJS/Runtime/GlobalObject.h>
  8. #include <LibJS/Runtime/Reference.h>
  9. namespace JS {
  10. void Reference::put(GlobalObject& global_object, Value value)
  11. {
  12. auto& vm = global_object.vm();
  13. if (is_unresolvable()) {
  14. throw_reference_error(global_object);
  15. return;
  16. }
  17. if (m_call_frame_argument_index.has_value()) {
  18. global_object.vm().call_frame().arguments[m_call_frame_argument_index.value()] = value;
  19. return;
  20. }
  21. if (is_local_variable() || is_global_variable()) {
  22. if (is_local_variable())
  23. vm.set_variable(m_name.to_string(), value, global_object);
  24. else
  25. global_object.put(m_name, value);
  26. return;
  27. }
  28. auto base = this->base();
  29. if (!base.is_object() && vm.in_strict_mode()) {
  30. if (base.is_nullish())
  31. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishSetProperty, m_name.to_value(vm).to_string_without_side_effects(), base.to_string_without_side_effects());
  32. else
  33. vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveSetProperty, m_name.to_value(vm).to_string_without_side_effects(), base.typeof(), base.to_string_without_side_effects());
  34. return;
  35. }
  36. if (base.is_nullish()) {
  37. // This will always fail the to_object() call below, let's throw the TypeError ourselves with a nice message instead.
  38. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishSetProperty, m_name.to_value(vm).to_string_without_side_effects(), base.to_string_without_side_effects());
  39. return;
  40. }
  41. auto* object = base.to_object(global_object);
  42. if (!object)
  43. return;
  44. object->put(m_name, value);
  45. }
  46. void Reference::throw_reference_error(GlobalObject& global_object)
  47. {
  48. auto& vm = global_object.vm();
  49. if (!m_name.is_valid())
  50. vm.throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
  51. else
  52. vm.throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, m_name.to_string_or_symbol().to_display_string());
  53. }
  54. Value Reference::get(GlobalObject& global_object)
  55. {
  56. auto& vm = global_object.vm();
  57. if (is_unresolvable()) {
  58. throw_reference_error(global_object);
  59. return {};
  60. }
  61. if (m_call_frame_argument_index.has_value())
  62. return global_object.vm().argument(m_call_frame_argument_index.value());
  63. if (is_local_variable() || is_global_variable()) {
  64. Value value;
  65. if (is_local_variable())
  66. value = vm.get_variable(m_name.to_string(), global_object);
  67. else
  68. value = global_object.get(m_name);
  69. if (vm.exception())
  70. return {};
  71. if (value.is_empty()) {
  72. throw_reference_error(global_object);
  73. return {};
  74. }
  75. return value;
  76. }
  77. auto base = this->base();
  78. if (base.is_nullish()) {
  79. // This will always fail the to_object() call below, let's throw the TypeError ourselves with a nice message instead.
  80. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishGetProperty, m_name.to_value(vm).to_string_without_side_effects(), base.to_string_without_side_effects());
  81. return {};
  82. }
  83. auto* object = base.to_object(global_object);
  84. if (!object)
  85. return {};
  86. return object->get(m_name).value_or(js_undefined());
  87. }
  88. bool Reference::delete_(GlobalObject& global_object)
  89. {
  90. if (is_unresolvable())
  91. return true;
  92. auto& vm = global_object.vm();
  93. if (is_local_variable() || is_global_variable()) {
  94. if (is_local_variable())
  95. return vm.delete_variable(m_name.to_string());
  96. else
  97. return global_object.delete_property(m_name);
  98. }
  99. auto base = this->base();
  100. if (base.is_nullish()) {
  101. // This will always fail the to_object() call below, let's throw the TypeError ourselves with a nice message instead.
  102. vm.throw_exception<TypeError>(global_object, ErrorType::ReferenceNullishDeleteProperty, m_name.to_value(vm).to_string_without_side_effects(), base.to_string_without_side_effects());
  103. return false;
  104. }
  105. auto* object = base.to_object(global_object);
  106. VERIFY(object);
  107. return object->delete_property(m_name);
  108. }
  109. }