ProxyObject.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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>("An operation was performed on a revoked Proxy object");
  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>("Proxy handler's getPrototypeOf trap wasn't undefined, null, or callable");
  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>("Proxy handler's getPrototypeOf trap violates invariant: must return an object or null");
  91. return nullptr;
  92. }
  93. if (m_target.is_extensible()) {
  94. if (trap_result.is_null())
  95. return nullptr;
  96. return &trap_result.as_object();
  97. }
  98. auto target_proto = m_target.prototype();
  99. if (interpreter().exception())
  100. return nullptr;
  101. if (!same_value(interpreter(), trap_result, Value(target_proto))) {
  102. interpreter().throw_exception<TypeError>("Proxy handler's getPrototypeOf trap violates invariant: cannot return a different prototype object for a non-extensible target");
  103. return nullptr;
  104. }
  105. return &trap_result.as_object();
  106. }
  107. const Object* ProxyObject::prototype() const
  108. {
  109. if (m_is_revoked) {
  110. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  111. return nullptr;
  112. }
  113. return const_cast<const Object*>(const_cast<ProxyObject*>(this)->prototype());
  114. }
  115. bool ProxyObject::set_prototype(Object* object)
  116. {
  117. if (m_is_revoked) {
  118. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  119. return false;
  120. }
  121. auto trap = m_handler.get("setPrototypeOf");
  122. if (interpreter().exception())
  123. return false;
  124. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  125. return m_target.set_prototype(object);
  126. if (!trap.is_function()) {
  127. interpreter().throw_exception<TypeError>("Proxy handler's setPrototypeOf trap wasn't undefined, null, or callable");
  128. return false;
  129. }
  130. MarkedValueList arguments(interpreter().heap());
  131. arguments.values().append(Value(&m_target));
  132. arguments.values().append(Value(object));
  133. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  134. if (interpreter().exception() || !trap_result)
  135. return false;
  136. if (m_target.is_extensible())
  137. return true;
  138. auto* target_proto = m_target.prototype();
  139. if (interpreter().exception())
  140. return false;
  141. if (!same_value(interpreter(), Value(object), Value(target_proto))) {
  142. interpreter().throw_exception<TypeError>("Proxy handler's setPrototypeOf trap violates invariant: the argument must match the prototype of the target if the target is non-extensible");
  143. return false;
  144. }
  145. return true;
  146. }
  147. bool ProxyObject::is_extensible() const
  148. {
  149. if (m_is_revoked) {
  150. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  151. return false;
  152. }
  153. auto trap = m_handler.get("isExtensible");
  154. if (interpreter().exception())
  155. return false;
  156. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  157. return m_target.is_extensible();
  158. if (!trap.is_function()) {
  159. interpreter().throw_exception<TypeError>("Proxy handler's isExtensible trap wasn't undefined, null, or callable");
  160. return {};
  161. }
  162. MarkedValueList arguments(interpreter().heap());
  163. arguments.values().append(Value(&m_target));
  164. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  165. if (interpreter().exception())
  166. return false;
  167. if (trap_result != m_target.is_extensible()) {
  168. interpreter().throw_exception<TypeError>("Proxy handler's isExtensible trap violates invariant: return value must match the target's extensibility");
  169. return false;
  170. }
  171. return trap_result;
  172. }
  173. bool ProxyObject::prevent_extensions()
  174. {
  175. if (m_is_revoked) {
  176. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  177. return false;
  178. }
  179. auto trap = m_handler.get("preventExtensions");
  180. if (interpreter().exception())
  181. return false;
  182. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  183. return m_target.prevent_extensions();
  184. if (!trap.is_function()) {
  185. interpreter().throw_exception<TypeError>("Proxy handler's preventExtensions trap wasn't undefined, null, or callable");
  186. return {};
  187. }
  188. MarkedValueList arguments(interpreter().heap());
  189. arguments.values().append(Value(&m_target));
  190. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  191. if (interpreter().exception())
  192. return false;
  193. if (trap_result && m_target.is_extensible()) {
  194. interpreter().throw_exception<TypeError>("Proxy handler's preventExtensions trap violates invariant: cannot return true if the target object is extensible");
  195. return false;
  196. }
  197. return trap_result;
  198. }
  199. Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(PropertyName name) const
  200. {
  201. if (m_is_revoked) {
  202. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  203. return {};
  204. }
  205. auto trap = m_handler.get("getOwnPropertyDescriptor");
  206. if (interpreter().exception())
  207. return {};
  208. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  209. return m_target.get_own_property_descriptor(name);
  210. if (!trap.is_function()) {
  211. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap wasn't undefined, null, or callable");
  212. return {};
  213. }
  214. MarkedValueList arguments(interpreter().heap());
  215. arguments.values().append(Value(&m_target));
  216. arguments.values().append(js_string(interpreter(), name.to_string()));
  217. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments));
  218. if (interpreter().exception())
  219. return {};
  220. if (!trap_result.is_object() && !trap_result.is_undefined()) {
  221. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: must return an object or undefined");
  222. return {};
  223. }
  224. auto target_desc = m_target.get_own_property_descriptor(name);
  225. if (interpreter().exception())
  226. return {};
  227. if (trap_result.is_undefined()) {
  228. if (!target_desc.has_value())
  229. return {};
  230. if (!target_desc.value().attributes.is_configurable()) {
  231. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot return undefined for a property on the target which is a non-configurable property");
  232. return {};
  233. }
  234. if (!m_target.is_extensible()) {
  235. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report a property as being undefined if it exists as an own property of the target and the target is non-extensible");
  236. return {};
  237. }
  238. return {};
  239. }
  240. auto result_desc = PropertyDescriptor::from_dictionary(interpreter(), trap_result.as_object());
  241. if (interpreter().exception())
  242. return {};
  243. if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), result_desc, target_desc)) {
  244. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: invalid property descriptor for existing property on the target");
  245. return {};
  246. }
  247. if (!result_desc.attributes.is_configurable() && (!target_desc.has_value() || target_desc.value().attributes.is_configurable())) {
  248. interpreter().throw_exception<TypeError>("Proxy handler's getOwnPropertyDescriptor trap violates invariant: cannot report target's property as non-configurable if the property does not exist, or if it is configurable");
  249. return {};
  250. }
  251. return result_desc;
  252. }
  253. bool ProxyObject::define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions)
  254. {
  255. if (m_is_revoked) {
  256. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  257. return false;
  258. }
  259. auto trap = m_handler.get("defineProperty");
  260. if (interpreter().exception())
  261. return false;
  262. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  263. return m_target.define_property(property_name, descriptor, throw_exceptions);
  264. if (!trap.is_function()) {
  265. interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap wasn't undefined, null, or callable");
  266. return false;
  267. }
  268. MarkedValueList arguments(interpreter().heap());
  269. arguments.values().append(Value(&m_target));
  270. arguments.values().append(js_string(interpreter(), property_name));
  271. arguments.values().append(Value(const_cast<Object*>(&descriptor)));
  272. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  273. if (interpreter().exception() || !trap_result)
  274. return false;
  275. auto target_desc = m_target.get_own_property_descriptor(property_name);
  276. if (interpreter().exception())
  277. return false;
  278. bool setting_config_false = false;
  279. if (descriptor.has_property("configurable") && !descriptor.get("configurable").to_boolean())
  280. setting_config_false = true;
  281. if (interpreter().exception())
  282. return false;
  283. if (!target_desc.has_value()) {
  284. if (!m_target.is_extensible()) {
  285. interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: a property cannot be reported as being defined if the property does not exist on the target and the target is non-extensible");
  286. return false;
  287. }
  288. if (setting_config_false) {
  289. interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it does not already exist on the target object");
  290. return false;
  291. }
  292. } else {
  293. if (!is_compatible_property_descriptor(interpreter(), m_target.is_extensible(), PropertyDescriptor::from_dictionary(interpreter(), descriptor), target_desc)) {
  294. interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: the new descriptor is not compatible with the existing descriptor of the property on the target");
  295. return false;
  296. }
  297. if (setting_config_false && target_desc.value().attributes.is_configurable()) {
  298. interpreter().throw_exception<TypeError>("Proxy handler's defineProperty trap violates invariant: a property cannot be defined as non-configurable if it already exists on the target object as a configurable property");
  299. return false;
  300. }
  301. }
  302. return true;
  303. }
  304. bool ProxyObject::has_property(PropertyName name) const
  305. {
  306. if (m_is_revoked) {
  307. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  308. return false;
  309. }
  310. auto trap = m_handler.get("has");
  311. if (interpreter().exception())
  312. return false;
  313. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  314. return m_target.has_property(name);
  315. if (!trap.is_function()) {
  316. interpreter().throw_exception<TypeError>("Proxy handler's has trap wasn't undefined, null, or callable");
  317. return false;
  318. }
  319. MarkedValueList arguments(interpreter().heap());
  320. arguments.values().append(Value(&m_target));
  321. arguments.values().append(js_string(interpreter(), name.to_string()));
  322. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  323. if (interpreter().exception())
  324. return false;
  325. if (!trap_result) {
  326. auto target_desc = m_target.get_own_property_descriptor(name);
  327. if (interpreter().exception())
  328. return false;
  329. if (target_desc.has_value()) {
  330. if (!target_desc.value().attributes.is_configurable()) {
  331. interpreter().throw_exception<TypeError>("Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exists on the target as a non-configurable property");
  332. return false;
  333. }
  334. if (!m_target.is_extensible()) {
  335. interpreter().throw_exception<TypeError>("Proxy handler's has trap violates invariant: a property cannot be reported as non-existent if it exist on the target and the target is non-extensible");
  336. return false;
  337. }
  338. }
  339. }
  340. return trap_result;
  341. }
  342. Value ProxyObject::get(PropertyName name) const
  343. {
  344. if (m_is_revoked) {
  345. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  346. return {};
  347. }
  348. auto trap = m_handler.get("get");
  349. if (interpreter().exception())
  350. return {};
  351. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  352. return m_target.get(name);
  353. if (!trap.is_function())
  354. return interpreter().throw_exception<TypeError>("Proxy handler's get trap wasn't undefined, null, or callable");
  355. MarkedValueList arguments(interpreter().heap());
  356. arguments.values().append(Value(&m_target));
  357. arguments.values().append(js_string(interpreter(), name.to_string()));
  358. arguments.values().append(Value(const_cast<ProxyObject*>(this)));
  359. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments));
  360. if (interpreter().exception())
  361. return {};
  362. auto target_desc = m_target.get_own_property_descriptor(name);
  363. if (target_desc.has_value()) {
  364. if (interpreter().exception())
  365. return {};
  366. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(interpreter(), trap_result, target_desc.value().value))
  367. return interpreter().throw_exception<TypeError>("Proxy handler's get trap violates invariant: the returned value must match the value on the target if the property exists on the target as a non-writable, non-configurable own data property");
  368. if (target_desc.value().is_accessor_descriptor() && target_desc.value().getter == nullptr && !trap_result.is_undefined())
  369. return interpreter().throw_exception<TypeError>("Proxy handler's get trap violates invariant: the returned value must be undefined if the property exists on the target as a non-configurable accessor property with an undefined get attribute");
  370. }
  371. return trap_result;
  372. }
  373. bool ProxyObject::put(PropertyName name, Value value)
  374. {
  375. if (m_is_revoked) {
  376. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  377. return false;
  378. }
  379. auto trap = m_handler.get("set");
  380. if (interpreter().exception())
  381. return false;
  382. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  383. return m_target.put(name, value);
  384. if (!trap.is_function()) {
  385. interpreter().throw_exception<TypeError>("Proxy handler's set trap wasn't undefined, null, or callable");
  386. return false;
  387. }
  388. MarkedValueList arguments(interpreter().heap());
  389. arguments.values().append(Value(&m_target));
  390. arguments.values().append(js_string(interpreter(), name.to_string()));
  391. arguments.values().append(value);
  392. arguments.values().append(Value(const_cast<ProxyObject*>(this)));
  393. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  394. if (interpreter().exception() || !trap_result)
  395. return false;
  396. auto target_desc = m_target.get_own_property_descriptor(name);
  397. if (interpreter().exception())
  398. return false;
  399. if (target_desc.has_value() && !target_desc.value().attributes.is_configurable()) {
  400. if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(interpreter(), value, target_desc.value().value)) {
  401. interpreter().throw_exception<TypeError>("Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable, non-writable own data property");
  402. return false;
  403. }
  404. if (target_desc.value().is_accessor_descriptor() && !target_desc.value().setter) {
  405. interpreter().throw_exception<TypeError>("Proxy handler's set trap violates invariant: cannot return true for a property on the target which is a non-configurable own accessor property with an undefined set attribute");
  406. }
  407. }
  408. return true;
  409. }
  410. Value ProxyObject::delete_property(PropertyName name)
  411. {
  412. if (m_is_revoked) {
  413. interpreter().throw_exception<TypeError>("An operation was performed on a revoked Proxy object");
  414. return {};
  415. }
  416. auto trap = m_handler.get("deleteProperty");
  417. if (interpreter().exception())
  418. return {};
  419. if (trap.is_empty() || trap.is_undefined() || trap.is_null())
  420. return m_target.delete_property(name);
  421. if (!trap.is_function())
  422. return interpreter().throw_exception<TypeError>("Proxy handler's delete trap wasn't undefined, null, or callable");
  423. MarkedValueList arguments(interpreter().heap());
  424. arguments.values().append(Value(&m_target));
  425. arguments.values().append(js_string(interpreter(), name.to_string()));
  426. auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
  427. if (interpreter().exception())
  428. return {};
  429. if (!trap_result)
  430. return Value(false);
  431. auto target_desc = m_target.get_own_property_descriptor(name);
  432. if (interpreter().exception())
  433. return {};
  434. if (!target_desc.has_value())
  435. return Value(true);
  436. if (!target_desc.value().attributes.is_configurable())
  437. return interpreter().throw_exception<TypeError>("Proxy handler's delete trap violates invariant: cannot report a non-configurable own property of the target as deleted");
  438. return Value(true);
  439. }
  440. void ProxyObject::visit_children(Cell::Visitor& visitor)
  441. {
  442. Object::visit_children(visitor);
  443. visitor.visit(&m_target);
  444. visitor.visit(&m_handler);
  445. }
  446. }