LCOV - code coverage report
Current view: top level - src/asmjs - asm-scanner.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 181 187 96.8 %
Date: 2019-02-19 Functions: 13 16 81.2 %

          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        3735 : 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       18679 :       preceded_by_newline_(false) {
      35             : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
      36      145690 :   STDLIB_MATH_FUNCTION_LIST(V)
      37       63515 :   STDLIB_ARRAY_TYPE_LIST(V)
      38             : #undef V
      39             : #define V(name, _junk1) property_names_[#name] = kToken_##name;
      40       63504 :   STDLIB_MATH_VALUE_LIST(V)
      41             : #undef V
      42             : #define V(name) property_names_[#name] = kToken_##name;
      43       26157 :   STDLIB_OTHER_LIST(V)
      44             : #undef V
      45             : #define V(name) global_names_[#name] = kToken_##name;
      46      130783 :   KEYWORD_NAME_LIST(V)
      47             : #undef V
      48        3737 :   Next();
      49        3733 : }
      50             : 
      51     9810471 : void AsmJsScanner::Next() {
      52     9810471 :   if (rewind_) {
      53     1788349 :     preceding_token_ = token_;
      54     1788349 :     preceding_position_ = position_;
      55     1788349 :     token_ = next_token_;
      56     1788349 :     position_ = next_position_;
      57     1788349 :     next_token_ = kUninitialized;
      58     1788349 :     next_position_ = 0;
      59     1788349 :     rewind_ = false;
      60     1788349 :     return;
      61             :   }
      62             : 
      63     8022122 :   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     8022150 :   preceded_by_newline_ = false;
      81     8022150 :   preceding_token_ = token_;
      82     8022150 :   preceding_position_ = position_;
      83             : 
      84             :   for (;;) {
      85    27392088 :     position_ = stream_->pos();
      86    13696044 :     uc32 ch = stream_->Advance();
      87    13696147 :     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      896056 :         preceded_by_newline_ = true;
      98      896056 :         break;
      99             : 
     100             :       case kEndOfInput:
     101          13 :         token_ = kEndOfInput;
     102          13 :         return;
     103             : 
     104             :       case '\'':
     105             :       case '"':
     106        3703 :         ConsumeString(ch);
     107        3704 :         return;
     108             : 
     109             :       case '/':
     110        1705 :         ch = stream_->Advance();
     111        1704 :         if (ch == '/') {
     112         312 :           ConsumeCPPComment();
     113        1392 :         } else if (ch == '*') {
     114           7 :           if (!ConsumeCComment()) {
     115           1 :             token_ = kParseError;
     116           1 :             return;
     117             :           }
     118             :         } else {
     119        1385 :           stream_->Back();
     120        1385 :           token_ = '/';
     121        1385 :           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     1173661 :         ConsumeCompareOrShift(ch);
     132     1173660 :         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     2913742 :         token_ = ch;
     139     2913742 :         return;
     140             : 
     141             :       default:
     142     3929741 :         if (IsIdentifierStart(ch)) {
     143     2472534 :           ConsumeIdentifier(ch);
     144     1457207 :         } else if (IsNumberStart(ch)) {
     145     1457202 :           ConsumeNumber(ch);
     146             :         } else {
     147             :           // TODO(bradnelson): Support unicode (probably via UnicodeCache).
     148           5 :           token_ = kParseError;
     149             :         }
     150             :         return;
     151             :     }
     152             :   }
     153             : }
     154             : 
     155     1788347 : 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     1788347 :   next_token_ = token_;
     162     1788347 :   next_position_ = position_;
     163     1788347 :   token_ = preceding_token_;
     164     1788347 :   position_ = preceding_position_;
     165     1788347 :   preceding_token_ = kUninitialized;
     166     1788347 :   preceding_position_ = 0;
     167     1788347 :   rewind_ = true;
     168     1788347 :   identifier_string_.clear();
     169     1788350 : }
     170             : 
     171       24032 : 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         855 : void AsmJsScanner::Seek(size_t pos) {
     213         855 :   stream_->Seek(pos);
     214         855 :   preceding_token_ = kUninitialized;
     215         855 :   token_ = kUninitialized;
     216         855 :   next_token_ = kUninitialized;
     217         855 :   preceding_position_ = 0;
     218         855 :   position_ = 0;
     219         855 :   next_position_ = 0;
     220         855 :   rewind_ = false;
     221         855 :   Next();
     222         855 : }
     223             : 
     224     2472541 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
     225             :   // Consume characters while still part of the identifier.
     226     2472541 :   identifier_string_.clear();
     227    11500547 :   while (IsIdentifierPart(ch)) {
     228     9027983 :     identifier_string_ += ch;
     229     9027986 :     ch = stream_->Advance();
     230             :   }
     231             :   // Go back one for next time.
     232     2472564 :   stream_->Back();
     233             : 
     234             :   // Decode what the identifier means.
     235     2472563 :   if (preceding_token_ == '.') {
     236             :     auto i = property_names_.find(identifier_string_);
     237       10332 :     if (i != property_names_.end()) {
     238        7865 :       token_ = i->second;
     239             :       return;
     240             :     }
     241             :   } else {
     242             :     {
     243             :       auto i = local_names_.find(identifier_string_);
     244     2462229 :       if (i != local_names_.end()) {
     245      779046 :         token_ = i->second;
     246             :         return;
     247             :       }
     248             :     }
     249     1683183 :     if (!in_local_scope_) {
     250             :       auto i = global_names_.find(identifier_string_);
     251     1107366 :       if (i != global_names_.end()) {
     252     1077607 :         token_ = i->second;
     253             :         return;
     254             :       }
     255             :     }
     256             :   }
     257      608047 :   if (preceding_token_ == '.') {
     258        2466 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     259        2466 :     token_ = kGlobalsStart + global_count_++;
     260        2466 :     property_names_[identifier_string_] = token_;
     261      605581 :   } else if (in_local_scope_) {
     262      575820 :     CHECK_LT(local_names_.size(), kMaxIdentifierCount);
     263      575820 :     token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
     264      575820 :     local_names_[identifier_string_] = token_;
     265             :   } else {
     266       29761 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     267       29761 :     token_ = kGlobalsStart + global_count_++;
     268       29760 :     global_names_[identifier_string_] = token_;
     269             :   }
     270             : }
     271             : 
     272     1457201 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
     273             :   std::string number;
     274     1457201 :   number = ch;
     275     1457217 :   bool has_dot = ch == '.';
     276             :   bool has_prefix = false;
     277             :   for (;;) {
     278     2123401 :     ch = stream_->Advance();
     279     5714427 :     if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     280     4393357 :         (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
     281     5039554 :         ch == 'x' ||
     282     1464251 :         ((ch == '-' || ch == '+') && !has_prefix &&
     283        9288 :          (number[number.size() - 1] == 'e' ||
     284        5516 :           number[number.size() - 1] == 'E'))) {
     285             :       // TODO(bradnelson): Test weird cases ending in -.
     286      666187 :       if (ch == '.') {
     287             :         has_dot = true;
     288             :       }
     289      666187 :       if (ch == 'b' || ch == 'o' || ch == 'x') {
     290             :         has_prefix = true;
     291             :       }
     292      666187 :       number.push_back(ch);
     293             :     } else {
     294             :       break;
     295             :     }
     296             :   }
     297     1457212 :   stream_->Back();
     298             :   // Special case the most common number.
     299     2686701 :   if (number.size() == 1 && number[0] == '0') {
     300      918956 :     unsigned_value_ = 0;
     301      918956 :     token_ = kUnsigned;
     302      918956 :     return;
     303             :   }
     304             :   // Pick out dot.
     305      848790 :   if (number.size() == 1 && number[0] == '.') {
     306        7928 :     token_ = '.';
     307        7928 :     return;
     308             :   }
     309             :   // Decode numbers.
     310             :   double_value_ = StringToDouble(
     311             :       Vector<const uint8_t>::cast(VectorOf(number)),
     312      530324 :       ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
     313      530322 :   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        2422 :     if (number[0] == '.') {
     320        9031 :       for (size_t k = 1; k < number.size(); ++k) {
     321        3305 :         stream_->Back();
     322             :       }
     323        2421 :       token_ = '.';
     324        2421 :       return;
     325             :     }
     326             :     // Anything else that doesn't parse is an error.
     327           1 :     token_ = kParseError;
     328           1 :     return;
     329             :   }
     330      527900 :   if (has_dot) {
     331        9769 :     token_ = kDouble;
     332             :   } else {
     333             :     // Exceeding safe integer range is an error.
     334      518131 :     if (double_value_ > static_cast<double>(kMaxUInt32)) {
     335          13 :       token_ = kParseError;
     336          13 :       return;
     337             :     }
     338      518118 :     unsigned_value_ = static_cast<uint32_t>(double_value_);
     339      518118 :     token_ = kUnsigned;
     340             :   }
     341             : }
     342             : 
     343           7 : bool AsmJsScanner::ConsumeCComment() {
     344             :   for (;;) {
     345         123 :     uc32 ch = stream_->Advance();
     346         249 :     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         312 : void AsmJsScanner::ConsumeCPPComment() {
     362             :   for (;;) {
     363       10310 :     uc32 ch = stream_->Advance();
     364       10308 :     if (ch == '\n') {
     365         311 :       preceded_by_newline_ = true;
     366         311 :       return;
     367             :     }
     368        9997 :     if (ch == kEndOfInput) {
     369             :       return;
     370             :     }
     371             :   }
     372             : }
     373             : 
     374        3710 : void AsmJsScanner::ConsumeString(uc32 quote) {
     375             :   // Only string allowed is 'use asm' / "use asm".
     376             :   const char* expected = "use asm";
     377       29450 :   for (; *expected != '\0'; ++expected) {
     378       51532 :     if (stream_->Advance() != *expected) {
     379          24 :       token_ = kParseError;
     380          24 :       return;
     381             :     }
     382             :   }
     383        7362 :   if (stream_->Advance() != quote) {
     384           0 :     token_ = kParseError;
     385           0 :     return;
     386             :   }
     387        3680 :   token_ = kToken_UseAsm;
     388             : }
     389             : 
     390     1173662 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
     391     1173662 :   uc32 next_ch = stream_->Advance();
     392     1173659 :   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     1120626 :   } else if (ch == '<' && next_ch == '<') {
     410       24539 :     token_ = kToken_SHL;
     411     1096087 :   } else if (ch == '>' && next_ch == '>') {
     412      390792 :     if (stream_->Advance() == '>') {
     413       25395 :       token_ = kToken_SHR;
     414             :     } else {
     415      170001 :       token_ = kToken_SAR;
     416      170001 :       stream_->Back();
     417             :     }
     418             :   } else {
     419      900691 :     stream_->Back();
     420      900691 :     token_ = ch;
     421             :   }
     422     1173659 : }
     423             : 
     424           0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
     425     3929741 :   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     2903126 :   return ch == '.' || IsDecimalDigit(ch);
     432             : }
     433             : 
     434             : }  // namespace internal
     435      178779 : }  // namespace v8

Generated by: LCOV version 1.10