blob: 70b362fd7d8656b9fd8a137cfcb3cf5139190aa0 [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),
jkummerow@chromium.org8fa5bd92013-09-02 11:45:09 +000046 factory_(zone->isolate(), zone) {
47 InitializeAstVisitor(zone->isolate());
yangguo@chromium.orga6bbcc82012-12-21 12:35:02 +000048 }
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
danno@chromium.org1fd77d52013-06-07 16:01:45 +0000171void Processor::VisitForOfStatement(ForOfStatement* node) {
172 VisitIterationStatement(node);
173}
174
175
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000176void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000177 // Rewrite both try and catch blocks (reversed order).
178 bool set_after_catch = is_set_;
179 Visit(node->catch_block());
180 is_set_ = is_set_ && set_after_catch;
181 bool save = in_try_;
182 in_try_ = true;
183 Visit(node->try_block());
184 in_try_ = save;
185}
186
187
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000188void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000189 // Rewrite both try and finally block (reversed order).
190 Visit(node->finally_block());
191 bool save = in_try_;
192 in_try_ = true;
193 Visit(node->try_block());
194 in_try_ = save;
195}
196
197
198void Processor::VisitSwitchStatement(SwitchStatement* node) {
199 // Rewrite statements in all case clauses in reversed order.
200 ZoneList<CaseClause*>* clauses = node->cases();
201 bool set_after_switch = is_set_;
202 for (int i = clauses->length() - 1; i >= 0; --i) {
203 CaseClause* clause = clauses->at(i);
204 Process(clause->statements());
205 }
206 is_set_ = is_set_ && set_after_switch;
207}
208
209
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000210void Processor::VisitCaseClause(CaseClause* clause) {
211 UNREACHABLE();
212}
213
214
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000215void Processor::VisitContinueStatement(ContinueStatement* node) {
216 is_set_ = false;
217}
218
219
220void Processor::VisitBreakStatement(BreakStatement* node) {
221 is_set_ = false;
222}
223
224
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000225void Processor::VisitWithStatement(WithStatement* node) {
226 bool set_after_body = is_set_;
227 Visit(node->statement());
228 is_set_ = is_set_ && set_after_body;
229}
230
231
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000232// Do nothing:
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000233void Processor::VisitVariableDeclaration(VariableDeclaration* node) {}
ulan@chromium.org812308e2012-02-29 15:58:45 +0000234void Processor::VisitFunctionDeclaration(FunctionDeclaration* node) {}
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000235void Processor::VisitModuleDeclaration(ModuleDeclaration* node) {}
ulan@chromium.org812308e2012-02-29 15:58:45 +0000236void Processor::VisitImportDeclaration(ImportDeclaration* node) {}
237void Processor::VisitExportDeclaration(ExportDeclaration* node) {}
yangguo@chromium.org78d1ad42012-02-09 13:53:47 +0000238void Processor::VisitModuleLiteral(ModuleLiteral* node) {}
239void Processor::VisitModuleVariable(ModuleVariable* node) {}
240void Processor::VisitModulePath(ModulePath* node) {}
241void Processor::VisitModuleUrl(ModuleUrl* node) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000242void Processor::VisitEmptyStatement(EmptyStatement* node) {}
243void Processor::VisitReturnStatement(ReturnStatement* node) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000244void Processor::VisitDebuggerStatement(DebuggerStatement* node) {}
245
246
247// Expressions are never visited yet.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000248#define DEF_VISIT(type) \
249 void Processor::Visit##type(type* expr) { UNREACHABLE(); }
250EXPRESSION_NODE_LIST(DEF_VISIT)
251#undef DEF_VISIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000252
253
yangguo@chromium.org355cfd12012-08-29 15:32:24 +0000254// Assumes code has been parsed. Mutates the AST, so the AST should not
255// continue to be used in the case of failure.
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000256bool Rewriter::Rewrite(CompilationInfo* info) {
257 FunctionLiteral* function = info->function();
258 ASSERT(function != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000259 Scope* scope = function->scope();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000260 ASSERT(scope != NULL);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000261 if (!scope->is_global_scope() && !scope->is_eval_scope()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000262
263 ZoneList<Statement*>* body = function->body();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000264 if (!body->is_empty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000265 Variable* result = scope->NewTemporary(
yangguo@chromium.org4a9f6552013-03-04 14:46:33 +0000266 info->isolate()->factory()->result_string());
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000267 Processor processor(result, info->zone());
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000268 processor.Process(body);
269 if (processor.HasStackOverflow()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000271 if (processor.result_assigned()) {
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000272 ASSERT(function->end_position() != RelocInfo::kNoPosition);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000273 // Set the position of the assignment statement one character past the
274 // source code, such that it definitely is not in the source code range
275 // of an immediate inner scope. For example in
276 // eval('with ({x:1}) x = 1');
277 // the end position of the function generated for executing the eval code
278 // coincides with the end of the with scope which is the position of '1'.
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000279 int pos = function->end_position();
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +0000280 VariableProxy* result_proxy = processor.factory()->NewVariableProxy(
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000281 result->name(), false, result->interface(), pos);
mstarzinger@chromium.org1b3afd12011-11-29 14:28:56 +0000282 result_proxy->BindTo(result);
svenpanne@chromium.orgb1df11d2012-02-08 10:26:21 +0000283 Statement* result_statement =
mstarzinger@chromium.orga2e1a402013-10-15 08:25:05 +0000284 processor.factory()->NewReturnStatement(result_proxy, pos);
yangguo@chromium.org5a11aaf2012-06-20 11:29:00 +0000285 body->Add(result_statement, info->zone());
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000286 }
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000287 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000288
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000289 return true;
290}
291
292
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293} } // namespace v8::internal