blob: 44fe0504e586a6fadff6f12e065802f5433d74d1 [file] [log] [blame]
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +00001// Copyright 2012 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000030#include "rewriter.h"
31
ager@chromium.orgb61a0d12010-10-13 08:35:23 +000032#include "ast.h"
33#include "compiler.h"
34#include "scopes.h"
35
kasperl@chromium.org71affb52009-05-26 05:44:31 +000036namespace v8 {
37namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000038
ager@chromium.orga74f0da2008-12-03 16:05:52 +000039class Processor: public AstVisitor {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040 public:
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +000041 Processor(Variable* result, Zone* zone)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042 : result_(result),
43 result_assigned_(false),
44 is_set_(false),
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +000045 in_try_(false),
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000046 factory_(Isolate::Current(), zone) {
47 InitializeAstVisitor();
48 }
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +000049
50 virtual ~Processor() { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000051
52 void Process(ZoneList<Statement*>* statements);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000053 bool result_assigned() const { return result_assigned_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000054
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +000055 AstNodeFactory<AstNullVisitor>* factory() {
56 return &factory_;
57 }
58
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000059 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +000060 Variable* result_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000061
62 // We are not tracking result usage via the result_'s use
63 // counts (we leave the accurate computation to the
64 // usage analyzer). Instead we simple remember if
65 // there was ever an assignment to result_.
66 bool result_assigned_;
67
68 // To avoid storing to .result all the time, we eliminate some of
69 // the stores by keeping track of whether or not we're sure .result
70 // will be overwritten anyway. This is a bit more tricky than what I
71 // was hoping for
72 bool is_set_;
73 bool in_try_;
74
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +000075 AstNodeFactory<AstNullVisitor> factory_;
76
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000077 Expression* SetResult(Expression* value) {
78 result_assigned_ = true;
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +000079 VariableProxy* result_proxy = factory()->NewVariableProxy(result_);
80 return factory()->NewAssignment(
81 Token::ASSIGN, result_proxy, value, RelocInfo::kNoPosition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082 }
83
84 // Node visitors.
85#define DEF_VISIT(type) \
86 virtual void Visit##type(type* node);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000087 AST_NODE_LIST(DEF_VISIT)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000088#undef DEF_VISIT
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000089
90 void VisitIterationStatement(IterationStatement* stmt);
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000091
92 DEFINE_AST_VISITOR_SUBCLASS_MEMBERS();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000093};
94
95
96void Processor::Process(ZoneList<Statement*>* statements) {
97 for (int i = statements->length() - 1; i >= 0; --i) {
98 Visit(statements->at(i));
99 }
100}
101
102
103void Processor::VisitBlock(Block* node) {
104 // An initializer block is the rewritten form of a variable declaration
105 // with initialization expressions. The initializer block contains the
106 // list of assignments corresponding to the initialization expressions.
107 // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of
108 // a variable declaration with initialization expression is 'undefined'
109 // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
110 // returns 'undefined'. To obtain the same behavior with v8, we need
111 // to prevent rewriting in that case.
112 if (!node->is_initializer_block()) Process(node->statements());
113}
114
115
ulan@chromium.org8e8d8822012-11-23 14:36:46 +0000116void Processor::VisitModuleStatement(ModuleStatement* node) {
117 bool set_after_body = is_set_;
118 Visit(node->body());
119 is_set_ = is_set_ && set_after_body;
120}
121
122
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000123void Processor::VisitExpressionStatement(ExpressionStatement* node) {
124 // Rewrite : <x>; -> .result = <x>;
fschneider@chromium.org7d10be52012-04-10 12:30:14 +0000125 if (!is_set_ && !node->expression()->IsThrow()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000126 node->set_expression(SetResult(node->expression()));
127 if (!in_try_) is_set_ = true;
128 }
129}
130
131
132void Processor::VisitIfStatement(IfStatement* node) {
133 // Rewrite both then and else parts (reversed).
134 bool save = is_set_;
135 Visit(node->else_statement());
136 bool set_after_then = is_set_;
137 is_set_ = save;
138 Visit(node->then_statement());
139 is_set_ = is_set_ && set_after_then;
140}
141
142
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000143void Processor::VisitIterationStatement(IterationStatement* node) {
144 // Rewrite the body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000145 bool set_after_loop = is_set_;
146 Visit(node->body());
147 is_set_ = is_set_ && set_after_loop;
148}
149
150
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000151void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
152 VisitIterationStatement(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000153}
154
155
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000156void Processor::VisitWhileStatement(WhileStatement* node) {
157 VisitIterationStatement(node);
158}
159
160
161void Processor::VisitForStatement(ForStatement* node) {
162 VisitIterationStatement(node);
163}
164
165
166void Processor::VisitForInStatement(ForInStatement* node) {
167 VisitIterationStatement(node);
168}
169
170
171void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000172 // Rewrite both try and catch blocks (reversed order).
173 bool set_after_catch = is_set_;
174 Visit(node->catch_block());
175 is_set_ = is_set_ && set_after_catch;
176 bool save = in_try_;
177 in_try_ = true;
178 Visit(node->try_block());
179 in_try_ = save;
180}
181
182
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000183void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000184 // Rewrite both try and finally block (reversed order).
185 Visit(node->finally_block());
186 bool save = in_try_;
187 in_try_ = true;
188 Visit(node->try_block());
189 in_try_ = save;
190}
191
192
193void Processor::VisitSwitchStatement(SwitchStatement* node) {
194 // Rewrite statements in all case clauses in reversed order.
195 ZoneList<CaseClause*>* clauses = node->cases();
196 bool set_after_switch = is_set_;
197 for (int i = clauses->length() - 1; i >= 0; --i) {
198 CaseClause* clause = clauses->at(i);
199 Process(clause->statements());
200 }
201 is_set_ = is_set_ && set_after_switch;
202}
203
204
205void Processor::VisitContinueStatement(ContinueStatement* node) {
206 is_set_ = false;
207}
208
209
210void Processor::VisitBreakStatement(BreakStatement* node) {
211 is_set_ = false;
212}
213
214
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000215void Processor::VisitWithStatement(WithStatement* node) {
216 bool set_after_body = is_set_;
217 Visit(node->statement());
218 is_set_ = is_set_ && set_after_body;
219}
220
221
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000222// Do nothing:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000223void Processor::VisitVariableDeclaration(VariableDeclaration* node) {}
ulan@chromium.org812308e2012-02-29 15:58:45 +0000224void Processor::VisitFunctionDeclaration(FunctionDeclaration* node) {}
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000225void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {}
ulan@chromium.org812308e2012-02-29 15:58:45 +0000226void Processor::VisitImportDeclaration(ImportDeclaration* node) {}
227void Processor::VisitExportDeclaration(ExportDeclaration* node) {}
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000228void Processor::VisitModuleLiteral(ModuleLiteral* node) {}
229void Processor::VisitModuleVariable(ModuleVariable* node) {}
230void Processor::VisitModulePath(ModulePath* node) {}
231void Processor::VisitModuleUrl(ModuleUrl* node) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000232void Processor::VisitEmptyStatement(EmptyStatement* node) {}
233void Processor::VisitReturnStatement(ReturnStatement* node) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000234void Processor::VisitDebuggerStatement(DebuggerStatement* node) {}
235
236
237// Expressions are never visited yet.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000238#define DEF_VISIT(type) \
239 void Processor::Visit##type(type* expr) { UNREACHABLE(); }
240EXPRESSION_NODE_LIST(DEF_VISIT)
241#undef DEF_VISIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000242
243
yangguo@chromium.org355cfd12012-08-29 15:32:24 +0000244// Assumes code has been parsed. Mutates the AST, so the AST should not
245// continue to be used in the case of failure.
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000246bool Rewriter::Rewrite(CompilationInfo* info) {
247 FunctionLiteral* function = info->function();
248 ASSERT(function != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000249 Scope* scope = function->scope();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000250 ASSERT(scope != NULL);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000251 if (!scope->is_global_scope() && !scope->is_eval_scope()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000252
253 ZoneList<Statement*>* body = function->body();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000254 if (!body->is_empty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000255 Variable* result = scope->NewTemporary(
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000256 info->isolate()->factory()->result_string());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000257 Processor processor(result, info->zone());
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000258 processor.Process(body);
259 if (processor.HasStackOverflow()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000260
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000261 if (processor.result_assigned()) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000262 ASSERT(function->end_position() != RelocInfo::kNoPosition);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000263 // Set the position of the assignment statement one character past the
264 // source code, such that it definitely is not in the source code range
265 // of an immediate inner scope. For example in
266 // eval('with ({x:1}) x = 1');
267 // the end position of the function generated for executing the eval code
268 // coincides with the end of the with scope which is the position of '1'.
269 int position = function->end_position();
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +0000270 VariableProxy* result_proxy = processor.factory()->NewVariableProxy(
ulan@chromium.org8e8d8822012-11-23 14:36:46 +0000271 result->name(), false, result->interface(), position);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000272 result_proxy->BindTo(result);
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +0000273 Statement* result_statement =
274 processor.factory()->NewReturnStatement(result_proxy);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000275 result_statement->set_statement_pos(position);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000276 body->Add(result_statement, info->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000277 }
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000278 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000279
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000280 return true;
281}
282
283
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000284} } // namespace v8::internal