reflect.js 76 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200
  1. // Copyright (C) 2011-2012 Software Languages Lab, Vrije Universiteit Brussel
  2. // This code is dual-licensed under both the Apache License and the MPL
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /* Version: MPL 1.1
  15. *
  16. * The contents of this file are subject to the Mozilla Public License Version
  17. * 1.1 (the "License"); you may not use this file except in compliance with
  18. * the License. You may obtain a copy of the License at
  19. * http://www.mozilla.org/MPL/
  20. *
  21. * Software distributed under the License is distributed on an "AS IS" basis,
  22. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  23. * for the specific language governing rights and limitations under the
  24. * License.
  25. *
  26. * The Original Code is a shim for the ES-Harmony reflection module
  27. *
  28. * The Initial Developer of the Original Code is
  29. * Tom Van Cutsem, Vrije Universiteit Brussel.
  30. * Portions created by the Initial Developer are Copyright (C) 2011-2012
  31. * the Initial Developer. All Rights Reserved.
  32. *
  33. * Contributor(s):
  34. *
  35. */
  36. // ----------------------------------------------------------------------------
  37. // This file is a polyfill for the upcoming ECMAScript Reflect API,
  38. // including support for Proxies. See the draft specification at:
  39. // http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api
  40. // http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies
  41. // For an implementation of the Handler API, see handlers.js, which implements:
  42. // http://wiki.ecmascript.org/doku.php?id=harmony:virtual_object_api
  43. // This implementation supersedes the earlier polyfill at:
  44. // code.google.com/p/es-lab/source/browse/trunk/src/proxies/DirectProxies.js
  45. // This code was tested on tracemonkey / Firefox 12
  46. // (and should run fine on older Firefox versions starting with FF4)
  47. // The code also works correctly on
  48. // v8 --harmony_proxies --harmony_weakmaps (v3.6.5.1)
  49. // Language Dependencies:
  50. // - ECMAScript 5/strict
  51. // - "old" (i.e. non-direct) Harmony Proxies
  52. // - Harmony WeakMaps
  53. // Patches:
  54. // - Object.{freeze,seal,preventExtensions}
  55. // - Object.{isFrozen,isSealed,isExtensible}
  56. // - Object.getPrototypeOf
  57. // - Object.keys
  58. // - Object.prototype.valueOf
  59. // - Object.prototype.isPrototypeOf
  60. // - Object.prototype.toString
  61. // - Object.prototype.hasOwnProperty
  62. // - Object.getOwnPropertyDescriptor
  63. // - Object.defineProperty
  64. // - Object.defineProperties
  65. // - Object.getOwnPropertyNames
  66. // - Object.getOwnPropertySymbols
  67. // - Object.getPrototypeOf
  68. // - Object.setPrototypeOf
  69. // - Object.assign
  70. // - Function.prototype.toString
  71. // - Date.prototype.toString
  72. // - Array.isArray
  73. // - Array.prototype.concat
  74. // - Proxy
  75. // Adds new globals:
  76. // - Reflect
  77. // Direct proxies can be created via Proxy(target, handler)
  78. // ----------------------------------------------------------------------------
  79. (function(global){ // function-as-module pattern
  80. "use strict";
  81. // === Direct Proxies: Invariant Enforcement ===
  82. // Direct proxies build on non-direct proxies by automatically wrapping
  83. // all user-defined proxy handlers in a Validator handler that checks and
  84. // enforces ES5 invariants.
  85. // A direct proxy is a proxy for an existing object called the target object.
  86. // A Validator handler is a wrapper for a target proxy handler H.
  87. // The Validator forwards all operations to H, but additionally
  88. // performs a number of integrity checks on the results of some traps,
  89. // to make sure H does not violate the ES5 invariants w.r.t. non-configurable
  90. // properties and non-extensible, sealed or frozen objects.
  91. // For each property that H exposes as own, non-configurable
  92. // (e.g. by returning a descriptor from a call to getOwnPropertyDescriptor)
  93. // the Validator handler defines those properties on the target object.
  94. // When the proxy becomes non-extensible, also configurable own properties
  95. // are checked against the target.
  96. // We will call properties that are defined on the target object
  97. // "fixed properties".
  98. // We will name fixed non-configurable properties "sealed properties".
  99. // We will name fixed non-configurable non-writable properties "frozen
  100. // properties".
  101. // The Validator handler upholds the following invariants w.r.t. non-configurability:
  102. // - getOwnPropertyDescriptor cannot report sealed properties as non-existent
  103. // - getOwnPropertyDescriptor cannot report incompatible changes to the
  104. // attributes of a sealed property (e.g. reporting a non-configurable
  105. // property as configurable, or reporting a non-configurable, non-writable
  106. // property as writable)
  107. // - getPropertyDescriptor cannot report sealed properties as non-existent
  108. // - getPropertyDescriptor cannot report incompatible changes to the
  109. // attributes of a sealed property. It _can_ report incompatible changes
  110. // to the attributes of non-own, inherited properties.
  111. // - defineProperty cannot make incompatible changes to the attributes of
  112. // sealed properties
  113. // - deleteProperty cannot report a successful deletion of a sealed property
  114. // - hasOwn cannot report a sealed property as non-existent
  115. // - has cannot report a sealed property as non-existent
  116. // - get cannot report inconsistent values for frozen data
  117. // properties, and must report undefined for sealed accessors with an
  118. // undefined getter
  119. // - set cannot report a successful assignment for frozen data
  120. // properties or sealed accessors with an undefined setter.
  121. // - get{Own}PropertyNames lists all sealed properties of the target.
  122. // - keys lists all enumerable sealed properties of the target.
  123. // - enumerate lists all enumerable sealed properties of the target.
  124. // - if a property of a non-extensible proxy is reported as non-existent,
  125. // then it must forever be reported as non-existent. This applies to
  126. // own and inherited properties and is enforced in the
  127. // deleteProperty, get{Own}PropertyDescriptor, has{Own},
  128. // get{Own}PropertyNames, keys and enumerate traps
  129. // Violation of any of these invariants by H will result in TypeError being
  130. // thrown.
  131. // Additionally, once Object.preventExtensions, Object.seal or Object.freeze
  132. // is invoked on the proxy, the set of own property names for the proxy is
  133. // fixed. Any property name that is not fixed is called a 'new' property.
  134. // The Validator upholds the following invariants regarding extensibility:
  135. // - getOwnPropertyDescriptor cannot report new properties as existent
  136. // (it must report them as non-existent by returning undefined)
  137. // - defineProperty cannot successfully add a new property (it must reject)
  138. // - getOwnPropertyNames cannot list new properties
  139. // - hasOwn cannot report true for new properties (it must report false)
  140. // - keys cannot list new properties
  141. // Invariants currently not enforced:
  142. // - getOwnPropertyNames lists only own property names
  143. // - keys lists only enumerable own property names
  144. // Both traps may list more property names than are actually defined on the
  145. // target.
  146. // Invariants with regard to inheritance are currently not enforced.
  147. // - a non-configurable potentially inherited property on a proxy with
  148. // non-mutable ancestry cannot be reported as non-existent
  149. // (An object with non-mutable ancestry is a non-extensible object whose
  150. // [[Prototype]] is either null or an object with non-mutable ancestry.)
  151. // Changes in Handler API compared to previous harmony:proxies, see:
  152. // http://wiki.ecmascript.org/doku.php?id=strawman:direct_proxies
  153. // http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies
  154. // ----------------------------------------------------------------------------
  155. // ---- WeakMap polyfill ----
  156. // TODO: find a proper WeakMap polyfill
  157. // define an empty WeakMap so that at least the Reflect module code
  158. // will work in the absence of WeakMaps. Proxy emulation depends on
  159. // actual WeakMaps, so will not work with this little shim.
  160. if (typeof WeakMap === "undefined") {
  161. global.WeakMap = function(){};
  162. global.WeakMap.prototype = {
  163. get: function(k) { return undefined; },
  164. set: function(k,v) { throw new Error("WeakMap not supported"); }
  165. };
  166. }
  167. // ---- Normalization functions for property descriptors ----
  168. function isStandardAttribute(name) {
  169. return /^(get|set|value|writable|enumerable|configurable)$/.test(name);
  170. }
  171. // Adapted from ES5 section 8.10.5
  172. function toPropertyDescriptor(obj) {
  173. if (Object(obj) !== obj) {
  174. throw new TypeError("property descriptor should be an Object, given: "+
  175. obj);
  176. }
  177. var desc = {};
  178. if ('enumerable' in obj) { desc.enumerable = !!obj.enumerable; }
  179. if ('configurable' in obj) { desc.configurable = !!obj.configurable; }
  180. if ('value' in obj) { desc.value = obj.value; }
  181. if ('writable' in obj) { desc.writable = !!obj.writable; }
  182. if ('get' in obj) {
  183. var getter = obj.get;
  184. if (getter !== undefined && typeof getter !== "function") {
  185. throw new TypeError("property descriptor 'get' attribute must be "+
  186. "callable or undefined, given: "+getter);
  187. }
  188. desc.get = getter;
  189. }
  190. if ('set' in obj) {
  191. var setter = obj.set;
  192. if (setter !== undefined && typeof setter !== "function") {
  193. throw new TypeError("property descriptor 'set' attribute must be "+
  194. "callable or undefined, given: "+setter);
  195. }
  196. desc.set = setter;
  197. }
  198. if ('get' in desc || 'set' in desc) {
  199. if ('value' in desc || 'writable' in desc) {
  200. throw new TypeError("property descriptor cannot be both a data and an "+
  201. "accessor descriptor: "+obj);
  202. }
  203. }
  204. return desc;
  205. }
  206. function isAccessorDescriptor(desc) {
  207. if (desc === undefined) return false;
  208. return ('get' in desc || 'set' in desc);
  209. }
  210. function isDataDescriptor(desc) {
  211. if (desc === undefined) return false;
  212. return ('value' in desc || 'writable' in desc);
  213. }
  214. function isGenericDescriptor(desc) {
  215. if (desc === undefined) return false;
  216. return !isAccessorDescriptor(desc) && !isDataDescriptor(desc);
  217. }
  218. function toCompletePropertyDescriptor(desc) {
  219. var internalDesc = toPropertyDescriptor(desc);
  220. if (isGenericDescriptor(internalDesc) || isDataDescriptor(internalDesc)) {
  221. if (!('value' in internalDesc)) { internalDesc.value = undefined; }
  222. if (!('writable' in internalDesc)) { internalDesc.writable = false; }
  223. } else {
  224. if (!('get' in internalDesc)) { internalDesc.get = undefined; }
  225. if (!('set' in internalDesc)) { internalDesc.set = undefined; }
  226. }
  227. if (!('enumerable' in internalDesc)) { internalDesc.enumerable = false; }
  228. if (!('configurable' in internalDesc)) { internalDesc.configurable = false; }
  229. return internalDesc;
  230. }
  231. function isEmptyDescriptor(desc) {
  232. return !('get' in desc) &&
  233. !('set' in desc) &&
  234. !('value' in desc) &&
  235. !('writable' in desc) &&
  236. !('enumerable' in desc) &&
  237. !('configurable' in desc);
  238. }
  239. function isEquivalentDescriptor(desc1, desc2) {
  240. return sameValue(desc1.get, desc2.get) &&
  241. sameValue(desc1.set, desc2.set) &&
  242. sameValue(desc1.value, desc2.value) &&
  243. sameValue(desc1.writable, desc2.writable) &&
  244. sameValue(desc1.enumerable, desc2.enumerable) &&
  245. sameValue(desc1.configurable, desc2.configurable);
  246. }
  247. // copied from http://wiki.ecmascript.org/doku.php?id=harmony:egal
  248. function sameValue(x, y) {
  249. if (x === y) {
  250. // 0 === -0, but they are not identical
  251. return x !== 0 || 1 / x === 1 / y;
  252. }
  253. // NaN !== NaN, but they are identical.
  254. // NaNs are the only non-reflexive value, i.e., if x !== x,
  255. // then x is a NaN.
  256. // isNaN is broken: it converts its argument to number, so
  257. // isNaN("foo") => true
  258. return x !== x && y !== y;
  259. }
  260. /**
  261. * Returns a fresh property descriptor that is guaranteed
  262. * to be complete (i.e. contain all the standard attributes).
  263. * Additionally, any non-standard enumerable properties of
  264. * attributes are copied over to the fresh descriptor.
  265. *
  266. * If attributes is undefined, returns undefined.
  267. *
  268. * See also: http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics
  269. */
  270. function normalizeAndCompletePropertyDescriptor(attributes) {
  271. if (attributes === undefined) { return undefined; }
  272. var desc = toCompletePropertyDescriptor(attributes);
  273. // Note: no need to call FromPropertyDescriptor(desc), as we represent
  274. // "internal" property descriptors as proper Objects from the start
  275. for (var name in attributes) {
  276. if (!isStandardAttribute(name)) {
  277. Object.defineProperty(desc, name,
  278. { value: attributes[name],
  279. writable: true,
  280. enumerable: true,
  281. configurable: true });
  282. }
  283. }
  284. return desc;
  285. }
  286. /**
  287. * Returns a fresh property descriptor whose standard
  288. * attributes are guaranteed to be data properties of the right type.
  289. * Additionally, any non-standard enumerable properties of
  290. * attributes are copied over to the fresh descriptor.
  291. *
  292. * If attributes is undefined, will throw a TypeError.
  293. *
  294. * See also: http://wiki.ecmascript.org/doku.php?id=harmony:proxies_semantics
  295. */
  296. function normalizePropertyDescriptor(attributes) {
  297. var desc = toPropertyDescriptor(attributes);
  298. // Note: no need to call FromGenericPropertyDescriptor(desc), as we represent
  299. // "internal" property descriptors as proper Objects from the start
  300. for (var name in attributes) {
  301. if (!isStandardAttribute(name)) {
  302. Object.defineProperty(desc, name,
  303. { value: attributes[name],
  304. writable: true,
  305. enumerable: true,
  306. configurable: true });
  307. }
  308. }
  309. return desc;
  310. }
  311. // store a reference to the real ES5 primitives before patching them later
  312. var prim_preventExtensions = Object.preventExtensions,
  313. prim_seal = Object.seal,
  314. prim_freeze = Object.freeze,
  315. prim_isExtensible = Object.isExtensible,
  316. prim_isSealed = Object.isSealed,
  317. prim_isFrozen = Object.isFrozen,
  318. prim_getPrototypeOf = Object.getPrototypeOf,
  319. prim_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor,
  320. prim_defineProperty = Object.defineProperty,
  321. prim_defineProperties = Object.defineProperties,
  322. prim_keys = Object.keys,
  323. prim_getOwnPropertyNames = Object.getOwnPropertyNames,
  324. prim_getOwnPropertySymbols = Object.getOwnPropertySymbols,
  325. prim_assign = Object.assign,
  326. prim_isArray = Array.isArray,
  327. prim_concat = Array.prototype.concat,
  328. prim_isPrototypeOf = Object.prototype.isPrototypeOf,
  329. prim_hasOwnProperty = Object.prototype.hasOwnProperty;
  330. // these will point to the patched versions of the respective methods on
  331. // Object. They are used within this module as the "intrinsic" bindings
  332. // of these methods (i.e. the "original" bindings as defined in the spec)
  333. var Object_isFrozen,
  334. Object_isSealed,
  335. Object_isExtensible,
  336. Object_getPrototypeOf,
  337. Object_getOwnPropertyNames;
  338. /**
  339. * A property 'name' is fixed if it is an own property of the target.
  340. */
  341. function isFixed(name, target) {
  342. return ({}).hasOwnProperty.call(target, name);
  343. }
  344. function isSealed(name, target) {
  345. var desc = Object.getOwnPropertyDescriptor(target, name);
  346. if (desc === undefined) { return false; }
  347. return desc.configurable === false;
  348. }
  349. function isSealedDesc(desc) {
  350. return desc !== undefined && desc.configurable === false;
  351. }
  352. /**
  353. * Performs all validation that Object.defineProperty performs,
  354. * without actually defining the property. Returns a boolean
  355. * indicating whether validation succeeded.
  356. *
  357. * Implementation transliterated from ES5.1 section 8.12.9
  358. */
  359. function isCompatibleDescriptor(extensible, current, desc) {
  360. if (current === undefined && extensible === false) {
  361. return false;
  362. }
  363. if (current === undefined && extensible === true) {
  364. return true;
  365. }
  366. if (isEmptyDescriptor(desc)) {
  367. return true;
  368. }
  369. if (isEquivalentDescriptor(current, desc)) {
  370. return true;
  371. }
  372. if (current.configurable === false) {
  373. if (desc.configurable === true) {
  374. return false;
  375. }
  376. if ('enumerable' in desc && desc.enumerable !== current.enumerable) {
  377. return false;
  378. }
  379. }
  380. if (isGenericDescriptor(desc)) {
  381. return true;
  382. }
  383. if (isDataDescriptor(current) !== isDataDescriptor(desc)) {
  384. if (current.configurable === false) {
  385. return false;
  386. }
  387. return true;
  388. }
  389. if (isDataDescriptor(current) && isDataDescriptor(desc)) {
  390. if (current.configurable === false) {
  391. if (current.writable === false && desc.writable === true) {
  392. return false;
  393. }
  394. if (current.writable === false) {
  395. if ('value' in desc && !sameValue(desc.value, current.value)) {
  396. return false;
  397. }
  398. }
  399. }
  400. return true;
  401. }
  402. if (isAccessorDescriptor(current) && isAccessorDescriptor(desc)) {
  403. if (current.configurable === false) {
  404. if ('set' in desc && !sameValue(desc.set, current.set)) {
  405. return false;
  406. }
  407. if ('get' in desc && !sameValue(desc.get, current.get)) {
  408. return false;
  409. }
  410. }
  411. }
  412. return true;
  413. }
  414. // ES6 7.3.11 SetIntegrityLevel
  415. // level is one of "sealed" or "frozen"
  416. function setIntegrityLevel(target, level) {
  417. var ownProps = Object_getOwnPropertyNames(target);
  418. var pendingException = undefined;
  419. if (level === "sealed") {
  420. var l = +ownProps.length;
  421. var k;
  422. for (var i = 0; i < l; i++) {
  423. k = String(ownProps[i]);
  424. try {
  425. Object.defineProperty(target, k, { configurable: false });
  426. } catch (e) {
  427. if (pendingException === undefined) {
  428. pendingException = e;
  429. }
  430. }
  431. }
  432. } else {
  433. // level === "frozen"
  434. var l = +ownProps.length;
  435. var k;
  436. for (var i = 0; i < l; i++) {
  437. k = String(ownProps[i]);
  438. try {
  439. var currentDesc = Object.getOwnPropertyDescriptor(target, k);
  440. if (currentDesc !== undefined) {
  441. var desc;
  442. if (isAccessorDescriptor(currentDesc)) {
  443. desc = { configurable: false }
  444. } else {
  445. desc = { configurable: false, writable: false }
  446. }
  447. Object.defineProperty(target, k, desc);
  448. }
  449. } catch (e) {
  450. if (pendingException === undefined) {
  451. pendingException = e;
  452. }
  453. }
  454. }
  455. }
  456. if (pendingException !== undefined) {
  457. throw pendingException;
  458. }
  459. return Reflect.preventExtensions(target);
  460. }
  461. // ES6 7.3.12 TestIntegrityLevel
  462. // level is one of "sealed" or "frozen"
  463. function testIntegrityLevel(target, level) {
  464. var isExtensible = Object_isExtensible(target);
  465. if (isExtensible) return false;
  466. var ownProps = Object_getOwnPropertyNames(target);
  467. var pendingException = undefined;
  468. var configurable = false;
  469. var writable = false;
  470. var l = +ownProps.length;
  471. var k;
  472. var currentDesc;
  473. for (var i = 0; i < l; i++) {
  474. k = String(ownProps[i]);
  475. try {
  476. currentDesc = Object.getOwnPropertyDescriptor(target, k);
  477. configurable = configurable || currentDesc.configurable;
  478. if (isDataDescriptor(currentDesc)) {
  479. writable = writable || currentDesc.writable;
  480. }
  481. } catch (e) {
  482. if (pendingException === undefined) {
  483. pendingException = e;
  484. configurable = true;
  485. }
  486. }
  487. }
  488. if (pendingException !== undefined) {
  489. throw pendingException;
  490. }
  491. if (level === "frozen" && writable === true) {
  492. return false;
  493. }
  494. if (configurable === true) {
  495. return false;
  496. }
  497. return true;
  498. }
  499. // ---- The Validator handler wrapper around user handlers ----
  500. /**
  501. * @param target the object wrapped by this proxy.
  502. * As long as the proxy is extensible, only non-configurable properties
  503. * are checked against the target. Once the proxy becomes non-extensible,
  504. * invariants w.r.t. non-extensibility are also enforced.
  505. *
  506. * @param handler the handler of the direct proxy. The object emulated by
  507. * this handler is validated against the target object of the direct proxy.
  508. * Any violations that the handler makes against the invariants
  509. * of the target will cause a TypeError to be thrown.
  510. *
  511. * Both target and handler must be proper Objects at initialization time.
  512. */
  513. function Validator(target, handler) {
  514. // for non-revokable proxies, these are const references
  515. // for revokable proxies, on revocation:
  516. // - this.target is set to null
  517. // - this.handler is set to a handler that throws on all traps
  518. this.target = target;
  519. this.handler = handler;
  520. }
  521. Validator.prototype = {
  522. /**
  523. * If getTrap returns undefined, the caller should perform the
  524. * default forwarding behavior.
  525. * If getTrap returns normally otherwise, the return value
  526. * will be a callable trap function. When calling the trap function,
  527. * the caller is responsible for binding its |this| to |this.handler|.
  528. */
  529. getTrap: function(trapName) {
  530. var trap = this.handler[trapName];
  531. if (trap === undefined) {
  532. // the trap was not defined,
  533. // perform the default forwarding behavior
  534. return undefined;
  535. }
  536. if (typeof trap !== "function") {
  537. throw new TypeError(trapName + " trap is not callable: "+trap);
  538. }
  539. return trap;
  540. },
  541. // === fundamental traps ===
  542. /**
  543. * If name denotes a fixed property, check:
  544. * - whether targetHandler reports it as existent
  545. * - whether the returned descriptor is compatible with the fixed property
  546. * If the proxy is non-extensible, check:
  547. * - whether name is not a new property
  548. * Additionally, the returned descriptor is normalized and completed.
  549. */
  550. getOwnPropertyDescriptor: function(name) {
  551. "use strict";
  552. var trap = this.getTrap("getOwnPropertyDescriptor");
  553. if (trap === undefined) {
  554. return Reflect.getOwnPropertyDescriptor(this.target, name);
  555. }
  556. name = String(name);
  557. var desc = trap.call(this.handler, this.target, name);
  558. desc = normalizeAndCompletePropertyDescriptor(desc);
  559. var targetDesc = Object.getOwnPropertyDescriptor(this.target, name);
  560. var extensible = Object.isExtensible(this.target);
  561. if (desc === undefined) {
  562. if (isSealedDesc(targetDesc)) {
  563. throw new TypeError("cannot report non-configurable property '"+name+
  564. "' as non-existent");
  565. }
  566. if (!extensible && targetDesc !== undefined) {
  567. // if handler is allowed to return undefined, we cannot guarantee
  568. // that it will not return a descriptor for this property later.
  569. // Once a property has been reported as non-existent on a non-extensible
  570. // object, it should forever be reported as non-existent
  571. throw new TypeError("cannot report existing own property '"+name+
  572. "' as non-existent on a non-extensible object");
  573. }
  574. return undefined;
  575. }
  576. // at this point, we know (desc !== undefined), i.e.
  577. // targetHandler reports 'name' as an existing property
  578. // Note: we could collapse the following two if-tests into a single
  579. // test. Separating out the cases to improve error reporting.
  580. if (!extensible) {
  581. if (targetDesc === undefined) {
  582. throw new TypeError("cannot report a new own property '"+
  583. name + "' on a non-extensible object");
  584. }
  585. }
  586. if (name !== undefined) {
  587. if (!isCompatibleDescriptor(extensible, targetDesc, desc)) {
  588. throw new TypeError("cannot report incompatible property descriptor "+
  589. "for property '"+name+"'");
  590. }
  591. }
  592. if (desc.configurable === false) {
  593. if (targetDesc === undefined || targetDesc.configurable === true) {
  594. // if the property is configurable or non-existent on the target,
  595. // but is reported as a non-configurable property, it may later be
  596. // reported as configurable or non-existent, which violates the
  597. // invariant that if the property might change or disappear, the
  598. // configurable attribute must be true.
  599. throw new TypeError(
  600. "cannot report a non-configurable descriptor " +
  601. "for configurable or non-existent property '" + name + "'");
  602. }
  603. if ('writable' in desc && desc.writable === false) {
  604. if (targetDesc.writable === true) {
  605. // if the property is non-configurable, writable on the target,
  606. // but is reported as non-configurable, non-writable, it may later
  607. // be reported as non-configurable, writable again, which violates
  608. // the invariant that a non-configurable, non-writable property
  609. // may not change state.
  610. throw new TypeError(
  611. "cannot report non-configurable, writable property '" + name +
  612. "' as non-configurable, non-writable");
  613. }
  614. }
  615. }
  616. return desc;
  617. },
  618. /**
  619. * In the direct proxies design with refactored prototype climbing,
  620. * this trap is deprecated. For proxies-as-prototypes, instead
  621. * of calling this trap, the get, set, has or enumerate traps are
  622. * called instead.
  623. *
  624. * In this implementation, we "abuse" getPropertyDescriptor to
  625. * support trapping the get or set traps for proxies-as-prototypes.
  626. * We do this by returning a getter/setter pair that invokes
  627. * the corresponding traps.
  628. *
  629. * While this hack works for inherited property access, it has some
  630. * quirks:
  631. *
  632. * In Firefox, this trap is only called after a prior invocation
  633. * of the 'has' trap has returned true. Hence, expect the following
  634. * behavior:
  635. * <code>
  636. * var child = Object.create(Proxy(target, handler));
  637. * child[name] // triggers handler.has(target, name)
  638. * // if that returns true, triggers handler.get(target, name, child)
  639. * </code>
  640. *
  641. * On v8, the 'in' operator, when applied to an object that inherits
  642. * from a proxy, will call getPropertyDescriptor and walk the proto-chain.
  643. * That calls the below getPropertyDescriptor trap on the proxy. The
  644. * result of the 'in'-operator is then determined by whether this trap
  645. * returns undefined or a property descriptor object. That is why
  646. * we first explicitly trigger the 'has' trap to determine whether
  647. * the property exists.
  648. *
  649. * This has the side-effect that when enumerating properties on
  650. * an object that inherits from a proxy in v8, only properties
  651. * for which 'has' returns true are returned:
  652. *
  653. * <code>
  654. * var child = Object.create(Proxy(target, handler));
  655. * for (var prop in child) {
  656. * // only enumerates prop if (prop in child) returns true
  657. * }
  658. * </code>
  659. */
  660. getPropertyDescriptor: function(name) {
  661. var handler = this;
  662. if (!handler.has(name)) return undefined;
  663. return {
  664. get: function() {
  665. return handler.get(this, name);
  666. },
  667. set: function(val) {
  668. if (handler.set(this, name, val)) {
  669. return val;
  670. } else {
  671. throw new TypeError("failed assignment to "+name);
  672. }
  673. },
  674. enumerable: true,
  675. configurable: true
  676. };
  677. },
  678. /**
  679. * If name denotes a fixed property, check for incompatible changes.
  680. * If the proxy is non-extensible, check that new properties are rejected.
  681. */
  682. defineProperty: function(name, desc) {
  683. // TODO(tvcutsem): the current tracemonkey implementation of proxies
  684. // auto-completes 'desc', which is not correct. 'desc' should be
  685. // normalized, but not completed. Consider:
  686. // Object.defineProperty(proxy, 'foo', {enumerable:false})
  687. // This trap will receive desc =
  688. // {value:undefined,writable:false,enumerable:false,configurable:false}
  689. // This will also set all other attributes to their default value,
  690. // which is unexpected and different from [[DefineOwnProperty]].
  691. // Bug filed: https://bugzilla.mozilla.org/show_bug.cgi?id=601329
  692. var trap = this.getTrap("defineProperty");
  693. if (trap === undefined) {
  694. // default forwarding behavior
  695. return Reflect.defineProperty(this.target, name, desc);
  696. }
  697. name = String(name);
  698. var descObj = normalizePropertyDescriptor(desc);
  699. var success = trap.call(this.handler, this.target, name, descObj);
  700. success = !!success; // coerce to Boolean
  701. if (success === true) {
  702. var targetDesc = Object.getOwnPropertyDescriptor(this.target, name);
  703. var extensible = Object.isExtensible(this.target);
  704. // Note: we could collapse the following two if-tests into a single
  705. // test. Separating out the cases to improve error reporting.
  706. if (!extensible) {
  707. if (targetDesc === undefined) {
  708. throw new TypeError("cannot successfully add a new property '"+
  709. name + "' to a non-extensible object");
  710. }
  711. }
  712. if (targetDesc !== undefined) {
  713. if (!isCompatibleDescriptor(extensible, targetDesc, desc)) {
  714. throw new TypeError("cannot define incompatible property "+
  715. "descriptor for property '"+name+"'");
  716. }
  717. if (isDataDescriptor(targetDesc) &&
  718. targetDesc.configurable === false &&
  719. targetDesc.writable === true) {
  720. if (desc.configurable === false && desc.writable === false) {
  721. // if the property is non-configurable, writable on the target
  722. // but was successfully reported to be updated to
  723. // non-configurable, non-writable, it can later be reported
  724. // again as non-configurable, writable, which violates
  725. // the invariant that non-configurable, non-writable properties
  726. // cannot change state
  727. throw new TypeError(
  728. "cannot successfully define non-configurable, writable " +
  729. " property '" + name + "' as non-configurable, non-writable");
  730. }
  731. }
  732. }
  733. if (desc.configurable === false && !isSealedDesc(targetDesc)) {
  734. // if the property is configurable or non-existent on the target,
  735. // but is successfully being redefined as a non-configurable property,
  736. // it may later be reported as configurable or non-existent, which violates
  737. // the invariant that if the property might change or disappear, the
  738. // configurable attribute must be true.
  739. throw new TypeError(
  740. "cannot successfully define a non-configurable " +
  741. "descriptor for configurable or non-existent property '" +
  742. name + "'");
  743. }
  744. }
  745. return success;
  746. },
  747. /**
  748. * On success, check whether the target object is indeed non-extensible.
  749. */
  750. preventExtensions: function() {
  751. var trap = this.getTrap("preventExtensions");
  752. if (trap === undefined) {
  753. // default forwarding behavior
  754. return Reflect.preventExtensions(this.target);
  755. }
  756. var success = trap.call(this.handler, this.target);
  757. success = !!success; // coerce to Boolean
  758. if (success) {
  759. if (Object_isExtensible(this.target)) {
  760. throw new TypeError("can't report extensible object as non-extensible: "+
  761. this.target);
  762. }
  763. }
  764. return success;
  765. },
  766. /**
  767. * If name denotes a sealed property, check whether handler rejects.
  768. */
  769. delete: function(name) {
  770. "use strict";
  771. var trap = this.getTrap("deleteProperty");
  772. if (trap === undefined) {
  773. // default forwarding behavior
  774. return Reflect.deleteProperty(this.target, name);
  775. }
  776. name = String(name);
  777. var res = trap.call(this.handler, this.target, name);
  778. res = !!res; // coerce to Boolean
  779. var targetDesc;
  780. if (res === true) {
  781. targetDesc = Object.getOwnPropertyDescriptor(this.target, name);
  782. if (targetDesc !== undefined && targetDesc.configurable === false) {
  783. throw new TypeError("property '" + name + "' is non-configurable "+
  784. "and can't be deleted");
  785. }
  786. if (targetDesc !== undefined && !Object_isExtensible(this.target)) {
  787. // if the property still exists on a non-extensible target but
  788. // is reported as successfully deleted, it may later be reported
  789. // as present, which violates the invariant that an own property,
  790. // deleted from a non-extensible object cannot reappear.
  791. throw new TypeError(
  792. "cannot successfully delete existing property '" + name +
  793. "' on a non-extensible object");
  794. }
  795. }
  796. return res;
  797. },
  798. /**
  799. * The getOwnPropertyNames trap was replaced by the ownKeys trap,
  800. * which now also returns an array (of strings or symbols) and
  801. * which performs the same rigorous invariant checks as getOwnPropertyNames
  802. *
  803. * See issue #48 on how this trap can still get invoked by external libs
  804. * that don't use the patched Object.getOwnPropertyNames function.
  805. */
  806. getOwnPropertyNames: function() {
  807. // Note: removed deprecation warning to avoid dependency on 'console'
  808. // (and on node, should anyway use util.deprecate). Deprecation warnings
  809. // can also be annoying when they are outside of the user's control, e.g.
  810. // when an external library calls unpatched Object.getOwnPropertyNames.
  811. // Since there is a clean fallback to `ownKeys`, the fact that the
  812. // deprecated method is still called is mostly harmless anyway.
  813. // See also issues #65 and #66.
  814. // console.warn("getOwnPropertyNames trap is deprecated. Use ownKeys instead");
  815. return this.ownKeys();
  816. },
  817. /**
  818. * Checks whether the trap result does not contain any new properties
  819. * if the proxy is non-extensible.
  820. *
  821. * Any own non-configurable properties of the target that are not included
  822. * in the trap result give rise to a TypeError. As such, we check whether the
  823. * returned result contains at least all sealed properties of the target
  824. * object.
  825. *
  826. * Additionally, the trap result is normalized.
  827. * Instead of returning the trap result directly:
  828. * - create and return a fresh Array,
  829. * - of which each element is coerced to a String
  830. *
  831. * This trap is called a.o. by Reflect.ownKeys, Object.getOwnPropertyNames
  832. * and Object.keys (the latter filters out only the enumerable own properties).
  833. */
  834. ownKeys: function() {
  835. var trap = this.getTrap("ownKeys");
  836. if (trap === undefined) {
  837. // default forwarding behavior
  838. return Reflect.ownKeys(this.target);
  839. }
  840. var trapResult = trap.call(this.handler, this.target);
  841. // propNames is used as a set of strings
  842. var propNames = Object.create(null);
  843. var numProps = +trapResult.length;
  844. var result = new Array(numProps);
  845. for (var i = 0; i < numProps; i++) {
  846. var s = String(trapResult[i]);
  847. if (!Object.isExtensible(this.target) && !isFixed(s, this.target)) {
  848. // non-extensible proxies don't tolerate new own property names
  849. throw new TypeError("ownKeys trap cannot list a new "+
  850. "property '"+s+"' on a non-extensible object");
  851. }
  852. propNames[s] = true;
  853. result[i] = s;
  854. }
  855. var ownProps = Object_getOwnPropertyNames(this.target);
  856. var target = this.target;
  857. ownProps.forEach(function (ownProp) {
  858. if (!propNames[ownProp]) {
  859. if (isSealed(ownProp, target)) {
  860. throw new TypeError("ownKeys trap failed to include "+
  861. "non-configurable property '"+ownProp+"'");
  862. }
  863. if (!Object.isExtensible(target) &&
  864. isFixed(ownProp, target)) {
  865. // if handler is allowed to report ownProp as non-existent,
  866. // we cannot guarantee that it will never later report it as
  867. // existent. Once a property has been reported as non-existent
  868. // on a non-extensible object, it should forever be reported as
  869. // non-existent
  870. throw new TypeError("ownKeys trap cannot report existing own property '"+
  871. ownProp+"' as non-existent on a non-extensible object");
  872. }
  873. }
  874. });
  875. return result;
  876. },
  877. /**
  878. * Checks whether the trap result is consistent with the state of the
  879. * wrapped target.
  880. */
  881. isExtensible: function() {
  882. var trap = this.getTrap("isExtensible");
  883. if (trap === undefined) {
  884. // default forwarding behavior
  885. return Reflect.isExtensible(this.target);
  886. }
  887. var result = trap.call(this.handler, this.target);
  888. result = !!result; // coerce to Boolean
  889. var state = Object_isExtensible(this.target);
  890. if (result !== state) {
  891. if (result) {
  892. throw new TypeError("cannot report non-extensible object as extensible: "+
  893. this.target);
  894. } else {
  895. throw new TypeError("cannot report extensible object as non-extensible: "+
  896. this.target);
  897. }
  898. }
  899. return state;
  900. },
  901. /**
  902. * Check whether the trap result corresponds to the target's [[Prototype]]
  903. */
  904. getPrototypeOf: function() {
  905. var trap = this.getTrap("getPrototypeOf");
  906. if (trap === undefined) {
  907. // default forwarding behavior
  908. return Reflect.getPrototypeOf(this.target);
  909. }
  910. var allegedProto = trap.call(this.handler, this.target);
  911. if (!Object_isExtensible(this.target)) {
  912. var actualProto = Object_getPrototypeOf(this.target);
  913. if (!sameValue(allegedProto, actualProto)) {
  914. throw new TypeError("prototype value does not match: " + this.target);
  915. }
  916. }
  917. return allegedProto;
  918. },
  919. /**
  920. * If target is non-extensible and setPrototypeOf trap returns true,
  921. * check whether the trap result corresponds to the target's [[Prototype]]
  922. */
  923. setPrototypeOf: function(newProto) {
  924. var trap = this.getTrap("setPrototypeOf");
  925. if (trap === undefined) {
  926. // default forwarding behavior
  927. return Reflect.setPrototypeOf(this.target, newProto);
  928. }
  929. var success = trap.call(this.handler, this.target, newProto);
  930. success = !!success;
  931. if (success && !Object_isExtensible(this.target)) {
  932. var actualProto = Object_getPrototypeOf(this.target);
  933. if (!sameValue(newProto, actualProto)) {
  934. throw new TypeError("prototype value does not match: " + this.target);
  935. }
  936. }
  937. return success;
  938. },
  939. /**
  940. * In the direct proxies design with refactored prototype climbing,
  941. * this trap is deprecated. For proxies-as-prototypes, for-in will
  942. * call the enumerate() trap. If that trap is not defined, the
  943. * operation is forwarded to the target, no more fallback on this
  944. * fundamental trap.
  945. */
  946. getPropertyNames: function() {
  947. throw new TypeError("getPropertyNames trap is deprecated");
  948. },
  949. // === derived traps ===
  950. /**
  951. * If name denotes a fixed property, check whether the trap returns true.
  952. */
  953. has: function(name) {
  954. var trap = this.getTrap("has");
  955. if (trap === undefined) {
  956. // default forwarding behavior
  957. return Reflect.has(this.target, name);
  958. }
  959. name = String(name);
  960. var res = trap.call(this.handler, this.target, name);
  961. res = !!res; // coerce to Boolean
  962. if (res === false) {
  963. if (isSealed(name, this.target)) {
  964. throw new TypeError("cannot report existing non-configurable own "+
  965. "property '"+ name + "' as a non-existent "+
  966. "property");
  967. }
  968. if (!Object.isExtensible(this.target) &&
  969. isFixed(name, this.target)) {
  970. // if handler is allowed to return false, we cannot guarantee
  971. // that it will not return true for this property later.
  972. // Once a property has been reported as non-existent on a non-extensible
  973. // object, it should forever be reported as non-existent
  974. throw new TypeError("cannot report existing own property '"+name+
  975. "' as non-existent on a non-extensible object");
  976. }
  977. }
  978. // if res === true, we don't need to check for extensibility
  979. // even for a non-extensible proxy that has no own name property,
  980. // the property may have been inherited
  981. return res;
  982. },
  983. /**
  984. * If name denotes a fixed non-configurable, non-writable data property,
  985. * check its return value against the previously asserted value of the
  986. * fixed property.
  987. */
  988. get: function(receiver, name) {
  989. // experimental support for invoke() trap on platforms that
  990. // support __noSuchMethod__
  991. /*
  992. if (name === '__noSuchMethod__') {
  993. var handler = this;
  994. return function(name, args) {
  995. return handler.invoke(receiver, name, args);
  996. }
  997. }
  998. */
  999. var trap = this.getTrap("get");
  1000. if (trap === undefined) {
  1001. // default forwarding behavior
  1002. return Reflect.get(this.target, name, receiver);
  1003. }
  1004. name = String(name);
  1005. var res = trap.call(this.handler, this.target, name, receiver);
  1006. var fixedDesc = Object.getOwnPropertyDescriptor(this.target, name);
  1007. // check consistency of the returned value
  1008. if (fixedDesc !== undefined) { // getting an existing property
  1009. if (isDataDescriptor(fixedDesc) &&
  1010. fixedDesc.configurable === false &&
  1011. fixedDesc.writable === false) { // own frozen data property
  1012. if (!sameValue(res, fixedDesc.value)) {
  1013. throw new TypeError("cannot report inconsistent value for "+
  1014. "non-writable, non-configurable property '"+
  1015. name+"'");
  1016. }
  1017. } else { // it's an accessor property
  1018. if (isAccessorDescriptor(fixedDesc) &&
  1019. fixedDesc.configurable === false &&
  1020. fixedDesc.get === undefined) {
  1021. if (res !== undefined) {
  1022. throw new TypeError("must report undefined for non-configurable "+
  1023. "accessor property '"+name+"' without getter");
  1024. }
  1025. }
  1026. }
  1027. }
  1028. return res;
  1029. },
  1030. /**
  1031. * If name denotes a fixed non-configurable, non-writable data property,
  1032. * check that the trap rejects the assignment.
  1033. */
  1034. set: function(receiver, name, val) {
  1035. var trap = this.getTrap("set");
  1036. if (trap === undefined) {
  1037. // default forwarding behavior
  1038. return Reflect.set(this.target, name, val, receiver);
  1039. }
  1040. name = String(name);
  1041. var res = trap.call(this.handler, this.target, name, val, receiver);
  1042. res = !!res; // coerce to Boolean
  1043. // if success is reported, check whether property is truly assignable
  1044. if (res === true) {
  1045. var fixedDesc = Object.getOwnPropertyDescriptor(this.target, name);
  1046. if (fixedDesc !== undefined) { // setting an existing property
  1047. if (isDataDescriptor(fixedDesc) &&
  1048. fixedDesc.configurable === false &&
  1049. fixedDesc.writable === false) {
  1050. if (!sameValue(val, fixedDesc.value)) {
  1051. throw new TypeError("cannot successfully assign to a "+
  1052. "non-writable, non-configurable property '"+
  1053. name+"'");
  1054. }
  1055. } else {
  1056. if (isAccessorDescriptor(fixedDesc) &&
  1057. fixedDesc.configurable === false && // non-configurable
  1058. fixedDesc.set === undefined) { // accessor with undefined setter
  1059. throw new TypeError("setting a property '"+name+"' that has "+
  1060. " only a getter");
  1061. }
  1062. }
  1063. }
  1064. }
  1065. return res;
  1066. },
  1067. /**
  1068. * Any own enumerable non-configurable properties of the target that are not
  1069. * included in the trap result give rise to a TypeError. As such, we check
  1070. * whether the returned result contains at least all sealed enumerable properties
  1071. * of the target object.
  1072. *
  1073. * The trap should return an iterator.
  1074. *
  1075. * However, as implementations of pre-direct proxies still expect enumerate
  1076. * to return an array of strings, we convert the iterator into an array.
  1077. */
  1078. enumerate: function() {
  1079. var trap = this.getTrap("enumerate");
  1080. if (trap === undefined) {
  1081. // default forwarding behavior
  1082. var trapResult = Reflect.enumerate(this.target);
  1083. var result = [];
  1084. var nxt = trapResult.next();
  1085. while (!nxt.done) {
  1086. result.push(String(nxt.value));
  1087. nxt = trapResult.next();
  1088. }
  1089. return result;
  1090. }
  1091. var trapResult = trap.call(this.handler, this.target);
  1092. if (trapResult === null ||
  1093. trapResult === undefined ||
  1094. trapResult.next === undefined) {
  1095. throw new TypeError("enumerate trap should return an iterator, got: "+
  1096. trapResult);
  1097. }
  1098. // propNames is used as a set of strings
  1099. var propNames = Object.create(null);
  1100. // var numProps = +trapResult.length;
  1101. var result = []; // new Array(numProps);
  1102. // trapResult is supposed to be an iterator
  1103. // drain iterator to array as current implementations still expect
  1104. // enumerate to return an array of strings
  1105. var nxt = trapResult.next();
  1106. while (!nxt.done) {
  1107. var s = String(nxt.value);
  1108. if (propNames[s]) {
  1109. throw new TypeError("enumerate trap cannot list a "+
  1110. "duplicate property '"+s+"'");
  1111. }
  1112. propNames[s] = true;
  1113. result.push(s);
  1114. nxt = trapResult.next();
  1115. }
  1116. /*for (var i = 0; i < numProps; i++) {
  1117. var s = String(trapResult[i]);
  1118. if (propNames[s]) {
  1119. throw new TypeError("enumerate trap cannot list a "+
  1120. "duplicate property '"+s+"'");
  1121. }
  1122. propNames[s] = true;
  1123. result[i] = s;
  1124. } */
  1125. var ownEnumerableProps = Object.keys(this.target);
  1126. var target = this.target;
  1127. ownEnumerableProps.forEach(function (ownEnumerableProp) {
  1128. if (!propNames[ownEnumerableProp]) {
  1129. if (isSealed(ownEnumerableProp, target)) {
  1130. throw new TypeError("enumerate trap failed to include "+
  1131. "non-configurable enumerable property '"+
  1132. ownEnumerableProp+"'");
  1133. }
  1134. if (!Object.isExtensible(target) &&
  1135. isFixed(ownEnumerableProp, target)) {
  1136. // if handler is allowed not to report ownEnumerableProp as an own
  1137. // property, we cannot guarantee that it will never report it as
  1138. // an own property later. Once a property has been reported as
  1139. // non-existent on a non-extensible object, it should forever be
  1140. // reported as non-existent
  1141. throw new TypeError("cannot report existing own property '"+
  1142. ownEnumerableProp+"' as non-existent on a "+
  1143. "non-extensible object");
  1144. }
  1145. }
  1146. });
  1147. return result;
  1148. },
  1149. /**
  1150. * The iterate trap is deprecated by the enumerate trap.
  1151. */
  1152. iterate: Validator.prototype.enumerate,
  1153. /**
  1154. * Any own non-configurable properties of the target that are not included
  1155. * in the trap result give rise to a TypeError. As such, we check whether the
  1156. * returned result contains at least all sealed properties of the target
  1157. * object.
  1158. *
  1159. * The trap result is normalized.
  1160. * The trap result is not returned directly. Instead:
  1161. * - create and return a fresh Array,
  1162. * - of which each element is coerced to String,
  1163. * - which does not contain duplicates
  1164. *
  1165. * FIXME: keys trap is deprecated
  1166. */
  1167. /*
  1168. keys: function() {
  1169. var trap = this.getTrap("keys");
  1170. if (trap === undefined) {
  1171. // default forwarding behavior
  1172. return Reflect.keys(this.target);
  1173. }
  1174. var trapResult = trap.call(this.handler, this.target);
  1175. // propNames is used as a set of strings
  1176. var propNames = Object.create(null);
  1177. var numProps = +trapResult.length;
  1178. var result = new Array(numProps);
  1179. for (var i = 0; i < numProps; i++) {
  1180. var s = String(trapResult[i]);
  1181. if (propNames[s]) {
  1182. throw new TypeError("keys trap cannot list a "+
  1183. "duplicate property '"+s+"'");
  1184. }
  1185. if (!Object.isExtensible(this.target) && !isFixed(s, this.target)) {
  1186. // non-extensible proxies don't tolerate new own property names
  1187. throw new TypeError("keys trap cannot list a new "+
  1188. "property '"+s+"' on a non-extensible object");
  1189. }
  1190. propNames[s] = true;
  1191. result[i] = s;
  1192. }
  1193. var ownEnumerableProps = Object.keys(this.target);
  1194. var target = this.target;
  1195. ownEnumerableProps.forEach(function (ownEnumerableProp) {
  1196. if (!propNames[ownEnumerableProp]) {
  1197. if (isSealed(ownEnumerableProp, target)) {
  1198. throw new TypeError("keys trap failed to include "+
  1199. "non-configurable enumerable property '"+
  1200. ownEnumerableProp+"'");
  1201. }
  1202. if (!Object.isExtensible(target) &&
  1203. isFixed(ownEnumerableProp, target)) {
  1204. // if handler is allowed not to report ownEnumerableProp as an own
  1205. // property, we cannot guarantee that it will never report it as
  1206. // an own property later. Once a property has been reported as
  1207. // non-existent on a non-extensible object, it should forever be
  1208. // reported as non-existent
  1209. throw new TypeError("cannot report existing own property '"+
  1210. ownEnumerableProp+"' as non-existent on a "+
  1211. "non-extensible object");
  1212. }
  1213. }
  1214. });
  1215. return result;
  1216. },
  1217. */
  1218. /**
  1219. * New trap that reifies [[Call]].
  1220. * If the target is a function, then a call to
  1221. * proxy(...args)
  1222. * Triggers this trap
  1223. */
  1224. apply: function(target, thisBinding, args) {
  1225. var trap = this.getTrap("apply");
  1226. if (trap === undefined) {
  1227. return Reflect.apply(target, thisBinding, args);
  1228. }
  1229. if (typeof this.target === "function") {
  1230. return trap.call(this.handler, target, thisBinding, args);
  1231. } else {
  1232. throw new TypeError("apply: "+ target + " is not a function");
  1233. }
  1234. },
  1235. /**
  1236. * New trap that reifies [[Construct]].
  1237. * If the target is a function, then a call to
  1238. * new proxy(...args)
  1239. * Triggers this trap
  1240. */
  1241. construct: function(target, args, newTarget) {
  1242. var trap = this.getTrap("construct");
  1243. if (trap === undefined) {
  1244. return Reflect.construct(target, args, newTarget);
  1245. }
  1246. if (typeof target !== "function") {
  1247. throw new TypeError("new: "+ target + " is not a function");
  1248. }
  1249. if (newTarget === undefined) {
  1250. newTarget = target;
  1251. } else {
  1252. if (typeof newTarget !== "function") {
  1253. throw new TypeError("new: "+ newTarget + " is not a function");
  1254. }
  1255. }
  1256. return trap.call(this.handler, target, args, newTarget);
  1257. }
  1258. };
  1259. // ---- end of the Validator handler wrapper handler ----
  1260. // In what follows, a 'direct proxy' is a proxy
  1261. // whose handler is a Validator. Such proxies can be made non-extensible,
  1262. // sealed or frozen without losing the ability to trap.
  1263. // maps direct proxies to their Validator handlers
  1264. var directProxies = new WeakMap();
  1265. // patch Object.{preventExtensions,seal,freeze} so that
  1266. // they recognize fixable proxies and act accordingly
  1267. Object.preventExtensions = function(subject) {
  1268. var vhandler = directProxies.get(subject);
  1269. if (vhandler !== undefined) {
  1270. if (vhandler.preventExtensions()) {
  1271. return subject;
  1272. } else {
  1273. throw new TypeError("preventExtensions on "+subject+" rejected");
  1274. }
  1275. } else {
  1276. return prim_preventExtensions(subject);
  1277. }
  1278. };
  1279. Object.seal = function(subject) {
  1280. setIntegrityLevel(subject, "sealed");
  1281. return subject;
  1282. };
  1283. Object.freeze = function(subject) {
  1284. setIntegrityLevel(subject, "frozen");
  1285. return subject;
  1286. };
  1287. Object.isExtensible = Object_isExtensible = function(subject) {
  1288. var vHandler = directProxies.get(subject);
  1289. if (vHandler !== undefined) {
  1290. return vHandler.isExtensible();
  1291. } else {
  1292. return prim_isExtensible(subject);
  1293. }
  1294. };
  1295. Object.isSealed = Object_isSealed = function(subject) {
  1296. return testIntegrityLevel(subject, "sealed");
  1297. };
  1298. Object.isFrozen = Object_isFrozen = function(subject) {
  1299. return testIntegrityLevel(subject, "frozen");
  1300. };
  1301. Object.getPrototypeOf = Object_getPrototypeOf = function(subject) {
  1302. var vHandler = directProxies.get(subject);
  1303. if (vHandler !== undefined) {
  1304. return vHandler.getPrototypeOf();
  1305. } else {
  1306. return prim_getPrototypeOf(subject);
  1307. }
  1308. };
  1309. // patch Object.getOwnPropertyDescriptor to directly call
  1310. // the Validator.prototype.getOwnPropertyDescriptor trap
  1311. // This is to circumvent an assertion in the built-in Proxy
  1312. // trapping mechanism of v8, which disallows that trap to
  1313. // return non-configurable property descriptors (as per the
  1314. // old Proxy design)
  1315. Object.getOwnPropertyDescriptor = function(subject, name) {
  1316. var vhandler = directProxies.get(subject);
  1317. if (vhandler !== undefined) {
  1318. return vhandler.getOwnPropertyDescriptor(name);
  1319. } else {
  1320. return prim_getOwnPropertyDescriptor(subject, name);
  1321. }
  1322. };
  1323. // patch Object.defineProperty to directly call
  1324. // the Validator.prototype.defineProperty trap
  1325. // This is to circumvent two issues with the built-in
  1326. // trap mechanism:
  1327. // 1) the current tracemonkey implementation of proxies
  1328. // auto-completes 'desc', which is not correct. 'desc' should be
  1329. // normalized, but not completed. Consider:
  1330. // Object.defineProperty(proxy, 'foo', {enumerable:false})
  1331. // This trap will receive desc =
  1332. // {value:undefined,writable:false,enumerable:false,configurable:false}
  1333. // This will also set all other attributes to their default value,
  1334. // which is unexpected and different from [[DefineOwnProperty]].
  1335. // Bug filed: https://bugzilla.mozilla.org/show_bug.cgi?id=601329
  1336. // 2) the current spidermonkey implementation does not
  1337. // throw an exception when this trap returns 'false', but instead silently
  1338. // ignores the operation (this is regardless of strict-mode)
  1339. // 2a) v8 does throw an exception for this case, but includes the rather
  1340. // unhelpful error message:
  1341. // 'Proxy handler #<Object> returned false from 'defineProperty' trap'
  1342. Object.defineProperty = function(subject, name, desc) {
  1343. var vhandler = directProxies.get(subject);
  1344. if (vhandler !== undefined) {
  1345. var normalizedDesc = normalizePropertyDescriptor(desc);
  1346. var success = vhandler.defineProperty(name, normalizedDesc);
  1347. if (success === false) {
  1348. throw new TypeError("can't redefine property '"+name+"'");
  1349. }
  1350. return subject;
  1351. } else {
  1352. return prim_defineProperty(subject, name, desc);
  1353. }
  1354. };
  1355. Object.defineProperties = function(subject, descs) {
  1356. var vhandler = directProxies.get(subject);
  1357. if (vhandler !== undefined) {
  1358. var names = Object.keys(descs);
  1359. for (var i = 0; i < names.length; i++) {
  1360. var name = names[i];
  1361. var normalizedDesc = normalizePropertyDescriptor(descs[name]);
  1362. var success = vhandler.defineProperty(name, normalizedDesc);
  1363. if (success === false) {
  1364. throw new TypeError("can't redefine property '"+name+"'");
  1365. }
  1366. }
  1367. return subject;
  1368. } else {
  1369. return prim_defineProperties(subject, descs);
  1370. }
  1371. };
  1372. Object.keys = function(subject) {
  1373. var vHandler = directProxies.get(subject);
  1374. if (vHandler !== undefined) {
  1375. var ownKeys = vHandler.ownKeys();
  1376. var result = [];
  1377. for (var i = 0; i < ownKeys.length; i++) {
  1378. var k = String(ownKeys[i]);
  1379. var desc = Object.getOwnPropertyDescriptor(subject, k);
  1380. if (desc !== undefined && desc.enumerable === true) {
  1381. result.push(k);
  1382. }
  1383. }
  1384. return result;
  1385. } else {
  1386. return prim_keys(subject);
  1387. }
  1388. }
  1389. Object.getOwnPropertyNames = Object_getOwnPropertyNames = function(subject) {
  1390. var vHandler = directProxies.get(subject);
  1391. if (vHandler !== undefined) {
  1392. return vHandler.ownKeys();
  1393. } else {
  1394. return prim_getOwnPropertyNames(subject);
  1395. }
  1396. }
  1397. // fixes issue #71 (Calling Object.getOwnPropertySymbols() on a Proxy
  1398. // throws an error)
  1399. if (prim_getOwnPropertySymbols !== undefined) {
  1400. Object.getOwnPropertySymbols = function(subject) {
  1401. var vHandler = directProxies.get(subject);
  1402. if (vHandler !== undefined) {
  1403. // as this shim does not support symbols, a Proxy never advertises
  1404. // any symbol-valued own properties
  1405. return [];
  1406. } else {
  1407. return prim_getOwnPropertySymbols(subject);
  1408. }
  1409. };
  1410. }
  1411. // fixes issue #72 ('Illegal access' error when using Object.assign)
  1412. // Object.assign polyfill based on a polyfill posted on MDN:
  1413. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/\
  1414. // Global_Objects/Object/assign
  1415. // Note that this polyfill does not support Symbols, but this Proxy Shim
  1416. // does not support Symbols anyway.
  1417. if (prim_assign !== undefined) {
  1418. Object.assign = function (target) {
  1419. // check if any argument is a proxy object
  1420. var noProxies = true;
  1421. for (var i = 0; i < arguments.length; i++) {
  1422. var vHandler = directProxies.get(arguments[i]);
  1423. if (vHandler !== undefined) {
  1424. noProxies = false;
  1425. break;
  1426. }
  1427. }
  1428. if (noProxies) {
  1429. // not a single argument is a proxy, perform built-in algorithm
  1430. return prim_assign.apply(Object, arguments);
  1431. }
  1432. // there is at least one proxy argument, use the polyfill
  1433. if (target === undefined || target === null) {
  1434. throw new TypeError('Cannot convert undefined or null to object');
  1435. }
  1436. var output = Object(target);
  1437. for (var index = 1; index < arguments.length; index++) {
  1438. var source = arguments[index];
  1439. if (source !== undefined && source !== null) {
  1440. for (var nextKey in source) {
  1441. if (source.hasOwnProperty(nextKey)) {
  1442. output[nextKey] = source[nextKey];
  1443. }
  1444. }
  1445. }
  1446. }
  1447. return output;
  1448. };
  1449. }
  1450. // returns whether an argument is a reference to an object,
  1451. // which is legal as a WeakMap key.
  1452. function isObject(arg) {
  1453. var type = typeof arg;
  1454. return (type === 'object' && arg !== null) || (type === 'function');
  1455. };
  1456. // a wrapper for WeakMap.get which returns the undefined value
  1457. // for keys that are not objects (in which case the underlying
  1458. // WeakMap would have thrown a TypeError).
  1459. function safeWeakMapGet(map, key) {
  1460. return isObject(key) ? map.get(key) : undefined;
  1461. };
  1462. // returns a new function of zero arguments that recursively
  1463. // unwraps any proxies specified as the |this|-value.
  1464. // The primitive is assumed to be a zero-argument method
  1465. // that uses its |this|-binding.
  1466. function makeUnwrapping0ArgMethod(primitive) {
  1467. return function builtin() {
  1468. var vHandler = safeWeakMapGet(directProxies, this);
  1469. if (vHandler !== undefined) {
  1470. return builtin.call(vHandler.target);
  1471. } else {
  1472. return primitive.call(this);
  1473. }
  1474. }
  1475. };
  1476. // returns a new function of 1 arguments that recursively
  1477. // unwraps any proxies specified as the |this|-value.
  1478. // The primitive is assumed to be a 1-argument method
  1479. // that uses its |this|-binding.
  1480. function makeUnwrapping1ArgMethod(primitive) {
  1481. return function builtin(arg) {
  1482. var vHandler = safeWeakMapGet(directProxies, this);
  1483. if (vHandler !== undefined) {
  1484. return builtin.call(vHandler.target, arg);
  1485. } else {
  1486. return primitive.call(this, arg);
  1487. }
  1488. }
  1489. };
  1490. Object.prototype.valueOf =
  1491. makeUnwrapping0ArgMethod(Object.prototype.valueOf);
  1492. Object.prototype.toString =
  1493. makeUnwrapping0ArgMethod(Object.prototype.toString);
  1494. Function.prototype.toString =
  1495. makeUnwrapping0ArgMethod(Function.prototype.toString);
  1496. Date.prototype.toString =
  1497. makeUnwrapping0ArgMethod(Date.prototype.toString);
  1498. Object.prototype.isPrototypeOf = function builtin(arg) {
  1499. // bugfix thanks to Bill Mark:
  1500. // built-in isPrototypeOf does not unwrap proxies used
  1501. // as arguments. So, we implement the builtin ourselves,
  1502. // based on the ECMAScript 6 spec. Our encoding will
  1503. // make sure that if a proxy is used as an argument,
  1504. // its getPrototypeOf trap will be called.
  1505. while (true) {
  1506. var vHandler2 = safeWeakMapGet(directProxies, arg);
  1507. if (vHandler2 !== undefined) {
  1508. arg = vHandler2.getPrototypeOf();
  1509. if (arg === null) {
  1510. return false;
  1511. } else if (sameValue(arg, this)) {
  1512. return true;
  1513. }
  1514. } else {
  1515. return prim_isPrototypeOf.call(this, arg);
  1516. }
  1517. }
  1518. };
  1519. Array.isArray = function(subject) {
  1520. var vHandler = safeWeakMapGet(directProxies, subject);
  1521. if (vHandler !== undefined) {
  1522. return Array.isArray(vHandler.target);
  1523. } else {
  1524. return prim_isArray(subject);
  1525. }
  1526. };
  1527. function isProxyArray(arg) {
  1528. var vHandler = safeWeakMapGet(directProxies, arg);
  1529. if (vHandler !== undefined) {
  1530. return Array.isArray(vHandler.target);
  1531. }
  1532. return false;
  1533. }
  1534. // Array.prototype.concat internally tests whether one of its
  1535. // arguments is an Array, by checking whether [[Class]] == "Array"
  1536. // As such, it will fail to recognize proxies-for-arrays as arrays.
  1537. // We patch Array.prototype.concat so that it "unwraps" proxies-for-arrays
  1538. // by making a copy. This will trigger the exact same sequence of
  1539. // traps on the proxy-for-array as if we would not have unwrapped it.
  1540. // See <https://github.com/tvcutsem/harmony-reflect/issues/19> for more.
  1541. Array.prototype.concat = function(/*...args*/) {
  1542. var length;
  1543. for (var i = 0; i < arguments.length; i++) {
  1544. if (isProxyArray(arguments[i])) {
  1545. length = arguments[i].length;
  1546. arguments[i] = Array.prototype.slice.call(arguments[i], 0, length);
  1547. }
  1548. }
  1549. return prim_concat.apply(this, arguments);
  1550. };
  1551. // setPrototypeOf support on platforms that support __proto__
  1552. var prim_setPrototypeOf = Object.setPrototypeOf;
  1553. // patch and extract original __proto__ setter
  1554. var __proto__setter = (function() {
  1555. var protoDesc = prim_getOwnPropertyDescriptor(Object.prototype,'__proto__');
  1556. if (protoDesc === undefined ||
  1557. typeof protoDesc.set !== "function") {
  1558. return function() {
  1559. throw new TypeError("setPrototypeOf not supported on this platform");
  1560. }
  1561. }
  1562. // see if we can actually mutate a prototype with the generic setter
  1563. // (e.g. Chrome v28 doesn't allow setting __proto__ via the generic setter)
  1564. try {
  1565. protoDesc.set.call({},{});
  1566. } catch (e) {
  1567. return function() {
  1568. throw new TypeError("setPrototypeOf not supported on this platform");
  1569. }
  1570. }
  1571. prim_defineProperty(Object.prototype, '__proto__', {
  1572. set: function(newProto) {
  1573. return Object.setPrototypeOf(this, Object(newProto));
  1574. }
  1575. });
  1576. return protoDesc.set;
  1577. }());
  1578. Object.setPrototypeOf = function(target, newProto) {
  1579. var handler = directProxies.get(target);
  1580. if (handler !== undefined) {
  1581. if (handler.setPrototypeOf(newProto)) {
  1582. return target;
  1583. } else {
  1584. throw new TypeError("proxy rejected prototype mutation");
  1585. }
  1586. } else {
  1587. if (!Object_isExtensible(target)) {
  1588. throw new TypeError("can't set prototype on non-extensible object: " +
  1589. target);
  1590. }
  1591. if (prim_setPrototypeOf)
  1592. return prim_setPrototypeOf(target, newProto);
  1593. if (Object(newProto) !== newProto || newProto === null) {
  1594. throw new TypeError("Object prototype may only be an Object or null: " +
  1595. newProto);
  1596. // throw new TypeError("prototype must be an object or null")
  1597. }
  1598. __proto__setter.call(target, newProto);
  1599. return target;
  1600. }
  1601. }
  1602. Object.prototype.hasOwnProperty = function(name) {
  1603. var handler = safeWeakMapGet(directProxies, this);
  1604. if (handler !== undefined) {
  1605. var desc = handler.getOwnPropertyDescriptor(name);
  1606. return desc !== undefined;
  1607. } else {
  1608. return prim_hasOwnProperty.call(this, name);
  1609. }
  1610. }
  1611. // ============= Reflection module =============
  1612. // see http://wiki.ecmascript.org/doku.php?id=harmony:reflect_api
  1613. var Reflect = {
  1614. getOwnPropertyDescriptor: function(target, name) {
  1615. return Object.getOwnPropertyDescriptor(target, name);
  1616. },
  1617. defineProperty: function(target, name, desc) {
  1618. // if target is a proxy, invoke its "defineProperty" trap
  1619. var handler = directProxies.get(target);
  1620. if (handler !== undefined) {
  1621. return handler.defineProperty(target, name, desc);
  1622. }
  1623. // Implementation transliterated from [[DefineOwnProperty]]
  1624. // see ES5.1 section 8.12.9
  1625. // this is the _exact same algorithm_ as the isCompatibleDescriptor
  1626. // algorithm defined above, except that at every place it
  1627. // returns true, this algorithm actually does define the property.
  1628. var current = Object.getOwnPropertyDescriptor(target, name);
  1629. var extensible = Object.isExtensible(target);
  1630. if (current === undefined && extensible === false) {
  1631. return false;
  1632. }
  1633. if (current === undefined && extensible === true) {
  1634. Object.defineProperty(target, name, desc); // should never fail
  1635. return true;
  1636. }
  1637. if (isEmptyDescriptor(desc)) {
  1638. return true;
  1639. }
  1640. if (isEquivalentDescriptor(current, desc)) {
  1641. return true;
  1642. }
  1643. if (current.configurable === false) {
  1644. if (desc.configurable === true) {
  1645. return false;
  1646. }
  1647. if ('enumerable' in desc && desc.enumerable !== current.enumerable) {
  1648. return false;
  1649. }
  1650. }
  1651. if (isGenericDescriptor(desc)) {
  1652. // no further validation necessary
  1653. } else if (isDataDescriptor(current) !== isDataDescriptor(desc)) {
  1654. if (current.configurable === false) {
  1655. return false;
  1656. }
  1657. } else if (isDataDescriptor(current) && isDataDescriptor(desc)) {
  1658. if (current.configurable === false) {
  1659. if (current.writable === false && desc.writable === true) {
  1660. return false;
  1661. }
  1662. if (current.writable === false) {
  1663. if ('value' in desc && !sameValue(desc.value, current.value)) {
  1664. return false;
  1665. }
  1666. }
  1667. }
  1668. } else if (isAccessorDescriptor(current) && isAccessorDescriptor(desc)) {
  1669. if (current.configurable === false) {
  1670. if ('set' in desc && !sameValue(desc.set, current.set)) {
  1671. return false;
  1672. }
  1673. if ('get' in desc && !sameValue(desc.get, current.get)) {
  1674. return false;
  1675. }
  1676. }
  1677. }
  1678. Object.defineProperty(target, name, desc); // should never fail
  1679. return true;
  1680. },
  1681. deleteProperty: function(target, name) {
  1682. var handler = directProxies.get(target);
  1683. if (handler !== undefined) {
  1684. return handler.delete(name);
  1685. }
  1686. var desc = Object.getOwnPropertyDescriptor(target, name);
  1687. if (desc === undefined) {
  1688. return true;
  1689. }
  1690. if (desc.configurable === true) {
  1691. delete target[name];
  1692. return true;
  1693. }
  1694. return false;
  1695. },
  1696. getPrototypeOf: function(target) {
  1697. return Object.getPrototypeOf(target);
  1698. },
  1699. setPrototypeOf: function(target, newProto) {
  1700. var handler = directProxies.get(target);
  1701. if (handler !== undefined) {
  1702. return handler.setPrototypeOf(newProto);
  1703. }
  1704. if (Object(newProto) !== newProto || newProto === null) {
  1705. throw new TypeError("Object prototype may only be an Object or null: " +
  1706. newProto);
  1707. }
  1708. if (!Object_isExtensible(target)) {
  1709. return false;
  1710. }
  1711. var current = Object.getPrototypeOf(target);
  1712. if (sameValue(current, newProto)) {
  1713. return true;
  1714. }
  1715. if (prim_setPrototypeOf) {
  1716. try {
  1717. prim_setPrototypeOf(target, newProto);
  1718. return true;
  1719. } catch (e) {
  1720. return false;
  1721. }
  1722. }
  1723. __proto__setter.call(target, newProto);
  1724. return true;
  1725. },
  1726. preventExtensions: function(target) {
  1727. var handler = directProxies.get(target);
  1728. if (handler !== undefined) {
  1729. return handler.preventExtensions();
  1730. }
  1731. prim_preventExtensions(target);
  1732. return true;
  1733. },
  1734. isExtensible: function(target) {
  1735. return Object.isExtensible(target);
  1736. },
  1737. has: function(target, name) {
  1738. return name in target;
  1739. },
  1740. get: function(target, name, receiver) {
  1741. receiver = receiver || target;
  1742. // if target is a proxy, invoke its "get" trap
  1743. var handler = directProxies.get(target);
  1744. if (handler !== undefined) {
  1745. return handler.get(receiver, name);
  1746. }
  1747. var desc = Object.getOwnPropertyDescriptor(target, name);
  1748. if (desc === undefined) {
  1749. var proto = Object.getPrototypeOf(target);
  1750. if (proto === null) {
  1751. return undefined;
  1752. }
  1753. return Reflect.get(proto, name, receiver);
  1754. }
  1755. if (isDataDescriptor(desc)) {
  1756. return desc.value;
  1757. }
  1758. var getter = desc.get;
  1759. if (getter === undefined) {
  1760. return undefined;
  1761. }
  1762. return desc.get.call(receiver);
  1763. },
  1764. // Reflect.set implementation based on latest version of [[SetP]] at
  1765. // http://wiki.ecmascript.org/doku.php?id=harmony:proto_climbing_refactoring
  1766. set: function(target, name, value, receiver) {
  1767. receiver = receiver || target;
  1768. // if target is a proxy, invoke its "set" trap
  1769. var handler = directProxies.get(target);
  1770. if (handler !== undefined) {
  1771. return handler.set(receiver, name, value);
  1772. }
  1773. // first, check whether target has a non-writable property
  1774. // shadowing name on receiver
  1775. var ownDesc = Object.getOwnPropertyDescriptor(target, name);
  1776. if (ownDesc === undefined) {
  1777. // name is not defined in target, search target's prototype
  1778. var proto = Object.getPrototypeOf(target);
  1779. if (proto !== null) {
  1780. // continue the search in target's prototype
  1781. return Reflect.set(proto, name, value, receiver);
  1782. }
  1783. // Rev16 change. Cf. https://bugs.ecmascript.org/show_bug.cgi?id=1549
  1784. // target was the last prototype, now we know that 'name' is not shadowed
  1785. // by an existing (accessor or data) property, so we can add the property
  1786. // to the initial receiver object
  1787. // (this branch will intentionally fall through to the code below)
  1788. ownDesc =
  1789. { value: undefined,
  1790. writable: true,
  1791. enumerable: true,
  1792. configurable: true };
  1793. }
  1794. // we now know that ownDesc !== undefined
  1795. if (isAccessorDescriptor(ownDesc)) {
  1796. var setter = ownDesc.set;
  1797. if (setter === undefined) return false;
  1798. setter.call(receiver, value); // assumes Function.prototype.call
  1799. return true;
  1800. }
  1801. // otherwise, isDataDescriptor(ownDesc) must be true
  1802. if (ownDesc.writable === false) return false;
  1803. // we found an existing writable data property on the prototype chain.
  1804. // Now update or add the data property on the receiver, depending on
  1805. // whether the receiver already defines the property or not.
  1806. var existingDesc = Object.getOwnPropertyDescriptor(receiver, name);
  1807. if (existingDesc !== undefined) {
  1808. var updateDesc =
  1809. { value: value,
  1810. // FIXME: it should not be necessary to describe the following
  1811. // attributes. Added to circumvent a bug in tracemonkey:
  1812. // https://bugzilla.mozilla.org/show_bug.cgi?id=601329
  1813. writable: existingDesc.writable,
  1814. enumerable: existingDesc.enumerable,
  1815. configurable: existingDesc.configurable };
  1816. Object.defineProperty(receiver, name, updateDesc);
  1817. return true;
  1818. } else {
  1819. if (!Object.isExtensible(receiver)) return false;
  1820. var newDesc =
  1821. { value: value,
  1822. writable: true,
  1823. enumerable: true,
  1824. configurable: true };
  1825. Object.defineProperty(receiver, name, newDesc);
  1826. return true;
  1827. }
  1828. },
  1829. /*invoke: function(target, name, args, receiver) {
  1830. receiver = receiver || target;
  1831. var handler = directProxies.get(target);
  1832. if (handler !== undefined) {
  1833. return handler.invoke(receiver, name, args);
  1834. }
  1835. var fun = Reflect.get(target, name, receiver);
  1836. return Function.prototype.apply.call(fun, receiver, args);
  1837. },*/
  1838. enumerate: function(target) {
  1839. var handler = directProxies.get(target);
  1840. var result;
  1841. if (handler !== undefined) {
  1842. // handler.enumerate should return an iterator directly, but the
  1843. // iterator gets converted to an array for backward-compat reasons,
  1844. // so we must re-iterate over the array
  1845. result = handler.enumerate(handler.target);
  1846. } else {
  1847. result = [];
  1848. for (var name in target) { result.push(name); };
  1849. }
  1850. var l = +result.length;
  1851. var idx = 0;
  1852. return {
  1853. next: function() {
  1854. if (idx === l) return { done: true };
  1855. return { done: false, value: result[idx++] };
  1856. }
  1857. };
  1858. },
  1859. // imperfect ownKeys implementation: in ES6, should also include
  1860. // symbol-keyed properties.
  1861. ownKeys: function(target) {
  1862. return Object_getOwnPropertyNames(target);
  1863. },
  1864. apply: function(target, receiver, args) {
  1865. // target.apply(receiver, args)
  1866. return Function.prototype.apply.call(target, receiver, args);
  1867. },
  1868. construct: function(target, args, newTarget) {
  1869. // return new target(...args);
  1870. // if target is a proxy, invoke its "construct" trap
  1871. var handler = directProxies.get(target);
  1872. if (handler !== undefined) {
  1873. return handler.construct(handler.target, args, newTarget);
  1874. }
  1875. if (typeof target !== "function") {
  1876. throw new TypeError("target is not a function: " + target);
  1877. }
  1878. if (newTarget === undefined || newTarget === target) {
  1879. // If newTarget is undefined, then newTarget is set to `target` and
  1880. // `Reflect.construct(target, ...args)` becomes equivalent to
  1881. // `new target(...args)`
  1882. // if `target` is an ES2015 Class constructor, it must be called using
  1883. // the `new` operator. Hence we use the new operator on a bound function
  1884. // to trigger the [[Construct]] internal method. This technique will work
  1885. // for both plain constructor functions and ES2015 classes
  1886. return new (Function.prototype.bind.apply(target, [null].concat(args)));
  1887. } else {
  1888. if (typeof newTarget !== "function") {
  1889. throw new TypeError("newTarget is not a function: " + target);
  1890. }
  1891. // if newTarget is a *different* constructor function, we need to
  1892. // emulate [[Construct]] by falling back to [[Call]] with a hand-crafted
  1893. // new instance inheriting from newTarget.prototype
  1894. // Unfortunately this won't work if target is an ES2015 Constructor
  1895. // function, whose [[Call]] method throws an error (it must be invoked
  1896. // using the `new` operator)
  1897. var proto = newTarget.prototype;
  1898. var instance = (Object(proto) === proto) ? Object.create(proto) : {};
  1899. var result = Function.prototype.apply.call(target, instance, args);
  1900. return Object(result) === result ? result : instance;
  1901. }
  1902. }
  1903. };
  1904. // feature-test whether the Reflect global exists
  1905. if (global.Reflect !== undefined) {
  1906. // Reflect exists, add/override the shimmed methods
  1907. Object.getOwnPropertyNames(Reflect).forEach(function (key) {
  1908. global.Reflect[key] = Reflect[key];
  1909. });
  1910. } else {
  1911. // Reflect doesn't exist, define it as the shimmed Reflect object
  1912. global.Reflect = Reflect;
  1913. }
  1914. // feature-test whether the Proxy global exists, with
  1915. // the harmony-era Proxy.create API
  1916. if (typeof Proxy !== "undefined" &&
  1917. typeof Proxy.create !== "undefined") {
  1918. var primCreate = Proxy.create,
  1919. primCreateFunction = Proxy.createFunction;
  1920. var revokedHandler = primCreate({
  1921. get: function() { throw new TypeError("proxy is revoked"); }
  1922. });
  1923. global.Proxy = function(target, handler) {
  1924. // check that target is an Object
  1925. if (Object(target) !== target) {
  1926. throw new TypeError("Proxy target must be an Object, given "+target);
  1927. }
  1928. // check that handler is an Object
  1929. if (Object(handler) !== handler) {
  1930. throw new TypeError("Proxy handler must be an Object, given "+handler);
  1931. }
  1932. var vHandler = new Validator(target, handler);
  1933. var proxy;
  1934. if (typeof target === "function") {
  1935. proxy = primCreateFunction(vHandler,
  1936. // call trap
  1937. function() {
  1938. var args = Array.prototype.slice.call(arguments);
  1939. return vHandler.apply(target, this, args);
  1940. },
  1941. // construct trap
  1942. function() {
  1943. var args = Array.prototype.slice.call(arguments);
  1944. return vHandler.construct(target, args);
  1945. });
  1946. } else {
  1947. proxy = primCreate(vHandler, Object.getPrototypeOf(target));
  1948. }
  1949. directProxies.set(proxy, vHandler);
  1950. return proxy;
  1951. };
  1952. global.Proxy.revocable = function(target, handler) {
  1953. var proxy = new Proxy(target, handler);
  1954. var revoke = function() {
  1955. var vHandler = directProxies.get(proxy);
  1956. if (vHandler !== null) {
  1957. vHandler.target = null;
  1958. vHandler.handler = revokedHandler;
  1959. }
  1960. return undefined;
  1961. };
  1962. return {proxy: proxy, revoke: revoke};
  1963. }
  1964. // add the old Proxy.create and Proxy.createFunction methods
  1965. // so old code that still depends on the harmony-era Proxy object
  1966. // is not broken. Also ensures that multiple versions of this
  1967. // library should load fine
  1968. global.Proxy.create = primCreate;
  1969. global.Proxy.createFunction = primCreateFunction;
  1970. } else {
  1971. // Proxy global not defined, or old API not available
  1972. if (typeof Proxy === "undefined") {
  1973. // Proxy global not defined, add a Proxy function stub
  1974. global.Proxy = function(_target, _handler) {
  1975. throw new Error("proxies not supported on this platform. On v8/node/iojs, make sure to pass the --harmony_proxies flag");
  1976. };
  1977. }
  1978. // Proxy global defined but old API not available
  1979. // presumably Proxy global already supports new API, leave untouched
  1980. }
  1981. // for node.js modules, export every property in the Reflect object
  1982. // as part of the module interface
  1983. if (typeof exports !== 'undefined') {
  1984. Object.keys(Reflect).forEach(function (key) {
  1985. exports[key] = Reflect[key];
  1986. });
  1987. }
  1988. // function-as-module pattern
  1989. }(typeof exports !== 'undefined' ? global : this));