@as-pect/assembly 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,662 +1,698 @@
1
- import { ArrayBufferView } from "arraybuffer";
2
- import { Set } from "set";
3
- import { assert } from "./assert";
4
- import { ReflectedValueType } from "./ReflectedValueType";
5
- import { OBJECT, OBJECT_OVERHEAD } from "rt/common";
6
-
7
- function pairSeen(a1: usize, a2: usize, b1: usize, b2: usize): bool {
8
- return bool(
9
- (i32(a1 == b1) & i32(a2 == b2)) | (i32(a1 == b2) & i32(a2 == b1)),
10
- );
11
- }
12
-
13
- // @ts-ignore: Decorators *are* valid here!
14
- @external("__aspect", "attachStackTraceToReflectedValue")
15
- declare function attachStackTraceToReflectedValue(id: i32): void;
16
-
17
- // @ts-ignore: linked function decorator
18
- @external("__aspect", "createReflectedValue")
19
- declare function createReflectedValue(
20
- isNull: bool,
21
- hasKeys: bool,
22
- nullable: bool,
23
- offset: i32,
24
- pointer: usize,
25
- signed: bool,
26
- size: i32,
27
- reflectedTypeValue: ReflectedValueType,
28
- typeId: i32,
29
- typeName: string,
30
- value: usize,
31
- hasValues: bool,
32
- isManaged: bool,
33
- ): i32;
34
-
35
- // @ts-ignore: linked function decorator
36
- @external("__aspect", "createReflectedNumber")
37
- declare function createReflectedNumber(
38
- signed: bool,
39
- size: i32,
40
- reflectedTypeValue: ReflectedValueType,
41
- typeName: string,
42
- value: f64,
43
- ): i32;
44
-
45
- // @ts-ignore: linked function decorator
46
- @external("__aspect", "createReflectedLong")
47
- declare function createReflectedLong(
48
- signed: bool,
49
- size: i32,
50
- reflectedTypeValue: ReflectedValueType,
51
- typeName: string,
52
- lowValue: i32,
53
- highValue: i32,
54
- ): i32;
55
-
56
- // @ts-ignore: external declaration
57
- @external("__aspect", "pushReflectedObjectValue")
58
- @global
59
- declare function __aspectPushReflectedObjectValue(
60
- parentID: i32,
61
- value: i32,
62
- ): void;
63
-
64
- // @ts-ignore: external declaration
65
- @external("__aspect", "pushReflectedObjectKey")
66
- @global
67
- declare function __aspectPushReflectedObjectKey(
68
- parentID: i32,
69
- value: i32,
70
- ): void;
71
-
72
- @inline function sizeOf(ptr: usize): i32 {
73
- return changetype<OBJECT>(ptr - OBJECT_OVERHEAD).rtSize;
74
- }
75
-
76
- @global
77
- // @ts-ignore: global decorator is allowed here
78
- export class Reflect {
79
- public static toReflectedValue<T>(
80
- value: T,
81
- seen: Map<usize, i32> = new Map<usize, i32>(),
82
- ): i32 {
83
- // if T is a reference
84
- if (isReference<T>()) {
85
- // if the value is null, create a Null reflected value
86
- if (changetype<usize>(value) == 0) {
87
- return createReflectedValue(
88
- true,
89
- false,
90
- isNullable<T>(),
91
- 0,
92
- 0,
93
- false,
94
- sizeof<T>(),
95
- isFunction<T>()
96
- ? ReflectedValueType.Function
97
- : ReflectedValueType.Class,
98
- isManaged<T>() ? idof<T>() : 0,
99
- isFunction<T>() ? "Function" : nameof<T>(),
100
- 0,
101
- false,
102
- isManaged<T>(),
103
- );
104
- }
105
-
106
- // check the cache for anything that isn't a function
107
- if (!isFunction<T>()) {
108
- if (seen.has(changetype<usize>(value))) {
109
- return seen.get(changetype<usize>(value));
110
- }
111
- }
112
-
113
- // @ts-ignore: __aspectReflectAs() can potentially be implemented
114
- if (isDefined(value.__aspectReflectAs())) {
115
- // @ts-ignore: typesafe call to __aspectReflectAs()
116
- let displayValue = value.__aspectReflectAs();
117
- if (
118
- !isInteger(displayValue) &&
119
- !isFloat(displayValue) &&
120
- !isManaged(displayValue)
121
- ) {
122
- ERROR(
123
- "__aspectReflectAs() function should return a managed type or a number",
124
- );
125
- }
126
- return Reflect.toReflectedValue(displayValue, seen);
127
- }
128
-
129
- if (value instanceof ArrayBuffer) {
130
- let reflectedValue = createReflectedValue(
131
- false,
132
- false,
133
- isNullable<T>(),
134
- value.byteLength,
135
- changetype<usize>(value),
136
- false,
137
- value.byteLength,
138
- ReflectedValueType.ArrayBuffer,
139
- idof<T>(),
140
- nameof<T>(),
141
- 0,
142
- true,
143
- true,
144
- );
145
- seen.set(changetype<usize>(value), reflectedValue);
146
- let length = value.byteLength;
147
- for (let i = 0; i < length; i++) {
148
- __aspectPushReflectedObjectValue(
149
- reflectedValue,
150
- Reflect.toReflectedValue(
151
- load<u8>(changetype<usize>(value) + <usize>i),
152
- seen,
153
- ),
154
- );
155
- }
156
- return reflectedValue;
157
- } else if (isFunction<T>()) {
158
- let func = value;
159
- let reflectedValue = createReflectedValue(
160
- false,
161
- false,
162
- isNullable<T>(),
163
- 0,
164
- // @ts-ignore has field
165
- func.index,
166
- false,
167
- 0,
168
- ReflectedValueType.Function,
169
- 0,
170
- "Function",
171
- // @ts-ignore has field
172
- func.index,
173
- false,
174
- isManaged<T>(),
175
- );
176
- return reflectedValue;
177
- } else if (value instanceof Set) {
178
- // create a Set reflected value
179
- let reflectedObject = createReflectedValue(
180
- false,
181
- false, // sets don't have keys
182
- isNullable<T>(),
183
- 0,
184
- changetype<usize>(value),
185
- false,
186
- value.size,
187
- ReflectedValueType.Set,
188
- idof<T>(),
189
- nameof<T>(),
190
- 0,
191
- true, // sets have values
192
- true,
193
- );
194
-
195
- // cache this value
196
- seen.set(changetype<usize>(value), reflectedObject);
197
-
198
- // loop over each item and push it to the Set
199
- let values = value.values();
200
- let length = values.length;
201
- for (let i = 0; i < length; i++) {
202
- let value = unchecked(values[i]);
203
- let reflectedValueID = Reflect.toReflectedValue(value, seen);
204
- __aspectPushReflectedObjectValue(reflectedObject, reflectedValueID);
205
- }
206
- } else if (value instanceof Map) {
207
- // create a Set reflected object
208
- let reflectedValue = createReflectedValue(
209
- false,
210
- true, // maps have keys
211
- isNullable<T>(),
212
- 0,
213
- changetype<usize>(value),
214
- false,
215
- value.size,
216
- ReflectedValueType.Map,
217
- idof<T>(),
218
- nameof<T>(),
219
- 0,
220
- true, // maps have values
221
- true,
222
- );
223
-
224
- // cache this value
225
- seen.set(changetype<usize>(value), reflectedValue);
226
-
227
- // loop over each key and push the key value pair to the reflected Map
228
- let keys = value.keys();
229
- let length = keys.length;
230
- for (let i = 0; i < length; i++) {
231
- let mapKey = unchecked(keys[i]);
232
- let reflectedKeyID = Reflect.toReflectedValue(mapKey, seen);
233
- __aspectPushReflectedObjectKey(reflectedValue, reflectedKeyID);
234
-
235
- let mapValue = value.get(mapKey);
236
- let reflectedValueID = Reflect.toReflectedValue(mapValue, seen);
237
- __aspectPushReflectedObjectValue(reflectedValue, reflectedValueID);
238
- }
239
-
240
- return reflectedValue;
241
- } else if (value instanceof ArrayBufferView) {
242
- // @ts-ignore: has field
243
- let length = value.length;
244
-
245
- // create an arraylike reflected value
246
- let reflectedValue = createReflectedValue(
247
- false,
248
- false, // arrays don't have keys
249
- isNullable<T>(),
250
- 0,
251
- changetype<usize>(value),
252
- false,
253
- length,
254
- value instanceof Array
255
- ? ReflectedValueType.Array
256
- : ReflectedValueType.TypedArray,
257
- idof<T>(),
258
- nameof<T>(),
259
- 0,
260
- true, // maps have values
261
- isManaged<T>(),
262
- );
263
-
264
- // cache this value
265
- seen.set(changetype<usize>(value), reflectedValue);
266
-
267
- // loop over each value and push it to the reflected value
268
- for (let i = 0; i < length; i++) {
269
- // @ts-ignore index signature is garunteed at this point
270
- let arrayValue = unchecked(value[i]);
271
- let reflectedArrayValueID = Reflect.toReflectedValue(
272
- arrayValue,
273
- seen,
274
- );
275
- __aspectPushReflectedObjectValue(
276
- reflectedValue,
277
- reflectedArrayValueID,
278
- );
279
- }
280
-
281
- return reflectedValue;
282
- } else if (value instanceof String) {
283
- let reflectedStringID = createReflectedValue(
284
- false,
285
- false,
286
- isNullable<T>(),
287
- 0,
288
- changetype<usize>(value),
289
- false,
290
- value.length,
291
- ReflectedValueType.String,
292
- idof<T>(),
293
- nameof<T>(),
294
- changetype<usize>(value),
295
- false,
296
- true,
297
- );
298
- seen.set(changetype<usize>(value), reflectedStringID);
299
- return reflectedStringID;
300
- } else if (isArrayLike<T>()) {
301
- // @ts-ignore: arraylike has length property
302
- let length = <i32>value.length;
303
-
304
- // create an arraylike reflected value
305
- let reflectedValue = createReflectedValue(
306
- false,
307
- false, // arrays don't have keys
308
- isNullable<T>(),
309
- 0,
310
- changetype<usize>(value),
311
- false,
312
- length,
313
- ReflectedValueType.Array,
314
- isManaged<T>() ? idof<T>() : 0,
315
- nameof<T>(),
316
- 0,
317
- true, // maps have values
318
- isManaged<T>(),
319
- );
320
-
321
- // cache this value
322
- seen.set(changetype<usize>(value), reflectedValue);
323
-
324
- // loop over each array item and push it to the reflected value
325
- for (let i = 0; i < length; i++) {
326
- // @ts-ignore: index signature in arraylike
327
- let arrayValue = unchecked(value[i]);
328
- let reflectedArrayValueID = Reflect.toReflectedValue(
329
- arrayValue,
330
- seen,
331
- );
332
- __aspectPushReflectedObjectValue(
333
- reflectedValue,
334
- reflectedArrayValueID,
335
- );
336
- }
337
- return reflectedValue;
338
- } else {
339
- // generic class
340
- let reflectedObjectID = createReflectedValue(
341
- false,
342
- true, // classes have keys
343
- isNullable<T>(),
344
- offsetof<T>(),
345
- changetype<usize>(value),
346
- false,
347
- sizeOf(changetype<usize>(value)),
348
- ReflectedValueType.Class,
349
- isManaged<T>() ? idof<T>() : 0,
350
- nameof<T>(),
351
- 0,
352
- true, // classes have values
353
- isManaged<T>(),
354
- );
355
-
356
- // cache this object
357
- seen.set(changetype<usize>(value), reflectedObjectID);
358
-
359
- // @ts-ignore: __aspectAddReflectedValueKeyValuePairs is auto-generated by the transform
360
- value.__aspectAddReflectedValueKeyValuePairs(
361
- reflectedObjectID,
362
- seen,
363
- [] as StaticArray<i64>,
364
- );
365
-
366
- return reflectedObjectID;
367
- }
368
- } else if (alignof<T>() === 3 && isInteger<T>()) {
369
- // u64, i64, isize, or usize (when targeting 64 bit WebAssembly)
370
- // @ts-ignore: value is a number
371
- let reflectedValue = createReflectedLong(
372
- isSigned<T>(),
373
- sizeof<T>(),
374
- ReflectedValueType.Integer,
375
- nameof<T>(),
376
- // @ts-ignore: value is a 64 bit number
377
- <i32>(value & 0xffffffff),
378
- // @ts-ignore: value is a 64 bit number
379
- <i32>(value >>> 32),
380
- );
381
-
382
- return reflectedValue;
383
- } else {
384
- // boolean, i32, u32, f32, isize, usize (when targeting 32 bit WebAssembly), or numbers with less bits
385
- let reflectedValue = createReflectedNumber(
386
- isSigned<T>(),
387
- sizeof<T>(),
388
- isBoolean<T>()
389
- ? ReflectedValueType.Boolean
390
- : isInteger<T>()
391
- ? ReflectedValueType.Integer
392
- : ReflectedValueType.Float,
393
- nameof<T>(),
394
- // @ts-ignore: type is bool, i32, f64, or f32
395
- <f64>value,
396
- );
397
- return reflectedValue;
398
- }
399
- return 0;
400
- }
401
-
402
- public static equals<T>(
403
- left: T,
404
- right: T,
405
- stack: usize[] = [],
406
- cache: usize[] = [],
407
- ): i32 {
408
- // use `==` operator to work with operator overloads and strings
409
- if (left == right) return Reflect.SUCCESSFUL_MATCH; // works immutably for string comparison
410
-
411
- // floats should equal each other
412
- if (isFloat<T>()) {
413
- if (i32(isNaN(left)) & i32(isNaN(right))) return Reflect.SUCCESSFUL_MATCH;
414
- }
415
-
416
- if (isReference<T>()) {
417
- // T can always be null if it's a reference, emit a runtime check for it regardless of type
418
- if (
419
- i32(changetype<usize>(left) == 0) ^ i32(changetype<usize>(right) == 0)
420
- ) {
421
- return Reflect.FAILED_MATCH;
422
- }
423
- }
424
-
425
- // check every reference that isn't a function reference
426
- if (isReference<T>() && !isFunction<T>()) {
427
- if (isNullable<T>()) {
428
- return referencesEqual(left!, right!, stack, cache);
429
- } else {
430
- return referencesEqual(left, right, stack, cache);
431
- }
432
- } else {
433
- // value type, and strict equality cannot be asserted
434
- return Reflect.FAILED_MATCH;
435
- }
436
- }
437
-
438
- public static attachStackTrace(id: i32): void {
439
- attachStackTraceToReflectedValue(id);
440
- }
441
- }
442
-
443
- function referencesEqual<T>(
444
- left: T,
445
- right: T,
446
- stack: usize[],
447
- cache: usize[],
448
- ): i32 {
449
- let a = changetype<usize>(left);
450
- let b = changetype<usize>(right);
451
-
452
- let cacheLength = cache.length;
453
- // must be EVEN or there's a big problem
454
- assert(i32((cacheLength & 0x00000001) == 0), "cacheLength should be even");
455
-
456
- // short circuit for strings
457
- if (left instanceof String) {
458
- return Reflect.FAILED_MATCH;
459
- }
460
-
461
- // check the cache for matched pairs
462
- for (let i = 0; i < cacheLength; i += 2) {
463
- if (pairSeen(a, b, unchecked(cache[i]), unchecked(cache[i + 1])))
464
- return Reflect.SUCCESSFUL_MATCH;
465
- }
466
-
467
- // short circuit because this pair might already be resolving
468
- let length = stack.length;
469
- for (let i = 0; i < length; i += 2) {
470
- if (pairSeen(a, b, unchecked(stack[i]), unchecked(stack[i + 1])))
471
- return Reflect.DEFER_MATCH;
472
- }
473
-
474
- // once we've determined we need to check the references for their values, arraybuffers
475
- // require a memory compare
476
- if (left instanceof ArrayBuffer) {
477
- // @ts-ignore: typesafe access to byteLength property because T is ArrayBuffer
478
- if (left.byteLength != right.byteLength) return Reflect.FAILED_MATCH;
479
- let result = memory.compare(a, b, left.byteLength);
480
- if (result == 0) {
481
- cache.push(a);
482
- cache.push(b);
483
- return Reflect.SUCCESSFUL_MATCH;
484
- } else return Reflect.FAILED_MATCH;
485
- }
486
-
487
- // @ts-ignore: valid index signature check
488
- if (isDefined(left[0])) {
489
- // test for safe indexof usage
490
- // set match
491
- if (left instanceof Set) {
492
- // @ts-ignore: size is a valid property of Set
493
- if (left.size != right.size) return Reflect.FAILED_MATCH;
494
- stack.push(a);
495
- stack.push(b);
496
- // @ts-ignore: values() is a valid function of Set
497
- let leftValues = left.values();
498
- // @ts-ignore: values() is a valid function of Set
499
- let rightValues = right.values();
500
- let length = leftValues.length;
501
- let leftoverLength = length;
502
- for (let i = 0; i < length; i++) {
503
- let leftItem = unchecked(leftValues[i]);
504
- let continueOuter = false;
505
- for (let j = 0; j < leftoverLength; j++) {
506
- let rightItem = unchecked(rightValues[j]);
507
- if (
508
- Reflect.equals(leftItem, rightItem, stack, cache) !=
509
- Reflect.FAILED_MATCH
510
- ) {
511
- rightValues.splice(j, 1);
512
- leftoverLength--;
513
- continueOuter = true;
514
- break;
515
- }
516
- }
517
- if (continueOuter) continue;
518
-
519
- stack.pop();
520
- stack.pop();
521
- return Reflect.FAILED_MATCH;
522
- }
523
-
524
- cache.push(a);
525
- cache.push(b);
526
-
527
- stack.pop();
528
- stack.pop();
529
- return Reflect.SUCCESSFUL_MATCH;
530
- }
531
-
532
- if (left instanceof Map) {
533
- // @ts-ignore: size is a valid property of Map
534
- if (left.size != right.size) return Reflect.FAILED_MATCH;
535
- stack.push(a);
536
- stack.push(b);
537
-
538
- // collect all the keys and loop over each one
539
- let leftKeys = left.keys();
540
- // @ts-ignore: keys() is a valid function of Map
541
- let rightKeys = right.keys();
542
-
543
- // @ts-ignore: length is a valid property of Array
544
- let keyLength = leftKeys.length;
545
- let leftoverKeyLength = keyLength;
546
-
547
- // assume we match and determine if the match was a failure
548
- let result = Reflect.SUCCESSFUL_MATCH;
549
-
550
- // for each key
551
- for (let i = 0; i < keyLength; i++) {
552
- let leftKey = unchecked(leftKeys[i]);
553
- // assume won't find it
554
- let found = false;
555
-
556
- // find a matching key
557
- for (let j = 0; j < leftoverKeyLength; j++) {
558
- let rightKey = unchecked(rightKeys[j]);
559
-
560
- // if the keys match, or are still being resolved
561
- if (
562
- Reflect.equals(leftKey, rightKey, stack, cache) !=
563
- Reflect.FAILED_MATCH
564
- ) {
565
- // the key potentially matches, obtain the values associated with the keys
566
- let leftValue = left.get(leftKey);
567
- // @ts-ignore: get() is a valid function of Map
568
- let rightValue = right.get(rightKey);
569
-
570
- // if the values match, or are still being resolved
571
- if (
572
- Reflect.equals(leftValue, rightValue, stack, cache) !=
573
- Reflect.FAILED_MATCH
574
- ) {
575
- leftoverKeyLength--;
576
- rightKeys.splice(j, 1); // remove this key from the list
577
- found = true;
578
- break;
579
- }
580
- }
581
- }
582
-
583
- // if there was no match for this key value pair, the result is Failed
584
- if (!found) {
585
- result = Reflect.FAILED_MATCH;
586
- break;
587
- }
588
- }
589
-
590
- // if every key matched, result is still equal to `Reflect.MATCH`
591
- if (result == Reflect.SUCCESSFUL_MATCH) {
592
- cache.push(a);
593
- cache.push(b);
594
- }
595
-
596
- stack.pop();
597
- stack.pop();
598
- return result;
599
- }
600
-
601
- // compile time array values should be compared over a for loop
602
- // @ts-ignore: typesafe access to length
603
- if (isDefined(left.length)) {
604
- // @ts-ignore: typesafe access to length
605
- let aLength = left.length;
606
- // @ts-ignore: typesafe access to length
607
- let bLength = right.length;
608
-
609
- // assert the lengths are good
610
- if (aLength != bLength) return Reflect.FAILED_MATCH;
611
-
612
- // check each item
613
- for (let i = 0; i < aLength; i++) {
614
- let result = Reflect.equals(
615
- // @ts-ignore: typesafe and runtime check safe array access
616
- unchecked(left[i]),
617
- // @ts-ignore: typesafe and runtime check safe array access
618
- unchecked(right[i]),
619
- stack,
620
- cache,
621
- );
622
- if (result == Reflect.FAILED_MATCH) return Reflect.FAILED_MATCH;
623
- }
624
-
625
- // cache this result
626
- cache.push(a);
627
- cache.push(b);
628
-
629
- return Reflect.SUCCESSFUL_MATCH;
630
- }
631
- }
632
-
633
- // todo: handle Set<keyof<T>> and Map<keyof<T>, valueof<T>>
634
-
635
- // we are trying to resolve this pair, push it to the stack
636
- stack.push(a);
637
- stack.push(b);
638
-
639
- let result = false;
640
- // @ts-ignore: __aspectStrictEquals is defined at this point
641
- result = (isNullable(left) ? left! : left).__aspectStrictEquals(
642
- right,
643
- stack,
644
- cache,
645
- [] as StaticArray<i64>,
646
- );
647
-
648
- if (result) {
649
- cache.push(a);
650
- cache.push(b);
651
- }
652
-
653
- stack.pop();
654
- stack.pop();
655
- return select(Reflect.SUCCESSFUL_MATCH, Reflect.FAILED_MATCH, result);
656
- }
657
-
658
- export namespace Reflect {
659
- export const FAILED_MATCH = 0;
660
- export const SUCCESSFUL_MATCH = 1;
661
- export const DEFER_MATCH = 2;
662
- }
1
+ import { ArrayBufferView } from "arraybuffer";
2
+ import { Set } from "set";
3
+ import { assert } from "./assert";
4
+ import { ReflectedValueType } from "./ReflectedValueType";
5
+ import { OBJECT, OBJECT_OVERHEAD } from "rt/common";
6
+
7
+ function pairSeen(a1: usize, a2: usize, b1: usize, b2: usize): bool {
8
+ return bool(
9
+ (i32(a1 == b1) & i32(a2 == b2)) | (i32(a1 == b2) & i32(a2 == b1)),
10
+ );
11
+ }
12
+
13
+ // @ts-ignore: Decorators *are* valid here!
14
+ @external("__aspect", "attachStackTraceToReflectedValue")
15
+ declare function attachStackTraceToReflectedValue(id: i32): void;
16
+
17
+ // @ts-ignore: linked function decorator
18
+ @external("__aspect", "createReflectedValue")
19
+ declare function createReflectedValue(
20
+ isNull: bool,
21
+ hasKeys: bool,
22
+ nullable: bool,
23
+ offset: i32,
24
+ pointer: usize,
25
+ signed: bool,
26
+ size: i32,
27
+ reflectedTypeValue: ReflectedValueType,
28
+ typeId: i32,
29
+ typeName: string,
30
+ value: usize,
31
+ hasValues: bool,
32
+ isManaged: bool,
33
+ ): i32;
34
+
35
+ // @ts-ignore: linked function decorator
36
+ @external("__aspect", "createReflectedNumber")
37
+ declare function createReflectedNumber(
38
+ signed: bool,
39
+ size: i32,
40
+ reflectedTypeValue: ReflectedValueType,
41
+ typeName: string,
42
+ value: f64,
43
+ ): i32;
44
+
45
+ // @ts-ignore: linked function decorator
46
+ @external("__aspect", "createReflectedLong")
47
+ declare function createReflectedLong(
48
+ signed: bool,
49
+ size: i32,
50
+ reflectedTypeValue: ReflectedValueType,
51
+ typeName: string,
52
+ lowValue: i32,
53
+ highValue: i32,
54
+ ): i32;
55
+
56
+ // @ts-ignore: external declaration
57
+ @external("__aspect", "pushReflectedObjectValue")
58
+ @global
59
+ declare function __aspectPushReflectedObjectValue(
60
+ parentID: i32,
61
+ value: i32,
62
+ ): void;
63
+
64
+ // @ts-ignore: external declaration
65
+ @external("__aspect", "pushReflectedObjectKey")
66
+ @global
67
+ declare function __aspectPushReflectedObjectKey(
68
+ parentID: i32,
69
+ value: i32,
70
+ ): void;
71
+
72
+ @inline function sizeOf(ptr: usize): i32 {
73
+ return changetype<OBJECT>(ptr - OBJECT_OVERHEAD).rtSize;
74
+ }
75
+
76
+ @global
77
+ // @ts-ignore: global decorator is allowed here
78
+ export class Reflect {
79
+ public static toReflectedValue<T>(
80
+ value: T,
81
+ seen: Map<usize, i32> = new Map<usize, i32>(),
82
+ ): i32 {
83
+ // if T is a reference
84
+ if (isReference<T>()) {
85
+ // if the value is null, create a Null reflected value
86
+ if (changetype<usize>(value) == 0) {
87
+ return createReflectedValue(
88
+ true,
89
+ false,
90
+ isNullable<T>(),
91
+ 0,
92
+ 0,
93
+ false,
94
+ sizeof<T>(),
95
+ isFunction<T>()
96
+ ? ReflectedValueType.Function
97
+ : ReflectedValueType.Class,
98
+ isManaged<T>() ? idof<T>() : 0,
99
+ isFunction<T>() ? "Function" : nameof<T>(),
100
+ 0,
101
+ false,
102
+ isManaged<T>(),
103
+ );
104
+ }
105
+
106
+ // check the cache for anything that isn't a function
107
+ if (!isFunction<T>()) {
108
+ if (seen.has(changetype<usize>(value))) {
109
+ return seen.get(changetype<usize>(value));
110
+ }
111
+ }
112
+
113
+ // @ts-ignore: __aspectReflectAs() can potentially be implemented
114
+ if (isDefined(value.__aspectReflectAs())) {
115
+ // @ts-ignore: typesafe call to __aspectReflectAs()
116
+ let displayValue = value.__aspectReflectAs();
117
+ if (!isInteger(displayValue)) {
118
+ if (!isFloat(displayValue)) {
119
+ // @as-covers: ignore compile time check
120
+ if (!isManaged(displayValue)) {
121
+ ERROR(
122
+ "__aspectReflectAs() function should return a managed type or a number",
123
+ );
124
+ }
125
+ }
126
+ }
127
+ return Reflect.toReflectedValue(displayValue, seen);
128
+ }
129
+
130
+ if (value instanceof ArrayBuffer) {
131
+ let reflectedValue = createReflectedValue(
132
+ false,
133
+ false,
134
+ isNullable<T>(),
135
+ value.byteLength,
136
+ changetype<usize>(value),
137
+ false,
138
+ value.byteLength,
139
+ ReflectedValueType.ArrayBuffer,
140
+ idof<T>(),
141
+ nameof<T>(),
142
+ 0,
143
+ true,
144
+ true,
145
+ );
146
+ seen.set(changetype<usize>(value), reflectedValue);
147
+ let length = value.byteLength;
148
+ for (let i = 0; i < length; i++) {
149
+ __aspectPushReflectedObjectValue(
150
+ reflectedValue,
151
+ Reflect.toReflectedValue(
152
+ load<u8>(changetype<usize>(value) + <usize>i),
153
+ seen,
154
+ ),
155
+ );
156
+ }
157
+ return reflectedValue;
158
+ } else if (isFunction<T>()) {
159
+ let func = value;
160
+ let reflectedValue = createReflectedValue(
161
+ false,
162
+ false,
163
+ isNullable<T>(),
164
+ 0,
165
+ // @ts-ignore has field
166
+ func.index,
167
+ false,
168
+ 0,
169
+ ReflectedValueType.Function,
170
+ 0,
171
+ "Function",
172
+ // @ts-ignore has field
173
+ func.index,
174
+ false,
175
+ isManaged<T>(),
176
+ );
177
+ return reflectedValue;
178
+ } else if (value instanceof Set) {
179
+ // create a Set reflected value
180
+ let reflectedObject = createReflectedValue(
181
+ false,
182
+ false, // sets don't have keys
183
+ isNullable<T>(),
184
+ 0,
185
+ changetype<usize>(value),
186
+ false,
187
+ value.size,
188
+ ReflectedValueType.Set,
189
+ idof<T>(),
190
+ nameof<T>(),
191
+ 0,
192
+ true, // sets have values
193
+ true,
194
+ );
195
+
196
+ // cache this value
197
+ seen.set(changetype<usize>(value), reflectedObject);
198
+
199
+ // loop over each item and push it to the Set
200
+ let values = value.values();
201
+ let length = values.length;
202
+ for (let i = 0; i < length; i++) {
203
+ let value = unchecked(values[i]);
204
+ let reflectedValueID = Reflect.toReflectedValue(value, seen);
205
+ __aspectPushReflectedObjectValue(reflectedObject, reflectedValueID);
206
+ }
207
+ } else if (value instanceof Map) {
208
+ // create a Set reflected object
209
+ let reflectedValue = createReflectedValue(
210
+ false,
211
+ true, // maps have keys
212
+ isNullable<T>(),
213
+ 0,
214
+ changetype<usize>(value),
215
+ false,
216
+ value.size,
217
+ ReflectedValueType.Map,
218
+ idof<T>(),
219
+ nameof<T>(),
220
+ 0,
221
+ true, // maps have values
222
+ true,
223
+ );
224
+
225
+ // cache this value
226
+ seen.set(changetype<usize>(value), reflectedValue);
227
+
228
+ // loop over each key and push the key value pair to the reflected Map
229
+ let keys = value.keys();
230
+ let length = keys.length;
231
+ for (let i = 0; i < length; i++) {
232
+ let mapKey = unchecked(keys[i]);
233
+ let reflectedKeyID = Reflect.toReflectedValue(mapKey, seen);
234
+ __aspectPushReflectedObjectKey(reflectedValue, reflectedKeyID);
235
+
236
+ let mapValue = value.get(mapKey);
237
+ let reflectedValueID = Reflect.toReflectedValue(mapValue, seen);
238
+ __aspectPushReflectedObjectValue(reflectedValue, reflectedValueID);
239
+ }
240
+
241
+ return reflectedValue;
242
+ } else if (value instanceof ArrayBufferView) {
243
+ // @ts-ignore: has field
244
+ let length = value.length;
245
+
246
+ // create an arraylike reflected value
247
+ let reflectedValue = createReflectedValue(
248
+ false,
249
+ false, // arrays don't have keys
250
+ isNullable<T>(),
251
+ 0,
252
+ changetype<usize>(value),
253
+ false,
254
+ length,
255
+ ReflectedValueType.TypedArray,
256
+ idof<T>(),
257
+ nameof<T>(),
258
+ 0,
259
+ true, // maps have values
260
+ isManaged<T>(),
261
+ );
262
+
263
+ // cache this value
264
+ seen.set(changetype<usize>(value), reflectedValue);
265
+
266
+ // loop over each value and push it to the reflected value
267
+ for (let i = 0; i < length; i++) {
268
+ // @ts-ignore index signature is garunteed at this point
269
+ let arrayValue = unchecked(value[i]);
270
+ let reflectedArrayValueID = Reflect.toReflectedValue(
271
+ arrayValue,
272
+ seen,
273
+ );
274
+ __aspectPushReflectedObjectValue(
275
+ reflectedValue,
276
+ reflectedArrayValueID,
277
+ );
278
+ }
279
+
280
+ return reflectedValue;
281
+ } else if (value instanceof String) {
282
+ let reflectedStringID = createReflectedValue(
283
+ false,
284
+ false,
285
+ isNullable<T>(),
286
+ 0,
287
+ changetype<usize>(value),
288
+ false,
289
+ value.length,
290
+ ReflectedValueType.String,
291
+ idof<T>(),
292
+ nameof<T>(),
293
+ changetype<usize>(value),
294
+ false,
295
+ true,
296
+ );
297
+ seen.set(changetype<usize>(value), reflectedStringID);
298
+ return reflectedStringID;
299
+ } else if (isArrayLike<T>()) {
300
+ // @ts-ignore: arraylike has length property
301
+ let length = <i32>value.length;
302
+
303
+ // create an arraylike reflected value
304
+ let reflectedValue = createReflectedValue(
305
+ false,
306
+ false, // arrays don't have keys
307
+ isNullable<T>(),
308
+ 0,
309
+ changetype<usize>(value),
310
+ false,
311
+ length,
312
+ ReflectedValueType.Array,
313
+ isManaged<T>() ? idof<T>() : 0,
314
+ nameof<T>(),
315
+ 0,
316
+ true, // maps have values
317
+ isManaged<T>(),
318
+ );
319
+
320
+ // cache this value
321
+ seen.set(changetype<usize>(value), reflectedValue);
322
+
323
+ // loop over each array item and push it to the reflected value
324
+ for (let i = 0; i < length; i++) {
325
+ // @ts-ignore: index signature in arraylike
326
+ let arrayValue = unchecked(value[i]);
327
+ let reflectedArrayValueID = Reflect.toReflectedValue(
328
+ arrayValue,
329
+ seen,
330
+ );
331
+ __aspectPushReflectedObjectValue(
332
+ reflectedValue,
333
+ reflectedArrayValueID,
334
+ );
335
+ }
336
+ return reflectedValue;
337
+ } else {
338
+ // generic class
339
+ let reflectedObjectID = createReflectedValue(
340
+ false,
341
+ true, // classes have keys
342
+ isNullable<T>(),
343
+ offsetof<T>(),
344
+ changetype<usize>(value),
345
+ false,
346
+ sizeOf(changetype<usize>(value)),
347
+ ReflectedValueType.Class,
348
+ isManaged<T>() ? idof<T>() : 0,
349
+ nameof<T>(),
350
+ 0,
351
+ true, // classes have values
352
+ isManaged<T>(),
353
+ );
354
+
355
+ // cache this object
356
+ seen.set(changetype<usize>(value), reflectedObjectID);
357
+
358
+ // @ts-ignore: __aspectAddReflectedValueKeyValuePairs is auto-generated by the transform
359
+ value.__aspectAddReflectedValueKeyValuePairs(
360
+ reflectedObjectID,
361
+ seen,
362
+ [] as StaticArray<i64>,
363
+ );
364
+
365
+ return reflectedObjectID;
366
+ }
367
+ } else if (alignof<T>() === 3) {
368
+ if (isInteger<T>()) {
369
+ // u64, i64, isize, or usize (when targeting 64 bit WebAssembly)
370
+ // @ts-ignore: value is a number
371
+ let reflectedValue = createReflectedLong(
372
+ isSigned<T>(),
373
+ sizeof<T>(),
374
+ ReflectedValueType.Integer,
375
+ nameof<T>(),
376
+ // @ts-ignore: value is a 64 bit number
377
+ <i32>(value & 0xffffffff),
378
+ // @ts-ignore: value is a 64 bit number
379
+ <i32>(value >>> 32),
380
+ );
381
+ return reflectedValue;
382
+ }
383
+ // float64
384
+ let reflectedValue = createReflectedNumber(
385
+ isSigned<T>(),
386
+ sizeof<T>(),
387
+ ReflectedValueType.Float,
388
+ nameof<T>(),
389
+ // @ts-ignore: type is bool, i32, f64, or f32
390
+ <f64>value,
391
+ );
392
+ return reflectedValue;
393
+ } else {
394
+ // boolean, i32, u32, f32, isize, usize (when targeting 32 bit WebAssembly), or numbers with less bits
395
+ let reflectedValue = createReflectedNumber(
396
+ isSigned<T>(),
397
+ sizeof<T>(),
398
+ isBoolean<T>()
399
+ ? ReflectedValueType.Boolean
400
+ : isInteger<T>()
401
+ ? ReflectedValueType.Integer
402
+ : ReflectedValueType.Float,
403
+ nameof<T>(),
404
+ // @ts-ignore: type is bool, i32, f64, or f32
405
+ <f64>value,
406
+ );
407
+ return reflectedValue;
408
+ }
409
+ return 0;
410
+ }
411
+
412
+ public static equals<T>(
413
+ left: T,
414
+ right: T,
415
+ stack: usize[] = [],
416
+ cache: usize[] = [],
417
+ ): i32 {
418
+ // use `==` operator to work with operator overloads and strings
419
+ if (isReference(left)) {
420
+ if (changetype<usize>(left) == changetype<usize>(right)) return Reflect.SUCCESSFUL_MATCH;
421
+ if (isNullable(left)) {
422
+ if(
423
+ i32(changetype<usize>(left) == 0) ^ i32(changetype<usize>(right) == 0)
424
+ ) {
425
+ return Reflect.FAILED_MATCH;
426
+ }
427
+ // @ts-expect-error: operator overload check
428
+ if (left! == right!) return Reflect.SUCCESSFUL_MATCH;
429
+ } else {
430
+ if (left == right) return Reflect.SUCCESSFUL_MATCH;
431
+ }
432
+ } else {
433
+ if (left == right) return Reflect.SUCCESSFUL_MATCH; // works immutably for string comparison
434
+ }
435
+
436
+ // floats should equal each other
437
+ if (isFloat<T>()) {
438
+ if (i32(isNaN(left)) & i32(isNaN(right))) return Reflect.SUCCESSFUL_MATCH;
439
+ }
440
+
441
+ if (isReference<T>()) {
442
+ // T can always be null if it's a reference, emit a runtime check for it regardless of type
443
+ if (
444
+ i32(changetype<usize>(left) == 0) ^ i32(changetype<usize>(right) == 0)
445
+ ) {
446
+ return Reflect.FAILED_MATCH;
447
+ }
448
+ }
449
+
450
+ // check every reference that isn't a function reference
451
+ if (isReference<T>()) {
452
+ if (!isFunction<T>()) {
453
+ if (isNullable<T>()) {
454
+ return referencesEqual(left!, right!, stack, cache);
455
+ } else {
456
+ return referencesEqual(left, right, stack, cache);
457
+ }
458
+ }
459
+ }
460
+ // value type, and strict equality cannot be asserted
461
+ return Reflect.FAILED_MATCH;
462
+ }
463
+
464
+ public static attachStackTrace(id: i32): void {
465
+ attachStackTraceToReflectedValue(id);
466
+ }
467
+ }
468
+
469
+ function referencesEqual<T>(
470
+ left: T,
471
+ right: T,
472
+ stack: usize[],
473
+ cache: usize[],
474
+ ): i32 {
475
+ let a = changetype<usize>(left);
476
+ let b = changetype<usize>(right);
477
+
478
+ let cacheLength = cache.length;
479
+ // must be EVEN or there's a big problem
480
+ assert(i32((cacheLength & 0x00000001) == 0), "cacheLength should be even");
481
+
482
+ // short circuit for strings
483
+ if (left instanceof String) {
484
+ return Reflect.FAILED_MATCH;
485
+ }
486
+
487
+ // check the cache for matched pairs
488
+ for (let i = 0; i < cacheLength; i += 2) {
489
+ if (pairSeen(a, b, unchecked(cache[i]), unchecked(cache[i + 1])))
490
+ return Reflect.SUCCESSFUL_MATCH;
491
+ }
492
+
493
+ // short circuit because this pair might already be resolving
494
+ let length = stack.length;
495
+ for (let i = 0; i < length; i += 2) {
496
+ if (pairSeen(a, b, unchecked(stack[i]), unchecked(stack[i + 1])))
497
+ return Reflect.DEFER_MATCH;
498
+ }
499
+
500
+ // once we've determined we need to check the references for their values, arraybuffers
501
+ // require a memory compare
502
+ if (left instanceof ArrayBuffer) {
503
+ // @ts-ignore: typesafe access to byteLength property because T is ArrayBuffer
504
+ if (left.byteLength != right.byteLength) return Reflect.FAILED_MATCH;
505
+ let result = memory.compare(a, b, left.byteLength);
506
+ if (result == 0) {
507
+ cache.push(a);
508
+ cache.push(b);
509
+ return Reflect.SUCCESSFUL_MATCH;
510
+ } else return Reflect.FAILED_MATCH;
511
+ }
512
+
513
+ // @ts-ignore: valid index signature check
514
+ if (isDefined(left[0])) {
515
+ // test for safe indexof usage
516
+ // set match
517
+ if (left instanceof Set) {
518
+ // @ts-ignore: size is a valid property of Set
519
+ if (left.size != right.size) return Reflect.FAILED_MATCH;
520
+ stack.push(a);
521
+ stack.push(b);
522
+ // @ts-ignore: values() is a valid function of Set
523
+ let leftValues = left.values();
524
+ // @ts-ignore: values() is a valid function of Set
525
+ let rightValues = right.values();
526
+ let length = leftValues.length;
527
+ let leftoverLength = length;
528
+ for (let i = 0; i < length; i++) {
529
+ let leftItem = unchecked(leftValues[i]);
530
+ let continueOuter = false;
531
+ for (let j = 0; j < leftoverLength; j++) {
532
+ let rightItem = unchecked(rightValues[j]);
533
+ if (
534
+ Reflect.equals(leftItem, rightItem, stack, cache) !=
535
+ Reflect.FAILED_MATCH
536
+ ) {
537
+ rightValues.splice(j, 1);
538
+ leftoverLength--;
539
+ continueOuter = true;
540
+ break;
541
+ }
542
+ }
543
+ if (continueOuter) continue;
544
+
545
+ stack.pop();
546
+ stack.pop();
547
+ return Reflect.FAILED_MATCH;
548
+ }
549
+
550
+ cache.push(a);
551
+ cache.push(b);
552
+
553
+ stack.pop();
554
+ stack.pop();
555
+ return Reflect.SUCCESSFUL_MATCH;
556
+ }
557
+
558
+ if (left instanceof Map) {
559
+ // @ts-ignore: size is a valid property of Map
560
+ if (left.size != right.size) return Reflect.FAILED_MATCH;
561
+ stack.push(a);
562
+ stack.push(b);
563
+
564
+ // collect all the keys and loop over each one
565
+ let leftKeys = left.keys();
566
+ // @ts-ignore: keys() is a valid function of Map
567
+ let rightKeys = right.keys();
568
+
569
+ // @ts-ignore: length is a valid property of Array
570
+ let keyLength = leftKeys.length;
571
+ let leftoverKeyLength = keyLength;
572
+
573
+ // assume we match and determine if the match was a failure
574
+ let result = Reflect.SUCCESSFUL_MATCH;
575
+
576
+ // for each key
577
+ for (let i = 0; i < keyLength; i++) {
578
+ let leftKey = unchecked(leftKeys[i]);
579
+ // assume won't find it
580
+ let found = false;
581
+
582
+ // find a matching key
583
+ for (let j = 0; j < leftoverKeyLength; j++) {
584
+ let rightKey = unchecked(rightKeys[j]);
585
+
586
+ // if the keys match, or are still being resolved
587
+ if (
588
+ Reflect.equals(leftKey, rightKey, stack, cache) !=
589
+ Reflect.FAILED_MATCH
590
+ ) {
591
+ // the key potentially matches, obtain the values associated with the keys
592
+ let leftValue = left.get(leftKey);
593
+ // @ts-ignore: get() is a valid function of Map
594
+ let rightValue = right.get(rightKey);
595
+
596
+ // if the values match, or are still being resolved
597
+ if (
598
+ Reflect.equals(leftValue, rightValue, stack, cache) !=
599
+ Reflect.FAILED_MATCH
600
+ ) {
601
+ leftoverKeyLength--;
602
+ rightKeys.splice(j, 1); // remove this key from the list
603
+ found = true;
604
+ break;
605
+ }
606
+ }
607
+ }
608
+
609
+ // if there was no match for this key value pair, the result is Failed
610
+ if (!found) {
611
+ result = Reflect.FAILED_MATCH;
612
+ break;
613
+ }
614
+ }
615
+
616
+ // if every key matched, result is still equal to `Reflect.MATCH`
617
+ if (result == Reflect.SUCCESSFUL_MATCH) {
618
+ cache.push(a);
619
+ cache.push(b);
620
+ }
621
+
622
+ stack.pop();
623
+ stack.pop();
624
+ return result;
625
+ }
626
+
627
+ // compile time array values should be compared over a for loop
628
+ // @ts-ignore: typesafe access to length
629
+ if (isDefined(left.length)) {
630
+ // @ts-ignore: typesafe access to length
631
+ let aLength = left.length;
632
+ // @ts-ignore: typesafe access to length
633
+ let bLength = right.length;
634
+
635
+ // assert the lengths are good
636
+ if (aLength != bLength) return Reflect.FAILED_MATCH;
637
+
638
+ // check each item
639
+ for (let i = 0; i < aLength; i++) {
640
+ let result = Reflect.equals(
641
+ // @ts-ignore: typesafe and runtime check safe array access
642
+ unchecked(left[i]),
643
+ // @ts-ignore: typesafe and runtime check safe array access
644
+ unchecked(right[i]),
645
+ stack,
646
+ cache,
647
+ );
648
+ if (result == Reflect.FAILED_MATCH) return Reflect.FAILED_MATCH;
649
+ }
650
+
651
+ // cache this result
652
+ cache.push(a);
653
+ cache.push(b);
654
+
655
+ return Reflect.SUCCESSFUL_MATCH;
656
+ }
657
+ }
658
+
659
+ // todo: handle Set<keyof<T>> and Map<keyof<T>, valueof<T>>
660
+
661
+ // we are trying to resolve this pair, push it to the stack
662
+ stack.push(a);
663
+ stack.push(b);
664
+
665
+ let result = false;
666
+ if (isNullable(left)) {
667
+ // @ts-ignore: __aspectStrictEquals is defined at this point
668
+ result = left!.__aspectStrictEquals(
669
+ right,
670
+ stack,
671
+ cache,
672
+ [] as StaticArray<i64>,
673
+ );
674
+ } else {
675
+ // @ts-ignore: __aspectStrictEquals is defined at this point
676
+ result = left.__aspectStrictEquals(
677
+ right,
678
+ stack,
679
+ cache,
680
+ [] as StaticArray<i64>,
681
+ );
682
+ }
683
+
684
+ if (result) {
685
+ cache.push(a);
686
+ cache.push(b);
687
+ }
688
+
689
+ stack.pop();
690
+ stack.pop();
691
+ return select(Reflect.SUCCESSFUL_MATCH, Reflect.FAILED_MATCH, result);
692
+ }
693
+
694
+ export namespace Reflect {
695
+ export const FAILED_MATCH = 0;
696
+ export const SUCCESSFUL_MATCH = 1;
697
+ export const DEFER_MATCH = 2;
698
+ }