ProxyObject.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
  3. * Copyright (c) 2021, Linus Groh <mail@linusgroh.de>
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form must reproduce the above copyright notice,
  13. * this list of conditions and the following disclaimer in the documentation
  14. * and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  24. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. #include <LibJS/Runtime/Accessor.h>
  28. #include <LibJS/Runtime/Array.h>
  29. #include <LibJS/Runtime/Error.h>
  30. #include <LibJS/Runtime/GlobalObject.h>
  31. #include <LibJS/Runtime/ProxyObject.h>
  32. namespace JS {
  33. bool static is_compatible_property_descriptor(bool is_extensible, PropertyDescriptor new_descriptor, Optional<PropertyDescriptor> current_descriptor_optional)
  34. {
  35. if (!current_descriptor_optional.has_value())
  36. return is_extensible;
  37. auto current_descriptor = current_descriptor_optional.value();
  38. if (new_descriptor.attributes.is_empty() && new_descriptor.value.is_empty() && !new_descriptor.getter && !new_descriptor.setter)
  39. return true;
  40. if (!current_descriptor.attributes.is_configurable()) {
  41. if (new_descriptor.attributes.is_configurable())
  42. return false;
  43. if (new_descriptor.attributes.has_enumerable() && new_descriptor.attributes.is_enumerable() != current_descriptor.attributes.is_enumerable())
  44. return false;
  45. }
  46. if (new_descriptor.is_generic_descriptor())
  47. return true;
  48. if (current_descriptor.is_data_descriptor() != new_descriptor.is_data_descriptor() && !current_descriptor.attributes.is_configurable())
  49. return false;
  50. if (current_descriptor.is_data_descriptor() && new_descriptor.is_data_descriptor() && !current_descriptor.attributes.is_configurable() && !current_descriptor.attributes.is_writable()) {
  51. if (new_descriptor.attributes.is_writable())
  52. return false;
  53. return new_descriptor.value.is_empty() && same_value(new_descriptor.value, current_descriptor.value);
  54. }
  55. return true;
  56. }
  57. ProxyObject* ProxyObject::create(GlobalObject& global_object, Object& target, Object& handler)
  58. {
  59. return global_object.heap().allocate<ProxyObject>(global_object, target, handler, *global_object.object_prototype());
  60. }
  61. ProxyObject::ProxyObject(Object& target, Object& handler, Object& prototype)
  62. : Function(prototype)
  63. , m_target(target)
  64. , m_handler(handler)
  65. {
  66. }
  67. ProxyObject::~ProxyObject()
  68. {
  69. }
  70. Object* ProxyObject::prototype()
  71. {
  72. auto& vm = this->vm();
  73. if (m_is_revoked) {
  74. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  75. return nullptr;
  76. }
  77. auto trap = get_method(global_object(), Value(&m_handler), vm.names.getPrototypeOf);
  78. if (vm.exception())
  79. return nullptr;
  80. if (!trap)
  81. return m_target.prototype();
  82. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target));
  83. if (vm.exception())
  84. return nullptr;
  85. if (!trap_result.is_object() && !trap_result.is_null()) {
  86. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfReturn);
  87. return nullptr;
  88. }
  89. if (m_target.is_extensible()) {
  90. if (vm.exception())
  91. return nullptr;
  92. if (trap_result.is_null())
  93. return nullptr;
  94. return &trap_result.as_object();
  95. }
  96. auto target_proto = m_target.prototype();
  97. if (vm.exception())
  98. return nullptr;
  99. if (!same_value(trap_result, Value(target_proto))) {
  100. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfNonExtensible);
  101. return nullptr;
  102. }
  103. return &trap_result.as_object();
  104. }
  105. const Object* ProxyObject::prototype() const
  106. {
  107. auto& vm = this->vm();
  108. if (m_is_revoked) {
  109. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  110. return nullptr;
  111. }
  112. return const_cast<const Object*>(const_cast<ProxyObject*>(this)->prototype());
  113. }
  114. bool ProxyObject::set_prototype(Object* object)
  115. {
  116. auto& vm = this->vm();
  117. if (m_is_revoked) {
  118. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  119. return false;
  120. }
  121. auto trap = get_method(global_object(), Value(&m_handler), vm.names.setPrototypeOf);
  122. if (vm.exception())
  123. return false;
  124. if (!trap)
  125. return m_target.set_prototype(object);
  126. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), Value(object));
  127. if (vm.exception() || !trap_result.to_boolean())
  128. return false;
  129. if (m_target.is_extensible())
  130. return true;
  131. auto* target_proto = m_target.prototype();
  132. if (vm.exception())
  133. return false;
  134. if (!same_value(Value(object), Value(target_proto))) {
  135. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetPrototypeOfNonExtensible);
  136. return false;
  137. }
  138. return true;
  139. }
  140. bool ProxyObject::is_extensible() const
  141. {
  142. auto& vm = this->vm();
  143. if (m_is_revoked) {
  144. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  145. return false;
  146. }
  147. auto trap = get_method(global_object(), Value(&m_handler), vm.names.isExtensible);
  148. if (vm.exception())
  149. return false;
  150. if (!trap)
  151. return m_target.is_extensible();
  152. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target));
  153. if (vm.exception())
  154. return false;
  155. if (trap_result.to_boolean() != m_target.is_extensible()) {
  156. if (!vm.exception())
  157. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyIsExtensibleReturn);
  158. return false;
  159. }
  160. return trap_result.to_boolean();
  161. }
  162. bool ProxyObject::prevent_extensions()
  163. {
  164. auto& vm = this->vm();
  165. if (m_is_revoked) {
  166. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  167. return false;
  168. }
  169. auto trap = get_method(global_object(), Value(&m_handler), vm.names.preventExtensions);
  170. if (vm.exception())
  171. return false;
  172. if (!trap)
  173. return m_target.prevent_extensions();
  174. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target));
  175. if (vm.exception())
  176. return false;
  177. if (trap_result.to_boolean() && m_target.is_extensible()) {
  178. if (!vm.exception())
  179. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyPreventExtensionsReturn);
  180. return false;
  181. }
  182. return trap_result.to_boolean();
  183. }
  184. Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const PropertyName& name) const
  185. {
  186. auto& vm = this->vm();
  187. if (m_is_revoked) {
  188. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  189. return {};
  190. }
  191. auto trap = get_method(global_object(), Value(&m_handler), vm.names.getOwnPropertyDescriptor);
  192. if (vm.exception())
  193. return {};
  194. if (!trap)
  195. return m_target.get_own_property_descriptor(name);
  196. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm));
  197. if (vm.exception())
  198. return {};
  199. if (!trap_result.is_object() && !trap_result.is_undefined()) {
  200. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorReturn);
  201. return {};
  202. }
  203. auto target_desc = m_target.get_own_property_descriptor(name);
  204. if (vm.exception())
  205. return {};
  206. if (trap_result.is_undefined()) {
  207. if (!target_desc.has_value())
  208. return {};
  209. if (!target_desc.value().attributes.is_configurable()) {
  210. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorNonConfigurable);
  211. return {};
  212. }
  213. if (!m_target.is_extensible()) {
  214. if (!vm.exception())
  215. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorUndefinedReturn);
  216. return {};
  217. }
  218. return {};
  219. }
  220. auto result_desc = PropertyDescriptor::from_dictionary(vm, trap_result.as_object());
  221. if (vm.exception())
  222. return {};
  223. if (!is_compatible_property_descriptor(m_target.is_extensible(), result_desc, target_desc)) {
  224. if (!vm.exception())
  225. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidDescriptor);
  226. return {};
  227. }
  228. if (!result_desc.attributes.is_configurable() && (!target_desc.has_value() || target_desc.value().attributes.is_configurable())) {
  229. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidNonConfig);
  230. return {};
  231. }
  232. return result_desc;
  233. }
  234. bool ProxyObject::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
  235. {
  236. auto& vm = this->vm();
  237. if (m_is_revoked) {
  238. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  239. return false;
  240. }
  241. auto trap = get_method(global_object(), Value(&m_handler), vm.names.defineProperty);
  242. if (vm.exception())
  243. return false;
  244. if (!trap)
  245. return m_target.define_property(property_name, descriptor, throw_exceptions);
  246. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), property_name.to_value(vm), Value(const_cast<Object*>(&descriptor)));
  247. if (vm.exception() || !trap_result.to_boolean())
  248. return false;
  249. auto target_desc = m_target.get_own_property_descriptor(property_name);
  250. if (vm.exception())
  251. return false;
  252. bool setting_config_false = false;
  253. if (descriptor.has_property(vm.names.configurable) && !descriptor.get(vm.names.configurable).to_boolean())
  254. setting_config_false = true;
  255. if (vm.exception())
  256. return false;
  257. if (!target_desc.has_value()) {
  258. if (!m_target.is_extensible()) {
  259. if (!vm.exception())
  260. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonExtensible);
  261. return false;
  262. }
  263. if (setting_config_false) {
  264. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonConfigurableNonExisting);
  265. return false;
  266. }
  267. } else {
  268. if (!is_compatible_property_descriptor(m_target.is_extensible(), PropertyDescriptor::from_dictionary(vm, descriptor), target_desc)) {
  269. if (!vm.exception())
  270. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropIncompatibleDescriptor);
  271. return false;
  272. }
  273. if (setting_config_false && target_desc.value().attributes.is_configurable()) {
  274. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropExistingConfigurable);
  275. return false;
  276. }
  277. }
  278. return true;
  279. }
  280. bool ProxyObject::has_property(const PropertyName& name) const
  281. {
  282. auto& vm = this->vm();
  283. if (m_is_revoked) {
  284. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  285. return false;
  286. }
  287. auto trap = get_method(global_object(), Value(&m_handler), vm.names.has);
  288. if (vm.exception())
  289. return false;
  290. if (!trap)
  291. return m_target.has_property(name);
  292. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm));
  293. if (vm.exception())
  294. return false;
  295. if (!trap_result.to_boolean()) {
  296. auto target_desc = m_target.get_own_property_descriptor(name);
  297. if (vm.exception())
  298. return false;
  299. if (target_desc.has_value()) {
  300. if (!target_desc.value().attributes.is_configurable()) {
  301. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonConfigurable);
  302. return false;
  303. }
  304. if (!m_target.is_extensible()) {
  305. if (!vm.exception())
  306. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonExtensible);
  307. return false;
  308. }
  309. }
  310. }
  311. return trap_result.to_boolean();
  312. }
  313. Value ProxyObject::get(const PropertyName& name, Value receiver) const
  314. {
  315. auto& vm = this->vm();
  316. if (m_is_revoked) {
  317. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  318. return {};
  319. }
  320. if (receiver.is_empty())
  321. receiver = Value(const_cast<ProxyObject*>(this));
  322. auto trap = get_method(global_object(), Value(&m_handler), vm.names.get);
  323. if (vm.exception())
  324. return {};
  325. if (!trap)
  326. return m_target.get(name, receiver);
  327. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm), receiver);
  328. if (vm.exception())
  329. return {};
  330. auto target_desc = m_target.get_own_property_descriptor(name);
  331. if (target_desc.has_value()) {
  332. if (vm.exception())
  333. return {};
  334. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(trap_result, target_desc.value().value)) {
  335. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetImmutableDataProperty);
  336. return {};
  337. }
  338. if (target_desc.value().is_accessor_descriptor() && target_desc.value().getter == nullptr && !trap_result.is_undefined()) {
  339. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetNonConfigurableAccessor);
  340. return {};
  341. }
  342. }
  343. return trap_result;
  344. }
  345. bool ProxyObject::put(const PropertyName& name, Value value, Value receiver)
  346. {
  347. auto& vm = this->vm();
  348. if (m_is_revoked) {
  349. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  350. return false;
  351. }
  352. if (receiver.is_empty())
  353. receiver = Value(const_cast<ProxyObject*>(this));
  354. auto trap = get_method(global_object(), Value(&m_handler), vm.names.set);
  355. if (vm.exception())
  356. return false;
  357. if (!trap)
  358. return m_target.put(name, value, receiver);
  359. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm), value, receiver);
  360. if (vm.exception() || !trap_result.to_boolean())
  361. return false;
  362. auto target_desc = m_target.get_own_property_descriptor(name);
  363. if (vm.exception())
  364. return false;
  365. if (target_desc.has_value() && !target_desc.value().attributes.is_configurable()) {
  366. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(value, target_desc.value().value)) {
  367. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetImmutableDataProperty);
  368. return false;
  369. }
  370. if (target_desc.value().is_accessor_descriptor() && !target_desc.value().setter) {
  371. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetNonConfigurableAccessor);
  372. }
  373. }
  374. return true;
  375. }
  376. bool ProxyObject::delete_property(const PropertyName& name)
  377. {
  378. auto& vm = this->vm();
  379. if (m_is_revoked) {
  380. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  381. return false;
  382. }
  383. auto trap = get_method(global_object(), Value(&m_handler), vm.names.deleteProperty);
  384. if (vm.exception())
  385. return false;
  386. if (!trap)
  387. return m_target.delete_property(name);
  388. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm));
  389. if (vm.exception())
  390. return false;
  391. if (!trap_result.to_boolean())
  392. return false;
  393. auto target_desc = m_target.get_own_property_descriptor(name);
  394. if (vm.exception())
  395. return false;
  396. if (!target_desc.has_value())
  397. return true;
  398. if (!target_desc.value().attributes.is_configurable()) {
  399. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDeleteNonConfigurable);
  400. return false;
  401. }
  402. return true;
  403. }
  404. void ProxyObject::visit_edges(Cell::Visitor& visitor)
  405. {
  406. Function::visit_edges(visitor);
  407. visitor.visit(&m_target);
  408. visitor.visit(&m_handler);
  409. }
  410. Value ProxyObject::call()
  411. {
  412. auto& vm = this->vm();
  413. if (!is_function()) {
  414. vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, Value(this).to_string_without_side_effects());
  415. return {};
  416. }
  417. if (m_is_revoked) {
  418. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  419. return {};
  420. }
  421. auto trap = get_method(global_object(), Value(&m_handler), vm.names.apply);
  422. if (vm.exception())
  423. return {};
  424. if (!trap)
  425. return static_cast<Function&>(m_target).call();
  426. MarkedValueList arguments(heap());
  427. arguments.append(Value(&m_target));
  428. arguments.append(Value(&m_handler));
  429. // FIXME: Pass global object
  430. auto arguments_array = Array::create(global_object());
  431. vm.for_each_argument([&](auto& argument) {
  432. arguments_array->indexed_properties().append(argument);
  433. });
  434. arguments.append(arguments_array);
  435. return vm.call(*trap, Value(&m_handler), move(arguments));
  436. }
  437. Value ProxyObject::construct(Function& new_target)
  438. {
  439. auto& vm = this->vm();
  440. if (!is_function()) {
  441. vm.throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, Value(this).to_string_without_side_effects());
  442. return {};
  443. }
  444. if (m_is_revoked) {
  445. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  446. return {};
  447. }
  448. auto trap = get_method(global_object(), Value(&m_handler), vm.names.construct);
  449. if (vm.exception())
  450. return {};
  451. if (!trap)
  452. return static_cast<Function&>(m_target).construct(new_target);
  453. MarkedValueList arguments(vm.heap());
  454. arguments.append(Value(&m_target));
  455. auto arguments_array = Array::create(global_object());
  456. vm.for_each_argument([&](auto& argument) {
  457. arguments_array->indexed_properties().append(argument);
  458. });
  459. arguments.append(arguments_array);
  460. arguments.append(Value(&new_target));
  461. auto result = vm.call(*trap, Value(&m_handler), move(arguments));
  462. if (!result.is_object()) {
  463. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructBadReturnType);
  464. return {};
  465. }
  466. return result;
  467. }
  468. const FlyString& ProxyObject::name() const
  469. {
  470. VERIFY(is_function());
  471. return static_cast<Function&>(m_target).name();
  472. }
  473. LexicalEnvironment* ProxyObject::create_environment()
  474. {
  475. VERIFY(is_function());
  476. return static_cast<Function&>(m_target).create_environment();
  477. }
  478. }