13
Place,
14
ReactiveValue,
15
} from '../HIR';
16
+import {Macro, MacroMethod} from '../HIR/Environment';
17
import {eachReactiveValueOperand} from './visitors';
18
19
/**
44
export function memoizeFbtAndMacroOperandsInSameScope(
45
fn: HIRFunction,
46
): Set<IdentifierId> {
46
- const fbtMacroTags = new Set([
47
- ...FBT_TAGS,
47
+ const fbtMacroTags = new Set<Macro>([
48
+ ...Array.from(FBT_TAGS).map((tag): Macro => [tag, []]),
49
...(fn.env.config.customMacros ?? []),
50
]);
51
const fbtValues: Set<IdentifierId> = new Set();
52
+ const macroMethods = new Map<IdentifierId, Array<Array<MacroMethod>>>();
53
while (true) {
52
- let size = fbtValues.size;
53
- visit(fn, fbtMacroTags, fbtValues);
54
- if (size === fbtValues.size) {
54
+ let vsize = fbtValues.size;
55
+ let msize = macroMethods.size;
56
+ visit(fn, fbtMacroTags, fbtValues, macroMethods);
57
+ if (vsize === fbtValues.size && msize === macroMethods.size) {
58
break;
59
}
60
}
74
75
function visit(
76
fn: HIRFunction,
74
- fbtMacroTags: Set<string>,
77
+ fbtMacroTags: Set<Macro>,
78
fbtValues: Set<IdentifierId>,
79
+ macroMethods: Map<IdentifierId, Array<Array<MacroMethod>>>,
80
): void {
81
for (const [, block] of fn.body.blocks) {
82
for (const instruction of block.instructions) {
87
if (
88
value.kind === 'Primitive' &&
89
typeof value.value === 'string' &&
86
- fbtMacroTags.has(value.value)
90
+ matchesExactTag(value.value, fbtMacroTags)
91
) {
92
/*
93
* We don't distinguish between tag names and strings, so record
96
fbtValues.add(lvalue.identifier.id);
97
} else if (
98
value.kind === 'LoadGlobal' &&
95
- fbtMacroTags.has(value.binding.name)
99
+ matchesExactTag(value.binding.name, fbtMacroTags)
100
) {
101
// Record references to `fbt` as a global
102
fbtValues.add(lvalue.identifier.id);
103
+ } else if (
104
+ value.kind === 'LoadGlobal' &&
105
+ matchTagRoot(value.binding.name, fbtMacroTags) !== null
106
+ ) {
107
+ const methods = matchTagRoot(value.binding.name, fbtMacroTags)!;
108
+ macroMethods.set(lvalue.identifier.id, methods);
109
+ } else if (
110
+ value.kind === 'PropertyLoad' &&
111
+ macroMethods.has(value.object.identifier.id)
112
+ ) {
113
+ const methods = macroMethods.get(value.object.identifier.id)!;
114
+ const newMethods = [];
115
+ for (const method of methods) {
116
+ if (
117
+ method.length > 0 &&
118
+ (method[0].type === 'wildcard' ||
119
+ (method[0].type === 'name' && method[0].name === value.property))
120
+ ) {
121
+ if (method.length > 1) {
122
+ newMethods.push(method.slice(1));
123
+ } else {
124
+ fbtValues.add(lvalue.identifier.id);
125
+ }
126
+ }
127
+ }
128
+ if (newMethods.length > 0) {
129
+ macroMethods.set(lvalue.identifier.id, newMethods);
130
+ }
131
} else if (isFbtCallExpression(fbtValues, value)) {
132
const fbtScope = lvalue.identifier.scope;
133
if (fbtScope === null) {
199
}
200
}
201
202
+function matchesExactTag(s: string, tags: Set<Macro>): boolean {
203
+ return Array.from(tags).some(macro =>
204
+ typeof macro === 'string'
205
+ ? s === macro
206
+ : macro[1].length === 0 && macro[0] === s,
207
+ );
208
+}
209
+
210
+function matchTagRoot(
211
+ s: string,
212
+ tags: Set<Macro>,
213
+): Array<Array<MacroMethod>> | null {
214
+ const methods: Array<Array<MacroMethod>> = [];
215
+ for (const macro of tags) {
216
+ if (typeof macro === 'string') {
217
+ continue;
218
+ }
219
+ const [tag, rest] = macro;
220
+ if (tag === s && rest.length > 0) {
221
+ methods.push(rest);
222
+ }
223
+ }
224
+ if (methods.length > 0) {
225
+ return methods;
226
+ } else {
227
+ return null;
228
+ }
229
+}
230
+
231
function isFbtCallExpression(
232
fbtValues: Set<IdentifierId>,
233
value: ReactiveValue,
234
): boolean {
235
return (
175
- value.kind === 'CallExpression' && fbtValues.has(value.callee.identifier.id)
236
+ (value.kind === 'CallExpression' &&
237
+ fbtValues.has(value.callee.identifier.id)) ||
238
+ (value.kind === 'MethodCall' && fbtValues.has(value.property.identifier.id))
239
);
240
}
241
242
function isFbtJsxExpression(
180
- fbtMacroTags: Set<string>,
243
+ fbtMacroTags: Set<Macro>,
244
fbtValues: Set<IdentifierId>,
245
value: ReactiveValue,
246
): boolean {
248
value.kind === 'JsxExpression' &&
249
((value.tag.kind === 'Identifier' &&
250
fbtValues.has(value.tag.identifier.id)) ||
188
- (value.tag.kind === 'BuiltinTag' && fbtMacroTags.has(value.tag.name)))
251
+ (value.tag.kind === 'BuiltinTag' &&
252
+ matchesExactTag(value.tag.name, fbtMacroTags)))
253
);
254
}
255