Line data Source code
1 : // Copyright 2012 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/parsing/rewriter.h"
6 :
7 : #include "src/ast/ast.h"
8 : #include "src/ast/scopes.h"
9 : #include "src/objects-inl.h"
10 : #include "src/parsing/parse-info.h"
11 : #include "src/parsing/parser.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 : class Processor final : public AstVisitor<Processor> {
17 : public:
18 : Processor(uintptr_t stack_limit, DeclarationScope* closure_scope,
19 1086088 : Variable* result, AstValueFactory* ast_value_factory)
20 : : result_(result),
21 : replacement_(nullptr),
22 : zone_(ast_value_factory->zone()),
23 : closure_scope_(closure_scope),
24 : factory_(ast_value_factory, ast_value_factory->zone()),
25 : result_assigned_(false),
26 : is_set_(false),
27 2172176 : breakable_(false) {
28 : DCHECK_EQ(closure_scope, closure_scope->GetClosureScope());
29 : InitializeAstVisitor(stack_limit);
30 : }
31 :
32 : Processor(Parser* parser, DeclarationScope* closure_scope, Variable* result,
33 : AstValueFactory* ast_value_factory)
34 : : result_(result),
35 : replacement_(nullptr),
36 : zone_(ast_value_factory->zone()),
37 : closure_scope_(closure_scope),
38 : factory_(ast_value_factory, zone_),
39 : result_assigned_(false),
40 : is_set_(false),
41 : breakable_(false) {
42 : DCHECK_EQ(closure_scope, closure_scope->GetClosureScope());
43 : InitializeAstVisitor(parser->stack_limit());
44 : }
45 :
46 : void Process(ZonePtrList<Statement>* statements);
47 : bool result_assigned() const { return result_assigned_; }
48 :
49 : Zone* zone() { return zone_; }
50 : DeclarationScope* closure_scope() { return closure_scope_; }
51 : AstNodeFactory* factory() { return &factory_; }
52 :
53 : // Returns ".result = value"
54 1094162 : Expression* SetResult(Expression* value) {
55 1094162 : result_assigned_ = true;
56 1094162 : VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
57 : return factory()->NewAssignment(Token::ASSIGN, result_proxy, value,
58 1094155 : kNoSourcePosition);
59 : }
60 :
61 : // Inserts '.result = undefined' in front of the given statement.
62 : Statement* AssignUndefinedBefore(Statement* s);
63 :
64 : private:
65 : Variable* result_;
66 :
67 : // When visiting a node, we "return" a replacement for that node in
68 : // [replacement_]. In many cases this will just be the original node.
69 : Statement* replacement_;
70 :
71 : class BreakableScope final {
72 : public:
73 : explicit BreakableScope(Processor* processor, bool breakable = true)
74 191923 : : processor_(processor), previous_(processor->breakable_) {
75 191923 : processor->breakable_ = processor->breakable_ || breakable;
76 : }
77 :
78 191925 : ~BreakableScope() { processor_->breakable_ = previous_; }
79 :
80 : private:
81 : Processor* processor_;
82 : bool previous_;
83 : };
84 :
85 : Zone* zone_;
86 : DeclarationScope* closure_scope_;
87 : AstNodeFactory factory_;
88 :
89 : // Node visitors.
90 : #define DEF_VISIT(type) void Visit##type(type* node);
91 : AST_NODE_LIST(DEF_VISIT)
92 : #undef DEF_VISIT
93 :
94 : void VisitIterationStatement(IterationStatement* stmt);
95 :
96 9072095 : DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
97 :
98 : // We are not tracking result usage via the result_'s use
99 : // counts (we leave the accurate computation to the
100 : // usage analyzer). Instead we simple remember if
101 : // there was ever an assignment to result_.
102 : bool result_assigned_;
103 :
104 : // To avoid storing to .result all the time, we eliminate some of
105 : // the stores by keeping track of whether or not we're sure .result
106 : // will be overwritten anyway. This is a bit more tricky than what I
107 : // was hoping for.
108 : bool is_set_;
109 :
110 : bool breakable_;
111 : };
112 :
113 :
114 193010 : Statement* Processor::AssignUndefinedBefore(Statement* s) {
115 64336 : Expression* undef = factory()->NewUndefinedLiteral(kNoSourcePosition);
116 64337 : Expression* assignment = SetResult(undef);
117 64336 : Block* b = factory()->NewBlock(2, false);
118 : b->statements()->Add(
119 128673 : factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
120 64337 : b->statements()->Add(s, zone());
121 64337 : return b;
122 : }
123 :
124 3799270 : void Processor::Process(ZonePtrList<Statement>* statements) {
125 : // If we're in a breakable scope (named block, iteration, or switch), we walk
126 : // all statements. The last value producing statement before the break needs
127 : // to assign to .result. If we're not in a breakable scope, only the last
128 : // value producing statement in the block assigns to .result, so we can stop
129 : // early.
130 3799270 : for (int i = statements->length() - 1; i >= 0 && (breakable_ || !is_set_);
131 : --i) {
132 2581435 : Visit(statements->at(i));
133 2581442 : statements->Set(i, replacement_);
134 : }
135 1217835 : }
136 :
137 :
138 1541704 : void Processor::VisitBlock(Block* node) {
139 : // An initializer block is the rewritten form of a variable declaration
140 : // with initialization expressions. The initializer block contains the
141 : // list of assignments corresponding to the initialization expressions.
142 : // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of
143 : // a variable declaration with initialization expression is 'undefined'
144 : // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
145 : // returns 'undefined'. To obtain the same behavior with v8, we need
146 : // to prevent rewriting in that case.
147 1541704 : if (!node->ignore_completion_value()) {
148 : BreakableScope scope(this, node->labels() != nullptr);
149 131450 : Process(node->statements());
150 : }
151 1541705 : replacement_ = node;
152 1541705 : }
153 :
154 :
155 2069379 : void Processor::VisitExpressionStatement(ExpressionStatement* node) {
156 : // Rewrite : <x>; -> .result = <x>;
157 1039554 : if (!is_set_) {
158 1029825 : node->set_expression(SetResult(node->expression()));
159 1029820 : is_set_ = true;
160 : }
161 1039549 : replacement_ = node;
162 1039549 : }
163 :
164 :
165 8106 : void Processor::VisitIfStatement(IfStatement* node) {
166 : // Rewrite both branches.
167 2702 : bool set_after = is_set_;
168 :
169 2702 : Visit(node->then_statement());
170 2702 : node->set_then_statement(replacement_);
171 2702 : bool set_in_then = is_set_;
172 :
173 2702 : is_set_ = set_after;
174 2702 : Visit(node->else_statement());
175 2702 : node->set_else_statement(replacement_);
176 :
177 2702 : replacement_ = set_in_then && is_set_ ? node : AssignUndefinedBefore(node);
178 2702 : is_set_ = true;
179 2702 : }
180 :
181 :
182 120478 : void Processor::VisitIterationStatement(IterationStatement* node) {
183 : // The statement may have to produce a value, so always assign undefined
184 : // before.
185 : // TODO(verwaest): Omit it if we know that there's no break/continue leaving
186 : // it early.
187 : DCHECK(breakable_ || !is_set_);
188 : BreakableScope scope(this);
189 :
190 60239 : Visit(node->body());
191 60238 : node->set_body(replacement_);
192 :
193 60238 : replacement_ = AssignUndefinedBefore(node);
194 60240 : is_set_ = true;
195 60240 : }
196 :
197 :
198 523 : void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
199 523 : VisitIterationStatement(node);
200 523 : }
201 :
202 :
203 744 : void Processor::VisitWhileStatement(WhileStatement* node) {
204 744 : VisitIterationStatement(node);
205 745 : }
206 :
207 :
208 50339 : void Processor::VisitForStatement(ForStatement* node) {
209 50339 : VisitIterationStatement(node);
210 50339 : }
211 :
212 :
213 4152 : void Processor::VisitForInStatement(ForInStatement* node) {
214 4152 : VisitIterationStatement(node);
215 4152 : }
216 :
217 :
218 4480 : void Processor::VisitForOfStatement(ForOfStatement* node) {
219 4480 : VisitIterationStatement(node);
220 4481 : }
221 :
222 :
223 7589 : void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
224 : // Rewrite both try and catch block.
225 3795 : bool set_after = is_set_;
226 :
227 3795 : Visit(node->try_block());
228 3794 : node->set_try_block(static_cast<Block*>(replacement_));
229 3794 : bool set_in_try = is_set_;
230 :
231 3794 : is_set_ = set_after;
232 3794 : Visit(node->catch_block());
233 3794 : node->set_catch_block(static_cast<Block*>(replacement_));
234 :
235 3794 : replacement_ = is_set_ && set_in_try ? node : AssignUndefinedBefore(node);
236 3794 : is_set_ = true;
237 3794 : }
238 :
239 :
240 867 : void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
241 : // Only rewrite finally if it could contain 'break' or 'continue'. Always
242 : // rewrite try.
243 255 : if (breakable_) {
244 : // Only set result before a 'break' or 'continue'.
245 102 : is_set_ = true;
246 102 : Visit(node->finally_block());
247 102 : node->set_finally_block(replacement_->AsBlock());
248 : // Save .result value at the beginning of the finally block and restore it
249 : // at the end again: ".backup = .result; ...; .result = .backup"
250 : // This is necessary because the finally block does not normally contribute
251 : // to the completion value.
252 102 : CHECK_NOT_NULL(closure_scope());
253 : Variable* backup = closure_scope()->NewTemporary(
254 204 : factory()->ast_value_factory()->dot_result_string());
255 102 : Expression* backup_proxy = factory()->NewVariableProxy(backup);
256 102 : Expression* result_proxy = factory()->NewVariableProxy(result_);
257 : Expression* save = factory()->NewAssignment(
258 102 : Token::ASSIGN, backup_proxy, result_proxy, kNoSourcePosition);
259 : Expression* restore = factory()->NewAssignment(
260 102 : Token::ASSIGN, result_proxy, backup_proxy, kNoSourcePosition);
261 : node->finally_block()->statements()->InsertAt(
262 306 : 0, factory()->NewExpressionStatement(save, kNoSourcePosition), zone());
263 : node->finally_block()->statements()->Add(
264 306 : factory()->NewExpressionStatement(restore, kNoSourcePosition), zone());
265 : // We can't tell whether the finally-block is guaranteed to set .result, so
266 : // reset is_set_ before visiting the try-block.
267 102 : is_set_ = false;
268 : }
269 255 : Visit(node->try_block());
270 255 : node->set_try_block(replacement_->AsBlock());
271 :
272 255 : replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
273 255 : is_set_ = true;
274 255 : }
275 :
276 :
277 234 : void Processor::VisitSwitchStatement(SwitchStatement* node) {
278 : // The statement may have to produce a value, so always assign undefined
279 : // before.
280 : // TODO(verwaest): Omit it if we know that there's no break/continue leaving
281 : // it early.
282 : DCHECK(breakable_ || !is_set_);
283 : BreakableScope scope(this);
284 : // Rewrite statements in all case clauses.
285 234 : ZonePtrList<CaseClause>* clauses = node->cases();
286 534 : for (int i = clauses->length() - 1; i >= 0; --i) {
287 300 : CaseClause* clause = clauses->at(i);
288 300 : Process(clause->statements());
289 : }
290 :
291 234 : replacement_ = AssignUndefinedBefore(node);
292 234 : is_set_ = true;
293 234 : }
294 :
295 :
296 0 : void Processor::VisitContinueStatement(ContinueStatement* node) {
297 235 : is_set_ = false;
298 235 : replacement_ = node;
299 0 : }
300 :
301 :
302 0 : void Processor::VisitBreakStatement(BreakStatement* node) {
303 1152 : is_set_ = false;
304 1152 : replacement_ = node;
305 0 : }
306 :
307 :
308 3486 : void Processor::VisitWithStatement(WithStatement* node) {
309 3486 : Visit(node->statement());
310 3486 : node->set_statement(replacement_);
311 :
312 3486 : replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
313 3486 : is_set_ = true;
314 3486 : }
315 :
316 :
317 1742 : void Processor::VisitSloppyBlockFunctionStatement(
318 1742 : SloppyBlockFunctionStatement* node) {
319 1742 : Visit(node->statement());
320 1742 : node->set_statement(replacement_);
321 1742 : replacement_ = node;
322 1742 : }
323 :
324 :
325 0 : void Processor::VisitEmptyStatement(EmptyStatement* node) {
326 2939 : replacement_ = node;
327 0 : }
328 :
329 :
330 0 : void Processor::VisitReturnStatement(ReturnStatement* node) {
331 64 : is_set_ = true;
332 64 : replacement_ = node;
333 0 : }
334 :
335 :
336 0 : void Processor::VisitDebuggerStatement(DebuggerStatement* node) {
337 2147 : replacement_ = node;
338 0 : }
339 :
340 0 : void Processor::VisitInitializeClassMembersStatement(
341 : InitializeClassMembersStatement* node) {
342 0 : replacement_ = node;
343 0 : }
344 :
345 : // Expressions are never visited.
346 : #define DEF_VISIT(type) \
347 : void Processor::Visit##type(type* expr) { UNREACHABLE(); }
348 0 : EXPRESSION_NODE_LIST(DEF_VISIT)
349 : #undef DEF_VISIT
350 :
351 :
352 : // Declarations are never visited.
353 : #define DEF_VISIT(type) \
354 : void Processor::Visit##type(type* expr) { UNREACHABLE(); }
355 0 : DECLARATION_NODE_LIST(DEF_VISIT)
356 : #undef DEF_VISIT
357 :
358 :
359 : // Assumes code has been parsed. Mutates the AST, so the AST should not
360 : // continue to be used in the case of failure.
361 6418526 : bool Rewriter::Rewrite(ParseInfo* info) {
362 : DisallowHeapAllocation no_allocation;
363 : DisallowHandleAllocation no_handles;
364 : DisallowHandleDereference no_deref;
365 :
366 : RuntimeCallTimerScope runtimeTimer(
367 : info->runtime_call_stats(),
368 : info->on_background_thread()
369 : ? RuntimeCallCounterId::kCompileBackgroundRewriteReturnResult
370 3554956 : : RuntimeCallCounterId::kCompileRewriteReturnResult);
371 :
372 1777482 : FunctionLiteral* function = info->literal();
373 : DCHECK_NOT_NULL(function);
374 1777482 : Scope* scope = function->scope();
375 : DCHECK_NOT_NULL(scope);
376 : DCHECK_EQ(scope, scope->GetClosureScope());
377 :
378 3315643 : if (!(scope->is_script_scope() || scope->is_eval_scope() ||
379 2459137 : scope->is_module_scope())) {
380 : return true;
381 : }
382 :
383 1104578 : ZonePtrList<Statement>* body = function->body();
384 : DCHECK_IMPLIES(scope->is_module_scope(), !body->is_empty());
385 1104578 : if (!body->is_empty()) {
386 1086092 : Variable* result = scope->AsDeclarationScope()->NewTemporary(
387 2172185 : info->ast_value_factory()->dot_result_string());
388 : Processor processor(info->stack_limit(), scope->AsDeclarationScope(),
389 1086088 : result, info->ast_value_factory());
390 1086083 : processor.Process(body);
391 :
392 : DCHECK_IMPLIES(scope->is_module_scope(), processor.result_assigned());
393 1086078 : if (processor.result_assigned()) {
394 : int pos = kNoSourcePosition;
395 : Expression* result_value =
396 1049155 : processor.factory()->NewVariableProxy(result, pos);
397 : Statement* result_statement =
398 2098309 : processor.factory()->NewReturnStatement(result_value, pos);
399 1049154 : body->Add(result_statement, info->zone());
400 : }
401 :
402 1086075 : if (processor.HasStackOverflow()) return false;
403 : }
404 :
405 : return true;
406 : }
407 :
408 : } // namespace internal
409 183867 : } // namespace v8
|