blob: 7ae8dcfa386d038b929306129e1df66c7ff17eef [file] [log] [blame]
Steve Blockd0582a62009-12-15 09:54:21 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
2// 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 <stdlib.h>
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -080029#include <stdio.h>
Steve Blockd0582a62009-12-15 09:54:21 +000030
31#include "v8.h"
32
33#include "token.h"
34#include "scanner.h"
Iain Merrick9ac36c92010-09-13 15:29:50 +010035#include "parser.h"
Steve Blockd0582a62009-12-15 09:54:21 +000036#include "utils.h"
Iain Merrick9ac36c92010-09-13 15:29:50 +010037#include "execution.h"
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -080038#include "scanner.h"
39#include "preparser.h"
Steve Blockd0582a62009-12-15 09:54:21 +000040#include "cctest.h"
41
42namespace i = ::v8::internal;
43
44TEST(KeywordMatcher) {
45 struct KeywordToken {
46 const char* keyword;
47 i::Token::Value token;
48 };
49
50 static const KeywordToken keywords[] = {
51#define KEYWORD(t, s, d) { s, i::Token::t },
52#define IGNORE(t, s, d) /* */
53 TOKEN_LIST(IGNORE, KEYWORD, IGNORE)
54#undef KEYWORD
55 { NULL, i::Token::IDENTIFIER }
56 };
57
58 static const char* future_keywords[] = {
59#define FUTURE(t, s, d) s,
60 TOKEN_LIST(IGNORE, IGNORE, FUTURE)
61#undef FUTURE
62#undef IGNORE
63 NULL
64 };
65
66 KeywordToken key_token;
67 for (int i = 0; (key_token = keywords[i]).keyword != NULL; i++) {
68 i::KeywordMatcher matcher;
69 const char* keyword = key_token.keyword;
70 int length = i::StrLength(keyword);
71 for (int j = 0; j < length; j++) {
72 if (key_token.token == i::Token::INSTANCEOF && j == 2) {
73 // "in" is a prefix of "instanceof". It's the only keyword
74 // that is a prefix of another.
75 CHECK_EQ(i::Token::IN, matcher.token());
76 } else {
77 CHECK_EQ(i::Token::IDENTIFIER, matcher.token());
78 }
79 matcher.AddChar(keyword[j]);
80 }
81 CHECK_EQ(key_token.token, matcher.token());
82 // Adding more characters will make keyword matching fail.
83 matcher.AddChar('z');
84 CHECK_EQ(i::Token::IDENTIFIER, matcher.token());
85 // Adding a keyword later will not make it match again.
86 matcher.AddChar('i');
87 matcher.AddChar('f');
88 CHECK_EQ(i::Token::IDENTIFIER, matcher.token());
89 }
90
91 // Future keywords are not recognized.
92 const char* future_keyword;
93 for (int i = 0; (future_keyword = future_keywords[i]) != NULL; i++) {
94 i::KeywordMatcher matcher;
95 int length = i::StrLength(future_keyword);
96 for (int j = 0; j < length; j++) {
97 matcher.AddChar(future_keyword[j]);
98 }
99 CHECK_EQ(i::Token::IDENTIFIER, matcher.token());
100 }
101
102 // Zero isn't ignored at first.
103 i::KeywordMatcher bad_start;
104 bad_start.AddChar(0);
105 CHECK_EQ(i::Token::IDENTIFIER, bad_start.token());
106 bad_start.AddChar('i');
107 bad_start.AddChar('f');
108 CHECK_EQ(i::Token::IDENTIFIER, bad_start.token());
109
110 // Zero isn't ignored at end.
111 i::KeywordMatcher bad_end;
112 bad_end.AddChar('i');
113 bad_end.AddChar('f');
114 CHECK_EQ(i::Token::IF, bad_end.token());
115 bad_end.AddChar(0);
116 CHECK_EQ(i::Token::IDENTIFIER, bad_end.token());
117
118 // Case isn't ignored.
119 i::KeywordMatcher bad_case;
120 bad_case.AddChar('i');
121 bad_case.AddChar('F');
122 CHECK_EQ(i::Token::IDENTIFIER, bad_case.token());
123
124 // If we mark it as failure, continuing won't help.
125 i::KeywordMatcher full_stop;
126 full_stop.AddChar('i');
127 CHECK_EQ(i::Token::IDENTIFIER, full_stop.token());
128 full_stop.Fail();
129 CHECK_EQ(i::Token::IDENTIFIER, full_stop.token());
130 full_stop.AddChar('f');
131 CHECK_EQ(i::Token::IDENTIFIER, full_stop.token());
132}
133
Iain Merrick9ac36c92010-09-13 15:29:50 +0100134
135TEST(ScanHTMLEndComments) {
136 // Regression test. See:
137 // http://code.google.com/p/chromium/issues/detail?id=53548
138 // Tests that --> is correctly interpreted as comment-to-end-of-line if there
139 // is only whitespace before it on the line, even after a multiline-comment
140 // comment. This was not the case if it occurred before the first real token
141 // in the input.
142 const char* tests[] = {
143 // Before first real token.
144 "--> is eol-comment\nvar y = 37;\n",
145 "\n --> is eol-comment\nvar y = 37;\n",
146 "/* precomment */ --> is eol-comment\nvar y = 37;\n",
147 "\n/* precomment */ --> is eol-comment\nvar y = 37;\n",
148 // After first real token.
149 "var x = 42;\n--> is eol-comment\nvar y = 37;\n",
150 "var x = 42;\n/* precomment */ --> is eol-comment\nvar y = 37;\n",
151 NULL
152 };
153
154 // Parser/Scanner needs a stack limit.
155 int marker;
156 i::StackGuard::SetStackLimit(
157 reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
158
159 for (int i = 0; tests[i]; i++) {
160 v8::ScriptData* data =
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100161 v8::ScriptData::PreCompile(tests[i], i::StrLength(tests[i]));
Iain Merrick9ac36c92010-09-13 15:29:50 +0100162 CHECK(data != NULL && !data->HasError());
163 delete data;
164 }
165}
166
167
168class ScriptResource : public v8::String::ExternalAsciiStringResource {
169 public:
170 ScriptResource(const char* data, size_t length)
171 : data_(data), length_(length) { }
172
173 const char* data() const { return data_; }
174 size_t length() const { return length_; }
175
176 private:
177 const char* data_;
178 size_t length_;
179};
180
181
182TEST(Preparsing) {
183 v8::HandleScope handles;
184 v8::Persistent<v8::Context> context = v8::Context::New();
185 v8::Context::Scope context_scope(context);
186 int marker;
187 i::StackGuard::SetStackLimit(
188 reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
189
190 // Source containing functions that might be lazily compiled and all types
191 // of symbols (string, propertyName, regexp).
192 const char* source =
193 "var x = 42;"
194 "function foo(a) { return function nolazy(b) { return a + b; } }"
195 "function bar(a) { if (a) return function lazy(b) { return b; } }"
196 "var z = {'string': 'string literal', bareword: 'propertyName', "
197 " 42: 'number literal', for: 'keyword as propertyName', "
198 " f\\u006fr: 'keyword propertyname with escape'};"
199 "var v = /RegExp Literal/;"
200 "var w = /RegExp Literal\\u0020With Escape/gin;"
201 "var y = { get getter() { return 42; }, "
202 " set setter(v) { this.value = v; }};";
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100203 int source_length = i::StrLength(source);
Iain Merrick9ac36c92010-09-13 15:29:50 +0100204 const char* error_source = "var x = y z;";
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100205 int error_source_length = i::StrLength(error_source);
Iain Merrick9ac36c92010-09-13 15:29:50 +0100206
207 v8::ScriptData* preparse =
208 v8::ScriptData::PreCompile(source, source_length);
209 CHECK(!preparse->HasError());
210 bool lazy_flag = i::FLAG_lazy;
211 {
212 i::FLAG_lazy = true;
213 ScriptResource* resource = new ScriptResource(source, source_length);
214 v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
215 v8::Script::Compile(script_source, NULL, preparse);
216 }
217
218 {
219 i::FLAG_lazy = false;
220
221 ScriptResource* resource = new ScriptResource(source, source_length);
222 v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
223 v8::Script::New(script_source, NULL, preparse, v8::Local<v8::String>());
224 }
225 delete preparse;
226 i::FLAG_lazy = lazy_flag;
227
228 // Syntax error.
229 v8::ScriptData* error_preparse =
230 v8::ScriptData::PreCompile(error_source, error_source_length);
231 CHECK(error_preparse->HasError());
232 i::ScriptDataImpl *pre_impl =
233 reinterpret_cast<i::ScriptDataImpl*>(error_preparse);
234 i::Scanner::Location error_location =
235 pre_impl->MessageLocation();
236 // Error is at "z" in source, location 10..11.
237 CHECK_EQ(10, error_location.beg_pos);
238 CHECK_EQ(11, error_location.end_pos);
239 // Should not crash.
240 const char* message = pre_impl->BuildMessage();
241 i::Vector<const char*> args = pre_impl->BuildArgs();
242 CHECK_GT(strlen(message), 0);
243}
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800244
245
246TEST(StandAlonePreParser) {
247 int marker;
248 i::StackGuard::SetStackLimit(
249 reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
250
251 const char* programs[] = {
252 "{label: 42}",
253 "var x = 42;",
254 "function foo(x, y) { return x + y; }",
255 "native function foo(); return %ArgleBargle(glop);",
256 "var x = new new Function('this.x = 42');",
257 NULL
258 };
259
260 for (int i = 0; programs[i]; i++) {
261 const char* program = programs[i];
262 unibrow::Utf8InputBuffer<256> stream(program, strlen(program));
263 i::CompleteParserRecorder log;
264 i::Scanner scanner;
265 scanner.Initialize(i::Handle<i::String>::null(), &stream, i::JAVASCRIPT);
266 v8::preparser::PreParser<i::Scanner, i::CompleteParserRecorder> preparser;
267 bool result = preparser.PreParseProgram(&scanner, &log, true);
268 CHECK(result);
269 i::ScriptDataImpl data(log.ExtractData());
270 CHECK(!data.has_error());
271 }
272}