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-01-20 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             : };
      20             : 
      21        4559 : 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       22794 :       preceded_by_newline_(false) {
      35             : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
      36      177752 :   STDLIB_MATH_FUNCTION_LIST(V)
      37       77484 :   STDLIB_ARRAY_TYPE_LIST(V)
      38             : #undef V
      39             : #define V(name, _junk1) property_names_[#name] = kToken_##name;
      40       77487 :   STDLIB_MATH_VALUE_LIST(V)
      41             : #undef V
      42             : #define V(name) property_names_[#name] = kToken_##name;
      43       31901 :   STDLIB_OTHER_LIST(V)
      44             : #undef V
      45             : #define V(name) global_names_[#name] = kToken_##name;
      46      159551 :   KEYWORD_NAME_LIST(V)
      47             : #undef V
      48        4559 :   Next();
      49        4555 : }
      50             : 
      51    11240364 : void AsmJsScanner::Next() {
      52    11240364 :   if (rewind_) {
      53     2006325 :     preceding_token_ = token_;
      54     2006325 :     preceding_position_ = position_;
      55     2006325 :     token_ = next_token_;
      56     2006325 :     position_ = next_position_;
      57     2006325 :     next_token_ = kUninitialized;
      58     2006325 :     next_position_ = 0;
      59     2006325 :     rewind_ = false;
      60     2006325 :     return;
      61             :   }
      62             : 
      63     9234039 :   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     9234084 :   preceded_by_newline_ = false;
      81     9234084 :   preceding_token_ = token_;
      82     9234084 :   preceding_position_ = position_;
      83             : 
      84             :   for (;;) {
      85    32089442 :     position_ = stream_->pos();
      86    16044721 :     uc32 ch = stream_->Advance();
      87    16044834 :     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     1076145 :         preceded_by_newline_ = true;
      98     1076145 :         break;
      99             : 
     100             :       case kEndOfInput:
     101          13 :         token_ = kEndOfInput;
     102          13 :         return;
     103             : 
     104             :       case '\'':
     105             :       case '"':
     106        4520 :         ConsumeString(ch);
     107        4521 :         return;
     108             : 
     109             :       case '/':
     110        1928 :         ch = stream_->Advance();
     111        1928 :         if (ch == '/') {
     112         378 :           ConsumeCPPComment();
     113        1550 :         } else if (ch == '*') {
     114           8 :           if (!ConsumeCComment()) {
     115           1 :             token_ = kParseError;
     116           1 :             return;
     117             :           }
     118             :         } else {
     119        1542 :           stream_->Back();
     120        1542 :           token_ = '/';
     121        1542 :           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     1357598 :         ConsumeCompareOrShift(ch);
     132     1357599 :         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     3326099 :         token_ = ch;
     139     3326099 :         return;
     140             : 
     141             :       default:
     142     4544424 :         if (IsIdentifierStart(ch)) {
     143     2865437 :           ConsumeIdentifier(ch);
     144     1678987 :         } else if (IsNumberStart(ch)) {
     145     1678981 :           ConsumeNumber(ch);
     146             :         } else {
     147             :           // TODO(bradnelson): Support unicode (probably via UnicodeCache).
     148           6 :           token_ = kParseError;
     149             :         }
     150             :         return;
     151             :     }
     152             :   }
     153             : }
     154             : 
     155     2006330 : 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     2006330 :   next_token_ = token_;
     162     2006330 :   next_position_ = position_;
     163     2006330 :   token_ = preceding_token_;
     164     2006330 :   position_ = preceding_position_;
     165     2006330 :   preceding_token_ = kUninitialized;
     166     2006330 :   preceding_position_ = 0;
     167     2006330 :   rewind_ = true;
     168     2006330 :   identifier_string_.clear();
     169     2006336 : }
     170             : 
     171       28046 : 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         996 : void AsmJsScanner::Seek(size_t pos) {
     213         996 :   stream_->Seek(pos);
     214         996 :   preceding_token_ = kUninitialized;
     215         996 :   token_ = kUninitialized;
     216         996 :   next_token_ = kUninitialized;
     217         996 :   preceding_position_ = 0;
     218         996 :   position_ = 0;
     219         996 :   next_position_ = 0;
     220         996 :   rewind_ = false;
     221         996 :   Next();
     222         996 : }
     223             : 
     224     2865438 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
     225             :   // Consume characters while still part of the identifier.
     226     2865438 :   identifier_string_.clear();
     227    13546796 :   while (IsIdentifierPart(ch)) {
     228    10681339 :     identifier_string_ += ch;
     229    10681402 :     ch = stream_->Advance();
     230             :   }
     231             :   // Go back one for next time.
     232     2865457 :   stream_->Back();
     233             : 
     234             :   // Decode what the identifier means.
     235     2865456 :   if (preceding_token_ == '.') {
     236             :     auto i = property_names_.find(identifier_string_);
     237       12458 :     if (i != property_names_.end()) {
     238        9524 :       token_ = i->second;
     239             :       return;
     240             :     }
     241             :   } else {
     242             :     {
     243             :       auto i = local_names_.find(identifier_string_);
     244     2853006 :       if (i != local_names_.end()) {
     245      877530 :         token_ = i->second;
     246             :         return;
     247             :       }
     248             :     }
     249     1975476 :     if (!in_local_scope_) {
     250             :       auto i = global_names_.find(identifier_string_);
     251     1289361 :       if (i != global_names_.end()) {
     252     1253899 :         token_ = i->second;
     253             :         return;
     254             :       }
     255             :     }
     256             :   }
     257      724508 :   if (preceding_token_ == '.') {
     258        2934 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     259        2934 :     token_ = kGlobalsStart + global_count_++;
     260        2934 :     property_names_[identifier_string_] = token_;
     261      721574 :   } else if (in_local_scope_) {
     262      686108 :     CHECK_LT(local_names_.size(), kMaxIdentifierCount);
     263      686108 :     token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
     264      686108 :     local_names_[identifier_string_] = token_;
     265             :   } else {
     266       35466 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     267       35466 :     token_ = kGlobalsStart + global_count_++;
     268       35468 :     global_names_[identifier_string_] = token_;
     269             :   }
     270             : }
     271             : 
     272     1678982 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
     273             :   std::string number;
     274     1678982 :   number = ch;
     275     1678990 :   bool has_dot = ch == '.';
     276             :   bool has_prefix = false;
     277             :   for (;;) {
     278     2455355 :     ch = stream_->Advance();
     279     6601826 :     if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     280     5062297 :         (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
     281     5815427 :         ch == 'x' ||
     282     1686332 :         ((ch == '-' || ch == '+') && !has_prefix &&
     283        9490 :          (number[number.size() - 1] == 'e' ||
     284        5516 :           number[number.size() - 1] == 'E'))) {
     285             :       // TODO(bradnelson): Test weird cases ending in -.
     286      776365 :       if (ch == '.') {
     287             :         has_dot = true;
     288             :       }
     289      776365 :       if (ch == 'b' || ch == 'o' || ch == 'x') {
     290             :         has_prefix = true;
     291             :       }
     292      776365 :       number.push_back(ch);
     293             :     } else {
     294             :       break;
     295             :     }
     296             :   }
     297     1678986 :   stream_->Back();
     298             :   // Special case the most common number.
     299     3097392 :   if (number.size() == 1 && number[0] == '0') {
     300     1068882 :     unsigned_value_ = 0;
     301     1068882 :     token_ = kUnsigned;
     302     1068882 :     return;
     303             :   }
     304             :   // Pick out dot.
     305      959629 :   if (number.size() == 1 && number[0] == '.') {
     306        9548 :     token_ = '.';
     307        9548 :     return;
     308             :   }
     309             :   // Decode numbers.
     310             :   double_value_ = StringToDouble(
     311             :       Vector<const uint8_t>::cast(VectorOf(number)),
     312      600557 :       ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
     313      600557 :   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        2932 :     if (number[0] == '.') {
     320       10923 :       for (size_t k = 1; k < number.size(); ++k) {
     321        3996 :         stream_->Back();
     322             :       }
     323        2931 :       token_ = '.';
     324        2931 :       return;
     325             :     }
     326             :     // Anything else that doesn't parse is an error.
     327           1 :     token_ = kParseError;
     328           1 :     return;
     329             :   }
     330      597625 :   if (has_dot) {
     331       11235 :     token_ = kDouble;
     332             :   } else {
     333             :     // Exceeding safe integer range is an error.
     334      586390 :     if (double_value_ > static_cast<double>(kMaxUInt32)) {
     335          16 :       token_ = kParseError;
     336          16 :       return;
     337             :     }
     338      586374 :     unsigned_value_ = static_cast<uint32_t>(double_value_);
     339      586374 :     token_ = kUnsigned;
     340             :   }
     341             : }
     342             : 
     343           8 : bool AsmJsScanner::ConsumeCComment() {
     344             :   for (;;) {
     345         147 :     uc32 ch = stream_->Advance();
     346         297 :     while (ch == '*') {
     347          10 :       ch = stream_->Advance();
     348          10 :       if (ch == '/') {
     349             :         return true;
     350             :       }
     351             :     }
     352         140 :     if (ch == '\n') {
     353           6 :       preceded_by_newline_ = true;
     354             :     }
     355         140 :     if (ch == kEndOfInput) {
     356             :       return false;
     357             :     }
     358             :   }
     359             : }
     360             : 
     361         378 : void AsmJsScanner::ConsumeCPPComment() {
     362             :   for (;;) {
     363       12483 :     uc32 ch = stream_->Advance();
     364       12483 :     if (ch == '\n') {
     365         379 :       preceded_by_newline_ = true;
     366         379 :       return;
     367             :     }
     368       12104 :     if (ch == kEndOfInput) {
     369             :       return;
     370             :     }
     371             :   }
     372             : }
     373             : 
     374        4523 : void AsmJsScanner::ConsumeString(uc32 quote) {
     375             :   // Only string allowed is 'use asm' / "use asm".
     376             :   const char* expected = "use asm";
     377       35965 :   for (; *expected != '\0'; ++expected) {
     378       62942 :     if (stream_->Advance() != *expected) {
     379          28 :       token_ = kParseError;
     380          28 :       return;
     381             :     }
     382             :   }
     383        8986 :   if (stream_->Advance() != quote) {
     384           0 :     token_ = kParseError;
     385           0 :     return;
     386             :   }
     387        4493 :   token_ = kToken_UseAsm;
     388             : }
     389             : 
     390     1357598 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
     391     1357598 :   uc32 next_ch = stream_->Advance();
     392     1357597 :   if (next_ch == '=') {
     393       59350 :     switch (ch) {
     394             :       case '<':
     395        2768 :         token_ = kToken_LE;
     396        2768 :         break;
     397             :       case '>':
     398        3137 :         token_ = kToken_GE;
     399        3137 :         break;
     400             :       case '=':
     401       37935 :         token_ = kToken_EQ;
     402       37935 :         break;
     403             :       case '!':
     404       15510 :         token_ = kToken_NE;
     405       15510 :         break;
     406             :       default:
     407           0 :         UNREACHABLE();
     408             :     }
     409     1298247 :   } else if (ch == '<' && next_ch == '<') {
     410       27464 :     token_ = kToken_SHL;
     411     1270783 :   } else if (ch == '>' && next_ch == '>') {
     412      438922 :     if (stream_->Advance() == '>') {
     413       28961 :       token_ = kToken_SHR;
     414             :     } else {
     415      190500 :       token_ = kToken_SAR;
     416      190500 :       stream_->Back();
     417             :     }
     418             :   } else {
     419     1051322 :     stream_->Back();
     420     1051324 :     token_ = ch;
     421             :   }
     422     1357599 : }
     423             : 
     424           0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
     425     4544424 :   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     3344466 :   return ch == '.' || IsDecimalDigit(ch);
     432             : }
     433             : 
     434             : }  // namespace internal
     435      183867 : }  // namespace v8

Generated by: LCOV version 1.10