1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | import assert from "assert";
|
12 | import * as t from "babel-types";
|
13 | import { hoist } from "./hoist";
|
14 | import { Emitter } from "./emit";
|
15 | import * as util from "./util";
|
16 |
|
17 | let getMarkInfo = require("private").makeAccessor();
|
18 |
|
19 | exports.visitor = {
|
20 | Function: {
|
21 | exit: function(path, state) {
|
22 | let node = path.node;
|
23 |
|
24 | if (node.generator) {
|
25 | if (node.async) {
|
26 |
|
27 | if (state.opts.asyncGenerators === false) return;
|
28 | } else {
|
29 |
|
30 | if (state.opts.generators === false) return;
|
31 | }
|
32 | } else if (node.async) {
|
33 |
|
34 | if (state.opts.async === false) return;
|
35 | } else {
|
36 |
|
37 | return;
|
38 | }
|
39 |
|
40 | let contextId = path.scope.generateUidIdentifier("context");
|
41 | let argsId = path.scope.generateUidIdentifier("args");
|
42 |
|
43 | path.ensureBlock();
|
44 | let bodyBlockPath = path.get("body");
|
45 |
|
46 | if (node.async) {
|
47 | bodyBlockPath.traverse(awaitVisitor);
|
48 | }
|
49 |
|
50 | bodyBlockPath.traverse(functionSentVisitor, {
|
51 | context: contextId
|
52 | });
|
53 |
|
54 | let outerBody = [];
|
55 | let innerBody = [];
|
56 |
|
57 | bodyBlockPath.get("body").forEach(function(childPath) {
|
58 | let node = childPath.node;
|
59 | if (node && node._blockHoist != null) {
|
60 | outerBody.push(node);
|
61 | } else {
|
62 | innerBody.push(node);
|
63 | }
|
64 | });
|
65 |
|
66 | if (outerBody.length > 0) {
|
67 |
|
68 |
|
69 | bodyBlockPath.node.body = innerBody;
|
70 | }
|
71 |
|
72 | let outerFnExpr = getOuterFnExpr(path);
|
73 |
|
74 |
|
75 |
|
76 | t.assertIdentifier(node.id);
|
77 | let innerFnId = t.identifier(node.id.name + "$");
|
78 |
|
79 |
|
80 |
|
81 | let vars = hoist(path);
|
82 |
|
83 | let didRenameArguments = renameArguments(path, argsId);
|
84 | if (didRenameArguments) {
|
85 | vars = vars || t.variableDeclaration("var", []);
|
86 | vars.declarations.push(t.variableDeclarator(
|
87 | argsId, t.identifier("arguments")
|
88 | ));
|
89 | }
|
90 |
|
91 | let emitter = new Emitter(contextId);
|
92 | emitter.explode(path.get("body"));
|
93 |
|
94 | if (vars && vars.declarations.length > 0) {
|
95 | outerBody.push(vars);
|
96 | }
|
97 |
|
98 | let wrapArgs = [
|
99 | emitter.getContextFunction(innerFnId),
|
100 |
|
101 |
|
102 |
|
103 | node.generator ? outerFnExpr : t.nullLiteral(),
|
104 | t.thisExpression()
|
105 | ];
|
106 |
|
107 | let tryLocsList = emitter.getTryLocsList();
|
108 | if (tryLocsList) {
|
109 | wrapArgs.push(tryLocsList);
|
110 | }
|
111 |
|
112 | let wrapCall = t.callExpression(
|
113 | util.runtimeProperty(node.async ? "async" : "wrap"),
|
114 | wrapArgs
|
115 | );
|
116 |
|
117 | outerBody.push(t.returnStatement(wrapCall));
|
118 | node.body = t.blockStatement(outerBody);
|
119 |
|
120 | let wasGeneratorFunction = node.generator;
|
121 | if (wasGeneratorFunction) {
|
122 | node.generator = false;
|
123 | }
|
124 |
|
125 | if (node.async) {
|
126 | node.async = false;
|
127 | }
|
128 |
|
129 | if (wasGeneratorFunction && t.isExpression(node)) {
|
130 | path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node]));
|
131 | }
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | path.requeue();
|
137 | }
|
138 | }
|
139 | };
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | function getOuterFnExpr(funPath) {
|
146 | let node = funPath.node;
|
147 | t.assertFunction(node);
|
148 |
|
149 | if (!node.id){
|
150 |
|
151 |
|
152 | node.id = funPath.scope.parent.generateUidIdentifier("callee");
|
153 | }
|
154 |
|
155 | if (node.generator &&
|
156 | t.isFunctionDeclaration(node)) {
|
157 | let pp = funPath.findParent(function (path) {
|
158 | return path.isProgram() || path.isBlockStatement();
|
159 | });
|
160 |
|
161 | if (!pp) {
|
162 | return node.id;
|
163 | }
|
164 |
|
165 | let markDecl = getRuntimeMarkDecl(pp);
|
166 | let markedArray = markDecl.declarations[0].id;
|
167 | let funDeclIdArray = markDecl.declarations[0].init.callee.object;
|
168 | t.assertArrayExpression(funDeclIdArray);
|
169 |
|
170 | let index = funDeclIdArray.elements.length;
|
171 | funDeclIdArray.elements.push(node.id);
|
172 |
|
173 | return t.memberExpression(
|
174 | markedArray,
|
175 | t.numericLiteral(index),
|
176 | true
|
177 | );
|
178 | }
|
179 |
|
180 | return node.id;
|
181 | }
|
182 |
|
183 | function getRuntimeMarkDecl(blockPath) {
|
184 | let block = blockPath.node;
|
185 | assert.ok(Array.isArray(block.body));
|
186 |
|
187 | let info = getMarkInfo(block);
|
188 | if (info.decl) {
|
189 | return info.decl;
|
190 | }
|
191 |
|
192 | info.decl = t.variableDeclaration("var", [
|
193 | t.variableDeclarator(
|
194 | blockPath.scope.generateUidIdentifier("marked"),
|
195 | t.callExpression(
|
196 | t.memberExpression(
|
197 | t.arrayExpression([]),
|
198 | t.identifier("map"),
|
199 | false
|
200 | ),
|
201 | [util.runtimeProperty("mark")]
|
202 | )
|
203 | )
|
204 | ]);
|
205 |
|
206 | blockPath.unshiftContainer("body", info.decl);
|
207 |
|
208 | return info.decl;
|
209 | }
|
210 |
|
211 | function renameArguments(funcPath, argsId) {
|
212 | let state = {
|
213 | didRenameArguments: false,
|
214 | argsId: argsId
|
215 | };
|
216 |
|
217 | funcPath.traverse(argumentsVisitor, state);
|
218 |
|
219 |
|
220 |
|
221 |
|
222 |
|
223 | return state.didRenameArguments;
|
224 | }
|
225 |
|
226 | let argumentsVisitor = {
|
227 | "FunctionExpression|FunctionDeclaration": function(path) {
|
228 | path.skip();
|
229 | },
|
230 |
|
231 | Identifier: function(path, state) {
|
232 | if (path.node.name === "arguments" && util.isReference(path)) {
|
233 | path.replaceWith(state.argsId);
|
234 | state.didRenameArguments = true;
|
235 | }
|
236 | }
|
237 | };
|
238 |
|
239 | let functionSentVisitor = {
|
240 | MetaProperty(path) {
|
241 | let { node } = path;
|
242 |
|
243 | if (node.meta.name === "function" && node.property.name === "sent") {
|
244 | path.replaceWith(t.memberExpression(this.context, t.identifier("_sent")));
|
245 | }
|
246 | }
|
247 | };
|
248 |
|
249 | let awaitVisitor = {
|
250 | Function: function(path) {
|
251 | path.skip();
|
252 | },
|
253 |
|
254 | AwaitExpression: function(path) {
|
255 |
|
256 | let argument = path.node.argument;
|
257 |
|
258 |
|
259 |
|
260 |
|
261 | path.replaceWith(t.yieldExpression(
|
262 | t.callExpression(
|
263 | util.runtimeProperty("awrap"),
|
264 | [argument]
|
265 | ),
|
266 | false
|
267 | ));
|
268 | }
|
269 | };
|