LCOV - code coverage report
Current view: top level - src/asmjs - asm-scanner.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 178 183 97.3 %
Date: 2017-10-20 Functions: 12 14 85.7 %

          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      346063 : 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     1730316 :       preceded_by_newline_(false) {
      35             : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
      36    13496492 :   STDLIB_MATH_FUNCTION_LIST(V)
      37     5883086 :   STDLIB_ARRAY_TYPE_LIST(V)
      38             : #undef V
      39             : #define V(name, _junk1) property_names_[#name] = kToken_##name;
      40     5883084 :   STDLIB_MATH_VALUE_LIST(V)
      41             : #undef V
      42             : #define V(name) property_names_[#name] = kToken_##name;
      43     2422447 :   STDLIB_OTHER_LIST(V)
      44             : #undef V
      45             : #define V(name) global_names_[#name] = kToken_##name;
      46    12112239 :   KEYWORD_NAME_LIST(V)
      47             : #undef V
      48      346064 :   Next();
      49      346064 : }
      50             : 
      51    10277498 : void AsmJsScanner::Next() {
      52    10277498 :   if (rewind_) {
      53     2222938 :     preceding_token_ = token_;
      54     2222938 :     preceding_position_ = position_;
      55     2222938 :     token_ = next_token_;
      56     2222938 :     position_ = next_position_;
      57     2222938 :     next_token_ = kUninitialized;
      58     2222938 :     next_position_ = 0;
      59     2222938 :     rewind_ = false;
      60     2222938 :     return;
      61             :   }
      62             : 
      63     8054560 :   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     8054560 :   preceded_by_newline_ = false;
      81     8054560 :   preceding_token_ = token_;
      82     8054560 :   preceding_position_ = position_;
      83             : 
      84             :   for (;;) {
      85    28995580 :     position_ = stream_->pos();
      86    14497790 :     uc32 ch = stream_->Advance();
      87    14497790 :     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     1238026 :         preceded_by_newline_ = true;
      98     1238026 :         break;
      99             : 
     100             :       case kEndOfInput:
     101        1024 :         token_ = kEndOfInput;
     102        1024 :         return;
     103             : 
     104             :       case '\'':
     105             :       case '"':
     106       25928 :         ConsumeString(ch);
     107       25928 :         return;
     108             : 
     109             :       case '/':
     110      249947 :         ch = stream_->Advance();
     111      249947 :         if (ch == '/') {
     112      199928 :           ConsumeCPPComment();
     113       50019 :         } else if (ch == '*') {
     114       47553 :           if (!ConsumeCComment()) {
     115           1 :             token_ = kParseError;
     116           1 :             return;
     117             :           }
     118             :         } else {
     119        2466 :           stream_->Back();
     120        2466 :           token_ = '/';
     121        2466 :           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      850856 :         ConsumeCompareOrShift(ch);
     132      850856 :         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     3674499 :         token_ = ch;
     139     3674499 :         return;
     140             : 
     141             :       default:
     142     3499786 :         if (IsIdentifierStart(ch)) {
     143     2279763 :           ConsumeIdentifier(ch);
     144     1220023 :         } else if (IsNumberStart(ch)) {
     145     1219974 :           ConsumeNumber(ch);
     146             :         } else {
     147             :           // TODO(bradnelson): Support unicode (probably via UnicodeCache).
     148          49 :           token_ = kParseError;
     149             :         }
     150             :         return;
     151             :     }
     152             :   }
     153             : }
     154             : 
     155     2222939 : 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     2222939 :   next_token_ = token_;
     162     2222939 :   next_position_ = position_;
     163     2222939 :   token_ = preceding_token_;
     164     2222939 :   position_ = preceding_position_;
     165     2222939 :   preceding_token_ = kUninitialized;
     166     2222939 :   preceding_position_ = 0;
     167     2222939 :   rewind_ = true;
     168     2222939 :   identifier_string_.clear();
     169     2222939 : }
     170             : 
     171       31904 : 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        1119 : void AsmJsScanner::Seek(size_t pos) {
     213        1119 :   stream_->Seek(pos);
     214        1119 :   preceding_token_ = kUninitialized;
     215        1119 :   token_ = kUninitialized;
     216        1119 :   next_token_ = kUninitialized;
     217        1119 :   preceding_position_ = 0;
     218        1119 :   position_ = 0;
     219        1119 :   next_position_ = 0;
     220        1119 :   rewind_ = false;
     221        1119 :   Next();
     222        1119 : }
     223             : 
     224     2279763 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
     225             :   // Consume characters while still part of the identifier.
     226     2279763 :   identifier_string_.clear();
     227    10395678 :   while (IsIdentifierPart(ch)) {
     228     8115913 :     identifier_string_ += ch;
     229     8115913 :     ch = stream_->Advance();
     230             :   }
     231             :   // Go back one for next time.
     232     2279763 :   stream_->Back();
     233             : 
     234             :   // Decode what the identifier means.
     235     2279762 :   if (preceding_token_ == '.') {
     236             :     auto i = property_names_.find(identifier_string_);
     237       19699 :     if (i != property_names_.end()) {
     238       16307 :       token_ = i->second;
     239             :       return;
     240             :     }
     241             :   } else {
     242             :     {
     243             :       auto i = local_names_.find(identifier_string_);
     244     2260063 :       if (i != local_names_.end()) {
     245      948015 :         token_ = i->second;
     246             :         return;
     247             :       }
     248             :     }
     249     1312048 :     if (!in_local_scope_) {
     250             :       auto i = global_names_.find(identifier_string_);
     251     1229687 :       if (i != global_names_.end()) {
     252      936555 :         token_ = i->second;
     253             :         return;
     254             :       }
     255             :     }
     256             :   }
     257      378885 :   if (preceding_token_ == '.') {
     258        3392 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     259        3392 :     token_ = kGlobalsStart + global_count_++;
     260        3392 :     property_names_[identifier_string_] = token_;
     261      375493 :   } else if (in_local_scope_) {
     262       82361 :     CHECK_LT(local_names_.size(), kMaxIdentifierCount);
     263       82361 :     token_ = kLocalsStart - static_cast<token_t>(local_names_.size());
     264       82361 :     local_names_[identifier_string_] = token_;
     265             :   } else {
     266      293132 :     CHECK_LT(global_count_, kMaxIdentifierCount);
     267      293132 :     token_ = kGlobalsStart + global_count_++;
     268      293133 :     global_names_[identifier_string_] = token_;
     269             :   }
     270             : }
     271             : 
     272     1219974 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
     273             :   std::string number;
     274     1219974 :   number = ch;
     275     1219974 :   bool has_dot = ch == '.';
     276             :   for (;;) {
     277     2155191 :     ch = stream_->Advance();
     278     5545967 :     if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     279     3692887 :         (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
     280     4598114 :         ch == 'x' ||
     281     1230499 :         ((ch == '-' || ch == '+') && (number[number.size() - 1] == 'e' ||
     282        5566 :                                       number[number.size() - 1] == 'E'))) {
     283             :       // TODO(bradnelson): Test weird cases ending in -.
     284      935217 :       if (ch == '.') {
     285             :         has_dot = true;
     286             :       }
     287      935217 :       number.push_back(ch);
     288             :     } else {
     289             :       break;
     290             :     }
     291             :   }
     292     1219974 :   stream_->Back();
     293             :   // Special case the most common number.
     294     2132693 :   if (number.size() == 1 && number[0] == '0') {
     295      519276 :     unsigned_value_ = 0;
     296      519276 :     token_ = kUnsigned;
     297      519276 :     return;
     298             :   }
     299             :   // Pick out dot.
     300     1094141 :   if (number.size() == 1 && number[0] == '.') {
     301       14962 :     token_ = '.';
     302       14962 :     return;
     303             :   }
     304             :   // Decode numbers.
     305      685736 :   UnicodeCache cache;
     306             :   double_value_ = StringToDouble(
     307             :       &cache,
     308             :       Vector<const uint8_t>(reinterpret_cast<const uint8_t*>(number.data()),
     309             :                             static_cast<int>(number.size())),
     310     1371472 :       ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
     311      685736 :   if (std::isnan(double_value_)) {
     312             :     // Check if string to number conversion didn't consume all the characters.
     313             :     // This happens if the character filter let through something invalid
     314             :     // like: 0123ef for example.
     315             :     // TODO(bradnelson): Check if this happens often enough to be a perf
     316             :     // problem.
     317        5175 :     if (number[0] == '.') {
     318       21504 :       for (size_t k = 1; k < number.size(); ++k) {
     319        8165 :         stream_->Back();
     320             :       }
     321        5174 :       token_ = '.';
     322        5174 :       return;
     323             :     }
     324             :     // Anything else that doesn't parse is an error.
     325           1 :     token_ = kParseError;
     326           1 :     return;
     327             :   }
     328      680561 :   if (has_dot) {
     329       13211 :     token_ = kDouble;
     330             :   } else {
     331             :     // Exceeding safe integer range is an error.
     332      667350 :     if (double_value_ > static_cast<double>(kMaxUInt32)) {
     333        1447 :       token_ = kParseError;
     334        1447 :       return;
     335             :     }
     336      665903 :     unsigned_value_ = static_cast<uint32_t>(double_value_);
     337      665903 :     token_ = kUnsigned;
     338             :   }
     339             : }
     340             : 
     341       47553 : bool AsmJsScanner::ConsumeCComment() {
     342             :   for (;;) {
     343      155769 :     uc32 ch = stream_->Advance();
     344      311854 :     while (ch == '*') {
     345       47868 :       ch = stream_->Advance();
     346       47868 :       if (ch == '/') {
     347             :         return true;
     348             :       }
     349             :     }
     350      108217 :     if (ch == kEndOfInput) {
     351             :       return false;
     352             :     }
     353             :   }
     354             : }
     355             : 
     356      199928 : void AsmJsScanner::ConsumeCPPComment() {
     357             :   for (;;) {
     358    11722961 :     uc32 ch = stream_->Advance();
     359    11722961 :     if (ch == '\n' || ch == kEndOfInput) {
     360      199928 :       return;
     361             :     }
     362             :   }
     363             : }
     364             : 
     365       25928 : void AsmJsScanner::ConsumeString(uc32 quote) {
     366             :   // Only string allowed is 'use asm' / "use asm".
     367             :   const char* expected = "use asm";
     368       96517 :   for (; *expected != '\0'; ++expected) {
     369       91329 :     if (stream_->Advance() != *expected) {
     370       20740 :       token_ = kParseError;
     371       20740 :       return;
     372             :     }
     373             :   }
     374        5188 :   if (stream_->Advance() != quote) {
     375           0 :     token_ = kParseError;
     376           0 :     return;
     377             :   }
     378        5188 :   token_ = kToken_UseAsm;
     379             : }
     380             : 
     381      850856 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
     382      850856 :   uc32 next_ch = stream_->Advance();
     383      850856 :   if (next_ch == '=') {
     384       65711 :     switch (ch) {
     385             :       case '<':
     386        3073 :         token_ = kToken_LE;
     387        3073 :         break;
     388             :       case '>':
     389        3357 :         token_ = kToken_GE;
     390        3357 :         break;
     391             :       case '=':
     392       42087 :         token_ = kToken_EQ;
     393       42087 :         break;
     394             :       case '!':
     395       17194 :         token_ = kToken_NE;
     396       17194 :         break;
     397             :       default:
     398           0 :         UNREACHABLE();
     399             :     }
     400      785145 :   } else if (ch == '<' && next_ch == '<') {
     401       30391 :     token_ = kToken_SHL;
     402      754754 :   } else if (ch == '>' && next_ch == '>') {
     403      243478 :     if (stream_->Advance() == '>') {
     404       32527 :       token_ = kToken_SHR;
     405             :     } else {
     406      210951 :       token_ = kToken_SAR;
     407      210951 :       stream_->Back();
     408             :     }
     409             :   } else {
     410      511276 :     stream_->Back();
     411      511276 :     token_ = ch;
     412             :   }
     413      850856 : }
     414             : 
     415           0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
     416    18964983 :   return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
     417     5069520 :          ch == '$';
     418             : }
     419             : 
     420    10395677 : bool AsmJsScanner::IsIdentifierPart(uc32 ch) {
     421    10395677 :   return IsIdentifierStart(ch) || (ch >= '0' && ch <= '9');
     422             : }
     423             : 
     424           0 : bool AsmJsScanner::IsNumberStart(uc32 ch) {
     425     1220023 :   return ch == '.' || (ch >= '0' && ch <= '9');
     426             : }
     427             : 
     428             : }  // namespace internal
     429             : }  // namespace v8

Generated by: LCOV version 1.10