2012-01-26 21:47:57 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
2014-04-29 06:42:26 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/v8.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/api.h"
|
|
|
|
#include "src/ast.h"
|
2014-06-30 13:25:46 +00:00
|
|
|
#include "src/base/platform/platform.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/bootstrapper.h"
|
|
|
|
#include "src/char-predicates-inl.h"
|
|
|
|
#include "src/codegen.h"
|
|
|
|
#include "src/compiler.h"
|
|
|
|
#include "src/messages.h"
|
|
|
|
#include "src/parser.h"
|
|
|
|
#include "src/preparser.h"
|
|
|
|
#include "src/runtime.h"
|
|
|
|
#include "src/scanner-character-streams.h"
|
|
|
|
#include "src/scopeinfo.h"
|
|
|
|
#include "src/string-stream.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-25 10:05:56 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2012-06-04 14:42:58 +00:00
|
|
|
RegExpBuilder::RegExpBuilder(Zone* zone)
|
|
|
|
: zone_(zone),
|
2011-04-04 06:29:02 +00:00
|
|
|
pending_empty_(false),
|
|
|
|
characters_(NULL),
|
|
|
|
terms_(),
|
|
|
|
alternatives_()
|
2008-11-25 11:07:48 +00:00
|
|
|
#ifdef DEBUG
|
2011-04-04 06:29:02 +00:00
|
|
|
, last_added_(ADD_NONE)
|
2008-11-25 11:07:48 +00:00
|
|
|
#endif
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::FlushCharacters() {
|
|
|
|
pending_empty_ = false;
|
|
|
|
if (characters_ != NULL) {
|
2011-04-04 06:29:02 +00:00
|
|
|
RegExpTree* atom = new(zone()) RegExpAtom(characters_->ToConstVector());
|
2008-11-25 11:07:48 +00:00
|
|
|
characters_ = NULL;
|
2012-06-11 12:42:31 +00:00
|
|
|
text_.Add(atom, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
LAST(ADD_ATOM);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::FlushText() {
|
|
|
|
FlushCharacters();
|
|
|
|
int num_text = text_.length();
|
|
|
|
if (num_text == 0) {
|
|
|
|
return;
|
|
|
|
} else if (num_text == 1) {
|
2012-06-11 12:42:31 +00:00
|
|
|
terms_.Add(text_.last(), zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
} else {
|
2012-06-11 12:42:31 +00:00
|
|
|
RegExpText* text = new(zone()) RegExpText(zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
for (int i = 0; i < num_text; i++)
|
2012-06-11 12:42:31 +00:00
|
|
|
text_.Get(i)->AppendToText(text, zone());
|
|
|
|
terms_.Add(text, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
text_.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::AddCharacter(uc16 c) {
|
|
|
|
pending_empty_ = false;
|
|
|
|
if (characters_ == NULL) {
|
2012-06-11 12:42:31 +00:00
|
|
|
characters_ = new(zone()) ZoneList<uc16>(4, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
characters_->Add(c, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
LAST(ADD_CHAR);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::AddEmpty() {
|
|
|
|
pending_empty_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::AddAtom(RegExpTree* term) {
|
|
|
|
if (term->IsEmpty()) {
|
|
|
|
AddEmpty();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (term->IsTextElement()) {
|
|
|
|
FlushCharacters();
|
2012-06-11 12:42:31 +00:00
|
|
|
text_.Add(term, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
} else {
|
|
|
|
FlushText();
|
2012-06-11 12:42:31 +00:00
|
|
|
terms_.Add(term, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
LAST(ADD_ATOM);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::AddAssertion(RegExpTree* assert) {
|
|
|
|
FlushText();
|
2012-06-11 12:42:31 +00:00
|
|
|
terms_.Add(assert, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
LAST(ADD_ASSERT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::NewAlternative() {
|
|
|
|
FlushTerms();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpBuilder::FlushTerms() {
|
|
|
|
FlushText();
|
|
|
|
int num_terms = terms_.length();
|
|
|
|
RegExpTree* alternative;
|
|
|
|
if (num_terms == 0) {
|
|
|
|
alternative = RegExpEmpty::GetInstance();
|
|
|
|
} else if (num_terms == 1) {
|
|
|
|
alternative = terms_.last();
|
|
|
|
} else {
|
2012-06-11 12:42:31 +00:00
|
|
|
alternative = new(zone()) RegExpAlternative(terms_.GetList(zone()));
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
alternatives_.Add(alternative, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
terms_.Clear();
|
|
|
|
LAST(ADD_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RegExpTree* RegExpBuilder::ToRegExp() {
|
|
|
|
FlushTerms();
|
|
|
|
int num_alternatives = alternatives_.length();
|
|
|
|
if (num_alternatives == 0) {
|
|
|
|
return RegExpEmpty::GetInstance();
|
|
|
|
}
|
|
|
|
if (num_alternatives == 1) {
|
|
|
|
return alternatives_.last();
|
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
return new(zone()) RegExpDisjunction(alternatives_.GetList(zone()));
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
void RegExpBuilder::AddQuantifierToAtom(
|
|
|
|
int min, int max, RegExpQuantifier::QuantifierType quantifier_type) {
|
2008-11-25 11:07:48 +00:00
|
|
|
if (pending_empty_) {
|
|
|
|
pending_empty_ = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
RegExpTree* atom;
|
|
|
|
if (characters_ != NULL) {
|
|
|
|
ASSERT(last_added_ == ADD_CHAR);
|
|
|
|
// Last atom was character.
|
|
|
|
Vector<const uc16> char_vector = characters_->ToConstVector();
|
|
|
|
int num_chars = char_vector.length();
|
|
|
|
if (num_chars > 1) {
|
|
|
|
Vector<const uc16> prefix = char_vector.SubVector(0, num_chars - 1);
|
2012-06-11 12:42:31 +00:00
|
|
|
text_.Add(new(zone()) RegExpAtom(prefix), zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
char_vector = char_vector.SubVector(num_chars - 1, num_chars);
|
|
|
|
}
|
|
|
|
characters_ = NULL;
|
2011-04-04 06:29:02 +00:00
|
|
|
atom = new(zone()) RegExpAtom(char_vector);
|
2008-11-25 11:07:48 +00:00
|
|
|
FlushText();
|
|
|
|
} else if (text_.length() > 0) {
|
|
|
|
ASSERT(last_added_ == ADD_ATOM);
|
|
|
|
atom = text_.RemoveLast();
|
|
|
|
FlushText();
|
|
|
|
} else if (terms_.length() > 0) {
|
|
|
|
ASSERT(last_added_ == ADD_ATOM);
|
|
|
|
atom = terms_.RemoveLast();
|
2008-12-17 10:59:14 +00:00
|
|
|
if (atom->max_match() == 0) {
|
|
|
|
// Guaranteed to only match an empty string.
|
2008-11-25 11:07:48 +00:00
|
|
|
LAST(ADD_TERM);
|
|
|
|
if (min == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
terms_.Add(atom, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Only call immediately after adding an atom or character!
|
|
|
|
UNREACHABLE();
|
|
|
|
return;
|
|
|
|
}
|
2013-06-06 13:28:22 +00:00
|
|
|
terms_.Add(
|
|
|
|
new(zone()) RegExpQuantifier(min, max, quantifier_type, atom), zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
LAST(ADD_TERM);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
FunctionEntry ParseData::GetFunctionEntry(int start) {
|
2010-08-27 08:26:29 +00:00
|
|
|
// The current pre-data entry must be a FunctionEntry with the given
|
|
|
|
// start position.
|
2014-07-10 10:28:05 +00:00
|
|
|
if ((function_index_ + FunctionEntry::kSize <= Length()) &&
|
|
|
|
(static_cast<int>(Data()[function_index_]) == start)) {
|
2010-09-07 12:52:16 +00:00
|
|
|
int index = function_index_;
|
|
|
|
function_index_ += FunctionEntry::kSize;
|
2014-07-10 10:28:05 +00:00
|
|
|
Vector<unsigned> subvector(&(Data()[index]), FunctionEntry::kSize);
|
|
|
|
return FunctionEntry(subvector);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return FunctionEntry();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
int ParseData::FunctionCount() {
|
|
|
|
int functions_size = FunctionsSize();
|
|
|
|
if (functions_size < 0) return 0;
|
|
|
|
if (functions_size % FunctionEntry::kSize != 0) return 0;
|
|
|
|
return functions_size / FunctionEntry::kSize;
|
2010-09-07 12:52:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
bool ParseData::IsSane() {
|
2010-09-07 12:52:16 +00:00
|
|
|
// Check that the header data is valid and doesn't specify
|
|
|
|
// point to positions outside the store.
|
2014-07-10 10:28:05 +00:00
|
|
|
int data_length = Length();
|
|
|
|
if (data_length < PreparseDataConstants::kHeaderSize) return false;
|
|
|
|
if (Magic() != PreparseDataConstants::kMagicNumber) return false;
|
|
|
|
if (Version() != PreparseDataConstants::kCurrentVersion) return false;
|
|
|
|
if (HasError()) return false;
|
2010-09-07 12:52:16 +00:00
|
|
|
// Check that the space allocated for function entries is sane.
|
2014-07-10 10:28:05 +00:00
|
|
|
int functions_size = FunctionsSize();
|
2010-09-07 12:52:16 +00:00
|
|
|
if (functions_size < 0) return false;
|
|
|
|
if (functions_size % FunctionEntry::kSize != 0) return false;
|
2010-09-15 10:54:35 +00:00
|
|
|
// Check that the total size has room for header and function entries.
|
2010-09-07 12:52:16 +00:00
|
|
|
int minimum_size =
|
2010-11-23 11:46:36 +00:00
|
|
|
PreparseDataConstants::kHeaderSize + functions_size;
|
2014-07-10 10:28:05 +00:00
|
|
|
if (data_length < minimum_size) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
void ParseData::Initialize() {
|
|
|
|
// Prepares state for use.
|
|
|
|
int data_length = Length();
|
|
|
|
if (data_length >= PreparseDataConstants::kHeaderSize) {
|
|
|
|
function_index_ = PreparseDataConstants::kHeaderSize;
|
2010-08-25 06:46:53 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
bool ParseData::HasError() {
|
|
|
|
return Data()[PreparseDataConstants::kHasErrorOffset];
|
2014-04-01 14:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
unsigned ParseData::Magic() {
|
|
|
|
return Data()[PreparseDataConstants::kMagicOffset];
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
unsigned ParseData::Version() {
|
|
|
|
return Data()[PreparseDataConstants::kVersionOffset];
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
int ParseData::FunctionsSize() {
|
|
|
|
return static_cast<int>(Data()[PreparseDataConstants::kFunctionsSizeOffset]);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
void Parser::SetCachedData() {
|
|
|
|
if (cached_data_mode() == NO_CACHED_DATA) {
|
|
|
|
cached_parse_data_ = NULL;
|
|
|
|
} else {
|
|
|
|
ASSERT(info_->cached_data() != NULL);
|
|
|
|
if (cached_data_mode() == CONSUME_CACHED_DATA) {
|
|
|
|
cached_parse_data_ = new ParseData(*info_->cached_data());
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
Scope* Parser::NewScope(Scope* parent, ScopeType scope_type) {
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(ast_value_factory_);
|
|
|
|
Scope* result =
|
|
|
|
new (zone()) Scope(parent, scope_type, ast_value_factory_, zone());
|
2011-10-17 09:29:37 +00:00
|
|
|
result->Initialize();
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-06-30 14:37:55 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Target is a support class to facilitate manipulation of the
|
|
|
|
// Parser's target_stack_ (the stack of potential 'break' and
|
|
|
|
// 'continue' statement targets). Upon construction, a new target is
|
|
|
|
// added; it is removed upon destruction.
|
|
|
|
|
|
|
|
class Target BASE_EMBEDDED {
|
|
|
|
public:
|
2010-10-27 12:33:48 +00:00
|
|
|
Target(Target** variable, AstNode* node)
|
|
|
|
: variable_(variable), node_(node), previous_(*variable) {
|
|
|
|
*variable = this;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~Target() {
|
2010-10-27 12:33:48 +00:00
|
|
|
*variable_ = previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous() { return previous_; }
|
2009-07-30 11:53:29 +00:00
|
|
|
AstNode* node() { return node_; }
|
2009-05-15 14:58:02 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
private:
|
2010-10-27 12:33:48 +00:00
|
|
|
Target** variable_;
|
2009-07-30 11:53:29 +00:00
|
|
|
AstNode* node_;
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class TargetScope BASE_EMBEDDED {
|
|
|
|
public:
|
2010-10-27 12:33:48 +00:00
|
|
|
explicit TargetScope(Target** variable)
|
|
|
|
: variable_(variable), previous_(*variable) {
|
|
|
|
*variable = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~TargetScope() {
|
2010-10-27 12:33:48 +00:00
|
|
|
*variable_ = previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-10-27 12:33:48 +00:00
|
|
|
Target** variable_;
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// The CHECK_OK macro is a convenient macro to enforce error
|
|
|
|
// handling for functions that may fail (by returning !*ok).
|
|
|
|
//
|
|
|
|
// CAUTION: This macro appends extra statements after a call,
|
|
|
|
// thus it must never be used where only a single statement
|
|
|
|
// is correct (e.g. an if statement branch w/o braces)!
|
|
|
|
|
|
|
|
#define CHECK_OK ok); \
|
|
|
|
if (!*ok) return NULL; \
|
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
|
|
|
|
2008-12-01 15:42:35 +00:00
|
|
|
#define CHECK_FAILED /**/); \
|
2008-12-01 15:32:20 +00:00
|
|
|
if (failed_) return NULL; \
|
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Implementation of Parser
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory_->eval_string() ||
|
|
|
|
identifier == parser_->ast_value_factory_->arguments_string();
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-14 09:43:04 +00:00
|
|
|
bool ParserTraits::IsThisProperty(Expression* expression) {
|
|
|
|
ASSERT(expression != NULL);
|
|
|
|
Property* property = expression->AsProperty();
|
|
|
|
return property != NULL &&
|
|
|
|
property->obj()->AsVariableProxy() != NULL &&
|
|
|
|
property->obj()->AsVariableProxy()->is_this();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-19 14:08:47 +00:00
|
|
|
bool ParserTraits::IsIdentifier(Expression* expression) {
|
|
|
|
VariableProxy* operand = expression->AsVariableProxy();
|
|
|
|
return operand != NULL && !operand->is_this();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-21 10:34:51 +00:00
|
|
|
void ParserTraits::PushPropertyName(FuncNameInferrer* fni,
|
|
|
|
Expression* expression) {
|
|
|
|
if (expression->IsPropertyName()) {
|
2014-06-24 14:03:24 +00:00
|
|
|
fni->PushLiteralName(expression->AsLiteral()->AsRawPropertyName());
|
2014-03-21 10:34:51 +00:00
|
|
|
} else {
|
|
|
|
fni->PushLiteralName(
|
2014-06-24 14:03:24 +00:00
|
|
|
parser_->ast_value_factory_->anonymous_function_string());
|
2014-03-21 10:34:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-14 09:43:04 +00:00
|
|
|
void ParserTraits::CheckAssigningFunctionLiteralToProperty(Expression* left,
|
|
|
|
Expression* right) {
|
|
|
|
ASSERT(left != NULL);
|
|
|
|
if (left->AsProperty() != NULL &&
|
|
|
|
right->AsFunctionLiteral() != NULL) {
|
|
|
|
right->AsFunctionLiteral()->set_pretenure();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-21 09:51:33 +00:00
|
|
|
void ParserTraits::CheckPossibleEvalCall(Expression* expression,
|
|
|
|
Scope* scope) {
|
|
|
|
VariableProxy* callee = expression->AsVariableProxy();
|
|
|
|
if (callee != NULL &&
|
2014-06-24 14:03:24 +00:00
|
|
|
callee->raw_name() == parser_->ast_value_factory_->eval_string()) {
|
2014-03-21 09:51:33 +00:00
|
|
|
scope->DeclarationScope()->RecordEvalCall();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-26 11:59:42 +00:00
|
|
|
Expression* ParserTraits::MarkExpressionAsAssigned(Expression* expression) {
|
|
|
|
VariableProxy* proxy =
|
|
|
|
expression != NULL ? expression->AsVariableProxy() : NULL;
|
|
|
|
if (proxy != NULL) proxy->set_is_assigned();
|
2014-03-14 09:43:04 +00:00
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-17 13:54:42 +00:00
|
|
|
bool ParserTraits::ShortcutNumericLiteralBinaryExpression(
|
|
|
|
Expression** x, Expression* y, Token::Value op, int pos,
|
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
2014-06-24 14:03:24 +00:00
|
|
|
if ((*x)->AsLiteral() && (*x)->AsLiteral()->raw_value()->IsNumber() &&
|
|
|
|
y->AsLiteral() && y->AsLiteral()->raw_value()->IsNumber()) {
|
|
|
|
double x_val = (*x)->AsLiteral()->raw_value()->AsNumber();
|
|
|
|
double y_val = y->AsLiteral()->raw_value()->AsNumber();
|
2014-03-17 13:54:42 +00:00
|
|
|
switch (op) {
|
|
|
|
case Token::ADD:
|
|
|
|
*x = factory->NewNumberLiteral(x_val + y_val, pos);
|
|
|
|
return true;
|
|
|
|
case Token::SUB:
|
|
|
|
*x = factory->NewNumberLiteral(x_val - y_val, pos);
|
|
|
|
return true;
|
|
|
|
case Token::MUL:
|
|
|
|
*x = factory->NewNumberLiteral(x_val * y_val, pos);
|
|
|
|
return true;
|
|
|
|
case Token::DIV:
|
|
|
|
*x = factory->NewNumberLiteral(x_val / y_val, pos);
|
|
|
|
return true;
|
|
|
|
case Token::BIT_OR: {
|
|
|
|
int value = DoubleToInt32(x_val) | DoubleToInt32(y_val);
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::BIT_AND: {
|
|
|
|
int value = DoubleToInt32(x_val) & DoubleToInt32(y_val);
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::BIT_XOR: {
|
|
|
|
int value = DoubleToInt32(x_val) ^ DoubleToInt32(y_val);
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SHL: {
|
|
|
|
int value = DoubleToInt32(x_val) << (DoubleToInt32(y_val) & 0x1f);
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SHR: {
|
|
|
|
uint32_t shift = DoubleToInt32(y_val) & 0x1f;
|
|
|
|
uint32_t value = DoubleToUint32(x_val) >> shift;
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SAR: {
|
|
|
|
uint32_t shift = DoubleToInt32(y_val) & 0x1f;
|
|
|
|
int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift);
|
|
|
|
*x = factory->NewNumberLiteral(value, pos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-19 14:08:47 +00:00
|
|
|
Expression* ParserTraits::BuildUnaryExpression(
|
|
|
|
Expression* expression, Token::Value op, int pos,
|
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
|
|
|
ASSERT(expression != NULL);
|
2014-06-03 07:40:43 +00:00
|
|
|
if (expression->IsLiteral()) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstValue* literal = expression->AsLiteral()->raw_value();
|
2014-03-19 14:08:47 +00:00
|
|
|
if (op == Token::NOT) {
|
|
|
|
// Convert the literal to a boolean condition and negate it.
|
|
|
|
bool condition = literal->BooleanValue();
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewBooleanLiteral(!condition, pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
} else if (literal->IsNumber()) {
|
|
|
|
// Compute some expressions involving only number literals.
|
2014-06-24 14:03:24 +00:00
|
|
|
double value = literal->AsNumber();
|
2014-03-19 14:08:47 +00:00
|
|
|
switch (op) {
|
|
|
|
case Token::ADD:
|
|
|
|
return expression;
|
|
|
|
case Token::SUB:
|
|
|
|
return factory->NewNumberLiteral(-value, pos);
|
|
|
|
case Token::BIT_NOT:
|
|
|
|
return factory->NewNumberLiteral(~DoubleToInt32(value), pos);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Desugar '+foo' => 'foo*1'
|
|
|
|
if (op == Token::ADD) {
|
|
|
|
return factory->NewBinaryOperation(
|
|
|
|
Token::MUL, expression, factory->NewNumberLiteral(1, pos), pos);
|
|
|
|
}
|
|
|
|
// The same idea for '-foo' => 'foo*(-1)'.
|
|
|
|
if (op == Token::SUB) {
|
|
|
|
return factory->NewBinaryOperation(
|
|
|
|
Token::MUL, expression, factory->NewNumberLiteral(-1, pos), pos);
|
|
|
|
}
|
|
|
|
// ...and one more time for '~foo' => 'foo^(~0)'.
|
|
|
|
if (op == Token::BIT_NOT) {
|
|
|
|
return factory->NewBinaryOperation(
|
|
|
|
Token::BIT_XOR, expression, factory->NewNumberLiteral(~0, pos), pos);
|
|
|
|
}
|
|
|
|
return factory->NewUnaryOperation(op, expression, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-02 12:38:01 +00:00
|
|
|
Expression* ParserTraits::NewThrowReferenceError(const char* message, int pos) {
|
2014-04-02 11:03:05 +00:00
|
|
|
return NewThrowError(
|
2014-06-24 14:03:24 +00:00
|
|
|
parser_->ast_value_factory_->make_reference_error_string(), message, NULL,
|
|
|
|
pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::NewThrowSyntaxError(
|
2014-06-24 14:03:24 +00:00
|
|
|
const char* message, const AstRawString* arg, int pos) {
|
|
|
|
return NewThrowError(parser_->ast_value_factory_->make_syntax_error_string(),
|
|
|
|
message, arg, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::NewThrowTypeError(
|
2014-06-24 14:03:24 +00:00
|
|
|
const char* message, const AstRawString* arg, int pos) {
|
|
|
|
return NewThrowError(parser_->ast_value_factory_->make_type_error_string(),
|
|
|
|
message, arg, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::NewThrowError(
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* constructor, const char* message,
|
|
|
|
const AstRawString* arg, int pos) {
|
2014-04-02 11:03:05 +00:00
|
|
|
Zone* zone = parser_->zone();
|
2014-06-24 14:03:24 +00:00
|
|
|
int argc = arg != NULL ? 1 : 0;
|
|
|
|
const AstRawString* type =
|
2014-06-26 11:59:42 +00:00
|
|
|
parser_->ast_value_factory_->GetOneByteString(message);
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*>* array =
|
|
|
|
new (zone) ZoneList<const AstRawString*>(argc, zone);
|
|
|
|
if (arg != NULL) {
|
|
|
|
array->Add(arg, zone);
|
|
|
|
}
|
|
|
|
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(2, zone);
|
|
|
|
args->Add(parser_->factory()->NewStringLiteral(type, pos), zone);
|
|
|
|
args->Add(parser_->factory()->NewStringListLiteral(array, pos), zone);
|
2014-04-02 11:03:05 +00:00
|
|
|
CallRuntime* call_constructor =
|
|
|
|
parser_->factory()->NewCallRuntime(constructor, NULL, args, pos);
|
|
|
|
return parser_->factory()->NewThrow(call_constructor, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-11 09:35:32 +00:00
|
|
|
void ParserTraits::ReportMessageAt(Scanner::Location source_location,
|
|
|
|
const char* message,
|
2014-05-15 09:44:57 +00:00
|
|
|
const char* arg,
|
2014-03-17 10:21:01 +00:00
|
|
|
bool is_reference_error) {
|
2014-03-12 13:27:32 +00:00
|
|
|
if (parser_->stack_overflow()) {
|
|
|
|
// Suppress the error message (syntax error or such) in the presence of a
|
|
|
|
// stack overflow. The isolate allows only one pending exception at at time
|
|
|
|
// and we want to report the stack overflow later.
|
|
|
|
return;
|
|
|
|
}
|
2014-05-20 12:22:04 +00:00
|
|
|
parser_->has_pending_error_ = true;
|
|
|
|
parser_->pending_error_location_ = source_location;
|
|
|
|
parser_->pending_error_message_ = message;
|
|
|
|
parser_->pending_error_char_arg_ = arg;
|
2014-06-24 14:03:24 +00:00
|
|
|
parser_->pending_error_arg_ = NULL;
|
2014-05-20 12:22:04 +00:00
|
|
|
parser_->pending_error_is_reference_error_ = is_reference_error;
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParserTraits::ReportMessage(const char* message,
|
2014-06-24 14:03:24 +00:00
|
|
|
const char* arg,
|
|
|
|
bool is_reference_error) {
|
|
|
|
Scanner::Location source_location = parser_->scanner()->location();
|
|
|
|
ReportMessageAt(source_location, message, arg, is_reference_error);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParserTraits::ReportMessage(const char* message,
|
|
|
|
const AstRawString* arg,
|
2014-03-17 10:21:01 +00:00
|
|
|
bool is_reference_error) {
|
2014-02-14 12:13:33 +00:00
|
|
|
Scanner::Location source_location = parser_->scanner()->location();
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessageAt(source_location, message, arg, is_reference_error);
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParserTraits::ReportMessageAt(Scanner::Location source_location,
|
|
|
|
const char* message,
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* arg,
|
2014-03-17 10:21:01 +00:00
|
|
|
bool is_reference_error) {
|
2014-03-12 13:27:32 +00:00
|
|
|
if (parser_->stack_overflow()) {
|
|
|
|
// Suppress the error message (syntax error or such) in the presence of a
|
|
|
|
// stack overflow. The isolate allows only one pending exception at at time
|
|
|
|
// and we want to report the stack overflow later.
|
|
|
|
return;
|
|
|
|
}
|
2014-05-20 12:22:04 +00:00
|
|
|
parser_->has_pending_error_ = true;
|
|
|
|
parser_->pending_error_location_ = source_location;
|
|
|
|
parser_->pending_error_message_ = message;
|
|
|
|
parser_->pending_error_char_arg_ = NULL;
|
|
|
|
parser_->pending_error_arg_ = arg;
|
|
|
|
parser_->pending_error_is_reference_error_ = is_reference_error;
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* ParserTraits::GetSymbol(Scanner* scanner) {
|
|
|
|
const AstRawString* result =
|
|
|
|
parser_->scanner()->CurrentSymbol(parser_->ast_value_factory_);
|
|
|
|
ASSERT(result != NULL);
|
2014-03-25 09:09:24 +00:00
|
|
|
return result;
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
2014-02-11 11:51:01 +00:00
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* ParserTraits::GetNextSymbol(Scanner* scanner) {
|
|
|
|
return parser_->scanner()->NextSymbol(parser_->ast_value_factory_);
|
2014-02-11 11:51:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-14 11:24:26 +00:00
|
|
|
Expression* ParserTraits::ThisExpression(
|
|
|
|
Scope* scope,
|
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
|
|
|
return factory->NewVariableProxy(scope->receiver());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-11 15:40:41 +00:00
|
|
|
Literal* ParserTraits::ExpressionFromLiteral(
|
2014-02-14 11:24:26 +00:00
|
|
|
Token::Value token, int pos,
|
|
|
|
Scanner* scanner,
|
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
|
|
|
switch (token) {
|
|
|
|
case Token::NULL_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewNullLiteral(pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::TRUE_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewBooleanLiteral(true, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::FALSE_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewBooleanLiteral(false, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::NUMBER: {
|
2014-03-12 14:03:25 +00:00
|
|
|
double value = scanner->DoubleValue();
|
2014-02-14 11:24:26 +00:00
|
|
|
return factory->NewNumberLiteral(value, pos);
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::ExpressionFromIdentifier(
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name, int pos, Scope* scope,
|
2014-02-14 11:24:26 +00:00
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
|
|
|
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
|
|
|
|
// The name may refer to a module instance object, so its type is unknown.
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Variable %.*s ", name->length(), name->raw_data());
|
2014-02-14 11:24:26 +00:00
|
|
|
#endif
|
|
|
|
Interface* interface = Interface::NewUnknown(parser_->zone());
|
|
|
|
return scope->NewUnresolved(factory, name, interface, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::ExpressionFromString(
|
|
|
|
int pos, Scanner* scanner,
|
|
|
|
AstNodeFactory<AstConstructionVisitor>* factory) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* symbol = GetSymbol(scanner);
|
2014-02-14 11:24:26 +00:00
|
|
|
if (parser_->fni_ != NULL) parser_->fni_->PushLiteralName(symbol);
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewStringLiteral(symbol, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-19 08:56:11 +00:00
|
|
|
Literal* ParserTraits::GetLiteralTheHole(
|
|
|
|
int position, AstNodeFactory<AstConstructionVisitor>* factory) {
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewTheHoleLiteral(RelocInfo::kNoPosition);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::ParseV8Intrinsic(bool* ok) {
|
|
|
|
return parser_->ParseV8Intrinsic(ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-11 15:40:41 +00:00
|
|
|
FunctionLiteral* ParserTraits::ParseFunctionLiteral(
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name,
|
2014-03-11 15:40:41 +00:00
|
|
|
Scanner::Location function_name_location,
|
|
|
|
bool name_is_strict_reserved,
|
|
|
|
bool is_generator,
|
|
|
|
int function_token_position,
|
|
|
|
FunctionLiteral::FunctionType type,
|
2014-06-17 07:23:26 +00:00
|
|
|
FunctionLiteral::ArityRestriction arity_restriction,
|
2014-03-11 15:40:41 +00:00
|
|
|
bool* ok) {
|
|
|
|
return parser_->ParseFunctionLiteral(name, function_name_location,
|
|
|
|
name_is_strict_reserved, is_generator,
|
2014-06-17 07:23:26 +00:00
|
|
|
function_token_position, type,
|
|
|
|
arity_restriction, ok);
|
2014-03-11 15:40:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
Parser::Parser(CompilationInfo* info)
|
2014-02-11 09:35:32 +00:00
|
|
|
: ParserBase<ParserTraits>(&scanner_,
|
|
|
|
info->isolate()->stack_guard()->real_climit(),
|
2014-07-10 10:28:05 +00:00
|
|
|
info->extension(), NULL, info->zone(), this),
|
2013-10-14 16:46:51 +00:00
|
|
|
isolate_(info->isolate()),
|
2012-06-20 10:56:53 +00:00
|
|
|
script_(info->script()),
|
2011-04-12 08:27:38 +00:00
|
|
|
scanner_(isolate_->unicode_cache()),
|
2011-11-25 09:36:31 +00:00
|
|
|
reusable_preparser_(NULL),
|
2013-08-23 09:25:37 +00:00
|
|
|
original_scope_(NULL),
|
2008-07-03 15:10:15 +00:00
|
|
|
target_stack_(NULL),
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_(NULL),
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_(NULL),
|
2014-05-20 12:22:04 +00:00
|
|
|
info_(info),
|
|
|
|
has_pending_error_(false),
|
|
|
|
pending_error_message_(NULL),
|
2014-06-24 14:03:24 +00:00
|
|
|
pending_error_arg_(NULL),
|
2014-05-20 12:22:04 +00:00
|
|
|
pending_error_char_arg_(NULL) {
|
2012-06-20 10:56:53 +00:00
|
|
|
ASSERT(!script_.is_null());
|
2012-02-14 14:14:51 +00:00
|
|
|
isolate_->set_ast_node_id(0);
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
set_allow_harmony_scoping(!info->is_native() && FLAG_harmony_scoping);
|
|
|
|
set_allow_modules(!info->is_native() && FLAG_harmony_modules);
|
|
|
|
set_allow_natives_syntax(FLAG_allow_natives_syntax || info->is_native());
|
|
|
|
set_allow_lazy(false); // Must be explicitly enabled.
|
|
|
|
set_allow_generators(FLAG_harmony_generators);
|
2013-06-06 14:38:26 +00:00
|
|
|
set_allow_for_of(FLAG_harmony_iteration);
|
Implement handling of arrow functions in the parser
Arrow functions are parsed from ParseAssignmentExpression(). Handling the
parameter list is done by letting ParseConditionalExpression() parse a comma
separated list of identifiers, and it returns a tree of BinaryOperation nodes
with VariableProxy leaves, or a single VariableProxy if there is only one
parameter. When the arrow token "=>" is found, the VariableProxy nodes are
passed to ParseArrowFunctionLiteral(), which will then skip parsing the
paramaeter list. This avoids having to rewind when the arrow is found and
restart parsing the parameter list.
Note that the empty parameter list "()" is handled directly in
ParsePrimaryExpression(): after is has consumed the opening parenthesis,
if a closing parenthesis follows, then the only valid input is an arrow
function. In this case, ParsePrimaryExpression() directly calls
ParseArrowFunctionLiteral(), to avoid needing to return a sentinel value
to signal the empty parameter list. Because it will consume the body of
the arrow function, ParseAssignmentExpression() will not see the arrow
"=>" token as next, and return the already-parser expression.
The implementation is done in ParserBase, so it was needed to do some
additions to ParserBase, ParserTraits and PreParserTraits. Some of the
glue code can be removed later on when more more functionality is moved
to ParserBase.
Additionally, this adds a runtime flag "harmony_arrow_functions"
(disabled by default); enabling "harmony" will enable it as well.
BUG=v8:2700
LOG=N
R=marja@chromium.org
Review URL: https://codereview.chromium.org/385553003
Patch from Adrián Pérez de Castro <aperez@igalia.com>.
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22320 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-10 12:27:07 +00:00
|
|
|
set_allow_arrow_functions(FLAG_harmony_arrow_functions);
|
2013-07-19 09:57:35 +00:00
|
|
|
set_allow_harmony_numeric_literals(FLAG_harmony_numeric_literals);
|
2014-06-30 13:35:16 +00:00
|
|
|
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
|
|
|
++feature) {
|
|
|
|
use_counts_[feature] = 0;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-06-20 10:56:53 +00:00
|
|
|
FunctionLiteral* Parser::ParseProgram() {
|
2013-10-29 11:44:04 +00:00
|
|
|
// TODO(bmeurer): We temporarily need to pass allow_nesting = true here,
|
|
|
|
// see comment for HistogramTimerScope class.
|
|
|
|
HistogramTimerScope timer_scope(isolate()->counters()->parse(), true);
|
2011-11-15 13:48:40 +00:00
|
|
|
Handle<String> source(String::cast(script_->source()));
|
2011-03-23 11:13:07 +00:00
|
|
|
isolate()->counters()->total_parse_size()->Increment(source->length());
|
2014-06-30 13:25:46 +00:00
|
|
|
base::ElapsedTimer timer;
|
2013-08-29 09:15:13 +00:00
|
|
|
if (FLAG_trace_parse) {
|
|
|
|
timer.Start();
|
|
|
|
}
|
2014-06-24 14:03:24 +00:00
|
|
|
fni_ = new(zone()) FuncNameInferrer(ast_value_factory_, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Initialize parser state.
|
2014-03-19 13:24:13 +00:00
|
|
|
CompleteParserRecorder recorder;
|
2014-07-10 10:28:05 +00:00
|
|
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
2014-03-19 13:24:13 +00:00
|
|
|
log_ = &recorder;
|
2014-07-10 10:28:05 +00:00
|
|
|
} else if (cached_data_mode() == CONSUME_CACHED_DATA) {
|
|
|
|
cached_parse_data_->Initialize();
|
2014-03-19 13:24:13 +00:00
|
|
|
}
|
|
|
|
|
2014-04-08 09:49:49 +00:00
|
|
|
source = String::Flatten(source);
|
2012-07-18 11:22:46 +00:00
|
|
|
FunctionLiteral* result;
|
2010-12-07 14:03:59 +00:00
|
|
|
if (source->IsExternalTwoByteString()) {
|
|
|
|
// Notice that the stream is destroyed at the end of the branch block.
|
|
|
|
// The last line of the blocks can't be moved outside, even though they're
|
|
|
|
// identical calls.
|
2012-03-12 12:35:28 +00:00
|
|
|
ExternalTwoByteStringUtf16CharacterStream stream(
|
2010-12-07 14:03:59 +00:00
|
|
|
Handle<ExternalTwoByteString>::cast(source), 0, source->length());
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(&stream);
|
2013-06-26 08:05:41 +00:00
|
|
|
result = DoParseProgram(info(), source);
|
2010-12-07 14:03:59 +00:00
|
|
|
} else {
|
2012-03-12 12:35:28 +00:00
|
|
|
GenericStringUtf16CharacterStream stream(source, 0, source->length());
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(&stream);
|
2013-06-26 08:05:41 +00:00
|
|
|
result = DoParseProgram(info(), source);
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
2012-07-18 11:22:46 +00:00
|
|
|
|
|
|
|
if (FLAG_trace_parse && result != NULL) {
|
2013-08-29 09:15:13 +00:00
|
|
|
double ms = timer.Elapsed().InMillisecondsF();
|
2012-07-18 11:22:46 +00:00
|
|
|
if (info()->is_eval()) {
|
|
|
|
PrintF("[parsing eval");
|
|
|
|
} else if (info()->script()->name()->IsString()) {
|
|
|
|
String* name = String::cast(info()->script()->name());
|
|
|
|
SmartArrayPointer<char> name_chars = name->ToCString();
|
2013-12-09 07:41:20 +00:00
|
|
|
PrintF("[parsing script: %s", name_chars.get());
|
2012-07-18 11:22:46 +00:00
|
|
|
} else {
|
|
|
|
PrintF("[parsing script");
|
|
|
|
}
|
|
|
|
PrintF(" - took %0.3f ms]\n", ms);
|
|
|
|
}
|
2014-07-10 10:28:05 +00:00
|
|
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
|
|
|
if (result != NULL) *info_->cached_data() = recorder.GetScriptData();
|
2014-03-19 13:24:13 +00:00
|
|
|
log_ = NULL;
|
|
|
|
}
|
2012-07-18 11:22:46 +00:00
|
|
|
return result;
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-15 13:48:40 +00:00
|
|
|
FunctionLiteral* Parser::DoParseProgram(CompilationInfo* info,
|
2013-06-26 08:05:41 +00:00
|
|
|
Handle<String> source) {
|
2014-02-12 12:02:07 +00:00
|
|
|
ASSERT(scope_ == NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(target_stack_ == NULL);
|
|
|
|
|
|
|
|
FunctionLiteral* result = NULL;
|
2014-02-12 12:02:07 +00:00
|
|
|
{ Scope* scope = NewScope(scope_, GLOBAL_SCOPE);
|
2011-11-15 13:48:40 +00:00
|
|
|
info->SetGlobalScope(scope);
|
2014-07-09 14:50:23 +00:00
|
|
|
if (!info->context().is_null() && !info->context()->IsNativeContext()) {
|
2012-08-28 11:25:08 +00:00
|
|
|
scope = Scope::DeserializeScopeChain(*info->context(), scope, zone());
|
2014-06-24 14:03:24 +00:00
|
|
|
// The Scope is backed up by ScopeInfo (which is in the V8 heap); this
|
|
|
|
// means the Parser cannot operate independent of the V8 heap. Tell the
|
|
|
|
// string table to internalize strings and values right after they're
|
|
|
|
// created.
|
|
|
|
ast_value_factory_->Internalize(isolate());
|
2012-08-28 11:25:08 +00:00
|
|
|
}
|
2013-08-23 09:25:37 +00:00
|
|
|
original_scope_ = scope;
|
2012-03-15 13:02:21 +00:00
|
|
|
if (info->is_eval()) {
|
2014-03-11 14:41:22 +00:00
|
|
|
if (!scope->is_global_scope() || info->strict_mode() == STRICT) {
|
2012-03-15 13:02:21 +00:00
|
|
|
scope = NewScope(scope, EVAL_SCOPE);
|
|
|
|
}
|
2012-08-28 11:25:08 +00:00
|
|
|
} else if (info->is_global()) {
|
|
|
|
scope = NewScope(scope, GLOBAL_SCOPE);
|
2011-11-15 13:48:40 +00:00
|
|
|
}
|
2011-10-21 10:26:59 +00:00
|
|
|
scope->set_start_position(0);
|
|
|
|
scope->set_end_position(source->length());
|
2012-08-28 11:25:08 +00:00
|
|
|
|
2012-12-07 10:35:50 +00:00
|
|
|
// Compute the parsing mode.
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
Mode mode = (FLAG_lazy && allow_lazy()) ? PARSE_LAZILY : PARSE_EAGERLY;
|
|
|
|
if (allow_natives_syntax() ||
|
|
|
|
extension_ != NULL ||
|
|
|
|
scope->is_eval_scope()) {
|
2012-12-07 10:35:50 +00:00
|
|
|
mode = PARSE_EAGERLY;
|
|
|
|
}
|
|
|
|
ParsingModeScope parsing_mode(this, mode);
|
|
|
|
|
2013-04-02 17:34:59 +00:00
|
|
|
// Enters 'scope'.
|
2014-06-24 14:03:24 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, scope, zone(),
|
|
|
|
ast_value_factory_);
|
2013-04-02 17:34:59 +00:00
|
|
|
|
2014-03-11 14:41:22 +00:00
|
|
|
scope_->SetStrictMode(info->strict_mode());
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
bool ok = true;
|
2014-02-14 12:13:33 +00:00
|
|
|
int beg_pos = scanner()->location().beg_pos;
|
2012-10-05 09:14:08 +00:00
|
|
|
ParseSourceElements(body, Token::EOS, info->is_eval(), true, &ok);
|
2014-07-02 07:01:31 +00:00
|
|
|
|
|
|
|
HandleSourceURLComments();
|
|
|
|
|
2014-03-11 14:41:22 +00:00
|
|
|
if (ok && strict_mode() == STRICT) {
|
2014-02-14 12:13:33 +00:00
|
|
|
CheckOctalLiteral(beg_pos, scanner()->location().end_pos, &ok);
|
2011-01-24 18:13:18 +00:00
|
|
|
}
|
2011-09-01 12:31:18 +00:00
|
|
|
|
2014-03-24 14:41:55 +00:00
|
|
|
if (ok && allow_harmony_scoping() && strict_mode() == STRICT) {
|
2014-02-12 12:02:07 +00:00
|
|
|
CheckConflictingVarDeclarations(scope_, &ok);
|
2011-09-01 12:31:18 +00:00
|
|
|
}
|
|
|
|
|
2013-03-07 15:46:14 +00:00
|
|
|
if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) {
|
|
|
|
if (body->length() != 1 ||
|
|
|
|
!body->at(0)->IsExpressionStatement() ||
|
|
|
|
!body->at(0)->AsExpressionStatement()->
|
|
|
|
expression()->IsFunctionLiteral()) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("single_function_literal");
|
2013-03-07 15:46:14 +00:00
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->Internalize(isolate());
|
2008-07-03 15:10:15 +00:00
|
|
|
if (ok) {
|
2012-02-08 09:56:33 +00:00
|
|
|
result = factory()->NewFunctionLiteral(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->empty_string(),
|
|
|
|
ast_value_factory_,
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_,
|
2010-11-02 11:45:47 +00:00
|
|
|
body,
|
2011-11-09 13:54:26 +00:00
|
|
|
function_state.materialized_literal_count(),
|
|
|
|
function_state.expected_property_count(),
|
2011-11-11 13:48:14 +00:00
|
|
|
function_state.handler_count(),
|
2009-08-19 07:30:20 +00:00
|
|
|
0,
|
2012-02-14 14:14:51 +00:00
|
|
|
FunctionLiteral::kNoDuplicateParameters,
|
2011-08-09 12:43:08 +00:00
|
|
|
FunctionLiteral::ANONYMOUS_EXPRESSION,
|
2012-08-07 14:47:36 +00:00
|
|
|
FunctionLiteral::kGlobalOrEval,
|
2013-04-02 17:34:59 +00:00
|
|
|
FunctionLiteral::kNotParenthesized,
|
2013-10-14 09:24:58 +00:00
|
|
|
FunctionLiteral::kNotGenerator,
|
|
|
|
0);
|
2012-02-08 09:56:33 +00:00
|
|
|
result->set_ast_properties(factory()->visitor()->ast_properties());
|
2013-09-05 13:20:51 +00:00
|
|
|
result->set_dont_optimize_reason(
|
|
|
|
factory()->visitor()->dont_optimize_reason());
|
2013-10-14 16:46:51 +00:00
|
|
|
} else if (stack_overflow()) {
|
2011-03-18 20:35:07 +00:00
|
|
|
isolate()->StackOverflow();
|
2014-05-20 12:22:04 +00:00
|
|
|
} else {
|
|
|
|
ThrowPendingError();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the target stack is empty.
|
|
|
|
ASSERT(target_stack_ == NULL);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2012-06-20 10:56:53 +00:00
|
|
|
FunctionLiteral* Parser::ParseLazy() {
|
2013-08-29 09:15:13 +00:00
|
|
|
HistogramTimerScope timer_scope(isolate()->counters()->parse_lazy());
|
2010-09-30 09:28:58 +00:00
|
|
|
Handle<String> source(String::cast(script_->source()));
|
2011-03-23 11:13:07 +00:00
|
|
|
isolate()->counters()->total_parse_size()->Increment(source->length());
|
2014-06-30 13:25:46 +00:00
|
|
|
base::ElapsedTimer timer;
|
2013-08-29 09:15:13 +00:00
|
|
|
if (FLAG_trace_parse) {
|
|
|
|
timer.Start();
|
|
|
|
}
|
2012-06-20 10:56:53 +00:00
|
|
|
Handle<SharedFunctionInfo> shared_info = info()->shared_info();
|
2012-07-18 11:22:46 +00:00
|
|
|
|
2010-12-07 14:03:59 +00:00
|
|
|
// Initialize parser state.
|
2014-04-08 09:49:49 +00:00
|
|
|
source = String::Flatten(source);
|
2012-07-18 11:22:46 +00:00
|
|
|
FunctionLiteral* result;
|
2010-12-07 14:03:59 +00:00
|
|
|
if (source->IsExternalTwoByteString()) {
|
2012-03-12 12:35:28 +00:00
|
|
|
ExternalTwoByteStringUtf16CharacterStream stream(
|
2010-12-07 14:03:59 +00:00
|
|
|
Handle<ExternalTwoByteString>::cast(source),
|
2011-03-09 16:57:03 +00:00
|
|
|
shared_info->start_position(),
|
|
|
|
shared_info->end_position());
|
2013-06-26 08:05:41 +00:00
|
|
|
result = ParseLazy(&stream);
|
2010-12-07 14:03:59 +00:00
|
|
|
} else {
|
2012-03-12 12:35:28 +00:00
|
|
|
GenericStringUtf16CharacterStream stream(source,
|
|
|
|
shared_info->start_position(),
|
|
|
|
shared_info->end_position());
|
2013-06-26 08:05:41 +00:00
|
|
|
result = ParseLazy(&stream);
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
2012-07-18 11:22:46 +00:00
|
|
|
|
|
|
|
if (FLAG_trace_parse && result != NULL) {
|
2013-08-29 09:15:13 +00:00
|
|
|
double ms = timer.Elapsed().InMillisecondsF();
|
2012-08-16 11:54:48 +00:00
|
|
|
SmartArrayPointer<char> name_chars = result->debug_name()->ToCString();
|
2013-12-09 07:41:20 +00:00
|
|
|
PrintF("[parsing function: %s - took %0.3f ms]\n", name_chars.get(), ms);
|
2012-07-18 11:22:46 +00:00
|
|
|
}
|
|
|
|
return result;
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-26 08:05:41 +00:00
|
|
|
FunctionLiteral* Parser::ParseLazy(Utf16CharacterStream* source) {
|
2012-06-20 10:56:53 +00:00
|
|
|
Handle<SharedFunctionInfo> shared_info = info()->shared_info();
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(source);
|
2014-02-12 12:02:07 +00:00
|
|
|
ASSERT(scope_ == NULL);
|
2010-12-07 14:03:59 +00:00
|
|
|
ASSERT(target_stack_ == NULL);
|
|
|
|
|
2011-03-09 16:57:03 +00:00
|
|
|
Handle<String> name(String::cast(shared_info->name()));
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(ast_value_factory_);
|
|
|
|
fni_ = new(zone()) FuncNameInferrer(ast_value_factory_, zone());
|
|
|
|
const AstRawString* raw_name = ast_value_factory_->GetString(name);
|
|
|
|
fni_->PushEnclosingName(raw_name);
|
2010-08-23 13:26:03 +00:00
|
|
|
|
2012-08-07 14:47:36 +00:00
|
|
|
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Place holder for the result.
|
|
|
|
FunctionLiteral* result = NULL;
|
|
|
|
|
|
|
|
{
|
|
|
|
// Parse the function literal.
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* scope = NewScope(scope_, GLOBAL_SCOPE);
|
2012-06-20 10:56:53 +00:00
|
|
|
info()->SetGlobalScope(scope);
|
|
|
|
if (!info()->closure().is_null()) {
|
|
|
|
scope = Scope::DeserializeScopeChain(info()->closure()->context(), scope,
|
2012-06-11 12:42:31 +00:00
|
|
|
zone());
|
2011-03-09 16:57:03 +00:00
|
|
|
}
|
2013-08-23 09:25:37 +00:00
|
|
|
original_scope_ = scope;
|
2014-06-24 14:03:24 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, scope, zone(),
|
|
|
|
ast_value_factory_);
|
2014-03-11 14:41:22 +00:00
|
|
|
ASSERT(scope->strict_mode() == SLOPPY || info()->strict_mode() == STRICT);
|
|
|
|
ASSERT(info()->strict_mode() == shared_info->strict_mode());
|
|
|
|
scope->SetStrictMode(shared_info->strict_mode());
|
2013-06-06 13:28:22 +00:00
|
|
|
FunctionLiteral::FunctionType function_type = shared_info->is_expression()
|
2011-08-09 12:43:08 +00:00
|
|
|
? (shared_info->is_anonymous()
|
|
|
|
? FunctionLiteral::ANONYMOUS_EXPRESSION
|
|
|
|
: FunctionLiteral::NAMED_EXPRESSION)
|
|
|
|
: FunctionLiteral::DECLARATION;
|
2008-07-03 15:10:15 +00:00
|
|
|
bool ok = true;
|
2014-06-24 14:03:24 +00:00
|
|
|
result = ParseFunctionLiteral(raw_name,
|
2014-02-04 11:26:19 +00:00
|
|
|
Scanner::Location::invalid(),
|
2011-08-08 16:14:46 +00:00
|
|
|
false, // Strict mode name already checked.
|
2013-04-02 17:34:59 +00:00
|
|
|
shared_info->is_generator(),
|
2011-08-08 16:14:46 +00:00
|
|
|
RelocInfo::kNoPosition,
|
2013-06-06 13:28:22 +00:00
|
|
|
function_type,
|
2014-06-17 07:23:26 +00:00
|
|
|
FunctionLiteral::NORMAL_ARITY,
|
2011-08-08 16:14:46 +00:00
|
|
|
&ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
// Make sure the results agree.
|
|
|
|
ASSERT(ok == (result != NULL));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the target stack is empty.
|
|
|
|
ASSERT(target_stack_ == NULL);
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->Internalize(isolate());
|
2008-07-03 15:10:15 +00:00
|
|
|
if (result == NULL) {
|
2014-05-20 12:22:04 +00:00
|
|
|
if (stack_overflow()) {
|
|
|
|
isolate()->StackOverflow();
|
|
|
|
} else {
|
|
|
|
ThrowPendingError();
|
|
|
|
}
|
2010-12-07 11:31:57 +00:00
|
|
|
} else {
|
2011-03-09 16:57:03 +00:00
|
|
|
Handle<String> inferred_name(shared_info->inferred_name());
|
2010-12-07 11:31:57 +00:00
|
|
|
result->set_inferred_name(inferred_name);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-09-30 09:28:58 +00:00
|
|
|
|
2010-11-02 11:45:47 +00:00
|
|
|
void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
|
2008-07-03 15:10:15 +00:00
|
|
|
int end_token,
|
2012-03-15 13:02:21 +00:00
|
|
|
bool is_eval,
|
2012-10-05 09:14:08 +00:00
|
|
|
bool is_global,
|
2008-07-03 15:10:15 +00:00
|
|
|
bool* ok) {
|
|
|
|
// SourceElements ::
|
2012-02-20 14:02:59 +00:00
|
|
|
// (ModuleElement)* <end_token>
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate a target stack to use for this set of source
|
|
|
|
// elements. This way, all scripts and functions get their own
|
|
|
|
// target stack thus avoiding illegal breaks and continues across
|
|
|
|
// functions.
|
2010-10-27 12:33:48 +00:00
|
|
|
TargetScope scope(&this->target_stack_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
ASSERT(processor != NULL);
|
2011-01-20 18:51:47 +00:00
|
|
|
bool directive_prologue = true; // Parsing directive prologue.
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
while (peek() != end_token) {
|
2011-01-20 18:51:47 +00:00
|
|
|
if (directive_prologue && peek() != Token::STRING) {
|
|
|
|
directive_prologue = false;
|
|
|
|
}
|
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
Scanner::Location token_loc = scanner()->peek_location();
|
2012-10-05 09:14:08 +00:00
|
|
|
Statement* stat;
|
|
|
|
if (is_global && !is_eval) {
|
|
|
|
stat = ParseModuleElement(NULL, CHECK_OK);
|
|
|
|
} else {
|
|
|
|
stat = ParseBlockElement(NULL, CHECK_OK);
|
|
|
|
}
|
2011-01-20 18:51:47 +00:00
|
|
|
if (stat == NULL || stat->IsEmpty()) {
|
|
|
|
directive_prologue = false; // End of directive prologue.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (directive_prologue) {
|
|
|
|
// A shot at a directive.
|
2012-01-13 13:09:52 +00:00
|
|
|
ExpressionStatement* e_stat;
|
|
|
|
Literal* literal;
|
2011-01-20 18:51:47 +00:00
|
|
|
// Still processing directive prologue?
|
|
|
|
if ((e_stat = stat->AsExpressionStatement()) != NULL &&
|
|
|
|
(literal = e_stat->expression()->AsLiteral()) != NULL &&
|
2014-06-24 14:03:24 +00:00
|
|
|
literal->raw_value()->IsString()) {
|
2014-06-30 13:35:16 +00:00
|
|
|
// Check "use strict" directive (ES5 14.1) and "use asm" directive. Only
|
|
|
|
// one can be present.
|
2014-03-11 14:41:22 +00:00
|
|
|
if (strict_mode() == SLOPPY &&
|
2014-06-24 14:03:24 +00:00
|
|
|
literal->raw_value()->AsString() ==
|
|
|
|
ast_value_factory_->use_strict_string() &&
|
2014-06-30 13:35:16 +00:00
|
|
|
token_loc.end_pos - token_loc.beg_pos ==
|
|
|
|
ast_value_factory_->use_strict_string()->length() + 2) {
|
2012-03-15 13:02:21 +00:00
|
|
|
// TODO(mstarzinger): Global strict eval calls, need their own scope
|
|
|
|
// as specified in ES5 10.4.2(3). The correct fix would be to always
|
|
|
|
// add this scope in DoParseProgram(), but that requires adaptations
|
|
|
|
// all over the code base, so we go with a quick-fix for now.
|
2012-12-07 10:35:50 +00:00
|
|
|
// In the same manner, we have to patch the parsing mode.
|
2014-02-12 12:02:07 +00:00
|
|
|
if (is_eval && !scope_->is_eval_scope()) {
|
|
|
|
ASSERT(scope_->is_global_scope());
|
|
|
|
Scope* scope = NewScope(scope_, EVAL_SCOPE);
|
|
|
|
scope->set_start_position(scope_->start_position());
|
|
|
|
scope->set_end_position(scope_->end_position());
|
|
|
|
scope_ = scope;
|
2012-12-07 10:35:50 +00:00
|
|
|
mode_ = PARSE_EAGERLY;
|
2012-03-15 13:02:21 +00:00
|
|
|
}
|
2014-03-11 14:41:22 +00:00
|
|
|
scope_->SetStrictMode(STRICT);
|
2011-01-20 18:51:47 +00:00
|
|
|
// "use strict" is the only directive for now.
|
|
|
|
directive_prologue = false;
|
2014-06-30 13:35:16 +00:00
|
|
|
} else if (literal->raw_value()->AsString() ==
|
|
|
|
ast_value_factory_->use_asm_string() &&
|
|
|
|
token_loc.end_pos - token_loc.beg_pos ==
|
|
|
|
ast_value_factory_->use_asm_string()->length() + 2) {
|
|
|
|
// Store the usage count; The actual use counter on the isolate is
|
|
|
|
// incremented after parsing is done.
|
|
|
|
++use_counts_[v8::Isolate::kUseAsm];
|
2011-01-20 18:51:47 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// End of the directive prologue.
|
|
|
|
directive_prologue = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-11 12:42:31 +00:00
|
|
|
processor->Add(stat, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-08-19 07:30:20 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseModuleElement(ZoneList<const AstRawString*>* labels,
|
2012-02-20 14:02:59 +00:00
|
|
|
bool* ok) {
|
|
|
|
// (Ecma 262 5th Edition, clause 14):
|
|
|
|
// SourceElement:
|
|
|
|
// Statement
|
|
|
|
// FunctionDeclaration
|
|
|
|
//
|
|
|
|
// In harmony mode we allow additionally the following productions
|
|
|
|
// ModuleElement:
|
|
|
|
// LetDeclaration
|
|
|
|
// ConstDeclaration
|
|
|
|
// ModuleDeclaration
|
|
|
|
// ImportDeclaration
|
|
|
|
// ExportDeclaration
|
2013-04-02 17:34:59 +00:00
|
|
|
// GeneratorDeclaration
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::FUNCTION:
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseFunctionDeclaration(NULL, ok);
|
2012-02-20 14:02:59 +00:00
|
|
|
case Token::IMPORT:
|
|
|
|
return ParseImportDeclaration(ok);
|
|
|
|
case Token::EXPORT:
|
|
|
|
return ParseExportDeclaration(ok);
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::CONST:
|
|
|
|
return ParseVariableStatement(kModuleElement, NULL, ok);
|
|
|
|
case Token::LET:
|
|
|
|
ASSERT(allow_harmony_scoping());
|
|
|
|
if (strict_mode() == STRICT) {
|
|
|
|
return ParseVariableStatement(kModuleElement, NULL, ok);
|
|
|
|
}
|
|
|
|
// Fall through.
|
2012-02-24 15:53:09 +00:00
|
|
|
default: {
|
|
|
|
Statement* stmt = ParseStatement(labels, CHECK_OK);
|
|
|
|
// Handle 'module' as a context-sensitive keyword.
|
|
|
|
if (FLAG_harmony_modules &&
|
|
|
|
peek() == Token::IDENTIFIER &&
|
2014-02-14 12:13:33 +00:00
|
|
|
!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2012-02-24 15:53:09 +00:00
|
|
|
stmt != NULL) {
|
|
|
|
ExpressionStatement* estmt = stmt->AsExpressionStatement();
|
2014-06-24 14:03:24 +00:00
|
|
|
if (estmt != NULL && estmt->expression()->AsVariableProxy() != NULL &&
|
|
|
|
estmt->expression()->AsVariableProxy()->raw_name() ==
|
|
|
|
ast_value_factory_->module_string() &&
|
2014-02-14 12:13:33 +00:00
|
|
|
!scanner()->literal_contains_escapes()) {
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseModuleDeclaration(NULL, ok);
|
2012-02-24 15:53:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return stmt;
|
|
|
|
}
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseModuleDeclaration(ZoneList<const AstRawString*>* names,
|
|
|
|
bool* ok) {
|
2012-02-20 14:02:59 +00:00
|
|
|
// ModuleDeclaration:
|
|
|
|
// 'module' Identifier Module
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name =
|
|
|
|
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2012-03-08 13:03:07 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Module %.*s ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
|
|
|
|
2012-02-28 10:12:39 +00:00
|
|
|
Module* module = ParseModule(CHECK_OK);
|
Get rid of static module allocation, do it in code.
Modules now have their own local scope, represented by their own context.
Module instance objects have an accessor for every export that forwards
access to the respective slot from the module's context. (Exports that are
modules themselves, however, are simple data properties.)
All modules have a _hosting_ scope/context, which (currently) is the
(innermost) enclosing global scope. To deal with recursion, nested modules
are hosted by the same scope as global ones.
For every (global or nested) module literal, the hosting context has an
internal slot that points directly to the respective module context. This
enables quick access to (statically resolved) module members by 2-dimensional
access through the hosting context. For example,
module A {
let x;
module B { let y; }
}
module C { let z; }
allocates contexts as follows:
[header| .A | .B | .C | A | C ] (global)
| | |
| | +-- [header| z ] (module)
| |
| +------- [header| y ] (module)
|
+------------ [header| x | B ] (module)
Here, .A, .B, .C are the internal slots pointing to the hosted module
contexts, whereas A, B, C hold the actual instance objects (note that every
module context also points to the respective instance object through its
extension slot in the header).
To deal with arbitrary recursion and aliases between modules,
they are created and initialized in several stages. Each stage applies to
all modules in the hosting global scope, including nested ones.
1. Allocate: for each module _literal_, allocate the module contexts and
respective instance object and wire them up. This happens in the
PushModuleContext runtime function, as generated by AllocateModules
(invoked by VisitDeclarations in the hosting scope).
2. Bind: for each module _declaration_ (i.e. literals as well as aliases),
assign the respective instance object to respective local variables. This
happens in VisitModuleDeclaration, and uses the instance objects created
in the previous stage.
For each module _literal_, this phase also constructs a module descriptor
for the next stage. This happens in VisitModuleLiteral.
3. Populate: invoke the DeclareModules runtime function to populate each
_instance_ object with accessors for it exports. This is generated by
DeclareModules (invoked by VisitDeclarations in the hosting scope again),
and uses the descriptors generated in the previous stage.
4. Initialize: execute the module bodies (and other code) in sequence. This
happens by the separate statements generated for module bodies. To reenter
the module scopes properly, the parser inserted ModuleStatements.
R=mstarzinger@chromium.org,svenpanne@chromium.org
BUG=
Review URL: https://codereview.chromium.org/11093074
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13033 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-11-22 10:25:22 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, MODULE, module->interface());
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewModuleDeclaration(proxy, module, scope_, pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declare(declaration, true, CHECK_OK);
|
|
|
|
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Module %.*s ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
if (FLAG_print_interfaces) {
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("module %.*s: ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
module->interface()->Print();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-06-11 12:42:31 +00:00
|
|
|
if (names) names->Add(name, zone());
|
2012-07-09 08:59:03 +00:00
|
|
|
if (module->body() == NULL)
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(pos);
|
2012-07-09 08:59:03 +00:00
|
|
|
else
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewModuleStatement(proxy, module->body(), pos);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Module* Parser::ParseModule(bool* ok) {
|
|
|
|
// Module:
|
|
|
|
// '{' ModuleElement '}'
|
2012-02-29 12:12:52 +00:00
|
|
|
// '=' ModulePath ';'
|
|
|
|
// 'at' String ';'
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::LBRACE:
|
|
|
|
return ParseModuleLiteral(ok);
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
case Token::ASSIGN: {
|
2012-02-20 14:02:59 +00:00
|
|
|
Expect(Token::ASSIGN, CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
Module* result = ParseModulePath(CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
return result;
|
|
|
|
}
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
default: {
|
2013-06-06 14:38:26 +00:00
|
|
|
ExpectContextualKeyword(CStrVector("at"), CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
Module* result = ParseModuleUrl(CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
return result;
|
|
|
|
}
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Module* Parser::ParseModuleLiteral(bool* ok) {
|
|
|
|
// Module:
|
|
|
|
// '{' ModuleElement '}'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2012-02-20 14:02:59 +00:00
|
|
|
// Construct block expecting 16 statements.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body = factory()->NewBlock(NULL, 16, false, RelocInfo::kNoPosition);
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details) PrintF("# Literal ");
|
|
|
|
#endif
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* scope = NewScope(scope_, MODULE_SCOPE);
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
scope->set_start_position(scanner()->location().beg_pos);
|
2014-03-11 14:41:22 +00:00
|
|
|
scope->SetStrictMode(STRICT);
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
{
|
2014-02-12 12:02:07 +00:00
|
|
|
BlockState block_state(&scope_, scope);
|
2012-06-11 12:42:31 +00:00
|
|
|
TargetCollector collector(zone());
|
2012-02-20 14:02:59 +00:00
|
|
|
Target target(&this->target_stack_, &collector);
|
|
|
|
Target target_body(&this->target_stack_, body);
|
|
|
|
|
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
Statement* stat = ParseModuleElement(NULL, CHECK_OK);
|
|
|
|
if (stat && !stat->IsEmpty()) {
|
2012-06-04 14:42:58 +00:00
|
|
|
body->AddStatement(stat, zone());
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
scope->set_end_position(scanner()->location().end_pos);
|
2012-04-16 14:43:27 +00:00
|
|
|
body->set_scope(scope);
|
2012-03-08 13:03:07 +00:00
|
|
|
|
2012-07-09 08:59:03 +00:00
|
|
|
// Check that all exports are bound.
|
2012-04-16 14:43:27 +00:00
|
|
|
Interface* interface = scope->interface();
|
2012-07-09 08:59:03 +00:00
|
|
|
for (Interface::Iterator it = interface->iterator();
|
|
|
|
!it.done(); it.Advance()) {
|
2014-05-22 15:27:57 +00:00
|
|
|
if (scope->LookupLocal(it.name()) == NULL) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("module_export_undefined", it.name());
|
2012-07-09 08:59:03 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-16 14:43:27 +00:00
|
|
|
interface->MakeModule(ok);
|
2012-07-09 08:59:03 +00:00
|
|
|
ASSERT(*ok);
|
2012-04-16 14:43:27 +00:00
|
|
|
interface->Freeze(ok);
|
2012-07-09 08:59:03 +00:00
|
|
|
ASSERT(*ok);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewModuleLiteral(body, interface, pos);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Module* Parser::ParseModulePath(bool* ok) {
|
|
|
|
// ModulePath:
|
|
|
|
// Identifier
|
|
|
|
// ModulePath '.' Identifier
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2012-02-20 14:02:59 +00:00
|
|
|
Module* result = ParseModuleVariable(CHECK_OK);
|
|
|
|
while (Check(Token::PERIOD)) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifierName(CHECK_OK);
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Path .%.*s ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
2013-10-14 09:24:58 +00:00
|
|
|
Module* member = factory()->NewModulePath(result, name, pos);
|
2012-06-11 12:42:31 +00:00
|
|
|
result->interface()->Add(name, member->interface(), zone(), ok);
|
2012-03-08 13:03:07 +00:00
|
|
|
if (!*ok) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interfaces) {
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("PATH TYPE ERROR at '%.*s'\n", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
PrintF("result: ");
|
|
|
|
result->interface()->Print();
|
|
|
|
PrintF("member: ");
|
|
|
|
member->interface()->Print();
|
|
|
|
}
|
|
|
|
#endif
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("invalid_module_path", name);
|
2012-03-08 13:03:07 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
result = member;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Module* Parser::ParseModuleVariable(bool* ok) {
|
|
|
|
// ModulePath:
|
|
|
|
// Identifier
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name =
|
|
|
|
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Module variable %.*s ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
2014-02-12 12:02:07 +00:00
|
|
|
VariableProxy* proxy = scope_->NewUnresolved(
|
2012-07-13 09:29:43 +00:00
|
|
|
factory(), name, Interface::NewModule(zone()),
|
2014-02-14 12:13:33 +00:00
|
|
|
scanner()->location().beg_pos);
|
2012-03-08 13:03:07 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewModuleVariable(proxy, pos);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Module* Parser::ParseModuleUrl(bool* ok) {
|
|
|
|
// Module:
|
2012-02-29 12:12:52 +00:00
|
|
|
// String
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2012-02-20 14:02:59 +00:00
|
|
|
Expect(Token::STRING, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* symbol = GetSymbol(scanner());
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2012-03-08 13:03:07 +00:00
|
|
|
// TODO(ES6): Request JS resource from environment...
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details) PrintF("# Url ");
|
|
|
|
#endif
|
2012-04-16 14:43:27 +00:00
|
|
|
|
Get rid of static module allocation, do it in code.
Modules now have their own local scope, represented by their own context.
Module instance objects have an accessor for every export that forwards
access to the respective slot from the module's context. (Exports that are
modules themselves, however, are simple data properties.)
All modules have a _hosting_ scope/context, which (currently) is the
(innermost) enclosing global scope. To deal with recursion, nested modules
are hosted by the same scope as global ones.
For every (global or nested) module literal, the hosting context has an
internal slot that points directly to the respective module context. This
enables quick access to (statically resolved) module members by 2-dimensional
access through the hosting context. For example,
module A {
let x;
module B { let y; }
}
module C { let z; }
allocates contexts as follows:
[header| .A | .B | .C | A | C ] (global)
| | |
| | +-- [header| z ] (module)
| |
| +------- [header| y ] (module)
|
+------------ [header| x | B ] (module)
Here, .A, .B, .C are the internal slots pointing to the hosted module
contexts, whereas A, B, C hold the actual instance objects (note that every
module context also points to the respective instance object through its
extension slot in the header).
To deal with arbitrary recursion and aliases between modules,
they are created and initialized in several stages. Each stage applies to
all modules in the hosting global scope, including nested ones.
1. Allocate: for each module _literal_, allocate the module contexts and
respective instance object and wire them up. This happens in the
PushModuleContext runtime function, as generated by AllocateModules
(invoked by VisitDeclarations in the hosting scope).
2. Bind: for each module _declaration_ (i.e. literals as well as aliases),
assign the respective instance object to respective local variables. This
happens in VisitModuleDeclaration, and uses the instance objects created
in the previous stage.
For each module _literal_, this phase also constructs a module descriptor
for the next stage. This happens in VisitModuleLiteral.
3. Populate: invoke the DeclareModules runtime function to populate each
_instance_ object with accessors for it exports. This is generated by
DeclareModules (invoked by VisitDeclarations in the hosting scope again),
and uses the descriptors generated in the previous stage.
4. Initialize: execute the module bodies (and other code) in sequence. This
happens by the separate statements generated for module bodies. To reenter
the module scopes properly, the parser inserted ModuleStatements.
R=mstarzinger@chromium.org,svenpanne@chromium.org
BUG=
Review URL: https://codereview.chromium.org/11093074
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13033 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-11-22 10:25:22 +00:00
|
|
|
// Create an empty literal as long as the feature isn't finished.
|
|
|
|
USE(symbol);
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* scope = NewScope(scope_, MODULE_SCOPE);
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
|
Get rid of static module allocation, do it in code.
Modules now have their own local scope, represented by their own context.
Module instance objects have an accessor for every export that forwards
access to the respective slot from the module's context. (Exports that are
modules themselves, however, are simple data properties.)
All modules have a _hosting_ scope/context, which (currently) is the
(innermost) enclosing global scope. To deal with recursion, nested modules
are hosted by the same scope as global ones.
For every (global or nested) module literal, the hosting context has an
internal slot that points directly to the respective module context. This
enables quick access to (statically resolved) module members by 2-dimensional
access through the hosting context. For example,
module A {
let x;
module B { let y; }
}
module C { let z; }
allocates contexts as follows:
[header| .A | .B | .C | A | C ] (global)
| | |
| | +-- [header| z ] (module)
| |
| +------- [header| y ] (module)
|
+------------ [header| x | B ] (module)
Here, .A, .B, .C are the internal slots pointing to the hosted module
contexts, whereas A, B, C hold the actual instance objects (note that every
module context also points to the respective instance object through its
extension slot in the header).
To deal with arbitrary recursion and aliases between modules,
they are created and initialized in several stages. Each stage applies to
all modules in the hosting global scope, including nested ones.
1. Allocate: for each module _literal_, allocate the module contexts and
respective instance object and wire them up. This happens in the
PushModuleContext runtime function, as generated by AllocateModules
(invoked by VisitDeclarations in the hosting scope).
2. Bind: for each module _declaration_ (i.e. literals as well as aliases),
assign the respective instance object to respective local variables. This
happens in VisitModuleDeclaration, and uses the instance objects created
in the previous stage.
For each module _literal_, this phase also constructs a module descriptor
for the next stage. This happens in VisitModuleLiteral.
3. Populate: invoke the DeclareModules runtime function to populate each
_instance_ object with accessors for it exports. This is generated by
DeclareModules (invoked by VisitDeclarations in the hosting scope again),
and uses the descriptors generated in the previous stage.
4. Initialize: execute the module bodies (and other code) in sequence. This
happens by the separate statements generated for module bodies. To reenter
the module scopes properly, the parser inserted ModuleStatements.
R=mstarzinger@chromium.org,svenpanne@chromium.org
BUG=
Review URL: https://codereview.chromium.org/11093074
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@13033 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2012-11-22 10:25:22 +00:00
|
|
|
body->set_scope(scope);
|
|
|
|
Interface* interface = scope->interface();
|
2013-10-14 09:24:58 +00:00
|
|
|
Module* result = factory()->NewModuleLiteral(body, interface, pos);
|
2012-04-16 14:43:27 +00:00
|
|
|
interface->Freeze(ok);
|
2012-07-09 08:59:03 +00:00
|
|
|
ASSERT(*ok);
|
|
|
|
interface->Unify(scope->interface(), zone(), ok);
|
|
|
|
ASSERT(*ok);
|
2012-04-16 14:43:27 +00:00
|
|
|
return result;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
Module* Parser::ParseModuleSpecifier(bool* ok) {
|
|
|
|
// ModuleSpecifier:
|
|
|
|
// String
|
|
|
|
// ModulePath
|
|
|
|
|
|
|
|
if (peek() == Token::STRING) {
|
|
|
|
return ParseModuleUrl(ok);
|
|
|
|
} else {
|
|
|
|
return ParseModulePath(ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-20 14:02:59 +00:00
|
|
|
Block* Parser::ParseImportDeclaration(bool* ok) {
|
2012-02-29 12:12:52 +00:00
|
|
|
// ImportDeclaration:
|
|
|
|
// 'import' IdentifierName (',' IdentifierName)* 'from' ModuleSpecifier ';'
|
|
|
|
//
|
|
|
|
// TODO(ES6): implement destructuring ImportSpecifiers
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2012-02-29 12:12:52 +00:00
|
|
|
Expect(Token::IMPORT, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*> names(1, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifierName(CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
names.Add(name, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
while (peek() == Token::COMMA) {
|
|
|
|
Consume(Token::COMMA);
|
|
|
|
name = ParseIdentifierName(CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
names.Add(name, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 14:38:26 +00:00
|
|
|
ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
Module* module = ParseModuleSpecifier(CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
|
|
|
|
// Generate a separate declaration for each identifier.
|
|
|
|
// TODO(ES6): once we implement destructuring, make that one declaration.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* block = factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition);
|
2012-02-29 12:12:52 +00:00
|
|
|
for (int i = 0; i < names.length(); ++i) {
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Import %.*s ", name->length(), name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
2012-06-11 12:42:31 +00:00
|
|
|
Interface* interface = Interface::NewUnknown(zone());
|
|
|
|
module->interface()->Add(names[i], interface, zone(), ok);
|
2012-03-08 13:03:07 +00:00
|
|
|
if (!*ok) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interfaces) {
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("IMPORT TYPE ERROR at '%.*s'\n", name->length(),
|
|
|
|
name->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
PrintF("module: ");
|
|
|
|
module->interface()->Print();
|
|
|
|
}
|
|
|
|
#endif
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("invalid_module_path", name);
|
2012-03-08 13:03:07 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
VariableProxy* proxy = NewUnresolved(names[i], LET, interface);
|
2012-02-29 12:12:52 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewImportDeclaration(proxy, module, scope_, pos);
|
2012-02-29 12:12:52 +00:00
|
|
|
Declare(declaration, true, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
return block;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
Statement* Parser::ParseExportDeclaration(bool* ok) {
|
|
|
|
// ExportDeclaration:
|
|
|
|
// 'export' Identifier (',' Identifier)* ';'
|
|
|
|
// 'export' VariableDeclaration
|
|
|
|
// 'export' FunctionDeclaration
|
2013-04-02 17:34:59 +00:00
|
|
|
// 'export' GeneratorDeclaration
|
2012-02-29 12:12:52 +00:00
|
|
|
// 'export' ModuleDeclaration
|
|
|
|
//
|
|
|
|
// TODO(ES6): implement structuring ExportSpecifiers
|
|
|
|
|
2014-07-10 14:06:37 +00:00
|
|
|
ASSERT(strict_mode() == STRICT);
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
Expect(Token::EXPORT, CHECK_OK);
|
|
|
|
|
|
|
|
Statement* result = NULL;
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*> names(1, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
switch (peek()) {
|
|
|
|
case Token::IDENTIFIER: {
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name =
|
2014-02-05 16:26:48 +00:00
|
|
|
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
// Handle 'module' as a context-sensitive keyword.
|
2014-06-24 14:03:24 +00:00
|
|
|
if (name != ast_value_factory_->module_string()) {
|
2012-06-11 12:42:31 +00:00
|
|
|
names.Add(name, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
while (peek() == Token::COMMA) {
|
|
|
|
Consume(Token::COMMA);
|
2014-02-05 16:26:48 +00:00
|
|
|
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
names.Add(name, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
result = factory()->NewEmptyStatement(pos);
|
2012-02-29 12:12:52 +00:00
|
|
|
} else {
|
|
|
|
result = ParseModuleDeclaration(&names, CHECK_OK);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::FUNCTION:
|
|
|
|
result = ParseFunctionDeclaration(&names, CHECK_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Token::VAR:
|
|
|
|
case Token::LET:
|
|
|
|
case Token::CONST:
|
|
|
|
result = ParseVariableStatement(kModuleElement, &names, CHECK_OK);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*ok = false;
|
2014-02-14 12:13:33 +00:00
|
|
|
ReportUnexpectedToken(scanner()->current_token());
|
2012-02-29 12:12:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-08 13:03:07 +00:00
|
|
|
// Extract declared names into export declarations and interface.
|
2014-02-12 12:02:07 +00:00
|
|
|
Interface* interface = scope_->interface();
|
2012-02-29 12:12:52 +00:00
|
|
|
for (int i = 0; i < names.length(); ++i) {
|
2012-03-08 13:03:07 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interface_details)
|
2014-06-24 14:03:24 +00:00
|
|
|
PrintF("# Export %.*s ", names[i]->length(), names[i]->raw_data());
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
2012-06-11 12:42:31 +00:00
|
|
|
Interface* inner = Interface::NewUnknown(zone());
|
|
|
|
interface->Add(names[i], inner, zone(), CHECK_OK);
|
|
|
|
if (!*ok)
|
|
|
|
return NULL;
|
2012-03-08 13:03:07 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(names[i], LET, inner);
|
|
|
|
USE(proxy);
|
|
|
|
// TODO(rossberg): Rethink whether we actually need to store export
|
|
|
|
// declarations (for compilation?).
|
|
|
|
// ExportDeclaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
// factory()->NewExportDeclaration(proxy, scope_, position);
|
|
|
|
// scope_->AddDeclaration(declaration);
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(result != NULL);
|
|
|
|
return result;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseBlockElement(ZoneList<const AstRawString*>* labels,
|
2012-02-20 14:02:59 +00:00
|
|
|
bool* ok) {
|
|
|
|
// (Ecma 262 5th Edition, clause 14):
|
|
|
|
// SourceElement:
|
|
|
|
// Statement
|
|
|
|
// FunctionDeclaration
|
|
|
|
//
|
|
|
|
// In harmony mode we allow additionally the following productions
|
|
|
|
// BlockElement (aka SourceElement):
|
|
|
|
// LetDeclaration
|
|
|
|
// ConstDeclaration
|
2013-04-02 17:34:59 +00:00
|
|
|
// GeneratorDeclaration
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::FUNCTION:
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseFunctionDeclaration(NULL, ok);
|
2012-02-20 14:02:59 +00:00
|
|
|
case Token::CONST:
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseVariableStatement(kModuleElement, NULL, ok);
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::LET:
|
|
|
|
ASSERT(allow_harmony_scoping());
|
|
|
|
if (strict_mode() == STRICT) {
|
|
|
|
return ParseVariableStatement(kModuleElement, NULL, ok);
|
|
|
|
}
|
|
|
|
// Fall through.
|
2012-02-20 14:02:59 +00:00
|
|
|
default:
|
|
|
|
return ParseStatement(labels, ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Statement ::
|
|
|
|
// Block
|
|
|
|
// VariableStatement
|
|
|
|
// EmptyStatement
|
|
|
|
// ExpressionStatement
|
|
|
|
// IfStatement
|
|
|
|
// IterationStatement
|
|
|
|
// ContinueStatement
|
|
|
|
// BreakStatement
|
|
|
|
// ReturnStatement
|
|
|
|
// WithStatement
|
|
|
|
// LabelledStatement
|
|
|
|
// SwitchStatement
|
|
|
|
// ThrowStatement
|
|
|
|
// TryStatement
|
|
|
|
// DebuggerStatement
|
|
|
|
|
|
|
|
// Note: Since labels can only be used by 'break' and 'continue'
|
|
|
|
// statements, which themselves are only valid within blocks,
|
|
|
|
// iterations or 'switch' statements (i.e., BreakableStatements),
|
|
|
|
// labels can be simply ignored in all other cases; except for
|
2009-01-15 19:08:34 +00:00
|
|
|
// trivial labeled break statements 'label: break label' which is
|
2008-07-03 15:10:15 +00:00
|
|
|
// parsed into an empty statement.
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::LBRACE:
|
|
|
|
return ParseBlock(labels, ok);
|
|
|
|
|
|
|
|
case Token::SEMICOLON:
|
|
|
|
Next();
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::IF:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseIfStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::DO:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseDoWhileStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::WHILE:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseWhileStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::FOR:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseForStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::CONTINUE:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseContinueStatement(ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::BREAK:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseBreakStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::RETURN:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseReturnStatement(ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::WITH:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseWithStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::SWITCH:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseSwitchStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::THROW:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseThrowStatement(ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::TRY: {
|
|
|
|
// NOTE: It is somewhat complicated to have labels on
|
|
|
|
// try-statements. When breaking out of a try-finally statement,
|
|
|
|
// one must take great care not to treat it as a
|
|
|
|
// fall-through. It is much easier just to wrap the entire
|
|
|
|
// try-statement in a statement block and put the labels there
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* result =
|
|
|
|
factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, result);
|
2008-07-03 15:10:15 +00:00
|
|
|
TryStatement* statement = ParseTryStatement(CHECK_OK);
|
2012-06-04 14:42:58 +00:00
|
|
|
if (result) result->AddStatement(statement, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-02-28 19:07:02 +00:00
|
|
|
case Token::FUNCTION: {
|
2011-09-21 12:27:07 +00:00
|
|
|
// FunctionDeclaration is only allowed in the context of SourceElements
|
|
|
|
// (Ecma 262 5th Edition, clause 14):
|
|
|
|
// SourceElement:
|
|
|
|
// Statement
|
|
|
|
// FunctionDeclaration
|
|
|
|
// Common language extension is to allow function declaration in place
|
|
|
|
// of any statement. This language extension is disabled in strict mode.
|
2013-04-02 17:34:59 +00:00
|
|
|
//
|
|
|
|
// In Harmony mode, this case also handles the extension:
|
|
|
|
// Statement:
|
|
|
|
// GeneratorDeclaration
|
2014-03-11 14:41:22 +00:00
|
|
|
if (strict_mode() == STRICT) {
|
2014-02-14 12:13:33 +00:00
|
|
|
ReportMessageAt(scanner()->peek_location(), "strict_function");
|
2011-02-28 19:07:02 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseFunctionDeclaration(NULL, ok);
|
2011-02-28 19:07:02 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::DEBUGGER:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseDebuggerStatement(ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::VAR:
|
|
|
|
case Token::CONST:
|
|
|
|
return ParseVariableStatement(kStatement, NULL, ok);
|
|
|
|
|
|
|
|
case Token::LET:
|
|
|
|
ASSERT(allow_harmony_scoping());
|
|
|
|
if (strict_mode() == STRICT) {
|
|
|
|
return ParseVariableStatement(kStatement, NULL, ok);
|
|
|
|
}
|
|
|
|
// Fall through.
|
2008-07-03 15:10:15 +00:00
|
|
|
default:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseExpressionOrLabelledStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
VariableProxy* Parser::NewUnresolved(const AstRawString* name,
|
|
|
|
VariableMode mode, Interface* interface) {
|
2011-08-16 14:24:12 +00:00
|
|
|
// If we are inside a function, a declaration of a var/const variable is a
|
|
|
|
// truly local variable, and the scope of the variable is always the function
|
|
|
|
// scope.
|
2011-10-25 08:33:08 +00:00
|
|
|
// Let/const variables in harmony mode are always added to the immediately
|
|
|
|
// enclosing scope.
|
2012-02-28 10:12:39 +00:00
|
|
|
return DeclarationScope(mode)->NewUnresolved(
|
2013-10-14 09:24:58 +00:00
|
|
|
factory(), name, interface, position());
|
2012-02-28 10:12:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
2012-03-08 13:03:07 +00:00
|
|
|
VariableProxy* proxy = declaration->proxy();
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(proxy->raw_name() != NULL);
|
|
|
|
const AstRawString* name = proxy->raw_name();
|
2012-02-28 10:12:39 +00:00
|
|
|
VariableMode mode = declaration->mode();
|
|
|
|
Scope* declaration_scope = DeclarationScope(mode);
|
|
|
|
Variable* var = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2012-07-09 08:59:03 +00:00
|
|
|
// If a suitable scope exists, then we can statically declare this
|
2008-07-03 15:10:15 +00:00
|
|
|
// variable and also set its mode. In any case, a Declaration node
|
|
|
|
// will be added to the scope so that the declaration can be added
|
|
|
|
// to the corresponding activation frame at runtime if necessary.
|
|
|
|
// For instance declarations inside an eval scope need to be added
|
|
|
|
// to the calling function context.
|
2011-04-29 15:31:39 +00:00
|
|
|
// Similarly, strict mode eval scope does not leak variable declarations to
|
|
|
|
// the caller's scope so we declare all locals, too.
|
2011-06-30 14:37:55 +00:00
|
|
|
if (declaration_scope->is_function_scope() ||
|
2014-03-11 14:41:22 +00:00
|
|
|
declaration_scope->is_strict_eval_scope() ||
|
2012-02-20 14:02:59 +00:00
|
|
|
declaration_scope->is_block_scope() ||
|
2012-03-08 13:03:07 +00:00
|
|
|
declaration_scope->is_module_scope() ||
|
2012-08-28 11:25:08 +00:00
|
|
|
declaration_scope->is_global_scope()) {
|
2012-07-09 08:59:03 +00:00
|
|
|
// Declare the variable in the declaration scope.
|
2012-08-28 11:25:08 +00:00
|
|
|
// For the global scope, we have to check for collisions with earlier
|
|
|
|
// (i.e., enclosing) global scopes, to maintain the illusion of a single
|
|
|
|
// global scope.
|
|
|
|
var = declaration_scope->is_global_scope()
|
|
|
|
? declaration_scope->Lookup(name)
|
2014-05-22 15:27:57 +00:00
|
|
|
: declaration_scope->LookupLocal(name);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (var == NULL) {
|
|
|
|
// Declare the name.
|
2012-02-28 10:12:39 +00:00
|
|
|
var = declaration_scope->DeclareLocal(
|
2012-03-08 13:03:07 +00:00
|
|
|
name, mode, declaration->initialization(), proxy->interface());
|
2014-07-09 11:35:05 +00:00
|
|
|
} else if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(var->mode())
|
|
|
|
|| ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
|
|
|
|
!declaration_scope->is_global_scope())) {
|
2011-09-01 12:31:18 +00:00
|
|
|
// The name was declared in this scope before; check for conflicting
|
|
|
|
// re-declarations. We have a conflict if either of the declarations is
|
2012-08-28 11:25:08 +00:00
|
|
|
// not a var (in the global scope, we also have to ignore legacy const for
|
|
|
|
// compatibility). There is similar code in runtime.cc in the Declare
|
2014-07-09 11:35:05 +00:00
|
|
|
// functions. The function CheckConflictingVarDeclarations checks for
|
2011-09-01 12:31:18 +00:00
|
|
|
// var and let bindings from different scopes whereas this is a check for
|
|
|
|
// conflicting declarations within the same scope. This check also covers
|
2012-08-28 11:25:08 +00:00
|
|
|
// the special case
|
2011-09-01 12:31:18 +00:00
|
|
|
//
|
|
|
|
// function () { let x; { var x; } }
|
|
|
|
//
|
|
|
|
// because the var declaration is hoisted to the function scope where 'x'
|
|
|
|
// is already bound.
|
2012-08-29 09:19:53 +00:00
|
|
|
ASSERT(IsDeclaredVariableMode(var->mode()));
|
2014-03-24 14:41:55 +00:00
|
|
|
if (allow_harmony_scoping() && strict_mode() == STRICT) {
|
2014-03-11 14:41:22 +00:00
|
|
|
// In harmony we treat re-declarations as early errors. See
|
2012-08-28 11:25:08 +00:00
|
|
|
// ES5 16 for a definition of early errors.
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("var_redeclaration", name);
|
2012-08-28 11:25:08 +00:00
|
|
|
*ok = false;
|
|
|
|
return;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2014-04-02 12:38:01 +00:00
|
|
|
Expression* expression = NewThrowTypeError(
|
|
|
|
"var_redeclaration", name, declaration->position());
|
2012-08-28 11:25:08 +00:00
|
|
|
declaration_scope->SetIllegalRedeclaration(expression);
|
2014-06-26 11:59:42 +00:00
|
|
|
} else if (mode == VAR) {
|
|
|
|
var->set_maybe_assigned();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We add a declaration node for every declaration. The compiler
|
|
|
|
// will only generate code if necessary. In particular, declarations
|
|
|
|
// for inner local variables that do not represent functions won't
|
|
|
|
// result in any generated code.
|
|
|
|
//
|
|
|
|
// Note that we always add an unresolved proxy even if it's not
|
|
|
|
// used, simply because we don't know in this method (w/o extra
|
|
|
|
// parameters) if the proxy is needed or not. The proxy will be
|
|
|
|
// bound during variable resolution time unless it was pre-bound
|
|
|
|
// below.
|
|
|
|
//
|
|
|
|
// WARNING: This will lead to multiple declaration nodes for the
|
|
|
|
// same variable if it is declared several times. This is not a
|
|
|
|
// semantic issue as long as we keep the source order, but it may be
|
|
|
|
// a performance issue since it may lead to repeated
|
2014-03-26 15:51:48 +00:00
|
|
|
// RuntimeHidden_DeclareContextSlot calls.
|
2012-02-28 10:12:39 +00:00
|
|
|
declaration_scope->AddDeclaration(declaration);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-03-11 14:41:22 +00:00
|
|
|
if (mode == CONST_LEGACY && declaration_scope->is_global_scope()) {
|
2011-11-15 13:48:40 +00:00
|
|
|
// For global const variables we bind the proxy to a variable.
|
2008-07-03 15:10:15 +00:00
|
|
|
ASSERT(resolve); // should be set by all callers
|
2009-06-24 08:01:38 +00:00
|
|
|
Variable::Kind kind = Variable::NORMAL;
|
2013-07-15 14:12:20 +00:00
|
|
|
var = new(zone()) Variable(
|
|
|
|
declaration_scope, name, mode, true, kind,
|
|
|
|
kNeedsInitialization, proxy->interface());
|
2011-11-15 13:48:40 +00:00
|
|
|
} else if (declaration_scope->is_eval_scope() &&
|
2014-03-11 14:41:22 +00:00
|
|
|
declaration_scope->strict_mode() == SLOPPY) {
|
2014-03-11 14:39:08 +00:00
|
|
|
// For variable declarations in a sloppy eval scope the proxy is bound
|
2011-11-15 13:48:40 +00:00
|
|
|
// to a lookup variable to force a dynamic declaration using the
|
|
|
|
// DeclareContextSlot runtime function.
|
|
|
|
Variable::Kind kind = Variable::NORMAL;
|
2013-07-15 14:12:20 +00:00
|
|
|
var = new(zone()) Variable(
|
|
|
|
declaration_scope, name, mode, true, kind,
|
|
|
|
declaration->initialization(), proxy->interface());
|
2011-11-15 13:48:40 +00:00
|
|
|
var->AllocateTo(Variable::LOOKUP, -1);
|
|
|
|
resolve = true;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If requested and we have a local variable, bind the proxy to the variable
|
|
|
|
// at parse-time. This is used for functions (and consts) declared inside
|
|
|
|
// statements: the corresponding function (or const) variable must be in the
|
|
|
|
// function scope and not a statement-local scope, e.g. as provided with a
|
|
|
|
// 'with' statement:
|
|
|
|
//
|
|
|
|
// with (obj) {
|
|
|
|
// function f() {}
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// which is translated into:
|
|
|
|
//
|
|
|
|
// with (obj) {
|
|
|
|
// // in this case this is not: 'var f; f = function () {};'
|
|
|
|
// var f = function () {};
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Note that if 'f' is accessed from inside the 'with' statement, it
|
|
|
|
// will be allocated in the context (because we must be able to look
|
|
|
|
// it up dynamically) but it will also be accessed statically, i.e.,
|
|
|
|
// with a context slot index and a context chain length for this
|
|
|
|
// initialization code. Thus, inside the 'with' statement, we need
|
|
|
|
// both access to the static and the dynamic context chain; the
|
|
|
|
// runtime needs to provide both.
|
2012-03-08 13:03:07 +00:00
|
|
|
if (resolve && var != NULL) {
|
|
|
|
proxy->BindTo(var);
|
|
|
|
|
|
|
|
if (FLAG_harmony_modules) {
|
|
|
|
bool ok;
|
|
|
|
#ifdef DEBUG
|
2014-06-24 14:03:24 +00:00
|
|
|
if (FLAG_print_interface_details) {
|
|
|
|
PrintF("# Declare %.*s ", var->raw_name()->length(),
|
|
|
|
var->raw_name()->raw_data());
|
|
|
|
}
|
2012-03-08 13:03:07 +00:00
|
|
|
#endif
|
2012-06-11 12:42:31 +00:00
|
|
|
proxy->interface()->Unify(var->interface(), zone(), &ok);
|
2012-03-08 13:03:07 +00:00
|
|
|
if (!ok) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (FLAG_print_interfaces) {
|
|
|
|
PrintF("DECLARE TYPE ERROR\n");
|
|
|
|
PrintF("proxy: ");
|
|
|
|
proxy->interface()->Print();
|
|
|
|
PrintF("var: ");
|
|
|
|
var->interface()->Print();
|
|
|
|
}
|
|
|
|
#endif
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("module_type_error", name);
|
2012-03-08 13:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Language extension which is only enabled for source files loaded
|
|
|
|
// through the API's extension mechanism. A native function
|
|
|
|
// declaration is resolved by looking up the function through a
|
|
|
|
// callback provided by the extension.
|
|
|
|
Statement* Parser::ParseNativeDeclaration(bool* ok) {
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FUNCTION, CHECK_OK);
|
2014-02-05 16:26:48 +00:00
|
|
|
// Allow "eval" or "arguments" for backward compatibility.
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
bool done = (peek() == Token::RPAREN);
|
|
|
|
while (!done) {
|
2014-02-05 16:26:48 +00:00
|
|
|
ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
done = (peek() == Token::RPAREN);
|
2010-11-02 07:21:37 +00:00
|
|
|
if (!done) {
|
|
|
|
Expect(Token::COMMA, CHECK_OK);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
Expect(Token::SEMICOLON, CHECK_OK);
|
|
|
|
|
|
|
|
// Make sure that the function containing the native declaration
|
|
|
|
// isn't lazily compiled. The extension structures are only
|
|
|
|
// accessible while parsing the first time not when reparsing
|
|
|
|
// because of lazy compilation.
|
2012-02-28 10:12:39 +00:00
|
|
|
DeclarationScope(VAR)->ForceEagerCompilation();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// TODO(1240846): It's weird that native function declarations are
|
|
|
|
// introduced dynamically when we meet their declarations, whereas
|
2012-01-13 13:09:52 +00:00
|
|
|
// other functions are set up when entering the surrounding scope.
|
2012-07-13 09:29:43 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, VAR, Interface::NewValue());
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewVariableDeclaration(proxy, VAR, scope_, pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declare(declaration, true, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
NativeFunctionLiteral* lit = factory()->NewNativeFunctionLiteral(
|
|
|
|
name, extension_, RelocInfo::kNoPosition);
|
2012-02-08 09:56:33 +00:00
|
|
|
return factory()->NewExpressionStatement(
|
|
|
|
factory()->NewAssignment(
|
2013-10-14 09:24:58 +00:00
|
|
|
Token::INIT_VAR, proxy, lit, RelocInfo::kNoPosition),
|
|
|
|
pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseFunctionDeclaration(
|
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
2009-10-02 12:47:15 +00:00
|
|
|
// FunctionDeclaration ::
|
|
|
|
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
|
2013-04-02 17:34:59 +00:00
|
|
|
// GeneratorDeclaration ::
|
|
|
|
// 'function' '*' Identifier '(' FormalParameterListopt ')'
|
|
|
|
// '{' FunctionBody '}'
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FUNCTION, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
bool is_generator = allow_generators() && Check(Token::MUL);
|
2011-06-24 14:59:51 +00:00
|
|
|
bool is_strict_reserved = false;
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifierOrStrictReservedWord(
|
2011-06-24 14:59:51 +00:00
|
|
|
&is_strict_reserved, CHECK_OK);
|
2009-10-02 12:47:15 +00:00
|
|
|
FunctionLiteral* fun = ParseFunctionLiteral(name,
|
2014-02-14 12:13:33 +00:00
|
|
|
scanner()->location(),
|
2011-06-24 14:59:51 +00:00
|
|
|
is_strict_reserved,
|
2013-04-02 17:34:59 +00:00
|
|
|
is_generator,
|
2013-10-14 09:24:58 +00:00
|
|
|
pos,
|
2011-08-09 12:43:08 +00:00
|
|
|
FunctionLiteral::DECLARATION,
|
2014-06-17 07:23:26 +00:00
|
|
|
FunctionLiteral::NORMAL_ARITY,
|
2009-10-02 12:47:15 +00:00
|
|
|
CHECK_OK);
|
|
|
|
// Even if we're not at the top-level of the global or a function
|
2012-08-28 11:25:08 +00:00
|
|
|
// scope, we treat it as such and introduce the function with its
|
2009-10-02 12:47:15 +00:00
|
|
|
// initial value upon entering the corresponding scope.
|
2014-07-09 11:35:05 +00:00
|
|
|
// In ES6, a function behaves as a lexical binding, except in the
|
|
|
|
// global scope, or the initial scope of eval or another function.
|
2012-08-28 11:25:08 +00:00
|
|
|
VariableMode mode =
|
2014-07-09 11:35:05 +00:00
|
|
|
allow_harmony_scoping() && strict_mode() == STRICT &&
|
|
|
|
!(scope_->is_global_scope() || scope_->is_eval_scope() ||
|
|
|
|
scope_->is_function_scope()) ? LET : VAR;
|
2012-07-13 09:29:43 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue());
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewFunctionDeclaration(proxy, mode, fun, scope_, pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declare(declaration, true, CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
if (names) names->Add(name, zone());
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok) {
|
2014-03-24 14:41:55 +00:00
|
|
|
if (allow_harmony_scoping() && strict_mode() == STRICT) {
|
2014-03-11 14:41:22 +00:00
|
|
|
return ParseScopedBlock(labels, ok);
|
|
|
|
}
|
2011-08-11 16:29:28 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Block ::
|
|
|
|
// '{' Statement* '}'
|
|
|
|
|
|
|
|
// Note that a Block does not introduce a new execution scope!
|
|
|
|
// (ECMA-262, 3rd, 12.2)
|
|
|
|
//
|
|
|
|
// Construct block expecting 16 statements.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* result =
|
|
|
|
factory()->NewBlock(labels, 16, false, RelocInfo::kNoPosition);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, result);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
Statement* stat = ParseStatement(NULL, CHECK_OK);
|
2011-07-06 17:21:32 +00:00
|
|
|
if (stat && !stat->IsEmpty()) {
|
2012-06-04 14:42:58 +00:00
|
|
|
result->AddStatement(stat, zone());
|
2011-07-06 17:21:32 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Block* Parser::ParseScopedBlock(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2012-02-20 14:02:59 +00:00
|
|
|
// The harmony mode uses block elements instead of statements.
|
2011-09-21 12:27:07 +00:00
|
|
|
//
|
|
|
|
// Block ::
|
2012-02-20 14:02:59 +00:00
|
|
|
// '{' BlockElement* '}'
|
2011-09-21 12:27:07 +00:00
|
|
|
|
2011-08-11 16:29:28 +00:00
|
|
|
// Construct block expecting 16 statements.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body =
|
|
|
|
factory()->NewBlock(labels, 16, false, RelocInfo::kNoPosition);
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
2011-08-11 16:29:28 +00:00
|
|
|
|
|
|
|
// Parse the statements and collect escaping labels.
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
block_scope->set_start_position(scanner()->location().beg_pos);
|
2014-02-12 12:02:07 +00:00
|
|
|
{ BlockState block_state(&scope_, block_scope);
|
2012-06-11 12:42:31 +00:00
|
|
|
TargetCollector collector(zone());
|
2011-10-17 09:29:37 +00:00
|
|
|
Target target(&this->target_stack_, &collector);
|
2011-08-11 16:29:28 +00:00
|
|
|
Target target_body(&this->target_stack_, body);
|
|
|
|
|
|
|
|
while (peek() != Token::RBRACE) {
|
2012-02-20 14:02:59 +00:00
|
|
|
Statement* stat = ParseBlockElement(NULL, CHECK_OK);
|
2011-08-11 16:29:28 +00:00
|
|
|
if (stat && !stat->IsEmpty()) {
|
2012-06-04 14:42:58 +00:00
|
|
|
body->AddStatement(stat, zone());
|
2011-08-11 16:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
block_scope->set_end_position(scanner()->location().end_pos);
|
2011-09-02 12:43:28 +00:00
|
|
|
block_scope = block_scope->FinalizeBlockScope();
|
2012-04-16 14:43:27 +00:00
|
|
|
body->set_scope(block_scope);
|
2011-09-06 22:00:59 +00:00
|
|
|
return body;
|
2011-08-11 16:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-16 14:24:12 +00:00
|
|
|
Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*>* names,
|
2011-08-16 14:24:12 +00:00
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// VariableStatement ::
|
|
|
|
// VariableDeclarations ';'
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* ignore;
|
2012-02-20 14:02:59 +00:00
|
|
|
Block* result =
|
2012-02-29 12:12:52 +00:00
|
|
|
ParseVariableDeclarations(var_context, NULL, names, &ignore, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-03-22 13:20:04 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// If the variable declaration declares exactly one non-const
|
2012-02-09 13:39:26 +00:00
|
|
|
// variable, then *out is set to that variable. In all other cases,
|
|
|
|
// *out is untouched; in particular, it is the caller's responsibility
|
2008-07-03 15:10:15 +00:00
|
|
|
// to initialize it properly. This mechanism is used for the parsing
|
|
|
|
// of 'for-in' loops.
|
2011-10-17 12:19:06 +00:00
|
|
|
Block* Parser::ParseVariableDeclarations(
|
|
|
|
VariableDeclarationContext var_context,
|
|
|
|
VariableDeclarationProperties* decl_props,
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*>* names,
|
|
|
|
const AstRawString** out,
|
2011-10-17 12:19:06 +00:00
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// VariableDeclarations ::
|
2011-10-25 08:33:08 +00:00
|
|
|
// ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
|
|
|
|
//
|
|
|
|
// The ES6 Draft Rev3 specifies the following grammar for const declarations
|
|
|
|
//
|
|
|
|
// ConstDeclaration ::
|
|
|
|
// const ConstBinding (',' ConstBinding)* ';'
|
|
|
|
// ConstBinding ::
|
|
|
|
// Identifier '=' AssignmentExpression
|
|
|
|
//
|
|
|
|
// TODO(ES6):
|
|
|
|
// ConstBinding ::
|
|
|
|
// BindingPattern '=' AssignmentExpression
|
2013-10-14 09:24:58 +00:00
|
|
|
|
|
|
|
int pos = peek_position();
|
2011-10-11 08:41:19 +00:00
|
|
|
VariableMode mode = VAR;
|
2011-08-30 11:23:57 +00:00
|
|
|
// True if the binding needs initialization. 'let' and 'const' declared
|
|
|
|
// bindings are created uninitialized by their declaration nodes and
|
|
|
|
// need initialization. 'var' declared bindings are always initialized
|
|
|
|
// immediately by their declaration nodes.
|
|
|
|
bool needs_init = false;
|
2008-07-03 15:10:15 +00:00
|
|
|
bool is_const = false;
|
2011-08-30 11:23:57 +00:00
|
|
|
Token::Value init_op = Token::INIT_VAR;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (peek() == Token::VAR) {
|
|
|
|
Consume(Token::VAR);
|
|
|
|
} else if (peek() == Token::CONST) {
|
2011-11-29 06:38:04 +00:00
|
|
|
// TODO(ES6): The ES6 Draft Rev4 section 12.2.2 reads:
|
|
|
|
//
|
|
|
|
// ConstDeclaration : const ConstBinding (',' ConstBinding)* ';'
|
|
|
|
//
|
|
|
|
// * It is a Syntax Error if the code that matches this production is not
|
|
|
|
// contained in extended code.
|
|
|
|
//
|
2014-03-11 14:39:08 +00:00
|
|
|
// However disallowing const in sloppy mode will break compatibility with
|
2011-11-29 06:38:04 +00:00
|
|
|
// existing pages. Therefore we keep allowing const with the old
|
2014-03-11 14:39:08 +00:00
|
|
|
// non-harmony semantics in sloppy mode.
|
2008-07-03 15:10:15 +00:00
|
|
|
Consume(Token::CONST);
|
2014-03-11 14:41:22 +00:00
|
|
|
switch (strict_mode()) {
|
|
|
|
case SLOPPY:
|
|
|
|
mode = CONST_LEGACY;
|
|
|
|
init_op = Token::INIT_CONST_LEGACY;
|
2011-11-24 15:17:04 +00:00
|
|
|
break;
|
2014-03-11 14:41:22 +00:00
|
|
|
case STRICT:
|
2014-03-24 14:41:55 +00:00
|
|
|
if (allow_harmony_scoping()) {
|
2014-03-11 14:41:22 +00:00
|
|
|
if (var_context == kStatement) {
|
|
|
|
// In strict mode 'const' declarations are only allowed in source
|
|
|
|
// element positions.
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("unprotected_const");
|
2014-03-11 14:41:22 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
mode = CONST;
|
|
|
|
init_op = Token::INIT_CONST;
|
|
|
|
} else {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("strict_const");
|
2011-11-24 15:17:04 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-02-28 18:38:17 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
is_const = true;
|
2011-08-30 11:23:57 +00:00
|
|
|
needs_init = true;
|
2014-07-10 14:06:37 +00:00
|
|
|
} else if (peek() == Token::LET && strict_mode() == STRICT) {
|
2011-08-16 14:24:12 +00:00
|
|
|
Consume(Token::LET);
|
2012-02-20 14:02:59 +00:00
|
|
|
if (var_context == kStatement) {
|
2011-10-25 08:33:08 +00:00
|
|
|
// Let declarations are only allowed in source element positions.
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("unprotected_let");
|
2011-08-16 14:24:12 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-10-11 08:41:19 +00:00
|
|
|
mode = LET;
|
2011-08-30 11:23:57 +00:00
|
|
|
needs_init = true;
|
|
|
|
init_op = Token::INIT_LET;
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
|
|
|
UNREACHABLE(); // by current callers
|
|
|
|
}
|
|
|
|
|
2012-02-28 10:12:39 +00:00
|
|
|
Scope* declaration_scope = DeclarationScope(mode);
|
|
|
|
|
2011-08-16 14:24:12 +00:00
|
|
|
// The scope of a var/const declared variable anywhere inside a function
|
2008-07-03 15:10:15 +00:00
|
|
|
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
|
2011-08-16 14:24:12 +00:00
|
|
|
// transform a source-level var/const declaration into a (Function)
|
2008-07-03 15:10:15 +00:00
|
|
|
// Scope declaration, and rewrite the source-level initialization into an
|
|
|
|
// assignment statement. We use a block to collect multiple assignments.
|
|
|
|
//
|
|
|
|
// We mark the block as initializer block because we don't want the
|
|
|
|
// rewriter to add a '.result' assignment to such a block (to get compliant
|
|
|
|
// behavior for code such as print(eval('var x = 7')), and for cosmetic
|
|
|
|
// reasons when pretty-printing. Also, unless an assignment (initialization)
|
|
|
|
// is inside an initializer block, it is ignored.
|
|
|
|
//
|
|
|
|
// Create new block with one expected declaration.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* block = factory()->NewBlock(NULL, 1, true, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
int nvars = 0; // the number of variables declared
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
do {
|
2010-08-23 13:26:03 +00:00
|
|
|
if (fni_ != NULL) fni_->Enter();
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Parse variable name.
|
|
|
|
if (nvars > 0) Consume(Token::COMMA);
|
2014-02-05 16:26:48 +00:00
|
|
|
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2010-08-23 13:26:03 +00:00
|
|
|
if (fni_ != NULL) fni_->PushVariableName(name);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Declare variable.
|
|
|
|
// Note that we *always* must treat the initial value via a separate init
|
|
|
|
// assignment for variables and constants because the value must be assigned
|
|
|
|
// when the variable is encountered in the source. But the variable/constant
|
|
|
|
// is declared (and set to 'undefined') upon entering the function within
|
|
|
|
// which the variable or constant is declared. Only function variables have
|
|
|
|
// an initial value in the declaration (because they are initialized upon
|
|
|
|
// entering the function).
|
|
|
|
//
|
|
|
|
// If we have a const declaration, in an inner scope, the proxy is always
|
|
|
|
// bound to the declared variable (independent of possibly surrounding with
|
|
|
|
// statements).
|
2011-10-25 08:33:08 +00:00
|
|
|
// For let/const declarations in harmony mode, we can also immediately
|
|
|
|
// pre-resolve the proxy because it resides in the same scope as the
|
|
|
|
// declaration.
|
2012-07-13 09:29:43 +00:00
|
|
|
Interface* interface =
|
|
|
|
is_const ? Interface::NewConst() : Interface::NewValue();
|
|
|
|
VariableProxy* proxy = NewUnresolved(name, mode, interface);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declare(declaration, mode != VAR, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
nvars++;
|
2011-06-30 14:37:55 +00:00
|
|
|
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
2014-06-03 16:12:48 +00:00
|
|
|
ReportMessage("too_many_variables");
|
2011-05-16 08:27:52 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
if (names) names->Add(name, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Parse initialization expression if present and/or needed. A
|
|
|
|
// declaration of the form:
|
|
|
|
//
|
|
|
|
// var v = x;
|
|
|
|
//
|
|
|
|
// is syntactic sugar for:
|
|
|
|
//
|
|
|
|
// var v; v = x;
|
|
|
|
//
|
2014-02-12 12:02:07 +00:00
|
|
|
// In particular, we need to re-lookup 'v' (in scope_, not
|
2011-06-30 14:37:55 +00:00
|
|
|
// declaration_scope) as it may be a different 'v' than the 'v' in the
|
|
|
|
// declaration (e.g., if we are inside a 'with' statement or 'catch'
|
|
|
|
// block).
|
2008-07-03 15:10:15 +00:00
|
|
|
//
|
|
|
|
// However, note that const declarations are different! A const
|
|
|
|
// declaration of the form:
|
|
|
|
//
|
|
|
|
// const c = x;
|
|
|
|
//
|
|
|
|
// is *not* syntactic sugar for:
|
|
|
|
//
|
|
|
|
// const c; c = x;
|
|
|
|
//
|
|
|
|
// The "variable" c initialized to x is the same as the declared
|
|
|
|
// one - there is no re-lookup (see the last parameter of the
|
|
|
|
// Declare() call above).
|
|
|
|
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* initialization_scope = is_const ? declaration_scope : scope_;
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* value = NULL;
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = -1;
|
2011-10-25 08:33:08 +00:00
|
|
|
// Harmony consts have non-optional initializers.
|
2014-03-11 14:41:22 +00:00
|
|
|
if (peek() == Token::ASSIGN || mode == CONST) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::ASSIGN, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
pos = position();
|
2011-08-16 14:24:12 +00:00
|
|
|
value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
|
2010-08-23 13:26:03 +00:00
|
|
|
// Don't infer if it is "a = function(){...}();"-like expression.
|
2011-06-22 20:23:48 +00:00
|
|
|
if (fni_ != NULL &&
|
|
|
|
value->AsCall() == NULL &&
|
|
|
|
value->AsCallNew() == NULL) {
|
|
|
|
fni_->Infer();
|
2011-10-03 19:18:05 +00:00
|
|
|
} else {
|
|
|
|
fni_->RemoveLastFunction();
|
2011-06-22 20:23:48 +00:00
|
|
|
}
|
2011-10-17 12:19:06 +00:00
|
|
|
if (decl_props != NULL) *decl_props = kHasInitializers;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2011-11-08 13:28:53 +00:00
|
|
|
// Record the end position of the initializer.
|
|
|
|
if (proxy->var() != NULL) {
|
2013-10-14 09:24:58 +00:00
|
|
|
proxy->var()->set_initializer_position(position());
|
2011-11-08 13:28:53 +00:00
|
|
|
}
|
|
|
|
|
2011-08-30 11:23:57 +00:00
|
|
|
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
|
|
|
if (value == NULL && needs_init) {
|
2013-10-14 09:24:58 +00:00
|
|
|
value = GetLiteralUndefined(position());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Global variable declarations must be compiled in a specific
|
|
|
|
// way. When the script containing the global variable declaration
|
|
|
|
// is entered, the global variable must be declared, so that if it
|
2012-04-16 13:20:50 +00:00
|
|
|
// doesn't exist (on the global object itself, see ES5 errata) it
|
2008-07-03 15:10:15 +00:00
|
|
|
// gets created with an initial undefined value. This is handled
|
|
|
|
// by the declarations part of the function representing the
|
|
|
|
// top-level global code; see Runtime::DeclareGlobalVariable. If
|
|
|
|
// it already exists (in the object or in a prototype), it is
|
|
|
|
// *not* touched until the variable declaration statement is
|
|
|
|
// executed.
|
|
|
|
//
|
|
|
|
// Executing the variable declaration statement will always
|
2014-07-09 11:35:05 +00:00
|
|
|
// guarantee to give the global object an own property.
|
|
|
|
// This way, global variable declarations can shadow
|
2008-07-03 15:10:15 +00:00
|
|
|
// properties in the prototype chain, but only after the variable
|
|
|
|
// declaration statement has been executed. This is important in
|
|
|
|
// browsers where the global object (window) has lots of
|
|
|
|
// properties defined in prototype objects.
|
2012-08-28 11:25:08 +00:00
|
|
|
if (initialization_scope->is_global_scope() &&
|
2012-08-29 09:19:53 +00:00
|
|
|
!IsLexicalVariableMode(mode)) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Compute the arguments for the runtime call.
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<Expression*>* arguments =
|
|
|
|
new(zone()) ZoneList<Expression*>(3, zone());
|
2011-04-04 06:29:02 +00:00
|
|
|
// We have at least 1 parameter.
|
2014-06-24 14:03:24 +00:00
|
|
|
arguments->Add(factory()->NewStringLiteral(name, pos), zone());
|
2011-03-01 06:10:41 +00:00
|
|
|
CallRuntime* initialize;
|
2011-03-02 04:53:43 +00:00
|
|
|
|
2011-03-01 06:10:41 +00:00
|
|
|
if (is_const) {
|
2012-06-11 12:42:31 +00:00
|
|
|
arguments->Add(value, zone());
|
2011-03-02 04:53:43 +00:00
|
|
|
value = NULL; // zap the value to avoid the unnecessary assignment
|
|
|
|
|
|
|
|
// Construct the call to Runtime_InitializeConstGlobal
|
|
|
|
// and add it to the initialization statement block.
|
|
|
|
// Note that the function does different things depending on
|
|
|
|
// the number of arguments (1 or 2).
|
2012-02-08 09:56:33 +00:00
|
|
|
initialize = factory()->NewCallRuntime(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->initialize_const_global_string(),
|
2014-06-25 15:26:10 +00:00
|
|
|
Runtime::FunctionForId(Runtime::kInitializeConstGlobal),
|
2013-10-14 09:24:58 +00:00
|
|
|
arguments, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2011-03-02 04:53:43 +00:00
|
|
|
// Add strict mode.
|
|
|
|
// We may want to pass singleton to avoid Literal allocations.
|
2014-03-11 14:41:22 +00:00
|
|
|
StrictMode strict_mode = initialization_scope->strict_mode();
|
|
|
|
arguments->Add(factory()->NewNumberLiteral(strict_mode, pos), zone());
|
2011-03-02 04:53:43 +00:00
|
|
|
|
|
|
|
// Be careful not to assign a value to the global variable if
|
|
|
|
// we're in a with. The initialization value should not
|
|
|
|
// necessarily be stored in the global object in that case,
|
|
|
|
// which is why we need to generate a separate assignment node.
|
|
|
|
if (value != NULL && !inside_with()) {
|
2012-06-11 12:42:31 +00:00
|
|
|
arguments->Add(value, zone());
|
2011-03-02 04:53:43 +00:00
|
|
|
value = NULL; // zap the value to avoid the unnecessary assignment
|
|
|
|
}
|
|
|
|
|
|
|
|
// Construct the call to Runtime_InitializeVarGlobal
|
|
|
|
// and add it to the initialization statement block.
|
|
|
|
// Note that the function does different things depending on
|
|
|
|
// the number of arguments (2 or 3).
|
2012-02-08 09:56:33 +00:00
|
|
|
initialize = factory()->NewCallRuntime(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->initialize_var_global_string(),
|
2012-02-08 09:56:33 +00:00
|
|
|
Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
|
2013-10-14 09:24:58 +00:00
|
|
|
arguments, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2011-03-02 04:53:43 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
block->AddStatement(
|
|
|
|
factory()->NewExpressionStatement(initialize, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
2011-11-15 13:48:40 +00:00
|
|
|
} else if (needs_init) {
|
|
|
|
// Constant initializations always assign to the declared constant which
|
|
|
|
// is always at the function scope level. This is only relevant for
|
|
|
|
// dynamically looked-up variables and constants (the start context for
|
|
|
|
// constant lookups is always the function context, while it is the top
|
|
|
|
// context for var declared variables). Sigh...
|
|
|
|
// For 'let' and 'const' declared variables in harmony mode the
|
|
|
|
// initialization also always assigns to the declared variable.
|
|
|
|
ASSERT(proxy != NULL);
|
|
|
|
ASSERT(proxy->var() != NULL);
|
|
|
|
ASSERT(value != NULL);
|
|
|
|
Assignment* assignment =
|
2013-10-14 09:24:58 +00:00
|
|
|
factory()->NewAssignment(init_op, proxy, value, pos);
|
|
|
|
block->AddStatement(
|
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
2011-11-15 13:48:40 +00:00
|
|
|
value = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2011-09-06 21:22:35 +00:00
|
|
|
// Add an assignment node to the initialization statement block if we still
|
2011-11-15 13:48:40 +00:00
|
|
|
// have a pending initialization value.
|
2008-07-03 15:10:15 +00:00
|
|
|
if (value != NULL) {
|
2011-11-15 13:48:40 +00:00
|
|
|
ASSERT(mode == VAR);
|
|
|
|
// 'var' initializations are simply assignments (with all the consequences
|
|
|
|
// if they are inside a 'with' statement - they may change a 'with' object
|
|
|
|
// property).
|
2012-02-08 09:56:33 +00:00
|
|
|
VariableProxy* proxy =
|
2012-07-13 09:29:43 +00:00
|
|
|
initialization_scope->NewUnresolved(factory(), name, interface);
|
2011-04-04 06:29:02 +00:00
|
|
|
Assignment* assignment =
|
2013-10-14 09:24:58 +00:00
|
|
|
factory()->NewAssignment(init_op, proxy, value, pos);
|
|
|
|
block->AddStatement(
|
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2010-08-23 13:26:03 +00:00
|
|
|
|
|
|
|
if (fni_ != NULL) fni_->Leave();
|
2008-07-03 15:10:15 +00:00
|
|
|
} while (peek() == Token::COMMA);
|
|
|
|
|
2011-06-30 14:37:55 +00:00
|
|
|
// If there was a single non-const declaration, return it in the output
|
|
|
|
// parameter for possible use by for/in.
|
|
|
|
if (nvars == 1 && !is_const) {
|
|
|
|
*out = name;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
static bool ContainsLabel(ZoneList<const AstRawString*>* labels,
|
|
|
|
const AstRawString* label) {
|
|
|
|
ASSERT(label != NULL);
|
2014-06-05 08:41:29 +00:00
|
|
|
if (labels != NULL) {
|
|
|
|
for (int i = labels->length(); i-- > 0; ) {
|
2014-06-24 14:03:24 +00:00
|
|
|
if (labels->at(i) == label) {
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
2014-06-05 08:41:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseExpressionOrLabelledStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// ExpressionStatement | LabelledStatement ::
|
|
|
|
// Expression ';'
|
|
|
|
// Identifier ':' Statement
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2011-02-04 18:36:37 +00:00
|
|
|
bool starts_with_idenfifier = peek_any_identifier();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* expr = ParseExpression(true, CHECK_OK);
|
2011-06-20 10:20:57 +00:00
|
|
|
if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL &&
|
2014-02-06 11:59:16 +00:00
|
|
|
expr->AsVariableProxy() != NULL &&
|
|
|
|
!expr->AsVariableProxy()->is_this()) {
|
2010-11-16 12:10:48 +00:00
|
|
|
// Expression is a single identifier, and not, e.g., a parenthesized
|
|
|
|
// identifier.
|
2008-07-03 15:10:15 +00:00
|
|
|
VariableProxy* var = expr->AsVariableProxy();
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = var->raw_name();
|
2008-07-03 15:10:15 +00:00
|
|
|
// TODO(1240780): We don't check for redeclaration of labels
|
|
|
|
// during preparsing since keeping track of the set of active
|
|
|
|
// labels requires nontrivial changes to the way scopes are
|
|
|
|
// structured. However, these are probably changes we want to
|
|
|
|
// make later anyway so we should go back and fix this then.
|
2010-11-02 11:45:47 +00:00
|
|
|
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("label_redeclaration", label);
|
2010-11-02 11:45:47 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
if (labels == NULL) {
|
2014-06-24 14:03:24 +00:00
|
|
|
labels = new(zone()) ZoneList<const AstRawString*>(4, zone());
|
2012-06-11 12:42:31 +00:00
|
|
|
}
|
|
|
|
labels->Add(label, zone());
|
2010-11-02 11:45:47 +00:00
|
|
|
// Remove the "ghost" variable that turned out to be a label
|
|
|
|
// from the top scope. This way, we don't try to resolve it
|
|
|
|
// during the scope processing.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->RemoveUnresolved(var);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::COLON, CHECK_OK);
|
|
|
|
return ParseStatement(labels, ok);
|
|
|
|
}
|
|
|
|
|
2011-06-20 10:20:57 +00:00
|
|
|
// If we have an extension, we allow a native function declaration.
|
|
|
|
// A native function declaration starts with "native function" with
|
|
|
|
// no line-terminator between the two words.
|
|
|
|
if (extension_ != NULL &&
|
|
|
|
peek() == Token::FUNCTION &&
|
2014-02-14 12:13:33 +00:00
|
|
|
!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2011-06-20 10:20:57 +00:00
|
|
|
expr != NULL &&
|
2014-02-06 11:59:16 +00:00
|
|
|
expr->AsVariableProxy() != NULL &&
|
2014-06-24 14:03:24 +00:00
|
|
|
expr->AsVariableProxy()->raw_name() ==
|
|
|
|
ast_value_factory_->native_string() &&
|
2014-02-14 12:13:33 +00:00
|
|
|
!scanner()->literal_contains_escapes()) {
|
2011-06-20 10:20:57 +00:00
|
|
|
return ParseNativeDeclaration(ok);
|
|
|
|
}
|
|
|
|
|
2012-02-24 15:53:09 +00:00
|
|
|
// Parsed expression statement, or the context-sensitive 'module' keyword.
|
|
|
|
// Only expect semicolon in the former case.
|
|
|
|
if (!FLAG_harmony_modules ||
|
|
|
|
peek() != Token::IDENTIFIER ||
|
2014-02-14 12:13:33 +00:00
|
|
|
scanner()->HasAnyLineTerminatorBeforeNext() ||
|
2014-02-06 11:59:16 +00:00
|
|
|
expr->AsVariableProxy() == NULL ||
|
2014-06-24 14:03:24 +00:00
|
|
|
expr->AsVariableProxy()->raw_name() !=
|
|
|
|
ast_value_factory_->module_string() ||
|
2014-02-14 12:13:33 +00:00
|
|
|
scanner()->literal_contains_escapes()) {
|
2012-02-24 15:53:09 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
}
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewExpressionStatement(expr, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// IfStatement ::
|
|
|
|
// 'if' '(' Expression ')' Statement ('else' Statement)?
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::IF, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* condition = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
Statement* then_statement = ParseStatement(labels, CHECK_OK);
|
|
|
|
Statement* else_statement = NULL;
|
|
|
|
if (peek() == Token::ELSE) {
|
|
|
|
Next();
|
|
|
|
else_statement = ParseStatement(labels, CHECK_OK);
|
2010-11-02 11:45:47 +00:00
|
|
|
} else {
|
2013-10-14 09:24:58 +00:00
|
|
|
else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewIfStatement(
|
|
|
|
condition, then_statement, else_statement, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseContinueStatement(bool* ok) {
|
|
|
|
// ContinueStatement ::
|
|
|
|
// 'continue' Identifier? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::CONTINUE, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
Token::Value tok = peek();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2008-09-08 07:58:54 +00:00
|
|
|
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
2014-02-05 16:26:48 +00:00
|
|
|
// ECMA allows "eval" or "arguments" as labels even in strict mode.
|
|
|
|
label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2014-06-24 14:03:24 +00:00
|
|
|
IterationStatement* target = LookupContinueTarget(label, CHECK_OK);
|
2010-11-02 11:45:47 +00:00
|
|
|
if (target == NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
// Illegal continue statement.
|
|
|
|
const char* message = "illegal_continue";
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
message = "unknown_label";
|
|
|
|
}
|
2014-06-03 16:12:48 +00:00
|
|
|
ParserTraits::ReportMessage(message, label);
|
2011-01-17 09:36:10 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewContinueStatement(target, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// BreakStatement ::
|
|
|
|
// 'break' Identifier? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::BREAK, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
Token::Value tok = peek();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2008-09-08 07:58:54 +00:00
|
|
|
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
2014-02-05 16:26:48 +00:00
|
|
|
// ECMA allows "eval" or "arguments" as labels even in strict mode.
|
|
|
|
label = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-01-15 19:08:34 +00:00
|
|
|
// Parse labeled break statements that target themselves into
|
2008-07-03 15:10:15 +00:00
|
|
|
// empty statements, e.g. 'l1: l2: l3: break l2;'
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL && ContainsLabel(labels, label)) {
|
2012-02-08 08:40:11 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
BreakableStatement* target = NULL;
|
2010-11-02 11:45:47 +00:00
|
|
|
target = LookupBreakTarget(label, CHECK_OK);
|
|
|
|
if (target == NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
// Illegal break statement.
|
|
|
|
const char* message = "illegal_break";
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
message = "unknown_label";
|
|
|
|
}
|
2014-06-03 16:12:48 +00:00
|
|
|
ParserTraits::ReportMessage(message, label);
|
2011-01-17 09:36:10 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewBreakStatement(target, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseReturnStatement(bool* ok) {
|
|
|
|
// ReturnStatement ::
|
|
|
|
// 'return' Expression? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
// Consume the return token. It is necessary to do that before
|
2008-07-03 15:10:15 +00:00
|
|
|
// reporting any errors on it, because of the way errors are
|
|
|
|
// reported (underlining).
|
|
|
|
Expect(Token::RETURN, CHECK_OK);
|
2014-04-02 12:38:01 +00:00
|
|
|
Scanner::Location loc = scanner()->location();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2011-12-07 16:03:29 +00:00
|
|
|
Token::Value tok = peek();
|
|
|
|
Statement* result;
|
2013-04-19 14:11:23 +00:00
|
|
|
Expression* return_value;
|
2014-02-14 12:13:33 +00:00
|
|
|
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
|
2011-12-07 16:03:29 +00:00
|
|
|
tok == Token::SEMICOLON ||
|
|
|
|
tok == Token::RBRACE ||
|
|
|
|
tok == Token::EOS) {
|
2013-10-14 09:24:58 +00:00
|
|
|
return_value = GetLiteralUndefined(position());
|
2011-12-07 16:03:29 +00:00
|
|
|
} else {
|
2013-04-19 14:11:23 +00:00
|
|
|
return_value = ParseExpression(true, CHECK_OK);
|
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
if (is_generator()) {
|
|
|
|
Expression* generator = factory()->NewVariableProxy(
|
2014-02-12 12:02:07 +00:00
|
|
|
function_state_->generator_object_variable());
|
2013-04-19 14:11:23 +00:00
|
|
|
Expression* yield = factory()->NewYield(
|
2014-04-02 12:38:01 +00:00
|
|
|
generator, return_value, Yield::FINAL, loc.beg_pos);
|
|
|
|
result = factory()->NewExpressionStatement(yield, loc.beg_pos);
|
2013-04-19 14:11:23 +00:00
|
|
|
} else {
|
2014-04-02 12:38:01 +00:00
|
|
|
result = factory()->NewReturnStatement(return_value, loc.beg_pos);
|
2011-12-07 16:03:29 +00:00
|
|
|
}
|
|
|
|
|
2014-04-02 12:38:01 +00:00
|
|
|
Scope* decl_scope = scope_->DeclarationScope();
|
|
|
|
if (decl_scope->is_global_scope() || decl_scope->is_eval_scope()) {
|
|
|
|
ReportMessageAt(loc, "illegal_return");
|
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2011-12-07 16:03:29 +00:00
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// WithStatement ::
|
|
|
|
// 'with' '(' Expression ')' Statement
|
|
|
|
|
|
|
|
Expect(Token::WITH, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2011-01-20 18:51:47 +00:00
|
|
|
|
2014-03-11 14:41:22 +00:00
|
|
|
if (strict_mode() == STRICT) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("strict_mode_with");
|
2011-01-20 18:51:47 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* expr = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->DeclarationScope()->RecordWithStatement();
|
|
|
|
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
|
2011-10-17 09:29:37 +00:00
|
|
|
Statement* stmt;
|
2014-02-12 12:02:07 +00:00
|
|
|
{ BlockState block_state(&scope_, with_scope);
|
2014-02-14 12:13:33 +00:00
|
|
|
with_scope->set_start_position(scanner()->peek_location().beg_pos);
|
2011-10-17 09:29:37 +00:00
|
|
|
stmt = ParseStatement(labels, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
with_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 09:29:37 +00:00
|
|
|
}
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewWithStatement(with_scope, expr, stmt, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
|
|
|
|
// CaseClause ::
|
|
|
|
// 'case' Expression ':' Statement*
|
|
|
|
// 'default' ':' Statement*
|
|
|
|
|
|
|
|
Expression* label = NULL; // NULL expression indicates default case
|
|
|
|
if (peek() == Token::CASE) {
|
|
|
|
Expect(Token::CASE, CHECK_OK);
|
|
|
|
label = ParseExpression(true, CHECK_OK);
|
|
|
|
} else {
|
|
|
|
Expect(Token::DEFAULT, CHECK_OK);
|
|
|
|
if (*default_seen_ptr) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("multiple_defaults_in_switch");
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*default_seen_ptr = true;
|
|
|
|
}
|
|
|
|
Expect(Token::COLON, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<Statement*>* statements =
|
|
|
|
new(zone()) ZoneList<Statement*>(5, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
while (peek() != Token::CASE &&
|
|
|
|
peek() != Token::DEFAULT &&
|
|
|
|
peek() != Token::RBRACE) {
|
|
|
|
Statement* stat = ParseStatement(NULL, CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
statements->Add(stat, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2013-10-14 11:06:15 +00:00
|
|
|
return factory()->NewCaseClause(label, statements, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
SwitchStatement* Parser::ParseSwitchStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// SwitchStatement ::
|
|
|
|
// 'switch' '(' Expression ')' '{' CaseClause* '}'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
SwitchStatement* statement =
|
|
|
|
factory()->NewSwitchStatement(labels, peek_position());
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, statement);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::SWITCH, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* tag = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
bool default_seen = false;
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<CaseClause*>* cases = new(zone()) ZoneList<CaseClause*>(4, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
cases->Add(clause, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
|
2010-11-02 11:45:47 +00:00
|
|
|
if (statement) statement->Initialize(tag, cases);
|
2008-07-03 15:10:15 +00:00
|
|
|
return statement;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseThrowStatement(bool* ok) {
|
|
|
|
// ThrowStatement ::
|
|
|
|
// 'throw' Expression ';'
|
|
|
|
|
|
|
|
Expect(Token::THROW, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (scanner()->HasAnyLineTerminatorBeforeNext()) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("newline_after_throw");
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Expression* exception = ParseExpression(true, CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewExpressionStatement(
|
|
|
|
factory()->NewThrow(exception, pos), pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|
|
|
// TryStatement ::
|
|
|
|
// 'try' Block Catch
|
|
|
|
// 'try' Block Finally
|
|
|
|
// 'try' Block Catch Finally
|
|
|
|
//
|
|
|
|
// Catch ::
|
|
|
|
// 'catch' '(' Identifier ')' Block
|
|
|
|
//
|
|
|
|
// Finally ::
|
|
|
|
// 'finally' Block
|
|
|
|
|
|
|
|
Expect(Token::TRY, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2012-06-11 12:42:31 +00:00
|
|
|
TargetCollector try_collector(zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
Block* try_block;
|
|
|
|
|
2011-06-08 13:55:33 +00:00
|
|
|
{ Target target(&this->target_stack_, &try_collector);
|
2008-07-03 15:10:15 +00:00
|
|
|
try_block = ParseBlock(NULL, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
Token::Value tok = peek();
|
|
|
|
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("no_catch_or_finally");
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we can break out from the catch block and there is a finally block,
|
2011-06-08 13:55:33 +00:00
|
|
|
// then we will need to collect escaping targets from the catch
|
|
|
|
// block. Since we don't know yet if there will be a finally block, we
|
|
|
|
// always collect the targets.
|
2012-06-11 12:42:31 +00:00
|
|
|
TargetCollector catch_collector(zone());
|
2011-06-30 14:37:55 +00:00
|
|
|
Scope* catch_scope = NULL;
|
|
|
|
Variable* catch_variable = NULL;
|
2011-06-08 13:55:33 +00:00
|
|
|
Block* catch_block = NULL;
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (tok == Token::CATCH) {
|
|
|
|
Consume(Token::CATCH);
|
|
|
|
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2014-02-12 12:02:07 +00:00
|
|
|
catch_scope = NewScope(scope_, CATCH_SCOPE);
|
2014-02-14 12:13:33 +00:00
|
|
|
catch_scope->set_start_position(scanner()->location().beg_pos);
|
2014-02-05 16:26:48 +00:00
|
|
|
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
|
2011-01-20 18:51:47 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2014-02-10 08:45:13 +00:00
|
|
|
Target target(&this->target_stack_, &catch_collector);
|
2014-03-11 14:41:22 +00:00
|
|
|
VariableMode mode =
|
2014-03-24 14:41:55 +00:00
|
|
|
allow_harmony_scoping() && strict_mode() == STRICT ? LET : VAR;
|
2014-02-10 08:45:13 +00:00
|
|
|
catch_variable =
|
|
|
|
catch_scope->DeclareLocal(name, mode, kCreatedInitialized);
|
|
|
|
|
2014-02-12 12:02:07 +00:00
|
|
|
BlockState block_state(&scope_, catch_scope);
|
2014-02-10 08:45:13 +00:00
|
|
|
catch_block = ParseBlock(NULL, CHECK_OK);
|
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
catch_scope->set_end_position(scanner()->location().end_pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
tok = peek();
|
|
|
|
}
|
|
|
|
|
2011-06-08 13:55:33 +00:00
|
|
|
Block* finally_block = NULL;
|
2014-02-10 08:45:13 +00:00
|
|
|
ASSERT(tok == Token::FINALLY || catch_block != NULL);
|
|
|
|
if (tok == Token::FINALLY) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Consume(Token::FINALLY);
|
|
|
|
finally_block = ParseBlock(NULL, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simplify the AST nodes by converting:
|
2011-06-08 13:55:33 +00:00
|
|
|
// 'try B0 catch B1 finally B2'
|
2008-07-03 15:10:15 +00:00
|
|
|
// to:
|
2011-06-08 13:55:33 +00:00
|
|
|
// 'try { try B0 catch B1 } finally B2'
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2010-11-02 11:45:47 +00:00
|
|
|
if (catch_block != NULL && finally_block != NULL) {
|
2011-06-30 14:37:55 +00:00
|
|
|
// If we have both, create an inner try/catch.
|
|
|
|
ASSERT(catch_scope != NULL && catch_variable != NULL);
|
2014-02-12 12:02:07 +00:00
|
|
|
int index = function_state_->NextHandlerIndex();
|
2012-02-08 09:56:33 +00:00
|
|
|
TryCatchStatement* statement = factory()->NewTryCatchStatement(
|
2013-10-14 09:24:58 +00:00
|
|
|
index, try_block, catch_scope, catch_variable, catch_block,
|
|
|
|
RelocInfo::kNoPosition);
|
2011-06-08 13:55:33 +00:00
|
|
|
statement->set_escaping_targets(try_collector.targets());
|
2013-10-14 09:24:58 +00:00
|
|
|
try_block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
|
2012-06-04 14:42:58 +00:00
|
|
|
try_block->AddStatement(statement, zone());
|
2011-06-30 14:37:55 +00:00
|
|
|
catch_block = NULL; // Clear to indicate it's been handled.
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TryStatement* result = NULL;
|
2010-11-02 11:45:47 +00:00
|
|
|
if (catch_block != NULL) {
|
|
|
|
ASSERT(finally_block == NULL);
|
2011-06-30 14:37:55 +00:00
|
|
|
ASSERT(catch_scope != NULL && catch_variable != NULL);
|
2014-02-12 12:02:07 +00:00
|
|
|
int index = function_state_->NextHandlerIndex();
|
2012-02-08 09:56:33 +00:00
|
|
|
result = factory()->NewTryCatchStatement(
|
2013-10-14 09:24:58 +00:00
|
|
|
index, try_block, catch_scope, catch_variable, catch_block, pos);
|
2010-11-02 11:45:47 +00:00
|
|
|
} else {
|
|
|
|
ASSERT(finally_block != NULL);
|
2014-02-12 12:02:07 +00:00
|
|
|
int index = function_state_->NextHandlerIndex();
|
2013-10-14 09:24:58 +00:00
|
|
|
result = factory()->NewTryFinallyStatement(
|
|
|
|
index, try_block, finally_block, pos);
|
2011-06-08 13:55:33 +00:00
|
|
|
// Combine the jump targets of the try block and the possible catch block.
|
2012-06-11 12:42:31 +00:00
|
|
|
try_collector.targets()->AddAll(*catch_collector.targets(), zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2011-06-08 13:55:33 +00:00
|
|
|
result->set_escaping_targets(try_collector.targets());
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
DoWhileStatement* Parser::ParseDoWhileStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// DoStatement ::
|
|
|
|
// 'do' Statement 'while' '(' Expression ')' ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
DoWhileStatement* loop =
|
|
|
|
factory()->NewDoWhileStatement(labels, peek_position());
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::DO, CHECK_OK);
|
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
|
|
|
Expect(Token::WHILE, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2009-11-16 21:59:31 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* cond = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
// Allow do-statements to be terminated with and without
|
|
|
|
// semi-colons. This allows code such as 'do;while(0)return' to
|
|
|
|
// parse, which would not be the case if we had used the
|
|
|
|
// ExpectSemicolon() functionality here.
|
|
|
|
if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON);
|
|
|
|
|
2009-10-12 13:14:06 +00:00
|
|
|
if (loop != NULL) loop->Initialize(cond, body);
|
2008-07-03 15:10:15 +00:00
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
WhileStatement* Parser::ParseWhileStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// WhileStatement ::
|
|
|
|
// 'while' '(' Expression ')' Statement
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
WhileStatement* loop = factory()->NewWhileStatement(labels, peek_position());
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::WHILE, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* cond = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
|
|
|
|
2009-10-12 13:14:06 +00:00
|
|
|
if (loop != NULL) loop->Initialize(cond, body);
|
2008-07-03 15:10:15 +00:00
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-12 12:37:44 +00:00
|
|
|
bool Parser::CheckInOrOf(bool accept_OF,
|
|
|
|
ForEachStatement::VisitMode* visit_mode) {
|
2013-06-06 14:38:26 +00:00
|
|
|
if (Check(Token::IN)) {
|
|
|
|
*visit_mode = ForEachStatement::ENUMERATE;
|
|
|
|
return true;
|
2013-06-12 12:37:44 +00:00
|
|
|
} else if (allow_for_of() && accept_OF &&
|
|
|
|
CheckContextualKeyword(CStrVector("of"))) {
|
2013-06-06 14:38:26 +00:00
|
|
|
*visit_mode = ForEachStatement::ITERATE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-07 11:12:21 +00:00
|
|
|
void Parser::InitializeForEachStatement(ForEachStatement* stmt,
|
|
|
|
Expression* each,
|
|
|
|
Expression* subject,
|
|
|
|
Statement* body) {
|
|
|
|
ForOfStatement* for_of = stmt->AsForOfStatement();
|
|
|
|
|
|
|
|
if (for_of != NULL) {
|
2014-06-12 17:31:54 +00:00
|
|
|
Variable* iterable = scope_->DeclarationScope()->NewTemporary(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->dot_iterable_string());
|
2014-02-12 12:02:07 +00:00
|
|
|
Variable* iterator = scope_->DeclarationScope()->NewTemporary(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->dot_iterator_string());
|
2014-02-12 12:02:07 +00:00
|
|
|
Variable* result = scope_->DeclarationScope()->NewTemporary(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->dot_result_string());
|
2013-06-07 11:12:21 +00:00
|
|
|
|
2014-06-12 17:31:54 +00:00
|
|
|
Expression* assign_iterable;
|
2013-06-07 11:12:21 +00:00
|
|
|
Expression* assign_iterator;
|
|
|
|
Expression* next_result;
|
|
|
|
Expression* result_done;
|
|
|
|
Expression* assign_each;
|
|
|
|
|
2014-06-12 17:31:54 +00:00
|
|
|
// var iterable = subject;
|
2013-06-07 11:12:21 +00:00
|
|
|
{
|
2014-06-12 17:31:54 +00:00
|
|
|
Expression* iterable_proxy = factory()->NewVariableProxy(iterable);
|
|
|
|
assign_iterable = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, iterable_proxy, subject, subject->position());
|
|
|
|
}
|
|
|
|
|
|
|
|
// var iterator = iterable[Symbol.iterator]();
|
|
|
|
{
|
|
|
|
Expression* iterable_proxy = factory()->NewVariableProxy(iterable);
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* iterator_symbol_literal =
|
|
|
|
factory()->NewSymbolLiteral("symbolIterator", RelocInfo::kNoPosition);
|
2014-06-12 17:31:54 +00:00
|
|
|
// FIXME(wingo): Unhappily, it will be a common error that the RHS of a
|
|
|
|
// for-of doesn't have a Symbol.iterator property. We should do better
|
|
|
|
// than informing the user that "undefined is not a function".
|
|
|
|
int pos = subject->position();
|
|
|
|
Expression* iterator_property = factory()->NewProperty(
|
|
|
|
iterable_proxy, iterator_symbol_literal, pos);
|
|
|
|
ZoneList<Expression*>* iterator_arguments =
|
|
|
|
new(zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
Expression* iterator_call = factory()->NewCall(
|
|
|
|
iterator_property, iterator_arguments, pos);
|
2013-06-07 11:12:21 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
|
|
|
assign_iterator = factory()->NewAssignment(
|
2014-06-12 17:31:54 +00:00
|
|
|
Token::ASSIGN, iterator_proxy, iterator_call, RelocInfo::kNoPosition);
|
2013-06-07 11:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// var result = iterator.next();
|
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* next_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory_->next_string(), RelocInfo::kNoPosition);
|
2013-06-07 11:12:21 +00:00
|
|
|
Expression* next_property = factory()->NewProperty(
|
|
|
|
iterator_proxy, next_literal, RelocInfo::kNoPosition);
|
|
|
|
ZoneList<Expression*>* next_arguments =
|
|
|
|
new(zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
Expression* next_call = factory()->NewCall(
|
|
|
|
next_property, next_arguments, RelocInfo::kNoPosition);
|
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
next_result = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, result_proxy, next_call, RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// result.done
|
|
|
|
{
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* done_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory_->done_string(), RelocInfo::kNoPosition);
|
2013-06-07 11:12:21 +00:00
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
result_done = factory()->NewProperty(
|
|
|
|
result_proxy, done_literal, RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// each = result.value
|
|
|
|
{
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* value_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory_->value_string(), RelocInfo::kNoPosition);
|
2013-06-07 11:12:21 +00:00
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
Expression* result_value = factory()->NewProperty(
|
|
|
|
result_proxy, value_literal, RelocInfo::kNoPosition);
|
|
|
|
assign_each = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, each, result_value, RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
for_of->Initialize(each, subject, body,
|
2014-06-12 17:31:54 +00:00
|
|
|
assign_iterable,
|
|
|
|
assign_iterator,
|
|
|
|
next_result,
|
|
|
|
result_done,
|
|
|
|
assign_each);
|
2013-06-07 11:12:21 +00:00
|
|
|
} else {
|
|
|
|
stmt->Initialize(each, subject, body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-26 08:07:02 +00:00
|
|
|
Statement* Parser::DesugarLetBindingsInForStatement(
|
2014-06-24 14:03:24 +00:00
|
|
|
Scope* inner_scope, ZoneList<const AstRawString*>* names,
|
|
|
|
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
|
|
|
|
Statement* body, bool* ok) {
|
2014-05-26 08:07:02 +00:00
|
|
|
// ES6 13.6.3.4 specifies that on each loop iteration the let variables are
|
|
|
|
// copied into a new environment. After copying, the "next" statement of the
|
|
|
|
// loop is executed to update the loop variables. The loop condition is
|
|
|
|
// checked and the loop body is executed.
|
|
|
|
//
|
|
|
|
// We rewrite a for statement of the form
|
|
|
|
//
|
|
|
|
// for (let x = i; cond; next) body
|
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// let x = i;
|
|
|
|
// temp_x = x;
|
|
|
|
// flag = 1;
|
|
|
|
// for (;;) {
|
|
|
|
// let x = temp_x;
|
|
|
|
// if (flag == 1) {
|
|
|
|
// flag = 0;
|
|
|
|
// } else {
|
|
|
|
// next;
|
|
|
|
// }
|
|
|
|
// if (cond) {
|
|
|
|
// <empty>
|
|
|
|
// } else {
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// b
|
|
|
|
// temp_x = x;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
ASSERT(names->length() > 0);
|
|
|
|
Scope* for_scope = scope_;
|
|
|
|
ZoneList<Variable*> temps(names->length(), zone());
|
|
|
|
|
|
|
|
Block* outer_block = factory()->NewBlock(NULL, names->length() + 3, false,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
outer_block->AddStatement(init, zone());
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* temp_name = ast_value_factory_->dot_for_string();
|
2014-05-26 08:07:02 +00:00
|
|
|
|
|
|
|
// For each let variable x:
|
|
|
|
// make statement: temp_x = x.
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
|
|
|
VariableProxy* proxy =
|
|
|
|
NewUnresolved(names->at(i), LET, Interface::NewValue());
|
|
|
|
Variable* temp = scope_->DeclarationScope()->NewTemporary(temp_name);
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
|
|
|
outer_block->AddStatement(assignment_statement, zone());
|
|
|
|
temps.Add(temp, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Variable* flag = scope_->DeclarationScope()->NewTemporary(temp_name);
|
|
|
|
// Make statement: flag = 1.
|
|
|
|
{
|
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
|
|
|
outer_block->AddStatement(assignment_statement, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
outer_block->AddStatement(loop, zone());
|
|
|
|
outer_block->set_scope(for_scope);
|
|
|
|
scope_ = inner_scope;
|
|
|
|
|
|
|
|
Block* inner_block = factory()->NewBlock(NULL, 2 * names->length() + 3,
|
|
|
|
false, RelocInfo::kNoPosition);
|
|
|
|
int pos = scanner()->location().beg_pos;
|
|
|
|
ZoneList<Variable*> inner_vars(names->length(), zone());
|
|
|
|
|
|
|
|
// For each let variable x:
|
|
|
|
// make statement: let x = temp_x.
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
|
|
|
VariableProxy* proxy =
|
|
|
|
NewUnresolved(names->at(i), LET, Interface::NewValue());
|
|
|
|
Declaration* declaration =
|
|
|
|
factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
|
|
|
|
Declare(declaration, true, CHECK_OK);
|
|
|
|
inner_vars.Add(declaration->proxy()->var(), zone());
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT_LET, proxy, temp_proxy, pos);
|
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, pos);
|
|
|
|
proxy->var()->set_initializer_position(pos);
|
|
|
|
inner_block->AddStatement(assignment_statement, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make statement: if (flag == 1) { flag = 0; } else { next; }.
|
2014-07-09 07:50:11 +00:00
|
|
|
if (next) {
|
2014-05-26 08:07:02 +00:00
|
|
|
Expression* compare = NULL;
|
|
|
|
// Make compare expresion: flag == 1.
|
|
|
|
{
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
|
|
|
compare = factory()->NewCompareOperation(
|
2014-06-05 07:33:01 +00:00
|
|
|
Token::EQ, flag_proxy, const1, pos);
|
2014-05-26 08:07:02 +00:00
|
|
|
}
|
|
|
|
Statement* clear_flag = NULL;
|
|
|
|
// Make statement: flag = 0.
|
|
|
|
{
|
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* const0 = factory()->NewSmiLiteral(0, RelocInfo::kNoPosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, flag_proxy, const0, RelocInfo::kNoPosition);
|
|
|
|
clear_flag = factory()->NewExpressionStatement(assignment, pos);
|
|
|
|
}
|
|
|
|
Statement* clear_flag_or_next = factory()->NewIfStatement(
|
|
|
|
compare, clear_flag, next, RelocInfo::kNoPosition);
|
|
|
|
inner_block->AddStatement(clear_flag_or_next, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Make statement: if (cond) { } else { break; }.
|
2014-07-09 07:50:11 +00:00
|
|
|
if (cond) {
|
2014-05-26 08:07:02 +00:00
|
|
|
Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2014-06-24 14:03:24 +00:00
|
|
|
BreakableStatement* t = LookupBreakTarget(NULL, CHECK_OK);
|
2014-05-26 08:07:02 +00:00
|
|
|
Statement* stop = factory()->NewBreakStatement(t, RelocInfo::kNoPosition);
|
|
|
|
Statement* if_not_cond_break = factory()->NewIfStatement(
|
2014-06-05 07:33:01 +00:00
|
|
|
cond, empty, stop, cond->position());
|
2014-05-26 08:07:02 +00:00
|
|
|
inner_block->AddStatement(if_not_cond_break, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
inner_block->AddStatement(body, zone());
|
|
|
|
|
|
|
|
// For each let variable x:
|
|
|
|
// make statement: temp_x = x;
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
int pos = scanner()->location().end_pos;
|
|
|
|
VariableProxy* proxy = factory()->NewVariableProxy(inner_vars.at(i), pos);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
|
|
|
inner_block->AddStatement(assignment_statement, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
inner_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
inner_block->set_scope(inner_scope);
|
|
|
|
scope_ = for_scope;
|
|
|
|
|
|
|
|
loop->Initialize(NULL, NULL, NULL, inner_block);
|
|
|
|
return outer_block;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// ForStatement ::
|
|
|
|
// 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Statement* init = NULL;
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*> let_bindings(1, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2011-10-17 12:19:06 +00:00
|
|
|
// Create an in-between scope for let-bound iteration variables.
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* saved_scope = scope_;
|
|
|
|
Scope* for_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
scope_ = for_scope;
|
2011-10-17 12:19:06 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FOR, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_start_position(scanner()->location().beg_pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (peek() != Token::SEMICOLON) {
|
|
|
|
if (peek() == Token::VAR || peek() == Token::CONST) {
|
2012-07-13 09:29:43 +00:00
|
|
|
bool is_const = peek() == Token::CONST;
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = NULL;
|
2013-06-12 12:37:44 +00:00
|
|
|
VariableDeclarationProperties decl_props = kHasNoInitializers;
|
2008-07-03 15:10:15 +00:00
|
|
|
Block* variable_statement =
|
2013-06-12 12:37:44 +00:00
|
|
|
ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
|
|
|
|
CHECK_OK);
|
|
|
|
bool accept_OF = decl_props == kHasNoInitializers;
|
2013-06-06 14:38:26 +00:00
|
|
|
ForEachStatement::VisitMode mode;
|
2011-06-30 14:37:55 +00:00
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
if (name != NULL && CheckInOrOf(accept_OF, &mode)) {
|
2012-07-13 09:29:43 +00:00
|
|
|
Interface* interface =
|
|
|
|
is_const ? Interface::NewConst() : Interface::NewValue();
|
2013-10-14 09:24:58 +00:00
|
|
|
ForEachStatement* loop =
|
|
|
|
factory()->NewForEachStatement(mode, labels, pos);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expression* enumerable = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2012-10-05 09:07:53 +00:00
|
|
|
VariableProxy* each =
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->NewUnresolved(factory(), name, interface);
|
2008-07-03 15:10:15 +00:00
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
2013-06-07 11:12:21 +00:00
|
|
|
InitializeForEachStatement(loop, each, enumerable, body);
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* result =
|
|
|
|
factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
|
2012-06-04 14:42:58 +00:00
|
|
|
result->AddStatement(variable_statement, zone());
|
|
|
|
result->AddStatement(loop, zone());
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_ = saved_scope;
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
|
|
|
ASSERT(for_scope == NULL);
|
2010-11-02 11:45:47 +00:00
|
|
|
// Parsed for-in loop w/ variable/const declaration.
|
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
|
|
|
init = variable_statement;
|
|
|
|
}
|
2014-07-10 14:06:37 +00:00
|
|
|
} else if (peek() == Token::LET && strict_mode() == STRICT) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = NULL;
|
2011-10-17 12:19:06 +00:00
|
|
|
VariableDeclarationProperties decl_props = kHasNoInitializers;
|
|
|
|
Block* variable_statement =
|
2014-06-24 14:03:24 +00:00
|
|
|
ParseVariableDeclarations(kForStatement, &decl_props, &let_bindings,
|
|
|
|
&name, CHECK_OK);
|
|
|
|
bool accept_IN = name != NULL && decl_props != kHasInitializers;
|
2013-06-12 12:37:44 +00:00
|
|
|
bool accept_OF = decl_props == kHasNoInitializers;
|
2013-06-06 14:38:26 +00:00
|
|
|
ForEachStatement::VisitMode mode;
|
|
|
|
|
2013-06-12 12:37:44 +00:00
|
|
|
if (accept_IN && CheckInOrOf(accept_OF, &mode)) {
|
2011-10-17 12:19:06 +00:00
|
|
|
// Rewrite a for-in statement of the form
|
|
|
|
//
|
|
|
|
// for (let x in e) b
|
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
|
|
|
// <let x' be a temporary variable>
|
|
|
|
// for (x' in e) {
|
|
|
|
// let x;
|
|
|
|
// x = x';
|
|
|
|
// b;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// TODO(keuchel): Move the temporary variable to the block scope, after
|
|
|
|
// implementing stack allocated block scoped variables.
|
2014-06-24 14:03:24 +00:00
|
|
|
Variable* temp = scope_->DeclarationScope()->NewTemporary(
|
|
|
|
ast_value_factory_->dot_for_string());
|
2012-02-08 09:56:33 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
2013-10-14 09:24:58 +00:00
|
|
|
ForEachStatement* loop =
|
|
|
|
factory()->NewForEachStatement(mode, labels, pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
|
|
|
|
2012-10-05 09:07:53 +00:00
|
|
|
// The expression does not see the loop variable.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_ = saved_scope;
|
2011-10-17 12:19:06 +00:00
|
|
|
Expression* enumerable = ParseExpression(true, CHECK_OK);
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_ = for_scope;
|
2011-10-17 12:19:06 +00:00
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2012-10-05 09:07:53 +00:00
|
|
|
VariableProxy* each =
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->NewUnresolved(factory(), name, Interface::NewValue());
|
2011-10-17 12:19:06 +00:00
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body_block =
|
|
|
|
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
2012-02-08 09:56:33 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, each, temp_proxy, RelocInfo::kNoPosition);
|
2013-10-14 09:24:58 +00:00
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
2012-06-04 14:42:58 +00:00
|
|
|
body_block->AddStatement(variable_statement, zone());
|
|
|
|
body_block->AddStatement(assignment_statement, zone());
|
|
|
|
body_block->AddStatement(body, zone());
|
2013-06-07 11:12:21 +00:00
|
|
|
InitializeForEachStatement(loop, temp_proxy, enumerable, body_block);
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_ = saved_scope;
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
2012-04-16 14:43:27 +00:00
|
|
|
body_block->set_scope(for_scope);
|
2011-10-17 12:19:06 +00:00
|
|
|
// Parsed for-in loop w/ let declaration.
|
|
|
|
return loop;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2011-10-17 12:19:06 +00:00
|
|
|
} else {
|
|
|
|
init = variable_statement;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2014-03-17 10:21:01 +00:00
|
|
|
Scanner::Location lhs_location = scanner()->peek_location();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* expression = ParseExpression(false, CHECK_OK);
|
2013-06-06 14:38:26 +00:00
|
|
|
ForEachStatement::VisitMode mode;
|
2013-06-12 12:37:44 +00:00
|
|
|
bool accept_OF = expression->AsVariableProxy();
|
2013-06-06 14:38:26 +00:00
|
|
|
|
2013-06-12 12:37:44 +00:00
|
|
|
if (CheckInOrOf(accept_OF, &mode)) {
|
2014-04-02 11:03:05 +00:00
|
|
|
expression = this->CheckAndRewriteReferenceExpression(
|
|
|
|
expression, lhs_location, "invalid_lhs_in_for", CHECK_OK);
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
ForEachStatement* loop =
|
|
|
|
factory()->NewForEachStatement(mode, labels, pos);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expression* enumerable = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
2013-06-07 11:12:21 +00:00
|
|
|
InitializeForEachStatement(loop, expression, enumerable, body);
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_ = saved_scope;
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
|
|
|
ASSERT(for_scope == NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
// Parsed for-in loop.
|
|
|
|
return loop;
|
|
|
|
|
|
|
|
} else {
|
2013-10-14 09:24:58 +00:00
|
|
|
init = factory()->NewExpressionStatement(
|
|
|
|
expression, RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Standard 'for' loop
|
2013-10-14 09:24:58 +00:00
|
|
|
ForStatement* loop = factory()->NewForStatement(labels, pos);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Parsed initializer at this point.
|
|
|
|
Expect(Token::SEMICOLON, CHECK_OK);
|
|
|
|
|
2014-05-26 08:07:02 +00:00
|
|
|
// If there are let bindings, then condition and the next statement of the
|
|
|
|
// for loop must be parsed in a new scope.
|
|
|
|
Scope* inner_scope = NULL;
|
|
|
|
if (let_bindings.length() > 0) {
|
|
|
|
inner_scope = NewScope(for_scope, BLOCK_SCOPE);
|
|
|
|
inner_scope->set_start_position(scanner()->location().beg_pos);
|
|
|
|
scope_ = inner_scope;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* cond = NULL;
|
|
|
|
if (peek() != Token::SEMICOLON) {
|
|
|
|
cond = ParseExpression(true, CHECK_OK);
|
|
|
|
}
|
|
|
|
Expect(Token::SEMICOLON, CHECK_OK);
|
|
|
|
|
|
|
|
Statement* next = NULL;
|
|
|
|
if (peek() != Token::RPAREN) {
|
|
|
|
Expression* exp = ParseExpression(true, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
next = factory()->NewExpressionStatement(exp, RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
Statement* body = ParseStatement(NULL, CHECK_OK);
|
2014-05-26 08:07:02 +00:00
|
|
|
|
|
|
|
Statement* result = NULL;
|
|
|
|
if (let_bindings.length() > 0) {
|
|
|
|
scope_ = for_scope;
|
|
|
|
result = DesugarLetBindingsInForStatement(inner_scope, &let_bindings, loop,
|
|
|
|
init, cond, next, body, CHECK_OK);
|
|
|
|
scope_ = saved_scope;
|
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
} else {
|
2014-05-26 08:07:02 +00:00
|
|
|
scope_ = saved_scope;
|
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2014-07-09 07:50:11 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
|
|
|
if (for_scope) {
|
|
|
|
// Rewrite a for statement of the form
|
|
|
|
// for (const x = i; c; n) b
|
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// const x = i;
|
|
|
|
// for (; c; n) b
|
|
|
|
// }
|
|
|
|
ASSERT(init != NULL);
|
|
|
|
Block* block =
|
|
|
|
factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
|
|
|
|
block->AddStatement(init, zone());
|
|
|
|
block->AddStatement(loop, zone());
|
|
|
|
block->set_scope(for_scope);
|
|
|
|
loop->Initialize(NULL, cond, next, body);
|
|
|
|
result = block;
|
|
|
|
} else {
|
|
|
|
loop->Initialize(init, cond, next, body);
|
|
|
|
result = loop;
|
|
|
|
}
|
2011-10-17 12:19:06 +00:00
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
|
|
|
|
// In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
|
|
|
|
// contexts this is used as a statement which invokes the debugger as i a
|
|
|
|
// break point is present.
|
|
|
|
// DebuggerStatement ::
|
|
|
|
// 'debugger' ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::DEBUGGER, CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewDebuggerStatement(pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-23 07:27:47 +00:00
|
|
|
bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
|
2014-06-03 07:40:43 +00:00
|
|
|
if (expression->IsLiteral()) return true;
|
2009-03-23 07:27:47 +00:00
|
|
|
MaterializedLiteral* lit = expression->AsMaterializedLiteral();
|
|
|
|
return lit != NULL && lit->is_simple();
|
|
|
|
}
|
|
|
|
|
2010-08-16 16:06:46 +00:00
|
|
|
|
2013-09-04 07:05:11 +00:00
|
|
|
Handle<FixedArray> CompileTimeValue::GetValue(Isolate* isolate,
|
|
|
|
Expression* expression) {
|
|
|
|
Factory* factory = isolate->factory();
|
2009-03-23 07:27:47 +00:00
|
|
|
ASSERT(IsCompileTimeValue(expression));
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<FixedArray> result = factory->NewFixedArray(2, TENURED);
|
2009-03-23 07:27:47 +00:00
|
|
|
ObjectLiteral* object_literal = expression->AsObjectLiteral();
|
|
|
|
if (object_literal != NULL) {
|
|
|
|
ASSERT(object_literal->is_simple());
|
2010-03-11 10:34:29 +00:00
|
|
|
if (object_literal->fast_elements()) {
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS));
|
2010-03-11 10:34:29 +00:00
|
|
|
} else {
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS));
|
2010-03-11 10:34:29 +00:00
|
|
|
}
|
2009-03-23 07:27:47 +00:00
|
|
|
result->set(kElementsSlot, *object_literal->constant_properties());
|
|
|
|
} else {
|
|
|
|
ArrayLiteral* array_literal = expression->AsArrayLiteral();
|
|
|
|
ASSERT(array_literal != NULL && array_literal->is_simple());
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(ARRAY_LITERAL));
|
2009-12-22 12:41:45 +00:00
|
|
|
result->set(kElementsSlot, *array_literal->constant_elements());
|
2009-03-23 07:27:47 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
CompileTimeValue::LiteralType CompileTimeValue::GetLiteralType(
|
|
|
|
Handle<FixedArray> value) {
|
|
|
|
Smi* literal_type = Smi::cast(value->get(kLiteralTypeSlot));
|
|
|
|
return static_cast<LiteralType>(literal_type->value());
|
2009-03-23 07:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
|
|
|
|
return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Implement handling of arrow functions in the parser
Arrow functions are parsed from ParseAssignmentExpression(). Handling the
parameter list is done by letting ParseConditionalExpression() parse a comma
separated list of identifiers, and it returns a tree of BinaryOperation nodes
with VariableProxy leaves, or a single VariableProxy if there is only one
parameter. When the arrow token "=>" is found, the VariableProxy nodes are
passed to ParseArrowFunctionLiteral(), which will then skip parsing the
paramaeter list. This avoids having to rewind when the arrow is found and
restart parsing the parameter list.
Note that the empty parameter list "()" is handled directly in
ParsePrimaryExpression(): after is has consumed the opening parenthesis,
if a closing parenthesis follows, then the only valid input is an arrow
function. In this case, ParsePrimaryExpression() directly calls
ParseArrowFunctionLiteral(), to avoid needing to return a sentinel value
to signal the empty parameter list. Because it will consume the body of
the arrow function, ParseAssignmentExpression() will not see the arrow
"=>" token as next, and return the already-parser expression.
The implementation is done in ParserBase, so it was needed to do some
additions to ParserBase, ParserTraits and PreParserTraits. Some of the
glue code can be removed later on when more more functionality is moved
to ParserBase.
Additionally, this adds a runtime flag "harmony_arrow_functions"
(disabled by default); enabling "harmony" will enable it as well.
BUG=v8:2700
LOG=N
R=marja@chromium.org
Review URL: https://codereview.chromium.org/385553003
Patch from Adrián Pérez de Castro <aperez@igalia.com>.
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22320 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-10 12:27:07 +00:00
|
|
|
bool CheckAndCollectArrowParameter(ParserTraits* traits,
|
|
|
|
Collector<VariableProxy*>* collector,
|
|
|
|
Expression* expression) {
|
|
|
|
// Case for empty parameter lists:
|
|
|
|
// () => ...
|
|
|
|
if (expression == NULL) return true;
|
|
|
|
|
|
|
|
// Too many parentheses around expression:
|
|
|
|
// (( ... )) => ...
|
|
|
|
if (expression->parenthesization_level() > 1) return false;
|
|
|
|
|
|
|
|
// Case for a single parameter:
|
|
|
|
// (foo) => ...
|
|
|
|
// foo => ...
|
|
|
|
if (expression->IsVariableProxy()) {
|
|
|
|
if (expression->AsVariableProxy()->is_this()) return false;
|
|
|
|
|
|
|
|
const AstRawString* raw_name = expression->AsVariableProxy()->raw_name();
|
|
|
|
if (traits->IsEvalOrArguments(raw_name) ||
|
|
|
|
traits->IsFutureStrictReserved(raw_name))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
collector->Add(expression->AsVariableProxy());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case for more than one parameter:
|
|
|
|
// (foo, bar [, ...]) => ...
|
|
|
|
if (expression->IsBinaryOperation()) {
|
|
|
|
BinaryOperation* binop = expression->AsBinaryOperation();
|
|
|
|
if (binop->op() != Token::COMMA || binop->left()->is_parenthesized() ||
|
|
|
|
binop->right()->is_parenthesized())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return CheckAndCollectArrowParameter(traits, collector, binop->left()) &&
|
|
|
|
CheckAndCollectArrowParameter(traits, collector, binop->right());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Any other kind of expression is not a valid parameter list.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Vector<VariableProxy*> ParserTraits::ParameterListFromExpression(
|
|
|
|
Expression* expression, bool* ok) {
|
|
|
|
Collector<VariableProxy*> collector;
|
|
|
|
*ok = CheckAndCollectArrowParameter(this, &collector, expression);
|
|
|
|
return collector.ToVector();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
FunctionLiteral* Parser::ParseFunctionLiteral(
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* function_name,
|
2014-02-04 11:26:19 +00:00
|
|
|
Scanner::Location function_name_location,
|
2013-06-06 13:28:22 +00:00
|
|
|
bool name_is_strict_reserved,
|
|
|
|
bool is_generator,
|
2013-10-14 09:24:58 +00:00
|
|
|
int function_token_pos,
|
2013-06-06 13:28:22 +00:00
|
|
|
FunctionLiteral::FunctionType function_type,
|
2014-06-17 07:23:26 +00:00
|
|
|
FunctionLiteral::ArityRestriction arity_restriction,
|
2013-06-06 13:28:22 +00:00
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Function ::
|
|
|
|
// '(' FormalParameterList? ')' '{' FunctionBody '}'
|
2014-06-17 07:23:26 +00:00
|
|
|
//
|
|
|
|
// Getter ::
|
|
|
|
// '(' ')' '{' FunctionBody '}'
|
|
|
|
//
|
|
|
|
// Setter ::
|
|
|
|
// '(' PropertySetParameterList ')' '{' FunctionBody '}'
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = function_token_pos == RelocInfo::kNoPosition
|
|
|
|
? peek_position() : function_token_pos;
|
|
|
|
|
2011-08-08 16:14:46 +00:00
|
|
|
// Anonymous functions were passed either the empty symbol or a null
|
|
|
|
// handle as the function name. Remember if we were passed a non-empty
|
|
|
|
// handle to decide whether to invoke function name inference.
|
2014-06-24 14:03:24 +00:00
|
|
|
bool should_infer_name = function_name == NULL;
|
2011-08-08 16:14:46 +00:00
|
|
|
|
|
|
|
// We want a non-null handle as the function name.
|
|
|
|
if (should_infer_name) {
|
2014-06-24 14:03:24 +00:00
|
|
|
function_name = ast_value_factory_->empty_string();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int num_parameters = 0;
|
2011-08-16 14:24:12 +00:00
|
|
|
// Function declarations are function scoped in normal mode, so they are
|
|
|
|
// hoisted. In harmony block scoping mode they are block scoped, so they
|
|
|
|
// are not hoisted.
|
2013-08-23 09:25:37 +00:00
|
|
|
//
|
|
|
|
// One tricky case are function declarations in a local sloppy-mode eval:
|
|
|
|
// their declaration is hoisted, but they still see the local scope. E.g.,
|
|
|
|
//
|
|
|
|
// function() {
|
|
|
|
// var x = 0
|
|
|
|
// try { throw 1 } catch (x) { eval("function g() { return x }") }
|
|
|
|
// return g()
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// needs to return 1. To distinguish such cases, we need to detect
|
|
|
|
// (1) whether a function stems from a sloppy eval, and
|
|
|
|
// (2) whether it actually hoists across the eval.
|
|
|
|
// Unfortunately, we do not represent sloppy eval scopes, so we do not have
|
|
|
|
// either information available directly, especially not when lazily compiling
|
|
|
|
// a function like 'g'. We hence rely on the following invariants:
|
|
|
|
// - (1) is the case iff the innermost scope of the deserialized scope chain
|
|
|
|
// under which we compile is _not_ a declaration scope. This holds because
|
|
|
|
// in all normal cases, function declarations are fully hoisted to a
|
|
|
|
// declaration scope and compiled relative to that.
|
|
|
|
// - (2) is the case iff the current declaration scope is still the original
|
|
|
|
// one relative to the deserialized scope chain. Otherwise we must be
|
|
|
|
// compiling a function in an inner declaration scope in the eval, e.g. a
|
|
|
|
// nested function, and hoisting works normally relative to that.
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* declaration_scope = scope_->DeclarationScope();
|
2013-08-23 09:25:37 +00:00
|
|
|
Scope* original_declaration_scope = original_scope_->DeclarationScope();
|
2013-06-06 13:28:22 +00:00
|
|
|
Scope* scope =
|
2014-03-11 14:41:22 +00:00
|
|
|
function_type == FunctionLiteral::DECLARATION &&
|
2014-03-24 14:41:55 +00:00
|
|
|
(!allow_harmony_scoping() || strict_mode() == SLOPPY) &&
|
2013-08-23 09:25:37 +00:00
|
|
|
(original_scope_ == original_declaration_scope ||
|
|
|
|
declaration_scope != original_declaration_scope)
|
|
|
|
? NewScope(declaration_scope, FUNCTION_SCOPE)
|
2014-02-12 12:02:07 +00:00
|
|
|
: NewScope(scope_, FUNCTION_SCOPE);
|
2011-11-11 13:48:14 +00:00
|
|
|
ZoneList<Statement*>* body = NULL;
|
2011-11-25 09:36:31 +00:00
|
|
|
int materialized_literal_count = -1;
|
|
|
|
int expected_property_count = -1;
|
2011-11-11 13:48:14 +00:00
|
|
|
int handler_count = 0;
|
2012-02-14 14:14:51 +00:00
|
|
|
FunctionLiteral::ParameterFlag duplicate_parameters =
|
|
|
|
FunctionLiteral::kNoDuplicateParameters;
|
2012-08-07 14:47:36 +00:00
|
|
|
FunctionLiteral::IsParenthesizedFlag parenthesized = parenthesized_function_
|
|
|
|
? FunctionLiteral::kIsParenthesized
|
|
|
|
: FunctionLiteral::kNotParenthesized;
|
2012-02-08 09:56:33 +00:00
|
|
|
AstProperties ast_properties;
|
2013-09-05 13:20:51 +00:00
|
|
|
BailoutReason dont_optimize_reason = kNoReason;
|
2008-07-03 15:10:15 +00:00
|
|
|
// Parse function body.
|
2014-06-24 14:03:24 +00:00
|
|
|
{
|
|
|
|
FunctionState function_state(&function_state_, &scope_, scope, zone(),
|
|
|
|
ast_value_factory_);
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->SetScopeName(function_name);
|
2013-04-15 12:29:44 +00:00
|
|
|
|
|
|
|
if (is_generator) {
|
|
|
|
// For generators, allocating variables in contexts is currently a win
|
|
|
|
// because it minimizes the work needed to suspend and resume an
|
|
|
|
// activation.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->ForceContextAllocation();
|
2013-04-15 12:29:44 +00:00
|
|
|
|
|
|
|
// Calling a generator returns a generator object. That object is stored
|
|
|
|
// in a temporary variable, a definition that is used by "yield"
|
2014-02-13 16:17:55 +00:00
|
|
|
// expressions. This also marks the FunctionState as a generator.
|
2014-02-12 12:02:07 +00:00
|
|
|
Variable* temp = scope_->DeclarationScope()->NewTemporary(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->dot_generator_object_string());
|
2013-04-15 12:29:44 +00:00
|
|
|
function_state.set_generator_object_variable(temp);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// FormalParameterList ::
|
|
|
|
// '(' (Identifier)*[','] ')'
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
scope->set_start_position(scanner()->location().beg_pos);
|
2014-02-07 12:44:45 +00:00
|
|
|
|
|
|
|
// We don't yet know if the function will be strict, so we cannot yet
|
|
|
|
// produce errors for parameter names or duplicates. However, we remember
|
|
|
|
// the locations of these errors if they occur and produce the errors later.
|
|
|
|
Scanner::Location eval_args_error_log = Scanner::Location::invalid();
|
|
|
|
Scanner::Location dupe_error_loc = Scanner::Location::invalid();
|
2011-05-06 11:41:15 +00:00
|
|
|
Scanner::Location reserved_loc = Scanner::Location::invalid();
|
2011-01-20 18:51:47 +00:00
|
|
|
|
2014-06-17 07:23:26 +00:00
|
|
|
bool done = arity_restriction == FunctionLiteral::GETTER_ARITY ||
|
|
|
|
(peek() == Token::RPAREN &&
|
|
|
|
arity_restriction != FunctionLiteral::SETTER_ARITY);
|
2008-07-03 15:10:15 +00:00
|
|
|
while (!done) {
|
2011-06-24 14:59:51 +00:00
|
|
|
bool is_strict_reserved = false;
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* param_name =
|
Provide private symbols through internal APIs
Adds a notion of private symbols, mainly intended for internal use, especially, self-hosting of built-in types that would otherwise require new C++ classes.
On the JS side (i.e., in built-ins), private properties can be created and accessed through a set of macros:
NEW_PRIVATE(print_name)
HAS_PRIVATE(obj, sym)
GET_PRIVATE(obj, sym)
SET_PRIVATE(obj, sym, val)
DELETE_PRIVATE(obj, sym)
In the V8 API, they are accessible via a new class Private, and respective HasPrivate/Get/Private/SetPrivate/DeletePrivate methods on calss Object.
These APIs are designed and restricted such that their implementation can later be replaced by whatever ES7+ will officially provide.
R=yangguo@chromium.org
BUG=
Review URL: https://codereview.chromium.org/48923002
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17683 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-11-13 10:34:06 +00:00
|
|
|
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
|
2011-01-25 17:21:45 +00:00
|
|
|
|
|
|
|
// Store locations for possible future error reports.
|
2014-02-07 12:44:45 +00:00
|
|
|
if (!eval_args_error_log.IsValid() && IsEvalOrArguments(param_name)) {
|
2014-02-14 12:13:33 +00:00
|
|
|
eval_args_error_log = scanner()->location();
|
2011-01-25 17:21:45 +00:00
|
|
|
}
|
2011-06-24 14:59:51 +00:00
|
|
|
if (!reserved_loc.IsValid() && is_strict_reserved) {
|
2014-02-14 12:13:33 +00:00
|
|
|
reserved_loc = scanner()->location();
|
2011-02-04 18:36:37 +00:00
|
|
|
}
|
2014-02-12 12:02:07 +00:00
|
|
|
if (!dupe_error_loc.IsValid() && scope_->IsDeclared(param_name)) {
|
2014-02-07 12:44:45 +00:00
|
|
|
duplicate_parameters = FunctionLiteral::kHasDuplicateParameters;
|
2014-02-14 12:13:33 +00:00
|
|
|
dupe_error_loc = scanner()->location();
|
2014-02-07 12:44:45 +00:00
|
|
|
}
|
2011-01-25 17:21:45 +00:00
|
|
|
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->DeclareParameter(param_name, VAR);
|
2010-11-02 11:45:47 +00:00
|
|
|
num_parameters++;
|
2013-03-18 13:35:17 +00:00
|
|
|
if (num_parameters > Code::kMaxArguments) {
|
2014-06-03 16:12:48 +00:00
|
|
|
ReportMessage("too_many_parameters");
|
2011-02-09 12:46:22 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-17 07:23:26 +00:00
|
|
|
if (arity_restriction == FunctionLiteral::SETTER_ARITY) break;
|
2008-07-03 15:10:15 +00:00
|
|
|
done = (peek() == Token::RPAREN);
|
|
|
|
if (!done) Expect(Token::COMMA, CHECK_OK);
|
|
|
|
}
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
|
|
|
|
// If we have a named function expression, we add a local variable
|
|
|
|
// declaration to the body of the function with the name of the
|
|
|
|
// function and let it refer to the function itself (closure).
|
|
|
|
// NOTE: We create a proxy and resolve it here so that in the
|
|
|
|
// future we can change the AST to only refer to VariableProxies
|
|
|
|
// instead of Variables and Proxis as is the case now.
|
2011-11-11 13:48:14 +00:00
|
|
|
Variable* fvar = NULL;
|
2014-03-11 14:41:22 +00:00
|
|
|
Token::Value fvar_init_op = Token::INIT_CONST_LEGACY;
|
2013-06-06 13:28:22 +00:00
|
|
|
if (function_type == FunctionLiteral::NAMED_EXPRESSION) {
|
2014-03-24 14:41:55 +00:00
|
|
|
if (allow_harmony_scoping() && strict_mode() == STRICT) {
|
2014-03-11 14:41:22 +00:00
|
|
|
fvar_init_op = Token::INIT_CONST;
|
|
|
|
}
|
2014-03-24 14:41:55 +00:00
|
|
|
VariableMode fvar_mode =
|
2014-07-10 14:06:37 +00:00
|
|
|
allow_harmony_scoping() && strict_mode() == STRICT
|
|
|
|
? CONST : CONST_LEGACY;
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(function_name != NULL);
|
2014-02-12 12:02:07 +00:00
|
|
|
fvar = new(zone()) Variable(scope_,
|
2012-04-16 11:48:20 +00:00
|
|
|
function_name, fvar_mode, true /* is valid LHS */,
|
2012-07-13 09:29:43 +00:00
|
|
|
Variable::NORMAL, kCreatedInitialized, Interface::NewConst());
|
2012-04-16 11:48:20 +00:00
|
|
|
VariableProxy* proxy = factory()->NewVariableProxy(fvar);
|
2013-10-14 09:24:58 +00:00
|
|
|
VariableDeclaration* fvar_declaration = factory()->NewVariableDeclaration(
|
2014-02-12 12:02:07 +00:00
|
|
|
proxy, fvar_mode, scope_, RelocInfo::kNoPosition);
|
|
|
|
scope_->DeclareFunctionVar(fvar_declaration);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2014-02-19 14:50:33 +00:00
|
|
|
// Determine if the function can be parsed lazily. Lazy parsing is different
|
|
|
|
// from lazy compilation; we need to parse more eagerly than we compile.
|
|
|
|
|
|
|
|
// We can only parse lazily if we also compile lazily. The heuristics for
|
|
|
|
// lazy compilation are:
|
2011-11-25 09:36:31 +00:00
|
|
|
// - It must not have been prohibited by the caller to Parse (some callers
|
|
|
|
// need a full AST).
|
2012-06-28 14:56:28 +00:00
|
|
|
// - The outer scope must allow lazy compilation of inner functions.
|
2011-11-25 09:36:31 +00:00
|
|
|
// - The function mustn't be a function expression with an open parenthesis
|
|
|
|
// before; we consider that a hint that the function will be called
|
|
|
|
// immediately, and it would be a waste of time to make it lazily
|
|
|
|
// compiled.
|
|
|
|
// These are all things we can know at this point, without looking at the
|
|
|
|
// function itself.
|
2014-02-19 14:50:33 +00:00
|
|
|
|
|
|
|
// In addition, we need to distinguish between these cases:
|
|
|
|
// (function foo() {
|
|
|
|
// bar = function() { return 1; }
|
|
|
|
// })();
|
|
|
|
// and
|
|
|
|
// (function foo() {
|
|
|
|
// var a = 1;
|
|
|
|
// bar = function() { return a; }
|
|
|
|
// })();
|
|
|
|
|
|
|
|
// Now foo will be parsed eagerly and compiled eagerly (optimization: assume
|
|
|
|
// parenthesis before the function means that it will be called
|
|
|
|
// immediately). The inner function *must* be parsed eagerly to resolve the
|
|
|
|
// possible reference to the variable in foo's scope. However, it's possible
|
|
|
|
// that it will be compiled lazily.
|
|
|
|
|
|
|
|
// To make this additional case work, both Parser and PreParser implement a
|
|
|
|
// logic where only top-level functions will be parsed lazily.
|
|
|
|
bool is_lazily_parsed = (mode() == PARSE_LAZILY &&
|
|
|
|
scope_->AllowsLazyCompilation() &&
|
|
|
|
!parenthesized_function_);
|
2011-01-14 10:50:13 +00:00
|
|
|
parenthesized_function_ = false; // The bit was set for this function only.
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-02-19 14:50:33 +00:00
|
|
|
if (is_lazily_parsed) {
|
2014-04-15 08:29:24 +00:00
|
|
|
SkipLazyFunctionBody(function_name, &materialized_literal_count,
|
|
|
|
&expected_property_count, CHECK_OK);
|
|
|
|
} else {
|
|
|
|
body = ParseEagerFunctionBody(function_name, pos, fvar, fvar_init_op,
|
|
|
|
is_generator, CHECK_OK);
|
2011-11-09 13:54:26 +00:00
|
|
|
materialized_literal_count = function_state.materialized_literal_count();
|
|
|
|
expected_property_count = function_state.expected_property_count();
|
2011-11-11 13:48:14 +00:00
|
|
|
handler_count = function_state.handler_count();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2014-06-20 09:45:05 +00:00
|
|
|
// Validate strict mode.
|
2014-03-11 14:41:22 +00:00
|
|
|
if (strict_mode() == STRICT) {
|
2014-06-20 09:45:05 +00:00
|
|
|
CheckStrictFunctionNameAndParameters(function_name,
|
|
|
|
name_is_strict_reserved,
|
|
|
|
function_name_location,
|
|
|
|
eval_args_error_log,
|
|
|
|
dupe_error_loc,
|
|
|
|
reserved_loc,
|
|
|
|
CHECK_OK);
|
2011-10-21 10:26:59 +00:00
|
|
|
CheckOctalLiteral(scope->start_position(),
|
|
|
|
scope->end_position(),
|
|
|
|
CHECK_OK);
|
2011-01-20 18:51:47 +00:00
|
|
|
}
|
2012-02-08 09:56:33 +00:00
|
|
|
ast_properties = *factory()->visitor()->ast_properties();
|
2013-09-05 13:20:51 +00:00
|
|
|
dont_optimize_reason = factory()->visitor()->dont_optimize_reason();
|
2011-04-07 14:45:34 +00:00
|
|
|
|
2014-07-09 11:35:05 +00:00
|
|
|
if (allow_harmony_scoping() && strict_mode() == STRICT) {
|
|
|
|
CheckConflictingVarDeclarations(scope, CHECK_OK);
|
|
|
|
}
|
2011-09-01 12:31:18 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 08:29:24 +00:00
|
|
|
FunctionLiteral::IsGeneratorFlag generator = is_generator
|
|
|
|
? FunctionLiteral::kIsGenerator
|
|
|
|
: FunctionLiteral::kNotGenerator;
|
2011-04-07 14:45:34 +00:00
|
|
|
FunctionLiteral* function_literal =
|
2012-02-08 09:56:33 +00:00
|
|
|
factory()->NewFunctionLiteral(function_name,
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_,
|
2012-02-08 09:56:33 +00:00
|
|
|
scope,
|
|
|
|
body,
|
|
|
|
materialized_literal_count,
|
|
|
|
expected_property_count,
|
|
|
|
handler_count,
|
|
|
|
num_parameters,
|
2012-02-14 14:14:51 +00:00
|
|
|
duplicate_parameters,
|
2013-06-06 13:28:22 +00:00
|
|
|
function_type,
|
2012-08-07 14:47:36 +00:00
|
|
|
FunctionLiteral::kIsFunction,
|
2013-04-02 17:34:59 +00:00
|
|
|
parenthesized,
|
2013-10-14 09:24:58 +00:00
|
|
|
generator,
|
|
|
|
pos);
|
|
|
|
function_literal->set_function_token_position(function_token_pos);
|
2012-02-08 09:56:33 +00:00
|
|
|
function_literal->set_ast_properties(&ast_properties);
|
2013-09-05 13:20:51 +00:00
|
|
|
function_literal->set_dont_optimize_reason(dont_optimize_reason);
|
2011-04-07 14:45:34 +00:00
|
|
|
|
2011-08-08 16:14:46 +00:00
|
|
|
if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal);
|
2011-04-07 14:45:34 +00:00
|
|
|
return function_literal;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
|
2014-04-15 08:29:24 +00:00
|
|
|
int* materialized_literal_count,
|
|
|
|
int* expected_property_count,
|
|
|
|
bool* ok) {
|
|
|
|
int function_block_pos = position();
|
2014-07-10 10:28:05 +00:00
|
|
|
if (cached_data_mode() == CONSUME_CACHED_DATA) {
|
2014-04-15 08:29:24 +00:00
|
|
|
// If we have cached data, we use it to skip parsing the function body. The
|
|
|
|
// data contains the information we need to construct the lazy function.
|
|
|
|
FunctionEntry entry =
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_->GetFunctionEntry(function_block_pos);
|
|
|
|
// Check that cached data is valid.
|
|
|
|
CHECK(entry.is_valid());
|
|
|
|
// End position greater than end of stream is safe, and hard to check.
|
|
|
|
CHECK(entry.end_pos() > function_block_pos);
|
|
|
|
scanner()->SeekForward(entry.end_pos() - 1);
|
|
|
|
|
|
|
|
scope_->set_end_position(entry.end_pos());
|
|
|
|
Expect(Token::RBRACE, ok);
|
|
|
|
if (!*ok) {
|
2014-04-15 08:29:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-07-10 10:28:05 +00:00
|
|
|
isolate()->counters()->total_preparse_skipped()->Increment(
|
|
|
|
scope_->end_position() - function_block_pos);
|
|
|
|
*materialized_literal_count = entry.literal_count();
|
|
|
|
*expected_property_count = entry.property_count();
|
|
|
|
scope_->SetStrictMode(entry.strict_mode());
|
2014-04-15 08:29:24 +00:00
|
|
|
} else {
|
|
|
|
// With no cached data, we partially parse the function, without building an
|
|
|
|
// AST. This gathers the data needed to build a lazy function.
|
|
|
|
SingletonLogger logger;
|
|
|
|
PreParser::PreParseResult result =
|
|
|
|
ParseLazyFunctionBodyWithPreParser(&logger);
|
|
|
|
if (result == PreParser::kPreParseStackOverflow) {
|
|
|
|
// Propagate stack overflow.
|
|
|
|
set_stack_overflow();
|
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (logger.has_error()) {
|
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
Scanner::Location(logger.start(), logger.end()),
|
2014-05-15 09:44:57 +00:00
|
|
|
logger.message(), logger.argument_opt(), logger.is_reference_error());
|
2014-04-15 08:29:24 +00:00
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
scope_->set_end_position(logger.end());
|
|
|
|
Expect(Token::RBRACE, ok);
|
|
|
|
if (!*ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
isolate()->counters()->total_preparse_skipped()->Increment(
|
|
|
|
scope_->end_position() - function_block_pos);
|
|
|
|
*materialized_literal_count = logger.literals();
|
|
|
|
*expected_property_count = logger.properties();
|
|
|
|
scope_->SetStrictMode(logger.strict_mode());
|
2014-07-10 10:28:05 +00:00
|
|
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
2014-04-15 08:29:24 +00:00
|
|
|
ASSERT(log_);
|
|
|
|
// Position right after terminal '}'.
|
|
|
|
int body_end = scanner()->location().end_pos;
|
|
|
|
log_->LogFunction(function_block_pos, body_end,
|
|
|
|
*materialized_literal_count,
|
|
|
|
*expected_property_count,
|
|
|
|
scope_->strict_mode());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* function_name, int pos, Variable* fvar,
|
2014-04-15 08:29:24 +00:00
|
|
|
Token::Value fvar_init_op, bool is_generator, bool* ok) {
|
|
|
|
// Everything inside an eagerly parsed function will be parsed eagerly
|
|
|
|
// (see comment above).
|
|
|
|
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
|
|
|
|
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8, zone());
|
|
|
|
if (fvar != NULL) {
|
|
|
|
VariableProxy* fproxy = scope_->NewUnresolved(
|
|
|
|
factory(), function_name, Interface::NewConst());
|
|
|
|
fproxy->BindTo(fvar);
|
|
|
|
body->Add(factory()->NewExpressionStatement(
|
|
|
|
factory()->NewAssignment(fvar_init_op,
|
|
|
|
fproxy,
|
|
|
|
factory()->NewThisFunction(pos),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition), zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// For generators, allocate and yield an iterator on function entry.
|
|
|
|
if (is_generator) {
|
|
|
|
ZoneList<Expression*>* arguments =
|
|
|
|
new(zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
CallRuntime* allocation = factory()->NewCallRuntime(
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_->empty_string(),
|
2014-06-25 15:26:10 +00:00
|
|
|
Runtime::FunctionForId(Runtime::kCreateJSGeneratorObject),
|
2014-04-15 08:29:24 +00:00
|
|
|
arguments, pos);
|
|
|
|
VariableProxy* init_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT_VAR, init_proxy, allocation, RelocInfo::kNoPosition);
|
|
|
|
VariableProxy* get_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
|
|
|
Yield* yield = factory()->NewYield(
|
|
|
|
get_proxy, assignment, Yield::INITIAL, RelocInfo::kNoPosition);
|
|
|
|
body->Add(factory()->NewExpressionStatement(
|
|
|
|
yield, RelocInfo::kNoPosition), zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
ParseSourceElements(body, Token::RBRACE, false, false, CHECK_OK);
|
|
|
|
|
|
|
|
if (is_generator) {
|
|
|
|
VariableProxy* get_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* undefined =
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition);
|
|
|
|
Yield* yield = factory()->NewYield(get_proxy, undefined, Yield::FINAL,
|
|
|
|
RelocInfo::kNoPosition);
|
2014-04-15 08:29:24 +00:00
|
|
|
body->Add(factory()->NewExpressionStatement(
|
|
|
|
yield, RelocInfo::kNoPosition), zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
scope_->set_end_position(scanner()->location().end_pos);
|
|
|
|
|
|
|
|
return body;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
|
2011-11-25 09:36:31 +00:00
|
|
|
SingletonLogger* logger) {
|
|
|
|
HistogramTimerScope preparse_scope(isolate()->counters()->pre_parse());
|
2014-02-14 12:13:33 +00:00
|
|
|
ASSERT_EQ(Token::LBRACE, scanner()->current_token());
|
2011-11-25 09:36:31 +00:00
|
|
|
|
|
|
|
if (reusable_preparser_ == NULL) {
|
|
|
|
intptr_t stack_limit = isolate()->stack_guard()->real_climit();
|
2013-10-14 13:07:20 +00:00
|
|
|
reusable_preparser_ = new PreParser(&scanner_, NULL, stack_limit);
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
reusable_preparser_->set_allow_harmony_scoping(allow_harmony_scoping());
|
|
|
|
reusable_preparser_->set_allow_modules(allow_modules());
|
|
|
|
reusable_preparser_->set_allow_natives_syntax(allow_natives_syntax());
|
|
|
|
reusable_preparser_->set_allow_lazy(true);
|
|
|
|
reusable_preparser_->set_allow_generators(allow_generators());
|
2013-06-06 14:38:26 +00:00
|
|
|
reusable_preparser_->set_allow_for_of(allow_for_of());
|
Implement handling of arrow functions in the parser
Arrow functions are parsed from ParseAssignmentExpression(). Handling the
parameter list is done by letting ParseConditionalExpression() parse a comma
separated list of identifiers, and it returns a tree of BinaryOperation nodes
with VariableProxy leaves, or a single VariableProxy if there is only one
parameter. When the arrow token "=>" is found, the VariableProxy nodes are
passed to ParseArrowFunctionLiteral(), which will then skip parsing the
paramaeter list. This avoids having to rewind when the arrow is found and
restart parsing the parameter list.
Note that the empty parameter list "()" is handled directly in
ParsePrimaryExpression(): after is has consumed the opening parenthesis,
if a closing parenthesis follows, then the only valid input is an arrow
function. In this case, ParsePrimaryExpression() directly calls
ParseArrowFunctionLiteral(), to avoid needing to return a sentinel value
to signal the empty parameter list. Because it will consume the body of
the arrow function, ParseAssignmentExpression() will not see the arrow
"=>" token as next, and return the already-parser expression.
The implementation is done in ParserBase, so it was needed to do some
additions to ParserBase, ParserTraits and PreParserTraits. Some of the
glue code can be removed later on when more more functionality is moved
to ParserBase.
Additionally, this adds a runtime flag "harmony_arrow_functions"
(disabled by default); enabling "harmony" will enable it as well.
BUG=v8:2700
LOG=N
R=marja@chromium.org
Review URL: https://codereview.chromium.org/385553003
Patch from Adrián Pérez de Castro <aperez@igalia.com>.
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22320 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-10 12:27:07 +00:00
|
|
|
reusable_preparser_->set_allow_arrow_functions(allow_arrow_functions());
|
2013-07-19 09:57:35 +00:00
|
|
|
reusable_preparser_->set_allow_harmony_numeric_literals(
|
|
|
|
allow_harmony_numeric_literals());
|
2011-11-25 09:36:31 +00:00
|
|
|
}
|
2013-10-14 13:07:20 +00:00
|
|
|
PreParser::PreParseResult result =
|
2014-03-11 14:41:22 +00:00
|
|
|
reusable_preparser_->PreParseLazyFunction(strict_mode(),
|
2013-04-02 17:34:59 +00:00
|
|
|
is_generator(),
|
2011-11-25 09:36:31 +00:00
|
|
|
logger);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* Parser::ParseV8Intrinsic(bool* ok) {
|
|
|
|
// CallRuntime ::
|
|
|
|
// '%' Identifier Arguments
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::MOD, CHECK_OK);
|
2014-02-05 16:26:48 +00:00
|
|
|
// Allow "eval" or "arguments" for backward compatibility.
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
|
2010-09-14 14:52:53 +00:00
|
|
|
|
|
|
|
if (extension_ != NULL) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// The extension structures are only accessible while parsing the
|
|
|
|
// very first time not when reparsing because of lazy compilation.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->DeclarationScope()->ForceEagerCompilation();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const Runtime::Function* function = Runtime::FunctionForName(name->string());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2010-09-14 14:52:53 +00:00
|
|
|
// Check for built-in IS_VAR macro.
|
|
|
|
if (function != NULL &&
|
|
|
|
function->intrinsic_type == Runtime::RUNTIME &&
|
|
|
|
function->function_id == Runtime::kIS_VAR) {
|
|
|
|
// %IS_VAR(x) evaluates to x if x is a variable,
|
|
|
|
// leads to a parse error otherwise. Could be implemented as an
|
|
|
|
// inline function %_IS_VAR(x) to eliminate this special case.
|
|
|
|
if (args->length() == 1 && args->at(0)->AsVariableProxy() != NULL) {
|
|
|
|
return args->at(0);
|
|
|
|
} else {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("not_isvar");
|
2010-09-14 14:52:53 +00:00
|
|
|
*ok = false;
|
2008-07-03 15:10:15 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-14 14:52:53 +00:00
|
|
|
// Check that the expected number of arguments are being passed.
|
|
|
|
if (function != NULL &&
|
|
|
|
function->nargs != -1 &&
|
|
|
|
function->nargs != args->length()) {
|
2014-05-15 09:44:57 +00:00
|
|
|
ReportMessage("illegal_access");
|
2010-09-14 14:52:53 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2012-08-14 10:06:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the function is defined if it's an inline runtime call.
|
2014-06-24 14:03:24 +00:00
|
|
|
if (function == NULL && name->FirstCharacter() == '_') {
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessage("not_defined", name);
|
2012-08-14 10:06:34 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2010-03-11 09:27:12 +00:00
|
|
|
}
|
|
|
|
|
2010-09-14 14:52:53 +00:00
|
|
|
// We have a valid intrinsics call or a call to a builtin.
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewCallRuntime(name, function, args, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
Literal* Parser::GetLiteralUndefined(int position) {
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory()->NewUndefinedLiteral(position);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-01 12:31:18 +00:00
|
|
|
void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
|
|
|
|
Declaration* decl = scope->CheckConflictingVarDeclarations();
|
|
|
|
if (decl != NULL) {
|
|
|
|
// In harmony mode we treat conflicting variable bindinds as early
|
|
|
|
// errors. See ES5 16 for a definition of early errors.
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = decl->proxy()->raw_name();
|
2011-09-01 12:31:18 +00:00
|
|
|
int position = decl->proxy()->position();
|
|
|
|
Scanner::Location location = position == RelocInfo::kNoPosition
|
|
|
|
? Scanner::Location::invalid()
|
|
|
|
: Scanner::Location(position, position + 1);
|
2014-05-15 09:44:57 +00:00
|
|
|
ParserTraits::ReportMessageAt(location, "var_redeclaration", name);
|
2011-09-01 12:31:18 +00:00
|
|
|
*ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Parser support
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
bool Parser::TargetStackContainsLabel(const AstRawString* label) {
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* t = target_stack_; t != NULL; t = t->previous()) {
|
|
|
|
BreakableStatement* stat = t->node()->AsBreakableStatement();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (stat != NULL && ContainsLabel(stat->labels(), label))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label,
|
|
|
|
bool* ok) {
|
|
|
|
bool anonymous = label == NULL;
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* t = target_stack_; t != NULL; t = t->previous()) {
|
|
|
|
BreakableStatement* stat = t->node()->AsBreakableStatement();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (stat == NULL) continue;
|
|
|
|
if ((anonymous && stat->is_target_for_anonymous()) ||
|
|
|
|
(!anonymous && ContainsLabel(stat->labels(), label))) {
|
2009-05-15 14:58:02 +00:00
|
|
|
RegisterTargetUse(stat->break_target(), t->previous());
|
2008-07-03 15:10:15 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
IterationStatement* Parser::LookupContinueTarget(const AstRawString* label,
|
2008-07-03 15:10:15 +00:00
|
|
|
bool* ok) {
|
2014-06-24 14:03:24 +00:00
|
|
|
bool anonymous = label == NULL;
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* t = target_stack_; t != NULL; t = t->previous()) {
|
|
|
|
IterationStatement* stat = t->node()->AsIterationStatement();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (stat == NULL) continue;
|
|
|
|
|
|
|
|
ASSERT(stat->is_target_for_anonymous());
|
|
|
|
if (anonymous || ContainsLabel(stat->labels(), label)) {
|
2009-05-15 14:58:02 +00:00
|
|
|
RegisterTargetUse(stat->continue_target(), t->previous());
|
2008-07-03 15:10:15 +00:00
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-04-07 14:42:37 +00:00
|
|
|
void Parser::RegisterTargetUse(Label* target, Target* stop) {
|
2009-05-15 14:58:02 +00:00
|
|
|
// Register that a break target found at the given stop in the
|
2009-03-10 12:11:56 +00:00
|
|
|
// target stack has been used from the top of the target stack. Add
|
|
|
|
// the break target to any TargetCollectors passed on the stack.
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* t = target_stack_; t != stop; t = t->previous()) {
|
|
|
|
TargetCollector* collector = t->node()->AsTargetCollector();
|
2012-06-11 12:42:31 +00:00
|
|
|
if (collector != NULL) collector->AddTarget(target, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-02 07:01:31 +00:00
|
|
|
void Parser::HandleSourceURLComments() {
|
|
|
|
if (scanner_.source_url()->length() > 0) {
|
2014-07-02 12:01:56 +00:00
|
|
|
Handle<String> source_url = scanner_.source_url()->Internalize(isolate());
|
|
|
|
info_->script()->set_source_url(*source_url);
|
2014-07-02 07:01:31 +00:00
|
|
|
}
|
|
|
|
if (scanner_.source_mapping_url()->length() > 0) {
|
2014-07-02 12:01:56 +00:00
|
|
|
Handle<String> source_mapping_url =
|
|
|
|
scanner_.source_mapping_url()->Internalize(isolate());
|
|
|
|
info_->script()->set_source_mapping_url(*source_mapping_url);
|
2014-07-02 07:01:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-20 12:22:04 +00:00
|
|
|
void Parser::ThrowPendingError() {
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(ast_value_factory_->IsInternalized());
|
2014-05-20 12:22:04 +00:00
|
|
|
if (has_pending_error_) {
|
|
|
|
MessageLocation location(script_,
|
|
|
|
pending_error_location_.beg_pos,
|
|
|
|
pending_error_location_.end_pos);
|
|
|
|
Factory* factory = isolate()->factory();
|
|
|
|
bool has_arg =
|
2014-06-24 14:03:24 +00:00
|
|
|
pending_error_arg_ != NULL || pending_error_char_arg_ != NULL;
|
2014-05-20 12:22:04 +00:00
|
|
|
Handle<FixedArray> elements = factory->NewFixedArray(has_arg ? 1 : 0);
|
2014-06-24 14:03:24 +00:00
|
|
|
if (pending_error_arg_ != NULL) {
|
|
|
|
Handle<String> arg_string = pending_error_arg_->string();
|
|
|
|
elements->set(0, *arg_string);
|
2014-05-20 12:22:04 +00:00
|
|
|
} else if (pending_error_char_arg_ != NULL) {
|
|
|
|
Handle<String> arg_string =
|
|
|
|
factory->NewStringFromUtf8(CStrVector(pending_error_char_arg_))
|
|
|
|
.ToHandleChecked();
|
|
|
|
elements->set(0, *arg_string);
|
|
|
|
}
|
2014-06-26 16:03:52 +00:00
|
|
|
isolate()->debug()->OnCompileError(script_);
|
|
|
|
|
2014-05-20 12:22:04 +00:00
|
|
|
Handle<JSArray> array = factory->NewJSArrayWithElements(elements);
|
|
|
|
Handle<Object> result = pending_error_is_reference_error_
|
|
|
|
? factory->NewReferenceError(pending_error_message_, array)
|
|
|
|
: factory->NewSyntaxError(pending_error_message_, array);
|
|
|
|
isolate()->Throw(*result, &location);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-30 13:35:16 +00:00
|
|
|
void Parser::InternalizeUseCounts() {
|
|
|
|
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
|
|
|
++feature) {
|
|
|
|
for (int i = 0; i < use_counts_[feature]; ++i) {
|
|
|
|
isolate()->CountUsage(v8::Isolate::UseCounterFeature(feature));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-25 11:07:48 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Regular expressions
|
|
|
|
|
|
|
|
|
|
|
|
RegExpParser::RegExpParser(FlatStringReader* in,
|
|
|
|
Handle<String>* error,
|
2012-06-20 08:58:41 +00:00
|
|
|
bool multiline,
|
|
|
|
Zone* zone)
|
2013-09-04 07:05:11 +00:00
|
|
|
: isolate_(zone->isolate()),
|
2012-06-20 08:58:41 +00:00
|
|
|
zone_(zone),
|
2011-03-18 20:35:07 +00:00
|
|
|
error_(error),
|
|
|
|
captures_(NULL),
|
|
|
|
in_(in),
|
|
|
|
current_(kEndMarker),
|
|
|
|
next_pos_(0),
|
|
|
|
capture_count_(0),
|
|
|
|
has_more_(true),
|
|
|
|
multiline_(multiline),
|
|
|
|
simple_(false),
|
|
|
|
contains_anchor_(false),
|
|
|
|
is_scanned_for_captures_(false),
|
|
|
|
failed_(false) {
|
2010-12-20 10:44:41 +00:00
|
|
|
Advance();
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uc32 RegExpParser::Next() {
|
|
|
|
if (has_next()) {
|
|
|
|
return in()->Get(next_pos_);
|
|
|
|
} else {
|
|
|
|
return kEndMarker;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpParser::Advance() {
|
|
|
|
if (next_pos_ < in()->length()) {
|
2011-03-18 20:35:07 +00:00
|
|
|
StackLimitCheck check(isolate());
|
2008-12-01 15:32:20 +00:00
|
|
|
if (check.HasOverflowed()) {
|
2011-03-18 20:35:07 +00:00
|
|
|
ReportError(CStrVector(Isolate::kStackOverflowMessage));
|
2012-06-20 08:58:41 +00:00
|
|
|
} else if (zone()->excess_allocation()) {
|
2008-12-01 15:32:20 +00:00
|
|
|
ReportError(CStrVector("Regular expression too large"));
|
|
|
|
} else {
|
|
|
|
current_ = in()->Get(next_pos_);
|
|
|
|
next_pos_++;
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
} else {
|
|
|
|
current_ = kEndMarker;
|
|
|
|
has_more_ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpParser::Reset(int pos) {
|
|
|
|
next_pos_ = pos;
|
2013-05-27 10:53:37 +00:00
|
|
|
has_more_ = (pos < in()->length());
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void RegExpParser::Advance(int dist) {
|
2010-12-20 10:44:41 +00:00
|
|
|
next_pos_ += dist - 1;
|
|
|
|
Advance();
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-12 10:22:56 +00:00
|
|
|
bool RegExpParser::simple() {
|
|
|
|
return simple_;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2008-12-01 15:32:20 +00:00
|
|
|
RegExpTree* RegExpParser::ReportError(Vector<const char> message) {
|
|
|
|
failed_ = true;
|
2014-04-23 15:43:39 +00:00
|
|
|
*error_ = isolate()->factory()->NewStringFromAscii(message).ToHandleChecked();
|
2008-12-01 15:32:20 +00:00
|
|
|
// Zip to the end to make sure the no more input is read.
|
|
|
|
current_ = kEndMarker;
|
|
|
|
next_pos_ = in()->length();
|
2008-11-25 11:07:48 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Pattern ::
|
|
|
|
// Disjunction
|
2008-12-01 15:32:20 +00:00
|
|
|
RegExpTree* RegExpParser::ParsePattern() {
|
|
|
|
RegExpTree* result = ParseDisjunction(CHECK_FAILED);
|
2009-07-03 11:09:34 +00:00
|
|
|
ASSERT(!has_more());
|
2009-03-04 09:52:01 +00:00
|
|
|
// If the result of parsing is a literal string atom, and it has the
|
|
|
|
// same length as the input, then the atom is identical to the input.
|
|
|
|
if (result->IsAtom() && result->AsAtom()->length() == in()->length()) {
|
|
|
|
simple_ = true;
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Disjunction ::
|
|
|
|
// Alternative
|
|
|
|
// Alternative | Disjunction
|
|
|
|
// Alternative ::
|
|
|
|
// [empty]
|
|
|
|
// Term Alternative
|
|
|
|
// Term ::
|
|
|
|
// Assertion
|
|
|
|
// Atom
|
|
|
|
// Atom Quantifier
|
2008-12-01 15:32:20 +00:00
|
|
|
RegExpTree* RegExpParser::ParseDisjunction() {
|
2009-07-03 08:18:35 +00:00
|
|
|
// Used to store current state while parsing subexpressions.
|
2012-06-04 14:42:58 +00:00
|
|
|
RegExpParserState initial_state(NULL, INITIAL, 0, zone());
|
2009-07-03 08:18:35 +00:00
|
|
|
RegExpParserState* stored_state = &initial_state;
|
|
|
|
// Cache the builder in a local variable for quick access.
|
|
|
|
RegExpBuilder* builder = initial_state.builder();
|
2008-11-25 11:07:48 +00:00
|
|
|
while (true) {
|
|
|
|
switch (current()) {
|
|
|
|
case kEndMarker:
|
2009-07-03 08:18:35 +00:00
|
|
|
if (stored_state->IsSubexpression()) {
|
|
|
|
// Inside a parenthesized group when hitting end of input.
|
|
|
|
ReportError(CStrVector("Unterminated group") CHECK_FAILED);
|
|
|
|
}
|
|
|
|
ASSERT_EQ(INITIAL, stored_state->group_type());
|
|
|
|
// Parsing completed successfully.
|
|
|
|
return builder->ToRegExp();
|
|
|
|
case ')': {
|
|
|
|
if (!stored_state->IsSubexpression()) {
|
2009-07-03 11:09:34 +00:00
|
|
|
ReportError(CStrVector("Unmatched ')'") CHECK_FAILED);
|
2009-07-03 08:18:35 +00:00
|
|
|
}
|
|
|
|
ASSERT_NE(INITIAL, stored_state->group_type());
|
|
|
|
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
2009-07-03 08:18:35 +00:00
|
|
|
// End disjunction parsing and convert builder content to new single
|
|
|
|
// regexp atom.
|
|
|
|
RegExpTree* body = builder->ToRegExp();
|
|
|
|
|
|
|
|
int end_capture_index = captures_started();
|
|
|
|
|
|
|
|
int capture_index = stored_state->capture_index();
|
2013-06-06 13:28:22 +00:00
|
|
|
SubexpressionType group_type = stored_state->group_type();
|
2009-07-03 08:18:35 +00:00
|
|
|
|
|
|
|
// Restore previous state.
|
|
|
|
stored_state = stored_state->previous_state();
|
|
|
|
builder = stored_state->builder();
|
|
|
|
|
|
|
|
// Build result of subexpression.
|
2013-06-06 13:28:22 +00:00
|
|
|
if (group_type == CAPTURE) {
|
2011-04-04 06:29:02 +00:00
|
|
|
RegExpCapture* capture = new(zone()) RegExpCapture(body, capture_index);
|
2009-07-03 08:18:35 +00:00
|
|
|
captures_->at(capture_index - 1) = capture;
|
|
|
|
body = capture;
|
2013-06-06 13:28:22 +00:00
|
|
|
} else if (group_type != GROUPING) {
|
|
|
|
ASSERT(group_type == POSITIVE_LOOKAHEAD ||
|
|
|
|
group_type == NEGATIVE_LOOKAHEAD);
|
|
|
|
bool is_positive = (group_type == POSITIVE_LOOKAHEAD);
|
2011-04-04 06:29:02 +00:00
|
|
|
body = new(zone()) RegExpLookahead(body,
|
2009-07-03 08:18:35 +00:00
|
|
|
is_positive,
|
|
|
|
end_capture_index - capture_index,
|
|
|
|
capture_index);
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAtom(body);
|
2011-02-16 08:10:47 +00:00
|
|
|
// For compatability with JSC and ES3, we allow quantifiers after
|
|
|
|
// lookaheads, and break in all cases.
|
2009-07-03 08:18:35 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '|': {
|
|
|
|
Advance();
|
|
|
|
builder->NewAlternative();
|
2008-11-25 11:07:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case '*':
|
|
|
|
case '+':
|
|
|
|
case '?':
|
2009-07-22 12:33:16 +00:00
|
|
|
return ReportError(CStrVector("Nothing to repeat"));
|
2008-11-25 11:07:48 +00:00
|
|
|
case '^': {
|
|
|
|
Advance();
|
2009-02-03 11:43:55 +00:00
|
|
|
if (multiline_) {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAssertion(
|
2011-04-04 06:29:02 +00:00
|
|
|
new(zone()) RegExpAssertion(RegExpAssertion::START_OF_LINE));
|
2009-02-03 11:43:55 +00:00
|
|
|
} else {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAssertion(
|
2011-04-04 06:29:02 +00:00
|
|
|
new(zone()) RegExpAssertion(RegExpAssertion::START_OF_INPUT));
|
2009-02-03 11:43:55 +00:00
|
|
|
set_contains_anchor();
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case '$': {
|
|
|
|
Advance();
|
2013-06-06 13:28:22 +00:00
|
|
|
RegExpAssertion::AssertionType assertion_type =
|
2008-11-27 10:35:06 +00:00
|
|
|
multiline_ ? RegExpAssertion::END_OF_LINE :
|
|
|
|
RegExpAssertion::END_OF_INPUT;
|
2013-06-06 13:28:22 +00:00
|
|
|
builder->AddAssertion(new(zone()) RegExpAssertion(assertion_type));
|
2008-11-25 11:07:48 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
case '.': {
|
|
|
|
Advance();
|
|
|
|
// everything except \x0a, \x0d, \u2028 and \u2029
|
2011-05-23 13:00:11 +00:00
|
|
|
ZoneList<CharacterRange>* ranges =
|
2012-06-11 12:42:31 +00:00
|
|
|
new(zone()) ZoneList<CharacterRange>(2, zone());
|
|
|
|
CharacterRange::AddClassEscape('.', ranges, zone());
|
2011-04-04 06:29:02 +00:00
|
|
|
RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAtom(atom);
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '(': {
|
2013-06-06 13:28:22 +00:00
|
|
|
SubexpressionType subexpr_type = CAPTURE;
|
2009-07-03 08:18:35 +00:00
|
|
|
Advance();
|
|
|
|
if (current() == '?') {
|
|
|
|
switch (Next()) {
|
|
|
|
case ':':
|
2013-06-06 13:28:22 +00:00
|
|
|
subexpr_type = GROUPING;
|
2009-07-03 08:18:35 +00:00
|
|
|
break;
|
|
|
|
case '=':
|
2013-06-06 13:28:22 +00:00
|
|
|
subexpr_type = POSITIVE_LOOKAHEAD;
|
2009-07-03 08:18:35 +00:00
|
|
|
break;
|
|
|
|
case '!':
|
2013-06-06 13:28:22 +00:00
|
|
|
subexpr_type = NEGATIVE_LOOKAHEAD;
|
2009-07-03 08:18:35 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ReportError(CStrVector("Invalid group") CHECK_FAILED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Advance(2);
|
|
|
|
} else {
|
|
|
|
if (captures_ == NULL) {
|
2012-06-11 12:42:31 +00:00
|
|
|
captures_ = new(zone()) ZoneList<RegExpCapture*>(2, zone());
|
2009-07-03 08:18:35 +00:00
|
|
|
}
|
|
|
|
if (captures_started() >= kMaxCaptures) {
|
|
|
|
ReportError(CStrVector("Too many captures") CHECK_FAILED);
|
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
captures_->Add(NULL, zone());
|
2009-07-03 08:18:35 +00:00
|
|
|
}
|
|
|
|
// Store current state and begin new disjunction parsing.
|
2013-06-06 13:28:22 +00:00
|
|
|
stored_state = new(zone()) RegExpParserState(stored_state, subexpr_type,
|
2012-06-04 14:42:58 +00:00
|
|
|
captures_started(), zone());
|
2009-07-03 08:18:35 +00:00
|
|
|
builder = stored_state->builder();
|
2011-02-16 08:10:47 +00:00
|
|
|
continue;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
case '[': {
|
2008-12-01 15:32:20 +00:00
|
|
|
RegExpTree* atom = ParseCharacterClass(CHECK_FAILED);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAtom(atom);
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Atom ::
|
|
|
|
// \ AtomEscape
|
|
|
|
case '\\':
|
|
|
|
switch (Next()) {
|
|
|
|
case kEndMarker:
|
2009-07-22 12:33:16 +00:00
|
|
|
return ReportError(CStrVector("\\ at end of pattern"));
|
2008-11-25 11:07:48 +00:00
|
|
|
case 'b':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAssertion(
|
2011-04-04 06:29:02 +00:00
|
|
|
new(zone()) RegExpAssertion(RegExpAssertion::BOUNDARY));
|
2008-11-25 11:07:48 +00:00
|
|
|
continue;
|
|
|
|
case 'B':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAssertion(
|
2011-04-04 06:29:02 +00:00
|
|
|
new(zone()) RegExpAssertion(RegExpAssertion::NON_BOUNDARY));
|
2008-11-25 11:07:48 +00:00
|
|
|
continue;
|
2011-02-16 08:10:47 +00:00
|
|
|
// AtomEscape ::
|
|
|
|
// CharacterClassEscape
|
|
|
|
//
|
|
|
|
// CharacterClassEscape :: one of
|
|
|
|
// d D s S w W
|
2008-11-25 11:07:48 +00:00
|
|
|
case 'd': case 'D': case 's': case 'S': case 'w': case 'W': {
|
|
|
|
uc32 c = Next();
|
|
|
|
Advance(2);
|
2011-05-23 13:00:11 +00:00
|
|
|
ZoneList<CharacterRange>* ranges =
|
2012-06-11 12:42:31 +00:00
|
|
|
new(zone()) ZoneList<CharacterRange>(2, zone());
|
|
|
|
CharacterRange::AddClassEscape(c, ranges, zone());
|
2011-04-04 06:29:02 +00:00
|
|
|
RegExpTree* atom = new(zone()) RegExpCharacterClass(ranges, false);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAtom(atom);
|
2009-07-03 11:09:34 +00:00
|
|
|
break;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
case '1': case '2': case '3': case '4': case '5': case '6':
|
|
|
|
case '7': case '8': case '9': {
|
|
|
|
int index = 0;
|
|
|
|
if (ParseBackReferenceIndex(&index)) {
|
2009-07-03 08:18:35 +00:00
|
|
|
RegExpCapture* capture = NULL;
|
|
|
|
if (captures_ != NULL && index <= captures_->length()) {
|
|
|
|
capture = captures_->at(index - 1);
|
|
|
|
}
|
|
|
|
if (capture == NULL) {
|
|
|
|
builder->AddEmpty();
|
2009-07-03 11:09:34 +00:00
|
|
|
break;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2011-04-04 06:29:02 +00:00
|
|
|
RegExpTree* atom = new(zone()) RegExpBackReference(capture);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddAtom(atom);
|
2009-07-03 11:09:34 +00:00
|
|
|
break;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
uc32 first_digit = Next();
|
|
|
|
if (first_digit == '8' || first_digit == '9') {
|
|
|
|
// Treat as identity escape
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(first_digit);
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance(2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// FALLTHROUGH
|
|
|
|
case '0': {
|
|
|
|
Advance();
|
|
|
|
uc32 octal = ParseOctalLiteral();
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(octal);
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// ControlEscape :: one of
|
|
|
|
// f n r t v
|
|
|
|
case 'f':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('\f');
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('\n');
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('\r');
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('\t');
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
Advance(2);
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('\v');
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
case 'c': {
|
2011-01-07 12:35:42 +00:00
|
|
|
Advance();
|
|
|
|
uc32 controlLetter = Next();
|
|
|
|
// Special case if it is an ASCII letter.
|
|
|
|
// Convert lower case letters to uppercase.
|
|
|
|
uc32 letter = controlLetter & ~('a' ^ 'A');
|
|
|
|
if (letter < 'A' || 'Z' < letter) {
|
|
|
|
// controlLetter is not in range 'A'-'Z' or 'a'-'z'.
|
|
|
|
// This is outside the specification. We match JSC in
|
|
|
|
// reading the backslash as a literal character instead
|
|
|
|
// of as starting an escape.
|
|
|
|
builder->AddCharacter('\\');
|
|
|
|
} else {
|
|
|
|
Advance(2);
|
|
|
|
builder->AddCharacter(controlLetter & 0x1f);
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'x': {
|
|
|
|
Advance(2);
|
|
|
|
uc32 value;
|
|
|
|
if (ParseHexEscape(2, &value)) {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(value);
|
2008-11-25 11:07:48 +00:00
|
|
|
} else {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('x');
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'u': {
|
|
|
|
Advance(2);
|
|
|
|
uc32 value;
|
|
|
|
if (ParseHexEscape(4, &value)) {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(value);
|
2008-11-25 11:07:48 +00:00
|
|
|
} else {
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter('u');
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// Identity escape.
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(Next());
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance(2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '{': {
|
|
|
|
int dummy;
|
|
|
|
if (ParseIntervalQuantifier(&dummy, &dummy)) {
|
2008-12-01 15:32:20 +00:00
|
|
|
ReportError(CStrVector("Nothing to repeat") CHECK_FAILED);
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
// fallthrough
|
|
|
|
}
|
|
|
|
default:
|
2009-07-03 08:18:35 +00:00
|
|
|
builder->AddCharacter(current());
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
break;
|
|
|
|
} // end switch(current())
|
|
|
|
|
|
|
|
int min;
|
|
|
|
int max;
|
|
|
|
switch (current()) {
|
|
|
|
// QuantifierPrefix ::
|
|
|
|
// *
|
|
|
|
// +
|
|
|
|
// ?
|
|
|
|
// {
|
|
|
|
case '*':
|
|
|
|
min = 0;
|
2008-12-17 10:59:14 +00:00
|
|
|
max = RegExpTree::kInfinity;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
break;
|
|
|
|
case '+':
|
|
|
|
min = 1;
|
2008-12-17 10:59:14 +00:00
|
|
|
max = RegExpTree::kInfinity;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
break;
|
|
|
|
case '?':
|
|
|
|
min = 0;
|
|
|
|
max = 1;
|
|
|
|
Advance();
|
|
|
|
break;
|
|
|
|
case '{':
|
|
|
|
if (ParseIntervalQuantifier(&min, &max)) {
|
2009-01-29 14:14:13 +00:00
|
|
|
if (max < min) {
|
|
|
|
ReportError(CStrVector("numbers out of order in {} quantifier.")
|
|
|
|
CHECK_FAILED);
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
2013-06-06 13:28:22 +00:00
|
|
|
RegExpQuantifier::QuantifierType quantifier_type = RegExpQuantifier::GREEDY;
|
2008-11-25 11:07:48 +00:00
|
|
|
if (current() == '?') {
|
2013-06-06 13:28:22 +00:00
|
|
|
quantifier_type = RegExpQuantifier::NON_GREEDY;
|
2010-01-07 19:01:23 +00:00
|
|
|
Advance();
|
|
|
|
} else if (FLAG_regexp_possessive_quantifier && current() == '+') {
|
|
|
|
// FLAG_regexp_possessive_quantifier is a debug-only flag.
|
2013-06-06 13:28:22 +00:00
|
|
|
quantifier_type = RegExpQuantifier::POSSESSIVE;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
}
|
2013-06-06 13:28:22 +00:00
|
|
|
builder->AddQuantifierToAtom(min, max, quantifier_type);
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Currently only used in an ASSERT.
|
|
|
|
static bool IsSpecialClassEscape(uc32 c) {
|
|
|
|
switch (c) {
|
|
|
|
case 'd': case 'D':
|
|
|
|
case 's': case 'S':
|
|
|
|
case 'w': case 'W':
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// In order to know whether an escape is a backreference or not we have to scan
|
|
|
|
// the entire regexp and find the number of capturing parentheses. However we
|
|
|
|
// don't want to scan the regexp twice unless it is necessary. This mini-parser
|
|
|
|
// is called when needed. It can see the difference between capturing and
|
|
|
|
// noncapturing parentheses and can skip character classes and backslash-escaped
|
|
|
|
// characters.
|
|
|
|
void RegExpParser::ScanForCaptures() {
|
2008-11-25 14:14:04 +00:00
|
|
|
// Start with captures started previous to current position
|
|
|
|
int capture_count = captures_started();
|
|
|
|
// Add count of captures after this position.
|
2008-11-25 11:07:48 +00:00
|
|
|
int n;
|
|
|
|
while ((n = current()) != kEndMarker) {
|
|
|
|
Advance();
|
|
|
|
switch (n) {
|
|
|
|
case '\\':
|
|
|
|
Advance();
|
|
|
|
break;
|
|
|
|
case '[': {
|
|
|
|
int c;
|
|
|
|
while ((c = current()) != kEndMarker) {
|
|
|
|
Advance();
|
|
|
|
if (c == '\\') {
|
|
|
|
Advance();
|
|
|
|
} else {
|
|
|
|
if (c == ']') break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '(':
|
2008-11-25 14:14:04 +00:00
|
|
|
if (current() != '?') capture_count++;
|
2008-11-25 11:07:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-11-25 14:14:04 +00:00
|
|
|
capture_count_ = capture_count;
|
2008-11-25 11:07:48 +00:00
|
|
|
is_scanned_for_captures_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RegExpParser::ParseBackReferenceIndex(int* index_out) {
|
|
|
|
ASSERT_EQ('\\', current());
|
|
|
|
ASSERT('1' <= Next() && Next() <= '9');
|
2009-01-30 10:38:25 +00:00
|
|
|
// Try to parse a decimal literal that is no greater than the total number
|
|
|
|
// of left capturing parentheses in the input.
|
2008-11-25 11:07:48 +00:00
|
|
|
int start = position();
|
|
|
|
int value = Next() - '0';
|
|
|
|
Advance(2);
|
|
|
|
while (true) {
|
|
|
|
uc32 c = current();
|
|
|
|
if (IsDecimalDigit(c)) {
|
|
|
|
value = 10 * value + (c - '0');
|
2009-01-30 10:38:25 +00:00
|
|
|
if (value > kMaxCaptures) {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-11-25 14:14:04 +00:00
|
|
|
if (value > captures_started()) {
|
|
|
|
if (!is_scanned_for_captures_) {
|
|
|
|
int saved_position = position();
|
|
|
|
ScanForCaptures();
|
|
|
|
Reset(saved_position);
|
|
|
|
}
|
|
|
|
if (value > capture_count_) {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
*index_out = value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// QuantifierPrefix ::
|
|
|
|
// { DecimalDigits }
|
|
|
|
// { DecimalDigits , }
|
|
|
|
// { DecimalDigits , DecimalDigits }
|
2009-02-05 12:55:20 +00:00
|
|
|
//
|
|
|
|
// Returns true if parsing succeeds, and set the min_out and max_out
|
|
|
|
// values. Values are truncated to RegExpTree::kInfinity if they overflow.
|
2008-11-25 11:07:48 +00:00
|
|
|
bool RegExpParser::ParseIntervalQuantifier(int* min_out, int* max_out) {
|
|
|
|
ASSERT_EQ(current(), '{');
|
|
|
|
int start = position();
|
|
|
|
Advance();
|
|
|
|
int min = 0;
|
|
|
|
if (!IsDecimalDigit(current())) {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while (IsDecimalDigit(current())) {
|
2009-02-05 12:55:20 +00:00
|
|
|
int next = current() - '0';
|
|
|
|
if (min > (RegExpTree::kInfinity - next) / 10) {
|
|
|
|
// Overflow. Skip past remaining decimal digits and return -1.
|
2009-02-05 13:24:13 +00:00
|
|
|
do {
|
|
|
|
Advance();
|
|
|
|
} while (IsDecimalDigit(current()));
|
2009-02-05 12:55:20 +00:00
|
|
|
min = RegExpTree::kInfinity;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
min = 10 * min + next;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
}
|
|
|
|
int max = 0;
|
|
|
|
if (current() == '}') {
|
|
|
|
max = min;
|
|
|
|
Advance();
|
|
|
|
} else if (current() == ',') {
|
|
|
|
Advance();
|
|
|
|
if (current() == '}') {
|
2008-12-17 10:59:14 +00:00
|
|
|
max = RegExpTree::kInfinity;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
} else {
|
|
|
|
while (IsDecimalDigit(current())) {
|
2009-02-05 12:55:20 +00:00
|
|
|
int next = current() - '0';
|
|
|
|
if (max > (RegExpTree::kInfinity - next) / 10) {
|
2009-02-05 13:24:13 +00:00
|
|
|
do {
|
|
|
|
Advance();
|
|
|
|
} while (IsDecimalDigit(current()));
|
|
|
|
max = RegExpTree::kInfinity;
|
2009-02-05 12:55:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
max = 10 * max + next;
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance();
|
|
|
|
}
|
|
|
|
if (current() != '}') {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Advance();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*min_out = min;
|
|
|
|
*max_out = max;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uc32 RegExpParser::ParseOctalLiteral() {
|
2014-03-10 15:52:10 +00:00
|
|
|
ASSERT(('0' <= current() && current() <= '7') || current() == kEndMarker);
|
2008-11-25 11:07:48 +00:00
|
|
|
// For compatibility with some other browsers (not all), we parse
|
|
|
|
// up to three octal digits with a value below 256.
|
|
|
|
uc32 value = current() - '0';
|
|
|
|
Advance();
|
|
|
|
if ('0' <= current() && current() <= '7') {
|
|
|
|
value = value * 8 + current() - '0';
|
|
|
|
Advance();
|
|
|
|
if (value < 32 && '0' <= current() && current() <= '7') {
|
|
|
|
value = value * 8 + current() - '0';
|
|
|
|
Advance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RegExpParser::ParseHexEscape(int length, uc32 *value) {
|
|
|
|
int start = position();
|
|
|
|
uc32 val = 0;
|
|
|
|
bool done = false;
|
|
|
|
for (int i = 0; !done; i++) {
|
|
|
|
uc32 c = current();
|
|
|
|
int d = HexValue(c);
|
|
|
|
if (d < 0) {
|
|
|
|
Reset(start);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
val = val * 16 + d;
|
|
|
|
Advance();
|
|
|
|
if (i == length - 1) {
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*value = val;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-01 15:32:20 +00:00
|
|
|
uc32 RegExpParser::ParseClassCharacterEscape() {
|
2008-11-25 11:07:48 +00:00
|
|
|
ASSERT(current() == '\\');
|
|
|
|
ASSERT(has_next() && !IsSpecialClassEscape(Next()));
|
|
|
|
Advance();
|
|
|
|
switch (current()) {
|
|
|
|
case 'b':
|
|
|
|
Advance();
|
|
|
|
return '\b';
|
|
|
|
// ControlEscape :: one of
|
|
|
|
// f n r t v
|
|
|
|
case 'f':
|
|
|
|
Advance();
|
|
|
|
return '\f';
|
|
|
|
case 'n':
|
|
|
|
Advance();
|
|
|
|
return '\n';
|
|
|
|
case 'r':
|
|
|
|
Advance();
|
|
|
|
return '\r';
|
|
|
|
case 't':
|
|
|
|
Advance();
|
|
|
|
return '\t';
|
|
|
|
case 'v':
|
|
|
|
Advance();
|
|
|
|
return '\v';
|
2011-01-07 12:35:42 +00:00
|
|
|
case 'c': {
|
|
|
|
uc32 controlLetter = Next();
|
|
|
|
uc32 letter = controlLetter & ~('A' ^ 'a');
|
|
|
|
// For compatibility with JSC, inside a character class
|
|
|
|
// we also accept digits and underscore as control characters.
|
|
|
|
if ((controlLetter >= '0' && controlLetter <= '9') ||
|
|
|
|
controlLetter == '_' ||
|
|
|
|
(letter >= 'A' && letter <= 'Z')) {
|
|
|
|
Advance(2);
|
|
|
|
// Control letters mapped to ASCII control characters in the range
|
|
|
|
// 0x00-0x1f.
|
|
|
|
return controlLetter & 0x1f;
|
|
|
|
}
|
|
|
|
// We match JSC in reading the backslash as a literal
|
|
|
|
// character instead of as starting an escape.
|
|
|
|
return '\\';
|
|
|
|
}
|
2008-11-25 11:07:48 +00:00
|
|
|
case '0': case '1': case '2': case '3': case '4': case '5':
|
|
|
|
case '6': case '7':
|
|
|
|
// For compatibility, we interpret a decimal escape that isn't
|
|
|
|
// a back reference (and therefore either \0 or not valid according
|
|
|
|
// to the specification) as a 1..3 digit octal character code.
|
|
|
|
return ParseOctalLiteral();
|
|
|
|
case 'x': {
|
|
|
|
Advance();
|
|
|
|
uc32 value;
|
|
|
|
if (ParseHexEscape(2, &value)) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
// If \x is not followed by a two-digit hexadecimal, treat it
|
|
|
|
// as an identity escape.
|
|
|
|
return 'x';
|
|
|
|
}
|
|
|
|
case 'u': {
|
|
|
|
Advance();
|
|
|
|
uc32 value;
|
|
|
|
if (ParseHexEscape(4, &value)) {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
// If \u is not followed by a four-digit hexadecimal, treat it
|
|
|
|
// as an identity escape.
|
|
|
|
return 'u';
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
// Extended identity escape. We accept any character that hasn't
|
|
|
|
// been matched by a more specific case, not just the subset required
|
|
|
|
// by the ECMAScript specification.
|
|
|
|
uc32 result = current();
|
|
|
|
Advance();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-01 15:32:20 +00:00
|
|
|
CharacterRange RegExpParser::ParseClassAtom(uc16* char_class) {
|
2008-12-01 14:29:28 +00:00
|
|
|
ASSERT_EQ(0, *char_class);
|
2008-11-25 11:07:48 +00:00
|
|
|
uc32 first = current();
|
|
|
|
if (first == '\\') {
|
|
|
|
switch (Next()) {
|
|
|
|
case 'w': case 'W': case 'd': case 'D': case 's': case 'S': {
|
2008-12-01 14:29:28 +00:00
|
|
|
*char_class = Next();
|
2008-11-25 11:07:48 +00:00
|
|
|
Advance(2);
|
2008-12-01 14:29:28 +00:00
|
|
|
return CharacterRange::Singleton(0); // Return dummy value.
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2008-12-18 14:30:53 +00:00
|
|
|
case kEndMarker:
|
2009-07-22 12:33:16 +00:00
|
|
|
return ReportError(CStrVector("\\ at end of pattern"));
|
2008-11-25 11:07:48 +00:00
|
|
|
default:
|
2008-12-01 15:32:20 +00:00
|
|
|
uc32 c = ParseClassCharacterEscape(CHECK_FAILED);
|
2008-11-25 11:07:48 +00:00
|
|
|
return CharacterRange::Singleton(c);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Advance();
|
|
|
|
return CharacterRange::Singleton(first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-13 08:33:32 +00:00
|
|
|
static const uc16 kNoCharClass = 0;
|
|
|
|
|
|
|
|
// Adds range or pre-defined character class to character ranges.
|
|
|
|
// If char_class is not kInvalidClass, it's interpreted as a class
|
|
|
|
// escape (i.e., 's' means whitespace, from '\s').
|
|
|
|
static inline void AddRangeOrEscape(ZoneList<CharacterRange>* ranges,
|
|
|
|
uc16 char_class,
|
2012-06-11 12:42:31 +00:00
|
|
|
CharacterRange range,
|
|
|
|
Zone* zone) {
|
2010-12-13 08:33:32 +00:00
|
|
|
if (char_class != kNoCharClass) {
|
2012-06-11 12:42:31 +00:00
|
|
|
CharacterRange::AddClassEscape(char_class, ranges, zone);
|
2010-12-13 08:33:32 +00:00
|
|
|
} else {
|
2012-06-11 12:42:31 +00:00
|
|
|
ranges->Add(range, zone);
|
2010-12-13 08:33:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-01 15:32:20 +00:00
|
|
|
RegExpTree* RegExpParser::ParseCharacterClass() {
|
2008-11-25 11:07:48 +00:00
|
|
|
static const char* kUnterminated = "Unterminated character class";
|
|
|
|
static const char* kRangeOutOfOrder = "Range out of order in character class";
|
|
|
|
|
|
|
|
ASSERT_EQ(current(), '[');
|
|
|
|
Advance();
|
|
|
|
bool is_negated = false;
|
|
|
|
if (current() == '^') {
|
|
|
|
is_negated = true;
|
|
|
|
Advance();
|
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<CharacterRange>* ranges =
|
|
|
|
new(zone()) ZoneList<CharacterRange>(2, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
while (has_more() && current() != ']') {
|
2010-12-13 08:33:32 +00:00
|
|
|
uc16 char_class = kNoCharClass;
|
2008-12-01 15:32:20 +00:00
|
|
|
CharacterRange first = ParseClassAtom(&char_class CHECK_FAILED);
|
2008-12-01 14:29:28 +00:00
|
|
|
if (current() == '-') {
|
|
|
|
Advance();
|
|
|
|
if (current() == kEndMarker) {
|
|
|
|
// If we reach the end we break out of the loop and let the
|
|
|
|
// following code report an error.
|
|
|
|
break;
|
|
|
|
} else if (current() == ']') {
|
2012-06-11 12:42:31 +00:00
|
|
|
AddRangeOrEscape(ranges, char_class, first, zone());
|
|
|
|
ranges->Add(CharacterRange::Singleton('-'), zone());
|
2008-12-01 14:29:28 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-12-13 08:33:32 +00:00
|
|
|
uc16 char_class_2 = kNoCharClass;
|
|
|
|
CharacterRange next = ParseClassAtom(&char_class_2 CHECK_FAILED);
|
|
|
|
if (char_class != kNoCharClass || char_class_2 != kNoCharClass) {
|
|
|
|
// Either end is an escaped character class. Treat the '-' verbatim.
|
2012-06-11 12:42:31 +00:00
|
|
|
AddRangeOrEscape(ranges, char_class, first, zone());
|
|
|
|
ranges->Add(CharacterRange::Singleton('-'), zone());
|
|
|
|
AddRangeOrEscape(ranges, char_class_2, next, zone());
|
2010-12-09 12:07:52 +00:00
|
|
|
continue;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2008-12-01 14:29:28 +00:00
|
|
|
if (first.from() > next.to()) {
|
2008-12-01 15:32:20 +00:00
|
|
|
return ReportError(CStrVector(kRangeOutOfOrder) CHECK_FAILED);
|
2008-12-01 14:29:28 +00:00
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
ranges->Add(CharacterRange::Range(first.from(), next.to()), zone());
|
2008-12-01 14:29:28 +00:00
|
|
|
} else {
|
2012-06-11 12:42:31 +00:00
|
|
|
AddRangeOrEscape(ranges, char_class, first, zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!has_more()) {
|
2008-12-01 15:32:20 +00:00
|
|
|
return ReportError(CStrVector(kUnterminated) CHECK_FAILED);
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
Advance();
|
|
|
|
if (ranges->length() == 0) {
|
2012-06-11 12:42:31 +00:00
|
|
|
ranges->Add(CharacterRange::Everything(), zone());
|
2008-11-25 11:07:48 +00:00
|
|
|
is_negated = !is_negated;
|
|
|
|
}
|
2011-04-04 06:29:02 +00:00
|
|
|
return new(zone()) RegExpCharacterClass(ranges, is_negated);
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// The Parser interface.
|
|
|
|
|
2010-10-27 12:33:48 +00:00
|
|
|
bool RegExpParser::ParseRegExp(FlatStringReader* input,
|
|
|
|
bool multiline,
|
2012-06-20 08:58:41 +00:00
|
|
|
RegExpCompileData* result,
|
|
|
|
Zone* zone) {
|
2008-11-25 11:07:48 +00:00
|
|
|
ASSERT(result != NULL);
|
2012-06-20 08:58:41 +00:00
|
|
|
RegExpParser parser(input, &result->error, multiline, zone);
|
2008-12-12 10:22:56 +00:00
|
|
|
RegExpTree* tree = parser.ParsePattern();
|
2008-12-01 15:32:20 +00:00
|
|
|
if (parser.failed()) {
|
2008-12-12 10:22:56 +00:00
|
|
|
ASSERT(tree == NULL);
|
2008-11-25 11:07:48 +00:00
|
|
|
ASSERT(!result->error.is_null());
|
|
|
|
} else {
|
2008-12-12 10:22:56 +00:00
|
|
|
ASSERT(tree != NULL);
|
2008-11-25 11:07:48 +00:00
|
|
|
ASSERT(result->error.is_null());
|
2008-12-12 10:22:56 +00:00
|
|
|
result->tree = tree;
|
|
|
|
int capture_count = parser.captures_started();
|
|
|
|
result->simple = tree->IsAtom() && parser.simple() && capture_count == 0;
|
2009-02-03 11:43:55 +00:00
|
|
|
result->contains_anchor = parser.contains_anchor();
|
2008-12-12 10:22:56 +00:00
|
|
|
result->capture_count = capture_count;
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
2008-12-01 15:32:20 +00:00
|
|
|
return !parser.failed();
|
2008-11-25 11:07:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
bool Parser::Parse() {
|
|
|
|
ASSERT(info()->function() == NULL);
|
2010-10-04 11:35:46 +00:00
|
|
|
FunctionLiteral* result = NULL;
|
2014-06-24 14:03:24 +00:00
|
|
|
ast_value_factory_ = info()->ast_value_factory();
|
|
|
|
if (ast_value_factory_ == NULL) {
|
|
|
|
ast_value_factory_ =
|
|
|
|
new AstValueFactory(zone(), isolate()->heap()->HashSeed());
|
|
|
|
}
|
|
|
|
if (allow_natives_syntax() || extension_ != NULL) {
|
|
|
|
// If intrinsics are allowed, the Parser cannot operate independent of the
|
|
|
|
// V8 heap because of Rumtime. Tell the string table to internalize strings
|
|
|
|
// and values right after they're created.
|
|
|
|
ast_value_factory_->Internalize(isolate());
|
|
|
|
}
|
|
|
|
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
if (info()->is_lazy()) {
|
|
|
|
ASSERT(!info()->is_eval());
|
|
|
|
if (info()->shared_info()->is_function()) {
|
|
|
|
result = ParseLazy();
|
2012-02-14 14:14:51 +00:00
|
|
|
} else {
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
result = ParseProgram();
|
2012-02-14 14:14:51 +00:00
|
|
|
}
|
2010-02-01 10:31:55 +00:00
|
|
|
} else {
|
2014-07-10 10:28:05 +00:00
|
|
|
SetCachedData();
|
2014-07-07 09:31:58 +00:00
|
|
|
result = ParseProgram();
|
2010-02-01 10:31:55 +00:00
|
|
|
}
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
info()->SetFunction(result);
|
2014-06-24 14:03:24 +00:00
|
|
|
ASSERT(ast_value_factory_->IsInternalized());
|
|
|
|
// info takes ownership of ast_value_factory_.
|
|
|
|
if (info()->ast_value_factory() == NULL) {
|
|
|
|
info()->SetAstValueFactory(ast_value_factory_);
|
|
|
|
}
|
|
|
|
ast_value_factory_ = NULL;
|
2014-06-30 13:35:16 +00:00
|
|
|
|
|
|
|
InternalizeUseCounts();
|
|
|
|
|
2010-10-04 11:35:46 +00:00
|
|
|
return (result != NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} } // namespace v8::internal
|