ProxyObject.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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, bool without_side_effects) const
  314. {
  315. auto& vm = this->vm();
  316. if (without_side_effects) {
  317. // Sorry, we're not going to call anything on this proxy.
  318. return js_string(vm, "<ProxyObject>");
  319. }
  320. if (m_is_revoked) {
  321. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  322. return {};
  323. }
  324. if (receiver.is_empty())
  325. receiver = Value(const_cast<ProxyObject*>(this));
  326. auto trap = get_method(global_object(), Value(&m_handler), vm.names.get);
  327. if (vm.exception())
  328. return {};
  329. if (!trap)
  330. return m_target.get(name, receiver);
  331. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm), receiver);
  332. if (vm.exception())
  333. return {};
  334. auto target_desc = m_target.get_own_property_descriptor(name);
  335. if (target_desc.has_value()) {
  336. if (vm.exception())
  337. return {};
  338. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(trap_result, target_desc.value().value)) {
  339. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetImmutableDataProperty);
  340. return {};
  341. }
  342. if (target_desc.value().is_accessor_descriptor() && target_desc.value().getter == nullptr && !trap_result.is_undefined()) {
  343. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetNonConfigurableAccessor);
  344. return {};
  345. }
  346. }
  347. return trap_result;
  348. }
  349. bool ProxyObject::put(const PropertyName& name, Value value, Value receiver)
  350. {
  351. auto& vm = this->vm();
  352. if (m_is_revoked) {
  353. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  354. return false;
  355. }
  356. if (receiver.is_empty())
  357. receiver = Value(const_cast<ProxyObject*>(this));
  358. auto trap = get_method(global_object(), Value(&m_handler), vm.names.set);
  359. if (vm.exception())
  360. return false;
  361. if (!trap)
  362. return m_target.put(name, value, receiver);
  363. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm), value, receiver);
  364. if (vm.exception() || !trap_result.to_boolean())
  365. return false;
  366. auto target_desc = m_target.get_own_property_descriptor(name);
  367. if (vm.exception())
  368. return false;
  369. if (target_desc.has_value() && !target_desc.value().attributes.is_configurable()) {
  370. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(value, target_desc.value().value)) {
  371. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetImmutableDataProperty);
  372. return false;
  373. }
  374. if (target_desc.value().is_accessor_descriptor() && !target_desc.value().setter) {
  375. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetNonConfigurableAccessor);
  376. }
  377. }
  378. return true;
  379. }
  380. bool ProxyObject::delete_property(const PropertyName& name)
  381. {
  382. auto& vm = this->vm();
  383. if (m_is_revoked) {
  384. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  385. return false;
  386. }
  387. auto trap = get_method(global_object(), Value(&m_handler), vm.names.deleteProperty);
  388. if (vm.exception())
  389. return false;
  390. if (!trap)
  391. return m_target.delete_property(name);
  392. auto trap_result = vm.call(*trap, Value(&m_handler), Value(&m_target), name.to_value(vm));
  393. if (vm.exception())
  394. return false;
  395. if (!trap_result.to_boolean())
  396. return false;
  397. auto target_desc = m_target.get_own_property_descriptor(name);
  398. if (vm.exception())
  399. return false;
  400. if (!target_desc.has_value())
  401. return true;
  402. if (!target_desc.value().attributes.is_configurable()) {
  403. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDeleteNonConfigurable);
  404. return false;
  405. }
  406. return true;
  407. }
  408. void ProxyObject::visit_edges(Cell::Visitor& visitor)
  409. {
  410. Function::visit_edges(visitor);
  411. visitor.visit(&m_target);
  412. visitor.visit(&m_handler);
  413. }
  414. Value ProxyObject::call()
  415. {
  416. auto& vm = this->vm();
  417. if (!is_function()) {
  418. vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, Value(this).to_string_without_side_effects());
  419. return {};
  420. }
  421. if (m_is_revoked) {
  422. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  423. return {};
  424. }
  425. auto trap = get_method(global_object(), Value(&m_handler), vm.names.apply);
  426. if (vm.exception())
  427. return {};
  428. if (!trap)
  429. return static_cast<Function&>(m_target).call();
  430. MarkedValueList arguments(heap());
  431. arguments.append(Value(&m_target));
  432. arguments.append(Value(&m_handler));
  433. // FIXME: Pass global object
  434. auto arguments_array = Array::create(global_object());
  435. vm.for_each_argument([&](auto& argument) {
  436. arguments_array->indexed_properties().append(argument);
  437. });
  438. arguments.append(arguments_array);
  439. return vm.call(*trap, Value(&m_handler), move(arguments));
  440. }
  441. Value ProxyObject::construct(Function& new_target)
  442. {
  443. auto& vm = this->vm();
  444. if (!is_function()) {
  445. vm.throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, Value(this).to_string_without_side_effects());
  446. return {};
  447. }
  448. if (m_is_revoked) {
  449. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
  450. return {};
  451. }
  452. auto trap = get_method(global_object(), Value(&m_handler), vm.names.construct);
  453. if (vm.exception())
  454. return {};
  455. if (!trap)
  456. return static_cast<Function&>(m_target).construct(new_target);
  457. MarkedValueList arguments(vm.heap());
  458. arguments.append(Value(&m_target));
  459. auto arguments_array = Array::create(global_object());
  460. vm.for_each_argument([&](auto& argument) {
  461. arguments_array->indexed_properties().append(argument);
  462. });
  463. arguments.append(arguments_array);
  464. arguments.append(Value(&new_target));
  465. auto result = vm.call(*trap, Value(&m_handler), move(arguments));
  466. if (!result.is_object()) {
  467. vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructBadReturnType);
  468. return {};
  469. }
  470. return result;
  471. }
  472. const FlyString& ProxyObject::name() const
  473. {
  474. VERIFY(is_function());
  475. return static_cast<Function&>(m_target).name();
  476. }
  477. LexicalEnvironment* ProxyObject::create_environment()
  478. {
  479. VERIFY(is_function());
  480. return static_cast<Function&>(m_target).create_environment();
  481. }
  482. }