main
ts 1,126 lines 29.8 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 import {Effect, ValueKind, ValueReason} from './HIR';
9 import {
10 BUILTIN_SHAPES,
11 BuiltInArrayId,
12 BuiltInMapId,
13 BuiltInMixedReadonlyId,
14 BuiltInObjectId,
15 BuiltInSetId,
16 BuiltInUseActionStateId,
17 BuiltInUseContextHookId,
18 BuiltInUseEffectEventId,
19 BuiltInUseEffectHookId,
20 BuiltInUseInsertionEffectHookId,
21 BuiltInUseLayoutEffectHookId,
22 BuiltInUseOperatorId,
23 BuiltInUseOptimisticId,
24 BuiltInUseReducerId,
25 BuiltInUseRefId,
26 BuiltInUseStateId,
27 BuiltInUseTransitionId,
28 BuiltInWeakMapId,
29 BuiltInWeakSetId,
30 BuiltInEffectEventId,
31 ReanimatedSharedValueId,
32 ShapeRegistry,
33 addFunction,
34 addHook,
35 addObject,
36 } from './ObjectShape';
37 import {BuiltInType, ObjectType, PolyType} from './Types';
38 import {TypeConfig} from './TypeSchema';
39 import {assertExhaustive} from '../Utils/utils';
40 import {isHookName} from './Environment';
41 import {CompilerError, SourceLocation} from '..';
42
43 /*
44 * This file exports types and defaults for JavaScript global objects.
45 * A Forget `Environment` stores the GlobalRegistry and ShapeRegistry
46 * used for the current project. These ultimately help Forget refine
47 * its inference of types (i.e. Object vs Primitive) and effects
48 * (i.e. read vs mutate) in source programs.
49 */
50
51 // ShapeRegistry with default definitions for builtins and global objects.
52 export const DEFAULT_SHAPES: ShapeRegistry = new Map(BUILTIN_SHAPES);
53
54 // Hack until we add ObjectShapes for all globals
55 const UNTYPED_GLOBALS: Set<string> = new Set([
56 'Object',
57 'Function',
58 'RegExp',
59 'Date',
60 'Error',
61 'TypeError',
62 'RangeError',
63 'ReferenceError',
64 'SyntaxError',
65 'URIError',
66 'EvalError',
67 'DataView',
68 'Float32Array',
69 'Float64Array',
70 'Int8Array',
71 'Int16Array',
72 'Int32Array',
73 'WeakMap',
74 'Uint8Array',
75 'Uint8ClampedArray',
76 'Uint16Array',
77 'Uint32Array',
78 'ArrayBuffer',
79 'JSON',
80 'console',
81 'eval',
82 ]);
83
84 const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
85 [
86 'Object',
87 addObject(DEFAULT_SHAPES, 'Object', [
88 [
89 'keys',
90 addFunction(DEFAULT_SHAPES, [], {
91 positionalParams: [Effect.Read],
92 restParam: null,
93 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
94 calleeEffect: Effect.Read,
95 returnValueKind: ValueKind.Mutable,
96 }),
97 ],
98 [
99 /**
100 * Object.fromEntries(iterable)
101 * iterable: An iterable, such as an Array or Map, containing a list of
102 * objects. Each object should have two properties.
103 * Returns a new object whose properties are given by the entries of the
104 * iterable.
105 */
106 'fromEntries',
107 addFunction(DEFAULT_SHAPES, [], {
108 positionalParams: [Effect.ConditionallyMutate],
109 restParam: null,
110 returnType: {kind: 'Object', shapeId: BuiltInObjectId},
111 calleeEffect: Effect.Read,
112 returnValueKind: ValueKind.Mutable,
113 }),
114 ],
115 [
116 'entries',
117 addFunction(DEFAULT_SHAPES, [], {
118 positionalParams: [Effect.Capture],
119 restParam: null,
120 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
121 calleeEffect: Effect.Read,
122 returnValueKind: ValueKind.Mutable,
123 aliasing: {
124 receiver: '@receiver',
125 params: ['@object'],
126 rest: null,
127 returns: '@returns',
128 temporaries: [],
129 effects: [
130 {
131 kind: 'Create',
132 into: '@returns',
133 reason: ValueReason.KnownReturnSignature,
134 value: ValueKind.Mutable,
135 },
136 // Object values are captured into the return
137 {
138 kind: 'Capture',
139 from: '@object',
140 into: '@returns',
141 },
142 ],
143 },
144 }),
145 ],
146 [
147 'keys',
148 addFunction(DEFAULT_SHAPES, [], {
149 positionalParams: [Effect.Read],
150 restParam: null,
151 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
152 calleeEffect: Effect.Read,
153 returnValueKind: ValueKind.Mutable,
154 aliasing: {
155 receiver: '@receiver',
156 params: ['@object'],
157 rest: null,
158 returns: '@returns',
159 temporaries: [],
160 effects: [
161 {
162 kind: 'Create',
163 into: '@returns',
164 reason: ValueReason.KnownReturnSignature,
165 value: ValueKind.Mutable,
166 },
167 // Only keys are captured, and keys are immutable
168 {
169 kind: 'ImmutableCapture',
170 from: '@object',
171 into: '@returns',
172 },
173 ],
174 },
175 }),
176 ],
177 [
178 'values',
179 addFunction(DEFAULT_SHAPES, [], {
180 positionalParams: [Effect.Capture],
181 restParam: null,
182 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
183 calleeEffect: Effect.Read,
184 returnValueKind: ValueKind.Mutable,
185 aliasing: {
186 receiver: '@receiver',
187 params: ['@object'],
188 rest: null,
189 returns: '@returns',
190 temporaries: [],
191 effects: [
192 {
193 kind: 'Create',
194 into: '@returns',
195 reason: ValueReason.KnownReturnSignature,
196 value: ValueKind.Mutable,
197 },
198 // Object values are captured into the return
199 {
200 kind: 'Capture',
201 from: '@object',
202 into: '@returns',
203 },
204 ],
205 },
206 }),
207 ],
208 ]),
209 ],
210 [
211 'Array',
212 addObject(DEFAULT_SHAPES, 'Array', [
213 [
214 'isArray',
215 // Array.isArray(value)
216 addFunction(DEFAULT_SHAPES, [], {
217 positionalParams: [Effect.Read],
218 restParam: null,
219 returnType: {kind: 'Primitive'},
220 calleeEffect: Effect.Read,
221 returnValueKind: ValueKind.Primitive,
222 }),
223 ],
224 /*
225 * https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.from
226 * Array.from(arrayLike, optionalFn, optionalThis)
227 * Note that the Effect of `arrayLike` is polymorphic i.e.
228 * - Effect.read if
229 * - it does not have an @iterator property and is array-like
230 * (i.e. has a length property)
231 * - it is an iterable object whose iterator does not mutate itself
232 * - Effect.mutate if it is a self-mutative iterator (e.g. a generator
233 * function)
234 */
235 [
236 'from',
237 addFunction(DEFAULT_SHAPES, [], {
238 positionalParams: [
239 Effect.ConditionallyMutateIterator,
240 Effect.ConditionallyMutate,
241 Effect.ConditionallyMutate,
242 ],
243 restParam: Effect.Read,
244 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
245 calleeEffect: Effect.Read,
246 returnValueKind: ValueKind.Mutable,
247 }),
248 ],
249 [
250 'of',
251 // Array.of(element0, ..., elementN)
252 addFunction(DEFAULT_SHAPES, [], {
253 positionalParams: [],
254 restParam: Effect.Read,
255 returnType: {kind: 'Object', shapeId: BuiltInArrayId},
256 calleeEffect: Effect.Read,
257 returnValueKind: ValueKind.Mutable,
258 }),
259 ],
260 ]),
261 ],
262 [
263 'performance',
264 addObject(DEFAULT_SHAPES, 'performance', [
265 // Static methods (TODO)
266 [
267 'now',
268 // Date.now()
269 addFunction(DEFAULT_SHAPES, [], {
270 positionalParams: [],
271 restParam: Effect.Read,
272 returnType: {kind: 'Poly'}, // TODO: could be Primitive, but that would change existing compilation
273 calleeEffect: Effect.Read,
274 returnValueKind: ValueKind.Mutable, // same here
275 impure: true,
276 canonicalName: 'performance.now',
277 }),
278 ],
279 ]),
280 ],
281 [
282 'Date',
283 addObject(DEFAULT_SHAPES, 'Date', [
284 // Static methods (TODO)
285 [
286 'now',
287 // Date.now()
288 addFunction(DEFAULT_SHAPES, [], {
289 positionalParams: [],
290 restParam: Effect.Read,
291 returnType: {kind: 'Poly'}, // TODO: could be Primitive, but that would change existing compilation
292 calleeEffect: Effect.Read,
293 returnValueKind: ValueKind.Mutable, // same here
294 impure: true,
295 canonicalName: 'Date.now',
296 }),
297 ],
298 ]),
299 ],
300 [
301 'Math',
302 addObject(DEFAULT_SHAPES, 'Math', [
303 // Static properties (TODO)
304 ['PI', {kind: 'Primitive'}],
305 // Static methods (TODO)
306 [
307 'max',
308 // Math.max(value0, ..., valueN)
309 addFunction(DEFAULT_SHAPES, [], {
310 positionalParams: [],
311 restParam: Effect.Read,
312 returnType: {kind: 'Primitive'},
313 calleeEffect: Effect.Read,
314 returnValueKind: ValueKind.Primitive,
315 }),
316 ],
317 [
318 'min',
319 // Math.min(value0, ..., valueN)
320 addFunction(DEFAULT_SHAPES, [], {
321 positionalParams: [],
322 restParam: Effect.Read,
323 returnType: {kind: 'Primitive'},
324 calleeEffect: Effect.Read,
325 returnValueKind: ValueKind.Primitive,
326 }),
327 ],
328 [
329 'trunc',
330 addFunction(DEFAULT_SHAPES, [], {
331 positionalParams: [],
332 restParam: Effect.Read,
333 returnType: {kind: 'Primitive'},
334 calleeEffect: Effect.Read,
335 returnValueKind: ValueKind.Primitive,
336 }),
337 ],
338 [
339 'ceil',
340 addFunction(DEFAULT_SHAPES, [], {
341 positionalParams: [],
342 restParam: Effect.Read,
343 returnType: {kind: 'Primitive'},
344 calleeEffect: Effect.Read,
345 returnValueKind: ValueKind.Primitive,
346 }),
347 ],
348 [
349 'floor',
350 addFunction(DEFAULT_SHAPES, [], {
351 positionalParams: [],
352 restParam: Effect.Read,
353 returnType: {kind: 'Primitive'},
354 calleeEffect: Effect.Read,
355 returnValueKind: ValueKind.Primitive,
356 }),
357 ],
358 [
359 'pow',
360 addFunction(DEFAULT_SHAPES, [], {
361 positionalParams: [],
362 restParam: Effect.Read,
363 returnType: {kind: 'Primitive'},
364 calleeEffect: Effect.Read,
365 returnValueKind: ValueKind.Primitive,
366 }),
367 ],
368 [
369 'random',
370 addFunction(DEFAULT_SHAPES, [], {
371 positionalParams: [],
372 restParam: Effect.Read,
373 returnType: {kind: 'Poly'}, // TODO: could be Primitive, but that would change existing compilation
374 calleeEffect: Effect.Read,
375 returnValueKind: ValueKind.Mutable, // same here
376 impure: true,
377 canonicalName: 'Math.random',
378 }),
379 ],
380 ]),
381 ],
382 ['Infinity', {kind: 'Primitive'}],
383 ['NaN', {kind: 'Primitive'}],
384 [
385 'console',
386 addObject(DEFAULT_SHAPES, 'console', [
387 [
388 'error',
389 addFunction(DEFAULT_SHAPES, [], {
390 positionalParams: [],
391 restParam: Effect.Read,
392 returnType: {kind: 'Primitive'},
393 calleeEffect: Effect.Read,
394 returnValueKind: ValueKind.Primitive,
395 }),
396 ],
397 [
398 'info',
399 addFunction(DEFAULT_SHAPES, [], {
400 positionalParams: [],
401 restParam: Effect.Read,
402 returnType: {kind: 'Primitive'},
403 calleeEffect: Effect.Read,
404 returnValueKind: ValueKind.Primitive,
405 }),
406 ],
407 [
408 'log',
409 addFunction(DEFAULT_SHAPES, [], {
410 positionalParams: [],
411 restParam: Effect.Read,
412 returnType: {kind: 'Primitive'},
413 calleeEffect: Effect.Read,
414 returnValueKind: ValueKind.Primitive,
415 }),
416 ],
417 [
418 'table',
419 addFunction(DEFAULT_SHAPES, [], {
420 positionalParams: [],
421 restParam: Effect.Read,
422 returnType: {kind: 'Primitive'},
423 calleeEffect: Effect.Read,
424 returnValueKind: ValueKind.Primitive,
425 }),
426 ],
427 [
428 'trace',
429 addFunction(DEFAULT_SHAPES, [], {
430 positionalParams: [],
431 restParam: Effect.Read,
432 returnType: {kind: 'Primitive'},
433 calleeEffect: Effect.Read,
434 returnValueKind: ValueKind.Primitive,
435 }),
436 ],
437 [
438 'warn',
439 addFunction(DEFAULT_SHAPES, [], {
440 positionalParams: [],
441 restParam: Effect.Read,
442 returnType: {kind: 'Primitive'},
443 calleeEffect: Effect.Read,
444 returnValueKind: ValueKind.Primitive,
445 }),
446 ],
447 ]),
448 ],
449 [
450 'Boolean',
451 addFunction(DEFAULT_SHAPES, [], {
452 positionalParams: [],
453 restParam: Effect.Read,
454 returnType: {kind: 'Primitive'},
455 calleeEffect: Effect.Read,
456 returnValueKind: ValueKind.Primitive,
457 }),
458 ],
459 [
460 'Number',
461 addFunction(DEFAULT_SHAPES, [], {
462 positionalParams: [],
463 restParam: Effect.Read,
464 returnType: {kind: 'Primitive'},
465 calleeEffect: Effect.Read,
466 returnValueKind: ValueKind.Primitive,
467 }),
468 ],
469 [
470 'String',
471 addFunction(DEFAULT_SHAPES, [], {
472 positionalParams: [],
473 restParam: Effect.Read,
474 returnType: {kind: 'Primitive'},
475 calleeEffect: Effect.Read,
476 returnValueKind: ValueKind.Primitive,
477 }),
478 ],
479 [
480 'parseInt',
481 addFunction(DEFAULT_SHAPES, [], {
482 positionalParams: [],
483 restParam: Effect.Read,
484 returnType: {kind: 'Primitive'},
485 calleeEffect: Effect.Read,
486 returnValueKind: ValueKind.Primitive,
487 }),
488 ],
489 [
490 'parseFloat',
491 addFunction(DEFAULT_SHAPES, [], {
492 positionalParams: [],
493 restParam: Effect.Read,
494 returnType: {kind: 'Primitive'},
495 calleeEffect: Effect.Read,
496 returnValueKind: ValueKind.Primitive,
497 }),
498 ],
499 [
500 'isNaN',
501 addFunction(DEFAULT_SHAPES, [], {
502 positionalParams: [],
503 restParam: Effect.Read,
504 returnType: {kind: 'Primitive'},
505 calleeEffect: Effect.Read,
506 returnValueKind: ValueKind.Primitive,
507 }),
508 ],
509 [
510 'isFinite',
511 addFunction(DEFAULT_SHAPES, [], {
512 positionalParams: [],
513 restParam: Effect.Read,
514 returnType: {kind: 'Primitive'},
515 calleeEffect: Effect.Read,
516 returnValueKind: ValueKind.Primitive,
517 }),
518 ],
519 [
520 'encodeURI',
521 addFunction(DEFAULT_SHAPES, [], {
522 positionalParams: [],
523 restParam: Effect.Read,
524 returnType: {kind: 'Primitive'},
525 calleeEffect: Effect.Read,
526 returnValueKind: ValueKind.Primitive,
527 }),
528 ],
529 [
530 'encodeURIComponent',
531 addFunction(DEFAULT_SHAPES, [], {
532 positionalParams: [],
533 restParam: Effect.Read,
534 returnType: {kind: 'Primitive'},
535 calleeEffect: Effect.Read,
536 returnValueKind: ValueKind.Primitive,
537 }),
538 ],
539 [
540 'decodeURI',
541 addFunction(DEFAULT_SHAPES, [], {
542 positionalParams: [],
543 restParam: Effect.Read,
544 returnType: {kind: 'Primitive'},
545 calleeEffect: Effect.Read,
546 returnValueKind: ValueKind.Primitive,
547 }),
548 ],
549 [
550 'decodeURIComponent',
551 addFunction(DEFAULT_SHAPES, [], {
552 positionalParams: [],
553 restParam: Effect.Read,
554 returnType: {kind: 'Primitive'},
555 calleeEffect: Effect.Read,
556 returnValueKind: ValueKind.Primitive,
557 }),
558 ],
559 [
560 'Map',
561 addFunction(
562 DEFAULT_SHAPES,
563 [],
564 {
565 positionalParams: [Effect.ConditionallyMutateIterator],
566 restParam: null,
567 returnType: {kind: 'Object', shapeId: BuiltInMapId},
568 calleeEffect: Effect.Read,
569 returnValueKind: ValueKind.Mutable,
570 },
571 null,
572 true,
573 ),
574 ],
575 [
576 'Set',
577 addFunction(
578 DEFAULT_SHAPES,
579 [],
580 {
581 positionalParams: [Effect.ConditionallyMutateIterator],
582 restParam: null,
583 returnType: {kind: 'Object', shapeId: BuiltInSetId},
584 calleeEffect: Effect.Read,
585 returnValueKind: ValueKind.Mutable,
586 },
587 null,
588 true,
589 ),
590 ],
591 [
592 'WeakMap',
593 addFunction(
594 DEFAULT_SHAPES,
595 [],
596 {
597 positionalParams: [Effect.ConditionallyMutateIterator],
598 restParam: null,
599 returnType: {kind: 'Object', shapeId: BuiltInWeakMapId},
600 calleeEffect: Effect.Read,
601 returnValueKind: ValueKind.Mutable,
602 },
603 null,
604 true,
605 ),
606 ],
607 [
608 'WeakSet',
609 addFunction(
610 DEFAULT_SHAPES,
611 [],
612 {
613 positionalParams: [Effect.ConditionallyMutateIterator],
614 restParam: null,
615 returnType: {kind: 'Object', shapeId: BuiltInWeakSetId},
616 calleeEffect: Effect.Read,
617 returnValueKind: ValueKind.Mutable,
618 },
619 null,
620 true,
621 ),
622 ],
623 // TODO: rest of Global objects
624 ];
625
626 /*
627 * TODO(mofeiZ): We currently only store rest param effects for hooks.
628 * now that FeatureFlag `enableTreatHooksAsFunctions` is removed we can
629 * use positional params too (?)
630 */
631 const REACT_APIS: Array<[string, BuiltInType]> = [
632 [
633 'useContext',
634 addHook(
635 DEFAULT_SHAPES,
636 {
637 positionalParams: [],
638 restParam: Effect.Read,
639 returnType: {kind: 'Poly'},
640 calleeEffect: Effect.Read,
641 hookKind: 'useContext',
642 returnValueKind: ValueKind.Frozen,
643 returnValueReason: ValueReason.Context,
644 },
645 BuiltInUseContextHookId,
646 ),
647 ],
648 [
649 'useState',
650 addHook(DEFAULT_SHAPES, {
651 positionalParams: [],
652 restParam: Effect.Freeze,
653 returnType: {kind: 'Object', shapeId: BuiltInUseStateId},
654 calleeEffect: Effect.Read,
655 hookKind: 'useState',
656 returnValueKind: ValueKind.Frozen,
657 returnValueReason: ValueReason.State,
658 }),
659 ],
660 [
661 'useActionState',
662 addHook(DEFAULT_SHAPES, {
663 positionalParams: [],
664 restParam: Effect.Freeze,
665 returnType: {kind: 'Object', shapeId: BuiltInUseActionStateId},
666 calleeEffect: Effect.Read,
667 hookKind: 'useActionState',
668 returnValueKind: ValueKind.Frozen,
669 returnValueReason: ValueReason.State,
670 }),
671 ],
672 [
673 'useReducer',
674 addHook(DEFAULT_SHAPES, {
675 positionalParams: [],
676 restParam: Effect.Freeze,
677 returnType: {kind: 'Object', shapeId: BuiltInUseReducerId},
678 calleeEffect: Effect.Read,
679 hookKind: 'useReducer',
680 returnValueKind: ValueKind.Frozen,
681 returnValueReason: ValueReason.ReducerState,
682 }),
683 ],
684 [
685 'useRef',
686 addHook(DEFAULT_SHAPES, {
687 positionalParams: [],
688 restParam: Effect.Capture,
689 returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
690 calleeEffect: Effect.Read,
691 hookKind: 'useRef',
692 returnValueKind: ValueKind.Mutable,
693 }),
694 ],
695 [
696 'useImperativeHandle',
697 addHook(DEFAULT_SHAPES, {
698 positionalParams: [],
699 restParam: Effect.Freeze,
700 returnType: {kind: 'Primitive'},
701 calleeEffect: Effect.Read,
702 hookKind: 'useImperativeHandle',
703 returnValueKind: ValueKind.Frozen,
704 }),
705 ],
706 [
707 'useMemo',
708 addHook(DEFAULT_SHAPES, {
709 positionalParams: [],
710 restParam: Effect.Freeze,
711 returnType: {kind: 'Poly'},
712 calleeEffect: Effect.Read,
713 hookKind: 'useMemo',
714 returnValueKind: ValueKind.Frozen,
715 }),
716 ],
717 [
718 'useCallback',
719 addHook(DEFAULT_SHAPES, {
720 positionalParams: [],
721 restParam: Effect.Freeze,
722 returnType: {kind: 'Poly'},
723 calleeEffect: Effect.Read,
724 hookKind: 'useCallback',
725 returnValueKind: ValueKind.Frozen,
726 }),
727 ],
728 [
729 'useEffect',
730 addHook(
731 DEFAULT_SHAPES,
732 {
733 positionalParams: [],
734 restParam: Effect.Freeze,
735 returnType: {kind: 'Primitive'},
736 calleeEffect: Effect.Read,
737 hookKind: 'useEffect',
738 returnValueKind: ValueKind.Frozen,
739 aliasing: {
740 receiver: '@receiver',
741 params: [],
742 rest: '@rest',
743 returns: '@returns',
744 temporaries: ['@effect'],
745 effects: [
746 // Freezes the function and deps
747 {
748 kind: 'Freeze',
749 value: '@rest',
750 reason: ValueReason.Effect,
751 },
752 // Internally creates an effect object that captures the function and deps
753 {
754 kind: 'Create',
755 into: '@effect',
756 value: ValueKind.Frozen,
757 reason: ValueReason.KnownReturnSignature,
758 },
759 // The effect stores the function and dependencies
760 {
761 kind: 'Capture',
762 from: '@rest',
763 into: '@effect',
764 },
765 // Returns undefined
766 {
767 kind: 'Create',
768 into: '@returns',
769 value: ValueKind.Primitive,
770 reason: ValueReason.KnownReturnSignature,
771 },
772 ],
773 },
774 },
775 BuiltInUseEffectHookId,
776 ),
777 ],
778 [
779 'useLayoutEffect',
780 addHook(
781 DEFAULT_SHAPES,
782 {
783 positionalParams: [],
784 restParam: Effect.Freeze,
785 returnType: {kind: 'Poly'},
786 calleeEffect: Effect.Read,
787 hookKind: 'useLayoutEffect',
788 returnValueKind: ValueKind.Frozen,
789 },
790 BuiltInUseLayoutEffectHookId,
791 ),
792 ],
793 [
794 'useInsertionEffect',
795 addHook(
796 DEFAULT_SHAPES,
797 {
798 positionalParams: [],
799 restParam: Effect.Freeze,
800 returnType: {kind: 'Poly'},
801 calleeEffect: Effect.Read,
802 hookKind: 'useInsertionEffect',
803 returnValueKind: ValueKind.Frozen,
804 },
805 BuiltInUseInsertionEffectHookId,
806 ),
807 ],
808 [
809 'useTransition',
810 addHook(DEFAULT_SHAPES, {
811 positionalParams: [],
812 restParam: null,
813 returnType: {kind: 'Object', shapeId: BuiltInUseTransitionId},
814 calleeEffect: Effect.Read,
815 hookKind: 'useTransition',
816 returnValueKind: ValueKind.Frozen,
817 }),
818 ],
819 [
820 'useOptimistic',
821 addHook(DEFAULT_SHAPES, {
822 positionalParams: [],
823 restParam: Effect.Freeze,
824 returnType: {kind: 'Object', shapeId: BuiltInUseOptimisticId},
825 calleeEffect: Effect.Read,
826 hookKind: 'useOptimistic',
827 returnValueKind: ValueKind.Frozen,
828 returnValueReason: ValueReason.State,
829 }),
830 ],
831 [
832 'use',
833 addFunction(
834 DEFAULT_SHAPES,
835 [],
836 {
837 positionalParams: [],
838 restParam: Effect.Freeze,
839 returnType: {kind: 'Poly'},
840 calleeEffect: Effect.Read,
841 returnValueKind: ValueKind.Frozen,
842 },
843 BuiltInUseOperatorId,
844 ),
845 ],
846 [
847 'useEffectEvent',
848 addHook(
849 DEFAULT_SHAPES,
850 {
851 positionalParams: [],
852 restParam: Effect.Freeze,
853 returnType: {
854 kind: 'Function',
855 return: {kind: 'Poly'},
856 shapeId: BuiltInEffectEventId,
857 isConstructor: false,
858 },
859 calleeEffect: Effect.Read,
860 hookKind: 'useEffectEvent',
861 // Frozen because it should not mutate any locally-bound values
862 returnValueKind: ValueKind.Frozen,
863 },
864 BuiltInUseEffectEventId,
865 ),
866 ],
867 ];
868
869 TYPED_GLOBALS.push(
870 [
871 'React',
872 addObject(DEFAULT_SHAPES, null, [
873 ...REACT_APIS,
874 [
875 'createElement',
876 addFunction(DEFAULT_SHAPES, [], {
877 positionalParams: [],
878 restParam: Effect.Freeze,
879 returnType: {kind: 'Poly'},
880 calleeEffect: Effect.Read,
881 returnValueKind: ValueKind.Frozen,
882 }),
883 ],
884 [
885 'cloneElement',
886 addFunction(DEFAULT_SHAPES, [], {
887 positionalParams: [],
888 restParam: Effect.Freeze,
889 returnType: {kind: 'Poly'},
890 calleeEffect: Effect.Read,
891 returnValueKind: ValueKind.Frozen,
892 }),
893 ],
894 [
895 'createRef',
896 addFunction(DEFAULT_SHAPES, [], {
897 positionalParams: [],
898 restParam: Effect.Capture, // createRef takes no paramters
899 returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
900 calleeEffect: Effect.Read,
901 returnValueKind: ValueKind.Mutable,
902 }),
903 ],
904 ]),
905 ],
906 [
907 '_jsx',
908 addFunction(DEFAULT_SHAPES, [], {
909 positionalParams: [],
910 restParam: Effect.Freeze,
911 returnType: {kind: 'Poly'},
912 calleeEffect: Effect.Read,
913 returnValueKind: ValueKind.Frozen,
914 }),
915 ],
916 );
917
918 export type Global = BuiltInType | PolyType;
919 export type GlobalRegistry = Map<string, Global>;
920 export const DEFAULT_GLOBALS: GlobalRegistry = new Map(REACT_APIS);
921
922 // Hack until we add ObjectShapes for all globals
923 for (const name of UNTYPED_GLOBALS) {
924 DEFAULT_GLOBALS.set(name, {
925 kind: 'Poly',
926 });
927 }
928
929 for (const [name, type_] of TYPED_GLOBALS) {
930 DEFAULT_GLOBALS.set(name, type_);
931 }
932
933 // Recursive global types
934 DEFAULT_GLOBALS.set(
935 'globalThis',
936 addObject(DEFAULT_SHAPES, 'globalThis', TYPED_GLOBALS),
937 );
938 DEFAULT_GLOBALS.set(
939 'global',
940 addObject(DEFAULT_SHAPES, 'global', TYPED_GLOBALS),
941 );
942
943 export function installTypeConfig(
944 globals: GlobalRegistry,
945 shapes: ShapeRegistry,
946 typeConfig: TypeConfig,
947 moduleName: string,
948 loc: SourceLocation,
949 ): Global {
950 switch (typeConfig.kind) {
951 case 'type': {
952 switch (typeConfig.name) {
953 case 'Array': {
954 return {kind: 'Object', shapeId: BuiltInArrayId};
955 }
956 case 'MixedReadonly': {
957 return {kind: 'Object', shapeId: BuiltInMixedReadonlyId};
958 }
959 case 'Primitive': {
960 return {kind: 'Primitive'};
961 }
962 case 'Ref': {
963 return {kind: 'Object', shapeId: BuiltInUseRefId};
964 }
965 case 'Any': {
966 return {kind: 'Poly'};
967 }
968 default: {
969 assertExhaustive(
970 typeConfig.name,
971 `Unexpected type '${(typeConfig as any).name}'`,
972 );
973 }
974 }
975 }
976 case 'function': {
977 return addFunction(shapes, [], {
978 positionalParams: typeConfig.positionalParams,
979 restParam: typeConfig.restParam,
980 calleeEffect: typeConfig.calleeEffect,
981 returnType: installTypeConfig(
982 globals,
983 shapes,
984 typeConfig.returnType,
985 moduleName,
986 loc,
987 ),
988 returnValueKind: typeConfig.returnValueKind,
989 noAlias: typeConfig.noAlias === true,
990 mutableOnlyIfOperandsAreMutable:
991 typeConfig.mutableOnlyIfOperandsAreMutable === true,
992 aliasing: typeConfig.aliasing,
993 knownIncompatible: typeConfig.knownIncompatible ?? null,
994 });
995 }
996 case 'hook': {
997 return addHook(shapes, {
998 hookKind: 'Custom',
999 positionalParams: typeConfig.positionalParams ?? [],
1000 restParam: typeConfig.restParam ?? Effect.Freeze,
1001 calleeEffect: Effect.Read,
1002 returnType: installTypeConfig(
1003 globals,
1004 shapes,
1005 typeConfig.returnType,
1006 moduleName,
1007 loc,
1008 ),
1009 returnValueKind: typeConfig.returnValueKind ?? ValueKind.Frozen,
1010 noAlias: typeConfig.noAlias === true,
1011 aliasing: typeConfig.aliasing,
1012 knownIncompatible: typeConfig.knownIncompatible ?? null,
1013 });
1014 }
1015 case 'object': {
1016 return addObject(
1017 shapes,
1018 null,
1019 Object.entries(typeConfig.properties ?? {}).map(([key, value]) => {
1020 const type = installTypeConfig(
1021 globals,
1022 shapes,
1023 value,
1024 moduleName,
1025 loc,
1026 );
1027 const expectHook = isHookName(key);
1028 let isHook = false;
1029 if (type.kind === 'Function' && type.shapeId !== null) {
1030 const functionType = shapes.get(type.shapeId);
1031 if (functionType?.functionType?.hookKind !== null) {
1032 isHook = true;
1033 }
1034 }
1035 if (expectHook !== isHook) {
1036 CompilerError.throwInvalidConfig({
1037 reason: `Invalid type configuration for module`,
1038 description: `Expected type for object property '${key}' from module '${moduleName}' ${expectHook ? 'to be a hook' : 'not to be a hook'} based on the property name`,
1039 loc,
1040 });
1041 }
1042 return [key, type];
1043 }),
1044 );
1045 }
1046 default: {
1047 assertExhaustive(
1048 typeConfig,
1049 `Unexpected type kind '${(typeConfig as any).kind}'`,
1050 );
1051 }
1052 }
1053 }
1054
1055 export function getReanimatedModuleType(registry: ShapeRegistry): ObjectType {
1056 // hooks that freeze args and return frozen value
1057 const frozenHooks = [
1058 'useFrameCallback',
1059 'useAnimatedStyle',
1060 'useAnimatedProps',
1061 'useAnimatedScrollHandler',
1062 'useAnimatedReaction',
1063 'useWorkletCallback',
1064 ];
1065 const reanimatedType: Array<[string, BuiltInType]> = [];
1066 for (const hook of frozenHooks) {
1067 reanimatedType.push([
1068 hook,
1069 addHook(registry, {
1070 positionalParams: [],
1071 restParam: Effect.Freeze,
1072 returnType: {kind: 'Poly'},
1073 returnValueKind: ValueKind.Frozen,
1074 noAlias: true,
1075 calleeEffect: Effect.Read,
1076 hookKind: 'Custom',
1077 }),
1078 ]);
1079 }
1080
1081 /**
1082 * hooks that return a mutable value. ideally these should be modelled as a
1083 * ref, but this works for now.
1084 */
1085 const mutableHooks = ['useSharedValue', 'useDerivedValue'];
1086 for (const hook of mutableHooks) {
1087 reanimatedType.push([
1088 hook,
1089 addHook(registry, {
1090 positionalParams: [],
1091 restParam: Effect.Freeze,
1092 returnType: {kind: 'Object', shapeId: ReanimatedSharedValueId},
1093 returnValueKind: ValueKind.Mutable,
1094 noAlias: true,
1095 calleeEffect: Effect.Read,
1096 hookKind: 'Custom',
1097 }),
1098 ]);
1099 }
1100
1101 // functions that return mutable value
1102 const funcs = [
1103 'withTiming',
1104 'withSpring',
1105 'createAnimatedPropAdapter',
1106 'withDecay',
1107 'withRepeat',
1108 'runOnUI',
1109 'executeOnUIRuntimeSync',
1110 ];
1111 for (const fn of funcs) {
1112 reanimatedType.push([
1113 fn,
1114 addFunction(registry, [], {
1115 positionalParams: [],
1116 restParam: Effect.Read,
1117 returnType: {kind: 'Poly'},
1118 calleeEffect: Effect.Read,
1119 returnValueKind: ValueKind.Mutable,
1120 noAlias: true,
1121 }),
1122 ]);
1123 }
1124
1125 return addObject(registry, null, reanimatedType);
1126 }