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 : 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 1090765 : 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 1164893 : AstNodeFactory* factory() { return &factory_; }
53 :
54 : // Returns ".result = value"
55 1100425 : Expression* SetResult(Expression* value) {
56 1100425 : result_assigned_ = true;
57 1100425 : VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
58 : return factory()->NewAssignment(Token::ASSIGN, result_proxy, value,
59 1100425 : 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 192707 : : processor_(processor), previous_(processor->breakable_) {
76 192707 : processor->breakable_ = processor->breakable_ || breakable;
77 : }
78 :
79 192706 : ~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 9151891 : 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 64366 : Statement* Processor::AssignUndefinedBefore(Statement* s) {
116 : Expression* undef = factory()->NewUndefinedLiteral(kNoSourcePosition);
117 64366 : Expression* assignment = SetResult(undef);
118 64366 : Block* b = factory()->NewBlock(2, false);
119 128732 : b->statements()->Add(
120 64366 : factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
121 64366 : b->statements()->Add(s, zone());
122 64366 : return b;
123 : }
124 :
125 1223254 : 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 3829275 : for (int i = statements->length() - 1; i >= 0 && (breakable_ || !is_set_);
132 : --i) {
133 2606021 : Visit(statements->at(i));
134 : statements->Set(i, replacement_);
135 : }
136 1223254 : }
137 :
138 :
139 1560188 : 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 1560188 : if (!node->ignore_completion_value()) {
149 : BreakableScope scope(this, node->labels() != nullptr);
150 132189 : Process(node->statements());
151 : }
152 1560187 : replacement_ = node;
153 1560187 : }
154 :
155 :
156 0 : void Processor::VisitExpressionStatement(ExpressionStatement* node) {
157 : // Rewrite : <x>; -> .result = <x>;
158 1045554 : if (!is_set_) {
159 1036061 : node->set_expression(SetResult(node->expression()));
160 1036060 : is_set_ = true;
161 : }
162 1045553 : replacement_ = node;
163 0 : }
164 :
165 :
166 2668 : void Processor::VisitIfStatement(IfStatement* node) {
167 : // Rewrite both branches.
168 2668 : bool set_after = is_set_;
169 :
170 2668 : Visit(node->then_statement());
171 2668 : node->set_then_statement(replacement_);
172 2668 : bool set_in_then = is_set_;
173 :
174 2668 : is_set_ = set_after;
175 2668 : Visit(node->else_statement());
176 2668 : node->set_else_statement(replacement_);
177 :
178 2668 : replacement_ = set_in_then && is_set_ ? node : AssignUndefinedBefore(node);
179 2668 : is_set_ = true;
180 2668 : }
181 :
182 :
183 60284 : 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 60284 : Visit(node->body());
192 60284 : node->set_body(replacement_);
193 :
194 60284 : replacement_ = AssignUndefinedBefore(node);
195 60284 : is_set_ = true;
196 60284 : }
197 :
198 :
199 527 : void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
200 527 : VisitIterationStatement(node);
201 527 : }
202 :
203 :
204 732 : void Processor::VisitWhileStatement(WhileStatement* node) {
205 732 : VisitIterationStatement(node);
206 732 : }
207 :
208 :
209 50311 : void Processor::VisitForStatement(ForStatement* node) {
210 50311 : VisitIterationStatement(node);
211 50311 : }
212 :
213 :
214 4160 : void Processor::VisitForInStatement(ForInStatement* node) {
215 4160 : VisitIterationStatement(node);
216 4160 : }
217 :
218 :
219 4554 : void Processor::VisitForOfStatement(ForOfStatement* node) {
220 4554 : VisitIterationStatement(node);
221 4554 : }
222 :
223 :
224 4017 : void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
225 : // Rewrite both try and catch block.
226 4017 : bool set_after = is_set_;
227 :
228 4017 : Visit(node->try_block());
229 4017 : node->set_try_block(static_cast<Block*>(replacement_));
230 4017 : bool set_in_try = is_set_;
231 :
232 4017 : is_set_ = set_after;
233 4017 : Visit(node->catch_block());
234 4017 : node->set_catch_block(static_cast<Block*>(replacement_));
235 :
236 4017 : replacement_ = is_set_ && set_in_try ? node : AssignUndefinedBefore(node);
237 4017 : is_set_ = true;
238 4017 : }
239 :
240 :
241 298 : void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
242 : // Only rewrite finally if it could contain 'break' or 'continue'. Always
243 : // rewrite try.
244 298 : 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 102 : Variable* backup = closure_scope()->NewTemporary(
255 102 : factory()->ast_value_factory()->dot_result_string());
256 : 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 204 : node->finally_block()->statements()->InsertAt(
263 102 : 0, factory()->NewExpressionStatement(save, kNoSourcePosition), zone());
264 204 : node->finally_block()->statements()->Add(
265 102 : 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 298 : Visit(node->try_block());
271 298 : node->set_try_block(replacement_->AsBlock());
272 :
273 298 : replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
274 298 : is_set_ = true;
275 298 : }
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 : 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 247 : is_set_ = false;
299 247 : replacement_ = node;
300 0 : }
301 :
302 :
303 0 : void Processor::VisitBreakStatement(BreakStatement* node) {
304 1240 : is_set_ = false;
305 1240 : replacement_ = node;
306 0 : }
307 :
308 :
309 3492 : void Processor::VisitWithStatement(WithStatement* node) {
310 3492 : Visit(node->statement());
311 3492 : node->set_statement(replacement_);
312 :
313 3492 : replacement_ = is_set_ ? node : AssignUndefinedBefore(node);
314 3492 : is_set_ = true;
315 3492 : }
316 :
317 :
318 1736 : void Processor::VisitSloppyBlockFunctionStatement(
319 : 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 2985 : 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 2297 : 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 1785077 : 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 1785077 : : RuntimeCallCounterId::kCompileRewriteReturnResult);
372 :
373 : FunctionLiteral* function = info->literal();
374 : DCHECK_NOT_NULL(function);
375 : Scope* scope = function->scope();
376 : DCHECK_NOT_NULL(scope);
377 : DCHECK_EQ(scope, scope->GetClosureScope());
378 :
379 3331934 : if (!(scope->is_script_scope() || scope->is_eval_scope() ||
380 2469596 : scope->is_module_scope())) {
381 : return true;
382 : }
383 :
384 : ZonePtrList<Statement>* body = function->body();
385 : DCHECK_IMPLIES(scope->is_module_scope(), !body->is_empty());
386 1109457 : if (!body->is_empty()) {
387 1090765 : Variable* result = scope->AsDeclarationScope()->NewTemporary(
388 1090765 : info->ast_value_factory()->dot_result_string());
389 : Processor processor(info->stack_limit(), scope->AsDeclarationScope(),
390 1090765 : result, info->ast_value_factory());
391 1090765 : processor.Process(body);
392 :
393 : DCHECK_IMPLIES(scope->is_module_scope(), processor.result_assigned());
394 1090763 : if (processor.result_assigned()) {
395 : int pos = kNoSourcePosition;
396 : Expression* result_value =
397 : processor.factory()->NewVariableProxy(result, pos);
398 : Statement* result_statement =
399 1055282 : processor.factory()->NewReturnStatement(result_value, pos);
400 1055282 : body->Add(result_statement, info->zone());
401 : }
402 :
403 1090761 : if (processor.HasStackOverflow()) return false;
404 : }
405 :
406 : return true;
407 : }
408 :
409 : } // namespace internal
410 122036 : } // namespace v8
|