ProxyObject.cpp 20 KB

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