[bigint] Support parsing of BigInt literals

Reuses the existing logic for BigInt.parseInt, adapted slightly
to allow octal and binary radix prefixes (and to support parsing
of a raw character buffer, rather than a v8::internal::String).

Bug: v8:6791
Change-Id: I41904b2204721eac452e0765fa9ff0ab26ee343b
Reviewed-on: https://chromium-review.googlesource.com/711334
Commit-Queue: Adam Klein <adamk@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48560}
This commit is contained in:
Adam Klein 2017-10-13 11:23:53 -07:00 committed by Commit Bot
parent c076667b7f
commit 3872ed6543
17 changed files with 225 additions and 30 deletions

View File

@ -213,6 +213,17 @@ bool AstValue::BooleanValue() const {
return DoubleToBoolean(number_); return DoubleToBoolean(number_);
case SMI: case SMI:
return smi_ != 0; return smi_ != 0;
case BIGINT: {
size_t length = strlen(bigint_buffer_);
DCHECK_GT(length, 0);
if (length == 1 && bigint_buffer_[0] == '0') return false;
// Skip over any radix prefix; BigInts with length > 1 only
// begin with zero if they include a radix.
for (size_t i = (bigint_buffer_[0] == '0') ? 2 : 0; i < length; ++i) {
if (bigint_buffer_[i] != '0') return true;
}
return false;
}
case BOOLEAN: case BOOLEAN:
return bool_; return bool_;
case NULL_TYPE: case NULL_TYPE:
@ -247,6 +258,11 @@ void AstValue::Internalize(Isolate* isolate) {
case SMI: case SMI:
set_value(handle(Smi::FromInt(smi_), isolate)); set_value(handle(Smi::FromInt(smi_), isolate));
break; break;
case BIGINT:
// TODO(adamk): Don't check-fail on conversion failure; instead
// check for errors during parsing and throw at that point.
set_value(StringToBigInt(isolate, bigint_buffer_).ToHandleChecked());
break;
case BOOLEAN: case BOOLEAN:
if (bool_) { if (bool_) {
set_value(isolate->factory()->true_value()); set_value(isolate->factory()->true_value());
@ -396,6 +412,11 @@ const AstValue* AstValueFactory::NewSmi(uint32_t number) {
return AddValue(value); return AddValue(value);
} }
const AstValue* AstValueFactory::NewBigInt(const char* number) {
AstValue* value = new (zone_) AstValue(number);
return AddValue(value);
}
#define GENERATE_VALUE_GETTER(value, initializer) \ #define GENERATE_VALUE_GETTER(value, initializer) \
if (!value) { \ if (!value) { \
value = AddValue(new (zone_) AstValue(initializer)); \ value = AddValue(new (zone_) AstValue(initializer)); \

View File

@ -217,10 +217,11 @@ class AstValue : public ZoneObject {
bool IsPropertyName() const; bool IsPropertyName() const;
bool BooleanValue() const; V8_EXPORT_PRIVATE bool BooleanValue() const;
bool IsSmi() const { return type_ == SMI; } bool IsSmi() const { return type_ == SMI; }
bool IsHeapNumber() const { return type_ == NUMBER; } bool IsHeapNumber() const { return type_ == NUMBER; }
bool IsBigInt() const { return type_ == BIGINT; }
bool IsFalse() const { return type_ == BOOLEAN && !bool_; } bool IsFalse() const { return type_ == BOOLEAN && !bool_; }
bool IsTrue() const { return type_ == BOOLEAN && bool_; } bool IsTrue() const { return type_ == BOOLEAN && bool_; }
bool IsUndefined() const { return type_ == UNDEFINED; } bool IsUndefined() const { return type_ == UNDEFINED; }
@ -249,6 +250,7 @@ class AstValue : public ZoneObject {
SYMBOL, SYMBOL,
NUMBER, NUMBER,
SMI, SMI,
BIGINT,
BOOLEAN, BOOLEAN,
NULL_TYPE, NULL_TYPE,
UNDEFINED, UNDEFINED,
@ -270,6 +272,10 @@ class AstValue : public ZoneObject {
smi_ = i; smi_ = i;
} }
explicit AstValue(const char* n) : type_(BIGINT), next_(nullptr) {
bigint_buffer_ = n;
}
explicit AstValue(bool b) : type_(BOOLEAN), next_(nullptr) { bool_ = b; } explicit AstValue(bool b) : type_(BOOLEAN), next_(nullptr) { bool_ = b; }
explicit AstValue(Type t) : type_(t), next_(nullptr) { explicit AstValue(Type t) : type_(t), next_(nullptr) {
@ -292,6 +298,7 @@ class AstValue : public ZoneObject {
int smi_; int smi_;
bool bool_; bool bool_;
AstSymbol symbol_; AstSymbol symbol_;
const char* bigint_buffer_;
}; };
}; };
@ -430,6 +437,7 @@ class AstValueFactory {
const AstValue* NewSymbol(AstSymbol symbol); const AstValue* NewSymbol(AstSymbol symbol);
V8_EXPORT_PRIVATE const AstValue* NewNumber(double number); V8_EXPORT_PRIVATE const AstValue* NewNumber(double number);
const AstValue* NewSmi(uint32_t number); const AstValue* NewSmi(uint32_t number);
V8_EXPORT_PRIVATE const AstValue* NewBigInt(const char* number);
const AstValue* NewBoolean(bool b); const AstValue* NewBoolean(bool b);
const AstValue* NewStringList(ZoneList<const AstRawString*>* strings); const AstValue* NewStringList(ZoneList<const AstRawString*>* strings);
const AstValue* NewNull(); const AstValue* NewNull();

View File

@ -3134,6 +3134,10 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) Literal(ast_value_factory_->NewSmi(number), pos); return new (zone_) Literal(ast_value_factory_->NewSmi(number), pos);
} }
Literal* NewBigIntLiteral(const char* buffer, int pos) {
return new (zone_) Literal(ast_value_factory_->NewBigInt(buffer), pos);
}
Literal* NewBooleanLiteral(bool b, int pos) { Literal* NewBooleanLiteral(bool b, int pos) {
return new (zone_) Literal(ast_value_factory_->NewBoolean(b), pos); return new (zone_) Literal(ast_value_factory_->NewBoolean(b), pos);
} }

View File

@ -184,6 +184,11 @@ class StringToIntHelper {
: isolate_(isolate), subject_(subject), radix_(radix) { : isolate_(isolate), subject_(subject), radix_(radix) {
DCHECK(subject->IsFlat()); DCHECK(subject->IsFlat());
} }
// Used for parsing BigInt literals, where the input is a Zone-allocated
// buffer of one-byte digits, along with an optional radix prefix.
StringToIntHelper(Isolate* isolate, const uint8_t* subject, int length)
: isolate_(isolate), raw_one_byte_subject_(subject), length_(length) {}
virtual ~StringToIntHelper() {} virtual ~StringToIntHelper() {}
protected: protected:
@ -197,11 +202,26 @@ class StringToIntHelper {
// Subclasses may override this. // Subclasses may override this.
virtual void HandleSpecialCases() {} virtual void HandleSpecialCases() {}
bool IsOneByte() const {
return raw_one_byte_subject_ != nullptr ||
subject_->IsOneByteRepresentationUnderneath();
}
Vector<const uint8_t> GetOneByteVector() {
if (raw_one_byte_subject_ != nullptr) {
return Vector<const uint8_t>(raw_one_byte_subject_, length_);
}
return subject_->GetFlatContent().ToOneByteVector();
}
Vector<const uc16> GetTwoByteVector() {
return subject_->GetFlatContent().ToUC16Vector();
}
// Subclasses get access to internal state: // Subclasses get access to internal state:
enum State { kRunning, kError, kJunk, kZero, kDone }; enum State { kRunning, kError, kJunk, kZero, kDone };
Isolate* isolate() { return isolate_; } Isolate* isolate() { return isolate_; }
Handle<String> subject() { return subject_; }
int radix() { return radix_; } int radix() { return radix_; }
int cursor() { return cursor_; } int cursor() { return cursor_; }
int length() { return length_; } int length() { return length_; }
@ -209,6 +229,14 @@ class StringToIntHelper {
State state() { return state_; } State state() { return state_; }
void set_state(State state) { state_ = state; } void set_state(State state) { state_ = state; }
bool AllowOctalRadixPrefix() const {
return raw_one_byte_subject_ != nullptr;
}
bool AllowBinaryRadixPrefix() const {
return raw_one_byte_subject_ != nullptr;
}
private: private:
template <class Char> template <class Char>
void DetectRadixInternal(Char current, int length); void DetectRadixInternal(Char current, int length);
@ -217,7 +245,8 @@ class StringToIntHelper {
Isolate* isolate_; Isolate* isolate_;
Handle<String> subject_; Handle<String> subject_;
int radix_; const uint8_t* raw_one_byte_subject_ = nullptr;
int radix_ = 0;
int cursor_ = 0; int cursor_ = 0;
int length_ = 0; int length_ = 0;
bool negative_ = false; bool negative_ = false;
@ -228,12 +257,11 @@ class StringToIntHelper {
void StringToIntHelper::ParseInt() { void StringToIntHelper::ParseInt() {
{ {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String::FlatContent flat = subject_->GetFlatContent(); if (IsOneByte()) {
if (flat.IsOneByte()) { Vector<const uint8_t> vector = GetOneByteVector();
Vector<const uint8_t> vector = flat.ToOneByteVector();
DetectRadixInternal(vector.start(), vector.length()); DetectRadixInternal(vector.start(), vector.length());
} else { } else {
Vector<const uc16> vector = flat.ToUC16Vector(); Vector<const uc16> vector = GetTwoByteVector();
DetectRadixInternal(vector.start(), vector.length()); DetectRadixInternal(vector.start(), vector.length());
} }
} }
@ -243,13 +271,12 @@ void StringToIntHelper::ParseInt() {
if (state_ != kRunning) return; if (state_ != kRunning) return;
{ {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String::FlatContent flat = subject_->GetFlatContent(); if (IsOneByte()) {
if (flat.IsOneByte()) { Vector<const uint8_t> vector = GetOneByteVector();
Vector<const uint8_t> vector = flat.ToOneByteVector();
DCHECK_EQ(length_, vector.length()); DCHECK_EQ(length_, vector.length());
ParseInternal(vector.start()); ParseInternal(vector.start());
} else { } else {
Vector<const uc16> vector = flat.ToUC16Vector(); Vector<const uc16> vector = GetTwoByteVector();
DCHECK_EQ(length_, vector.length()); DCHECK_EQ(length_, vector.length());
ParseInternal(vector.start()); ParseInternal(vector.start());
} }
@ -292,6 +319,16 @@ void StringToIntHelper::DetectRadixInternal(Char current, int length) {
radix_ = 16; radix_ = 16;
++current; ++current;
if (current == end) return set_state(kJunk); if (current == end) return set_state(kJunk);
} else if (AllowOctalRadixPrefix() &&
(*current == 'o' || *current == 'O')) {
radix_ = 8;
++current;
DCHECK(current != end);
} else if (AllowBinaryRadixPrefix() &&
(*current == 'b' || *current == 'B')) {
radix_ = 2;
++current;
DCHECK(current != end);
} else { } else {
leading_zero_ = true; leading_zero_ = true;
} }
@ -419,14 +456,13 @@ class NumberParseIntHelper : public StringToIntHelper {
bool is_power_of_two = base::bits::IsPowerOfTwo(radix()); bool is_power_of_two = base::bits::IsPowerOfTwo(radix());
if (!is_power_of_two && radix() != 10) return; if (!is_power_of_two && radix() != 10) return;
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String::FlatContent flat = subject()->GetFlatContent(); if (IsOneByte()) {
if (flat.IsOneByte()) { Vector<const uint8_t> vector = GetOneByteVector();
Vector<const uint8_t> vector = flat.ToOneByteVector();
DCHECK_EQ(length(), vector.length()); DCHECK_EQ(length(), vector.length());
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start()) result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start())
: HandleBaseTenCase(vector.start()); : HandleBaseTenCase(vector.start());
} else { } else {
Vector<const uc16> vector = flat.ToUC16Vector(); Vector<const uc16> vector = GetTwoByteVector();
DCHECK_EQ(length(), vector.length()); DCHECK_EQ(length(), vector.length());
result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start()) result_ = is_power_of_two ? HandlePowerOfTwoCase(vector.start())
: HandleBaseTenCase(vector.start()); : HandleBaseTenCase(vector.start());
@ -802,9 +838,15 @@ double StringToInt(Isolate* isolate, Handle<String> string, int radix) {
class BigIntParseIntHelper : public StringToIntHelper { class BigIntParseIntHelper : public StringToIntHelper {
public: public:
// Used for BigInt.parseInt API, where the input is a Heap-allocated String.
BigIntParseIntHelper(Isolate* isolate, Handle<String> string, int radix) BigIntParseIntHelper(Isolate* isolate, Handle<String> string, int radix)
: StringToIntHelper(isolate, string, radix) {} : StringToIntHelper(isolate, string, radix) {}
// Used for parsing BigInt literals, where the input is a buffer of
// one-byte ASCII digits, along with an optional radix prefix.
BigIntParseIntHelper(Isolate* isolate, const uint8_t* string, int length)
: StringToIntHelper(isolate, string, length) {}
MaybeHandle<BigInt> GetResult() { MaybeHandle<BigInt> GetResult() {
ParseInt(); ParseInt();
switch (state()) { switch (state()) {
@ -855,6 +897,12 @@ MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string,
return helper.GetResult(); return helper.GetResult();
} }
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, const char* string) {
BigIntParseIntHelper helper(isolate, reinterpret_cast<const uint8_t*>(string),
static_cast<int>(strlen(string)));
return helper.GetResult();
}
const char* DoubleToCString(double v, Vector<char> buffer) { const char* DoubleToCString(double v, Vector<char> buffer) {
switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) { switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
case FP_NAN: return "NaN"; case FP_NAN: return "NaN";

View File

@ -108,6 +108,13 @@ double StringToInt(Isolate* isolate, Handle<String> string, int radix);
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string, MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, Handle<String> string,
int radix); int radix);
// This version expects a zero-terminated character array. Radix will
// be inferred from string prefix (case-insensitive):
// 0x -> hex
// 0o -> octal
// 0b -> binary
MaybeHandle<BigInt> StringToBigInt(Isolate* isolate, const char* string);
const int kDoubleToCStringMinBufferSize = 100; const int kDoubleToCStringMinBufferSize = 100;
// Converts a double to a string value according to ECMA-262 9.8.1. // Converts a double to a string value according to ECMA-262 9.8.1.

View File

@ -600,7 +600,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadLiteral(
return LoadTheHole(); return LoadTheHole();
} else if (ast_value->IsString()) { } else if (ast_value->IsString()) {
return LoadLiteral(ast_value->AsString()); return LoadLiteral(ast_value->AsString());
} else if (ast_value->IsHeapNumber()) { } else if (ast_value->IsHeapNumber() || ast_value->IsBigInt()) {
size_t entry = GetConstantPoolEntry(ast_value); size_t entry = GetConstantPoolEntry(ast_value);
OutputLdaConstant(entry); OutputLdaConstant(entry);
return *this; return *this;
@ -1448,7 +1448,7 @@ size_t BytecodeArrayBuilder::GetConstantPoolEntry(
} }
size_t BytecodeArrayBuilder::GetConstantPoolEntry(const AstValue* heap_number) { size_t BytecodeArrayBuilder::GetConstantPoolEntry(const AstValue* heap_number) {
DCHECK(heap_number->IsHeapNumber()); DCHECK(heap_number->IsHeapNumber() || heap_number->IsBigInt());
return constant_array_builder()->Insert(heap_number); return constant_array_builder()->Insert(heap_number);
} }

View File

@ -191,12 +191,12 @@ size_t ConstantArrayBuilder::Insert(const AstRawString* raw_string) {
} }
size_t ConstantArrayBuilder::Insert(const AstValue* heap_number) { size_t ConstantArrayBuilder::Insert(const AstValue* heap_number) {
// This method only accepts heap numbers. Other types of ast value should // This method only accepts heap numbers and BigInts. Other types of
// either be passed through as raw values (in the case of strings), use the // AstValue should either be passed through as raw values (in the
// singleton Insert methods (in the case of symbols), or skip the constant // case of strings), use the singleton Insert methods (in the case
// pool entirely and use bytecodes with immediate values (Smis, booleans, // of symbols), or skip the constant pool entirely and use bytecodes
// undefined, etc.). // with immediate values (Smis, booleans, undefined, etc.).
DCHECK(heap_number->IsHeapNumber()); DCHECK(heap_number->IsHeapNumber() || heap_number->IsBigInt());
return constants_map_ return constants_map_
.LookupOrInsert(reinterpret_cast<intptr_t>(heap_number), .LookupOrInsert(reinterpret_cast<intptr_t>(heap_number),
static_cast<uint32_t>(base::hash_value(heap_number)), static_cast<uint32_t>(base::hash_value(heap_number)),
@ -340,7 +340,7 @@ Handle<Object> ConstantArrayBuilder::Entry::ToHandle(Isolate* isolate) const {
case Tag::kRawString: case Tag::kRawString:
return raw_string_->string(); return raw_string_->string();
case Tag::kHeapNumber: case Tag::kHeapNumber:
DCHECK(heap_number_->IsHeapNumber()); DCHECK(heap_number_->IsHeapNumber() || heap_number_->IsBigInt());
return heap_number_->value(); return heap_number_->value();
case Tag::kScope: case Tag::kScope:
return scope_->scope_info(); return scope_->scope_info();

View File

@ -298,6 +298,13 @@ class ParserBase {
#undef ALLOW_ACCESSORS #undef ALLOW_ACCESSORS
bool allow_harmony_bigint() const {
return scanner()->allow_harmony_bigint();
}
void set_allow_harmony_bigint(bool allow) {
scanner()->set_allow_harmony_bigint(allow);
}
uintptr_t stack_limit() const { return stack_limit_; } uintptr_t stack_limit() const { return stack_limit_; }
void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; } void set_stack_limit(uintptr_t stack_limit) { stack_limit_ = stack_limit; }
@ -1549,6 +1556,7 @@ void ParserBase<Impl>::GetUnexpectedTokenMessage(
break; break;
case Token::SMI: case Token::SMI:
case Token::NUMBER: case Token::NUMBER:
case Token::BIGINT:
*message = MessageTemplate::kUnexpectedTokenNumber; *message = MessageTemplate::kUnexpectedTokenNumber;
break; break;
case Token::STRING: case Token::STRING:
@ -1780,6 +1788,7 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParsePrimaryExpression(
case Token::FALSE_LITERAL: case Token::FALSE_LITERAL:
case Token::SMI: case Token::SMI:
case Token::NUMBER: case Token::NUMBER:
case Token::BIGINT:
BindingPatternUnexpectedToken(); BindingPatternUnexpectedToken();
return impl()->ExpressionFromLiteral(Next(), beg_pos); return impl()->ExpressionFromLiteral(Next(), beg_pos);
@ -4233,9 +4242,10 @@ template <typename Impl>
bool ParserBase<Impl>::IsTrivialExpression() { bool ParserBase<Impl>::IsTrivialExpression() {
Token::Value peek_token = peek(); Token::Value peek_token = peek();
if (peek_token == Token::SMI || peek_token == Token::NUMBER || if (peek_token == Token::SMI || peek_token == Token::NUMBER ||
peek_token == Token::NULL_LITERAL || peek_token == Token::TRUE_LITERAL || peek_token == Token::BIGINT || peek_token == Token::NULL_LITERAL ||
peek_token == Token::FALSE_LITERAL || peek_token == Token::STRING || peek_token == Token::TRUE_LITERAL || peek_token == Token::FALSE_LITERAL ||
peek_token == Token::IDENTIFIER || peek_token == Token::THIS) { peek_token == Token::STRING || peek_token == Token::IDENTIFIER ||
peek_token == Token::THIS) {
// PeekAhead() is expensive & may not always be called, so we only call it // PeekAhead() is expensive & may not always be called, so we only call it
// after checking peek(). // after checking peek().
Token::Value peek_ahead = PeekAhead(); Token::Value peek_ahead = PeekAhead();

View File

@ -405,6 +405,9 @@ Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) {
double value = scanner()->DoubleValue(); double value = scanner()->DoubleValue();
return factory()->NewNumberLiteral(value, pos); return factory()->NewNumberLiteral(value, pos);
} }
case Token::BIGINT:
return factory()->NewBigIntLiteral(
scanner()->CurrentLiteralAsCString(zone()), pos);
default: default:
DCHECK(false); DCHECK(false);
} }
@ -513,6 +516,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_import_meta(FLAG_harmony_import_meta); set_allow_harmony_import_meta(FLAG_harmony_import_meta);
set_allow_harmony_async_iteration(FLAG_harmony_async_iteration); set_allow_harmony_async_iteration(FLAG_harmony_async_iteration);
set_allow_harmony_template_escapes(FLAG_harmony_template_escapes); set_allow_harmony_template_escapes(FLAG_harmony_template_escapes);
set_allow_harmony_bigint(FLAG_harmony_bigint);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount; for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) { ++feature) {
use_counts_[feature] = 0; use_counts_[feature] = 0;

View File

@ -299,6 +299,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
SET_ALLOW(harmony_async_iteration); SET_ALLOW(harmony_async_iteration);
SET_ALLOW(harmony_template_escapes); SET_ALLOW(harmony_template_escapes);
SET_ALLOW(harmony_restrictive_generators); SET_ALLOW(harmony_restrictive_generators);
SET_ALLOW(harmony_bigint);
#undef SET_ALLOW #undef SET_ALLOW
} }
return reusable_preparser_; return reusable_preparser_;

View File

@ -196,6 +196,7 @@ Scanner::Scanner(UnicodeCache* unicode_cache, int* use_counts)
octal_pos_(Location::invalid()), octal_pos_(Location::invalid()),
octal_message_(MessageTemplate::kNone), octal_message_(MessageTemplate::kNone),
found_html_comment_(false), found_html_comment_(false),
allow_harmony_bigint_(false),
use_counts_(use_counts) {} use_counts_(use_counts) {}
void Scanner::Initialize(Utf16CharacterStream* source, bool is_module) { void Scanner::Initialize(Utf16CharacterStream* source, bool is_module) {
@ -934,6 +935,7 @@ void Scanner::SanityCheckTokenDesc(const TokenDesc& token) const {
case Token::FUTURE_STRICT_RESERVED_WORD: case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::IDENTIFIER: case Token::IDENTIFIER:
case Token::NUMBER: case Token::NUMBER:
case Token::BIGINT:
case Token::REGEXP_LITERAL: case Token::REGEXP_LITERAL:
case Token::SMI: case Token::SMI:
case Token::STRING: case Token::STRING:
@ -1321,14 +1323,20 @@ Token::Value Scanner::ScanNumber(bool seen_period) {
ScanDecimalDigits(); // optional ScanDecimalDigits(); // optional
if (c0_ == '.') { if (c0_ == '.') {
seen_period = true;
AddLiteralCharAdvance(); AddLiteralCharAdvance();
ScanDecimalDigits(); // optional ScanDecimalDigits(); // optional
} }
} }
} }
// scan exponent, if any bool is_bigint = false;
if (c0_ == 'e' || c0_ == 'E') { if (allow_harmony_bigint() && c0_ == 'n' && !seen_period &&
(kind == DECIMAL || kind == HEX || kind == OCTAL || kind == BINARY)) {
is_bigint = true;
Advance();
} else if (c0_ == 'e' || c0_ == 'E') {
// scan exponent, if any
DCHECK(kind != HEX); // 'e'/'E' must be scanned as part of the hex number DCHECK(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (!(kind == DECIMAL || kind == DECIMAL_WITH_LEADING_ZERO)) if (!(kind == DECIMAL || kind == DECIMAL_WITH_LEADING_ZERO))
return Token::ILLEGAL; return Token::ILLEGAL;
@ -1357,7 +1365,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) {
octal_pos_ = Location(start_pos, source_pos()); octal_pos_ = Location(start_pos, source_pos());
octal_message_ = MessageTemplate::kStrictDecimalWithLeadingZero; octal_message_ = MessageTemplate::kStrictDecimalWithLeadingZero;
} }
return Token::NUMBER; return is_bigint ? Token::BIGINT : Token::NUMBER;
} }
@ -1787,6 +1795,16 @@ double Scanner::DoubleValue() {
ALLOW_HEX | ALLOW_OCTAL | ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY); ALLOW_HEX | ALLOW_OCTAL | ALLOW_IMPLICIT_OCTAL | ALLOW_BINARY);
} }
const char* Scanner::CurrentLiteralAsCString(Zone* zone) const {
DCHECK(is_literal_one_byte());
Vector<const uint8_t> vector = literal_one_byte_string();
int length = vector.length();
char* buffer = zone->NewArray<char>(length + 1);
memcpy(buffer, vector.start(), length);
buffer[length] = '\0';
return buffer;
}
bool Scanner::IsDuplicateSymbol(DuplicateFinder* duplicate_finder, bool Scanner::IsDuplicateSymbol(DuplicateFinder* duplicate_finder,
AstValueFactory* ast_value_factory) const { AstValueFactory* ast_value_factory) const {
DCHECK_NOT_NULL(duplicate_finder); DCHECK_NOT_NULL(duplicate_finder);

View File

@ -260,6 +260,8 @@ class Scanner {
double DoubleValue(); double DoubleValue();
const char* CurrentLiteralAsCString(Zone* zone) const;
inline bool CurrentMatches(Token::Value token) const { inline bool CurrentMatches(Token::Value token) const {
DCHECK(Token::IsKeyword(token)); DCHECK(Token::IsKeyword(token));
return current_.token == token; return current_.token == token;
@ -356,6 +358,9 @@ class Scanner {
bool FoundHtmlComment() const { return found_html_comment_; } bool FoundHtmlComment() const { return found_html_comment_; }
bool allow_harmony_bigint() const { return allow_harmony_bigint_; }
void set_allow_harmony_bigint(bool allow) { allow_harmony_bigint_ = allow; }
private: private:
// Scoped helper for saving & restoring scanner error state. // Scoped helper for saving & restoring scanner error state.
// This is used for tagged template literals, in which normally forbidden // This is used for tagged template literals, in which normally forbidden
@ -801,6 +806,9 @@ class Scanner {
// Whether this scanner encountered an HTML comment. // Whether this scanner encountered an HTML comment.
bool found_html_comment_; bool found_html_comment_;
// Whether to recognize BIGINT tokens.
bool allow_harmony_bigint_;
int* use_counts_; int* use_counts_;
MessageTemplate::Template scanner_error_; MessageTemplate::Template scanner_error_;

View File

@ -147,6 +147,7 @@ namespace internal {
T(NUMBER, nullptr, 0) \ T(NUMBER, nullptr, 0) \
T(SMI, nullptr, 0) \ T(SMI, nullptr, 0) \
T(STRING, nullptr, 0) \ T(STRING, nullptr, 0) \
T(BIGINT, NULL, 0) \
\ \
/* Identifiers (not keywords or future reserved words). */ \ /* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, nullptr, 0) \ T(IDENTIFIER, nullptr, 0) \

View File

@ -399,6 +399,26 @@ const six = BigInt(6);
assertThrows(() => +One, TypeError); assertThrows(() => +One, TypeError);
} }
// Literals
{
// Invalid literals.
assertThrows("00n", SyntaxError);
assertThrows("01n", SyntaxError);
assertThrows("0bn", SyntaxError);
assertThrows("0on", SyntaxError);
assertThrows("0xn", SyntaxError);
assertThrows("1.n", SyntaxError);
assertThrows("1.0n", SyntaxError);
assertThrows("1e25n", SyntaxError);
// Various radixes.
assertTrue(12345n === BigInt(12345));
assertTrue(0xabcden === BigInt(0xabcde));
assertTrue(0xAbCdEn === BigInt(0xabcde));
assertTrue(0o54321n === BigInt(0o54321));
assertTrue(0b1010101n === BigInt(0b1010101));
}
// Binary ops. // Binary ops.
{ {
assertTrue(one + two === three); assertTrue(one + two === three);

View File

@ -165,6 +165,7 @@ v8_source_set("unittests_sources") {
"libplatform/worker-thread-unittest.cc", "libplatform/worker-thread-unittest.cc",
"locked-queue-unittest.cc", "locked-queue-unittest.cc",
"object-unittest.cc", "object-unittest.cc",
"parser/ast-value-unittest.cc",
"parser/preparser-unittest.cc", "parser/preparser-unittest.cc",
"register-configuration-unittest.cc", "register-configuration-unittest.cc",
"run-all-unittests.cc", "run-all-unittests.cc",

View File

@ -0,0 +1,43 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ast/ast-value-factory.h"
#include "src/heap/heap-inl.h"
#include "src/isolate-inl.h"
#include "src/zone/zone.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
class AstValueTest : public TestWithIsolateAndZone {
protected:
AstValueTest()
: ast_value_factory_(zone(), i_isolate()->ast_string_constants(),
i_isolate()->heap()->HashSeed()) {}
AstValueFactory ast_value_factory_;
};
TEST_F(AstValueTest, BigIntBooleanValue) {
EXPECT_FALSE(ast_value_factory_.NewBigInt("0")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0b0")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0o0")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0x0")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0b000")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0o00000")->BooleanValue());
EXPECT_FALSE(ast_value_factory_.NewBigInt("0x000000000")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("3")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0b1")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0o6")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0xa")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0b0000001")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0o00005000")->BooleanValue());
EXPECT_TRUE(ast_value_factory_.NewBigInt("0x0000d00c0")->BooleanValue());
}
} // namespace internal
} // namespace v8

View File

@ -138,6 +138,7 @@
'libplatform/worker-thread-unittest.cc', 'libplatform/worker-thread-unittest.cc',
'locked-queue-unittest.cc', 'locked-queue-unittest.cc',
'object-unittest.cc', 'object-unittest.cc',
'parser/ast-value-unittest.cc',
'parser/preparser-unittest.cc', 'parser/preparser-unittest.cc',
'register-configuration-unittest.cc', 'register-configuration-unittest.cc',
'run-all-unittests.cc', 'run-all-unittests.cc',