8
import * as t from "@babel/types";
9
import { pruneUnusedLValues, pruneUnusedLabels, renameVariables } from ".";
10
import { CompilerError, ErrorSeverity } from "../CompilerError";
11
-import { Environment } from "../HIR";
11
+import { Environment, EnvironmentConfig, ExternalFunction } from "../HIR";
12
import {
13
BlockId,
14
GeneratedSource,
30
ReactiveValue,
31
SourceLocation,
32
SpreadPattern,
33
+ getHookKind,
34
} from "../HIR/HIR";
35
import { printPlace } from "../HIR/PrintHIR";
36
import { eachPatternOperand } from "../HIR/visitors";
37
import { Err, Ok, Result } from "../Utils/Result";
38
+import { GuardKind } from "../Utils/RuntimeDiagnosticConstants";
39
import { assertExhaustive } from "../Utils/utils";
40
import { buildReactiveFunction } from "./BuildReactiveFunction";
41
import { SINGLE_CHILD_FBT_TAGS } from "./MemoizeFbtOperandsInSameScope";
88
);
89
compiled.body.body.unshift(test);
90
}
91
+
92
+ const hookGuard = fn.env.config.enableEmitHookGuards;
93
+ if (hookGuard != null) {
94
+ compiled.body = t.blockStatement([
95
+ createHookGuard(
96
+ hookGuard,
97
+ compiled.body.body,
98
+ GuardKind.PushHookGuard,
99
+ GuardKind.PopHookGuard
100
+ ),
101
+ ]);
102
+ }
103
return compileResult;
104
}
105
984
}
985
986
const createBinaryExpression = withLoc(t.binaryExpression);
973
-const createCallExpression = withLoc(t.callExpression);
987
const createExpressionStatement = withLoc(t.expressionStatement);
988
const _createLabelledStatement = withLoc(t.labeledStatement);
989
const createVariableDeclaration = withLoc(t.variableDeclaration);
1002
const createJsxClosingElement = withLoc(t.jsxClosingElement);
1003
const createStringLiteral = withLoc(t.stringLiteral);
1004
1005
+function createHookGuard(
1006
+ guard: ExternalFunction,
1007
+ stmts: t.Statement[],
1008
+ before: GuardKind,
1009
+ after: GuardKind
1010
+): t.TryStatement {
1011
+ function createHookGuardImpl(kind: number): t.ExpressionStatement {
1012
+ return t.expressionStatement(
1013
+ t.callExpression(t.identifier(guard.importSpecifierName), [
1014
+ t.numericLiteral(kind),
1015
+ ])
1016
+ );
1017
+ }
1018
+
1019
+ return t.tryStatement(
1020
+ t.blockStatement([createHookGuardImpl(before), ...stmts]),
1021
+ null,
1022
+ t.blockStatement([createHookGuardImpl(after)])
1023
+ );
1024
+}
1025
+
1026
+/**
1027
+ * Create a call expression.
1028
+ * If enableEmitHookGuards is set and the callExpression is a hook call,
1029
+ * the following transform will be made.
1030
+ * ```js
1031
+ * // source
1032
+ * useHook(arg1, arg2)
1033
+ *
1034
+ * // codegen
1035
+ * (() => {
1036
+ * try {
1037
+ * $dispatcherGuard(PUSH_EXPECT_HOOK);
1038
+ * return useHook(arg1, arg2);
1039
+ * } finally {
1040
+ * $dispatcherGuard(POP_EXPECT_HOOK);
1041
+ * }
1042
+ * })()
1043
+ * ```
1044
+ */
1045
+function createCallExpression(
1046
+ config: EnvironmentConfig,
1047
+ callee: t.Expression,
1048
+ args: Array<t.Expression | t.SpreadElement>,
1049
+ loc: SourceLocation | null,
1050
+ isHook: boolean
1051
+): t.CallExpression {
1052
+ const callExpr = t.callExpression(callee, args);
1053
+ if (loc != null && loc != GeneratedSource) {
1054
+ callExpr.loc = loc;
1055
+ }
1056
+
1057
+ const hookGuard = config.enableEmitHookGuards;
1058
+ if (hookGuard != null && isHook) {
1059
+ const iife = t.arrowFunctionExpression(
1060
+ [],
1061
+ t.blockStatement([
1062
+ createHookGuard(
1063
+ hookGuard,
1064
+ [t.returnStatement(callExpr)],
1065
+ GuardKind.AllowHook,
1066
+ GuardKind.DisallowHook
1067
+ ),
1068
+ ])
1069
+ );
1070
+ return t.callExpression(iife, []);
1071
+ } else {
1072
+ return callExpr;
1073
+ }
1074
+}
1075
+
1076
type Temporaries = Map<IdentifierId, t.Expression | t.JSXText | null>;
1077
1078
function codegenLabel(id: BlockId): string {
1175
break;
1176
}
1177
case "CallExpression": {
1178
+ const isHook = getHookKind(cx.env, instrValue.callee.identifier) != null;
1179
const callee = codegenPlaceToExpression(cx, instrValue.callee);
1180
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
1096
- value = createCallExpression(instrValue.loc, callee, args);
1181
+ value = createCallExpression(
1182
+ cx.env.config,
1183
+ callee,
1184
+ args,
1185
+ instrValue.loc,
1186
+ isHook
1187
+ );
1188
break;
1189
}
1190
case "OptionalExpression": {
1238
break;
1239
}
1240
case "MethodCall": {
1241
+ const isHook =
1242
+ getHookKind(cx.env, instrValue.property.identifier) != null;
1243
const memberExpr = codegenPlaceToExpression(cx, instrValue.property);
1244
CompilerError.invariant(
1245
t.isMemberExpression(memberExpr) ||
1268
}
1269
);
1270
const args = instrValue.args.map((arg) => codegenArgument(cx, arg));
1178
- value = createCallExpression(instrValue.loc, memberExpr, args);
1271
+ value = createCallExpression(
1272
+ cx.env.config,
1273
+ memberExpr,
1274
+ args,
1275
+ instrValue.loc,
1276
+ isHook
1277
+ );
1278
break;
1279
}
1280
case "NewExpression": {