+export const {
+ /**
+ * Returns a proxy of the provided arraylike value for which every
+ * integer index less than its length will appear to be present.
+ *
+ * ※ The returned proxy will have the original object as its
+ * prototype and will update with changes to the original.
+ *
+ * ※ The returned proxy only reflects ⹐own⹑ properties on the
+ * underlying object; if an index is set on the prototype chain, but
+ * not as an own property on the underlying object, it will appear as
+ * undefined on the proxy. Like·wise, if the length is not an own
+ * property, it will appear to be zero.
+ *
+ * ※ Both data values and accessors are supported for indices,
+ * provided they are defined directly on the underlying object.
+ *
+ * ※ A proxy can be made non·extensible if the `.length´ of the
+ * underlying object is read·only (i·e, not defined using a getter)
+ * and nonconfigurable.
+ *
+ * ※ Integer indices on the returned proxy, as well as `.length´,
+ * are read·only and start out formally (but not manually)
+ * configurable. They can only be made nonconfigurable if the
+ * underlying value is guaranteed not to change. As a change in
+ * `.length´ deletes any integer indices larger than the `.length´,
+ * the `.length´ of the underlying object must be fixed for any
+ * integer index to be made nonconfigurable.
+ *
+ * ※ When iterating, it is probably faster to just make a copy of
+ * the original value, for example :—
+ *
+ * | `setPropertyValues(´
+ * | ` fill(setPropertyValue([], "length", original.length)),´
+ * | ` original,´
+ * | `);´
+ *
+ * This function is rather intended for the use·case where both the
+ * proxy and the underlying array are longlived, and the latter may
+ * change unexpectedly after the formers creation.
+ */
+ denseProxy,
+
+ /**
+ * Returns whether the provided value is a dense proxy (created with
+ * `denseProxy´).
+ */
+ isDenseProxy,
+} = (() => {
+ const {
+ deleteProperty: reflectDeleteProperty,
+ defineProperty: reflectDefineProperty,
+ getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor,
+ getPrototypeOf: reflectGetPrototypeOf,
+ has: reflectHas,
+ isExtensible: reflectIsExtensible,
+ ownKeys: reflectOwnKeys,
+ preventExtensions: reflectPreventExtensions,
+ setPrototypeOf: reflectSetPrototypeOf,
+ } = Reflect;
+ const isReadOnlyNonconfigurable = (Desc) =>
+ Desc !== UNDEFINED
+ && !(Desc.configurable || "get" in Desc
+ || "writable" in Desc && Desc.writable);
+
+ const denseProxyHandler = Object.assign(Object.create(null), {
+ defineProperty(O, P, Desc) {
+ const k = P === "length"
+ ? UNDEFINED
+ : canonicalNumericIndexString(P);
+ if (
+ P === "length"
+ || k !== UNDEFINED
+ && (sameValue(k, 0) || k > 0 && k === toLength(k))
+ ) {
+ // The provided property is either `"length"´ or an integer
+ // index.
+ const desc = maybe(Desc, toPropertyDescriptorRecord);
+ if (
+ desc.set !== UNDEFINED
+ || "writable" in desc && desc.writable
+ ) {
+ // The provided descriptor defines a setter or is attempting
+ // to make the property writable; this is not permitted.
+ return false;
+ } else {
+ // The provided descriptor does not define a setter.
+ const Q = reflectGetPrototypeOf(O);
+ const current = maybe(
+ reflectGetOwnPropertyDescriptor(O, P),
+ toPropertyDescriptorRecord,
+ );
+ const lenDesc = maybe(
+ reflectGetOwnPropertyDescriptor(Q, "length"),
+ toPropertyDescriptorRecord,
+ );
+ const currentlyConfigurable = current?.configurable ?? true;
+ const willChangeConfigurable = "configurable" in desc
+ && (desc.configurable !== currentlyConfigurable);
+ if (
+ willChangeConfigurable
+ && (!currentlyConfigurable
+ || !isReadOnlyNonconfigurable(lenDesc))
+ ) {
+ // The provided descriptor either aims to make a property
+ // nonconfigurable when the underlying length is not
+ // readonly or else aims to make a property configurable
+ // when it currently isn¦t; neither is permitted.
+ return false;
+ } else if (P === "length") {
+ // The provided descriptor is attempting to modify the
+ // length.
+ //
+ // It is known at this point that either the property
+ // descriptor is not trying to make the length
+ // nonconfigurable, or else the underlying length is
+ // readonly.
+ const len = !currentlyConfigurable
+ ? current.value
+ : lenDesc === UNDEFINED
+ ? 0 // force zero if not an own property
+ : !("value" in desc)
+ ? null // not needed yet
+ : lengthOfArraylike(Q);
+ if (
+ "get" in desc
+ || "enumerable" in desc && desc.enumerable
+ || "value" in desc && desc.value !== len
+ ) {
+ // The provided descriptor is attempting to create a
+ // getter for the length, change the enumerability of the
+ // length, or change the value of the length; these are
+ // not permitted.
+ return false;
+ } else if (!willChangeConfigurable) {
+ // The provided descriptor is not attempting to change
+ // the value of the length or its configurablility; this
+ // succeeds with no effect.
+ return true;
+ } else {
+ // The provided descriptor is attempting to make a
+ // read·only, configurable length nonconfigurable.
+ return reflectDefineProperty(
+ O,
+ P,
+ setPropertyValues(objectCreate(null), {
+ configurable: false,
+ enumerable: false,
+ value: len ?? lengthOfArraylike(Q),
+ writable: false,
+ }),
+ );
+ }
+ } else {
+ // The provided property is an integral canonical numeric
+ // index string.
+ const len = lenDesc === UNDEFINED
+ ? 0
+ : lengthOfArraylike(Q);
+ if (k < len) {
+ // The provided property is smaller than the length; this
+ // property can potentially be modified.
+ const kDesc = maybe(
+ reflectGetOwnPropertyDescriptor(Q, P),
+ toPropertyDescriptorRecord,
+ );
+ const kGet = current?.get; // use current getter
+ const kValue = // use underlying value
+ kDesc === UNDEFINED
+ ? UNDEFINED
+ : kDesc.value ?? call(kDesc.get, Q, []);
+ if (
+ "get" in desc && desc.get !== kGet
+ || "enumerable" in desc && !desc.enumerable
+ || "value" in desc && desc.value !== kValue
+ ) {
+ // The provided descriptor is attempting to change the
+ // value or enumerability of the property away from
+ // their current values; this is not permitted.
+ return false;
+ } else if (!willChangeConfigurable) {
+ // The provided descriptor is not attempting to change
+ // the configurability of the property; in this case,
+ // no actual change is being requested.
+ return true;
+ } else {
+ // The provided descriptor is attempting to make the
+ // property nonconfigurable, but it is currently
+ // configurable (and maybe not even present on the
+ // proxy target); this is only permissible if the value
+ // of the underlying property is known not to be able
+ // to change.
+ //
+ // Providing the value is okay if the underlying
+ // property is readonly, but if the underlying property
+ // is a getter, then the value must not be provided
+ // (since the resulting property will be defined with a
+ // brand new getter).
+ //
+ // At this point, it is known that the provided
+ // descriptor does not provide a getter, because
+ // getters are only supported on index properties which
+ // are already nonconfigurable.
+ //
+ // At this point, we have already confirmed that the
+ // length of the underlying object is immutable.
+ const dynamic = kDesc !== UNDEFINED
+ && !("writable" in kDesc);
+ const readonly =
+ kDesc === UNDEFINED && !reflectIsExtensible(Q)
+ || kDesc !== UNDEFINED && !kDesc.configurable && (
+ dynamic || !kDesc.writable
+ );
+ const noChange = !dynamic
+ || dynamic && !("value" in desc);
+ return readonly && noChange && reflectDefineProperty(
+ O,
+ P,
+ setPropertyValues(
+ objectCreate(null),
+ kDesc !== UNDEFINED && "get" in kDesc
+ ? {
+ configurable: false,
+ enumerable: true,
+ get: defineOwnProperty(
+ () => call(kDesc.get, Q, []),
+ "name",
+ defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ toFunctionName(P, "get"),
+ ),
+ ),
+ set: UNDEFINED,
+ }
+ : {
+ configurable: false,
+ enumerable: true,
+ value: kValue,
+ writable: false,
+ },
+ ),
+ );
+ }
+ } else {
+ // The provided property is not smaller than the length;
+ // this is not permitted.
+ return false;
+ }
+ }
+ }
+ } else {
+ // The provided property is not `"length"´ or an integer index.
+ return reflectDefineProperty(O, P, Desc);
+ }
+ },
+ deleteProperty(O, P) {
+ const k = P === "length"
+ ? UNDEFINED
+ : canonicalNumericIndexString(P);
+ if (
+ P === "length"
+ || k !== UNDEFINED
+ && (sameValue(k, 0) || k > 0 && k === toLength(k))
+ ) {
+ // The property is an integer index or `"length"´.
+ if (!reflectIsExtensible(O) || P === "length") {
+ // The proxied object is not extensible or the provided
+ // property is `"length"´; this is not permitted.
+ return false;
+ } else {
+ // The provided property is an integer index; it can only
+ // be deleted if it is greater than the length (in which
+ // case, it is not present in the first place).
+ const Q = reflectGetPrototypeOf(O);
+ const len = hasOwnProperty(Q, "length")
+ ? lengthOfArraylike(Q)
+ : 0;
+ return k < len ? false : true;
+ }
+ } else {
+ // The provided property is not `"length"´ or an integer index.
+ return reflectDeleteProperty(O, P);
+ }
+ },
+ getOwnPropertyDescriptor(O, P) {
+ const k = P === "length"
+ ? UNDEFINED
+ : canonicalNumericIndexString(P);
+ if (
+ P === "length"
+ || k !== UNDEFINED
+ && (sameValue(k, 0) || k > 0 && k === toLength(k))
+ ) {
+ // The property is an integer index or `"length"´.
+ const Q = reflectGetPrototypeOf(O);
+ const current = maybe(
+ reflectGetOwnPropertyDescriptor(O, P),
+ toPropertyDescriptorRecord,
+ );
+ if (current !== UNDEFINED && !current.configurable) {
+ // The property is defined and nonconfigurable on the object.
+ //
+ // Return its descriptor.
+ return current;
+ } else if (P === "length") {
+ // The property is `"length"´.
+ //
+ // Return the length of the underlying object.
+ return setPropertyValues(objectCreate(null), {
+ configurable: true,
+ enumerable: false,
+ value: hasOwnProperty(Q, "length")
+ ? lengthOfArraylike(Q)
+ : 0,
+ writable: false,
+ });
+ } else {
+ // The property is an integer index.
+ //
+ // Return a data descriptor with its value or undefined as
+ // appropriate.
+ const len = hasOwnProperty(Q, "length")
+ ? lengthOfArraylike(Q)
+ : 0;
+ if (k < len) {
+ // The property is an integer index less than the length.
+ //
+ // Provide the current value of the own property.
+ const kDesc = maybe(
+ reflectGetOwnPropertyDescriptor(Q, P),
+ toPropertyDescriptorRecord,
+ );
+ return setPropertyValues(objectCreate(null), {
+ configurable: true,
+ enumerable: true,
+ value: !kDesc
+ ? UNDEFINED
+ : "get" in kDesc
+ ? call(kDesc.get, Q, [])
+ : kDesc.value,
+ writable: false,
+ });
+ } else {
+ // The property is an integer index, but not less than the
+ // length.
+ //
+ // Return undefined.
+ return UNDEFINED;
+ }
+ }
+ } else {
+ // The provided property is not `"length"´ or an integer index.
+ return reflectGetOwnPropertyDescriptor(O, P);
+ }
+ },
+ has(O, P) {
+ const k = P === "length"
+ ? UNDEFINED
+ : canonicalNumericIndexString(P);
+ if (P === "length") {
+ // The provided property is `"length"´; this is always present.
+ return true;
+ } else if (
+ k !== UNDEFINED
+ && (sameValue(k, 0) || k > 0 && k === toLength(k))
+ ) {
+ // The provided property is an integer index.
+ //
+ // Return whether it is less than the length.
+ const Q = reflectGetPrototypeOf(O);
+ const len = hasOwnProperty(Q, "length")
+ ? lengthOfArraylike(Q)
+ : 0;
+ return k < len ? true : reflectHas(O, P);
+ } else {
+ // The provided property is not `"length"´ or an integer index.
+ return reflectHas(O, P);
+ }
+ },
+ ownKeys(O) {
+ const keys = reflectOwnKeys(O);
+ const Q = reflectGetPrototypeOf(O);
+ const len = hasOwnProperty(Q, "length")
+ ? lengthOfArraylike(Q)
+ : 0;
+ const result = [];
+ let i;
+ let hasHitLength = false;
+ for (i = 0; i < len && i < -1 >>> 0; ++i) {
+ // Iterate over those array indices which are less than the
+ // length of the underlying object and collect them in the
+ // result.
+ //
+ // Array indices are handled specially by the Ecmascript
+ // specification. Other integer indices may also be present
+ // (if they are too big to be array indices but still smaller
+ // than the length), but these are added later with all of the
+ // other keys.
+ defineOwnDataProperty(result, i, `${i}`);
+ }
+ for (let j = 0; j < keys.length; ++j) {
+ // Iterate over the own keys of the object and collect them in
+ // the result if necessary.
+ const P = keys[j];
+ const k = P === "length"
+ ? UNDEFINED
+ : canonicalNumericIndexString(P);
+ const isIntegerIndex = k !== UNDEFINED
+ && (sameValue(k, 0) || k > 0 && k === toLength(k));
+ if (!hasHitLength && (!isIntegerIndex || k >= -1 >>> 0)) {
+ // The current key is the first key which is not an array
+ // index; add `"length"´ to the result, as well as any
+ // integer indices which are not array indices.
+ //
+ // This may never occur, in which case these properties are
+ // added after the end of the loop.
+ //
+ // `"length"´ is added first as it is conceptually the first
+ // property on the object.
+ defineOwnDataProperty(result, result.length, "length");
+ for (; i < len; ++i) {
+ // Iterate over those remaining integer indices which are
+ // less than the length of the underlying object and
+ // collect them in the result.
+ defineOwnDataProperty(result, result.length, `${i}`);
+ }
+ hasHitLength = true;
+ } else {
+ // The current key is not the first key which is not an array
+ // index.
+ /* do nothing */
+ }
+ if (P === "length" || isIntegerIndex && k < len) {
+ // The current key is either `"length"´ or an integer index
+ // less than the length; it has already been collected.
+ /* do nothing */
+ } else {
+ // The current key has not yet been collected into the
+ // result; add it.
+ defineOwnDataProperty(result, result.length, P);
+ }
+ }
+ if (!hasHitLength) {
+ // All of the collected keys were array indices; `"length"´ and
+ // any outstanding integer indices still need to be collected.
+ defineOwnDataProperty(result, result.length, "length");
+ for (; i < len; ++i) {
+ // Iterate over those remaining integer indices which are
+ // less than the length of the underlying object and collect
+ // them in the result.
+ defineOwnDataProperty(result, result.length, `${i}`);
+ }
+ } else {
+ // There was at least one key collected which was not an array
+ // index.
+ /* do nothing */
+ }
+ return result;
+ },
+ preventExtensions(O) {
+ if (!reflectIsExtensible(O)) {
+ // The object is already not extensible; this is an automatic
+ // success.
+ return true;
+ } else {
+ // The object is currently extensible; see if it can be made
+ // non·extensible and attempt to do so.
+ const Q = reflectGetPrototypeOf(O);
+ const lenDesc = maybe(
+ reflectGetOwnPropertyDescriptor(Q, "length"),
+ toPropertyDescriptorRecord,
+ );
+ if (!isReadOnlyNonconfigurable(lenDesc)) {
+ // The underlying length is not read·only; the object cannot
+ // be made non·extensible because the indices may change.
+ return false;
+ } else {
+ // The underlying length is read·only; define the needed
+ // indices on the object and then prevent extensions.
+ const len = lengthOfArraylike(Q); // definitely exists
+ for (let k = 0; k < len; ++k) {
+ // Iterate over each index and define a placeholder for it.
+ reflectDefineProperty(
+ O,
+ k,
+ setPropertyValues(objectCreate(null), {
+ configurable: true,
+ enumerable: true,
+ value: UNDEFINED,
+ writable: false,
+ }),
+ );
+ }
+ return reflectPreventExtensions(O);
+ }
+ }
+ },
+ setPrototypeOf(O, V) {
+ const Q = reflectGetPrototypeOf(O);
+ return Q === V ? reflectSetPrototypeOf(O, V) : false;
+ },
+ });
+
+ const DenseProxy = createProxyConstructor(
+ denseProxyHandler,
+ function Dense($) {
+ return objectCreate(toObject($)); // throws if nullish
+ },
+ );
+
+ return {
+ denseProxy: Object.defineProperty(
+ ($) => new DenseProxy($),
+ "name",
+ { value: "denseProxy" },
+ ),
+ isDenseProxy: DenseProxy.isDenseProxy,
+ };
+})();
+