import {Effect, ValueKind, ValueReason} from './HIR';
import {
BUILTIN_SHAPES,
BuiltInArrayId,
BuiltInMixedReadonlyId,
BuiltInUseActionStateId,
BuiltInUseContextHookId,
BuiltInUseEffectHookId,
BuiltInUseInsertionEffectHookId,
BuiltInUseLayoutEffectHookId,
BuiltInUseOperatorId,
BuiltInUseReducerId,
BuiltInUseRefId,
BuiltInUseStateId,
BuiltInUseTransitionId,
ShapeRegistry,
addFunction,
addHook,
addObject,
} from './ObjectShape';
import {BuiltInType, PolyType} from './Types';
import {TypeConfig} from './TypeSchema';
import {assertExhaustive} from '../Utils/utils';
import {isHookName} from './Environment';
import {CompilerError, SourceLocation} from '..';
export const DEFAULT_SHAPES: ShapeRegistry = new Map(BUILTIN_SHAPES);
const UNTYPED_GLOBALS: Set<string> = new Set([
'String',
'Object',
'Function',
'Number',
'RegExp',
'Date',
'Error',
'Function',
'TypeError',
'RangeError',
'ReferenceError',
'SyntaxError',
'URIError',
'EvalError',
'Boolean',
'DataView',
'Float32Array',
'Float64Array',
'Int8Array',
'Int16Array',
'Int32Array',
'Map',
'Set',
'WeakMap',
'Uint8Array',
'Uint8ClampedArray',
'Uint16Array',
'Uint32Array',
'ArrayBuffer',
'JSON',
'parseFloat',
'parseInt',
'console',
'isNaN',
'eval',
'isFinite',
'encodeURI',
'decodeURI',
'encodeURIComponent',
'decodeURIComponent',
]);
const TYPED_GLOBALS: Array<[string, BuiltInType]> = [
[
'Array',
addObject(DEFAULT_SHAPES, 'Array', [
[
'isArray',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [Effect.Read],
restParam: null,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'of',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Object', shapeId: BuiltInArrayId},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Mutable,
}),
],
]),
],
[
'Math',
addObject(DEFAULT_SHAPES, 'Math', [
['PI', {kind: 'Primitive'}],
[
'max',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'min',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'trunc',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'ceil',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'floor',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'pow',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
]),
],
['Infinity', {kind: 'Primitive'}],
['NaN', {kind: 'Primitive'}],
[
'console',
addObject(DEFAULT_SHAPES, 'console', [
[
'error',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'info',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'log',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'table',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'trace',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'warn',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
]),
],
[
'Boolean',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'Number',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
[
'String',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
}),
],
];
const REACT_APIS: Array<[string, BuiltInType]> = [
[
'useContext',
addHook(
DEFAULT_SHAPES,
{
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
hookKind: 'useContext',
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.Context,
},
BuiltInUseContextHookId,
),
],
[
'useState',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Object', shapeId: BuiltInUseStateId},
calleeEffect: Effect.Read,
hookKind: 'useState',
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.State,
}),
],
[
'useActionState',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Object', shapeId: BuiltInUseActionStateId},
calleeEffect: Effect.Read,
hookKind: 'useActionState',
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.State,
}),
],
[
'useReducer',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Object', shapeId: BuiltInUseReducerId},
calleeEffect: Effect.Read,
hookKind: 'useReducer',
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.ReducerState,
}),
],
[
'useRef',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Capture,
returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
calleeEffect: Effect.Read,
hookKind: 'useRef',
returnValueKind: ValueKind.Mutable,
}),
],
[
'useImperativeHandle',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
hookKind: 'useImperativeHandle',
returnValueKind: ValueKind.Frozen,
}),
],
[
'useMemo',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
hookKind: 'useMemo',
returnValueKind: ValueKind.Frozen,
}),
],
[
'useCallback',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
hookKind: 'useCallback',
returnValueKind: ValueKind.Frozen,
}),
],
[
'useEffect',
addHook(
DEFAULT_SHAPES,
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Primitive'},
calleeEffect: Effect.Read,
hookKind: 'useEffect',
returnValueKind: ValueKind.Frozen,
},
BuiltInUseEffectHookId,
),
],
[
'useLayoutEffect',
addHook(
DEFAULT_SHAPES,
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
hookKind: 'useLayoutEffect',
returnValueKind: ValueKind.Frozen,
},
BuiltInUseLayoutEffectHookId,
),
],
[
'useInsertionEffect',
addHook(
DEFAULT_SHAPES,
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
hookKind: 'useInsertionEffect',
returnValueKind: ValueKind.Frozen,
},
BuiltInUseInsertionEffectHookId,
),
],
[
'useTransition',
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: null,
returnType: {kind: 'Object', shapeId: BuiltInUseTransitionId},
calleeEffect: Effect.Read,
hookKind: 'useTransition',
returnValueKind: ValueKind.Frozen,
}),
],
[
'use',
addFunction(
DEFAULT_SHAPES,
[],
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Frozen,
},
BuiltInUseOperatorId,
),
],
];
TYPED_GLOBALS.push(
[
'React',
addObject(DEFAULT_SHAPES, null, [
...REACT_APIS,
[
'createElement',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Frozen,
}),
],
[
'cloneElement',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Frozen,
}),
],
[
'createRef',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Capture,
returnType: {kind: 'Object', shapeId: BuiltInUseRefId},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Mutable,
}),
],
]),
],
[
'_jsx',
addFunction(DEFAULT_SHAPES, [], {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Frozen,
}),
],
);
export type Global = BuiltInType | PolyType;
export type GlobalRegistry = Map<string, Global>;
export const DEFAULT_GLOBALS: GlobalRegistry = new Map(REACT_APIS);
for (const name of UNTYPED_GLOBALS) {
DEFAULT_GLOBALS.set(name, {
kind: 'Poly',
});
}
for (const [name, type_] of TYPED_GLOBALS) {
DEFAULT_GLOBALS.set(name, type_);
}
DEFAULT_GLOBALS.set(
'globalThis',
addObject(DEFAULT_SHAPES, 'globalThis', TYPED_GLOBALS),
);
DEFAULT_GLOBALS.set(
'global',
addObject(DEFAULT_SHAPES, 'global', TYPED_GLOBALS),
);
export function installTypeConfig(
globals: GlobalRegistry,
shapes: ShapeRegistry,
typeConfig: TypeConfig,
moduleName: string,
loc: SourceLocation,
): Global {
switch (typeConfig.kind) {
case 'type': {
switch (typeConfig.name) {
case 'Array': {
return {kind: 'Object', shapeId: BuiltInArrayId};
}
case 'MixedReadonly': {
return {kind: 'Object', shapeId: BuiltInMixedReadonlyId};
}
case 'Primitive': {
return {kind: 'Primitive'};
}
case 'Ref': {
return {kind: 'Object', shapeId: BuiltInUseRefId};
}
case 'Any': {
return {kind: 'Poly'};
}
default: {
assertExhaustive(
typeConfig.name,
`Unexpected type '${(typeConfig as any).name}'`,
);
}
}
}
case 'function': {
return addFunction(shapes, [], {
positionalParams: typeConfig.positionalParams,
restParam: typeConfig.restParam,
calleeEffect: typeConfig.calleeEffect,
returnType: installTypeConfig(
globals,
shapes,
typeConfig.returnType,
moduleName,
loc,
),
returnValueKind: typeConfig.returnValueKind,
noAlias: typeConfig.noAlias === true,
mutableOnlyIfOperandsAreMutable:
typeConfig.mutableOnlyIfOperandsAreMutable === true,
});
}
case 'hook': {
return addHook(shapes, {
hookKind: 'Custom',
positionalParams: typeConfig.positionalParams ?? [],
restParam: typeConfig.restParam ?? Effect.Freeze,
calleeEffect: Effect.Read,
returnType: installTypeConfig(
globals,
shapes,
typeConfig.returnType,
moduleName,
loc,
),
returnValueKind: typeConfig.returnValueKind ?? ValueKind.Frozen,
noAlias: typeConfig.noAlias === true,
});
}
case 'object': {
return addObject(
shapes,
null,
Object.entries(typeConfig.properties ?? {}).map(([key, value]) => {
const type = installTypeConfig(
globals,
shapes,
value,
moduleName,
loc,
);
const expectHook = isHookName(key);
let isHook = false;
if (type.kind === 'Function' && type.shapeId !== null) {
const functionType = shapes.get(type.shapeId);
if (functionType?.functionType?.hookKind !== null) {
isHook = true;
}
}
if (expectHook !== isHook) {
CompilerError.throwInvalidConfig({
reason: `Invalid type configuration for module`,
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`,
loc,
});
}
return [key, type];
}),
);
}
default: {
assertExhaustive(
typeConfig,
`Unexpected type kind '${(typeConfig as any).kind}'`,
);
}
}
}
export function installReAnimatedTypes(
globals: GlobalRegistry,
registry: ShapeRegistry,
): void {
const frozenHooks = [
'useFrameCallback',
'useAnimatedStyle',
'useAnimatedProps',
'useAnimatedScrollHandler',
'useAnimatedReaction',
'useWorkletCallback',
];
for (const hook of frozenHooks) {
globals.set(
hook,
addHook(registry, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
returnValueKind: ValueKind.Frozen,
noAlias: true,
calleeEffect: Effect.Read,
hookKind: 'Custom',
}),
);
}
const mutableHooks = ['useSharedValue', 'useDerivedValue'];
for (const hook of mutableHooks) {
globals.set(
hook,
addHook(registry, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: {kind: 'Poly'},
returnValueKind: ValueKind.Mutable,
noAlias: true,
calleeEffect: Effect.Read,
hookKind: 'Custom',
}),
);
}
const funcs = [
'withTiming',
'withSpring',
'createAnimatedPropAdapter',
'withDecay',
'withRepeat',
'runOnUI',
'executeOnUIRuntimeSync',
];
for (const fn of funcs) {
globals.set(
fn,
addFunction(registry, [], {
positionalParams: [],
restParam: Effect.Read,
returnType: {kind: 'Poly'},
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Mutable,
noAlias: true,
}),
);
}
}