418
case "for": {
419
return t.forStatement(
420
codegenForInit(cx, terminal.init),
421
- codegenInstructionValue(cx, terminal.test),
421
+ codegenInstructionValueToExpression(cx, terminal.test),
422
terminal.update !== null
423
- ? codegenInstructionValue(cx, terminal.update)
423
+ ? codegenInstructionValueToExpression(cx, terminal.update)
424
: null,
425
codegenBlock(cx, terminal.loop)
426
);
504
createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
505
t.variableDeclarator(lval, null),
506
]),
507
- codegenInstructionValue(cx, iterableCollection.value),
507
+ codegenInstructionValueToExpression(cx, iterableCollection.value),
508
codegenBlock(cx, terminal.loop)
509
);
510
} else {
514
createVariableDeclaration(iterableItem.value.loc, varDeclKind, [
515
t.variableDeclarator(lval, null),
516
]),
517
- codegenInstructionValue(cx, iterableCollection.value),
517
+ codegenInstructionValueToExpression(cx, iterableCollection.value),
518
codegenBlock(cx, terminal.loop)
519
);
520
}
521
}
522
case "if": {
523
- const test = codegenPlace(cx, terminal.test);
523
+ const test = codegenPlaceToExpression(cx, terminal.test);
524
const consequent = codegenBlock(cx, terminal.consequent);
525
let alternate: t.Statement | null = null;
526
if (terminal.alternate !== null) {
532
return t.ifStatement(test, consequent, alternate);
533
}
534
case "return": {
535
- const value = codegenPlace(cx, terminal.value);
535
+ const value = codegenPlaceToExpression(cx, terminal.value);
536
if (value.type === "Identifier" && value.name === "undefined") {
537
// Use implicit undefined
538
return t.returnStatement();
541
}
542
case "switch": {
543
return t.switchStatement(
544
- codegenPlace(cx, terminal.test),
544
+ codegenPlaceToExpression(cx, terminal.test),
545
terminal.cases.map((case_) => {
546
const test =
547
- case_.test !== null ? codegenPlace(cx, case_.test) : null;
547
+ case_.test !== null
548
+ ? codegenPlaceToExpression(cx, case_.test)
549
+ : null;
550
const block = codegenBlock(cx, case_.block!);
551
return t.switchCase(test, [block]);
552
})
553
);
554
}
555
case "throw": {
554
- return t.throwStatement(codegenPlace(cx, terminal.value));
556
+ return t.throwStatement(codegenPlaceToExpression(cx, terminal.value));
557
}
558
case "do-while": {
557
- const test = codegenInstructionValue(cx, terminal.test);
559
+ const test = codegenInstructionValueToExpression(cx, terminal.test);
560
return t.doWhileStatement(test, codegenBlock(cx, terminal.loop));
561
}
562
case "while": {
561
- const test = codegenInstructionValue(cx, terminal.test);
563
+ const test = codegenInstructionValueToExpression(cx, terminal.test);
564
return t.whileStatement(test, codegenBlock(cx, terminal.loop));
565
}
566
case "label": {
605
? InstructionKind.Reassign
606
: kind;
607
lvalue = instr.value.lvalue.place;
606
- value = codegenPlace(cx, instr.value.value);
608
+ value = codegenPlaceToExpression(cx, instr.value.value);
609
} else if (instr.value.kind === "StoreContext") {
610
lvalue = instr.value.lvalue.place;
609
- value = codegenPlace(cx, instr.value.value);
611
+ value = codegenPlaceToExpression(cx, instr.value.value);
612
} else if (
613
instr.value.kind === "DeclareLocal" ||
614
instr.value.kind === "DeclareContext"
645
} else if (hasReasign) {
646
kind = InstructionKind.Reassign;
647
}
646
- value = codegenPlace(cx, instr.value.value);
648
+ value = codegenPlaceToExpression(cx, instr.value.value);
649
}
650
switch (kind) {
651
case InstructionKind.Const: {
751
});
752
return declaration;
753
} else {
752
- return codegenInstructionValue(cx, init);
754
+ return codegenInstructionValueToExpression(cx, init);
755
}
756
}
757
806
const createJsxClosingElement = withLoc(t.jsxClosingElement);
807
const createStringLiteral = withLoc(t.stringLiteral);
808
807
-type Temporaries = Map<IdentifierId, t.Expression | null>;
809
+type Temporaries = Map<IdentifierId, t.Expression | t.JSXText | null>;
810
811
function codegenLabel(id: BlockId): string {
812
return `bb${id}`;
815
function codegenInstruction(
816
cx: Context,
817
instr: ReactiveInstruction,
816
- value: t.Expression
818
+ value: t.Expression | t.JSXText
819
): t.Statement {
820
if (t.isStatement(value)) {
821
return value;
822
}
823
if (instr.lvalue === null) {
822
- return t.expressionStatement(value);
824
+ return t.expressionStatement(convertValueToExpression(value));
825
}
826
if (instr.lvalue.identifier.name === null) {
827
// temporary
828
cx.temp.set(instr.lvalue.identifier.id, value);
829
return t.emptyStatement();
830
} else {
831
+ const expressionValue = convertValueToExpression(value);
832
if (cx.hasDeclared(instr.lvalue.identifier)) {
833
return createExpressionStatement(
834
instr.loc,
835
t.assignmentExpression(
836
"=",
837
convertIdentifier(instr.lvalue.identifier),
835
- value
838
+ expressionValue
839
)
840
);
841
} else {
842
return createVariableDeclaration(instr.loc, "const", [
840
- t.variableDeclarator(convertIdentifier(instr.lvalue.identifier), value),
843
+ t.variableDeclarator(
844
+ convertIdentifier(instr.lvalue.identifier),
845
+ expressionValue
846
+ ),
847
]);
848
}
849
}
850
}
851
846
-function codegenInstructionValue(
852
+function convertValueToExpression(
853
+ value: t.JSXText | t.Expression
854
+): t.Expression {
855
+ if (value.type === "JSXText") {
856
+ return createStringLiteral(value.loc, value.value);
857
+ }
858
+ return value;
859
+}
860
+
861
+function codegenInstructionValueToExpression(
862
cx: Context,
863
instrValue: ReactiveValue
864
): t.Expression {
850
- let value: t.Expression;
865
+ const value = codegenInstructionValue(cx, instrValue);
866
+ return convertValueToExpression(value);
867
+}
868
+
869
+function codegenInstructionValue(
870
+ cx: Context,
871
+ instrValue: ReactiveValue
872
+): t.Expression | t.JSXText {
873
+ let value: t.Expression | t.JSXText;
874
switch (instrValue.kind) {
875
case "ArrayExpression": {
876
const elements = instrValue.elements.map((element) => {
877
if (element.kind === "Identifier") {
855
- return codegenPlace(cx, element);
878
+ return codegenPlaceToExpression(cx, element);
879
} else if (element.kind === "Spread") {
857
- return t.spreadElement(codegenPlace(cx, element.place));
880
+ return t.spreadElement(codegenPlaceToExpression(cx, element.place));
881
} else {
882
return null;
883
}
886
break;
887
}
888
case "BinaryExpression": {
866
- const left = codegenPlace(cx, instrValue.left);
867
- const right = codegenPlace(cx, instrValue.right);
889
+ const left = codegenPlaceToExpression(cx, instrValue.left);
890
+ const right = codegenPlaceToExpression(cx, instrValue.right);
891
value = createBinaryExpression(
892
instrValue.loc,
893
instrValue.operator,
899
case "UnaryExpression": {
900
value = t.unaryExpression(
901
instrValue.operator as "throw", // todo
879
- codegenPlace(cx, instrValue.value)
902
+ codegenPlaceToExpression(cx, instrValue.value)
903
);
904
break;
905
}
908
break;
909
}
910
case "CallExpression": {
888
- const callee = codegenPlace(cx, instrValue.callee);
911
+ const callee = codegenPlaceToExpression(cx, instrValue.callee);
912
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
913
value = createCallExpression(instrValue.loc, callee, args);
914
break;
915
}
916
case "OptionalExpression": {
894
- const optionalValue = codegenInstructionValue(cx, instrValue.value);
917
+ const optionalValue = codegenInstructionValueToExpression(
918
+ cx,
919
+ instrValue.value
920
+ );
921
switch (optionalValue.type) {
922
case "OptionalCallExpression":
923
case "CallExpression": {
964
break;
965
}
966
case "MethodCall": {
941
- const memberExpr = codegenPlace(cx, instrValue.property);
967
+ const memberExpr = codegenPlaceToExpression(cx, instrValue.property);
968
CompilerError.invariant(
969
t.isMemberExpression(memberExpr) ||
970
t.isOptionalMemberExpression(memberExpr),
980
CompilerError.invariant(
981
t.isNodesEquivalent(
982
memberExpr.object,
957
- codegenPlace(cx, instrValue.receiver)
983
+ codegenPlaceToExpression(cx, instrValue.receiver)
984
),
985
{
986
reason:
996
break;
997
}
998
case "NewExpression": {
973
- const callee = codegenPlace(cx, instrValue.callee);
999
+ const callee = codegenPlaceToExpression(cx, instrValue.callee);
1000
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
1001
value = t.newExpression(callee, args);
1002
break;
1009
1010
switch (property.type) {
1011
case "property": {
986
- const value = codegenPlace(cx, property.place);
1012
+ const value = codegenPlaceToExpression(cx, property.place);
1013
properties.push(
1014
t.objectProperty(
1015
key,
1055
);
1056
}
1057
} else {
1032
- properties.push(t.spreadElement(codegenPlace(cx, property.place)));
1058
+ properties.push(
1059
+ t.spreadElement(codegenPlaceToExpression(cx, property.place))
1060
+ );
1061
}
1062
}
1063
value = t.objectExpression(properties);
1064
break;
1065
}
1066
case "JSXText": {
1039
- value = createStringLiteral(instrValue.loc, instrValue.value);
1067
+ value = createJsxText(instrValue.loc, instrValue.value);
1068
break;
1069
}
1070
case "JsxExpression": {
1074
}
1075
let tagValue =
1076
instrValue.tag.kind === "Identifier"
1049
- ? codegenPlace(cx, instrValue.tag)
1077
+ ? codegenPlaceToExpression(cx, instrValue.tag)
1078
: t.stringLiteral(instrValue.tag.name);
1079
let tag: t.JSXIdentifier | t.JSXNamespacedName | t.JSXMemberExpression;
1080
if (tagValue.type === "Identifier") {
1158
value = t.assignmentExpression(
1159
"=",
1160
t.memberExpression(
1133
- codegenPlace(cx, instrValue.object),
1161
+ codegenPlaceToExpression(cx, instrValue.object),
1162
t.identifier(instrValue.property)
1163
),
1136
- codegenPlace(cx, instrValue.value)
1164
+ codegenPlaceToExpression(cx, instrValue.value)
1165
);
1166
break;
1167
}
1168
case "PropertyLoad": {
1141
- const object = codegenPlace(cx, instrValue.object);
1169
+ const object = codegenPlaceToExpression(cx, instrValue.object);
1170
// We currently only lower single chains of optional memberexpr.
1171
// (See BuildHIR.ts for more detail.)
1172
value = t.memberExpression(
1180
value = t.unaryExpression(
1181
"delete",
1182
t.memberExpression(
1155
- codegenPlace(cx, instrValue.object),
1183
+ codegenPlaceToExpression(cx, instrValue.object),
1184
t.identifier(instrValue.property)
1185
)
1186
);
1190
value = t.assignmentExpression(
1191
"=",
1192
t.memberExpression(
1165
- codegenPlace(cx, instrValue.object),
1166
- codegenPlace(cx, instrValue.property),
1193
+ codegenPlaceToExpression(cx, instrValue.object),
1194
+ codegenPlaceToExpression(cx, instrValue.property),
1195
true
1196
),
1169
- codegenPlace(cx, instrValue.value)
1197
+ codegenPlaceToExpression(cx, instrValue.value)
1198
);
1199
break;
1200
}
1201
case "ComputedLoad": {
1174
- const object = codegenPlace(cx, instrValue.object);
1175
- const property = codegenPlace(cx, instrValue.property);
1202
+ const object = codegenPlaceToExpression(cx, instrValue.object);
1203
+ const property = codegenPlaceToExpression(cx, instrValue.property);
1204
value = t.memberExpression(object, property, true);
1205
break;
1206
}
1208
value = t.unaryExpression(
1209
"delete",
1210
t.memberExpression(
1183
- codegenPlace(cx, instrValue.object),
1184
- codegenPlace(cx, instrValue.property),
1211
+ codegenPlaceToExpression(cx, instrValue.object),
1212
+ codegenPlaceToExpression(cx, instrValue.property),
1213
true
1214
)
1215
);
1217
}
1218
case "LoadLocal":
1219
case "LoadContext": {
1192
- value = codegenPlace(cx, instrValue.place);
1220
+ value = codegenPlaceToExpression(cx, instrValue.place);
1221
break;
1222
}
1223
case "FunctionExpression": {
1251
case "TaggedTemplateExpression": {
1252
value = createTaggedTemplateExpression(
1253
instrValue.loc,
1226
- codegenPlace(cx, instrValue.tag),
1254
+ codegenPlaceToExpression(cx, instrValue.tag),
1255
t.templateLiteral([t.templateElement(instrValue.value)], [])
1256
);
1257
break;
1258
}
1259
case "TypeCastExpression": {
1260
value = t.typeCastExpression(
1233
- codegenPlace(cx, instrValue.value),
1261
+ codegenPlaceToExpression(cx, instrValue.value),
1262
instrValue.type
1263
);
1264
break;
1267
value = createLogicalExpression(
1268
instrValue.loc,
1269
instrValue.operator,
1242
- codegenInstructionValue(cx, instrValue.left),
1243
- codegenInstructionValue(cx, instrValue.right)
1270
+ codegenInstructionValueToExpression(cx, instrValue.left),
1271
+ codegenInstructionValueToExpression(cx, instrValue.right)
1272
);
1273
break;
1274
}
1275
case "ConditionalExpression": {
1276
value = createConditionalExpression(
1277
instrValue.loc,
1250
- codegenInstructionValue(cx, instrValue.test),
1251
- codegenInstructionValue(cx, instrValue.consequent),
1252
- codegenInstructionValue(cx, instrValue.alternate)
1278
+ codegenInstructionValueToExpression(cx, instrValue.test),
1279
+ codegenInstructionValueToExpression(cx, instrValue.consequent),
1280
+ codegenInstructionValueToExpression(cx, instrValue.alternate)
1281
);
1282
break;
1283
}
1316
}
1317
});
1318
if (expressions.length === 0) {
1291
- value = codegenInstructionValue(cx, instrValue.value);
1319
+ value = codegenInstructionValueToExpression(cx, instrValue.value);
1320
} else {
1321
value = createSequenceExpression(instrValue.loc, [
1322
...expressions,
1295
- codegenInstructionValue(cx, instrValue.value),
1323
+ codegenInstructionValueToExpression(cx, instrValue.value),
1324
]);
1325
}
1326
break;
1329
value = createTemplateLiteral(
1330
instrValue.loc,
1331
instrValue.quasis.map((q) => t.templateElement(q)),
1304
- instrValue.subexprs.map((p) => codegenPlace(cx, p))
1332
+ instrValue.subexprs.map((p) => codegenPlaceToExpression(cx, p))
1333
);
1334
break;
1335
}
1342
break;
1343
}
1344
case "Await": {
1317
- value = t.awaitExpression(codegenPlace(cx, instrValue.value));
1345
+ value = t.awaitExpression(codegenPlaceToExpression(cx, instrValue.value));
1346
break;
1347
}
1348
case "NextIterableOf": {
1321
- value = codegenPlace(cx, instrValue.value);
1349
+ value = codegenPlaceToExpression(cx, instrValue.value);
1350
break;
1351
}
1352
case "NextPropertyOf": {
1325
- value = codegenPlace(cx, instrValue.value);
1353
+ value = codegenPlaceToExpression(cx, instrValue.value);
1354
break;
1355
}
1356
case "PostfixUpdate": {
1357
value = t.updateExpression(
1358
instrValue.operation,
1331
- codegenPlace(cx, instrValue.lvalue),
1359
+ codegenPlaceToExpression(cx, instrValue.lvalue),
1360
false
1361
);
1362
break;
1364
case "PrefixUpdate": {
1365
value = t.updateExpression(
1366
instrValue.operation,
1339
- codegenPlace(cx, instrValue.lvalue),
1367
+ codegenPlaceToExpression(cx, instrValue.lvalue),
1368
true
1369
);
1370
break;
1410
createJsxIdentifier(attribute.place.loc, name)
1411
);
1412
}
1385
- const innerValue = codegenPlace(cx, attribute.place);
1413
+ const innerValue = codegenPlaceToExpression(cx, attribute.place);
1414
let value;
1415
switch (innerValue.type) {
1416
case "StringLiteral": {
1429
return createJsxAttribute(attribute.place.loc, propName, value);
1430
}
1431
case "JsxSpreadAttribute": {
1404
- return t.jsxSpreadAttribute(codegenPlace(cx, attribute.argument));
1432
+ return t.jsxSpreadAttribute(
1433
+ codegenPlaceToExpression(cx, attribute.argument)
1434
+ );
1435
}
1436
default: {
1437
assertExhaustive(
1453
| t.JSXFragment {
1454
const value = codegenPlace(cx, place);
1455
switch (value.type) {
1426
- case "StringLiteral": {
1456
+ case "JSXText": {
1457
return createJsxText(place.loc, value.value);
1458
}
1459
case "JSXElement":
1478
const value = codegenPlace(cx, place);
1479
switch (value.type) {
1480
// fbt:param only allows JSX element or expression container as children
1481
+ case "JSXText":
1482
case "JSXElement": {
1483
return value;
1484
}
1596
arg: Place | SpreadPattern
1597
): t.Expression | t.SpreadElement {
1598
if (arg.kind === "Identifier") {
1568
- return codegenPlace(cx, arg);
1599
+ return codegenPlaceToExpression(cx, arg);
1600
} else {
1570
- return t.spreadElement(codegenPlace(cx, arg.place));
1601
+ return t.spreadElement(codegenPlaceToExpression(cx, arg.place));
1602
}
1603
}
1604
1574
-function codegenPlace(cx: Context, place: Place): t.Expression {
1605
+function codegenPlaceToExpression(cx: Context, place: Place): t.Expression {
1606
+ const value = codegenPlace(cx, place);
1607
+ return convertValueToExpression(value);
1608
+}
1609
+
1610
+function codegenPlace(cx: Context, place: Place): t.Expression | t.JSXText {
1611
let tmp = cx.temp.get(place.identifier.id);
1612
if (tmp != null) {
1613
return tmp;