enum EagerCompileHint { kShouldEagerCompile, kShouldLazyCompile };
+ enum ShouldBeUsedOnceHint { kShouldBeUsedOnce, kDontKnowIfShouldBeUsedOnce };
+
enum ArityRestriction {
NORMAL_ARITY,
GETTER_ARITY,
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(); }
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));
}
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> {
+ };
};
// 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();
}
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());
!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();
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
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();
// 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();
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) {
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();
}
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);
// 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(
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);
}
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);
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());
}
-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();
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;
+ }
}
}
}
-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());
// 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
// 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);
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,
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;
}
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_);
~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
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_;
};
pos_--;
}
+ virtual bool SetBookmark();
+ virtual void ResetToBookmark();
+
protected:
virtual size_t SlowSeekForward(size_t delta) {
// Fast case always handles seeking.
}
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
}
+// 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) {
}
+bool Scanner::SetBookmark() {
+ if (c0_ != kNoBookmark && bookmark_c0_ == kNoBookmark &&
+ source_->SetBookmark()) {
+ bookmark_c0_ = c0_;
+ CopyTokenDesc(&bookmark_current_, ¤t_);
+ 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);
}
// 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;
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;
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) { }
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_) ?
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_;