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 :
7 : #include "src/char-predicates-inl.h"
8 : #include "src/conversions.h"
9 : #include "src/flags.h"
10 : #include "src/parsing/scanner.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 :
15 : namespace {
16 : // Cap number of identifiers to ensure we can assign both global and
17 : // local ones a token id in the range of an int32_t.
18 : static const int kMaxIdentifierCount = 0xF000000;
19 : } // namespace
20 :
21 3778 : AsmJsScanner::AsmJsScanner(Utf16CharacterStream* stream)
22 : : stream_(stream),
23 : token_(kUninitialized),
24 : preceding_token_(kUninitialized),
25 : next_token_(kUninitialized),
26 : position_(0),
27 : preceding_position_(0),
28 : next_position_(0),
29 : rewind_(false),
30 : in_local_scope_(false),
31 : global_count_(0),
32 : double_value_(0.0),
33 : unsigned_value_(0),
34 11334 : preceded_by_newline_(false) {
35 : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
36 147342 : STDLIB_MATH_FUNCTION_LIST(V)
37 64224 : STDLIB_ARRAY_TYPE_LIST(V)
38 : #undef V
39 : #define V(name, _junk1) property_names_[#name] = kToken_##name;
40 64226 : STDLIB_MATH_VALUE_LIST(V)
41 : #undef V
42 : #define V(name) property_names_[#name] = kToken_##name;
43 26446 : STDLIB_OTHER_LIST(V)
44 : #undef V
45 : #define V(name) global_names_[#name] = kToken_##name;
46 132228 : KEYWORD_NAME_LIST(V)
47 : #undef V
48 3778 : Next();
49 3777 : }
50 :
51 19817737 : void AsmJsScanner::Next() {
52 19817737 : if (rewind_) {
53 1788506 : preceding_token_ = token_;
54 1788506 : preceding_position_ = position_;
55 1788506 : token_ = next_token_;
56 1788506 : position_ = next_position_;
57 1788506 : next_token_ = kUninitialized;
58 1788506 : next_position_ = 0;
59 1788506 : rewind_ = false;
60 1788506 : return;
61 : }
62 :
63 18029231 : if (token_ == kEndOfInput || token_ == kParseError) {
64 : return;
65 : }
66 :
67 : #if DEBUG
68 : if (FLAG_trace_asm_scanner) {
69 : if (Token() == kDouble) {
70 : PrintF("%lf ", AsDouble());
71 : } else if (Token() == kUnsigned) {
72 : PrintF("%" PRIu32 " ", AsUnsigned());
73 : } else {
74 : std::string name = Name(Token());
75 : PrintF("%s ", name.c_str());
76 : }
77 : }
78 : #endif
79 :
80 18029229 : preceded_by_newline_ = false;
81 18029229 : preceding_token_ = token_;
82 18029229 : preceding_position_ = position_;
83 :
84 : for (;;) {
85 47417396 : position_ = stream_->pos();
86 : uc32 ch = stream_->Advance();
87 23708704 : switch (ch) {
88 : case ' ':
89 : case '\t':
90 : case '\r':
91 : // Ignore whitespace.
92 : break;
93 :
94 : case '\n':
95 : // Track when we've passed a newline for optional semicolon support,
96 : // but keep scanning.
97 898573 : preceded_by_newline_ = true;
98 898573 : break;
99 :
100 : case kEndOfInput:
101 1350 : token_ = kEndOfInput;
102 1350 : return;
103 :
104 : case '\'':
105 : case '"':
106 3747 : ConsumeString(ch);
107 3748 : return;
108 :
109 : case '/':
110 1717 : ch = stream_->Advance();
111 1717 : if (ch == '/') {
112 324 : ConsumeCPPComment();
113 1393 : } else if (ch == '*') {
114 7 : if (!ConsumeCComment()) {
115 1 : token_ = kParseError;
116 1 : return;
117 : }
118 : } else {
119 1386 : stream_->Back();
120 1386 : token_ = '/';
121 1386 : return;
122 : }
123 : // Breaks out of switch, but loops again (i.e. the case when we parsed
124 : // a comment, but need to continue to look for the next token).
125 : break;
126 :
127 : case '<':
128 : case '>':
129 : case '=':
130 : case '!':
131 1173798 : ConsumeCompareOrShift(ch);
132 1173797 : return;
133 :
134 : #define V(single_char_token) case single_char_token:
135 : SIMPLE_SINGLE_TOKEN_LIST(V)
136 : #undef V
137 : // Use fixed token IDs for ASCII.
138 7918146 : token_ = ch;
139 7918146 : return;
140 :
141 : default:
142 8930808 : if (IsIdentifierStart(ch)) {
143 2473417 : ConsumeIdentifier(ch);
144 6457391 : } else if (IsNumberStart(ch)) {
145 6457386 : ConsumeNumber(ch);
146 : } else {
147 : // TODO(bradnelson): Support unicode (probably via UnicodeCache).
148 5 : token_ = kParseError;
149 : }
150 : return;
151 : }
152 : }
153 : }
154 :
155 1788507 : void AsmJsScanner::Rewind() {
156 : DCHECK_NE(kUninitialized, preceding_token_);
157 : // TODO(bradnelson): Currently rewinding needs to leave in place the
158 : // preceding newline state (in case a |0 ends a line).
159 : // This is weird and stateful, fix me.
160 : DCHECK(!rewind_);
161 1788507 : next_token_ = token_;
162 1788507 : next_position_ = position_;
163 1788507 : token_ = preceding_token_;
164 1788507 : position_ = preceding_position_;
165 1788507 : preceding_token_ = kUninitialized;
166 1788507 : preceding_position_ = 0;
167 1788507 : rewind_ = true;
168 : identifier_string_.clear();
169 1788507 : }
170 :
171 24143 : void AsmJsScanner::ResetLocals() { local_names_.clear(); }
172 :
173 : #if DEBUG
174 : // Only used for debugging.
175 : std::string AsmJsScanner::Name(token_t token) const {
176 : if (token >= 32 && token < 127) {
177 : return std::string(1, static_cast<char>(token));
178 : }
179 : for (auto& i : local_names_) {
180 : if (i.second == token) {
181 : return i.first;
182 : }
183 : }
184 : for (auto& i : global_names_) {
185 : if (i.second == token) {
186 : return i.first;
187 : }
188 : }
189 : for (auto& i : property_names_) {
190 : if (i.second == token) {
191 : return i.first;
192 : }
193 : }
194 : switch (token) {
195 : #define V(rawname, name) \
196 : case kToken_##name: \
197 : return rawname;
198 : LONG_SYMBOL_NAME_LIST(V)
199 : #undef V
200 : #define V(name, value, string_name) \
201 : case name: \
202 : return string_name;
203 : SPECIAL_TOKEN_LIST(V)
204 : default:
205 : break;
206 : #undef V
207 : }
208 : UNREACHABLE();
209 : }
210 : #endif
211 :
212 859 : void AsmJsScanner::Seek(size_t pos) {
213 859 : stream_->Seek(pos);
214 859 : preceding_token_ = kUninitialized;
215 859 : token_ = kUninitialized;
216 859 : next_token_ = kUninitialized;
217 859 : preceding_position_ = 0;
218 859 : position_ = 0;
219 859 : next_position_ = 0;
220 859 : rewind_ = false;
221 859 : Next();
222 859 : }
223 :
224 2473414 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
225 : // Consume characters while still part of the identifier.
226 : identifier_string_.clear();
227 20537146 : while (IsIdentifierPart(ch)) {
228 9031862 : identifier_string_ += ch;
229 9031865 : ch = stream_->Advance();
230 : }
231 : // Go back one for next time.
232 2473418 : stream_->Back();
233 :
234 : // Decode what the identifier means.
235 2473419 : if (preceding_token_ == '.') {
236 10421 : auto i = property_names_.find(identifier_string_);
237 10418 : if (i != property_names_.end()) {
238 7954 : token_ = i->second;
239 : return;
240 : }
241 : } else {
242 : {
243 2462998 : auto i = local_names_.find(identifier_string_);
244 2462998 : if (i != local_names_.end()) {
245 779167 : token_ = i->second;
246 : return;
247 : }
248 : }
249 1683831 : if (!in_local_scope_) {
250 : auto i = global_names_.find(identifier_string_);
251 1107964 : if (i != global_names_.end()) {
252 1078027 : token_ = i->second;
253 : return;
254 : }
255 : }
256 : }
257 608271 : if (preceding_token_ == '.') {
258 2467 : CHECK_LT(global_count_, kMaxIdentifierCount);
259 2467 : token_ = kGlobalsStart + global_count_++;
260 4934 : property_names_[identifier_string_] = token_;
261 605804 : } else if (in_local_scope_) {
262 575869 : CHECK_LT(local_names_.size(), kMaxIdentifierCount);
263 575869 : token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
264 1151738 : local_names_[identifier_string_] = token_;
265 : } else {
266 29935 : CHECK_LT(global_count_, kMaxIdentifierCount);
267 29935 : token_ = kGlobalsStart + global_count_++;
268 59872 : global_names_[identifier_string_] = token_;
269 : }
270 : }
271 :
272 6457386 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
273 : std::string number;
274 6457386 : number.assign(1, ch);
275 6457386 : bool has_dot = ch == '.';
276 : bool has_prefix = false;
277 10666268 : for (;;) {
278 17123654 : ch = stream_->Advance();
279 45715101 : if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
280 29393846 : (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
281 30040170 : ch == 'x' ||
282 6464427 : ((ch == '-' || ch == '+') && !has_prefix &&
283 9288 : (number[number.size() - 1] == 'e' ||
284 : number[number.size() - 1] == 'E'))) {
285 : // TODO(bradnelson): Test weird cases ending in -.
286 10666268 : if (ch == '.') {
287 : has_dot = true;
288 : }
289 10666268 : if (ch == 'b' || ch == 'o' || ch == 'x') {
290 : has_prefix = true;
291 : }
292 10666268 : number.push_back(ch);
293 : } else {
294 : break;
295 : }
296 : }
297 6457386 : stream_->Back();
298 : // Special case the most common number.
299 6457386 : if (number.size() == 1 && number[0] == '0') {
300 919040 : unsigned_value_ = 0;
301 919040 : token_ = kUnsigned;
302 919040 : return;
303 : }
304 : // Pick out dot.
305 5538346 : if (number.size() == 1 && number[0] == '.') {
306 7994 : token_ = '.';
307 7994 : return;
308 : }
309 : // Decode numbers.
310 5530352 : double_value_ = StringToDouble(
311 : Vector<const uint8_t>::cast(VectorOf(number)),
312 5530352 : ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
313 5530352 : if (std::isnan(double_value_)) {
314 : // Check if string to number conversion didn't consume all the characters.
315 : // This happens if the character filter let through something invalid
316 : // like: 0123ef for example.
317 : // TODO(bradnelson): Check if this happens often enough to be a perf
318 : // problem.
319 2446 : if (number[0] == '.') {
320 9111 : for (size_t k = 1; k < number.size(); ++k) {
321 3333 : stream_->Back();
322 : }
323 2445 : token_ = '.';
324 2445 : return;
325 : }
326 : // Anything else that doesn't parse is an error.
327 1 : token_ = kParseError;
328 1 : return;
329 : }
330 5527906 : if (has_dot) {
331 5009748 : token_ = kDouble;
332 : } else {
333 : // Exceeding safe integer range is an error.
334 518158 : if (double_value_ > static_cast<double>(kMaxUInt32)) {
335 13 : token_ = kParseError;
336 13 : return;
337 : }
338 518145 : unsigned_value_ = static_cast<uint32_t>(double_value_);
339 518145 : token_ = kUnsigned;
340 : }
341 : }
342 :
343 7 : bool AsmJsScanner::ConsumeCComment() {
344 : for (;;) {
345 123 : uc32 ch = stream_->Advance();
346 126 : while (ch == '*') {
347 9 : ch = stream_->Advance();
348 9 : if (ch == '/') {
349 : return true;
350 : }
351 : }
352 117 : if (ch == '\n') {
353 5 : preceded_by_newline_ = true;
354 : }
355 117 : if (ch == kEndOfInput) {
356 : return false;
357 : }
358 : }
359 : }
360 :
361 324 : void AsmJsScanner::ConsumeCPPComment() {
362 : for (;;) {
363 10872 : uc32 ch = stream_->Advance();
364 10872 : if (ch == '\n') {
365 324 : preceded_by_newline_ = true;
366 324 : return;
367 : }
368 10548 : if (ch == kEndOfInput) {
369 : return;
370 : }
371 : }
372 : }
373 :
374 3746 : void AsmJsScanner::ConsumeString(uc32 quote) {
375 : // Only string allowed is 'use asm' / "use asm".
376 : const char* expected = "use asm";
377 55880 : for (; *expected != '\0'; ++expected) {
378 52180 : if (stream_->Advance() != *expected) {
379 24 : token_ = kParseError;
380 24 : return;
381 : }
382 : }
383 7448 : if (stream_->Advance() != quote) {
384 0 : token_ = kParseError;
385 0 : return;
386 : }
387 3724 : token_ = kToken_UseAsm;
388 : }
389 :
390 1173798 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
391 1173798 : uc32 next_ch = stream_->Advance();
392 1173797 : if (next_ch == '=') {
393 53033 : switch (ch) {
394 : case '<':
395 2476 : token_ = kToken_LE;
396 2476 : break;
397 : case '>':
398 2931 : token_ = kToken_GE;
399 2931 : break;
400 : case '=':
401 33793 : token_ = kToken_EQ;
402 33793 : break;
403 : case '!':
404 13833 : token_ = kToken_NE;
405 13833 : break;
406 : default:
407 0 : UNREACHABLE();
408 : }
409 1120764 : } else if (ch == '<' && next_ch == '<') {
410 24539 : token_ = kToken_SHL;
411 1096225 : } else if (ch == '>' && next_ch == '>') {
412 390804 : if (stream_->Advance() == '>') {
413 25399 : token_ = kToken_SHR;
414 : } else {
415 170003 : token_ = kToken_SAR;
416 170003 : stream_->Back();
417 : }
418 : } else {
419 900823 : stream_->Back();
420 900823 : token_ = ch;
421 : }
422 1173797 : }
423 :
424 0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
425 8930808 : return IsInRange(AsciiAlphaToLower(ch), 'a', 'z') || ch == '_' || ch == '$';
426 : }
427 :
428 0 : bool AsmJsScanner::IsIdentifierPart(uc32 ch) { return IsAsciiIdentifier(ch); }
429 :
430 0 : bool AsmJsScanner::IsNumberStart(uc32 ch) {
431 12903402 : return ch == '.' || IsDecimalDigit(ch);
432 : }
433 :
434 : } // namespace internal
435 121996 : } // namespace v8
|