LCOV - code coverage report
Current view: top level - src/asmjs - asm-scanner.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 177 182 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      350870 : 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     1754356 :       preceded_by_newline_(false) {
      34             : #define V(name, _junk1, _junk2, _junk3) property_names_[#name] = kToken_##name;
      35    13684006 :   STDLIB_MATH_FUNCTION_LIST(V)
      36     5964823 :   STDLIB_ARRAY_TYPE_LIST(V)
      37             : #undef V
      38             : #define V(name, _junk1) property_names_[#name] = kToken_##name;
      39     5964822 :   STDLIB_MATH_VALUE_LIST(V)
      40             : #undef V
      41             : #define V(name) property_names_[#name] = kToken_##name;
      42     2456104 :   STDLIB_OTHER_LIST(V)
      43             : #undef V
      44             : #define V(name) global_names_[#name] = kToken_##name;
      45    12280519 :   KEYWORD_NAME_LIST(V)
      46             : #undef V
      47      350872 : }
      48             : 
      49      350872 : void AsmJsScanner::SetStream(std::unique_ptr<Utf16CharacterStream> stream) {
      50             :   stream_ = std::move(stream);
      51      350872 :   Next();
      52      350872 : }
      53             : 
      54     2143666 : void AsmJsScanner::Next() {
      55     2143666 :   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     1962617 :   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("%" PRIu64 " ", AsUnsigned());
      76             :     } else {
      77             :       std::string name = Name(Token());
      78             :       PrintF("%s ", name.c_str());
      79             :     }
      80             :   }
      81             : #endif
      82             : 
      83     1962617 :   preceded_by_newline_ = false;
      84     1962617 :   preceding_token_ = token_;
      85     1962617 :   preceding_position_ = position_;
      86             : 
      87             :   for (;;) {
      88     4002021 :     position_ = stream_->pos();
      89     4002021 :     uc32 ch = stream_->Advance();
      90     4002021 :     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      788957 :         preceded_by_newline_ = true;
     101      788957 :         break;
     102             : 
     103             :       case kEndOfInput:
     104         930 :         token_ = kEndOfInput;
     105         930 :         return;
     106             : 
     107             :       case '\'':
     108             :       case '"':
     109       21770 :         ConsumeString(ch);
     110       21770 :         return;
     111             : 
     112             :       case '/':
     113      236713 :         ch = stream_->Advance();
     114      236713 :         if (ch == '/') {
     115      188310 :           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       87938 :         ConsumeCompareOrShift(ch);
     135       87938 :         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     1033419 :         token_ = ch;
     142     1033419 :         return;
     143             : 
     144             :       default:
     145      817644 :         if (IsIdentifierStart(ch)) {
     146      679721 :           ConsumeIdentifier(ch);
     147      137923 :         } else if (IsNumberStart(ch)) {
     148      137874 :           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        4114 : 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      679721 : void AsmJsScanner::ConsumeIdentifier(uc32 ch) {
     228             :   // Consume characters while still part of the identifier.
     229      679721 :   identifier_string_.clear();
     230     3652398 :   while (IsIdentifierPart(ch)) {
     231     2972675 :     identifier_string_ += ch;
     232     2972676 :     ch = stream_->Advance();
     233             :   }
     234             :   // Go back one for next time.
     235      679722 :   stream_->Back();
     236             : 
     237             :   // Decode what the identifier means.
     238      679722 :   if (preceding_token_ == '.') {
     239             :     auto i = property_names_.find(identifier_string_);
     240        3038 :     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      676684 :       if (i != local_names_.end()) {
     248       95169 :         token_ = i->second;
     249             :         return;
     250             :       }
     251             :     }
     252      581515 :     if (!in_local_scope_) {
     253             :       auto i = global_names_.find(identifier_string_);
     254      572943 :       if (i != global_names_.end()) {
     255      295149 :         token_ = i->second;
     256             :         return;
     257             :       }
     258             :     }
     259             :   }
     260      286915 :   if (preceding_token_ == '.') {
     261         550 :     CHECK(global_count_ < kMaxIdentifierCount);
     262         550 :     token_ = kGlobalsStart + global_count_++;
     263         550 :     property_names_[identifier_string_] = token_;
     264      286365 :   } 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      277794 :     CHECK(global_count_ < kMaxIdentifierCount);
     270      277794 :     token_ = kGlobalsStart + global_count_++;
     271      277795 :     global_names_[identifier_string_] = token_;
     272             :   }
     273             : }
     274             : 
     275      137874 : void AsmJsScanner::ConsumeNumber(uc32 ch) {
     276             :   std::string number;
     277      137874 :   number = ch;
     278      137874 :   bool has_dot = ch == '.';
     279             :   for (;;) {
     280      296504 :     ch = stream_->Advance();
     281      734037 :     if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') ||
     282      420299 :         (ch >= 'A' && ch <= 'F') || ch == '.' || ch == 'b' || ch == 'o' ||
     283      572827 :         ch == 'x' ||
     284      138304 :         ((ch == '-' || ch == '+') && (number[number.size() - 1] == 'e' ||
     285          50 :                                       number[number.size() - 1] == 'E'))) {
     286             :       // TODO(bradnelson): Test weird cases ending in -.
     287      158630 :       if (ch == '.') {
     288             :         has_dot = true;
     289             :       }
     290      158630 :       number.push_back(ch);
     291             :     } else {
     292             :       break;
     293             :     }
     294             :   }
     295      137874 :   stream_->Back();
     296             :   // Special case the most common number.
     297      228989 :   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      128137 :   if (number.size() == 1 && number[0] == '.') {
     304        2196 :     token_ = '.';
     305        2196 :     return;
     306             :   }
     307             :   // Decode numbers.
     308       85252 :   UnicodeCache cache;
     309             :   double_value_ = StringToDouble(
     310             :       &cache,
     311             :       Vector<uint8_t>(
     312             :           const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(number.data())),
     313             :           static_cast<int>(number.size())),
     314       85252 :       ALLOW_HEX | ALLOW_OCTAL | ALLOW_BINARY | ALLOW_IMPLICIT_OCTAL);
     315       85252 :   if (std::isnan(double_value_)) {
     316             :     // Check if string to number conversion didn't consume all the characters.
     317             :     // This happens if the character filter let through something invalid
     318             :     // like: 0123ef for example.
     319             :     // TODO(bradnelson): Check if this happens often enough to be a perf
     320             :     // problem.
     321        1243 :     if (number[0] == '.') {
     322        5814 :       for (size_t k = 1; k < number.size(); ++k) {
     323        2286 :         stream_->Back();
     324             :       }
     325        1242 :       token_ = '.';
     326        1242 :       return;
     327             :     }
     328             :     // Anything else that doesn't parse is an error.
     329           1 :     token_ = kParseError;
     330           1 :     return;
     331             :   }
     332       84009 :   if (has_dot) {
     333        1971 :     token_ = kDouble;
     334             :   } else {
     335       82038 :     unsigned_value_ = static_cast<uint32_t>(double_value_);
     336       82038 :     token_ = kUnsigned;
     337             :   }
     338             : }
     339             : 
     340       47488 : bool AsmJsScanner::ConsumeCComment() {
     341             :   for (;;) {
     342      155358 :     uc32 ch = stream_->Advance();
     343      311026 :     while (ch == '*') {
     344       47797 :       ch = stream_->Advance();
     345       47797 :       if (ch == '/') {
     346             :         return true;
     347             :       }
     348             :     }
     349      107871 :     if (ch == kEndOfInput) {
     350             :       return false;
     351             :     }
     352             :   }
     353             : }
     354             : 
     355      188310 : void AsmJsScanner::ConsumeCPPComment() {
     356             :   for (;;) {
     357    11074589 :     uc32 ch = stream_->Advance();
     358    11074589 :     if (ch == '\n' || ch == kEndOfInput) {
     359      188310 :       return;
     360             :     }
     361             :   }
     362             : }
     363             : 
     364       21770 : void AsmJsScanner::ConsumeString(uc32 quote) {
     365             :   // Only string allowed is 'use asm' / "use asm".
     366             :   const char* expected = "use asm";
     367       63094 :   for (; *expected != '\0'; ++expected) {
     368       62181 :     if (stream_->Advance() != *expected) {
     369       20857 :       token_ = kParseError;
     370       20857 :       return;
     371             :     }
     372             :   }
     373         913 :   if (stream_->Advance() != quote) {
     374           0 :     token_ = kParseError;
     375           0 :     return;
     376             :   }
     377         913 :   token_ = kToken_UseAsm;
     378             : }
     379             : 
     380       87938 : void AsmJsScanner::ConsumeCompareOrShift(uc32 ch) {
     381       87938 :   uc32 next_ch = stream_->Advance();
     382       87938 :   if (next_ch == '=') {
     383        6318 :     switch (ch) {
     384             :       case '<':
     385         296 :         token_ = kToken_LE;
     386         296 :         break;
     387             :       case '>':
     388         210 :         token_ = kToken_GE;
     389         210 :         break;
     390             :       case '=':
     391        4132 :         token_ = kToken_EQ;
     392        4132 :         break;
     393             :       case '!':
     394        1680 :         token_ = kToken_NE;
     395        1680 :         break;
     396             :       default:
     397           0 :         UNREACHABLE();
     398             :     }
     399       81620 :   } else if (ch == '<' && next_ch == '<') {
     400        2925 :     token_ = kToken_SHL;
     401       78695 :   } else if (ch == '>' && next_ch == '>') {
     402       24050 :     if (stream_->Advance() == '>') {
     403        3564 :       token_ = kToken_SHR;
     404             :     } else {
     405       20486 :       token_ = kToken_SAR;
     406       20486 :       stream_->Back();
     407             :     }
     408             :   } else {
     409       54645 :     stream_->Back();
     410       54645 :     token_ = ch;
     411             :   }
     412       87938 : }
     413             : 
     414           0 : bool AsmJsScanner::IsIdentifierStart(uc32 ch) {
     415     5536677 :   return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' ||
     416     1066636 :          ch == '$';
     417             : }
     418             : 
     419     3652397 : bool AsmJsScanner::IsIdentifierPart(uc32 ch) {
     420     3652397 :   return IsIdentifierStart(ch) || (ch >= '0' && ch <= '9');
     421             : }
     422             : 
     423           0 : bool AsmJsScanner::IsNumberStart(uc32 ch) {
     424      137923 :   return ch == '.' || (ch >= '0' && ch <= '9');
     425             : }
     426             : 
     427             : }  // namespace internal
     428             : }  // namespace v8

Generated by: LCOV version 1.10