Line data Source code
1 : // Copyright 2017 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/asmjs/asm-scanner.h"
6 : #include "src/objects.h"
7 : #include "src/parsing/scanner-character-streams.h"
8 : #include "src/parsing/scanner.h"
9 : #include "testing/gtest/include/gtest/gtest.h"
10 :
11 : namespace v8 {
12 : namespace internal {
13 :
14 : #define TOK(t) AsmJsScanner::kToken_##t
15 :
16 48 : class AsmJsScannerTest : public ::testing::Test {
17 : protected:
18 16 : void SetupScanner(const char* source) {
19 32 : stream = ScannerStream::ForTesting(source);
20 16 : scanner.reset(new AsmJsScanner(stream.get()));
21 16 : }
22 :
23 109 : void Skip(AsmJsScanner::token_t t) {
24 109 : CHECK_EQ(t, scanner->Token());
25 109 : scanner->Next();
26 109 : }
27 :
28 3 : void SkipGlobal() {
29 3 : CHECK(scanner->IsGlobal());
30 3 : scanner->Next();
31 3 : }
32 :
33 1 : void SkipLocal() {
34 1 : CHECK(scanner->IsLocal());
35 1 : scanner->Next();
36 1 : }
37 :
38 26 : void CheckForEnd() { CHECK_EQ(scanner->Token(), AsmJsScanner::kEndOfInput); }
39 :
40 3 : void CheckForParseError() {
41 3 : CHECK_EQ(scanner->Token(), AsmJsScanner::kParseError);
42 3 : }
43 :
44 : std::unique_ptr<Utf16CharacterStream> stream;
45 : std::unique_ptr<AsmJsScanner> scanner;
46 : };
47 :
48 15188 : TEST_F(AsmJsScannerTest, SimpleFunction) {
49 1 : SetupScanner("function foo() { return; }");
50 1 : Skip(TOK(function));
51 : DCHECK_EQ("foo", scanner->GetIdentifierString());
52 1 : SkipGlobal();
53 1 : Skip('(');
54 1 : Skip(')');
55 1 : Skip('{');
56 : // clang-format off
57 1 : Skip(TOK(return));
58 : // clang-format on
59 1 : Skip(';');
60 1 : Skip('}');
61 1 : CheckForEnd();
62 1 : }
63 :
64 15188 : TEST_F(AsmJsScannerTest, JSKeywords) {
65 : SetupScanner(
66 : "arguments break case const continue\n"
67 : "default do else eval for function\n"
68 1 : "if new return switch var while\n");
69 1 : Skip(TOK(arguments));
70 1 : Skip(TOK(break));
71 1 : Skip(TOK(case));
72 1 : Skip(TOK(const));
73 1 : Skip(TOK(continue));
74 1 : Skip(TOK(default));
75 1 : Skip(TOK(do));
76 1 : Skip(TOK(else));
77 1 : Skip(TOK(eval));
78 1 : Skip(TOK(for));
79 1 : Skip(TOK(function));
80 1 : Skip(TOK(if));
81 1 : Skip(TOK(new));
82 : // clang-format off
83 1 : Skip(TOK(return));
84 : // clang-format on
85 1 : Skip(TOK(switch));
86 1 : Skip(TOK(var));
87 1 : Skip(TOK(while));
88 1 : CheckForEnd();
89 1 : }
90 :
91 15188 : TEST_F(AsmJsScannerTest, JSOperatorsSpread) {
92 : SetupScanner(
93 : "+ - * / % & | ^ ~ << >> >>>\n"
94 1 : "< > <= >= == !=\n");
95 1 : Skip('+');
96 1 : Skip('-');
97 1 : Skip('*');
98 1 : Skip('/');
99 1 : Skip('%');
100 1 : Skip('&');
101 1 : Skip('|');
102 1 : Skip('^');
103 1 : Skip('~');
104 1 : Skip(TOK(SHL));
105 1 : Skip(TOK(SAR));
106 1 : Skip(TOK(SHR));
107 1 : Skip('<');
108 1 : Skip('>');
109 1 : Skip(TOK(LE));
110 1 : Skip(TOK(GE));
111 1 : Skip(TOK(EQ));
112 1 : Skip(TOK(NE));
113 1 : CheckForEnd();
114 1 : }
115 :
116 15188 : TEST_F(AsmJsScannerTest, JSOperatorsTight) {
117 : SetupScanner(
118 : "+-*/%&|^~<<>> >>>\n"
119 1 : "<><=>= ==!=\n");
120 1 : Skip('+');
121 1 : Skip('-');
122 1 : Skip('*');
123 1 : Skip('/');
124 1 : Skip('%');
125 1 : Skip('&');
126 1 : Skip('|');
127 1 : Skip('^');
128 1 : Skip('~');
129 1 : Skip(TOK(SHL));
130 1 : Skip(TOK(SAR));
131 1 : Skip(TOK(SHR));
132 1 : Skip('<');
133 1 : Skip('>');
134 1 : Skip(TOK(LE));
135 1 : Skip(TOK(GE));
136 1 : Skip(TOK(EQ));
137 1 : Skip(TOK(NE));
138 1 : CheckForEnd();
139 1 : }
140 :
141 15188 : TEST_F(AsmJsScannerTest, UsesOfAsm) {
142 1 : SetupScanner("'use asm' \"use asm\"\n");
143 1 : Skip(TOK(UseAsm));
144 1 : Skip(TOK(UseAsm));
145 1 : CheckForEnd();
146 1 : }
147 :
148 15188 : TEST_F(AsmJsScannerTest, DefaultGlobalScope) {
149 1 : SetupScanner("var x = x + x;");
150 1 : Skip(TOK(var));
151 2 : CHECK_EQ("x", scanner->GetIdentifierString());
152 1 : AsmJsScanner::token_t x = scanner->Token();
153 1 : SkipGlobal();
154 1 : Skip('=');
155 1 : Skip(x);
156 1 : Skip('+');
157 1 : Skip(x);
158 1 : Skip(';');
159 1 : CheckForEnd();
160 1 : }
161 :
162 15188 : TEST_F(AsmJsScannerTest, GlobalScope) {
163 1 : SetupScanner("var x = x + x;");
164 : scanner->EnterGlobalScope();
165 1 : Skip(TOK(var));
166 2 : CHECK_EQ("x", scanner->GetIdentifierString());
167 1 : AsmJsScanner::token_t x = scanner->Token();
168 1 : SkipGlobal();
169 1 : Skip('=');
170 1 : Skip(x);
171 1 : Skip('+');
172 1 : Skip(x);
173 1 : Skip(';');
174 1 : CheckForEnd();
175 1 : }
176 :
177 15188 : TEST_F(AsmJsScannerTest, LocalScope) {
178 1 : SetupScanner("var x = x + x;");
179 : scanner->EnterLocalScope();
180 1 : Skip(TOK(var));
181 2 : CHECK_EQ("x", scanner->GetIdentifierString());
182 1 : AsmJsScanner::token_t x = scanner->Token();
183 1 : SkipLocal();
184 1 : Skip('=');
185 1 : Skip(x);
186 1 : Skip('+');
187 1 : Skip(x);
188 1 : Skip(';');
189 1 : CheckForEnd();
190 1 : }
191 :
192 15188 : TEST_F(AsmJsScannerTest, Numbers) {
193 1 : SetupScanner("1 1.2 0x1F 1.e3");
194 :
195 1 : CHECK(scanner->IsUnsigned());
196 1 : CHECK_EQ(1, scanner->AsUnsigned());
197 1 : scanner->Next();
198 :
199 1 : CHECK(scanner->IsDouble());
200 1 : CHECK_EQ(1.2, scanner->AsDouble());
201 1 : scanner->Next();
202 :
203 1 : CHECK(scanner->IsUnsigned());
204 1 : CHECK_EQ(31, scanner->AsUnsigned());
205 1 : scanner->Next();
206 :
207 1 : CHECK(scanner->IsDouble());
208 1 : CHECK_EQ(1.0e3, scanner->AsDouble());
209 1 : scanner->Next();
210 :
211 1 : CheckForEnd();
212 1 : }
213 :
214 15188 : TEST_F(AsmJsScannerTest, UnsignedNumbers) {
215 1 : SetupScanner("0x7FFFFFFF 0x80000000 0xFFFFFFFF 0x100000000");
216 :
217 1 : CHECK(scanner->IsUnsigned());
218 1 : CHECK_EQ(0x7FFFFFFF, scanner->AsUnsigned());
219 1 : scanner->Next();
220 :
221 1 : CHECK(scanner->IsUnsigned());
222 1 : CHECK_EQ(0x80000000, scanner->AsUnsigned());
223 1 : scanner->Next();
224 :
225 1 : CHECK(scanner->IsUnsigned());
226 1 : CHECK_EQ(0xFFFFFFFF, scanner->AsUnsigned());
227 1 : scanner->Next();
228 :
229 : // Numeric "unsigned" literals with a payload of more than 32-bit are rejected
230 : // by asm.js in all contexts, we hence consider `0x100000000` to be an error.
231 1 : CheckForParseError();
232 1 : }
233 :
234 15188 : TEST_F(AsmJsScannerTest, BadNumber) {
235 1 : SetupScanner(".123fe");
236 1 : Skip('.');
237 1 : CheckForParseError();
238 1 : }
239 :
240 15188 : TEST_F(AsmJsScannerTest, Rewind1) {
241 1 : SetupScanner("+ - * /");
242 1 : Skip('+');
243 1 : scanner->Rewind();
244 1 : Skip('+');
245 1 : Skip('-');
246 1 : scanner->Rewind();
247 1 : Skip('-');
248 1 : Skip('*');
249 1 : scanner->Rewind();
250 1 : Skip('*');
251 1 : Skip('/');
252 1 : scanner->Rewind();
253 1 : Skip('/');
254 1 : CheckForEnd();
255 1 : }
256 :
257 15188 : TEST_F(AsmJsScannerTest, Comments) {
258 : SetupScanner(
259 : "var // This is a test /* */ eval\n"
260 : "var /* test *** test */ eval\n"
261 1 : "function /* this */ ^");
262 1 : Skip(TOK(var));
263 1 : Skip(TOK(var));
264 1 : Skip(TOK(eval));
265 1 : Skip(TOK(function));
266 1 : Skip('^');
267 1 : CheckForEnd();
268 1 : }
269 :
270 15188 : TEST_F(AsmJsScannerTest, TrailingCComment) {
271 1 : SetupScanner("var /* test\n");
272 1 : Skip(TOK(var));
273 1 : CheckForParseError();
274 1 : }
275 :
276 15188 : TEST_F(AsmJsScannerTest, Seeking) {
277 1 : SetupScanner("var eval do arguments function break\n");
278 1 : Skip(TOK(var));
279 1 : size_t old_pos = scanner->Position();
280 1 : Skip(TOK(eval));
281 1 : Skip(TOK(do));
282 1 : Skip(TOK(arguments));
283 1 : scanner->Rewind();
284 1 : Skip(TOK(arguments));
285 1 : scanner->Rewind();
286 1 : scanner->Seek(old_pos);
287 1 : Skip(TOK(eval));
288 1 : Skip(TOK(do));
289 1 : Skip(TOK(arguments));
290 1 : Skip(TOK(function));
291 1 : Skip(TOK(break));
292 1 : CheckForEnd();
293 1 : }
294 :
295 15188 : TEST_F(AsmJsScannerTest, Newlines) {
296 : SetupScanner(
297 : "var x = 1\n"
298 1 : "var y = 2\n");
299 1 : Skip(TOK(var));
300 1 : scanner->Next();
301 1 : Skip('=');
302 1 : scanner->Next();
303 1 : CHECK(scanner->IsPrecededByNewline());
304 1 : Skip(TOK(var));
305 1 : scanner->Next();
306 1 : Skip('=');
307 1 : scanner->Next();
308 1 : CHECK(scanner->IsPrecededByNewline());
309 1 : CheckForEnd();
310 1 : }
311 :
312 : } // namespace internal
313 9111 : } // namespace v8
|