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/conversions.h"
8 : #include "src/flags.h"
9 : #include "src/parsing/scanner.h"
10 : #include "src/unicode-cache.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 : };
20 :
21 350870 : AsmJsScanner::AsmJsScanner()
22 : : token_(kUninitialized),
23 : preceding_token_(kUninitialized),
24 : next_token_(kUninitialized),
25 : position_(0),
26 : preceding_position_(0),
27 : next_position_(0),
28 : rewind_(false),
29 : in_local_scope_(false),
30 : global_count_(0),
31 : double_value_(0.0),
32 : unsigned_value_(0),
33 1754356 : preceded_by_newline_(false) {
34 : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
35 13684006 : STDLIB_MATH_FUNCTION_LIST(V)
36 5964823 : STDLIB_ARRAY_TYPE_LIST(V)
37 : #undef V
38 : #define V(name, _junk1) property_names_[#name] = kToken_##name;
39 5964822 : STDLIB_MATH_VALUE_LIST(V)
40 : #undef V
41 : #define V(name) property_names_[#name] = kToken_##name;
42 2456104 : STDLIB_OTHER_LIST(V)
43 : #undef V
44 : #define V(name) global_names_[#name] = kToken_##name;
45 12280519 : KEYWORD_NAME_LIST(V)
46 : #undef V
47 350872 : }
48 :
49 350872 : void AsmJsScanner::SetStream(std::unique_ptr<Utf16CharacterStream> stream) {
50 : stream_ = std::move(stream);
51 350872 : Next();
52 350872 : }
53 :
54 2143666 : void AsmJsScanner::Next() {
55 2143666 : if (rewind_) {
56 181049 : preceding_token_ = token_;
57 181049 : preceding_position_ = position_;
58 181049 : token_ = next_token_;
59 181049 : position_ = next_position_;
60 181049 : next_token_ = kUninitialized;
61 181049 : next_position_ = 0;
62 181049 : rewind_ = false;
63 181049 : return;
64 : }
65 :
66 1962617 : if (token_ == kEndOfInput || token_ == kParseError) {
67 : return;
68 : }
69 :
70 : #if DEBUG
71 : if (FLAG_trace_asm_scanner) {
72 : if (Token() == kDouble) {
73 : PrintF("%lf ", AsDouble());
74 : } else if (Token() == kUnsigned) {
75 : PrintF("%" PRIu64 " ", AsUnsigned());
76 : } else {
77 : std::string name = Name(Token());
78 : PrintF("%s ", name.c_str());
79 : }
80 : }
81 : #endif
82 :
83 1962617 : preceded_by_newline_ = false;
84 1962617 : preceding_token_ = token_;
85 1962617 : preceding_position_ = position_;
86 :
87 : for (;;) {
88 4002021 : position_ = stream_->pos();
89 4002021 : uc32 ch = stream_->Advance();
90 4002021 : switch (ch) {
91 : case ' ':
92 : case '\t':
93 : case '\r':
94 : // Ignore whitespace.
95 : break;
96 :
97 : case '\n':
98 : // Track when we've passed a newline for optional semicolon support,
99 : // but keep scanning.
100 788957 : preceded_by_newline_ = true;
101 788957 : break;
102 :
103 : case kEndOfInput:
104 930 : token_ = kEndOfInput;
105 930 : return;
106 :
107 : case '\'':
108 : case '"':
109 21770 : ConsumeString(ch);
110 21770 : return;
111 :
112 : case '/':
113 236713 : ch = stream_->Advance();
114 236713 : if (ch == '/') {
115 188310 : ConsumeCPPComment();
116 48403 : } else if (ch == '*') {
117 47488 : if (!ConsumeCComment()) {
118 1 : token_ = kParseError;
119 1 : return;
120 : }
121 : } else {
122 915 : stream_->Back();
123 915 : token_ = '/';
124 915 : return;
125 : }
126 : // Breaks out of switch, but loops again (i.e. the case when we parsed
127 : // a comment, but need to continue to look for the next token).
128 : break;
129 :
130 : case '<':
131 : case '>':
132 : case '=':
133 : case '!':
134 87938 : ConsumeCompareOrShift(ch);
135 87938 : return;
136 :
137 : #define V(single_char_token) case single_char_token:
138 : SIMPLE_SINGLE_TOKEN_LIST(V)
139 : #undef V
140 : // Use fixed token IDs for ASCII.
141 1033419 : token_ = ch;
142 1033419 : return;
143 :
144 : default:
145 817644 : if (IsIdentifierStart(ch)) {
146 679721 : ConsumeIdentifier(ch);
147 137923 : } else if (IsNumberStart(ch)) {
148 137874 : ConsumeNumber(ch);
149 : } else {
150 : // TODO(bradnelson): Support unicode (probably via UnicodeCache).
151 49 : token_ = kParseError;
152 : }
153 : return;
154 : }
155 : }
156 : }
157 :
158 181050 : void AsmJsScanner::Rewind() {
159 : DCHECK_NE(kUninitialized, preceding_token_);
160 : // TODO(bradnelson): Currently rewinding needs to leave in place the
161 : // preceding newline state (in case a |0 ends a line).
162 : // This is weird and stateful, fix me.
163 : DCHECK(!rewind_);
164 181050 : next_token_ = token_;
165 181050 : next_position_ = position_;
166 181050 : token_ = preceding_token_;
167 181050 : position_ = preceding_position_;
168 181050 : preceding_token_ = kUninitialized;
169 181050 : preceding_position_ = 0;
170 181050 : rewind_ = true;
171 181050 : identifier_string_.clear();
172 181050 : }
173 :
174 4114 : void AsmJsScanner::ResetLocals() { local_names_.clear(); }
175 :
176 : #if DEBUG
177 : // Only used for debugging.
178 : std::string AsmJsScanner::Name(token_t token) const {
179 : if (token >= 32 && token < 127) {
180 : return std::string(1, static_cast<char>(token));
181 : }
182 : for (auto& i : local_names_) {
183 : if (i.second == token) {
184 : return i.first;
185 : }
186 : }
187 : for (auto& i : global_names_) {
188 : if (i.second == token) {
189 : return i.first;
190 : }
191 : }
192 : for (auto& i : property_names_) {
193 : if (i.second == token) {
194 : return i.first;
195 : }
196 : }
197 : switch (token) {
198 : #define V(rawname, name) \
199 : case kToken_##name: \
200 : return rawname;
201 : LONG_SYMBOL_NAME_LIST(V)
202 : #undef V
203 : #define V(name, value, string_name) \
204 : case name: \
205 : return string_name;
206 : SPECIAL_TOKEN_LIST(V)
207 : default:
208 : break;
209 : }
210 : UNREACHABLE();
211 : return "{unreachable}";
212 : }
213 : #endif
214 :
215 137 : void AsmJsScanner::Seek(size_t pos) {
216 137 : stream_->Seek(pos);
217 137 : preceding_token_ = kUninitialized;
218 137 : token_ = kUninitialized;
219 137 : next_token_ = kUninitialized;
220 137 : preceding_position_ = 0;
221 137 : position_ = 0;
222 137 : next_position_ = 0;
223 137 : rewind_ = false;
224 137 : Next();
225 137 : }
226 :
227 679721 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
228 : // Consume characters while still part of the identifier.
229 679721 : identifier_string_.clear();
230 3652398 : while (IsIdentifierPart(ch)) {
231 2972675 : identifier_string_ += ch;
232 2972676 : ch = stream_->Advance();
233 : }
234 : // Go back one for next time.
235 679722 : stream_->Back();
236 :
237 : // Decode what the identifier means.
238 679722 : if (preceding_token_ == '.') {
239 : auto i = property_names_.find(identifier_string_);
240 3038 : if (i != property_names_.end()) {
241 2488 : token_ = i->second;
242 : return;
243 : }
244 : } else {
245 : {
246 : auto i = local_names_.find(identifier_string_);
247 676684 : if (i != local_names_.end()) {
248 95169 : token_ = i->second;
249 : return;
250 : }
251 : }
252 581515 : if (!in_local_scope_) {
253 : auto i = global_names_.find(identifier_string_);
254 572943 : if (i != global_names_.end()) {
255 295149 : token_ = i->second;
256 : return;
257 : }
258 : }
259 : }
260 286915 : if (preceding_token_ == '.') {
261 550 : CHECK(global_count_ < kMaxIdentifierCount);
262 550 : token_ = kGlobalsStart + global_count_++;
263 550 : property_names_[identifier_string_] = token_;
264 286365 : } else if (in_local_scope_) {
265 8571 : CHECK(local_names_.size() < kMaxIdentifierCount);
266 8571 : token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
267 8571 : local_names_[identifier_string_] = token_;
268 : } else {
269 277794 : CHECK(global_count_ < kMaxIdentifierCount);
270 277794 : token_ = kGlobalsStart + global_count_++;
271 277795 : global_names_[identifier_string_] = token_;
272 : }
273 : }
274 :
275 137874 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
276 : std::string number;
277 137874 : number = ch;
278 137874 : bool has_dot = ch == '.';
279 : for (;;) {
280 296504 : ch = stream_->Advance();
281 734037 : if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
282 420299 : (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
283 572827 : ch == 'x' ||
284 138304 : ((ch == '-' || ch == '+') && (number[number.size() - 1] == 'e' ||
285 50 : number[number.size() - 1] == 'E'))) {
286 : // TODO(bradnelson): Test weird cases ending in -.
287 158630 : if (ch == '.') {
288 : has_dot = true;
289 : }
290 158630 : number.push_back(ch);
291 : } else {
292 : break;
293 : }
294 : }
295 137874 : stream_->Back();
296 : // Special case the most common number.
297 228989 : if (number.size() == 1 && number[0] == '0') {
298 50426 : unsigned_value_ = 0;
299 50426 : token_ = kUnsigned;
300 50426 : return;
301 : }
302 : // Pick out dot.
303 128137 : if (number.size() == 1 && number[0] == '.') {
304 2196 : token_ = '.';
305 2196 : return;
306 : }
307 : // Decode numbers.
308 85252 : UnicodeCache cache;
309 : double_value_ = StringToDouble(
310 : &cache,
311 : Vector<uint8_t>(
312 : const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(number.data())),
313 : static_cast<int>(number.size())),
314 85252 : ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
315 85252 : if (std::isnan(double_value_)) {
316 : // Check if string to number conversion didn't consume all the characters.
317 : // This happens if the character filter let through something invalid
318 : // like: 0123ef for example.
319 : // TODO(bradnelson): Check if this happens often enough to be a perf
320 : // problem.
321 1243 : if (number[0] == '.') {
322 5814 : for (size_t k = 1; k < number.size(); ++k) {
323 2286 : stream_->Back();
324 : }
325 1242 : token_ = '.';
326 1242 : return;
327 : }
328 : // Anything else that doesn't parse is an error.
329 1 : token_ = kParseError;
330 1 : return;
331 : }
332 84009 : if (has_dot) {
333 1971 : token_ = kDouble;
334 : } else {
335 82038 : unsigned_value_ = static_cast<uint32_t>(double_value_);
336 82038 : token_ = kUnsigned;
337 : }
338 : }
339 :
340 47488 : bool AsmJsScanner::ConsumeCComment() {
341 : for (;;) {
342 155358 : uc32 ch = stream_->Advance();
343 311026 : while (ch == '*') {
344 47797 : ch = stream_->Advance();
345 47797 : if (ch == '/') {
346 : return true;
347 : }
348 : }
349 107871 : if (ch == kEndOfInput) {
350 : return false;
351 : }
352 : }
353 : }
354 :
355 188310 : void AsmJsScanner::ConsumeCPPComment() {
356 : for (;;) {
357 11074589 : uc32 ch = stream_->Advance();
358 11074589 : if (ch == '\n' || ch == kEndOfInput) {
359 188310 : return;
360 : }
361 : }
362 : }
363 :
364 21770 : void AsmJsScanner::ConsumeString(uc32 quote) {
365 : // Only string allowed is 'use asm' / "use asm".
366 : const char* expected = "use asm";
367 63094 : for (; *expected != '\0'; ++expected) {
368 62181 : if (stream_->Advance() != *expected) {
369 20857 : token_ = kParseError;
370 20857 : return;
371 : }
372 : }
373 913 : if (stream_->Advance() != quote) {
374 0 : token_ = kParseError;
375 0 : return;
376 : }
377 913 : token_ = kToken_UseAsm;
378 : }
379 :
380 87938 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
381 87938 : uc32 next_ch = stream_->Advance();
382 87938 : if (next_ch == '=') {
383 6318 : switch (ch) {
384 : case '<':
385 296 : token_ = kToken_LE;
386 296 : break;
387 : case '>':
388 210 : token_ = kToken_GE;
389 210 : break;
390 : case '=':
391 4132 : token_ = kToken_EQ;
392 4132 : break;
393 : case '!':
394 1680 : token_ = kToken_NE;
395 1680 : break;
396 : default:
397 0 : UNREACHABLE();
398 : }
399 81620 : } else if (ch == '<' && next_ch == '<') {
400 2925 : token_ = kToken_SHL;
401 78695 : } else if (ch == '>' && next_ch == '>') {
402 24050 : if (stream_->Advance() == '>') {
403 3564 : token_ = kToken_SHR;
404 : } else {
405 20486 : token_ = kToken_SAR;
406 20486 : stream_->Back();
407 : }
408 : } else {
409 54645 : stream_->Back();
410 54645 : token_ = ch;
411 : }
412 87938 : }
413 :
414 0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
415 5536677 : return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
416 1066636 : ch == '$';
417 : }
418 :
419 3652397 : bool AsmJsScanner::IsIdentifierPart(uc32 ch) {
420 3652397 : return IsIdentifierStart(ch) || (ch >= '0' && ch <= '9');
421 : }
422 :
423 0 : bool AsmJsScanner::IsNumberStart(uc32 ch) {
424 137923 : return ch == '.' || (ch >= '0' && ch <= '9');
425 : }
426 :
427 : } // namespace internal
428 : } // namespace v8
|