Implement a 'trial parse' step, that will abort pre-parsing excessively
authorvogelheim <vogelheim@chromium.org>
Wed, 6 May 2015 10:21:20 +0000 (03:21 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 6 May 2015 10:21:27 +0000 (10:21 +0000)
long and trivial functions, so that they can be eagerly compiled after all.
This essentially allows the parser to renege on its earlier decision to
lazy-parse, if additional information suggests it was a bad decision.

BUG=chromium:470930
LOG=Y

Review URL: https://codereview.chromium.org/1102523003

Cr-Commit-Position: refs/heads/master@{#28252}

src/ast.h
src/compiler.cc
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/scanner-character-streams.cc
src/scanner-character-streams.h
src/scanner.cc
src/scanner.h

index a686f91307e5190b6d8e66414ecc6a17a91a17a2..c669756d5216efbfa032cdda11f51670440470bd 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -2421,6 +2421,8 @@ class FunctionLiteral final : public Expression {
 
   enum EagerCompileHint { kShouldEagerCompile, kShouldLazyCompile };
 
+  enum ShouldBeUsedOnceHint { kShouldBeUsedOnce, kDontKnowIfShouldBeUsedOnce };
+
   enum ArityRestriction {
     NORMAL_ARITY,
     GETTER_ARITY,
@@ -2516,6 +2518,15 @@ class FunctionLiteral final : public Expression {
     bitfield_ = EagerCompileHintBit::update(bitfield_, kShouldEagerCompile);
   }
 
+  // A hint that we expect this function to be called (exactly) once,
+  // i.e. we suspect it's an initialization function.
+  bool should_be_used_once_hint() const {
+    return ShouldBeUsedOnceHintBit::decode(bitfield_) == kShouldBeUsedOnce;
+  }
+  void set_should_be_used_once_hint() {
+    bitfield_ = ShouldBeUsedOnceHintBit::update(bitfield_, kShouldBeUsedOnce);
+  }
+
   FunctionKind kind() { return FunctionKindBits::decode(bitfield_); }
 
   int ast_node_count() { return ast_properties_.node_count(); }
@@ -2560,7 +2571,8 @@ class FunctionLiteral final : public Expression {
                 HasDuplicateParameters::encode(has_duplicate_parameters) |
                 IsFunction::encode(is_function) |
                 EagerCompileHintBit::encode(eager_compile_hint) |
-                FunctionKindBits::encode(kind);
+                FunctionKindBits::encode(kind) |
+                ShouldBeUsedOnceHintBit::encode(kDontKnowIfShouldBeUsedOnce);
     DCHECK(IsValidFunctionKind(kind));
   }
 
@@ -2589,6 +2601,8 @@ class FunctionLiteral final : public Expression {
   class IsFunction : public BitField<IsFunctionFlag, 4, 1> {};
   class EagerCompileHintBit : public BitField<EagerCompileHint, 5, 1> {};
   class FunctionKindBits : public BitField<FunctionKind, 6, 8> {};
+  class ShouldBeUsedOnceHintBit : public BitField<ShouldBeUsedOnceHint, 15, 1> {
+  };
 };
 
 
index 2ad1ff447e26d51a7b4750ab979e8a9b96d0a1bb..53402416c59b3c65759714d752236827c42e3337 100644 (file)
@@ -1371,6 +1371,10 @@ Handle<SharedFunctionInfo> Compiler::BuildFunctionInfo(
     // appropriately sized.
     DCHECK(!info.code().is_null());
     scope_info = ScopeInfo::Create(info.isolate(), info.zone(), info.scope());
+    if (literal->should_eager_compile() &&
+        literal->should_be_used_once_hint()) {
+      info.code()->MarkToBeExecutedOnce(isolate);
+    }
   } else {
     return Handle<SharedFunctionInfo>::null();
   }
index feed1e4b12fcef812309dfe4c4a7870fbcd41a13..3b537d237532747898d75b5b2bb9ecc4465b79d5 100644 (file)
@@ -4036,6 +4036,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
   FunctionLiteral::EagerCompileHint eager_compile_hint =
       parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile
                               : FunctionLiteral::kShouldLazyCompile;
+  bool should_be_used_once_hint = false;
   // Parse function body.
   {
     AstNodeFactory function_factory(ast_value_factory());
@@ -4133,14 +4134,36 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
                              !parenthesized_function_);
     parenthesized_function_ = false;  // The bit was set for this function only.
 
+    // Eager or lazy parse?
+    // If is_lazily_parsed, we'll parse lazy. If we can set a bookmark, we'll
+    // pass it to SkipLazyFunctionBody, which may use it to abort lazy
+    // parsing if it suspect that wasn't a good idea. If so, or if we didn't
+    // try to lazy parse in the first place, we'll have to parse eagerly.
+    Scanner::BookmarkScope bookmark(scanner());
     if (is_lazily_parsed) {
       for (Scope* s = scope_->outer_scope();
            s != nullptr && (s != s->DeclarationScope()); s = s->outer_scope()) {
         s->ForceContextAllocation();
       }
+
+      Scanner::BookmarkScope* maybe_bookmark =
+          bookmark.Set() ? &bookmark : nullptr;
       SkipLazyFunctionBody(&materialized_literal_count,
-                           &expected_property_count, CHECK_OK);
-    } else {
+                           &expected_property_count, /*CHECK_OK*/ ok,
+                           maybe_bookmark);
+
+      if (bookmark.HasBeenReset()) {
+        // Trigger eager (re-)parsing, just below this block.
+        is_lazily_parsed = false;
+
+        // This is probably an initialization function. Inform the compiler it
+        // should also eager-compile this function, and that we expect it to be
+        // used once.
+        eager_compile_hint = FunctionLiteral::kShouldEagerCompile;
+        should_be_used_once_hint = true;
+      }
+    }
+    if (!is_lazily_parsed) {
       body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
                                     kind, CHECK_OK);
       materialized_literal_count = function_state.materialized_literal_count();
@@ -4183,6 +4206,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
       num_parameters, duplicate_parameters, function_type,
       FunctionLiteral::kIsFunction, eager_compile_hint, kind, pos);
   function_literal->set_function_token_position(function_token_pos);
+  if (should_be_used_once_hint)
+    function_literal->set_should_be_used_once_hint();
 
   if (scope->has_rest_parameter()) {
     // TODO(caitp): enable optimization of functions with rest params
@@ -4195,8 +4220,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
 
 
 void Parser::SkipLazyFunctionBody(int* materialized_literal_count,
-                                  int* expected_property_count,
-                                  bool* ok) {
+                                  int* expected_property_count, bool* ok,
+                                  Scanner::BookmarkScope* bookmark) {
+  DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet());
   if (produce_cached_parse_data()) CHECK(log_);
 
   int function_block_pos = position();
@@ -4229,7 +4255,10 @@ void Parser::SkipLazyFunctionBody(int* materialized_literal_count,
   // AST. This gathers the data needed to build a lazy function.
   SingletonLogger logger;
   PreParser::PreParseResult result =
-      ParseLazyFunctionBodyWithPreParser(&logger);
+      ParseLazyFunctionBodyWithPreParser(&logger, bookmark);
+  if (bookmark && bookmark->HasBeenReset()) {
+    return;  // Return immediately if pre-parser devided to abort parsing.
+  }
   if (result == PreParser::kPreParseStackOverflow) {
     // Propagate stack overflow.
     set_stack_overflow();
@@ -4358,7 +4387,7 @@ ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
 
 
 PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
-    SingletonLogger* logger) {
+    SingletonLogger* logger, Scanner::BookmarkScope* bookmark) {
   // This function may be called on a background thread too; record only the
   // main thread preparse times.
   if (pre_parse_timer_ != NULL) {
@@ -4390,7 +4419,7 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
     reusable_preparser_->set_allow_strong_mode(allow_strong_mode());
   }
   PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
-      language_mode(), function_state_->kind(), logger);
+      language_mode(), function_state_->kind(), logger, bookmark);
   if (pre_parse_timer_ != NULL) {
     pre_parse_timer_->Stop();
   }
index 58253e1bb6919309a444864f10bfff904efb837f..1a84ec4157aff1210e7a8aeb2458d6d4cd91a2e8 100644 (file)
@@ -773,8 +773,9 @@ class ParserTraits {
       bool name_is_strict_reserved, FunctionKind kind,
       int function_token_position, FunctionLiteral::FunctionType type,
       FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
-  V8_INLINE void SkipLazyFunctionBody(int* materialized_literal_count,
-                                      int* expected_property_count, bool* ok);
+  V8_INLINE void SkipLazyFunctionBody(
+      int* materialized_literal_count, int* expected_property_count, bool* ok,
+      Scanner::BookmarkScope* bookmark = nullptr);
   V8_INLINE ZoneList<Statement*>* ParseEagerFunctionBody(
       const AstRawString* name, int pos, Variable* fvar,
       Token::Value fvar_init_op, FunctionKind kind, bool* ok);
@@ -1023,12 +1024,16 @@ class Parser : public ParserBase<ParserTraits> {
 
   // Skip over a lazy function, either using cached data if we have it, or
   // by parsing the function with PreParser. Consumes the ending }.
+  //
+  // If bookmark is set, the (pre-)parser may decide to abort skipping
+  // in order to force the function to be eagerly parsed, after all.
+  // In this case, it'll reset the scanner using the bookmark.
   void SkipLazyFunctionBody(int* materialized_literal_count,
-                            int* expected_property_count,
-                            bool* ok);
+                            int* expected_property_count, bool* ok,
+                            Scanner::BookmarkScope* bookmark = nullptr);
 
   PreParser::PreParseResult ParseLazyFunctionBodyWithPreParser(
-      SingletonLogger* logger);
+      SingletonLogger* logger, Scanner::BookmarkScope* bookmark = nullptr);
 
   // Consumes the ending }.
   ZoneList<Statement*>* ParseEagerFunctionBody(
@@ -1089,10 +1094,10 @@ const AstRawString* ParserTraits::EmptyIdentifierString() {
 
 
 void ParserTraits::SkipLazyFunctionBody(int* materialized_literal_count,
-                                        int* expected_property_count,
-                                        bool* ok) {
-  return parser_->SkipLazyFunctionBody(
-      materialized_literal_count, expected_property_count, ok);
+                                        int* expected_property_count, bool* ok,
+                                        Scanner::BookmarkScope* bookmark) {
+  return parser_->SkipLazyFunctionBody(materialized_literal_count,
+                                       expected_property_count, ok, bookmark);
 }
 
 
index 95339dac33ca08b6b87bdf4bfed467c294a93280..e3c421ea140b8c3da40156c548880df358a2d4c1 100644 (file)
@@ -99,7 +99,8 @@ PreParserExpression PreParserTraits::ParseFunctionLiteral(
 
 
 PreParser::PreParseResult PreParser::PreParseLazyFunction(
-    LanguageMode language_mode, FunctionKind kind, ParserRecorder* log) {
+    LanguageMode language_mode, FunctionKind kind, ParserRecorder* log,
+    Scanner::BookmarkScope* bookmark) {
   log_ = log;
   // Lazy functions always have trivial outer scopes (no with/catch scopes).
   Scope* top_scope = NewScope(scope_, SCRIPT_SCOPE);
@@ -114,9 +115,12 @@ PreParser::PreParseResult PreParser::PreParseLazyFunction(
   DCHECK_EQ(Token::LBRACE, scanner()->current_token());
   bool ok = true;
   int start_position = peek_position();
-  ParseLazyFunctionLiteralBody(&ok);
-  if (stack_overflow()) return kPreParseStackOverflow;
-  if (!ok) {
+  ParseLazyFunctionLiteralBody(&ok, bookmark);
+  if (bookmark && bookmark->HasBeenReset()) {
+    ;  // Do nothing, as we've just aborted scanning this function.
+  } else if (stack_overflow()) {
+    return kPreParseStackOverflow;
+  } else if (!ok) {
     ReportUnexpectedToken(scanner()->current_token());
   } else {
     DCHECK_EQ(Token::RBRACE, scanner()->peek());
@@ -196,15 +200,22 @@ PreParser::Statement PreParser::ParseStatementListItem(bool* ok) {
 }
 
 
-void PreParser::ParseStatementList(int end_token, bool* ok) {
+void PreParser::ParseStatementList(int end_token, bool* ok,
+                                   Scanner::BookmarkScope* bookmark) {
   // SourceElements ::
   //   (Statement)* <end_token>
 
+  // Bookkeeping for trial parse if bookmark is set:
+  DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet());
+  bool maybe_reset = bookmark != nullptr;
+  int count_statements = 0;
+
   bool directive_prologue = true;
   while (peek() != end_token) {
     if (directive_prologue && peek() != Token::STRING) {
       directive_prologue = false;
     }
+    bool starts_with_identifier = peek() == Token::IDENTIFIER;
     Scanner::Location token_loc = scanner()->peek_location();
     Scanner::Location old_this_loc = function_state_->this_location();
     Scanner::Location old_super_loc = function_state_->super_location();
@@ -241,6 +252,20 @@ void PreParser::ParseStatementList(int end_token, bool* ok) {
         directive_prologue = false;
       }
     }
+
+    // If we're allowed to reset to a bookmark, we will do so when we see a long
+    // and trivial function.
+    // Our current definition of 'long and trivial' is:
+    // - over 200 statements
+    // - all starting with an identifier (i.e., no if, for, while, etc.)
+    if (maybe_reset && (!starts_with_identifier ||
+                        ++count_statements > kLazyParseTrialLimit)) {
+      if (count_statements > kLazyParseTrialLimit) {
+        bookmark->Reset();
+        return;
+      }
+      maybe_reset = false;
+    }
   }
 }
 
@@ -1057,10 +1082,12 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
 }
 
 
-void PreParser::ParseLazyFunctionLiteralBody(bool* ok) {
+void PreParser::ParseLazyFunctionLiteralBody(bool* ok,
+                                             Scanner::BookmarkScope* bookmark) {
   int body_start = position();
-  ParseStatementList(Token::RBRACE, ok);
+  ParseStatementList(Token::RBRACE, ok, bookmark);
   if (!*ok) return;
+  if (bookmark && bookmark->HasBeenReset()) return;
 
   // Position right after terminal '}'.
   DCHECK_EQ(Token::RBRACE, scanner()->peek());
index 8184bd8d5f5af2bc93a872c80c71ab3b7cce8001..c84009245457610b52a55ef41d8de557f8649c89 100644 (file)
@@ -1821,12 +1821,15 @@ class PreParser : public ParserBase<PreParserTraits> {
   // keyword and parameters, and have consumed the initial '{'.
   // At return, unless an error occurred, the scanner is positioned before the
   // the final '}'.
-  PreParseResult PreParseLazyFunction(LanguageMode language_mode,
-                                      FunctionKind kind, ParserRecorder* log);
+  PreParseResult PreParseLazyFunction(
+      LanguageMode language_mode, FunctionKind kind, ParserRecorder* log,
+      Scanner::BookmarkScope* bookmark = nullptr);
 
  private:
   friend class PreParserTraits;
 
+  static const int kLazyParseTrialLimit = 200;
+
   // These types form an algebra over syntactic categories that is just
   // rich enough to let us recognize and propagate the constructs that
   // are either being counted in the preparser data, or is important
@@ -1837,7 +1840,8 @@ class PreParser : public ParserBase<PreParserTraits> {
   // By making the 'exception handling' explicit, we are forced to check
   // for failure at the call sites.
   Statement ParseStatementListItem(bool* ok);
-  void ParseStatementList(int end_token, bool* ok);
+  void ParseStatementList(int end_token, bool* ok,
+                          Scanner::BookmarkScope* bookmark = nullptr);
   Statement ParseStatement(bool* ok);
   Statement ParseSubStatement(bool* ok);
   Statement ParseFunctionDeclaration(bool* ok);
@@ -1879,7 +1883,8 @@ class PreParser : public ParserBase<PreParserTraits> {
       bool name_is_strict_reserved, FunctionKind kind, int function_token_pos,
       FunctionLiteral::FunctionType function_type,
       FunctionLiteral::ArityRestriction arity_restriction, bool* ok);
-  void ParseLazyFunctionLiteralBody(bool* ok);
+  void ParseLazyFunctionLiteralBody(bool* ok,
+                                    Scanner::BookmarkScope* bookmark = nullptr);
 
   PreParserExpression ParseClassLiteral(PreParserIdentifier name,
                                         Scanner::Location class_name_location,
index cc4a18b540f3555a2ea53ab9b9c66147f9979b99..a710330a2be65c288df06ad6f1ec4741e8f52ee9 100644 (file)
@@ -130,7 +130,7 @@ size_t BufferedUtf16CharacterStream::SlowSeekForward(size_t delta) {
 
 GenericStringUtf16CharacterStream::GenericStringUtf16CharacterStream(
     Handle<String> data, size_t start_position, size_t end_position)
-    : string_(data), length_(end_position) {
+    : string_(data), length_(end_position), bookmark_(kNoBookmark) {
   DCHECK(end_position >= start_position);
   pos_ = start_position;
 }
@@ -139,6 +139,20 @@ GenericStringUtf16CharacterStream::GenericStringUtf16CharacterStream(
 GenericStringUtf16CharacterStream::~GenericStringUtf16CharacterStream() { }
 
 
+bool GenericStringUtf16CharacterStream::SetBookmark() {
+  bookmark_ = pos_;
+  return true;
+}
+
+
+void GenericStringUtf16CharacterStream::ResetToBookmark() {
+  DCHECK(bookmark_ != kNoBookmark);
+  pos_ = bookmark_;
+  buffer_cursor_ = buffer_;
+  buffer_end_ = buffer_ + FillBuffer(pos_);
+}
+
+
 size_t GenericStringUtf16CharacterStream::BufferSeekForward(size_t delta) {
   size_t old_pos = pos_;
   pos_ = Min(pos_ + delta, length_);
@@ -447,17 +461,29 @@ ExternalTwoByteStringUtf16CharacterStream::
     ~ExternalTwoByteStringUtf16CharacterStream() { }
 
 
-ExternalTwoByteStringUtf16CharacterStream
-    ::ExternalTwoByteStringUtf16CharacterStream(
-        Handle<ExternalTwoByteString> data,
-        int start_position,
+ExternalTwoByteStringUtf16CharacterStream::
+    ExternalTwoByteStringUtf16CharacterStream(
+        Handle<ExternalTwoByteString> data, int start_position,
         int end_position)
     : Utf16CharacterStream(),
       source_(data),
-      raw_data_(data->GetTwoByteData(start_position)) {
+      raw_data_(data->GetTwoByteData(start_position)),
+      bookmark_(kNoBookmark) {
   buffer_cursor_ = raw_data_,
   buffer_end_ = raw_data_ + (end_position - start_position);
   pos_ = start_position;
 }
 
+
+bool ExternalTwoByteStringUtf16CharacterStream::SetBookmark() {
+  bookmark_ = pos_;
+  return true;
+}
+
+
+void ExternalTwoByteStringUtf16CharacterStream::ResetToBookmark() {
+  DCHECK(bookmark_ != kNoBookmark);
+  pos_ = bookmark_;
+  buffer_cursor_ = raw_data_ + bookmark_;
+}
 } }  // namespace v8::internal
index 5944d7644572320f33498082ca362d0cdb2df564..36d84bc0f6b5d82a16b005bef0ea737138642a1f 100644 (file)
@@ -43,12 +43,18 @@ class GenericStringUtf16CharacterStream: public BufferedUtf16CharacterStream {
                                     size_t end_position);
   virtual ~GenericStringUtf16CharacterStream();
 
+  virtual bool SetBookmark();
+  virtual void ResetToBookmark();
+
  protected:
+  static const size_t kNoBookmark = -1;
+
   virtual size_t BufferSeekForward(size_t delta);
   virtual size_t FillBuffer(size_t position);
 
   Handle<String> string_;
   size_t length_;
+  size_t bookmark_;
 };
 
 
@@ -129,6 +135,9 @@ class ExternalTwoByteStringUtf16CharacterStream: public Utf16CharacterStream {
     pos_--;
   }
 
+  virtual bool SetBookmark();
+  virtual void ResetToBookmark();
+
  protected:
   virtual size_t SlowSeekForward(size_t delta) {
     // Fast case always handles seeking.
@@ -140,6 +149,11 @@ class ExternalTwoByteStringUtf16CharacterStream: public Utf16CharacterStream {
   }
   Handle<ExternalTwoByteString> source_;
   const uc16* raw_data_;  // Pointer to the actual array of characters.
+
+ private:
+  static const size_t kNoBookmark = -1;
+
+  size_t bookmark_;
 };
 
 } }  // namespace v8::internal
index aa5ae4a5e93c317dd804d92420b792fdf0707221..3092e33da83b8663944ca508100857650a541da2 100644 (file)
@@ -29,15 +29,26 @@ Handle<String> LiteralBuffer::Internalize(Isolate* isolate) const {
 }
 
 
+// Default implementation for streams that do not support bookmarks.
+bool Utf16CharacterStream::SetBookmark() { return false; }
+void Utf16CharacterStream::ResetToBookmark() { UNREACHABLE(); }
+
+
 // ----------------------------------------------------------------------------
 // Scanner
 
 Scanner::Scanner(UnicodeCache* unicode_cache)
     : unicode_cache_(unicode_cache),
+      bookmark_c0_(kNoBookmark),
       octal_pos_(Location::invalid()),
       harmony_modules_(false),
       harmony_classes_(false),
-      harmony_unicode_(false) {}
+      harmony_unicode_(false) {
+  bookmark_current_.literal_chars = &bookmark_current_literal_;
+  bookmark_current_.raw_literal_chars = &bookmark_current_raw_literal_;
+  bookmark_next_.literal_chars = &bookmark_next_literal_;
+  bookmark_next_.raw_literal_chars = &bookmark_next_raw_literal_;
+}
 
 
 void Scanner::Initialize(Utf16CharacterStream* source) {
@@ -1426,6 +1437,56 @@ int Scanner::FindSymbol(DuplicateFinder* finder, int value) {
 }
 
 
+bool Scanner::SetBookmark() {
+  if (c0_ != kNoBookmark && bookmark_c0_ == kNoBookmark &&
+      source_->SetBookmark()) {
+    bookmark_c0_ = c0_;
+    CopyTokenDesc(&bookmark_current_, &current_);
+    CopyTokenDesc(&bookmark_next_, &next_);
+    return true;
+  }
+  return false;
+}
+
+
+void Scanner::ResetToBookmark() {
+  DCHECK(BookmarkHasBeenSet());  // Caller hasn't called SetBookmark.
+
+  source_->ResetToBookmark();
+  c0_ = bookmark_c0_;
+  StartLiteral();
+  StartRawLiteral();
+  CopyTokenDesc(&next_, &bookmark_current_);
+  current_ = next_;
+  StartLiteral();
+  StartRawLiteral();
+  CopyTokenDesc(&next_, &bookmark_next_);
+
+  bookmark_c0_ = kBookmarkWasApplied;
+}
+
+
+bool Scanner::BookmarkHasBeenSet() { return bookmark_c0_ >= 0; }
+
+
+bool Scanner::BookmarkHasBeenReset() {
+  return bookmark_c0_ == kBookmarkWasApplied;
+}
+
+
+void Scanner::DropBookmark() { bookmark_c0_ = kNoBookmark; }
+
+
+void Scanner::CopyTokenDesc(TokenDesc* to, TokenDesc* from) {
+  DCHECK_NOT_NULL(to);
+  DCHECK_NOT_NULL(from);
+  to->token = from->token;
+  to->location = from->location;
+  to->literal_chars->CopyFrom(from->literal_chars);
+  to->raw_literal_chars->CopyFrom(from->raw_literal_chars);
+}
+
+
 int DuplicateFinder::AddOneByteSymbol(Vector<const uint8_t> key, int value) {
   return AddSymbol(key, true, value);
 }
index ef5f01baec03a228c9517ae8b862024f9e10832b..f387d84133f984ff5183b6b802d65cf5cb3293bb 100644 (file)
@@ -89,6 +89,9 @@ class Utf16CharacterStream {
   // Must not be used right after calling SeekForward.
   virtual void PushBack(int32_t code_unit) = 0;
 
+  virtual bool SetBookmark();
+  virtual void ResetToBookmark();
+
  protected:
   static const uc32 kEndOfInput = -1;
 
@@ -268,6 +271,17 @@ class LiteralBuffer {
 
   Handle<String> Internalize(Isolate* isolate) const;
 
+  void CopyFrom(const LiteralBuffer* other) {
+    if (other == nullptr) {
+      Reset();
+    } else {
+      is_one_byte_ = other->is_one_byte_;
+      position_ = other->position_;
+      backing_store_.Dispose();
+      backing_store_ = other->backing_store_.Clone();
+    }
+  }
+
  private:
   static const int kInitialCapacity = 16;
   static const int kGrowthFactory = 4;
@@ -342,6 +356,25 @@ class Scanner {
     bool complete_;
   };
 
+  // Scoped helper for a re-settable bookmark.
+  class BookmarkScope {
+   public:
+    explicit BookmarkScope(Scanner* scanner) : scanner_(scanner) {
+      DCHECK_NOT_NULL(scanner_);
+    }
+    ~BookmarkScope() { scanner_->DropBookmark(); }
+
+    bool Set() { return scanner_->SetBookmark(); }
+    void Reset() { scanner_->ResetToBookmark(); }
+    bool HasBeenSet() { return scanner_->BookmarkHasBeenSet(); }
+    bool HasBeenReset() { return scanner_->BookmarkHasBeenReset(); }
+
+   private:
+    Scanner* scanner_;
+
+    DISALLOW_COPY_AND_ASSIGN(BookmarkScope);
+  };
+
   // Representation of an interval of source positions.
   struct Location {
     Location(int b, int e) : beg_pos(b), end_pos(e) { }
@@ -510,6 +543,14 @@ class Scanner {
     current_.raw_literal_chars = NULL;
   }
 
+  // Support BookmarkScope functionality.
+  bool SetBookmark();
+  void ResetToBookmark();
+  bool BookmarkHasBeenSet();
+  bool BookmarkHasBeenReset();
+  void DropBookmark();
+  static void CopyTokenDesc(TokenDesc* to, TokenDesc* from);
+
   // Literal buffer support
   inline void StartLiteral() {
     LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ?
@@ -712,6 +753,37 @@ class Scanner {
   TokenDesc current_;  // desc for current token (as returned by Next())
   TokenDesc next_;     // desc for next token (one token look-ahead)
 
+  // Variables for Scanner::BookmarkScope and the *Bookmark implementation.
+  // These variables contain the scanner state when a bookmark is set.
+  //
+  // We will use bookmark_c0_ as a 'control' variable, where:
+  // - bookmark_c0_ >= 0: A bookmark has been set and this contains c0_.
+  // - bookmark_c0_ == -1: No bookmark has been set.
+  // - bookmark_c0_ == -2: The bookmark has been applied (ResetToBookmark).
+  //
+  // Which state is being bookmarked? The parser state is distributed over
+  // several variables, roughly like this:
+  //   ...    1234        +       5678 ..... [character stream]
+  //       [current_] [next_] c0_ |      [scanner state]
+  // So when the scanner is logically at the beginning of an expression
+  // like "1234 + 4567", then:
+  // - current_ contains "1234"
+  // - next_ contains "+"
+  // - c0_ contains ' ' (the space between "+" and "5678",
+  // - the source_ character stream points to the beginning of "5678".
+  // To be able to restore this state, we will keep copies of current_, next_,
+  // and c0_; we'll ask the stream to bookmark itself, and we'll copy the
+  // contents of current_'s and next_'s literal buffers to bookmark_*_literal_.
+  static const uc32 kNoBookmark = -1;
+  static const uc32 kBookmarkWasApplied = -2;
+  uc32 bookmark_c0_;
+  TokenDesc bookmark_current_;
+  TokenDesc bookmark_next_;
+  LiteralBuffer bookmark_current_literal_;
+  LiteralBuffer bookmark_current_raw_literal_;
+  LiteralBuffer bookmark_next_literal_;
+  LiteralBuffer bookmark_next_raw_literal_;
+
   // Input stream. Must be initialized to an Utf16CharacterStream.
   Utf16CharacterStream* source_;