LCOV - code coverage report
Current view: top level - src/asmjs - asm-scanner.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 180 185 97.3 %
Date: 2017-04-26 Functions: 13 15 86.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      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

Generated by: LCOV version 1.10