@tamagui/compiler-core 0.0.0-bootstrap.0 → 3.0.0-beta.1093.1
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.
- package/LICENSE +21 -0
- package/README.md +57 -1
- package/dist/cjs/ast.cjs +108 -0
- package/dist/cjs/contracts.cjs +61 -0
- package/dist/cjs/diagnostics.cjs +68 -0
- package/dist/cjs/evaluate.cjs +602 -0
- package/dist/cjs/graph.cjs +335 -0
- package/dist/cjs/hash.cjs +48 -0
- package/dist/cjs/index.cjs +43 -0
- package/dist/cjs/ir.cjs +32 -0
- package/dist/cjs/lower.cjs +232 -0
- package/dist/cjs/materialize.cjs +222 -0
- package/dist/cjs/normalize.cjs +628 -0
- package/dist/cjs/output.cjs +148 -0
- package/dist/cjs/planCache.cjs +262 -0
- package/dist/cjs/session.cjs +127 -0
- package/dist/cjs/yuku.cjs +166 -0
- package/dist/cjs/zero.cjs +389 -0
- package/dist/esm/ast.mjs +79 -0
- package/dist/esm/ast.mjs.map +1 -0
- package/dist/esm/contracts.mjs +34 -0
- package/dist/esm/contracts.mjs.map +1 -0
- package/dist/esm/diagnostics.mjs +44 -0
- package/dist/esm/diagnostics.mjs.map +1 -0
- package/dist/esm/evaluate.mjs +576 -0
- package/dist/esm/evaluate.mjs.map +1 -0
- package/dist/esm/graph.mjs +313 -0
- package/dist/esm/graph.mjs.map +1 -0
- package/dist/esm/hash.mjs +26 -0
- package/dist/esm/hash.mjs.map +1 -0
- package/dist/esm/index.mjs +30 -0
- package/dist/esm/ir.mjs +12 -0
- package/dist/esm/ir.mjs.map +1 -0
- package/dist/esm/lower.mjs +210 -0
- package/dist/esm/lower.mjs.map +1 -0
- package/dist/esm/materialize.mjs +203 -0
- package/dist/esm/materialize.mjs.map +1 -0
- package/dist/esm/normalize.mjs +604 -0
- package/dist/esm/normalize.mjs.map +1 -0
- package/dist/esm/output.mjs +119 -0
- package/dist/esm/output.mjs.map +1 -0
- package/dist/esm/planCache.mjs +232 -0
- package/dist/esm/planCache.mjs.map +1 -0
- package/dist/esm/session.mjs +108 -0
- package/dist/esm/session.mjs.map +1 -0
- package/dist/esm/yuku.mjs +144 -0
- package/dist/esm/yuku.mjs.map +1 -0
- package/dist/esm/zero.mjs +354 -0
- package/dist/esm/zero.mjs.map +1 -0
- package/package.json +42 -4
- package/src/ast.ts +106 -0
- package/src/contracts.ts +150 -0
- package/src/diagnostics.ts +96 -0
- package/src/evaluate.ts +875 -0
- package/src/graph.ts +425 -0
- package/src/hash.ts +36 -0
- package/src/index.ts +15 -0
- package/src/ir.ts +156 -0
- package/src/lower.ts +435 -0
- package/src/materialize.ts +383 -0
- package/src/normalize.ts +834 -0
- package/src/output.ts +175 -0
- package/src/planCache.ts +362 -0
- package/src/session.ts +192 -0
- package/src/yuku.ts +197 -0
- package/src/zero.ts +609 -0
- package/types/ast.d.ts +11 -0
- package/types/ast.d.ts.map +1 -0
- package/types/contracts.d.ts +99 -0
- package/types/contracts.d.ts.map +1 -0
- package/types/diagnostics.d.ts +31 -0
- package/types/diagnostics.d.ts.map +1 -0
- package/types/evaluate.d.ts +48 -0
- package/types/evaluate.d.ts.map +1 -0
- package/types/graph.d.ts +51 -0
- package/types/graph.d.ts.map +1 -0
- package/types/hash.d.ts +8 -0
- package/types/hash.d.ts.map +1 -0
- package/types/index.d.ts +16 -0
- package/types/index.d.ts.map +1 -0
- package/types/ir.d.ts +120 -0
- package/types/ir.d.ts.map +1 -0
- package/types/lower.d.ts +99 -0
- package/types/lower.d.ts.map +1 -0
- package/types/materialize.d.ts +121 -0
- package/types/materialize.d.ts.map +1 -0
- package/types/normalize.d.ts +7 -0
- package/types/normalize.d.ts.map +1 -0
- package/types/output.d.ts +32 -0
- package/types/output.d.ts.map +1 -0
- package/types/planCache.d.ts +123 -0
- package/types/planCache.d.ts.map +1 -0
- package/types/session.d.ts +52 -0
- package/types/session.d.ts.map +1 -0
- package/types/yuku.d.ts +4 -0
- package/types/yuku.d.ts.map +1 -0
- package/types/zero.d.ts +99 -0
- package/types/zero.d.ts.map +1 -0
package/src/evaluate.ts
ADDED
|
@@ -0,0 +1,875 @@
|
|
|
1
|
+
import { childNode, childNodes, identifierName, isAstNode, unwrapExpression } from './ast'
|
|
2
|
+
import type {
|
|
3
|
+
AstNode,
|
|
4
|
+
ExpressionReference,
|
|
5
|
+
ResolvedModuleId,
|
|
6
|
+
SourceSpan,
|
|
7
|
+
SymbolDefinition,
|
|
8
|
+
SymbolResolver,
|
|
9
|
+
} from './contracts'
|
|
10
|
+
import { expressionReference, spanOf } from './contracts'
|
|
11
|
+
import { linkedBailout, localBailout, type BailoutReason } from './diagnostics'
|
|
12
|
+
|
|
13
|
+
export type StaticEvaluationValue =
|
|
14
|
+
| string
|
|
15
|
+
| number
|
|
16
|
+
| boolean
|
|
17
|
+
| null
|
|
18
|
+
| StaticEvaluationValue[]
|
|
19
|
+
| { [key: string]: StaticEvaluationValue }
|
|
20
|
+
|
|
21
|
+
export type EvaluationResult =
|
|
22
|
+
| {
|
|
23
|
+
ok: true
|
|
24
|
+
value: StaticEvaluationValue
|
|
25
|
+
dependencies: ResolvedModuleId[]
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
ok: false
|
|
29
|
+
bailout: BailoutReason
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface DynamicEvaluation {
|
|
33
|
+
type: 'number' | 'string' | 'boolean' | 'null'
|
|
34
|
+
values?: (number | string | boolean | null)[]
|
|
35
|
+
dependencies: ResolvedModuleId[]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type InternalResult =
|
|
39
|
+
| { ok: true; value: StaticEvaluationValue }
|
|
40
|
+
| { ok: false; bailout: BailoutReason }
|
|
41
|
+
|
|
42
|
+
interface EvaluationState {
|
|
43
|
+
activeDefinitions: Set<string>
|
|
44
|
+
dependencies: Set<ResolvedModuleId>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function success(value: StaticEvaluationValue): InternalResult {
|
|
48
|
+
return { ok: true, value }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function literal(node: AstNode): StaticEvaluationValue | undefined {
|
|
52
|
+
if (node.type === 'NullLiteral') return null
|
|
53
|
+
if (node.type === 'Literal' || node.type.endsWith('Literal')) {
|
|
54
|
+
const value = node.value
|
|
55
|
+
if (
|
|
56
|
+
typeof value === 'string' ||
|
|
57
|
+
typeof value === 'number' ||
|
|
58
|
+
typeof value === 'boolean' ||
|
|
59
|
+
value === null
|
|
60
|
+
) {
|
|
61
|
+
return value
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return undefined
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function unsupported(
|
|
68
|
+
id: ResolvedModuleId,
|
|
69
|
+
node: AstNode,
|
|
70
|
+
detail = `Expression ${node.type} is not statically supported`
|
|
71
|
+
): InternalResult {
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
bailout: localBailout('local/unsupported-expression', spanOf(id, node), detail),
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function evaluateDefinition(
|
|
79
|
+
resolver: SymbolResolver,
|
|
80
|
+
definition: SymbolDefinition,
|
|
81
|
+
useSpan: SourceSpan,
|
|
82
|
+
state: EvaluationState
|
|
83
|
+
): InternalResult {
|
|
84
|
+
const key = `${definition.id}:${definition.span.start}`
|
|
85
|
+
if (state.activeDefinitions.has(key)) {
|
|
86
|
+
return {
|
|
87
|
+
ok: false,
|
|
88
|
+
bailout: localBailout(
|
|
89
|
+
'local/static-evaluation-cycle',
|
|
90
|
+
useSpan,
|
|
91
|
+
`Static evaluation cycle reached ${definition.name}`
|
|
92
|
+
),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!definition.initializer) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
bailout: linkedBailout(
|
|
99
|
+
'linked/missing-initializer',
|
|
100
|
+
useSpan,
|
|
101
|
+
`Binding ${definition.name} has no static initializer`,
|
|
102
|
+
definition.id
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!definition.constant) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
bailout: linkedBailout(
|
|
110
|
+
'linked/unresolved-binding',
|
|
111
|
+
useSpan,
|
|
112
|
+
`Binding ${definition.name} is mutable and cannot be statically evaluated`,
|
|
113
|
+
definition.id
|
|
114
|
+
),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const initializer = resolver.expressionNode(definition.initializer)
|
|
118
|
+
if (!initializer) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
bailout: linkedBailout(
|
|
122
|
+
'linked/missing-initializer',
|
|
123
|
+
useSpan,
|
|
124
|
+
`Binding ${definition.name} initializer is outside the linked graph`,
|
|
125
|
+
definition.id
|
|
126
|
+
),
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
state.dependencies.add(definition.id)
|
|
131
|
+
state.activeDefinitions.add(key)
|
|
132
|
+
try {
|
|
133
|
+
return evaluateNode(resolver, definition.id, initializer, state)
|
|
134
|
+
} finally {
|
|
135
|
+
state.activeDefinitions.delete(key)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function evaluateObject(
|
|
140
|
+
resolver: SymbolResolver,
|
|
141
|
+
id: ResolvedModuleId,
|
|
142
|
+
node: AstNode,
|
|
143
|
+
state: EvaluationState
|
|
144
|
+
): InternalResult {
|
|
145
|
+
const output: Record<string, StaticEvaluationValue> = {}
|
|
146
|
+
for (const property of childNodes(node, 'properties')) {
|
|
147
|
+
if (property.type === 'SpreadElement') {
|
|
148
|
+
const argument = childNode(property, 'argument')
|
|
149
|
+
if (!argument) return unsupported(id, property)
|
|
150
|
+
const spread = evaluateNode(resolver, id, argument, state)
|
|
151
|
+
if (!spread.ok) return spread
|
|
152
|
+
if (
|
|
153
|
+
!spread.value ||
|
|
154
|
+
typeof spread.value !== 'object' ||
|
|
155
|
+
Array.isArray(spread.value)
|
|
156
|
+
) {
|
|
157
|
+
return {
|
|
158
|
+
ok: false,
|
|
159
|
+
bailout: localBailout(
|
|
160
|
+
'local/non-object-spread',
|
|
161
|
+
spanOf(id, property),
|
|
162
|
+
'Static object spread did not evaluate to an object'
|
|
163
|
+
),
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
Object.assign(output, spread.value)
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
if (property.type !== 'Property' && property.type !== 'ObjectProperty') {
|
|
170
|
+
return unsupported(id, property)
|
|
171
|
+
}
|
|
172
|
+
const keyNode = childNode(property, 'key')
|
|
173
|
+
const valueNode = childNode(property, 'value')
|
|
174
|
+
if (!keyNode || !valueNode) return unsupported(id, property)
|
|
175
|
+
let key: StaticEvaluationValue | undefined = identifierName(keyNode) ?? undefined
|
|
176
|
+
if (property.computed === true || key === undefined) {
|
|
177
|
+
const evaluatedKey = evaluateNode(resolver, id, keyNode, state)
|
|
178
|
+
if (!evaluatedKey.ok) return evaluatedKey
|
|
179
|
+
key = evaluatedKey.value
|
|
180
|
+
}
|
|
181
|
+
if (typeof key !== 'string' && typeof key !== 'number') {
|
|
182
|
+
return unsupported(id, keyNode, 'Static object key is not a string or number')
|
|
183
|
+
}
|
|
184
|
+
const value = evaluateNode(resolver, id, valueNode, state)
|
|
185
|
+
if (!value.ok) return value
|
|
186
|
+
output[String(key)] = value.value
|
|
187
|
+
}
|
|
188
|
+
return success(output)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function evaluateArray(
|
|
192
|
+
resolver: SymbolResolver,
|
|
193
|
+
id: ResolvedModuleId,
|
|
194
|
+
node: AstNode,
|
|
195
|
+
state: EvaluationState
|
|
196
|
+
): InternalResult {
|
|
197
|
+
const output: StaticEvaluationValue[] = []
|
|
198
|
+
const rawElements = node.elements
|
|
199
|
+
if (
|
|
200
|
+
!Array.isArray(rawElements) ||
|
|
201
|
+
rawElements.some((element) => element === null || !isAstNode(element))
|
|
202
|
+
) {
|
|
203
|
+
return unsupported(id, node, 'Sparse arrays are not statically supported')
|
|
204
|
+
}
|
|
205
|
+
for (const element of rawElements.filter(isAstNode)) {
|
|
206
|
+
if (element.type === 'SpreadElement') {
|
|
207
|
+
const argument = childNode(element, 'argument')
|
|
208
|
+
if (!argument) return unsupported(id, element)
|
|
209
|
+
const spread = evaluateNode(resolver, id, argument, state)
|
|
210
|
+
if (!spread.ok) return spread
|
|
211
|
+
if (!Array.isArray(spread.value)) {
|
|
212
|
+
return unsupported(
|
|
213
|
+
id,
|
|
214
|
+
element,
|
|
215
|
+
'Static array spread did not evaluate to an array'
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
output.push(...spread.value)
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
221
|
+
const value = evaluateNode(resolver, id, element, state)
|
|
222
|
+
if (!value.ok) return value
|
|
223
|
+
output.push(value.value)
|
|
224
|
+
}
|
|
225
|
+
return success(output)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function evaluateNode(
|
|
229
|
+
resolver: SymbolResolver,
|
|
230
|
+
id: ResolvedModuleId,
|
|
231
|
+
input: AstNode,
|
|
232
|
+
state: EvaluationState
|
|
233
|
+
): InternalResult {
|
|
234
|
+
const node = unwrapExpression(input)
|
|
235
|
+
const literalValue = literal(node)
|
|
236
|
+
if (literalValue !== undefined) return success(literalValue)
|
|
237
|
+
|
|
238
|
+
switch (node.type) {
|
|
239
|
+
case 'Identifier': {
|
|
240
|
+
const name = identifierName(node)
|
|
241
|
+
if (!name) return unsupported(id, node)
|
|
242
|
+
// by position, so a function-scope constant resolves and a parameter
|
|
243
|
+
// shadowing a module constant does not read the module's value
|
|
244
|
+
const definition = resolver.resolveReference(expressionReference(id, node))
|
|
245
|
+
if (!definition) {
|
|
246
|
+
return {
|
|
247
|
+
ok: false,
|
|
248
|
+
bailout: linkedBailout(
|
|
249
|
+
'linked/unresolved-binding',
|
|
250
|
+
spanOf(id, node),
|
|
251
|
+
`Binding ${name} has no host-linked definition`
|
|
252
|
+
),
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return evaluateDefinition(resolver, definition, spanOf(id, node), state)
|
|
256
|
+
}
|
|
257
|
+
case 'ObjectExpression':
|
|
258
|
+
return evaluateObject(resolver, id, node, state)
|
|
259
|
+
case 'ArrayExpression':
|
|
260
|
+
return evaluateArray(resolver, id, node, state)
|
|
261
|
+
case 'MemberExpression': {
|
|
262
|
+
const objectNode = childNode(node, 'object')
|
|
263
|
+
const propertyNode = childNode(node, 'property')
|
|
264
|
+
if (!objectNode || !propertyNode) return unsupported(id, node)
|
|
265
|
+
const object = evaluateNode(resolver, id, objectNode, state)
|
|
266
|
+
if (!object.ok) return object
|
|
267
|
+
let property: StaticEvaluationValue | null = identifierName(propertyNode)
|
|
268
|
+
if (node.computed === true || property === null) {
|
|
269
|
+
const evaluatedProperty = evaluateNode(resolver, id, propertyNode, state)
|
|
270
|
+
if (!evaluatedProperty.ok) return evaluatedProperty
|
|
271
|
+
property = evaluatedProperty.value
|
|
272
|
+
}
|
|
273
|
+
if (
|
|
274
|
+
(typeof object.value !== 'object' || object.value === null) &&
|
|
275
|
+
typeof object.value !== 'string'
|
|
276
|
+
) {
|
|
277
|
+
return unsupported(id, node, 'Static member target is not indexable')
|
|
278
|
+
}
|
|
279
|
+
if (typeof property !== 'string' && typeof property !== 'number') {
|
|
280
|
+
return unsupported(
|
|
281
|
+
id,
|
|
282
|
+
propertyNode,
|
|
283
|
+
'Static member key is not a string or number'
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
const value = (object.value as Record<string, StaticEvaluationValue>)[
|
|
287
|
+
String(property)
|
|
288
|
+
]
|
|
289
|
+
return value === undefined
|
|
290
|
+
? unsupported(id, node, `Static member ${String(property)} does not exist`)
|
|
291
|
+
: success(value)
|
|
292
|
+
}
|
|
293
|
+
case 'UnaryExpression': {
|
|
294
|
+
const argument = childNode(node, 'argument')
|
|
295
|
+
if (!argument) return unsupported(id, node)
|
|
296
|
+
const value = evaluateNode(resolver, id, argument, state)
|
|
297
|
+
if (!value.ok) return value
|
|
298
|
+
if (node.operator === '-' && typeof value.value === 'number') {
|
|
299
|
+
return success(-value.value)
|
|
300
|
+
}
|
|
301
|
+
if (node.operator === '+' && typeof value.value === 'number') {
|
|
302
|
+
return success(value.value)
|
|
303
|
+
}
|
|
304
|
+
if (node.operator === '!') return success(!value.value)
|
|
305
|
+
return unsupported(
|
|
306
|
+
id,
|
|
307
|
+
node,
|
|
308
|
+
`Unary operator ${String(node.operator)} is unsupported`
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
case 'BinaryExpression':
|
|
312
|
+
case 'LogicalExpression': {
|
|
313
|
+
const leftNode = childNode(node, 'left')
|
|
314
|
+
const rightNode = childNode(node, 'right')
|
|
315
|
+
if (!leftNode || !rightNode) return unsupported(id, node)
|
|
316
|
+
const left = evaluateNode(resolver, id, leftNode, state)
|
|
317
|
+
if (!left.ok) return left
|
|
318
|
+
if (node.type === 'LogicalExpression') {
|
|
319
|
+
if (node.operator === '&&' && !left.value) return left
|
|
320
|
+
if (node.operator === '||' && left.value) return left
|
|
321
|
+
if (node.operator === '??' && left.value !== null) return left
|
|
322
|
+
}
|
|
323
|
+
const right = evaluateNode(resolver, id, rightNode, state)
|
|
324
|
+
if (!right.ok) return right
|
|
325
|
+
switch (node.operator) {
|
|
326
|
+
case '+':
|
|
327
|
+
if (typeof left.value === 'number' && typeof right.value === 'number') {
|
|
328
|
+
return success(left.value + right.value)
|
|
329
|
+
}
|
|
330
|
+
if (typeof left.value === 'string' || typeof right.value === 'string') {
|
|
331
|
+
return success(String(left.value) + String(right.value))
|
|
332
|
+
}
|
|
333
|
+
break
|
|
334
|
+
case '-':
|
|
335
|
+
if (typeof left.value === 'number' && typeof right.value === 'number') {
|
|
336
|
+
return success(left.value - right.value)
|
|
337
|
+
}
|
|
338
|
+
break
|
|
339
|
+
case '*':
|
|
340
|
+
if (typeof left.value === 'number' && typeof right.value === 'number') {
|
|
341
|
+
return success(left.value * right.value)
|
|
342
|
+
}
|
|
343
|
+
break
|
|
344
|
+
case '/':
|
|
345
|
+
if (typeof left.value === 'number' && typeof right.value === 'number') {
|
|
346
|
+
return success(left.value / right.value)
|
|
347
|
+
}
|
|
348
|
+
break
|
|
349
|
+
case '&&':
|
|
350
|
+
case '||':
|
|
351
|
+
case '??':
|
|
352
|
+
return right
|
|
353
|
+
}
|
|
354
|
+
return unsupported(
|
|
355
|
+
id,
|
|
356
|
+
node,
|
|
357
|
+
`Binary operator ${String(node.operator)} is unsupported`
|
|
358
|
+
)
|
|
359
|
+
}
|
|
360
|
+
case 'ConditionalExpression': {
|
|
361
|
+
const testNode = childNode(node, 'test')
|
|
362
|
+
const consequent = childNode(node, 'consequent')
|
|
363
|
+
const alternate = childNode(node, 'alternate')
|
|
364
|
+
if (!testNode || !consequent || !alternate) return unsupported(id, node)
|
|
365
|
+
const test = evaluateNode(resolver, id, testNode, state)
|
|
366
|
+
if (!test.ok) return test
|
|
367
|
+
return evaluateNode(resolver, id, test.value ? consequent : alternate, state)
|
|
368
|
+
}
|
|
369
|
+
case 'TemplateLiteral': {
|
|
370
|
+
const quasis = childNodes(node, 'quasis')
|
|
371
|
+
const expressions = childNodes(node, 'expressions')
|
|
372
|
+
let output = ''
|
|
373
|
+
for (let index = 0; index < quasis.length; index++) {
|
|
374
|
+
output += quasiText(quasis[index])
|
|
375
|
+
const expression = expressions[index]
|
|
376
|
+
if (expression) {
|
|
377
|
+
const value = evaluateNode(resolver, id, expression, state)
|
|
378
|
+
if (!value.ok) return value
|
|
379
|
+
output += String(value.value)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return success(output)
|
|
383
|
+
}
|
|
384
|
+
default:
|
|
385
|
+
return unsupported(id, node)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function quasiText(quasi: AstNode | undefined): string {
|
|
390
|
+
const cooked = (quasi?.value as { cooked?: unknown } | undefined)?.cooked
|
|
391
|
+
const raw = (quasi?.value as { raw?: unknown } | undefined)?.raw
|
|
392
|
+
return typeof cooked === 'string' ? cooked : typeof raw === 'string' ? raw : ''
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function dynamicPrimitive(
|
|
396
|
+
value: StaticEvaluationValue
|
|
397
|
+
): DynamicEvaluation['type'] | null {
|
|
398
|
+
if (value === null) return 'null'
|
|
399
|
+
if (typeof value === 'number') return 'number'
|
|
400
|
+
if (typeof value === 'string') return 'string'
|
|
401
|
+
if (typeof value === 'boolean') return 'boolean'
|
|
402
|
+
return null
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function evaluateDynamicNode(
|
|
406
|
+
resolver: SymbolResolver,
|
|
407
|
+
id: ResolvedModuleId,
|
|
408
|
+
input: AstNode,
|
|
409
|
+
state: EvaluationState
|
|
410
|
+
): Omit<DynamicEvaluation, 'dependencies'> | null {
|
|
411
|
+
const exact = evaluateNode(resolver, id, input, state)
|
|
412
|
+
if (exact.ok) {
|
|
413
|
+
const type = dynamicPrimitive(exact.value)
|
|
414
|
+
return type
|
|
415
|
+
? { type, values: [exact.value as number | string | boolean | null] }
|
|
416
|
+
: null
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const node = unwrapExpression(input)
|
|
420
|
+
if (node.type === 'Identifier') {
|
|
421
|
+
if (!identifierName(node)) return null
|
|
422
|
+
const definition = resolver.resolveReference(expressionReference(id, node))
|
|
423
|
+
if (!definition?.constant || !definition.initializer) return null
|
|
424
|
+
const key = `${definition.id}:${definition.span.start}`
|
|
425
|
+
if (state.activeDefinitions.has(key)) return null
|
|
426
|
+
const initializer = resolver.expressionNode(definition.initializer)
|
|
427
|
+
if (!initializer) return null
|
|
428
|
+
state.dependencies.add(definition.id)
|
|
429
|
+
state.activeDefinitions.add(key)
|
|
430
|
+
try {
|
|
431
|
+
return evaluateDynamicNode(resolver, definition.id, initializer, state)
|
|
432
|
+
} finally {
|
|
433
|
+
state.activeDefinitions.delete(key)
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (node.type === 'MemberExpression') {
|
|
438
|
+
const objectNode = childNode(node, 'object')
|
|
439
|
+
const propertyNode = childNode(node, 'property')
|
|
440
|
+
if (!objectNode || !propertyNode) return null
|
|
441
|
+
const object = evaluateNode(resolver, id, objectNode, state)
|
|
442
|
+
if (!object.ok || !Array.isArray(object.value)) return null
|
|
443
|
+
const property = evaluateDynamicNode(resolver, id, propertyNode, state)
|
|
444
|
+
if (property?.type !== 'number' || object.value.length > 32) return null
|
|
445
|
+
const values: (number | string | boolean | null)[] = []
|
|
446
|
+
let type: DynamicEvaluation['type'] | null = null
|
|
447
|
+
for (const value of object.value) {
|
|
448
|
+
const nextType = dynamicPrimitive(value)
|
|
449
|
+
if (!nextType || (type && type !== nextType)) return null
|
|
450
|
+
type = nextType
|
|
451
|
+
const primitive = value as number | string | boolean | null
|
|
452
|
+
if (!values.includes(primitive)) values.push(primitive)
|
|
453
|
+
}
|
|
454
|
+
return type ? { type, values } : null
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (node.type === 'BinaryExpression') {
|
|
458
|
+
const leftNode = childNode(node, 'left')
|
|
459
|
+
const rightNode = childNode(node, 'right')
|
|
460
|
+
if (!leftNode || !rightNode) return null
|
|
461
|
+
const left = evaluateDynamicNode(resolver, id, leftNode, state)
|
|
462
|
+
const right = evaluateDynamicNode(resolver, id, rightNode, state)
|
|
463
|
+
if (node.operator === '+') {
|
|
464
|
+
return left?.type === 'number' && right?.type === 'number'
|
|
465
|
+
? { type: 'number' }
|
|
466
|
+
: null
|
|
467
|
+
}
|
|
468
|
+
if (
|
|
469
|
+
node.operator === '-' ||
|
|
470
|
+
node.operator === '*' ||
|
|
471
|
+
node.operator === '/' ||
|
|
472
|
+
node.operator === '%' ||
|
|
473
|
+
node.operator === '**'
|
|
474
|
+
) {
|
|
475
|
+
// successful javascript arithmetic with a number operand produces a
|
|
476
|
+
// number. bigint mixed with the numeric literals used here throws before
|
|
477
|
+
// the style value exists, so it does not create another runtime domain.
|
|
478
|
+
if (left?.type === 'number' || right?.type === 'number') {
|
|
479
|
+
return { type: 'number' }
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// a template whose every interpolation takes finitely many primitives takes
|
|
485
|
+
// finitely many strings: `w-6 ${colors[index % 2]}` is two class strings
|
|
486
|
+
if (node.type === 'TemplateLiteral') {
|
|
487
|
+
const quasis = childNodes(node, 'quasis')
|
|
488
|
+
const expressions = childNodes(node, 'expressions')
|
|
489
|
+
let values: string[] = ['']
|
|
490
|
+
for (let position = 0; position < quasis.length; position++) {
|
|
491
|
+
const text = quasiText(quasis[position])
|
|
492
|
+
values = values.map((value) => value + text)
|
|
493
|
+
const expression = expressions[position]
|
|
494
|
+
if (!expression) continue
|
|
495
|
+
const part = evaluateDynamicNode(resolver, id, expression, state)
|
|
496
|
+
if (!part?.values) return null
|
|
497
|
+
const next: string[] = []
|
|
498
|
+
for (const value of values) {
|
|
499
|
+
for (const primitive of part.values) {
|
|
500
|
+
const joined = value + String(primitive)
|
|
501
|
+
if (!next.includes(joined)) next.push(joined)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (next.length > 32) return null
|
|
505
|
+
values = next
|
|
506
|
+
}
|
|
507
|
+
return { type: 'string', values }
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return null
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export type BranchDecisionNode =
|
|
514
|
+
| {
|
|
515
|
+
kind: 'leaf'
|
|
516
|
+
value?: StaticEvaluationValue
|
|
517
|
+
dependencies: ResolvedModuleId[]
|
|
518
|
+
}
|
|
519
|
+
| {
|
|
520
|
+
kind: 'branch'
|
|
521
|
+
test: SourceSpan
|
|
522
|
+
whenTrue: BranchDecisionNode
|
|
523
|
+
whenFalse: BranchDecisionNode
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function collectLeaves(
|
|
527
|
+
node: BranchDecisionNode
|
|
528
|
+
): { value?: StaticEvaluationValue; dependencies: ResolvedModuleId[] }[] {
|
|
529
|
+
if (node.kind === 'leaf') return [node]
|
|
530
|
+
return [...collectLeaves(node.whenTrue), ...collectLeaves(node.whenFalse)]
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
export function collectBranchDependencies(node: BranchDecisionNode): ResolvedModuleId[] {
|
|
534
|
+
if (node.kind === 'leaf') return node.dependencies
|
|
535
|
+
return [
|
|
536
|
+
...new Set([
|
|
537
|
+
...collectBranchDependencies(node.whenTrue),
|
|
538
|
+
...collectBranchDependencies(node.whenFalse),
|
|
539
|
+
]),
|
|
540
|
+
].sort()
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function evaluateBranchChild(
|
|
544
|
+
resolver: SymbolResolver,
|
|
545
|
+
id: ResolvedModuleId,
|
|
546
|
+
node: AstNode,
|
|
547
|
+
depth: number,
|
|
548
|
+
state: EvaluationState,
|
|
549
|
+
sourceId: ResolvedModuleId
|
|
550
|
+
): BranchDecisionNode | null {
|
|
551
|
+
const leafState: EvaluationState = {
|
|
552
|
+
activeDefinitions: new Set(state.activeDefinitions),
|
|
553
|
+
dependencies: new Set(state.dependencies),
|
|
554
|
+
}
|
|
555
|
+
const leafRes = evaluateNode(resolver, id, node, leafState)
|
|
556
|
+
if (leafRes.ok) {
|
|
557
|
+
return {
|
|
558
|
+
kind: 'leaf',
|
|
559
|
+
value: leafRes.value,
|
|
560
|
+
dependencies: [...leafState.dependencies].sort(),
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return evaluateBranchesNode(resolver, id, node, depth + 1, leafState, sourceId)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function evaluateBranchesNode(
|
|
567
|
+
resolver: SymbolResolver,
|
|
568
|
+
id: ResolvedModuleId,
|
|
569
|
+
input: AstNode,
|
|
570
|
+
depth: number,
|
|
571
|
+
state: EvaluationState,
|
|
572
|
+
sourceId: ResolvedModuleId
|
|
573
|
+
): BranchDecisionNode | null {
|
|
574
|
+
const node = unwrapExpression(input)
|
|
575
|
+
if (!isAstNode(node)) return null
|
|
576
|
+
|
|
577
|
+
if (node.type === 'Identifier') {
|
|
578
|
+
if (!identifierName(node)) return null
|
|
579
|
+
const definition = resolver.resolveReference(expressionReference(id, node))
|
|
580
|
+
if (!definition || !definition.constant || !definition.initializer) return null
|
|
581
|
+
const initializer = resolver.expressionNode(definition.initializer)
|
|
582
|
+
if (!initializer) return null
|
|
583
|
+
const key = `${definition.id}:${definition.span.start}`
|
|
584
|
+
if (state.activeDefinitions.has(key)) return null
|
|
585
|
+
state.activeDefinitions.add(key)
|
|
586
|
+
state.dependencies.add(definition.id)
|
|
587
|
+
try {
|
|
588
|
+
return evaluateBranchesNode(
|
|
589
|
+
resolver,
|
|
590
|
+
definition.id,
|
|
591
|
+
initializer,
|
|
592
|
+
depth,
|
|
593
|
+
state,
|
|
594
|
+
sourceId
|
|
595
|
+
)
|
|
596
|
+
} finally {
|
|
597
|
+
state.activeDefinitions.delete(key)
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (depth >= 3) return null
|
|
602
|
+
|
|
603
|
+
if (node.type === 'ConditionalExpression') {
|
|
604
|
+
const testNode = childNode(node, 'test')
|
|
605
|
+
const consequent = childNode(node, 'consequent')
|
|
606
|
+
const alternate = childNode(node, 'alternate')
|
|
607
|
+
if (!testNode || !consequent || !alternate) return null
|
|
608
|
+
|
|
609
|
+
const test = spanOf(id, testNode)
|
|
610
|
+
if (test.id !== sourceId) return null
|
|
611
|
+
const whenTrue = evaluateBranchChild(resolver, id, consequent, depth, state, sourceId)
|
|
612
|
+
if (!whenTrue) return null
|
|
613
|
+
const whenFalse = evaluateBranchChild(resolver, id, alternate, depth, state, sourceId)
|
|
614
|
+
if (!whenFalse) return null
|
|
615
|
+
|
|
616
|
+
return {
|
|
617
|
+
kind: 'branch',
|
|
618
|
+
test,
|
|
619
|
+
whenTrue,
|
|
620
|
+
whenFalse,
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (node.type === 'TemplateLiteral') {
|
|
625
|
+
return templateBranchesNode(
|
|
626
|
+
resolver,
|
|
627
|
+
id,
|
|
628
|
+
childNodes(node, 'quasis'),
|
|
629
|
+
childNodes(node, 'expressions'),
|
|
630
|
+
0,
|
|
631
|
+
'',
|
|
632
|
+
depth,
|
|
633
|
+
state,
|
|
634
|
+
sourceId
|
|
635
|
+
)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (node.type === 'LogicalExpression') {
|
|
639
|
+
const left = childNode(node, 'left')
|
|
640
|
+
const right = childNode(node, 'right')
|
|
641
|
+
if (!left || !right) return null
|
|
642
|
+
|
|
643
|
+
if (node.operator === '&&') {
|
|
644
|
+
const test = spanOf(id, left)
|
|
645
|
+
if (test.id !== sourceId) return null
|
|
646
|
+
const whenTrue = evaluateBranchChild(resolver, id, right, depth, state, sourceId)
|
|
647
|
+
if (!whenTrue) return null
|
|
648
|
+
const whenFalse: BranchDecisionNode = {
|
|
649
|
+
kind: 'leaf',
|
|
650
|
+
value: undefined,
|
|
651
|
+
dependencies: [...state.dependencies].sort(),
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
kind: 'branch',
|
|
655
|
+
test,
|
|
656
|
+
whenTrue,
|
|
657
|
+
whenFalse,
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (node.operator === '||' || node.operator === '??') {
|
|
662
|
+
const test = spanOf(id, left)
|
|
663
|
+
if (test.id !== sourceId) return null
|
|
664
|
+
const leftState: EvaluationState = {
|
|
665
|
+
activeDefinitions: new Set(state.activeDefinitions),
|
|
666
|
+
dependencies: new Set(state.dependencies),
|
|
667
|
+
}
|
|
668
|
+
const leftRes = evaluateNode(resolver, id, left, leftState)
|
|
669
|
+
if (!leftRes.ok) return null
|
|
670
|
+
const whenTrue: BranchDecisionNode = {
|
|
671
|
+
kind: 'leaf',
|
|
672
|
+
value: leftRes.value,
|
|
673
|
+
dependencies: [...leftState.dependencies].sort(),
|
|
674
|
+
}
|
|
675
|
+
const whenFalse = evaluateBranchChild(resolver, id, right, depth, state, sourceId)
|
|
676
|
+
if (!whenFalse) return null
|
|
677
|
+
return {
|
|
678
|
+
kind: 'branch',
|
|
679
|
+
test,
|
|
680
|
+
whenTrue,
|
|
681
|
+
whenFalse,
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return null
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function mapBranchLeaves(
|
|
690
|
+
node: BranchDecisionNode,
|
|
691
|
+
map: (leaf: Extract<BranchDecisionNode, { kind: 'leaf' }>) => BranchDecisionNode | null
|
|
692
|
+
): BranchDecisionNode | null {
|
|
693
|
+
if (node.kind === 'leaf') return map(node)
|
|
694
|
+
const whenTrue = mapBranchLeaves(node.whenTrue, map)
|
|
695
|
+
if (!whenTrue) return null
|
|
696
|
+
const whenFalse = mapBranchLeaves(node.whenFalse, map)
|
|
697
|
+
if (!whenFalse) return null
|
|
698
|
+
return { kind: 'branch', test: node.test, whenTrue, whenFalse }
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* A template literal is a decision tree once every value it interpolates is:
|
|
703
|
+
* `${active ? 'red10' : 'blue10'}` names the same two strings the bare ternary
|
|
704
|
+
* does, and the quasis around it are constants. Each interpolated tree
|
|
705
|
+
* multiplies the leaves, so every step spends a level of the shared depth
|
|
706
|
+
* budget and a wide template refuses instead of expanding.
|
|
707
|
+
*/
|
|
708
|
+
function templateBranchesNode(
|
|
709
|
+
resolver: SymbolResolver,
|
|
710
|
+
id: ResolvedModuleId,
|
|
711
|
+
quasis: AstNode[],
|
|
712
|
+
expressions: AstNode[],
|
|
713
|
+
index: number,
|
|
714
|
+
prefix: string,
|
|
715
|
+
depth: number,
|
|
716
|
+
state: EvaluationState,
|
|
717
|
+
sourceId: ResolvedModuleId
|
|
718
|
+
): BranchDecisionNode | null {
|
|
719
|
+
let text = prefix
|
|
720
|
+
for (let position = index; position < quasis.length; position++) {
|
|
721
|
+
text += quasiText(quasis[position])
|
|
722
|
+
const expression = expressions[position]
|
|
723
|
+
if (!expression) continue
|
|
724
|
+
const exact = evaluateNode(resolver, id, expression, state)
|
|
725
|
+
if (exact.ok) {
|
|
726
|
+
const interpolated = templateInterpolation(exact.value)
|
|
727
|
+
if (interpolated === null) return null
|
|
728
|
+
text += interpolated
|
|
729
|
+
continue
|
|
730
|
+
}
|
|
731
|
+
const tree = evaluateBranchesNode(
|
|
732
|
+
resolver,
|
|
733
|
+
id,
|
|
734
|
+
expression,
|
|
735
|
+
depth + 1,
|
|
736
|
+
state,
|
|
737
|
+
sourceId
|
|
738
|
+
)
|
|
739
|
+
if (!tree) return null
|
|
740
|
+
return mapBranchLeaves(tree, (leaf) => {
|
|
741
|
+
// an `&&` leaf carries `undefined` to mean "the falsy left operand", whose
|
|
742
|
+
// string form is the operand itself and so is not known here
|
|
743
|
+
if (leaf.value === undefined) return null
|
|
744
|
+
const interpolated = templateInterpolation(leaf.value)
|
|
745
|
+
if (interpolated === null) return null
|
|
746
|
+
return templateBranchesNode(
|
|
747
|
+
resolver,
|
|
748
|
+
id,
|
|
749
|
+
quasis,
|
|
750
|
+
expressions,
|
|
751
|
+
position + 1,
|
|
752
|
+
text + interpolated,
|
|
753
|
+
depth + 1,
|
|
754
|
+
state,
|
|
755
|
+
sourceId
|
|
756
|
+
)
|
|
757
|
+
})
|
|
758
|
+
}
|
|
759
|
+
return { kind: 'leaf', value: text, dependencies: [...state.dependencies].sort() }
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/** ToString on a primitive, which is all a template can be folded through */
|
|
763
|
+
function templateInterpolation(value: StaticEvaluationValue): string | null {
|
|
764
|
+
if (value === null) return 'null'
|
|
765
|
+
if (
|
|
766
|
+
typeof value === 'string' ||
|
|
767
|
+
typeof value === 'number' ||
|
|
768
|
+
typeof value === 'boolean'
|
|
769
|
+
) {
|
|
770
|
+
return String(value)
|
|
771
|
+
}
|
|
772
|
+
return null
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* A conditional or logical expression whose test resists static evaluation while
|
|
777
|
+
* its branch values evaluate statically or form a decision tree.
|
|
778
|
+
*/
|
|
779
|
+
export function evaluateBranches(
|
|
780
|
+
resolver: SymbolResolver,
|
|
781
|
+
reference: ExpressionReference
|
|
782
|
+
): BranchDecisionNode | null {
|
|
783
|
+
const node = resolver.expressionNode(reference)
|
|
784
|
+
if (!node) return null
|
|
785
|
+
const state: EvaluationState = {
|
|
786
|
+
activeDefinitions: new Set(),
|
|
787
|
+
dependencies: new Set(),
|
|
788
|
+
}
|
|
789
|
+
const tree = evaluateBranchesNode(resolver, reference.id, node, 0, state, reference.id)
|
|
790
|
+
return tree?.kind === 'branch' ? tree : null
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
export function evaluateExpression(
|
|
794
|
+
resolver: SymbolResolver,
|
|
795
|
+
reference: ExpressionReference
|
|
796
|
+
): EvaluationResult {
|
|
797
|
+
const node = resolver.expressionNode(reference)
|
|
798
|
+
if (!node) {
|
|
799
|
+
return {
|
|
800
|
+
ok: false,
|
|
801
|
+
bailout: linkedBailout(
|
|
802
|
+
'linked/missing-initializer',
|
|
803
|
+
reference,
|
|
804
|
+
'Expression is outside the linked project graph',
|
|
805
|
+
reference.id
|
|
806
|
+
),
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const state: EvaluationState = {
|
|
810
|
+
activeDefinitions: new Set(),
|
|
811
|
+
dependencies: new Set(),
|
|
812
|
+
}
|
|
813
|
+
const result = evaluateNode(resolver, reference.id, node, state)
|
|
814
|
+
return result.ok
|
|
815
|
+
? {
|
|
816
|
+
ok: true,
|
|
817
|
+
value: result.value,
|
|
818
|
+
dependencies: [...state.dependencies].sort(),
|
|
819
|
+
}
|
|
820
|
+
: result
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* proves the runtime domain of a dynamic expression without evaluating its
|
|
825
|
+
* changing inputs. this is deliberately narrow: numeric arithmetic and a
|
|
826
|
+
* bounded lookup into a static primitive array are the two forms a host style
|
|
827
|
+
* can consume directly without retaining the full Tamagui resolver.
|
|
828
|
+
*/
|
|
829
|
+
export function evaluateDynamicExpression(
|
|
830
|
+
resolver: SymbolResolver,
|
|
831
|
+
reference: ExpressionReference
|
|
832
|
+
): DynamicEvaluation | null {
|
|
833
|
+
const node = resolver.expressionNode(reference)
|
|
834
|
+
if (!node) return null
|
|
835
|
+
const state: EvaluationState = {
|
|
836
|
+
activeDefinitions: new Set(),
|
|
837
|
+
dependencies: new Set(),
|
|
838
|
+
}
|
|
839
|
+
const result = evaluateDynamicNode(resolver, reference.id, node, state)
|
|
840
|
+
return result ? { ...result, dependencies: [...state.dependencies].sort() } : null
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
export function evaluateBinding(
|
|
844
|
+
resolver: SymbolResolver,
|
|
845
|
+
id: ResolvedModuleId,
|
|
846
|
+
localName: string
|
|
847
|
+
): EvaluationResult {
|
|
848
|
+
const definition = resolver.resolveBinding(id, localName)
|
|
849
|
+
if (!definition) {
|
|
850
|
+
const emptySpan: SourceSpan = { id, start: 0, end: 0 }
|
|
851
|
+
return {
|
|
852
|
+
ok: false,
|
|
853
|
+
bailout: linkedBailout(
|
|
854
|
+
'linked/unresolved-binding',
|
|
855
|
+
emptySpan,
|
|
856
|
+
`Binding ${localName} has no host-linked definition`
|
|
857
|
+
),
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
if (!definition.initializer) {
|
|
861
|
+
return {
|
|
862
|
+
ok: false,
|
|
863
|
+
bailout: linkedBailout(
|
|
864
|
+
'linked/missing-initializer',
|
|
865
|
+
definition.span,
|
|
866
|
+
`Binding ${localName} has no static initializer`,
|
|
867
|
+
definition.id
|
|
868
|
+
),
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
return evaluateExpression(
|
|
872
|
+
resolver,
|
|
873
|
+
expressionReference(definition.id, definition.initializer)
|
|
874
|
+
)
|
|
875
|
+
}
|