• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Bnaya / objectbuffer / 1cc46d885c9a8c4464d8420dc6a68afe83ac9811

pending completion
1cc46d885c9a8c4464d8420dc6a68afe83ac9811

push

github

Bnaya Peretz
wip

458 of 552 branches covered (82.97%)

Branch coverage included in aggregate %.

1249 of 1361 relevant lines covered (91.77%)

277.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.79
/src/internal/arrayWrapper.ts
1
import {
2
  getFinalValueAtArrayIndex,
3
  setValueAtArrayIndex,
4
  arraySort,
5
  extendArrayIfNeeded,
6
  arrayReverse,
7
} from "./arrayHelpers";
8
import { INTERNAL_API_SYMBOL } from "./symbols";
9
import { arraySplice } from "./arraySplice";
10
import type { ExternalArgs, GlobalCarrier } from "./interfaces";
11
import {
12
  IllegalArrayIndexError,
13
  UnsupportedOperationError,
14
} from "./exceptions";
15
import { BaseProxyTrap } from "./BaseProxyTrap";
16
import { array_length_get } from "./generatedStructs";
17

18
const getOwnPropertyDescriptorLENGTH = {
19
  configurable: false,
20
  enumerable: false,
21
  writable: true,
22
} as const;
23

24
const getOwnPropertyDescriptorHAS = {
25
  configurable: false,
26
  enumerable: true,
27
} as const;
28

29
export class ArrayWrapper
30
  extends BaseProxyTrap
31
  implements ProxyHandler<Record<string, unknown>>
