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

Bnaya / objectbuffer / 16262692456

14 Jul 2025 09:05AM UTC coverage: 89.747% (-0.3%) from 90.0%
16262692456

Pull #196

github

Bnaya
WIP
Pull Request #196: Typescript iteration - TS 5.8 and isolated declarations!

635 of 732 branches covered (86.75%)

Branch coverage included in aggregate %.

3 of 11 new or added lines in 4 files covered. (27.27%)

21 existing lines in 5 files now uncovered.

1247 of 1365 relevant lines covered (91.36%)

1658.76 hits per line

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

78.38
/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 = {
174✔
19
  configurable: false,
20
  enumerable: false,
21
  writable: true,
22
} as const;
23

24
const getOwnPropertyDescriptorHAS = {
174✔
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) {
14,304✔
35
      return this;
336✔
36
    }
37

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

44
    if (p === "length") {
10,158✔
45
      return array_length_get(this.carrier.heap, this.entryPointer);
3,240✔
46
    }
47

48
    if (typeof p === "string" || typeof p === "number") {
6,918✔
49
      const asInt = typeof p === "string" ? Number.parseInt(p, 10) : p;
6,306!
50

51
      if (Number.isSafeInteger(asInt)) {
6,306✔
52
        return getFinalValueAtArrayIndex(
1,374✔
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];
5,544✔
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(): string[] {
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
    readonly configurable: false;
89
    readonly enumerable: false;
90
    readonly writable: true;
91
  } | {
92
    readonly configurable: false;
93
    readonly enumerable: true;
94
  } | undefined {
95
    if (prop === "length") {
×
96
      return getOwnPropertyDescriptorLENGTH;
×
97
    }
98

99
    if (!this.has(target, prop)) {
×
100
      return undefined;
×
101
    }
102

UNCOV
103
    return getOwnPropertyDescriptorHAS;
×
104
  }
105

106
  public has(target: Record<string, unknown>, p: PropertyKey): boolean {
107
    if (p === INTERNAL_API_SYMBOL) {
1,350✔
108
      return true;
6✔
109
    }
110

111
    const length = array_length_get(this.carrier.heap, this.entryPointer);
1,344✔
112

113
    if (typeof p === "number") {
1,344!
UNCOV
114
      return length - 1 >= p;
×
115
    } else if (typeof p === "string") {
1,344!
116
      return length - 1 >= Number.parseInt(p, 10);
1,344✔
117
    }
118

UNCOV
119
    throw new Error("unsupported");
×
120
  }
121

122
  public set(
123
    target: Record<string, unknown>,
124
    accessedProp: PropertyKey,
125
    value: unknown
126
  ): boolean {
127
    if (typeof accessedProp === "symbol") {
210✔
128
      throw new IllegalArrayIndexError();
6✔
129
    }
130

131
    if (accessedProp === "length") {
204✔
132
      return this.handleLengthChange(value);
42✔
133
    }
134

135
    const possibleIndex = Number.parseInt(accessedProp as string, 10);
162✔
136

137
    if (!Number.isSafeInteger(possibleIndex) || possibleIndex < 0) {
162✔
138
      throw new IllegalArrayIndexError();
12✔
139
    }
140

141
    // @todo: avoid closure/function runtime allocation
142
    this.carrier.allocator.transaction(() => {
150✔
143
      setValueAtArrayIndex(
150✔
144
        this.externalArgs,
145
        this.carrier,
146
        this.entryPointer,
147
        possibleIndex,
148
        value
149
      );
150
    });
151

152
    return true;
144✔
153
  }
154

155
  public *entries(): Iterable<[number, any]> {
156
    let index = 0;
6✔
157
    let length = 0;
6✔
158

159
    do {
6✔
160
      yield [
18✔
161
        index,
162
        getFinalValueAtArrayIndex(
163
          this.externalArgs,
164
          this.carrier,
165
          this.entryPointer,
166
          index
167
        ),
168
      ];
169

170
      index += 1;
18✔
171

172
      length = array_length_get(this.carrier.heap, this.entryPointer);
18✔
173
    } while (index < length);
174
  }
175

176
  public *keys(): Iterable<number> {
177
    let index = 0;
6✔
178
    let length = 0;
6✔
179

180
    do {
6✔
181
      yield index;
18✔
182

183
      index += 1;
18✔
184

185
      length = array_length_get(this.carrier.heap, this.entryPointer);
18✔
186
    } while (index < length);
187
  }
188

189
  public *values(): Iterable<any> {
190
    let index = 0;
72✔
191
    let length = 0;
72✔
192

193
    do {
72✔
194
      yield getFinalValueAtArrayIndex(
594✔
195
        this.externalArgs,
196
        this.carrier,
197
        this.entryPointer,
198
        index
199
      );
200

201
      index += 1;
594✔
202

203
      length = array_length_get(this.carrier.heap, this.entryPointer);
594✔
204
    } while (index < length);
205
  }
206

207
  get [Symbol.iterator](): () => Iterable<any> {
208
    return this.values;
66✔
209
  }
210

211
  public sort(comparator?: (a: any, b: any) => 1 | -1 | 0): void {
212
    arraySort(this.externalArgs, this.carrier, this.entryPointer, comparator);
12✔
213
  }
214

215
  public splice(start: number, deleteCount?: number, ...items: any[]): any[] {
216
    // @todo: avoid closure/function runtime allocation
217
    return this.carrier.allocator.transaction(() => {
108✔
218
      return arraySplice(
108✔
219
        this.externalArgs,
220
        this.carrier,
221
        this.entryPointer,
222
        start,
223
        deleteCount,
224
        ...items
225
      );
226
    });
227
  }
228

229
  public reverse(): this {
230
    arrayReverse(this.carrier, this.entryPointer);
18✔
231
    return this;
18✔
232
  }
233

234
  // no copy inside array is needed, so we can live with the built-in impl
235
  // public push() {
236
  // }
237

238
  // public pop() {}
239

240
  public shift(): any {
UNCOV
241
    return this.splice(0, 1)[0];
×
242
  }
243

244
  public unshift(...elements: any): number {
245
    this.splice(0, 0, ...elements);
6✔
246

247
    return array_length_get(this.carrier.heap, this.entryPointer);
6✔
248
  }
249

250
  public isExtensible() {
UNCOV
251
    return true;
×
252
  }
253

254
  public preventExtensions(): boolean {
UNCOV
255
    throw new UnsupportedOperationError();
×
256
  }
257

258
  public setPrototypeOf(): boolean {
259
    throw new UnsupportedOperationError();
6✔
260
  }
261

262
  public defineProperty(): // target: Record<string, unknown>,
263
  // p: PropertyKey,
264
  // attributes: PropertyDescriptor
265
  boolean {
UNCOV
266
    throw new UnsupportedOperationError();
×
267
    // if (
268
    //   typeof p === "symbol" ||
269
    //   attributes.enumerable === false ||
270
    //   attributes.get ||
271
    //   attributes.set
272
    // ) {
273
    //   throw new IllegalObjectPropConfigError();
274
    // }
275

276
    // return Object.defineProperty(target, p, attributes);
277
  }
278

279
  private handleLengthChange(newLength: unknown): boolean {
280
    if (
42✔
281
      typeof newLength !== "number" ||
54✔
282
      !Number.isSafeInteger(newLength) ||
283
      newLength < 0
284
    ) {
285
      throw new RangeError("Invalid array length");
18✔
286
    }
287

288
    const currentLength = array_length_get(
24✔
289
      this.carrier.heap,
290
      this.entryPointer
291
    );
292

293
    if (currentLength === newLength) {
24✔
294
      return true;
12✔
295
    }
296

297
    // @todo: avoid closure/function runtime allocation
298
    this.carrier.allocator.transaction(() => {
12✔
299
      if (currentLength > newLength) {
12✔
300
        this.splice(newLength, currentLength - newLength);
6✔
301

302
        return;
6✔
303
      }
304

305
      extendArrayIfNeeded(
6✔
306
        this.externalArgs,
307
        this.carrier,
308
        this.entryPointer,
309
        newLength
310
      );
311
    });
312

313
    return true;
12✔
314
  }
315
}
316

317
export function createArrayWrapper(
318
  externalArgs: ExternalArgs,
319
  globalCarrier: GlobalCarrier,
320
  entryPointer: number
321
): Array<any> {
322
  return new Proxy(
360✔
323
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
324
    // @ts-expect-error
325
    [],
326
    new ArrayWrapper(externalArgs, globalCarrier, entryPointer)
327
  ) as any;
328
}
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