blob: ad6ce056b2c85228a3772af4a3daa3a444f66b79 [file] [log] [blame]
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00001// Copyright 2011 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:
kasperl@chromium.orga5551262010-12-07 12:49:48 +000041 explicit Processor(Variable* result)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000042 : result_(result),
43 result_assigned_(false),
44 is_set_(false),
45 in_try_(false) {
46 }
47
48 void Process(ZoneList<Statement*>* statements);
whesse@chromium.org4a1fe7d2010-09-27 12:32:04 +000049 bool result_assigned() const { return result_assigned_; }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000050
51 private:
kasperl@chromium.orga5551262010-12-07 12:49:48 +000052 Variable* result_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000053
54 // We are not tracking result usage via the result_'s use
55 // counts (we leave the accurate computation to the
56 // usage analyzer). Instead we simple remember if
57 // there was ever an assignment to result_.
58 bool result_assigned_;
59
60 // To avoid storing to .result all the time, we eliminate some of
61 // the stores by keeping track of whether or not we're sure .result
62 // will be overwritten anyway. This is a bit more tricky than what I
63 // was hoping for
64 bool is_set_;
65 bool in_try_;
66
67 Expression* SetResult(Expression* value) {
68 result_assigned_ = true;
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000069 Zone* zone = isolate()->zone();
rossberg@chromium.org717967f2011-07-20 13:44:42 +000070 VariableProxy* result_proxy = new(zone) VariableProxy(isolate(), result_);
71 return new(zone) Assignment(isolate(),
72 Token::ASSIGN,
73 result_proxy,
74 value,
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +000075 RelocInfo::kNoPosition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000076 }
77
78 // Node visitors.
79#define DEF_VISIT(type) \
80 virtual void Visit##type(type* node);
sgjesse@chromium.org0b6db592009-07-30 14:48:31 +000081 AST_NODE_LIST(DEF_VISIT)
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000082#undef DEF_VISIT
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000083
84 void VisitIterationStatement(IterationStatement* stmt);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000085};
86
87
88void Processor::Process(ZoneList<Statement*>* statements) {
89 for (int i = statements->length() - 1; i >= 0; --i) {
90 Visit(statements->at(i));
91 }
92}
93
94
95void Processor::VisitBlock(Block* node) {
96 // An initializer block is the rewritten form of a variable declaration
97 // with initialization expressions. The initializer block contains the
98 // list of assignments corresponding to the initialization expressions.
99 // While unclear from the spec (ECMA-262, 3rd., 12.2), the value of
100 // a variable declaration with initialization expression is 'undefined'
101 // with some JS VMs: For instance, using smjs, print(eval('var x = 7'))
102 // returns 'undefined'. To obtain the same behavior with v8, we need
103 // to prevent rewriting in that case.
104 if (!node->is_initializer_block()) Process(node->statements());
105}
106
107
108void Processor::VisitExpressionStatement(ExpressionStatement* node) {
109 // Rewrite : <x>; -> .result = <x>;
110 if (!is_set_) {
111 node->set_expression(SetResult(node->expression()));
112 if (!in_try_) is_set_ = true;
113 }
114}
115
116
117void Processor::VisitIfStatement(IfStatement* node) {
118 // Rewrite both then and else parts (reversed).
119 bool save = is_set_;
120 Visit(node->else_statement());
121 bool set_after_then = is_set_;
122 is_set_ = save;
123 Visit(node->then_statement());
124 is_set_ = is_set_ && set_after_then;
125}
126
127
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000128void Processor::VisitIterationStatement(IterationStatement* node) {
129 // Rewrite the body.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000130 bool set_after_loop = is_set_;
131 Visit(node->body());
132 is_set_ = is_set_ && set_after_loop;
133}
134
135
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000136void Processor::VisitDoWhileStatement(DoWhileStatement* node) {
137 VisitIterationStatement(node);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000138}
139
140
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000141void Processor::VisitWhileStatement(WhileStatement* node) {
142 VisitIterationStatement(node);
143}
144
145
146void Processor::VisitForStatement(ForStatement* node) {
147 VisitIterationStatement(node);
148}
149
150
151void Processor::VisitForInStatement(ForInStatement* node) {
152 VisitIterationStatement(node);
153}
154
155
156void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000157 // Rewrite both try and catch blocks (reversed order).
158 bool set_after_catch = is_set_;
159 Visit(node->catch_block());
160 is_set_ = is_set_ && set_after_catch;
161 bool save = in_try_;
162 in_try_ = true;
163 Visit(node->try_block());
164 in_try_ = save;
165}
166
167
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000168void Processor::VisitTryFinallyStatement(TryFinallyStatement* node) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000169 // Rewrite both try and finally block (reversed order).
170 Visit(node->finally_block());
171 bool save = in_try_;
172 in_try_ = true;
173 Visit(node->try_block());
174 in_try_ = save;
175}
176
177
178void Processor::VisitSwitchStatement(SwitchStatement* node) {
179 // Rewrite statements in all case clauses in reversed order.
180 ZoneList<CaseClause*>* clauses = node->cases();
181 bool set_after_switch = is_set_;
182 for (int i = clauses->length() - 1; i >= 0; --i) {
183 CaseClause* clause = clauses->at(i);
184 Process(clause->statements());
185 }
186 is_set_ = is_set_ && set_after_switch;
187}
188
189
190void Processor::VisitContinueStatement(ContinueStatement* node) {
191 is_set_ = false;
192}
193
194
195void Processor::VisitBreakStatement(BreakStatement* node) {
196 is_set_ = false;
197}
198
199
whesse@chromium.org4acdc2c2011-08-15 13:01:23 +0000200void Processor::VisitWithStatement(WithStatement* node) {
201 bool set_after_body = is_set_;
202 Visit(node->statement());
203 is_set_ = is_set_ && set_after_body;
204}
205
206
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000207// Do nothing:
208void Processor::VisitDeclaration(Declaration* node) {}
209void Processor::VisitEmptyStatement(EmptyStatement* node) {}
210void Processor::VisitReturnStatement(ReturnStatement* node) {}
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000211void Processor::VisitExitContextStatement(ExitContextStatement* node) {}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000212void Processor::VisitDebuggerStatement(DebuggerStatement* node) {}
213
214
215// Expressions are never visited yet.
svenpanne@chromium.org6d786c92011-06-15 10:58:27 +0000216#define DEF_VISIT(type) \
217 void Processor::Visit##type(type* expr) { UNREACHABLE(); }
218EXPRESSION_NODE_LIST(DEF_VISIT)
219#undef DEF_VISIT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000220
221
sgjesse@chromium.orgc6c57182011-01-17 12:24:25 +0000222// Assumes code has been parsed and scopes have been analyzed. Mutates the
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000223// AST, so the AST should not continue to be used in the case of failure.
224bool Rewriter::Rewrite(CompilationInfo* info) {
225 FunctionLiteral* function = info->function();
226 ASSERT(function != NULL);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000227 Scope* scope = function->scope();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000228 ASSERT(scope != NULL);
ricow@chromium.org4f693d62011-07-04 14:01:31 +0000229 if (!scope->is_global_scope() && !scope->is_eval_scope()) return true;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000230
231 ZoneList<Statement*>* body = function->body();
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000232 if (!body->is_empty()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000233 Variable* result = scope->NewTemporary(
234 info->isolate()->factory()->result_symbol());
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000235 Processor processor(result);
236 processor.Process(body);
237 if (processor.HasStackOverflow()) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000238
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000239 if (processor.result_assigned()) {
rossberg@chromium.org717967f2011-07-20 13:44:42 +0000240 Isolate* isolate = info->isolate();
241 Zone* zone = isolate->zone();
242 VariableProxy* result_proxy = new(zone) VariableProxy(isolate, result);
svenpanne@chromium.org84bcc552011-07-18 09:50:57 +0000243 body->Add(new(zone) ReturnStatement(result_proxy));
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000244 }
ager@chromium.orgb61a0d12010-10-13 08:35:23 +0000245 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000246
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000247 return true;
248}
249
250
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000251} } // namespace v8::internal