Commit 2036e259 authored by Michael Achenbach's avatar Michael Achenbach Committed by Commit Bot

Revert "[json] Speed up json parsing"

This reverts commit b0c4a876.

Reason for revert:
https://ci.chromium.org/p/v8/builders/ci/V8%20Linux%20-%20arm64%20-%20sim%20-%20MSAN/26470

Original change's description:
> [json] Speed up json parsing
> 
> - scan using raw data pointers + GC callback
> - scan using scanner tables
> - cap internalizing large string values
> - inline fast transitioning logic
> 
> Fixes previous CL by moving AllowHeapAllocation to callers of
> ReportUnexpectedCharacter where needed to make it clear we need to exit.
> 
> Tbr: ulan@chromium.org
> Change-Id: Icfbb7cd536e0fbe153f34acca5d0fab6b5453d71
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1591778
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Commit-Queue: Toon Verwaest <verwaest@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#61159}

TBR=ulan@chromium.org,ishell@google.com,ishell@chromium.org,verwaest@chromium.org

Change-Id: Ibe823e187d9ab999be7278140b0ed31868440e9e
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1593090Reviewed-by: 's avatarMichael Achenbach <machenbach@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61163}
parent 3b0c0dd9
...@@ -2571,8 +2571,6 @@ v8_source_set("v8_base_without_compiler") { ...@@ -2571,8 +2571,6 @@ v8_source_set("v8_base_without_compiler") {
"src/parsing/expression-scope.h", "src/parsing/expression-scope.h",
"src/parsing/func-name-inferrer.cc", "src/parsing/func-name-inferrer.cc",
"src/parsing/func-name-inferrer.h", "src/parsing/func-name-inferrer.h",
"src/parsing/literal-buffer.cc",
"src/parsing/literal-buffer.h",
"src/parsing/parse-info.cc", "src/parsing/parse-info.cc",
"src/parsing/parse-info.h", "src/parsing/parse-info.h",
"src/parsing/parser-base.h", "src/parsing/parser-base.h",
......
...@@ -54,16 +54,37 @@ class OneByteStringStream { ...@@ -54,16 +54,37 @@ class OneByteStringStream {
} // namespace } // namespace
class AstRawStringInternalizationKey : public StringTableKey {
public:
explicit AstRawStringInternalizationKey(const AstRawString* string)
: StringTableKey(string->hash_field()), string_(string) {}
bool IsMatch(Object other) override {
if (string_->is_one_byte())
return String::cast(other)->IsOneByteEqualTo(string_->literal_bytes_);
return String::cast(other)->IsTwoByteEqualTo(
Vector<const uint16_t>::cast(string_->literal_bytes_));
}
Handle<String> AsHandle(Isolate* isolate) override {
if (string_->is_one_byte())
return isolate->factory()->NewOneByteInternalizedString(
string_->literal_bytes_, string_->hash_field());
return isolate->factory()->NewTwoByteInternalizedString(
Vector<const uint16_t>::cast(string_->literal_bytes_),
string_->hash_field());
}
private:
const AstRawString* string_;
};
void AstRawString::Internalize(Isolate* isolate) { void AstRawString::Internalize(Isolate* isolate) {
DCHECK(!has_string_); DCHECK(!has_string_);
if (literal_bytes_.length() == 0) { if (literal_bytes_.length() == 0) {
set_string(isolate->factory()->empty_string()); set_string(isolate->factory()->empty_string());
} else if (is_one_byte()) {
OneByteStringKey key(hash_field_, literal_bytes_);
set_string(StringTable::LookupKey(isolate, &key));
} else { } else {
TwoByteStringKey key(hash_field_, AstRawStringInternalizationKey key(this);
Vector<const uint16_t>::cast(literal_bytes_));
set_string(StringTable::LookupKey(isolate, &key)); set_string(StringTable::LookupKey(isolate, &key));
} }
} }
......
...@@ -685,8 +685,7 @@ Handle<AccessorPair> Factory::NewAccessorPair() { ...@@ -685,8 +685,7 @@ Handle<AccessorPair> Factory::NewAccessorPair() {
} }
// Internalized strings are created in the old generation (data space). // Internalized strings are created in the old generation (data space).
Handle<String> Factory::InternalizeUtf8String( Handle<String> Factory::InternalizeUtf8String(Vector<const char> string) {
const Vector<const char>& string) {
Vector<const uint8_t> utf8_data = Vector<const uint8_t>::cast(string); Vector<const uint8_t> utf8_data = Vector<const uint8_t>::cast(string);
Utf8Decoder decoder(utf8_data); Utf8Decoder decoder(utf8_data);
if (decoder.is_ascii()) return InternalizeOneByteString(utf8_data); if (decoder.is_ascii()) return InternalizeOneByteString(utf8_data);
...@@ -702,8 +701,7 @@ Handle<String> Factory::InternalizeUtf8String( ...@@ -702,8 +701,7 @@ Handle<String> Factory::InternalizeUtf8String(
Vector<const uc16>(buffer.get(), decoder.utf16_length())); Vector<const uc16>(buffer.get(), decoder.utf16_length()));
} }
Handle<String> Factory::InternalizeOneByteString( Handle<String> Factory::InternalizeOneByteString(Vector<const uint8_t> string) {
const Vector<const uint8_t>& string) {
OneByteStringKey key(string, HashSeed(isolate())); OneByteStringKey key(string, HashSeed(isolate()));
return InternalizeStringWithKey(&key); return InternalizeStringWithKey(&key);
} }
...@@ -714,8 +712,7 @@ Handle<String> Factory::InternalizeOneByteString( ...@@ -714,8 +712,7 @@ Handle<String> Factory::InternalizeOneByteString(
return InternalizeStringWithKey(&key); return InternalizeStringWithKey(&key);
} }
Handle<String> Factory::InternalizeTwoByteString( Handle<String> Factory::InternalizeTwoByteString(Vector<const uc16> string) {
const Vector<const uc16>& string) {
TwoByteStringKey key(string, HashSeed(isolate())); TwoByteStringKey key(string, HashSeed(isolate()));
return InternalizeStringWithKey(&key); return InternalizeStringWithKey(&key);
} }
...@@ -725,8 +722,8 @@ Handle<String> Factory::InternalizeStringWithKey(StringTableKey* key) { ...@@ -725,8 +722,8 @@ Handle<String> Factory::InternalizeStringWithKey(StringTableKey* key) {
return StringTable::LookupKey(isolate(), key); return StringTable::LookupKey(isolate(), key);
} }
MaybeHandle<String> Factory::NewStringFromOneByte( MaybeHandle<String> Factory::NewStringFromOneByte(Vector<const uint8_t> string,
const Vector<const uint8_t>& string, AllocationType allocation) { AllocationType allocation) {
DCHECK_NE(allocation, AllocationType::kReadOnly); DCHECK_NE(allocation, AllocationType::kReadOnly);
int length = string.length(); int length = string.length();
if (length == 0) return empty_string(); if (length == 0) return empty_string();
...@@ -743,9 +740,9 @@ MaybeHandle<String> Factory::NewStringFromOneByte( ...@@ -743,9 +740,9 @@ MaybeHandle<String> Factory::NewStringFromOneByte(
return result; return result;
} }
MaybeHandle<String> Factory::NewStringFromUtf8(const Vector<const char>& string, MaybeHandle<String> Factory::NewStringFromUtf8(Vector<const char> data,
AllocationType allocation) { AllocationType allocation) {
Vector<const uint8_t> utf8_data = Vector<const uint8_t>::cast(string); Vector<const uint8_t> utf8_data = Vector<const uint8_t>::cast(data);
Utf8Decoder decoder(utf8_data); Utf8Decoder decoder(utf8_data);
if (decoder.utf16_length() == 0) return empty_string(); if (decoder.utf16_length() == 0) return empty_string();
...@@ -849,8 +846,8 @@ MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string, ...@@ -849,8 +846,8 @@ MaybeHandle<String> Factory::NewStringFromTwoByte(const uc16* string,
} }
} }
MaybeHandle<String> Factory::NewStringFromTwoByte( MaybeHandle<String> Factory::NewStringFromTwoByte(Vector<const uc16> string,
const Vector<const uc16>& string, AllocationType allocation) { AllocationType allocation) {
return NewStringFromTwoByte(string.begin(), string.length(), allocation); return NewStringFromTwoByte(string.begin(), string.length(), allocation);
} }
...@@ -902,7 +899,7 @@ Handle<SeqOneByteString> Factory::AllocateRawOneByteInternalizedString( ...@@ -902,7 +899,7 @@ Handle<SeqOneByteString> Factory::AllocateRawOneByteInternalizedString(
} }
Handle<String> Factory::AllocateTwoByteInternalizedString( Handle<String> Factory::AllocateTwoByteInternalizedString(
const Vector<const uc16>& str, uint32_t hash_field) { Vector<const uc16> str, uint32_t hash_field) {
CHECK_GE(String::kMaxLength, str.length()); CHECK_GE(String::kMaxLength, str.length());
DCHECK_NE(0, str.length()); // Use Heap::empty_string() instead. DCHECK_NE(0, str.length()); // Use Heap::empty_string() instead.
...@@ -961,8 +958,8 @@ Handle<String> Factory::AllocateInternalizedStringImpl(T t, int chars, ...@@ -961,8 +958,8 @@ Handle<String> Factory::AllocateInternalizedStringImpl(T t, int chars,
return answer; return answer;
} }
Handle<String> Factory::NewOneByteInternalizedString( Handle<String> Factory::NewOneByteInternalizedString(Vector<const uint8_t> str,
const Vector<const uint8_t>& str, uint32_t hash_field) { uint32_t hash_field) {
Handle<SeqOneByteString> result = Handle<SeqOneByteString> result =
AllocateRawOneByteInternalizedString(str.length(), hash_field); AllocateRawOneByteInternalizedString(str.length(), hash_field);
DisallowHeapAllocation no_allocation; DisallowHeapAllocation no_allocation;
...@@ -981,8 +978,8 @@ Handle<String> Factory::NewOneByteInternalizedSubString( ...@@ -981,8 +978,8 @@ Handle<String> Factory::NewOneByteInternalizedSubString(
return result; return result;
} }
Handle<String> Factory::NewTwoByteInternalizedString( Handle<String> Factory::NewTwoByteInternalizedString(Vector<const uc16> str,
const Vector<const uc16>& str, uint32_t hash_field) { uint32_t hash_field) {
return AllocateTwoByteInternalizedString(str, hash_field); return AllocateTwoByteInternalizedString(str, hash_field);
} }
......
...@@ -236,16 +236,16 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -236,16 +236,16 @@ class V8_EXPORT_PRIVATE Factory {
// Finds the internalized copy for string in the string table. // Finds the internalized copy for string in the string table.
// If not found, a new string is added to the table and returned. // If not found, a new string is added to the table and returned.
Handle<String> InternalizeUtf8String(const Vector<const char>& str); Handle<String> InternalizeUtf8String(Vector<const char> str);
Handle<String> InternalizeUtf8String(const char* str) { Handle<String> InternalizeUtf8String(const char* str) {
return InternalizeUtf8String(CStrVector(str)); return InternalizeUtf8String(CStrVector(str));
} }
Handle<String> InternalizeOneByteString(const Vector<const uint8_t>& str); Handle<String> InternalizeOneByteString(Vector<const uint8_t> str);
Handle<String> InternalizeOneByteString(Handle<SeqOneByteString>, int from, Handle<String> InternalizeOneByteString(Handle<SeqOneByteString>, int from,
int length); int length);
Handle<String> InternalizeTwoByteString(const Vector<const uc16>& str); Handle<String> InternalizeTwoByteString(Vector<const uc16> str);
template <class StringTableKey> template <class StringTableKey>
Handle<String> InternalizeStringWithKey(StringTableKey* key); Handle<String> InternalizeStringWithKey(StringTableKey* key);
...@@ -276,7 +276,7 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -276,7 +276,7 @@ class V8_EXPORT_PRIVATE Factory {
// //
// One-byte strings are pretenured when used as keys in the SourceCodeCache. // One-byte strings are pretenured when used as keys in the SourceCodeCache.
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromOneByte( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromOneByte(
const Vector<const uint8_t>& str, Vector<const uint8_t> str,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
template <size_t N> template <size_t N>
...@@ -297,7 +297,7 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -297,7 +297,7 @@ class V8_EXPORT_PRIVATE Factory {
// UTF8 strings are pretenured when used for regexp literal patterns and // UTF8 strings are pretenured when used for regexp literal patterns and
// flags in the parser. // flags in the parser.
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf8( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf8(
const Vector<const char>& str, Vector<const char> str,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf8SubString( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromUtf8SubString(
...@@ -305,7 +305,7 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -305,7 +305,7 @@ class V8_EXPORT_PRIVATE Factory {
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromTwoByte( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromTwoByte(
const Vector<const uc16>& str, Vector<const uc16> str,
AllocationType allocation = AllocationType::kYoung); AllocationType allocation = AllocationType::kYoung);
V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromTwoByte( V8_WARN_UNUSED_RESULT MaybeHandle<String> NewStringFromTwoByte(
...@@ -314,14 +314,14 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -314,14 +314,14 @@ class V8_EXPORT_PRIVATE Factory {
Handle<JSStringIterator> NewJSStringIterator(Handle<String> string); Handle<JSStringIterator> NewJSStringIterator(Handle<String> string);
Handle<String> NewOneByteInternalizedString(const Vector<const uint8_t>& str, Handle<String> NewOneByteInternalizedString(Vector<const uint8_t> str,
uint32_t hash_field); uint32_t hash_field);
Handle<String> NewOneByteInternalizedSubString( Handle<String> NewOneByteInternalizedSubString(
Handle<SeqOneByteString> string, int offset, int length, Handle<SeqOneByteString> string, int offset, int length,
uint32_t hash_field); uint32_t hash_field);
Handle<String> NewTwoByteInternalizedString(const Vector<const uc16>& str, Handle<String> NewTwoByteInternalizedString(Vector<const uc16> str,
uint32_t hash_field); uint32_t hash_field);
Handle<String> NewInternalizedStringImpl(Handle<String> string, int chars, Handle<String> NewInternalizedStringImpl(Handle<String> string, int chars,
...@@ -1050,8 +1050,8 @@ class V8_EXPORT_PRIVATE Factory { ...@@ -1050,8 +1050,8 @@ class V8_EXPORT_PRIVATE Factory {
Handle<SeqOneByteString> AllocateRawOneByteInternalizedString( Handle<SeqOneByteString> AllocateRawOneByteInternalizedString(
int length, uint32_t hash_field); int length, uint32_t hash_field);
Handle<String> AllocateTwoByteInternalizedString( Handle<String> AllocateTwoByteInternalizedString(Vector<const uc16> str,
const Vector<const uc16>& str, uint32_t hash_field); uint32_t hash_field);
MaybeHandle<String> NewStringFromTwoByte(const uc16* string, int length, MaybeHandle<String> NewStringFromTwoByte(const uc16* string, int length,
AllocationType allocation); AllocationType allocation);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include "src/objects/hash-table-inl.h" #include "src/objects/hash-table-inl.h"
#include "src/property-descriptor.h" #include "src/property-descriptor.h"
#include "src/string-hasher.h" #include "src/string-hasher.h"
#include "src/transitions-inl.h" #include "src/transitions.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -49,101 +49,6 @@ class VectorSegment { ...@@ -49,101 +49,6 @@ class VectorSegment {
const typename Container::size_type begin_; const typename Container::size_type begin_;
}; };
constexpr JsonToken GetOneCharToken(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_tokens[256] = {
#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(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 MayTerminateString(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 GetScanFlags(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_scan_flags[256] = {
#define CALL_GET_SCAN_FLAGS(N) GetScanFlags(N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
#define CALL_GET_SCAN_FLAGS(N) GetScanFlags(128 + N),
INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
#undef CALL_GET_SCAN_FLAGS
};
} // namespace } // namespace
MaybeHandle<Object> JsonParseInternalizer::Internalize(Isolate* isolate, MaybeHandle<Object> JsonParseInternalizer::Internalize(Isolate* isolate,
...@@ -233,15 +138,16 @@ template <typename Char> ...@@ -233,15 +138,16 @@ template <typename Char>
JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source) JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
: isolate_(isolate), : isolate_(isolate),
zone_(isolate_->allocator(), ZONE_NAME), zone_(isolate_->allocator(), ZONE_NAME),
hash_seed_(HashSeed(isolate)),
object_constructor_(isolate_->object_function()), object_constructor_(isolate_->object_function()),
original_source_(source), offset_(0),
length_(source->length()),
position_(-1),
properties_(&zone_) { properties_(&zone_) {
size_t start = 0;
size_t length = source->length();
if (source->IsSlicedString()) { if (source->IsSlicedString()) {
SlicedString string = SlicedString::cast(*source); SlicedString string = SlicedString::cast(*source);
start = string.offset(); offset_ = string.offset();
length_ += offset_;
position_ += offset_;
String parent = string.parent(); String parent = string.parent();
if (parent.IsThinString()) parent = ThinString::cast(parent).actual(); if (parent.IsThinString()) parent = ThinString::cast(parent).actual();
source_ = handle(parent, isolate); source_ = handle(parent, isolate);
...@@ -252,66 +158,17 @@ JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source) ...@@ -252,66 +158,17 @@ JsonParser<Char>::JsonParser(Isolate* isolate, Handle<String> source)
if (StringShape(*source_).IsExternal()) { if (StringShape(*source_).IsExternal()) {
chars_ = chars_ =
static_cast<const Char*>(SeqExternalString::cast(*source_)->GetChars()); static_cast<const Char*>(SeqExternalString::cast(*source_)->GetChars());
chars_may_relocate_ = false;
} else { } else {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
isolate->heap()->AddGCEpilogueCallback(UpdatePointersCallback, isolate->heap()->AddGCEpilogueCallback(UpdatePointersCallback,
v8::kGCTypeAll, this); v8::kGCTypeAll, this);
chars_ = SeqString::cast(*source_)->GetChars(no_gc); chars_ = SeqString::cast(*source_)->GetChars(no_gc);
chars_may_relocate_ = true;
} }
cursor_ = chars_ + start;
end_ = cursor_ + length;
allocation_ = (source->length() >= kPretenureTreshold) allocation_ = (source->length() >= kPretenureTreshold)
? AllocationType::kOld ? AllocationType::kOld
: AllocationType::kYoung; : AllocationType::kYoung;
} }
template <typename Char>
void JsonParser<Char>::ReportUnexpectedCharacter(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> template <typename Char>
JsonParser<Char>::~JsonParser() { JsonParser<Char>::~JsonParser() {
if (StringShape(*source_).IsExternal()) { if (StringShape(*source_).IsExternal()) {
...@@ -328,9 +185,57 @@ JsonParser<Char>::~JsonParser() { ...@@ -328,9 +185,57 @@ JsonParser<Char>::~JsonParser() {
template <typename Char> template <typename Char>
MaybeHandle<Object> JsonParser<Char>::ParseJson() { MaybeHandle<Object> JsonParser<Char>::ParseJson() {
// Advance to the first character (possibly EOS)
AdvanceSkipWhitespace();
Handle<Object> result = ParseJsonValue(); Handle<Object> result = ParseJsonValue();
if (!Check(JsonToken::EOS)) ReportUnexpectedCharacter(peek()); if (result.is_null() || c0_ != kEndOfString) {
if (isolate_->has_pending_exception()) return MaybeHandle<Object>(); // Some exception (for example stack overflow) is already pending.
if (isolate_->has_pending_exception()) return Handle<Object>::null();
// 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 (c0_) {
case kEndOfString:
message = MessageTemplate::kJsonParseUnexpectedEOS;
break;
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
message = MessageTemplate::kJsonParseUnexpectedTokenNumber;
break;
case '"':
message = MessageTemplate::kJsonParseUnexpectedTokenString;
break;
default:
message = MessageTemplate::kJsonParseUnexpectedToken;
arg2 = arg1;
arg1 = factory->LookupSingleCharacterStringFromCode(c0_);
break;
}
Handle<Script> script(factory->NewScript(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);
return isolate()->template Throw<Object>(error, &location);
}
return result; return result;
} }
...@@ -338,111 +243,163 @@ MaybeHandle<Object> InternalizeJsonProperty(Handle<JSObject> holder, ...@@ -338,111 +243,163 @@ MaybeHandle<Object> InternalizeJsonProperty(Handle<JSObject> holder,
Handle<String> key); Handle<String> key);
template <typename Char> template <typename Char>
Char JsonParser<Char>::NextCharacter() { void JsonParser<Char>::Advance() {
advance(); position_++;
if (V8_UNLIKELY(is_at_end())) return kEndOfString; if (position_ >= length_) {
return *cursor_; c0_ = kEndOfString;
} else {
c0_ = chars_[position_];
}
}
template <typename Char>
void JsonParser<Char>::AdvanceSkipWhitespace() {
do {
Advance();
} while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r');
} }
template <typename Char> template <typename Char>
void JsonParser<Char>::SkipWhitespace() { void JsonParser<Char>::SkipWhitespace() {
next_ = JsonToken::EOS; while (c0_ == ' ' || c0_ == '\t' || c0_ == '\n' || c0_ == '\r') {
Advance();
cursor_ = std::find_if(cursor_, end_, [this](Char c) { }
JsonToken current = V8_LIKELY(c <= unibrow::Latin1::kMaxChar) }
? one_char_tokens[c]
: JsonToken::ILLEGAL; template <typename Char>
bool result = current != JsonToken::WHITESPACE; uc32 JsonParser<Char>::AdvanceGetChar() {
if (result) next_ = current; Advance();
return result; return c0_;
}); }
template <typename Char>
bool JsonParser<Char>::MatchSkipWhiteSpace(uc32 c) {
if (c0_ == c) {
AdvanceSkipWhitespace();
return true;
}
return false;
}
template <typename Char>
bool JsonParser<Char>::ParseJsonString(Handle<String> expected) {
int length = expected->length();
if (source_->length() - position_ - 1 > length) {
DisallowHeapAllocation no_gc;
String::FlatContent content = expected->GetFlatContent(no_gc);
DCHECK_EQ('"', c0_);
if (content.IsOneByte()) {
const Char* input_chars = chars_ + position_ + 1;
const uint8_t* expected_chars = content.ToOneByteVector().begin();
for (int i = 0; i < length; i++) {
Char c0 = input_chars[i];
if (c0 != expected_chars[i] || c0 == '"' || c0 < 0x20 || c0 == '\\') {
return false;
}
}
if (input_chars[length] == '"') {
position_ = position_ + length + 1;
AdvanceSkipWhitespace();
return true;
}
} else {
const Char* input_chars = chars_ + position_ + 1;
const uint16_t* expected_chars = content.ToUC16Vector().begin();
for (int i = 0; i < length; i++) {
Char c0 = input_chars[i];
if (c0 != expected_chars[i] || c0 == '"' || c0 < 0x20 || c0 == '\\') {
return false;
}
}
if (input_chars[length] == '"') {
position_ = position_ + length + 1;
AdvanceSkipWhitespace();
return true;
}
}
}
return false;
} }
// Parse any JSON value. // Parse any JSON value.
template <typename Char> template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonValue() { Handle<Object> JsonParser<Char>::ParseJsonValue() {
StackLimitCheck stack_check(isolate_); StackLimitCheck stack_check(isolate_);
if (stack_check.HasOverflowed()) {
isolate_->StackOverflow();
return Handle<Object>::null();
}
if (V8_UNLIKELY(stack_check.InterruptRequested())) { if (stack_check.InterruptRequested() &&
if (stack_check.HasOverflowed()) { isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) {
if (!isolate_->has_pending_exception()) isolate_->StackOverflow(); return Handle<Object>::null();
return factory()->undefined_value(); }
}
if (isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) { if (c0_ == '"') return ParseJsonString();
return factory()->undefined_value(); if ((c0_ >= '0' && c0_ <= '9') || c0_ == '-') return ParseJsonNumber();
if (c0_ == '{') return ParseJsonObject();
if (c0_ == '[') return ParseJsonArray();
if (c0_ == 'f') {
if (AdvanceGetChar() == 'a' && AdvanceGetChar() == 'l' &&
AdvanceGetChar() == 's' && AdvanceGetChar() == 'e') {
AdvanceSkipWhitespace();
return factory()->false_value();
} }
return ReportUnexpectedCharacter();
} }
if (c0_ == 't') {
SkipWhitespace(); if (AdvanceGetChar() == 'r' && AdvanceGetChar() == 'u' &&
AdvanceGetChar() == 'e') {
switch (peek()) { AdvanceSkipWhitespace();
case JsonToken::STRING:
Consume(JsonToken::STRING);
return ParseJsonString(false);
case JsonToken::NUMBER:
return ParseJsonNumber(1, cursor_);
case JsonToken::NEGATIVE_NUMBER:
return ParseJsonNumber(-1, cursor_++);
case JsonToken::LBRACE:
return ParseJsonObject();
case JsonToken::LBRACK:
return ParseJsonArray();
case JsonToken::TRUE_LITERAL:
ScanLiteral("true");
return factory()->true_value(); return factory()->true_value();
case JsonToken::FALSE_LITERAL: }
ScanLiteral("false"); return ReportUnexpectedCharacter();
return factory()->false_value(); }
case JsonToken::NULL_LITERAL: if (c0_ == 'n') {
ScanLiteral("null"); if (AdvanceGetChar() == 'u' && AdvanceGetChar() == 'l' &&
AdvanceGetChar() == 'l') {
AdvanceSkipWhitespace();
return factory()->null_value(); return factory()->null_value();
}
case JsonToken::COLON: return ReportUnexpectedCharacter();
case JsonToken::COMMA:
case JsonToken::ILLEGAL:
case JsonToken::RBRACE:
case JsonToken::RBRACK:
case JsonToken::EOS:
ReportUnexpectedCharacter(peek());
return factory()->undefined_value();
case JsonToken::WHITESPACE:
UNREACHABLE();
} }
return ReportUnexpectedCharacter();
} }
template <typename Char> template <typename Char>
bool JsonParser<Char>::ParseElement(Handle<JSObject> json_object) { ParseElementResult JsonParser<Char>::ParseElement(
Handle<JSObject> json_object) {
uint32_t index = 0; uint32_t index = 0;
{ // Maybe an array index, try to parse it.
// |cursor_| will only be updated if the key ends up being an index. if (c0_ == '0') {
DisallowHeapAllocation no_gc; // With a leading zero, the string has to be "0" only to be an index.
const Char* cursor = cursor_; Advance();
// Maybe an array index, try to parse it. } else {
if (*cursor == '0') { do {
// With a leading zero, the string has to be "0" only to be an index. int d = c0_ - '0';
cursor++; if (index > 429496729U - ((d + 3) >> 3)) break;
} else { index = (index * 10) + d;
cursor = std::find_if(cursor, end_, [&index](Char c) { Advance();
return !TryAddIndexChar(&index, c); } while (IsDecimalDigit(c0_));
}); }
}
if (V8_UNLIKELY(cursor == end_)) { if (c0_ == '"') {
ReportUnexpectedCharacter(JsonToken::EOS); // Successfully parsed index, parse and store element.
return true; AdvanceSkipWhitespace();
}
if (*cursor++ != '"') return false; if (c0_ == ':') {
cursor_ = cursor; AdvanceSkipWhitespace();
Handle<Object> value = ParseJsonValue();
if (!value.is_null()) {
JSObject::SetOwnElementIgnoreAttributes(json_object, index, value, NONE)
.Assert();
return kElementFound;
} else {
return kNullHandle;
}
}
} }
return kElementNotFound;
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 '{'. // Parse a JSON object. Position must be right at '{'.
...@@ -454,47 +411,66 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() { ...@@ -454,47 +411,66 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() {
Handle<Map> map(json_object->map(), isolate()); Handle<Map> map(json_object->map(), isolate());
int descriptor = 0; int descriptor = 0;
VectorSegment<ZoneVector<Handle<Object>>> properties(&properties_); VectorSegment<ZoneVector<Handle<Object>>> properties(&properties_);
Consume(JsonToken::LBRACE); DCHECK_EQ(c0_, '{');
bool transitioning = true; bool transitioning = true;
if (!Check(JsonToken::RBRACE)) { AdvanceSkipWhitespace();
if (c0_ != '}') {
do { do {
ExpectNext(JsonToken::STRING); if (c0_ != '"') return ReportUnexpectedCharacter();
if (is_at_end() ||
(IsDecimalDigit(*cursor_) && ParseElement(json_object))) { int start_position = position_;
continue; Advance();
if (IsDecimalDigit(c0_)) {
ParseElementResult element_result = ParseElement(json_object);
if (element_result == kNullHandle) return Handle<Object>::null();
if (element_result == kElementFound) continue;
} }
// Not an index, fallback to the slow path.
position_ = start_position;
#ifdef DEBUG
c0_ = '"';
#endif
Handle<String> key;
Handle<Object> value;
// Try to follow existing transitions as long as possible. Once we stop // Try to follow existing transitions as long as possible. Once we stop
// transitioning, no transition can be found anymore. // transitioning, no transition can be found anymore.
DCHECK(transitioning); DCHECK(transitioning);
Handle<Map> target;
// First check whether there is a single expected transition. If so, try // First check whether there is a single expected transition. If so, try
// to parse it first. // to parse it first.
Handle<String> expected; bool follow_expected = false;
{ Handle<Map> target;
if (kIsOneByte) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
TransitionsAccessor transitions(isolate(), *map, &no_gc); TransitionsAccessor transitions(isolate(), *map, &no_gc);
expected = transitions.ExpectedTransitionKey(); key = transitions.ExpectedTransitionKey();
follow_expected = !key.is_null() && ParseJsonString(key);
// If the expected transition hits, follow it.
if (follow_expected) {
target = transitions.ExpectedTransitionTarget();
}
} }
Handle<String> key = ParseJsonString(true, expected); if (!follow_expected) {
// If the expected transition hits, follow it. // If the expected transition failed, parse an internalized string and
if (key.is_identical_to(expected)) { // try to find a matching transition.
DisallowHeapAllocation no_gc; key = ParseJsonString();
target = TransitionsAccessor(isolate(), *map, &no_gc) if (key.is_null()) return ReportUnexpectedCharacter();
.ExpectedTransitionTarget();
} else {
// If a transition was found, follow it and continue. // If a transition was found, follow it and continue.
transitioning = TransitionsAccessor(isolate(), map) transitioning = TransitionsAccessor(isolate(), map)
.FindTransitionToField(key) .FindTransitionToField(key)
.ToHandle(&target); .ToHandle(&target);
} }
if (c0_ != ':') return ReportUnexpectedCharacter();
ExpectNext(JsonToken::COLON); AdvanceSkipWhitespace();
value = ParseJsonValue();
Handle<Object> value = ParseJsonValue(); if (value.is_null()) return ReportUnexpectedCharacter();
if (transitioning) { if (transitioning) {
PropertyDetails details = PropertyDetails details =
...@@ -531,23 +507,40 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() { ...@@ -531,23 +507,40 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() {
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value) JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value)
.Check(); .Check();
} while (transitioning && Check(JsonToken::COMMA)); } while (transitioning && MatchSkipWhiteSpace(','));
// If we transitioned until the very end, transition the map now. // If we transitioned until the very end, transition the map now.
if (transitioning) { if (transitioning) {
CommitStateToJsonObject(json_object, map, properties.GetVector()); CommitStateToJsonObject(json_object, map, properties.GetVector());
} else { } else {
while (Check(JsonToken::COMMA)) { while (MatchSkipWhiteSpace(',')) {
HandleScope local_scope(isolate()); HandleScope local_scope(isolate());
ExpectNext(JsonToken::STRING); if (c0_ != '"') return ReportUnexpectedCharacter();
if (is_at_end() ||
(IsDecimalDigit(*cursor_) && ParseElement(json_object))) { int start_position = position_;
continue; Advance();
if (IsDecimalDigit(c0_)) {
ParseElementResult element_result = ParseElement(json_object);
if (element_result == kNullHandle) return Handle<Object>::null();
if (element_result == kElementFound) continue;
} }
// Not an index, fallback to the slow path.
position_ = start_position;
#ifdef DEBUG
c0_ = '"';
#endif
Handle<String> key;
Handle<Object> value;
key = ParseJsonString();
if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter();
Handle<String> key = ParseJsonString(true); AdvanceSkipWhitespace();
ExpectNext(JsonToken::COLON); value = ParseJsonValue();
Handle<Object> value = ParseJsonValue(); if (value.is_null()) return ReportUnexpectedCharacter();
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key,
value) value)
...@@ -555,15 +548,18 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() { ...@@ -555,15 +548,18 @@ Handle<Object> JsonParser<Char>::ParseJsonObject() {
} }
} }
Expect(JsonToken::RBRACE); if (c0_ != '}') {
return ReportUnexpectedCharacter();
}
} }
AdvanceSkipWhitespace();
return scope.CloseAndEscape(json_object); return scope.CloseAndEscape(json_object);
} }
template <typename Char> template <typename Char>
void JsonParser<Char>::CommitStateToJsonObject( void JsonParser<Char>::CommitStateToJsonObject(
Handle<JSObject> json_object, Handle<Map> map, Handle<JSObject> json_object, Handle<Map> map,
const Vector<const Handle<Object>>& properties) { Vector<const Handle<Object>> properties) {
JSObject::AllocateStorageForMap(json_object, map); JSObject::AllocateStorageForMap(json_object, map);
DCHECK(!json_object->map()->is_dictionary_map()); DCHECK(!json_object->map()->is_dictionary_map());
...@@ -578,10 +574,10 @@ void JsonParser<Char>::CommitStateToJsonObject( ...@@ -578,10 +574,10 @@ void JsonParser<Char>::CommitStateToJsonObject(
class ElementKindLattice { class ElementKindLattice {
private: private:
enum Kind { enum {
SMI_ELEMENTS = 0, SMI_ELEMENTS,
NUMBER_ELEMENTS = 1, NUMBER_ELEMENTS,
OBJECT_ELEMENTS = (1 << 1) | NUMBER_ELEMENTS, OBJECT_ELEMENTS,
}; };
public: public:
...@@ -591,8 +587,9 @@ class ElementKindLattice { ...@@ -591,8 +587,9 @@ class ElementKindLattice {
if (o->IsSmi()) { if (o->IsSmi()) {
return; return;
} else if (o->IsHeapNumber()) { } else if (o->IsHeapNumber()) {
value_ = static_cast<Kind>(value_ | NUMBER_ELEMENTS); if (value_ < NUMBER_ELEMENTS) value_ = NUMBER_ELEMENTS;
} else { } else {
DCHECK(!o->IsNumber());
value_ = OBJECT_ELEMENTS; value_ = OBJECT_ELEMENTS;
} }
} }
...@@ -605,30 +602,38 @@ class ElementKindLattice { ...@@ -605,30 +602,38 @@ class ElementKindLattice {
return PACKED_DOUBLE_ELEMENTS; return PACKED_DOUBLE_ELEMENTS;
case OBJECT_ELEMENTS: case OBJECT_ELEMENTS:
return PACKED_ELEMENTS; return PACKED_ELEMENTS;
default:
UNREACHABLE();
return PACKED_ELEMENTS;
} }
} }
private: private:
Kind value_; int value_;
}; };
// Parse a JSON array. Position must be right at '['. // Parse a JSON array. Position must be right at '['.
template <typename Char> template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonArray() { Handle<Object> JsonParser<Char>::ParseJsonArray() {
HandleScope scope(isolate()); HandleScope scope(isolate());
ZoneVector<Handle<Object>> elements(&zone_); ZoneVector<Handle<Object>> elements(zone());
Consume(JsonToken::LBRACK); DCHECK_EQ(c0_, '[');
ElementKindLattice lattice; ElementKindLattice lattice;
if (!Check(JsonToken::RBRACK)) { AdvanceSkipWhitespace();
if (c0_ != ']') {
do { do {
Handle<Object> element = ParseJsonValue(); Handle<Object> element = ParseJsonValue();
if (element.is_null()) return ReportUnexpectedCharacter();
elements.push_back(element); elements.push_back(element);
lattice.Update(element); lattice.Update(element);
} while (Check(JsonToken::COMMA)); } while (MatchSkipWhiteSpace(','));
Expect(JsonToken::RBRACK); if (c0_ != ']') {
return ReportUnexpectedCharacter();
}
} }
AdvanceSkipWhitespace();
// Allocate a fixed array with all the elements. // Allocate a fixed array with all the elements.
...@@ -662,309 +667,343 @@ Handle<Object> JsonParser<Char>::ParseJsonArray() { ...@@ -662,309 +667,343 @@ Handle<Object> JsonParser<Char>::ParseJsonArray() {
} }
template <typename Char> template <typename Char>
Handle<Object> JsonParser<Char>::ParseJsonNumber(int sign, const Char* start) { Handle<Object> JsonParser<Char>::ParseJsonNumber() {
bool negative = false;
int beg_pos = position_;
if (c0_ == '-') {
Advance();
negative = true;
}
if (c0_ == '0') {
Advance();
// Prefix zero is only allowed if it's the only digit before
// a decimal point or exponent.
if (IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
} else {
uint32_t i = 0;
int digits = 0;
if (c0_ < '1' || c0_ > '9') return ReportUnexpectedCharacter();
do {
// This can overflow. That's OK, the "digits < 10" check below
// will discard overflown results.
i = i * 10 + c0_ - '0';
digits++;
Advance();
} while (IsDecimalDigit(c0_));
if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) {
SkipWhitespace();
return Handle<Smi>(Smi::FromInt((negative ? -static_cast<int>(i) : i)),
isolate());
}
}
if (c0_ == '.') {
Advance();
if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
do {
Advance();
} while (IsDecimalDigit(c0_));
}
if (AsciiAlphaToLower(c0_) == 'e') {
Advance();
if (c0_ == '-' || c0_ == '+') Advance();
if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
do {
Advance();
} while (IsDecimalDigit(c0_));
}
int length = position_ - beg_pos;
double number; double number;
if (kIsOneByte) {
{
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
Vector<const Char> chars(chars_ + beg_pos, length);
if (*cursor_ == '0') { number = StringToDouble(Vector<const uint8_t>::cast(chars),
// Prefix zero is only allowed if it's the only digit before
// a decimal point or exponent.
Char c = NextCharacter();
if (c <= unibrow::Latin1::kMaxChar &&
IsNumberPart(character_scan_flags[c])) {
if (V8_UNLIKELY(IsDecimalDigit(c))) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter();
return handle(Smi::FromInt(0), isolate_);
}
} else if (sign > 0) {
return handle(Smi::FromInt(0), isolate_);
}
} else {
int32_t i = 0;
int digits = 0;
const Char* start = cursor_;
const int kMaxSmiLength = 9;
cursor_ = std::find_if(cursor_, Min(end_, cursor_ + kMaxSmiLength),
[&i, &digits](Char c) {
if (!IsDecimalDigit(c)) return true;
i = i * 10 + (c - '0');
digits++;
return false;
});
if (V8_UNLIKELY(cursor_ == start)) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter();
return handle(Smi::FromInt(0), isolate_);
}
if (is_at_end() || *cursor_ > unibrow::Latin1::kMaxChar ||
!IsNumberPart(character_scan_flags[*cursor_])) {
// Smi.
// TODO(verwaest): Cache?
return handle(Smi::FromInt(i * sign), isolate_);
}
}
cursor_ = std::find_if(cursor_, end_, [](Char c) {
return !(c <= unibrow::Latin1::kMaxChar &&
IsNumberPart(character_scan_flags[c])) ||
c == '.';
});
// If we found a period, ensure that it's followed by a decimal digit.
if (!is_at_end() && *cursor_ == '.') {
advance();
if (is_at_end()) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter(JsonToken::EOS);
return handle(Smi::FromInt(0), isolate_);
} else if (!IsDecimalDigit(*cursor_)) {
AllowHeapAllocation allow_before_exception;
ReportUnexpectedCharacter();
return handle(Smi::FromInt(0), isolate_);
} else {
cursor_ = std::find_if(cursor_, end_, [](Char c) {
return !(c <= unibrow::Latin1::kMaxChar &&
IsNumberPart(character_scan_flags[c]));
});
}
}
Vector<const uint8_t> chars;
if (kIsOneByte) {
chars = Vector<const uint8_t>::cast(
Vector<const Char>(start, cursor_ - start));
} else {
literal_buffer_.Start();
while (start++ != cursor_) {
literal_buffer_.AddChar(*start++);
}
chars = literal_buffer_.one_byte_literal();
}
number = StringToDouble(chars,
NO_FLAGS, // Hex, octal or trailing junk. NO_FLAGS, // Hex, octal or trailing junk.
std::numeric_limits<double>::quiet_NaN()); std::numeric_limits<double>::quiet_NaN());
} else {
Vector<uint8_t> buffer = Vector<uint8_t>::New(length);
String::WriteToFlat(*source_, buffer.begin(), beg_pos, position_);
Vector<const uint8_t> result =
Vector<const uint8_t>(buffer.begin(), length);
number = StringToDouble(result,
NO_FLAGS, // Hex, octal or trailing junk.
0.0);
buffer.Dispose();
} }
SkipWhitespace();
if (V8_UNLIKELY(std::isnan(number))) ReportUnexpectedCharacter();
return factory()->NewNumber(number, allocation_); return factory()->NewNumber(number, allocation_);
} }
namespace { template <typename StringType>
inline void SeqStringSet(Handle<StringType> seq_str, int i, uc32 c);
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 template <>
// byte encoded as two-byte will return false here). inline void SeqStringSet(Handle<SeqTwoByteString> seq_str, int i, uc32 c) {
if ((sizeof(Char) == 1) != string->IsOneByteRepresentation()) return false; seq_str->SeqTwoByteStringSet(i, c);
if (chars.length() != string->length()) return false; }
DisallowHeapAllocation no_gc; template <>
const Char* string_data = string->GetChars<Char>(no_gc); inline void SeqStringSet(Handle<SeqOneByteString> seq_str, int i, uc32 c) {
return CompareChars(chars.begin(), string_data, chars.length()) == 0; seq_str->SeqOneByteStringSet(i, c);
} }
} // namespace template <typename StringType>
inline Handle<StringType> NewRawString(Factory* factory, int length,
AllocationType allocation);
template <typename Char> template <>
Handle<String> JsonParser<Char>::MakeString(bool requires_internalization, inline Handle<SeqTwoByteString> NewRawString(Factory* factory, int length,
int offset, int length) { AllocationType allocation) {
AllowHeapAllocation allow_gc; return factory->NewRawTwoByteString(length, allocation).ToHandleChecked();
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 <>
template <typename LiteralChar> inline Handle<SeqOneByteString> NewRawString(Factory* factory, int length,
Handle<String> JsonParser<Char>::MakeString( AllocationType allocation) {
bool requires_internalization, const Vector<const LiteralChar>& chars) { return factory->NewRawOneByteString(length, allocation).ToHandleChecked();
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),
allocation_)
.ToHandleChecked();
}
return factory()
->NewStringFromTwoByte(Vector<const uint16_t>::cast(chars), allocation_)
.ToHandleChecked();
}
SequentialStringKey<LiteralChar> key(chars, hash_seed_);
return StringTable::LookupKey(isolate_, &key);
} }
// Scans the rest of a JSON string starting from position_ and writes
// prefix[start..end] along with the scanned characters into a
// sequential string of type StringType.
template <typename Char> template <typename Char>
Handle<String> JsonParser<Char>::ParseJsonString(bool requires_internalization, template <typename StringType, typename SinkChar>
Handle<String> hint) { Handle<String> JsonParser<Char>::SlowScanJsonString(Handle<String> prefix,
// First try to fast scan without buffering in case the string doesn't have int start, int end) {
// escaped sequences. Always buffer two-byte input strings as the scanned int count = end - start;
// substring can be one-byte. int max_length = count + length_ - position_;
if (kIsOneByte) { int length = Min(max_length, Max(kInitialSpecialStringLength, 2 * count));
DisallowHeapAllocation no_gc; Handle<StringType> seq_string =
const Char* start = cursor_; NewRawString<StringType>(factory(), length, allocation_);
while (true) {
cursor_ = std::find_if(cursor_, end_, [](Char c) {
return MayTerminateString(character_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; DisallowHeapAllocation no_gc;
ReportUnexpectedCharacter(); // Copy prefix into seq_str.
return factory()->empty_string(); SinkChar* dest = seq_string->GetChars(no_gc);
} String::WriteToFlat(*prefix, dest, start, end);
// 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) { while (c0_ != '"') {
cursor_ = std::find_if(cursor_, end_, [this](Char c) { // Check for control character (0x00-0x1F) or unterminated string (<0).
if (V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) { if (c0_ < 0x20) return Handle<String>::null();
AddLiteralChar(c); if (count >= length) {
return false; // We need to create a longer sequential string for the result.
} return SlowScanJsonString<StringType, SinkChar>(seq_string, 0, count);
if (MayTerminateString(character_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 (c0_ != '\\') {
if (*cursor_ == '\\') { // If the sink can contain UC16 characters, or source_ contains only
uc32 c = NextCharacter(); // Latin1 characters, there's no need to test whether we can store the
if (V8_UNLIKELY(c > unibrow::Latin1::kMaxChar)) { // character. Otherwise check whether the UC16 source character can fit
ReportUnexpectedCharacter(); // in the Latin1 sink.
return factory()->empty_string(); if (sizeof(SinkChar) == kUC16Size || kIsOneByte ||
c0_ <= String::kMaxOneByteCharCode) {
SeqStringSet(seq_string, count++, c0_);
Advance();
} else {
// StringType is SeqOneByteString and we just read a non-Latin1 char.
return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string, 0, count);
} }
} else {
uc32 value; Advance(); // Advance past the \.
switch (c0_) {
switch (GetEscapeKind(character_scan_flags[c])) { case '"':
case EscapeKind::kSelf: case '\\':
value = c; case '/':
SeqStringSet(seq_string, count++, c0_);
break; break;
case 'b':
case EscapeKind::kBackspace: SeqStringSet(seq_string, count++, '\x08');
value = '\x08';
break; break;
case 'f':
case EscapeKind::kTab: SeqStringSet(seq_string, count++, '\x0C');
value = '\x09';
break; break;
case 'n':
case EscapeKind::kNewLine: SeqStringSet(seq_string, count++, '\x0A');
value = '\x0A';
break; break;
case 'r':
case EscapeKind::kFormFeed: SeqStringSet(seq_string, count++, '\x0D');
value = '\x0C';
break; break;
case 't':
case EscapeKind::kCarriageReturn: SeqStringSet(seq_string, count++, '\x09');
value = '\x0D';
break; break;
case 'u': {
case EscapeKind::kUnicode: { uc32 value = 0;
value = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
int digit = HexValue(NextCharacter()); Advance();
if (V8_UNLIKELY(digit < 0)) { int digit = HexValue(c0_);
ReportUnexpectedCharacter(); if (digit < 0) {
return factory()->empty_string(); return Handle<String>::null();
} }
value = value * 16 + digit; value = value * 16 + digit;
} }
break; if (sizeof(SinkChar) == kUC16Size ||
value <= String::kMaxOneByteCharCode) {
SeqStringSet(seq_string, count++, value);
break;
} else {
// StringType is SeqOneByteString and we just read a non-Latin1
// char.
position_ -= 6; // Rewind position_ to \ in \uxxxx.
Advance();
return SlowScanJsonString<SeqTwoByteString, uc16>(seq_string, 0,
count);
}
} }
default:
return Handle<String>::null();
}
Advance();
}
}
DCHECK_EQ('"', c0_);
// Advance past the last '"'.
AdvanceSkipWhitespace();
// Shrink seq_string length to count and return.
return SeqString::Truncate(seq_string, count);
}
template <typename Char>
Handle<String> JsonParser<Char>::ScanJsonString() {
DCHECK_EQ('"', c0_);
Advance();
if (c0_ == '"') {
AdvanceSkipWhitespace();
return factory()->empty_string();
}
if (kIsOneByte) {
// Fast path for existing internalized strings. If the the string being
// parsed is not a known internalized string, contains backslashes or
// unexpectedly reaches the end of string, return with an empty handle.
case EscapeKind::kIllegal: // We intentionally use local variables instead of fields, compute hash
ReportUnexpectedCharacter(); // while we are iterating a string and manually inline StringTable lookup
return factory()->empty_string(); // here.
int position = position_;
uc32 c0 = c0_;
uint32_t running_hash = static_cast<uint32_t>(HashSeed(isolate()));
uint32_t index = 0;
bool is_array_index = true;
do {
if (c0 == '\\') {
c0_ = c0;
int beg_pos = position_;
position_ = position;
return SlowScanJsonString<SeqOneByteString, uint8_t>(source_, beg_pos,
position_);
}
if (c0 < 0x20) {
c0_ = c0;
position_ = position;
return Handle<String>::null();
}
if (is_array_index) {
// With leading zero, the string has to be "0" to be a valid index.
if (!IsDecimalDigit(c0) || (position > position_ && index == 0)) {
is_array_index = false;
} else {
int d = c0 - '0';
is_array_index = index <= 429496729U - ((d + 3) >> 3);
index = (index * 10) + d;
}
}
running_hash = StringHasher::AddCharacterCore(running_hash,
static_cast<uint16_t>(c0));
position++;
if (position >= length_) {
c0_ = kEndOfString;
position_ = position;
return Handle<String>::null();
} }
c0 = chars_[position];
} while (c0 != '"');
int length = position - position_;
uint32_t hash;
if (is_array_index) {
hash =
StringHasher::MakeArrayIndexHash(index, length) >> String::kHashShift;
} else if (length <= String::kMaxHashCalcLength) {
hash = StringHasher::GetHashCore(running_hash);
} else {
hash = static_cast<uint32_t>(length);
}
StringTable string_table = isolate()->heap()->string_table();
uint32_t capacity = string_table->Capacity();
uint32_t entry = StringTable::FirstProbe(hash, capacity);
uint32_t count = 1;
Handle<String> result;
while (true) {
Object element = string_table->KeyAt(entry);
if (element->IsUndefined(isolate())) {
// Lookup failure.
result = Internalize(position_, length);
break;
}
if (!element->IsTheHole(isolate())) {
DisallowHeapAllocation no_gc;
Vector<const Char> string_vector(chars_ + position_, length);
if (String::cast(element)->IsOneByteEqualTo(
Vector<const uint8_t>::cast(string_vector))) {
result = Handle<String>(String::cast(element), isolate());
DCHECK_EQ(result->Hash(),
(hash << String::kHashShift) >> String::kHashShift);
break;
}
}
entry = StringTable::NextProbe(entry, count++, capacity);
}
position_ = position;
// Advance past the last '"'.
AdvanceSkipWhitespace();
return result;
}
AddLiteralChar(value); int beg_pos = position_;
advance(); // Fast case for Latin1 only without escape characters.
continue; do {
// Check for control character (0x00-0x1F) or unterminated string (<0).
if (c0_ < 0x20) return Handle<String>::null();
if (c0_ != '\\') {
if (kIsOneByte || c0_ <= String::kMaxOneByteCharCode) {
Advance();
} else {
return SlowScanJsonString<SeqTwoByteString, uc16>(source_, beg_pos,
position_);
}
} else {
return SlowScanJsonString<SeqOneByteString, uint8_t>(source_, beg_pos,
position_);
} }
} while (c0_ != '"');
int length = position_ - beg_pos;
Handle<String> result =
factory()->NewRawOneByteString(length, allocation_).ToHandleChecked();
DisallowHeapAllocation no_gc;
uint8_t* dest = SeqOneByteString::cast(*result)->GetChars(no_gc);
String::WriteToFlat(*source_, dest, beg_pos, position_);
DCHECK_LT(*cursor_, 0x20); DCHECK_EQ('"', c0_);
AllowHeapAllocation allow_before_exception; // Advance past the last '"'.
ReportUnexpectedCharacter(); AdvanceSkipWhitespace();
return factory()->empty_string(); return result;
}
template <>
Handle<String> JsonParser<uint8_t>::Internalize(int start, int length) {
if (StringShape(*source_).IsExternal()) {
return factory()->InternalizeOneByteString(
Vector<const uint8_t>(chars_ + start, length));
} }
Handle<SeqString> seq = Handle<SeqString>::cast(source_);
return factory()->InternalizeOneByteString(seq, start, length);
}
ReportUnexpectedCharacter(JsonToken::EOS); template <>
return factory()->empty_string(); Handle<String> JsonParser<uint16_t>::Internalize(int start, int length) {
UNREACHABLE();
} }
// Explicit instantiation. // Explicit instantiation.
......
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
#include "src/heap/factory.h" #include "src/heap/factory.h"
#include "src/isolate.h" #include "src/isolate.h"
#include "src/objects.h" #include "src/objects.h"
#include "src/parsing/literal-buffer.h"
#include "src/zone/zone-containers.h" #include "src/zone/zone-containers.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
enum ParseElementResult { kElementFound, kElementNotFound }; enum ParseElementResult { kElementFound, kElementNotFound, kNullHandle };
class JsonParseInternalizer { class JsonParseInternalizer {
public: public:
...@@ -35,24 +34,6 @@ class JsonParseInternalizer { ...@@ -35,24 +34,6 @@ class JsonParseInternalizer {
Handle<JSReceiver> reviver_; Handle<JSReceiver> reviver_;
}; };
enum class JsonToken : uint8_t {
NUMBER,
NEGATIVE_NUMBER,
STRING,
LBRACE,
RBRACE,
LBRACK,
RBRACK,
TRUE_LITERAL,
FALSE_LITERAL,
NULL_LITERAL,
WHITESPACE,
COLON,
COMMA,
ILLEGAL,
EOS
};
// A simple json parser. // A simple json parser.
template <typename Char> template <typename Char>
class JsonParser final { class JsonParser final {
...@@ -74,12 +55,7 @@ class JsonParser final { ...@@ -74,12 +55,7 @@ class JsonParser final {
static const int kEndOfString = -1; static const int kEndOfString = -1;
private: private:
template <typename LiteralChar> Handle<String> Internalize(int start, int length);
Handle<String> MakeString(bool requires_internalization,
const Vector<const LiteralChar>& chars);
Handle<String> MakeString(bool requires_internalization, int offset,
int length);
JsonParser(Isolate* isolate, Handle<String> source); JsonParser(Isolate* isolate, Handle<String> source);
~JsonParser(); ~JsonParser();
...@@ -87,67 +63,38 @@ class JsonParser final { ...@@ -87,67 +63,38 @@ class JsonParser final {
// Parse a string containing a single JSON value. // Parse a string containing a single JSON value.
MaybeHandle<Object> ParseJson(); MaybeHandle<Object> ParseJson();
void advance() { ++cursor_; } V8_INLINE void Advance();
Char NextCharacter();
V8_INLINE JsonToken peek() const { return next_; }
void Consume(JsonToken token) {
DCHECK_EQ(peek(), token);
advance();
}
void Expect(JsonToken token) {
if (V8_LIKELY(peek() == token)) {
advance();
} else {
ReportUnexpectedCharacter(peek());
}
}
void ExpectNext(JsonToken token) {
SkipWhitespace();
Expect(token);
}
bool Check(JsonToken token) {
SkipWhitespace();
if (next_ != token) return false;
advance();
return true;
}
template <size_t N>
void ScanLiteral(const char (&s)[N]) {
DCHECK(!is_at_end());
if (V8_UNLIKELY(static_cast<size_t>(end_ - cursor_) < N - 1)) {
ReportUnexpectedCharacter(JsonToken::EOS);
return;
}
// There's at least 1 character, we always consume a character and compare
// the next character. The first character was compared before we jumped to
// ScanLiteral.
STATIC_ASSERT(N > 2);
if (V8_LIKELY(CompareChars(s + 1, cursor_ + 1, N - 2) == 0)) {
cursor_ += N - 1;
} else {
ReportUnexpectedCharacter();
}
}
// The JSON lexical grammar is specified in the ECMAScript 5 standard, // The JSON lexical grammar is specified in the ECMAScript 5 standard,
// section 15.12.1.1. The only allowed whitespace characters between tokens // section 15.12.1.1. The only allowed whitespace characters between tokens
// are tab, carriage-return, newline and space. // are tab, carriage-return, newline and space.
void SkipWhitespace();
V8_INLINE void AdvanceSkipWhitespace();
V8_INLINE void SkipWhitespace();
V8_INLINE uc32 AdvanceGetChar();
// Checks that current charater is c.
// If so, then consume c and skip whitespace.
V8_INLINE bool MatchSkipWhiteSpace(uc32 c);
// A JSON string (production JSONString) is subset of valid JavaScript string // A JSON string (production JSONString) is subset of valid JavaScript string
// literals. The string must only be double-quoted (not single-quoted), and // literals. The string must only be double-quoted (not single-quoted), and
// the only allowed backslash-escapes are ", /, \, b, f, n, r, t and // the only allowed backslash-escapes are ", /, \, b, f, n, r, t and
// four-digit hex escapes (uXXXX). Any other use of backslashes is invalid. // four-digit hex escapes (uXXXX). Any other use of backslashes is invalid.
Handle<String> ParseJsonString(bool requires_internalization, bool ParseJsonString(Handle<String> expected);
Handle<String> expected = Handle<String>());
Handle<String> ParseJsonString() {
Handle<String> result = ScanJsonString();
if (result.is_null()) return result;
return factory()->InternalizeString(result);
}
Handle<String> ScanJsonString(); Handle<String> ScanJsonString();
// Creates a new string and copies prefix[start..end] into the beginning
// of it. Then scans the rest of the string, adding characters after the
// prefix. Called by ScanJsonString when reaching a '\' or non-Latin1 char.
template <typename StringType, typename SinkChar>
Handle<String> SlowScanJsonString(Handle<String> prefix, int start, int end);
// A JSON number (production JSONNumber) is a subset of the valid JavaScript // A JSON number (production JSONNumber) is a subset of the valid JavaScript
// decimal number literals. // decimal number literals.
...@@ -155,7 +102,7 @@ class JsonParser final { ...@@ -155,7 +102,7 @@ class JsonParser final {
// digit before and after a decimal point, may not have prefixed zeros (unless // digit before and after a decimal point, may not have prefixed zeros (unless
// the integer part is zero), and may include an exponent part (e.g., "e-10"). // the integer part is zero), and may include an exponent part (e.g., "e-10").
// Hexadecimal and octal numbers are not allowed. // Hexadecimal and octal numbers are not allowed.
Handle<Object> ParseJsonNumber(int sign, const Char* start); Handle<Object> ParseJsonNumber();
// Parse a single JSON value from input (grammar production JSONValue). // Parse a single JSON value from input (grammar production JSONValue).
// A JSON value is either a (double-quoted) string literal, a number literal, // A JSON value is either a (double-quoted) string literal, a number literal,
...@@ -171,9 +118,8 @@ class JsonParser final { ...@@ -171,9 +118,8 @@ class JsonParser final {
Handle<Object> ParseJsonObject(); Handle<Object> ParseJsonObject();
// Helper for ParseJsonObject. Parses the form "123": obj, which is recorded // Helper for ParseJsonObject. Parses the form "123": obj, which is recorded
// as an element, not a property. Returns false if we should retry parsing the // as an element, not a property.
// key as a non-element. (Returns true if it's an index or hits EOS). ParseElementResult ParseElement(Handle<JSObject> json_object);
bool ParseElement(Handle<JSObject> json_object);
// Parses a JSON array literal (grammar production JSONArray). An array // Parses a JSON array literal (grammar production JSONArray). An array
// literal is a square-bracketed and comma separated sequence (possibly empty) // literal is a square-bracketed and comma separated sequence (possibly empty)
...@@ -182,8 +128,12 @@ class JsonParser final { ...@@ -182,8 +128,12 @@ class JsonParser final {
// it allow a terminal comma, like a JavaScript array does. // it allow a terminal comma, like a JavaScript array does.
Handle<Object> ParseJsonArray(); Handle<Object> ParseJsonArray();
// Mark that a parsing error has happened at the current character.
void ReportUnexpectedCharacter(JsonToken token = JsonToken::ILLEGAL); // Mark that a parsing error has happened at the current token, and
// return a null handle. Primarily for readability.
inline Handle<Object> ReportUnexpectedCharacter() {
return Handle<Object>::null();
}
inline Isolate* isolate() { return isolate_; } inline Isolate* isolate() { return isolate_; }
inline Factory* factory() { return isolate_->factory(); } inline Factory* factory() { return isolate_->factory(); }
...@@ -201,58 +151,48 @@ class JsonParser final { ...@@ -201,58 +151,48 @@ class JsonParser final {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
const Char* chars = Handle<SeqString>::cast(source_)->GetChars(no_gc); const Char* chars = Handle<SeqString>::cast(source_)->GetChars(no_gc);
if (chars_ != chars) { if (chars_ != chars) {
size_t position = cursor_ - chars_;
size_t length = end_ - chars_;
chars_ = chars; chars_ = chars;
cursor_ = chars_ + position;
end_ = chars_ + length;
} }
} }
private: private:
static const bool kIsOneByte = sizeof(Char) == 1; static const bool kIsOneByte = sizeof(Char) == 1;
static const int kMaxInternalizedStringValueLength = 25;
// Casts |c| to uc32 avoiding LiteralBuffer::AddChar(char) in one-byte-strings Zone* zone() { return &zone_; }
// with escapes that can result in two-byte strings.
void AddLiteralChar(uc32 c) { literal_buffer_.AddChar(c); }
void CommitStateToJsonObject(Handle<JSObject> json_object, Handle<Map> map, void CommitStateToJsonObject(Handle<JSObject> json_object, Handle<Map> map,
const Vector<const Handle<Object>>& properties); Vector<const Handle<Object>> properties);
bool is_at_end() const {
DCHECK_LE(cursor_, end_);
return cursor_ == end_;
}
int position() const { return static_cast<int>(cursor_ - chars_); }
Isolate* isolate_; Isolate* isolate_;
Zone zone_; Zone zone_;
const uint64_t hash_seed_;
AllocationType allocation_; AllocationType allocation_;
Handle<JSFunction> object_constructor_; Handle<JSFunction> object_constructor_;
const Handle<String> original_source_;
Handle<String> source_; Handle<String> source_;
int offset_;
int length_;
// Cached pointer to the raw chars in source. In case source is on-heap, we // Cached pointer to the raw chars in source. In case source is on-heap, we
// register an UpdatePointers callback. For this reason, chars_, cursor_ and // register an UpdatePointers callback. For this reason, chars_ should never
// end_ should never be locally cached across a possible allocation. The scope // be locally cached across a possible allocation. The scope in which we
// in which we cache chars has to be guarded by a DisallowHeapAllocation // cache chars has to be guarded by a DisallowHeapAllocation scope.
// scope. // TODO(verwaest): Move chars_ and functions that operate over chars to a
// separate helper class that makes it clear that all functions need to be
// guarded.
const Char* chars_; const Char* chars_;
const Char* cursor_;
const Char* end_;
JsonToken next_; uc32 c0_;
LiteralBuffer literal_buffer_; int position_;
// Indicates whether the bytes underneath source_ can relocate during GC.
bool chars_may_relocate_;
// Property handles are stored here inside ParseJsonObject. // Property handles are stored here inside ParseJsonObject.
ZoneVector<Handle<Object>> properties_; ZoneVector<Handle<Object>> properties_;
}; };
template <>
Handle<String> JsonParser<uint8_t>::Internalize(int start, int length);
template <>
Handle<String> JsonParser<uint16_t>::Internalize(int start, int length);
// Explicit instantiation declarations. // Explicit instantiation declarations.
extern template class JsonParser<uint8_t>; extern template class JsonParser<uint8_t>;
extern template class JsonParser<uint16_t>; extern template class JsonParser<uint16_t>;
......
...@@ -6477,21 +6477,23 @@ class RegExpKey : public HashTableKey { ...@@ -6477,21 +6477,23 @@ class RegExpKey : public HashTableKey {
Smi flags_; Smi flags_;
}; };
Handle<String> OneByteStringKey::AsHandle(Isolate* isolate) {
return isolate->factory()->NewOneByteInternalizedString(string_, HashField());
}
Handle<String> TwoByteStringKey::AsHandle(Isolate* isolate) {
return isolate->factory()->NewTwoByteInternalizedString(string_, HashField());
}
Handle<String> SeqOneByteSubStringKey::AsHandle(Isolate* isolate) { Handle<String> SeqOneByteSubStringKey::AsHandle(Isolate* isolate) {
return isolate->factory()->NewOneByteInternalizedSubString( return isolate->factory()->NewOneByteInternalizedSubString(
string_, from_, length_, HashField()); string_, from_, length_, HashField());
} }
bool SeqOneByteSubStringKey::IsMatch(Object object) { bool SeqOneByteSubStringKey::IsMatch(Object string) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String string = String::cast(object); Vector<const uint8_t> chars(string_->GetChars(no_gc) + from_, length_);
if (string.length() != length_) return false; return String::cast(string)->IsOneByteEqualTo(chars);
if (string.IsOneByteRepresentation()) {
const uint8_t* data = string.GetChars<uint8_t>(no_gc);
return CompareChars(string_->GetChars(no_gc) + from_, data, length_) == 0;
}
const uint16_t* data = string.GetChars<uint16_t>(no_gc);
return CompareChars(string_->GetChars(no_gc) + from_, data, length_) == 0;
} }
// InternalizedStringKey carries a string/internalized-string object as key. // InternalizedStringKey carries a string/internalized-string object as key.
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include "src/objects/shared-function-info.h" #include "src/objects/shared-function-info.h"
#include "src/objects/templates-inl.h" #include "src/objects/templates-inl.h"
#include "src/property.h" #include "src/property.h"
#include "src/transitions-inl.h" #include "src/transitions.h"
// Has to be the last include (doesn't have include guards): // Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
......
...@@ -195,45 +195,29 @@ Char FlatStringReader::Get(int index) { ...@@ -195,45 +195,29 @@ Char FlatStringReader::Get(int index) {
} }
template <typename Char> template <typename Char>
class SequentialStringKey final : public StringTableKey { class SequentialStringKey : public StringTableKey {
public: public:
SequentialStringKey(const Vector<const Char>& chars, uint64_t seed) explicit SequentialStringKey(Vector<const Char> string, uint64_t seed)
: SequentialStringKey(StringHasher::HashSequentialString<Char>( : StringTableKey(StringHasher::HashSequentialString<Char>(
chars.begin(), chars.length(), seed), string.begin(), string.length(), seed)),
chars) {} string_(string) {}
SequentialStringKey(int hash, const Vector<const Char>& chars) Vector<const Char> string_;
: StringTableKey(hash), chars_(chars) {} };
bool IsMatch(Object other) override { class OneByteStringKey : public SequentialStringKey<uint8_t> {
DisallowHeapAllocation no_gc; public:
String s = String::cast(other); OneByteStringKey(Vector<const uint8_t> str, uint64_t seed)
if (s.length() != chars_.length()) return false; : SequentialStringKey<uint8_t>(str, seed) {}
if (s->IsOneByteRepresentation()) {
const uint8_t* chars = s.GetChars<uint8_t>(no_gc);
return CompareChars(chars, chars_.begin(), chars_.length()) == 0;
}
const uint16_t* chars = s.GetChars<uint16_t>(no_gc);
return CompareChars(chars, chars_.begin(), chars_.length()) == 0;
}
Handle<String> AsHandle(Isolate* isolate) override { bool IsMatch(Object string) override {
if (sizeof(Char) == 1) { return String::cast(string)->IsOneByteEqualTo(string_);
return isolate->factory()->NewOneByteInternalizedString(
Vector<const uint8_t>::cast(chars_), HashField());
}
return isolate->factory()->NewTwoByteInternalizedString(
Vector<const uint16_t>::cast(chars_), HashField());
} }
private: Handle<String> AsHandle(Isolate* isolate) override;
Vector<const Char> chars_;
}; };
using OneByteStringKey = SequentialStringKey<uint8_t>; class SeqOneByteSubStringKey : public StringTableKey {
using TwoByteStringKey = SequentialStringKey<uint16_t>;
class SeqOneByteSubStringKey final : public StringTableKey {
public: public:
// VS 2017 on official builds gives this spurious warning: // VS 2017 on official builds gives this spurious warning:
// warning C4789: buffer 'key' of size 16 bytes will be overrun; 4 bytes will // warning C4789: buffer 'key' of size 16 bytes will be overrun; 4 bytes will
...@@ -269,6 +253,18 @@ class SeqOneByteSubStringKey final : public StringTableKey { ...@@ -269,6 +253,18 @@ class SeqOneByteSubStringKey final : public StringTableKey {
int length_; int length_;
}; };
class TwoByteStringKey : public SequentialStringKey<uc16> {
public:
explicit TwoByteStringKey(Vector<const uc16> str, uint64_t seed)
: SequentialStringKey<uc16>(str, seed) {}
bool IsMatch(Object string) override {
return String::cast(string)->IsTwoByteEqualTo(string_);
}
Handle<String> AsHandle(Isolate* isolate) override;
};
bool String::Equals(String other) { bool String::Equals(String other) {
if (other == *this) return true; if (other == *this) return true;
if (this->IsInternalizedString() && other->IsInternalizedString()) { if (this->IsInternalizedString() && other->IsInternalizedString()) {
...@@ -285,13 +281,6 @@ bool String::Equals(Isolate* isolate, Handle<String> one, Handle<String> two) { ...@@ -285,13 +281,6 @@ bool String::Equals(Isolate* isolate, Handle<String> one, Handle<String> two) {
return SlowEquals(isolate, one, two); return SlowEquals(isolate, one, two);
} }
template <typename Char>
const Char* String::GetChars(const DisallowHeapAllocation& no_gc) {
return StringShape(*this).IsExternal()
? CharTraits<Char>::ExternalString::cast(*this).GetChars()
: CharTraits<Char>::String::cast(*this).GetChars(no_gc);
}
Handle<String> String::Flatten(Isolate* isolate, Handle<String> string, Handle<String> String::Flatten(Isolate* isolate, Handle<String> string,
AllocationType allocation) { AllocationType allocation) {
if (string->IsConsString()) { if (string->IsConsString()) {
......
...@@ -146,10 +146,6 @@ class String : public Name { ...@@ -146,10 +146,6 @@ class String : public Name {
V8_INLINE Vector<const Char> GetCharVector( V8_INLINE Vector<const Char> GetCharVector(
const DisallowHeapAllocation& no_gc); const DisallowHeapAllocation& no_gc);
// Get chars from sequential or external strings.
template <typename Char>
inline const Char* GetChars(const DisallowHeapAllocation& no_gc);
// Get and set the length of the string. // Get and set the length of the string.
inline int length() const; inline int length() const;
inline void set_length(int value); inline void set_length(int value);
......
// Copyright 2019 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/parsing/literal-buffer.h"
#include "src/heap/factory.h"
#include "src/isolate.h"
#include "src/memcopy.h"
namespace v8 {
namespace internal {
Handle<String> LiteralBuffer::Internalize(Isolate* isolate) const {
if (is_one_byte()) {
return isolate->factory()->InternalizeOneByteString(one_byte_literal());
}
return isolate->factory()->InternalizeTwoByteString(two_byte_literal());
}
int LiteralBuffer::NewCapacity(int min_capacity) {
return min_capacity < (kMaxGrowth / (kGrowthFactor - 1))
? min_capacity * kGrowthFactor
: min_capacity + kMaxGrowth;
}
void LiteralBuffer::ExpandBuffer() {
int min_capacity = Max(kInitialCapacity, backing_store_.length());
Vector<byte> new_store = Vector<byte>::New(NewCapacity(min_capacity));
if (position_ > 0) {
MemCopy(new_store.begin(), backing_store_.begin(), position_);
}
backing_store_.Dispose();
backing_store_ = new_store;
}
void LiteralBuffer::ConvertToTwoByte() {
DCHECK(is_one_byte());
Vector<byte> new_store;
int new_content_size = position_ * kUC16Size;
if (new_content_size >= backing_store_.length()) {
// Ensure room for all currently read code units as UC16 as well
// as the code unit about to be stored.
new_store = Vector<byte>::New(NewCapacity(new_content_size));
} else {
new_store = backing_store_;
}
uint8_t* src = backing_store_.begin();
uint16_t* dst = reinterpret_cast<uint16_t*>(new_store.begin());
for (int i = position_ - 1; i >= 0; i--) {
dst[i] = src[i];
}
if (new_store.begin() != backing_store_.begin()) {
backing_store_.Dispose();
backing_store_ = new_store;
}
position_ = new_content_size;
is_one_byte_ = false;
}
void LiteralBuffer::AddTwoByteChar(uc32 code_unit) {
DCHECK(!is_one_byte());
if (position_ >= backing_store_.length()) ExpandBuffer();
if (code_unit <=
static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) = code_unit;
position_ += kUC16Size;
} else {
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =
unibrow::Utf16::LeadSurrogate(code_unit);
position_ += kUC16Size;
if (position_ >= backing_store_.length()) ExpandBuffer();
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =
unibrow::Utf16::TrailSurrogate(code_unit);
position_ += kUC16Size;
}
}
} // namespace internal
} // namespace v8
// Copyright 2019 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.
#ifndef V8_PARSING_LITERAL_BUFFER_H_
#define V8_PARSING_LITERAL_BUFFER_H_
#include "src/unicode-decoder.h"
#include "src/vector.h"
namespace v8 {
namespace internal {
// LiteralBuffer - Collector of chars of literals.
class LiteralBuffer final {
public:
LiteralBuffer() : backing_store_(), position_(0), is_one_byte_(true) {}
~LiteralBuffer() { backing_store_.Dispose(); }
V8_INLINE void AddChar(char code_unit) {
DCHECK(IsValidAscii(code_unit));
AddOneByteChar(static_cast<byte>(code_unit));
}
V8_INLINE void AddChar(uc32 code_unit) {
if (is_one_byte()) {
if (code_unit <= static_cast<uc32>(unibrow::Latin1::kMaxChar)) {
AddOneByteChar(static_cast<byte>(code_unit));
return;
}
ConvertToTwoByte();
}
AddTwoByteChar(code_unit);
}
bool is_one_byte() const { return is_one_byte_; }
bool Equals(Vector<const char> keyword) const {
return is_one_byte() && keyword.length() == position_ &&
(memcmp(keyword.begin(), backing_store_.begin(), position_) == 0);
}
Vector<const uint16_t> two_byte_literal() const {
return literal<uint16_t>();
}
Vector<const uint8_t> one_byte_literal() const { return literal<uint8_t>(); }
template <typename Char>
Vector<const Char> literal() const {
DCHECK_EQ(is_one_byte_, sizeof(Char) == 1);
DCHECK_EQ(position_ & (sizeof(Char) - 1), 0);
return Vector<const Char>(
reinterpret_cast<const Char*>(backing_store_.begin()),
position_ >> (sizeof(Char) - 1));
}
int length() const { return is_one_byte() ? position_ : (position_ >> 1); }
void Start() {
position_ = 0;
is_one_byte_ = true;
}
Handle<String> Internalize(Isolate* isolate) const;
private:
static const int kInitialCapacity = 16;
static const int kGrowthFactor = 4;
static const int kMaxGrowth = 1 * MB;
inline bool IsValidAscii(char code_unit) {
// Control characters and printable characters span the range of
// valid ASCII characters (0-127). Chars are unsigned on some
// platforms which causes compiler warnings if the validity check
// tests the lower bound >= 0 as it's always true.
return iscntrl(code_unit) || isprint(code_unit);
}
V8_INLINE void AddOneByteChar(byte one_byte_char) {
DCHECK(is_one_byte());
if (position_ >= backing_store_.length()) ExpandBuffer();
backing_store_[position_] = one_byte_char;
position_ += kOneByteSize;
}
void AddTwoByteChar(uc32 code_unit);
int NewCapacity(int min_capacity);
void ExpandBuffer();
void ConvertToTwoByte();
Vector<byte> backing_store_;
int position_;
bool is_one_byte_;
DISALLOW_COPY_AND_ASSIGN(LiteralBuffer);
};
} // namespace internal
} // namespace v8
#endif // V8_PARSING_LITERAL_BUFFER_H_
...@@ -54,6 +54,74 @@ class Scanner::ErrorState { ...@@ -54,6 +54,74 @@ class Scanner::ErrorState {
Scanner::Location const old_location_; Scanner::Location const old_location_;
}; };
// ----------------------------------------------------------------------------
// Scanner::LiteralBuffer
Handle<String> Scanner::LiteralBuffer::Internalize(Isolate* isolate) const {
if (is_one_byte()) {
return isolate->factory()->InternalizeOneByteString(one_byte_literal());
}
return isolate->factory()->InternalizeTwoByteString(two_byte_literal());
}
int Scanner::LiteralBuffer::NewCapacity(int min_capacity) {
return min_capacity < (kMaxGrowth / (kGrowthFactor - 1))
? min_capacity * kGrowthFactor
: min_capacity + kMaxGrowth;
}
void Scanner::LiteralBuffer::ExpandBuffer() {
int min_capacity = Max(kInitialCapacity, backing_store_.length());
Vector<byte> new_store = Vector<byte>::New(NewCapacity(min_capacity));
if (position_ > 0) {
MemCopy(new_store.begin(), backing_store_.begin(), position_);
}
backing_store_.Dispose();
backing_store_ = new_store;
}
void Scanner::LiteralBuffer::ConvertToTwoByte() {
DCHECK(is_one_byte());
Vector<byte> new_store;
int new_content_size = position_ * kUC16Size;
if (new_content_size >= backing_store_.length()) {
// Ensure room for all currently read code units as UC16 as well
// as the code unit about to be stored.
new_store = Vector<byte>::New(NewCapacity(new_content_size));
} else {
new_store = backing_store_;
}
uint8_t* src = backing_store_.begin();
uint16_t* dst = reinterpret_cast<uint16_t*>(new_store.begin());
for (int i = position_ - 1; i >= 0; i--) {
dst[i] = src[i];
}
if (new_store.begin() != backing_store_.begin()) {
backing_store_.Dispose();
backing_store_ = new_store;
}
position_ = new_content_size;
is_one_byte_ = false;
}
void Scanner::LiteralBuffer::AddTwoByteChar(uc32 code_unit) {
DCHECK(!is_one_byte());
if (position_ >= backing_store_.length()) ExpandBuffer();
if (code_unit <=
static_cast<uc32>(unibrow::Utf16::kMaxNonSurrogateCharCode)) {
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) = code_unit;
position_ += kUC16Size;
} else {
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =
unibrow::Utf16::LeadSurrogate(code_unit);
position_ += kUC16Size;
if (position_ >= backing_store_.length()) ExpandBuffer();
*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =
unibrow::Utf16::TrailSurrogate(code_unit);
position_ += kUC16Size;
}
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Scanner::BookmarkScope // Scanner::BookmarkScope
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "src/char-predicates.h" #include "src/char-predicates.h"
#include "src/globals.h" #include "src/globals.h"
#include "src/message-template.h" #include "src/message-template.h"
#include "src/parsing/literal-buffer.h"
#include "src/parsing/token.h" #include "src/parsing/token.h"
#include "src/pointer-with-payload.h" #include "src/pointer-with-payload.h"
#include "src/unicode.h" #include "src/unicode.h"
...@@ -424,6 +423,92 @@ class V8_EXPORT_PRIVATE Scanner { ...@@ -424,6 +423,92 @@ class V8_EXPORT_PRIVATE Scanner {
// escape sequences are allowed. // escape sequences are allowed.
class ErrorState; class ErrorState;
// LiteralBuffer - Collector of chars of literals.
class LiteralBuffer {
public:
LiteralBuffer() : backing_store_(), position_(0), is_one_byte_(true) {}
~LiteralBuffer() { backing_store_.Dispose(); }
V8_INLINE void AddChar(char code_unit) {
DCHECK(IsValidAscii(code_unit));
AddOneByteChar(static_cast<byte>(code_unit));
}
V8_INLINE void AddChar(uc32 code_unit) {
if (is_one_byte()) {
if (code_unit <= static_cast<uc32>(unibrow::Latin1::kMaxChar)) {
AddOneByteChar(static_cast<byte>(code_unit));
return;
}
ConvertToTwoByte();
}
AddTwoByteChar(code_unit);
}
bool is_one_byte() const { return is_one_byte_; }
bool Equals(Vector<const char> keyword) const {
return is_one_byte() && keyword.length() == position_ &&
(memcmp(keyword.begin(), backing_store_.begin(), position_) == 0);
}
Vector<const uint16_t> two_byte_literal() const {
DCHECK(!is_one_byte());
DCHECK_EQ(position_ & 0x1, 0);
return Vector<const uint16_t>(
reinterpret_cast<const uint16_t*>(backing_store_.begin()),
position_ >> 1);
}
Vector<const uint8_t> one_byte_literal() const {
DCHECK(is_one_byte());
return Vector<const uint8_t>(
reinterpret_cast<const uint8_t*>(backing_store_.begin()), position_);
}
int length() const { return is_one_byte() ? position_ : (position_ >> 1); }
void Start() {
position_ = 0;
is_one_byte_ = true;
}
Handle<String> Internalize(Isolate* isolate) const;
private:
static const int kInitialCapacity = 16;
static const int kGrowthFactor = 4;
static const int kMaxGrowth = 1 * MB;
inline bool IsValidAscii(char code_unit) {
// Control characters and printable characters span the range of
// valid ASCII characters (0-127). Chars are unsigned on some
// platforms which causes compiler warnings if the validity check
// tests the lower bound >= 0 as it's always true.
return iscntrl(code_unit) || isprint(code_unit);
}
V8_INLINE void AddOneByteChar(byte one_byte_char) {
DCHECK(is_one_byte());
if (position_ >= backing_store_.length()) ExpandBuffer();
backing_store_[position_] = one_byte_char;
position_ += kOneByteSize;
}
void AddTwoByteChar(uc32 code_unit);
int NewCapacity(int min_capacity);
void ExpandBuffer();
void ConvertToTwoByte();
Vector<byte> backing_store_;
int position_;
bool is_one_byte_;
DISALLOW_COPY_AND_ASSIGN(LiteralBuffer);
};
// The current and look-ahead token. // The current and look-ahead token.
struct TokenDesc { struct TokenDesc {
Location location = {0, 0}; Location location = {0, 0};
......
...@@ -176,49 +176,6 @@ int TransitionArray::SearchName(Name name, int* out_insertion_index) { ...@@ -176,49 +176,6 @@ int TransitionArray::SearchName(Name name, int* out_insertion_index) {
out_insertion_index); out_insertion_index);
} }
TransitionsAccessor::TransitionsAccessor(Isolate* isolate, Map map,
DisallowHeapAllocation* no_gc)
: isolate_(isolate), map_(map) {
Initialize();
USE(no_gc);
}
TransitionsAccessor::TransitionsAccessor(Isolate* isolate, Handle<Map> map)
: isolate_(isolate), map_handle_(map), map_(*map) {
Initialize();
}
void TransitionsAccessor::Reload() {
DCHECK(!map_handle_.is_null());
map_ = *map_handle_;
Initialize();
}
void TransitionsAccessor::Initialize() {
raw_transitions_ = map_->raw_transitions();
HeapObject heap_object;
if (raw_transitions_->IsSmi() || raw_transitions_->IsCleared()) {
encoding_ = kUninitialized;
} else if (raw_transitions_->IsWeak()) {
encoding_ = kWeakRef;
} else if (raw_transitions_->GetHeapObjectIfStrong(&heap_object)) {
if (heap_object->IsTransitionArray()) {
encoding_ = kFullTransitionArray;
} else if (heap_object->IsPrototypeInfo()) {
encoding_ = kPrototypeInfo;
} else {
DCHECK(map_->is_deprecated());
DCHECK(heap_object->IsMap());
encoding_ = kMigrationTarget;
}
} else {
UNREACHABLE();
}
#if DEBUG
needs_reload_ = false;
#endif
}
int TransitionArray::number_of_transitions() const { int TransitionArray::number_of_transitions() const {
if (length() < kFirstIndex) return 0; if (length() < kFirstIndex) return 0;
return Get(kTransitionLengthIndex).ToSmi().value(); return Get(kTransitionLengthIndex).ToSmi().value();
...@@ -286,33 +243,6 @@ void TransitionArray::SetNumberOfTransitions(int number_of_transitions) { ...@@ -286,33 +243,6 @@ void TransitionArray::SetNumberOfTransitions(int number_of_transitions) {
MaybeObject::FromSmi(Smi::FromInt(number_of_transitions))); MaybeObject::FromSmi(Smi::FromInt(number_of_transitions)));
} }
Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
DisallowHeapAllocation no_gc;
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
case kFullTransitionArray:
return Handle<String>::null();
case kWeakRef: {
Map target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
PropertyDetails details = GetSimpleTargetDetails(target);
if (details.location() != kField) return Handle<String>::null();
DCHECK_EQ(kData, details.kind());
if (details.attributes() != NONE) return Handle<String>::null();
Name name = GetSimpleTransitionKey(target);
if (!name->IsString()) return Handle<String>::null();
return handle(String::cast(name), isolate_);
}
}
UNREACHABLE();
}
Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() {
DCHECK(!ExpectedTransitionKey().is_null());
return handle(GetTarget(0), isolate_);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -11,6 +11,31 @@ ...@@ -11,6 +11,31 @@
namespace v8 { namespace v8 {
namespace internal { namespace internal {
void TransitionsAccessor::Initialize() {
raw_transitions_ = map_->raw_transitions();
HeapObject heap_object;
if (raw_transitions_->IsSmi() || raw_transitions_->IsCleared()) {
encoding_ = kUninitialized;
} else if (raw_transitions_->IsWeak()) {
encoding_ = kWeakRef;
} else if (raw_transitions_->GetHeapObjectIfStrong(&heap_object)) {
if (heap_object->IsTransitionArray()) {
encoding_ = kFullTransitionArray;
} else if (heap_object->IsPrototypeInfo()) {
encoding_ = kPrototypeInfo;
} else {
DCHECK(map_->is_deprecated());
DCHECK(heap_object->IsMap());
encoding_ = kMigrationTarget;
}
} else {
UNREACHABLE();
}
#if DEBUG
needs_reload_ = false;
#endif
}
Map TransitionsAccessor::GetSimpleTransition() { Map TransitionsAccessor::GetSimpleTransition() {
switch (encoding()) { switch (encoding()) {
case kWeakRef: case kWeakRef:
...@@ -237,6 +262,33 @@ MaybeHandle<Map> TransitionsAccessor::FindTransitionToDataProperty( ...@@ -237,6 +262,33 @@ MaybeHandle<Map> TransitionsAccessor::FindTransitionToDataProperty(
return Handle<Map>(target, isolate_); return Handle<Map>(target, isolate_);
} }
Handle<String> TransitionsAccessor::ExpectedTransitionKey() {
DisallowHeapAllocation no_gc;
switch (encoding()) {
case kPrototypeInfo:
case kUninitialized:
case kMigrationTarget:
case kFullTransitionArray:
return Handle<String>::null();
case kWeakRef: {
Map target = Map::cast(raw_transitions_->GetHeapObjectAssumeWeak());
PropertyDetails details = GetSimpleTargetDetails(target);
if (details.location() != kField) return Handle<String>::null();
DCHECK_EQ(kData, details.kind());
if (details.attributes() != NONE) return Handle<String>::null();
Name name = GetSimpleTransitionKey(target);
if (!name->IsString()) return Handle<String>::null();
return handle(String::cast(name), isolate_);
}
}
UNREACHABLE();
}
Handle<Map> TransitionsAccessor::ExpectedTransitionTarget() {
DCHECK(!ExpectedTransitionKey().is_null());
return handle(GetTarget(0), isolate_);
}
bool TransitionsAccessor::CanHaveMoreTransitions() { bool TransitionsAccessor::CanHaveMoreTransitions() {
if (map_->is_dictionary_map()) return false; if (map_->is_dictionary_map()) return false;
if (encoding() == kFullTransitionArray) { if (encoding() == kFullTransitionArray) {
......
...@@ -38,9 +38,16 @@ namespace internal { ...@@ -38,9 +38,16 @@ namespace internal {
// cleared when the map they refer to is not otherwise reachable. // cleared when the map they refer to is not otherwise reachable.
class V8_EXPORT_PRIVATE TransitionsAccessor { class V8_EXPORT_PRIVATE TransitionsAccessor {
public: public:
inline TransitionsAccessor(Isolate* isolate, Map map, TransitionsAccessor(Isolate* isolate, Map map, DisallowHeapAllocation* no_gc)
DisallowHeapAllocation* no_gc); : isolate_(isolate), map_(map) {
inline TransitionsAccessor(Isolate* isolate, Handle<Map> map); Initialize();
USE(no_gc);
}
TransitionsAccessor(Isolate* isolate, Handle<Map> map)
: isolate_(isolate), map_handle_(map), map_(*map) {
Initialize();
}
// Insert a new transition into |map|'s transition array, extending it // Insert a new transition into |map|'s transition array, extending it
// as necessary. // as necessary.
// Requires the constructor that takes a Handle<Map> to have been used. // Requires the constructor that takes a Handle<Map> to have been used.
...@@ -63,8 +70,8 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -63,8 +70,8 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
return FindTransitionToDataProperty(name, kFieldOnly); return FindTransitionToDataProperty(name, kFieldOnly);
} }
inline Handle<String> ExpectedTransitionKey(); Handle<String> ExpectedTransitionKey();
inline Handle<Map> ExpectedTransitionTarget(); Handle<Map> ExpectedTransitionTarget();
int NumberOfTransitions(); int NumberOfTransitions();
// The size of transition arrays are limited so they do not end up in large // The size of transition arrays are limited so they do not end up in large
...@@ -136,7 +143,11 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -136,7 +143,11 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
kFullTransitionArray, kFullTransitionArray,
}; };
inline void Reload(); void Reload() {
DCHECK(!map_handle_.is_null());
map_ = *map_handle_;
Initialize();
}
inline Encoding encoding() { inline Encoding encoding() {
DCHECK(!needs_reload_); DCHECK(!needs_reload_);
...@@ -159,7 +170,7 @@ class V8_EXPORT_PRIVATE TransitionsAccessor { ...@@ -159,7 +170,7 @@ class V8_EXPORT_PRIVATE TransitionsAccessor {
#endif #endif
} }
inline void Initialize(); void Initialize();
inline Map GetSimpleTransition(); inline Map GetSimpleTransition();
bool HasSimpleTransitionTo(Map map); bool HasSimpleTransitionTo(Map map);
......
...@@ -133,7 +133,7 @@ class Utf16 { ...@@ -133,7 +133,7 @@ class Utf16 {
class Latin1 { class Latin1 {
public: public:
static const uint16_t kMaxChar = 0xff; static const unsigned kMaxChar = 0xff;
// Convert the character to Latin-1 case equivalent if possible. // Convert the character to Latin-1 case equivalent if possible.
static inline uint16_t TryConvertToLatin1(uint16_t c) { static inline uint16_t TryConvertToLatin1(uint16_t c) {
switch (c) { switch (c) {
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include "src/objects/ordered-hash-table-inl.h" #include "src/objects/ordered-hash-table-inl.h"
#include "src/objects/smi.h" #include "src/objects/smi.h"
#include "src/snapshot/code-serializer.h" #include "src/snapshot/code-serializer.h"
#include "src/transitions-inl.h" #include "src/transitions.h"
#include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-objects-inl.h" #include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-result.h" #include "src/wasm/wasm-result.h"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment