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