blob: 67044de7be48f9208c637a742270dbac7ff29de6 [file] [log] [blame]
// Copyright 2016 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/json-parser.h"
#include "src/char-predicates-inl.h"
#include "src/conversions.h"
#include "src/debug/debug.h"
#include "src/field-type.h"
#include "src/hash-seed-inl.h"
#include "src/heap/heap-inl.h" // For string_table().
#include "src/message-template.h"
#include "src/objects-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/property-descriptor.h"
#include "src/string-hasher.h"
#include "src/transitions-inl.h"
namespace v8 {
namespace internal {
namespace {
// A vector-like data structure that uses a larger vector for allocation, and
// provides limited utility access. The original vector must not be used for the
// duration, and it may even be reallocated. This allows vector storage to be
// reused for the properties of sibling objects.
template <typename Container>
class VectorSegment {
public:
using value_type = typename Container::value_type;
explicit VectorSegment(Container* container)
: container_(*container), begin_(container->size()) {}
~VectorSegment() { container_.resize(begin_); }
Vector<const value_type> GetVector() const {
return VectorOf(container_) + begin_;
}
template <typename T>
void push_back(T&& value) {
container_.push_back(std::forward<T>(value));
}
private:
Container& container_;
const typename Container::size_type begin_;
};
constexpr JsonToken GetOneCharJsonToken(uint8_t c) {
// clang-format off
return
c == '"' ? JsonToken::STRING :
IsDecimalDigit(c) ? JsonToken::NUMBER :
c == '-' ? JsonToken::NEGATIVE_NUMBER :
c == '[' ? JsonToken::LBRACK :
c == '{' ? JsonToken::LBRACE :
c == ']' ? JsonToken::RBRACK :
c == '}' ? JsonToken::RBRACE :
c == 't' ? JsonToken::TRUE_LITERAL :
c == 'f' ? JsonToken::FALSE_LITERAL :
c == 'n' ? JsonToken::NULL_LITERAL :
c == ' ' ? JsonToken::WHITESPACE :
c == '\t' ? JsonToken::WHITESPACE :
c == '\r' ? JsonToken::WHITESPACE :
c == '\n' ? JsonToken::WHITESPACE :
c == ':' ? JsonToken::COLON :
c == ',' ? JsonToken::COMMA :
JsonToken::ILLEGAL;
// clang-format on
}
// Table of one-character tokens, by character (0x00..0xFF only).
static const constexpr JsonToken one_char_json_tokens[256] = {
#define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
#define CALL_GET_SCAN_FLAGS(N) GetOneCharJsonToken(128 + N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
};
enum class EscapeKind : uint8_t {
kIllegal,
kSelf,
kBackspace,
kTab,
kNewLine,
kFormFeed,
kCarriageReturn,
kUnicode
};
using EscapeKindField = BitField8<EscapeKind, 0, 3>;
using MayTerminateStringField = BitField8<bool, EscapeKindField::kNext, 1>;
using NumberPartField = BitField8<bool, MayTerminateStringField::kNext, 1>;
constexpr bool MayTerminateJsonString(uint8_t flags) {
return MayTerminateStringField::decode(flags);
}
constexpr EscapeKind GetEscapeKind(uint8_t flags) {
return EscapeKindField::decode(flags);
}
constexpr bool IsNumberPart(uint8_t flags) {
return NumberPartField::decode(flags);
}
constexpr uint8_t GetJsonScanFlags(uint8_t c) {
// clang-format off
return (c == 'b' ? EscapeKindField::encode(EscapeKind::kBackspace)
: c == 't' ? EscapeKindField::encode(EscapeKind::kTab)
: c == 'n' ? EscapeKindField::encode(EscapeKind::kNewLine)
: c == 'f' ? EscapeKindField::encode(EscapeKind::kFormFeed)
: c == 'r' ? EscapeKindField::encode(EscapeKind::kCarriageReturn)
: c == 'u' ? EscapeKindField::encode(EscapeKind::kUnicode)
: c == '"' ? EscapeKindField::encode(EscapeKind::kSelf)
: c == '\\' ? EscapeKindField::encode(EscapeKind::kSelf)
: c == '/' ? EscapeKindField::encode(EscapeKind::kSelf)
: EscapeKindField::encode(EscapeKind::kIllegal)) |
(c < 0x20 ? MayTerminateStringField::encode(true)
: c == '"' ? MayTerminateStringField::encode(true)
: c == '\\' ? MayTerminateStringField::encode(true)
: MayTerminateStringField::encode(false)) |
NumberPartField::encode(c == '.' ||
c == 'e' ||
c == 'E' ||
IsDecimalDigit(c) ||
c == '-' ||
c == '+');
// clang-format on
}
// Table of one-character scan flags, by character (0x00..0xFF only).
static const constexpr uint8_t character_json_scan_flags[256] = {
#define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
#define CALL_GET_SCAN_FLAGS(N) GetJsonScanFlags(128 + N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
};
} // namespace
MaybeHandle<Object> JsonParseInternalizer::Internalize(Isolate* isolate,
Handle<Object> object,
Handle<Object> reviver) {
DCHECK(reviver->IsCallable());
JsonParseInternalizer internalizer(isolate,
Handle<JSReceiver>::cast(reviver));
Handle<JSObject> holder =
isolate->factory()->NewJSObject(isolate->object_function());
Handle<String> name = isolate->factory()->empty_string();
JSObject::AddProperty(isolate, holder, name, object, NONE);
return internalizer.InternalizeJsonProperty(holder, name);
}
MaybeHandle<Object> JsonParseInternalizer::InternalizeJsonProperty(
Handle<JSReceiver> holder, Handle<String> name) {
HandleScope outer_scope(isolate_);
Handle<Object> value;
ASSIGN_RETURN_ON_EXCEPTION(
isolate_, value, Object::GetPropertyOrElement(isolate_, holder, name),
Object);
if (value->IsJSReceiver()) {
Handle<JSReceiver> object = Handle<JSReceiver>::cast(value);
Maybe<bool> is_array = Object::IsArray(object);
if (is_array.IsNothing()) return MaybeHandle<Object>();
if (is_array.FromJust()) {
Handle<Object> length_object;
ASSIGN_RETURN_ON_EXCEPTION(
isolate_, length_object,
Object::GetLengthFromArrayLike(isolate_, object), Object);
double length = length_object->Number();
for (double i = 0; i < length; i++) {
HandleScope inner_scope(isolate_);
Handle<Object> index = isolate_->factory()->NewNumber(i);
Handle<String> name = isolate_->factory()->NumberToString(index);
if (!RecurseAndApply(object, name)) return MaybeHandle<Object>();
}
} else {
Handle<FixedArray> contents;
ASSIGN_RETURN_ON_EXCEPTION(
isolate_, contents,
KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS,
GetKeysConversion::kConvertToString),
Object);
for (int i = 0; i < contents->length(); i++) {
HandleScope inner_scope(isolate_);
Handle<String> name(String::cast(contents->get(i)), isolate_);
if (!RecurseAndApply(object, name)) return MaybeHandle<Object>();
}
}
}
Handle<Object> argv[] = {name, value};
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate_, result, Execution::Call(isolate_, reviver_, holder, 2, argv),
Object);
return outer_scope.CloseAndEscape(result);
}
bool JsonParseInternalizer::RecurseAndApply(Handle<JSReceiver> holder,
Handle<String> name) {
STACK_CHECK(isolate_, false);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, result, InternalizeJsonProperty(holder, name), false);
Maybe<bool> change_result = Nothing<bool>();
if (result->IsUndefined(isolate_)) {
change_result = JSReceiver::DeletePropertyOrElement(holder, name,
LanguageMode::kSloppy);
} else {
PropertyDescriptor desc;
desc.set_value(result);
desc.set_configurable(true);
desc.set_enumerable(true);
desc.set_writable(true);
change_result = JSReceiver::DefineOwnProperty(isolate_, holder, name, &desc,
Just(kDontThrow));
}
MAYBE_RETURN(change_result, false);
return true;
}
template <typename Char>
JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
: isolate_(isolate),
zone_(isolate_->allocator(), ZONE_NAME),
hash_seed_(HashSeed(isolate)),
object_constructor_(isolate_->object_function()),
original_source_(source),
properties_(&zone_) {
size_t start = 0;
size_t length = source->length();
if (source->IsSlicedString()) {
SlicedString string = SlicedString::cast(*source);
start = string.offset();
String parent = string.parent();
if (parent.IsThinString()) parent = ThinString::cast(parent).actual();
source_ = handle(parent, isolate);
} else {
source_ = String::Flatten(isolate, source);
}
if (StringShape(*source_).IsExternal()) {
chars_ =
static_cast<const Char*>(SeqExternalString::cast(*source_)->GetChars());
chars_may_relocate_ = false;
} else {
DisallowHeapAllocation no_gc;
isolate->heap()->AddGCEpilogueCallback(UpdatePointersCallback,
v8::kGCTypeAll, this);
chars_ = SeqString::cast(*source_)->GetChars(no_gc);
chars_may_relocate_ = true;
}
cursor_ = chars_ + start;
end_ = cursor_ + length;
}
template <typename Char>
void JsonParser<Char>::ReportUnexpectedToken(JsonToken token) {
// Some exception (for example stack overflow) is already pending.
if (isolate_->has_pending_exception()) return;
// Parse failed. Current character is the unexpected token.
Factory* factory = this->factory();
MessageTemplate message;
Handle<Object> arg1 = Handle<Smi>(Smi::FromInt(position()), isolate());
Handle<Object> arg2;
switch (token) {
case JsonToken::EOS:
message = MessageTemplate::kJsonParseUnexpectedEOS;
break;
case JsonToken::NUMBER:
case JsonToken::NEGATIVE_NUMBER:
message = MessageTemplate::kJsonParseUnexpectedTokenNumber;
break;
case JsonToken::STRING:
message = MessageTemplate::kJsonParseUnexpectedTokenString;
break;
default:
message = MessageTemplate::kJsonParseUnexpectedToken;
arg2 = arg1;
arg1 = factory->LookupSingleCharacterStringFromCode(*cursor_);
break;
}
Handle<Script> script(factory->NewScript(original_source_));
if (isolate()->NeedsSourcePositionsForProfiling()) {
Script::InitLineEnds(script);
}
// We should sent compile error event because we compile JSON object in
// separated source file.
isolate()->debug()->OnCompileError(script);
MessageLocation location(script, position(), position() + 1);
Handle<Object> error = factory->NewSyntaxError(message, arg1, arg2);
isolate()->Throw(*error, &location);
// Move the cursor to the end so we won't be able to proceed parsing.
cursor_ = end_;
}
template <typename Char>
void JsonParser<Char>::ReportUnexpectedCharacter(uc32 c) {
JsonToken token = JsonToken::ILLEGAL;
if (c == kEndOfString) {
token = JsonToken::EOS;
} else if (c <= unibrow::Latin1::kMaxChar) {
token = one_char_json_tokens[c];
}
return ReportUnexpectedToken(token);
}
template <typename Char>
JsonParser<Char>::~JsonParser() {
if (StringShape(*source_).IsExternal()) {
// Check that the string shape hasn't changed. Otherwise our GC hooks are
// broken.
SeqExternalString::cast(*source_);
} else {
// Check that the string shape hasn't changed. Otherwise our GC hooks are
// broken.
SeqString::cast(*source_);
isolate()->heap()->RemoveGCEpilogueCallback(UpdatePointersCallback, this);
}
}
template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJson() {
Handle<Object> result = ParseJsonValue();
if (!Check(JsonToken::EOS)) ReportUnexpectedToken(peek());
if (isolate_->has_pending_exception()) return MaybeHandle<Object>();
return result;
}
MaybeHandle<Object> InternalizeJsonProperty(Handle<JSObject> holder,
Handle<String> key);
template <typename Char>
void JsonParser<Char>::SkipWhitespace() {
next_ = JsonToken::EOS;
cursor_ = std::find_if(cursor_, end_, [this](Char c) {
JsonToken current = V8_LIKELY(c <= unibrow::Latin1::kMaxChar)
? one_char_json_tokens[c]
: JsonToken::ILLEGAL;
bool result = current != JsonToken::WHITESPACE;
if (result) next_ = current;
return result;
});
}
// Parse any JSON value.
template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonValue() {
StackLimitCheck stack_check(isolate_);
if (V8_UNLIKELY(stack_check.InterruptRequested())) {
if (stack_check.HasOverflowed()) {
if (!isolate_->has_pending_exception()) isolate_->StackOverflow();
return factory()->undefined_value();
}
if (isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) {
return factory()->undefined_value();
}
}
SkipWhitespace();
switch (peek()) {
case JsonToken::STRING:
Consume(JsonToken::STRING);
return ParseJsonString(false);
case JsonToken::NUMBER:
return ParseJsonNumber(1, cursor_);
case JsonToken::NEGATIVE_NUMBER:
advance();
if (is_at_end()) {
ReportUnexpectedToken(JsonToken::EOS);
return handle(Smi::FromInt(0), isolate_);
}
return ParseJsonNumber(-1, cursor_ - 1);
case JsonToken::LBRACE:
return ParseJsonObject();
case JsonToken::LBRACK:
return ParseJsonArray();
case JsonToken::TRUE_LITERAL:
ScanLiteral("true");
return factory()->true_value();
case JsonToken::FALSE_LITERAL:
ScanLiteral("false");
return factory()->false_value();
case JsonToken::NULL_LITERAL:
ScanLiteral("null");
return factory()->null_value();
case JsonToken::COLON:
case JsonToken::COMMA:
case JsonToken::ILLEGAL:
case JsonToken::RBRACE:
case JsonToken::RBRACK:
case JsonToken::EOS:
ReportUnexpectedCharacter(CurrentCharacter());
return factory()->undefined_value();
case JsonToken::WHITESPACE:
UNREACHABLE();
}
}
template <typename Char>
bool JsonParser<Char>::ParseElement(Handle<JSObject> json_object) {
uint32_t index = 0;
{
// |cursor_| will only be updated if the key ends up being an index.
DisallowHeapAllocation no_gc;
const Char* cursor = cursor_;
// Maybe an array index, try to parse it.
if (*cursor == '0') {
// With a leading zero, the string has to be "0" only to be an index.
cursor++;
} else {
cursor = std::find_if(cursor, end_, [&index](Char c) {
return !TryAddIndexChar(&index, c);
});
}
if (V8_UNLIKELY(cursor == end_) || *cursor != '"') return false;
cursor_ = cursor + 1;
}
ExpectNext(JsonToken::COLON);
Handle<Object> value = ParseJsonValue();
JSObject::SetOwnElementIgnoreAttributes(json_object, index, value, NONE)
.Assert();
return true;
}
// Parse a JSON object. Position must be right at '{'.
template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonObject() {
HandleScope scope(isolate());
Handle<JSObject> json_object = factory()->NewJSObject(object_constructor());
Handle<Map> map(json_object->map(), isolate());
int descriptor = 0;
VectorSegment<ZoneVector<Handle<Object>>> properties(&properties_);
Consume(JsonToken::LBRACE);
bool transitioning = true;
if (!Check(JsonToken::RBRACE)) {
do {
ExpectNext(JsonToken::STRING);
if (is_at_end() ||
(IsDecimalDigit(*cursor_) && ParseElement(json_object))) {
continue;
}
// Try to follow existing transitions as long as possible. Once we stop
// transitioning, no transition can be found anymore.
DCHECK(transitioning);
Handle<Map> target;
// First check whether there is a single expected transition. If so, try
// to parse it first.
Handle<String> expected;
{
DisallowHeapAllocation no_gc;
TransitionsAccessor transitions(isolate(), *map, &no_gc);
expected = transitions.ExpectedTransitionKey();
}
Handle<String> key = ParseJsonString(true, expected);
// If the expected transition hits, follow it.
if (key.is_identical_to(expected)) {
DisallowHeapAllocation no_gc;
target = TransitionsAccessor(isolate(), *map, &no_gc)
.ExpectedTransitionTarget();
} else {
// If a transition was found, follow it and continue.
transitioning = TransitionsAccessor(isolate(), map)
.FindTransitionToField(key)
.ToHandle(&target);
}
ExpectNext(JsonToken::COLON);
Handle<Object> value = ParseJsonValue();
if (transitioning) {
PropertyDetails details =
target->instance_descriptors()->GetDetails(descriptor);
Representation expected_representation = details.representation();
if (value->FitsRepresentation(expected_representation)) {
if (expected_representation.IsHeapObject() &&
!target->instance_descriptors()
->GetFieldType(descriptor)
->NowContains(value)) {
Handle<FieldType> value_type(
value->OptimalType(isolate(), expected_representation));
Map::GeneralizeField(isolate(), target, descriptor,
details.constness(), expected_representation,
value_type);
}
DCHECK(target->instance_descriptors()
->GetFieldType(descriptor)
->NowContains(value));
properties.push_back(value);
map = target;
descriptor++;
continue;
} else {
transitioning = false;
}
}
DCHECK(!transitioning);
// Commit the intermediate state to the object and stop transitioning.
CommitStateToJsonObject(json_object, map, properties.GetVector());
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value)
.Check();
} while (transitioning && Check(JsonToken::COMMA));
// If we transitioned until the very end, transition the map now.
if (transitioning) {
CommitStateToJsonObject(json_object, map, properties.GetVector());
} else {
while (Check(JsonToken::COMMA)) {
HandleScope local_scope(isolate());
ExpectNext(JsonToken::STRING);
if (is_at_end() ||
(IsDecimalDigit(*cursor_) && ParseElement(json_object))) {
continue;
}
Handle<String> key = ParseJsonString(true);
ExpectNext(JsonToken::COLON);
Handle<Object> value = ParseJsonValue();
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key,
value)
.Check();
}
}
Expect(JsonToken::RBRACE);
}
return scope.CloseAndEscape(json_object);
}
template <typename Char>
void JsonParser<Char>::CommitStateToJsonObject(
Handle<JSObject> json_object, Handle<Map> map,
const Vector<const Handle<Object>>& properties) {
JSObject::AllocateStorageForMap(json_object, map);
DCHECK(!json_object->map()->is_dictionary_map());
DisallowHeapAllocation no_gc;
DescriptorArray descriptors = json_object->map()->instance_descriptors();
for (int i = 0; i < properties.length(); i++) {
Handle<Object> value = properties[i];
// Initializing store.
json_object->WriteToField(i, descriptors->GetDetails(i), *value);
}
}
class ElementKindLattice {
private:
enum Kind {
SMI_ELEMENTS = 0,
NUMBER_ELEMENTS = 1,
OBJECT_ELEMENTS = (1 << 1) | NUMBER_ELEMENTS,
};
public:
ElementKindLattice() : value_(SMI_ELEMENTS) {}
void Update(Handle<Object> o) {
if (o->IsSmi()) {
return;
} else if (o->IsHeapNumber()) {
value_ = static_cast<Kind>(value_ | NUMBER_ELEMENTS);
} else {
value_ = OBJECT_ELEMENTS;
}
}
ElementsKind GetElementsKind() const {
switch (value_) {
case SMI_ELEMENTS:
return PACKED_SMI_ELEMENTS;
case NUMBER_ELEMENTS:
return PACKED_DOUBLE_ELEMENTS;
case OBJECT_ELEMENTS:
return PACKED_ELEMENTS;
}
}
private:
Kind value_;
};
// Parse a JSON array. Position must be right at '['.
template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonArray() {
HandleScope scope(isolate());
ZoneVector<Handle<Object>> elements(&zone_);
Consume(JsonToken::LBRACK);
ElementKindLattice lattice;
if (!Check(JsonToken::RBRACK)) {
do {
Handle<Object> element = ParseJsonValue();
elements.push_back(element);
lattice.Update(element);
} while (Check(JsonToken::COMMA));
Expect(JsonToken::RBRACK);
}
// Allocate a fixed array with all the elements.
Handle<Object> json_array;
const ElementsKind kind = lattice.GetElementsKind();
int elements_size = static_cast<int>(elements.size());
switch (kind) {
case PACKED_ELEMENTS:
case PACKED_SMI_ELEMENTS: {
Handle<FixedArray> elems = factory()->NewFixedArray(elements_size);
for (int i = 0; i < elements_size; i++) elems->set(i, *elements[i]);
json_array = factory()->NewJSArrayWithElements(elems, kind);
break;
}
case PACKED_DOUBLE_ELEMENTS: {
Handle<FixedDoubleArray> elems = Handle<FixedDoubleArray>::cast(
factory()->NewFixedDoubleArray(elements_size));
for (int i = 0; i < elements_size; i++) {
elems->set(i, elements[i]->Number());
}
json_array = factory()->NewJSArrayWithElements(elems, kind);
break;
}
default:
UNREACHABLE();
}
return scope.CloseAndEscape(json_array);
}
template <typename Char>
void JsonParser<Char>::AdvanceToNonDecimal() {
cursor_ =
std::find_if(cursor_, end_, [](Char c) { return !IsDecimalDigit(c); });
}
template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonNumber(int sign, const Char* start) {
double number;
{
DisallowHeapAllocation no_gc;
if (*cursor_ == '0') {
// Prefix zero is only allowed if it's the only digit before
// a decimal point or exponent.
uc32 c = NextCharacter();
if (IsInRange(c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)) &&
IsNumberPart(character_json_scan_flags[c])) {
if (V8_UNLIKELY(IsDecimalDigit(c))) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedToken(JsonToken::NUMBER);
return handle(Smi::FromInt(0), isolate_);
}
} else if (sign > 0) {
return handle(Smi::FromInt(0), isolate_);
}
} else {
const Char* smi_start = cursor_;
AdvanceToNonDecimal();
if (V8_UNLIKELY(smi_start == cursor_)) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter(CurrentCharacter());
return handle(Smi::FromInt(0), isolate_);
}
uc32 c = CurrentCharacter();
STATIC_ASSERT(Smi::IsValid(-999999999));
STATIC_ASSERT(Smi::IsValid(999999999));
const int kMaxSmiLength = 9;
if ((cursor_ - smi_start) <= kMaxSmiLength &&
(!IsInRange(c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)) ||
!IsNumberPart(character_json_scan_flags[c]))) {
// Smi.
int32_t i = 0;
for (; smi_start != cursor_; smi_start++) {
DCHECK(IsDecimalDigit(*smi_start));
i = (i * 10) + ((*smi_start) - '0');
}
// TODO(verwaest): Cache?
return handle(Smi::FromInt(i * sign), isolate_);
}
}
if (CurrentCharacter() == '.') {
uc32 c = NextCharacter();
if (!IsDecimalDigit(c)) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter(c);
return handle(Smi::FromInt(0), isolate_);
}
AdvanceToNonDecimal();
}
if (AsciiAlphaToLower(CurrentCharacter()) == 'e') {
uc32 c = NextCharacter();
if (c == '-' || c == '+') c = NextCharacter();
if (!IsDecimalDigit(c)) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter(c);
return handle(Smi::FromInt(0), isolate_);
}
AdvanceToNonDecimal();
}
Vector<const uint8_t> chars;
if (kIsOneByte) {
chars = Vector<const uint8_t>::cast(
Vector<const Char>(start, cursor_ - start));
} else {
literal_buffer_.Start();
do {
literal_buffer_.AddChar(*start++);
} while (start != cursor_);
chars = literal_buffer_.one_byte_literal();
}
number = StringToDouble(chars,
NO_FLAGS, // Hex, octal or trailing junk.
std::numeric_limits<double>::quiet_NaN());
DCHECK(!std::isnan(number));
}
return factory()->NewNumber(number);
}
namespace {
template <typename Char>
bool Matches(const Vector<const Char>& chars, Handle<String> string) {
if (string.is_null()) return false;
// Only supports internalized strings in their canonical representation (one
// byte encoded as two-byte will return false here).
if ((sizeof(Char) == 1) != string->IsOneByteRepresentation()) return false;
if (chars.length() != string->length()) return false;
DisallowHeapAllocation no_gc;
const Char* string_data = string->GetChars<Char>(no_gc);
return CompareChars(chars.begin(), string_data, chars.length()) == 0;
}
} // namespace
template <typename Char>
Handle<String> JsonParser<Char>::MakeString(bool requires_internalization,
int offset, int length) {
AllowHeapAllocation allow_gc;
DCHECK(chars_may_relocate_);
Handle<SeqOneByteString> source = Handle<SeqOneByteString>::cast(source_);
if (!requires_internalization && length > kMaxInternalizedStringValueLength) {
Handle<SeqOneByteString> result =
factory()->NewRawOneByteString(length).ToHandleChecked();
DisallowHeapAllocation no_gc;
uint8_t* d = result->GetChars(no_gc);
uint8_t* s = source->GetChars(no_gc) + offset;
MemCopy(d, s, length);
return result;
}
return factory()->InternalizeOneByteString(source, offset, length);
}
template <typename Char>
template <typename LiteralChar>
Handle<String> JsonParser<Char>::MakeString(
bool requires_internalization, const Vector<const LiteralChar>& chars) {
AllowHeapAllocation allow_gc;
DCHECK_IMPLIES(
chars_may_relocate_,
chars.begin() == literal_buffer_.literal<LiteralChar>().begin());
if (!requires_internalization &&
chars.length() > kMaxInternalizedStringValueLength) {
if (sizeof(LiteralChar) == 1) {
return factory()
->NewStringFromOneByte(Vector<const uint8_t>::cast(chars))
.ToHandleChecked();
}
return factory()
->NewStringFromTwoByte(Vector<const uint16_t>::cast(chars))
.ToHandleChecked();
}
SequentialStringKey<LiteralChar> key(chars, hash_seed_);
return StringTable::LookupKey(isolate_, &key);
}
template <typename Char>
Handle<String> JsonParser<Char>::ParseJsonString(bool requires_internalization,
Handle<String> hint) {
// First try to fast scan without buffering in case the string doesn't have
// escaped sequences. Always buffer two-byte input strings as the scanned
// substring can be one-byte.
if (kIsOneByte) {
DisallowHeapAllocation no_gc;
const Char* start = cursor_;
while (true) {
cursor_ = std::find_if(cursor_, end_, [](Char c) {
return MayTerminateJsonString(character_json_scan_flags[c]);
});
if (V8_UNLIKELY(is_at_end())) break;
if (*cursor_ == '"') {
Handle<String> result;
Vector<const Char> chars(start, cursor_ - start);
if (Matches(chars, hint)) {
result = hint;
} else if (chars_may_relocate_) {
result = MakeString(requires_internalization,
static_cast<int>(start - chars_),
static_cast<int>(cursor_ - start));
} else {
result = MakeString(requires_internalization,
Vector<const uint8_t>::cast(chars));
}
advance();
return result;
}
if (*cursor_ == '\\') break;
DCHECK_LT(*cursor_, 0x20);
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter(*cursor_);
return factory()->empty_string();
}
// We hit an escape sequence. Start buffering.
// TODO(verwaest): MemCopy.
literal_buffer_.Start();
while (start != cursor_) {
literal_buffer_.AddChar(*start++);
}
} else {
literal_buffer_.Start();
}
while (true) {
cursor_ = std::find_if(cursor_, end_, [this](Char c) {
if (V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) {
AddLiteralChar(c);
return false;
}
if (MayTerminateJsonString(character_json_scan_flags[c])) {
return true;
}
AddLiteralChar(c);
return false;
});
if (V8_UNLIKELY(is_at_end())) break;
if (*cursor_ == '"') {
Handle<String> result;
if (literal_buffer_.is_one_byte()) {
Vector<const uint8_t> chars = literal_buffer_.one_byte_literal();
result = Matches(chars, hint)
? hint
: MakeString(requires_internalization, chars);
} else {
Vector<const uint16_t> chars = literal_buffer_.two_byte_literal();
result = Matches(chars, hint)
? hint
: MakeString(requires_internalization, chars);
}
advance();
return result;
}
if (*cursor_ == '\\') {
uc32 c = NextCharacter();
if (V8_UNLIKELY(!IsInRange(
c, 0, static_cast<int32_t>(unibrow::Latin1::kMaxChar)))) {
ReportUnexpectedCharacter(c);
return factory()->empty_string();
}
uc32 value;
switch (GetEscapeKind(character_json_scan_flags[c])) {
case EscapeKind::kSelf:
value = c;
break;
case EscapeKind::kBackspace:
value = '\x08';
break;
case EscapeKind::kTab:
value = '\x09';
break;
case EscapeKind::kNewLine:
value = '\x0A';
break;
case EscapeKind::kFormFeed:
value = '\x0C';
break;
case EscapeKind::kCarriageReturn:
value = '\x0D';
break;
case EscapeKind::kUnicode: {
value = 0;
for (int i = 0; i < 4; i++) {
int digit = HexValue(NextCharacter());
if (V8_UNLIKELY(digit < 0)) {
ReportUnexpectedCharacter(CurrentCharacter());
return factory()->empty_string();
}
value = value * 16 + digit;
}
break;
}
case EscapeKind::kIllegal:
ReportUnexpectedCharacter(c);
return factory()->empty_string();
}
AddLiteralChar(value);
advance();
continue;
}
DCHECK_LT(*cursor_, 0x20);
ReportUnexpectedCharacter(*cursor_);
return factory()->empty_string();
}
ReportUnexpectedCharacter(kEndOfString);
return factory()->empty_string();
}
// Explicit instantiation.
template class JsonParser<uint8_t>;
template class JsonParser<uint16_t>;
} // namespace internal
} // namespace v8