32
{
33
  public get(target: Record<string, unknown>, p: PropertyKey): any {
34
    if (p === INTERNAL_API_SYMBOL) {
2,384✔
35
      return this;
36
    }
37

38
    if (p in this && p !== "constructor") {
5,599✔
39
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
40
      // @ts-expect-error
41
      return this[p];
42
    }
43

44
    if (p === "length") {
1,693✔
45
      return array_length_get(this.carrier.heap, this.entryPointer);
46
    }
47

48
    if (typeof p === "string" || typeof p === "number") {
2,408✔
49
      const asInt = typeof p === "string" ? Number.parseInt(p, 10) : p;
1,051!
50

51
      if (Number.isSafeInteger(asInt)) {
1,051✔
52
        return getFinalValueAtArrayIndex(
53
          this.externalArgs,
54
          this.carrier,
55
          this.entryPointer,
56
          asInt
57
        );
58
      }
59
    }
60

61
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
62
    // @ts-expect-error
63
    return target[p];
64
  }
65

66
  public deleteProperty(
67
    target: Record<string, unknown>,
68
    p: PropertyKey
69
  ): boolean {
70
    const index = typeof p === "number" ? p : Number.parseInt(p as string, 10);
×
71

72
    return this.splice(index, 1).length === 1;
73
  }
74

75
  public enumerate(): PropertyKey[] {
76
    throw new Error("unsupported enumerate");
77
  }
78

79
  public ownKeys() {
80
    const length = array_length_get(this.carrier.heap, this.entryPointer);
81

82
    return [...new Array(length).keys(), "length"].map((i) =>
83
      typeof i === "number" ? i.toString() : i
×
84
    );
85
  }
86

87
  public getOwnPropertyDescriptor(target: Record<string, unknown>, prop: any) {
88
    if (prop === "length") {
×
89
      return getOwnPropertyDescriptorLENGTH;
90
    }
91

92
    if (!this.has(target, prop)) {
×
93
      return undefined;
94
    }
95

96
    return getOwnPropertyDescriptorHAS;
97
  }
98

99
  public has(target: Record<string, unknown>, p: PropertyKey): boolean {
100
    if (p === INTERNAL_API_SYMBOL) {
225✔
101
      return true;
102
    }
103

104
    const length = array_length_get(this.carrier.heap, this.entryPointer);
105

106
    if (typeof p === "number") {
224!
107
      return length - 1 >= p;
108
    } else if (typeof p === "string") {
224!
109
      return length - 1 >= Number.parseInt(p, 10);
110
    }
111

112
    throw new Error("unsupported");
113
  }
114

115
  public set(
116
    target: Record<string, unknown>,
117
    accessedProp: PropertyKey,
118
    value: unknown
119
  ): boolean {
120
    if (typeof accessedProp === "symbol") {
35✔
121
      throw new IllegalArrayIndexError();
122
    }
123

124
    if (accessedProp === "length") {
34✔
125
      return this.handleLengthChange(value);
126
    }
127

128
    const possibleIndex = Number.parseInt(accessedProp as string, 10);
129

130
    if (!Number.isSafeInteger(possibleIndex) || possibleIndex < 0) {
80✔
131
      throw new IllegalArrayIndexError();
132
    }
133

134
    // @todo: avoid closure/function runtime allocation
135
    this.carrier.allocator.transaction(() => {
136
      setValueAtArrayIndex(
137
        this.externalArgs,
138
        this.carrier,
139
        this.entryPointer,
140
        possibleIndex,
141
        value
142
      );
143
    });
144

145
    return true;
146
  }
147

148
  public *entries(): Iterable<[number, any]> {
149
    let index = 0;
150
    let length = 0;
151

152
    do {
153
      yield [
154
        index,
155
        getFinalValueAtArrayIndex(
156
          this.externalArgs,
157
          this.carrier,
158
          this.entryPointer,
159
          index
160
        ),
161
      ];
162

163
      index += 1;
164

165
      length = array_length_get(this.carrier.heap, this.entryPointer);
166
    } while (index < length);
167
  }
168

169
  public *keys(): Iterable<number> {
170
    let index = 0;
171
    let length = 0;
172

173
    do {
174
      yield index;
175

176
      index += 1;
177

178
      length = array_length_get(this.carrier.heap, this.entryPointer);
179
    } while (index < length);
180
  }
181

182
  public *values(): Iterable<any> {
183
    let index = 0;
184
    let length = 0;
185

186
    do {
187
      yield getFinalValueAtArrayIndex(
188
        this.externalArgs,
189
        this.carrier,
190
        this.entryPointer,
191
        index
192
      );
193

194
      index += 1;
195

196
      length = array_length_get(this.carrier.heap, this.entryPointer);
197
    } while (index < length);
198
  }
199

200
  get [Symbol.iterator]() {
201
    return this.values;
202
  }
203

204
  public sort(comparator?: (a: any, b: any) => 1 | -1 | 0) {
205
    arraySort(this.externalArgs, this.carrier, this.entryPointer, comparator);
206
  }
207

208
  public splice(start: number, deleteCount?: number, ...items: any[]): any[] {
209
    // @todo: avoid closure/function runtime allocation
210
    return this.carrier.allocator.transaction(() => {
211
      return arraySplice(
212
        this.externalArgs,
213
        this.carrier,
214
        this.entryPointer,
215
        start,
216
        deleteCount,
217
        ...items
218
      );
219
    });
220
  }
221

222
  public reverse() {
223
    arrayReverse(this.carrier, this.entryPointer);
224
    return this;
225
  }
226

227
  // no copy inside array is needed, so we can live with the built-in impl
228
  // public push() {
229
  // }
230

231
  // public pop() {}
232

233
  public shift() {
234
    return this.splice(0, 1)[0];
235
  }
236

237
  public unshift(...elements: any) {
238
    this.splice(0, 0, ...elements);
239

240
    return array_length_get(this.carrier.heap, this.entryPointer);
241
  }
242

243
  public isExtensible() {
244
    return true;
245
  }
246

247
  public preventExtensions(): boolean {
248
    throw new UnsupportedOperationError();
249
  }
250

251
  public setPrototypeOf(): boolean {
252
    throw new UnsupportedOperationError();
253
  }
254

255
  public defineProperty(): // target: Record<string, unknown>,
256
  // p: PropertyKey,
257
  // attributes: PropertyDescriptor
258
  boolean {
259
    throw new UnsupportedOperationError();
260
    // if (
261
    //   typeof p === "symbol" ||
262
    //   attributes.enumerable === false ||
263
    //   attributes.get ||
264
    //   attributes.set
265
    // ) {
266
    //   throw new IllegalObjectPropConfigError();
267
    // }
268

269
    // return Object.defineProperty(target, p, attributes);
270
  }
271

272
  private handleLengthChange(newLength: unknown): boolean {
273
    if (
7✔
274
      typeof newLength !== "number" ||
18✔
275
      !Number.isSafeInteger(newLength) ||
276
      newLength < 0
277
    ) {
278
      throw new RangeError("Invalid array length");
279
    }
280

281
    const currentLength = array_length_get(
282
      this.carrier.heap,
283
      this.entryPointer
284
    );
285

286
    if (currentLength === newLength) {
4✔
287
      return true;
288
    }
289

290
    // @todo: avoid closure/function runtime allocation
291
    this.carrier.allocator.transaction(() => {
292
      if (currentLength > newLength) {
2✔
293
        this.splice(newLength, currentLength - newLength);
294

295
        return;
296
      }
297

298
      extendArrayIfNeeded(
299
        this.externalArgs,
300
        this.carrier,
301
        this.entryPointer,
302
        newLength
303
      );
304
    });
305

306
    return true;
307
  }
308
}
309

310
export function createArrayWrapper(
311
  externalArgs: ExternalArgs,
312
  globalCarrier: GlobalCarrier,
313
  entryPointer: number
314
): Array<any> {
315
  return new Proxy(
316
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
317
    // @ts-expect-error
318
    [],
319
    new ArrayWrapper(externalArgs, globalCarrier, entryPointer)
320
  ) as any;
321
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc