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 351173 : 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 1755884 : preceded_by_newline_(false) {
34 : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
35 13696008 : STDLIB_MATH_FUNCTION_LIST(V)
36 5970058 : STDLIB_ARRAY_TYPE_LIST(V)
37 : #undef V
38 : #define V(name, _junk1) property_names_[#name] = kToken_##name;
39 5970060 : STDLIB_MATH_VALUE_LIST(V)
40 : #undef V
41 : #define V(name) property_names_[#name] = kToken_##name;
42 2458260 : STDLIB_OTHER_LIST(V)
43 : #undef V
44 : #define V(name) global_names_[#name] = kToken_##name;
45 12291299 : KEYWORD_NAME_LIST(V)
46 : #undef V
47 351179 : }
48 :
49 351180 : void AsmJsScanner::SetStream(std::unique_ptr<Utf16CharacterStream> stream) {
50 : stream_ = std::move(stream);
51 351180 : Next();
52 351179 : }
53 :
54 2145201 : void AsmJsScanner::Next() {
55 2145201 : 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 1964152 : 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("%" PRIu32 " ", AsUnsigned());
76 : } else {
77 : std::string name = Name(Token());
78 : PrintF("%s ", name.c_str());
79 : }
80 : }
81 : #endif
82 :
83 1964152 : preceded_by_newline_ = false;
84 1964152 : preceding_token_ = token_;
85 1964152 : preceding_position_ = position_;
86 :
87 : for (;;) {
88 4004241 : position_ = stream_->pos();
89 4004241 : uc32 ch = stream_->Advance();
90 4004242 : 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 789187 : preceded_by_newline_ = true;
101 789187 : break;
102 :
103 : case kEndOfInput:
104 932 : token_ = kEndOfInput;
105 932 : return;
106 :
107 : case '\'':
108 : case '"':
109 21777 : ConsumeString(ch);
110 21777 : return;
111 :
112 : case '/':
113 236820 : ch = stream_->Advance();
114 236820 : if (ch == '/') {
115 188417 : 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 87954 : ConsumeCompareOrShift(ch);
135 87954 : 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 1034355 : token_ = ch;
142 1034355 : return;
143 :
144 : default:
145 818219 : if (IsIdentifierStart(ch)) {
146 680291 : ConsumeIdentifier(ch);
147 137928 : } else if (IsNumberStart(ch)) {
148 137879 : 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 4116 : 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 680289 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
228 : // Consume characters while still part of the identifier.
229 680289 : identifier_string_.clear();
230 3655244 : while (IsIdentifierPart(ch)) {
231 2974952 : identifier_string_ += ch;
232 2974949 : ch = stream_->Advance();
233 : }
234 : // Go back one for next time.
235 680291 : stream_->Back();
236 :
237 : // Decode what the identifier means.
238 680291 : if (preceding_token_ == '.') {
239 : auto i = property_names_.find(identifier_string_);
240 3037 : 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 677253 : if (i != local_names_.end()) {
248 95169 : token_ = i->second;
249 : return;
250 : }
251 : }
252 582084 : if (!in_local_scope_) {
253 : auto i = global_names_.find(identifier_string_);
254 573513 : if (i != global_names_.end()) {
255 295353 : token_ = i->second;
256 : return;
257 : }
258 : }
259 : }
260 287280 : if (preceding_token_ == '.') {
261 550 : CHECK(global_count_ < kMaxIdentifierCount);
262 550 : token_ = kGlobalsStart + global_count_++;
263 550 : property_names_[identifier_string_] = token_;
264 286730 : } 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 278159 : CHECK(global_count_ < kMaxIdentifierCount);
270 278159 : token_ = kGlobalsStart + global_count_++;
271 278159 : global_names_[identifier_string_] = token_;
272 : }
273 : }
274 :
275 137879 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
276 : std::string number;
277 137879 : number = ch;
278 137879 : bool has_dot = ch == '.';
279 : for (;;) {
280 296560 : ch = stream_->Advance();
281 734158 : if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
282 420326 : (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
283 572897 : ch == 'x' ||
284 138309 : ((ch == '-' || ch == '+') && (number[number.size() - 1] == 'e' ||
285 50 : number[number.size() - 1] == 'E'))) {
286 : // TODO(bradnelson): Test weird cases ending in -.
287 158681 : if (ch == '.') {
288 : has_dot = true;
289 : }
290 158681 : number.push_back(ch);
291 : } else {
292 : break;
293 : }
294 : }
295 137879 : stream_->Back();
296 : // Special case the most common number.
297 228994 : 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 128142 : if (number.size() == 1 && number[0] == '.') {
304 2196 : token_ = '.';
305 2196 : return;
306 : }
307 : // Decode numbers.
308 85257 : UnicodeCache cache;
309 : double_value_ = StringToDouble(
310 : &cache,
311 : Vector<const uint8_t>(reinterpret_cast<const uint8_t*>(number.data()),
312 : static_cast<int>(number.size())),
313 170514 : ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
314 85257 : if (std::isnan(double_value_)) {
315 : // Check if string to number conversion didn't consume all the characters.
316 : // This happens if the character filter let through something invalid
317 : // like: 0123ef for example.
318 : // TODO(bradnelson): Check if this happens often enough to be a perf
319 : // problem.
320 1243 : if (number[0] == '.') {
321 5814 : for (size_t k = 1; k < number.size(); ++k) {
322 2286 : stream_->Back();
323 : }
324 1242 : token_ = '.';
325 1242 : return;
326 : }
327 : // Anything else that doesn't parse is an error.
328 1 : token_ = kParseError;
329 1 : return;
330 : }
331 84014 : if (has_dot) {
332 1971 : token_ = kDouble;
333 : } else {
334 : // Exceeding safe integer range is an error.
335 82043 : if (double_value_ > static_cast<double>(kMaxUInt32)) {
336 1430 : token_ = kParseError;
337 1430 : return;
338 : }
339 80613 : unsigned_value_ = static_cast<uint32_t>(double_value_);
340 80613 : token_ = kUnsigned;
341 : }
342 : }
343 :
344 47488 : bool AsmJsScanner::ConsumeCComment() {
345 : for (;;) {
346 155358 : uc32 ch = stream_->Advance();
347 311026 : while (ch == '*') {
348 47797 : ch = stream_->Advance();
349 47797 : if (ch == '/') {
350 : return true;
351 : }
352 : }
353 107871 : if (ch == kEndOfInput) {
354 : return false;
355 : }
356 : }
357 : }
358 :
359 188417 : void AsmJsScanner::ConsumeCPPComment() {
360 : for (;;) {
361 11080606 : uc32 ch = stream_->Advance();
362 11080606 : if (ch == '\n' || ch == kEndOfInput) {
363 188417 : return;
364 : }
365 : }
366 : }
367 :
368 21777 : void AsmJsScanner::ConsumeString(uc32 quote) {
369 : // Only string allowed is 'use asm' / "use asm".
370 : const char* expected = "use asm";
371 63135 : for (; *expected != '\0'; ++expected) {
372 62220 : if (stream_->Advance() != *expected) {
373 20862 : token_ = kParseError;
374 20862 : return;
375 : }
376 : }
377 915 : if (stream_->Advance() != quote) {
378 0 : token_ = kParseError;
379 0 : return;
380 : }
381 915 : token_ = kToken_UseAsm;
382 : }
383 :
384 87954 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
385 87954 : uc32 next_ch = stream_->Advance();
386 87954 : if (next_ch == '=') {
387 6318 : switch (ch) {
388 : case '<':
389 296 : token_ = kToken_LE;
390 296 : break;
391 : case '>':
392 210 : token_ = kToken_GE;
393 210 : break;
394 : case '=':
395 4132 : token_ = kToken_EQ;
396 4132 : break;
397 : case '!':
398 1680 : token_ = kToken_NE;
399 1680 : break;
400 : default:
401 0 : UNREACHABLE();
402 : }
403 81636 : } else if (ch == '<' && next_ch == '<') {
404 2925 : token_ = kToken_SHL;
405 78711 : } else if (ch == '>' && next_ch == '>') {
406 24050 : if (stream_->Advance() == '>') {
407 3564 : token_ = kToken_SHR;
408 : } else {
409 20486 : token_ = kToken_SAR;
410 20486 : stream_->Back();
411 : }
412 : } else {
413 54661 : stream_->Back();
414 54661 : token_ = ch;
415 : }
416 87954 : }
417 :
418 0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
419 5540679 : return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
420 1067221 : ch == '$';
421 : }
422 :
423 3655239 : bool AsmJsScanner::IsIdentifierPart(uc32 ch) {
424 3655239 : return IsIdentifierStart(ch) || (ch >= '0' && ch <= '9');
425 : }
426 :
427 0 : bool AsmJsScanner::IsNumberStart(uc32 ch) {
428 137928 : return ch == '.' || (ch >= '0' && ch <= '9');
429 : }
430 :
431 : } // namespace internal
432 : } // namespace v8
|