Revision: 9424
Author: [email protected]
Date: Mon Sep 26 04:14:41 2011
Log: Added ability to lock strings to prevent their representation or
encoding from changing.
Use string locking to ensure consistent representation of source string
during JSON parsing.
Review URL: http://codereview.chromium.org/7977001
http://code.google.com/p/v8/source/detail?r=9424
Modified:
/branches/bleeding_edge/src/api.cc
/branches/bleeding_edge/src/handles.cc
/branches/bleeding_edge/src/handles.h
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/json-parser.h
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/test/cctest/test-api.cc
=======================================
--- /branches/bleeding_edge/src/api.cc Thu Sep 22 07:43:23 2011
+++ /branches/bleeding_edge/src/api.cc Mon Sep 26 04:14:41 2011
@@ -4479,6 +4479,9 @@
if (isolate->string_tracker()->IsFreshUnusedString(obj)) {
return false;
}
+ if (isolate->heap()->IsStringLocked(*obj)) {
+ return false;
+ }
if (isolate->heap()->IsInGCPostProcessing()) {
return false;
}
=======================================
--- /branches/bleeding_edge/src/handles.cc Thu Sep 22 06:54:53 2011
+++ /branches/bleeding_edge/src/handles.cc Mon Sep 26 04:14:41 2011
@@ -941,5 +941,21 @@
info.SetOptimizing(osr_ast_id);
return CompileLazyHelper(&info, flag);
}
+
+
+void LockString(Handle<String> string) {
+ CALL_HEAP_FUNCTION_VOID(string->GetHeap()->isolate(),
+ string->GetHeap()->LockString(*string));
+}
+
+
+StringLock::StringLock(Handle<String> string) : string_(string) {
+ LockString(string);
+}
+
+
+StringLock::~StringLock() {
+ string_->GetHeap()->UnlockString(*string_);
+}
} } // namespace v8::internal
=======================================
--- /branches/bleeding_edge/src/handles.h Thu Sep 22 06:54:53 2011
+++ /branches/bleeding_edge/src/handles.h Mon Sep 26 04:14:41 2011
@@ -163,9 +163,11 @@
//
----------------------------------------------------------------------------
// Handle operations.
-// They might invoke garbage collection. The result is an handle to
-// an object of expected type, or the handle is an error if running out
-// of space or encountering an internal error.
+// They might invoke garbage collection. The result is an handle to an
+// object of expected type, or the handle is a null handle if encountering
+// an internal error. Will not return a null handle due to out-of-memory
+// unless otherwise stated, but will instead try to do GC and die fatally
+// if that doesn't help.
void NormalizeProperties(Handle<JSObject> object,
PropertyNormalizationMode mode,
@@ -183,7 +185,7 @@
void FlattenString(Handle<String> str);
// Flattens a string and returns the underlying external or sequential
-// string.
+// string. Never returns a null handle.
Handle<String> FlattenGetString(Handle<String> str);
Handle<Object> SetProperty(Handle<JSReceiver> object,
@@ -379,6 +381,22 @@
#endif
};
+
+// Prevents a (non-cons, non-slice) string from having its representation
+// changed. This is just a Handle based wrapper around Heap::LockString.
+// Use Heap::UnlockString to unlock (that one can't cause allocation, so
+// it doesn't need a Handle wrapper).
+void LockString(Handle<String> string);
+
+// Scoped lock on a string.
+class StringLock {
+ public:
+ explicit StringLock(Handle<String> string);
+ ~StringLock();
+ private:
+ Handle<String> string_;
+};
+
} } // namespace v8::internal
#endif // V8_HANDLES_H_
=======================================
--- /branches/bleeding_edge/src/heap.cc Fri Sep 23 06:28:17 2011
+++ /branches/bleeding_edge/src/heap.cc Mon Sep 26 04:14:41 2011
@@ -2346,6 +2346,7 @@
set_intrinsic_function_names(StringDictionary::cast(obj));
if (InitializeNumberStringCache()->IsFailure()) return false;
+ if (InitializeStringLocks()->IsFailure()) return false;
// Allocate cache for single character ASCII strings.
{ MaybeObject* maybe_obj =
@@ -2560,6 +2561,76 @@
if (!maybe->To<Object>(&number)) return maybe;
return NumberToString(number, check_number_string_cache);
}
+
+
+MaybeObject* Heap::LockString(String* string) {
+ ASSERT(!string->IsConsString());
+ FixedArray* locks = string_locks();
+ ASSERT(locks->length() > 1);
+ int length = locks->length();
+ int element_count = Smi::cast(locks->get(0))->value();
+ int element_index = element_count + 1;
+ if (element_index >= length) {
+ int new_length = length * 2;
+ MaybeObject* allocation = AllocateFixedArray(new_length);
+ FixedArray* new_locks = NULL; // Initialized to please compiler.
+ if (!allocation->To<FixedArray>(&new_locks)) return allocation;
+ for (int i = 1; i < length; i++) {
+ new_locks->set(i, locks->get(i));
+ }
+ set_string_locks(new_locks);
+ locks = new_locks;
+ }
+ locks->set(element_index, string);
+ locks->set(0, Smi::FromInt(element_index));
+ return string;
+}
+
+
+void Heap::UnlockString(String* string) {
+ FixedArray* locks = string_locks();
+ ASSERT(locks->length() > 1);
+ int element_count = Smi::cast(locks->get(0))->value();
+ ASSERT(element_count > 0);
+ ASSERT(element_count < locks->length());
+ for (int i = 1; i <= element_count; i++) {
+ String* element = String::cast(locks->get(i));
+ if (element == string) {
+ if (i < element_count) {
+ locks->set(i, locks->get(element_count));
+ }
+ locks->set_undefined(element_count);
+ locks->set(0, Smi::FromInt(element_count - 1));
+ return;
+ }
+ }
+ // We should have found the string. It's an error to try to unlock
+ // a string that hasn't been locked.
+ UNREACHABLE();
+}
+
+
+bool Heap::IsStringLocked(String* string) {
+ if (string->IsConsString()) return false;
+ FixedArray* locks = string_locks();
+ ASSERT(locks->length() > 1);
+ int element_count = Smi::cast(locks->get(0))->value();
+ for (int i = 1; i <= element_count; i++) {
+ if (locks->get(i) == string) return true;
+ }
+ return false;
+}
+
+
+MaybeObject* Heap::InitializeStringLocks() {
+ const int kInitialSize = 6;
+ MaybeObject* allocation = AllocateFixedArray(kInitialSize);
+ if (allocation->IsFailure()) return allocation;
+ FixedArray* new_array =
FixedArray::cast(allocation->ToObjectUnchecked());
+ new_array->set(0, Smi::FromInt(0));
+ set_string_locks(new_array);
+ return new_array;
+}
Map* Heap::MapForExternalArrayType(ExternalArrayType array_type) {
=======================================
--- /branches/bleeding_edge/src/heap.h Fri Sep 23 06:28:17 2011
+++ /branches/bleeding_edge/src/heap.h Mon Sep 26 04:14:41 2011
@@ -77,6 +77,7 @@
V(Map, hash_table_map,
HashTableMap) \
V(Smi, stack_limit,
StackLimit) \
V(FixedArray, number_string_cache,
NumberStringCache) \
+ V(FixedArray, string_locks, StringLocks) \
V(Object, instanceof_cache_function,
InstanceofCacheFunction) \
V(Object, instanceof_cache_map,
InstanceofCacheMap) \
V(Object, instanceof_cache_answer,
InstanceofCacheAnswer) \
@@ -1214,6 +1215,25 @@
// the string representation of the number. Otherwise return undefined.
Object* GetNumberStringCache(Object* number);
+ // Locks a string to prevent changes to the string's representation or
+ // encoding, e.g., due to externalization.
+ // It does not prevent moving the string during a GC
+ // (i.e., it's not a way to keep a pointer to an underlying character
+ // sequence valid). Might fail if the underlying data structure can't
+ // grow to accomodate the string, otherwise returns the string itself.
+ //
+ // Stores data in Heap::string_locks(), a FixedArray with the number
+ // of filled in elements in the first position, and that number of
+ // string pointers in the following positions (in no particular order).
+ // The FixedArray is padded with undefined or similar uninteresting
values.
+ MaybeObject* LockString(String* string);
+ // Removes the lock on the string.
+ void UnlockString(String* string);
+ // Check if a string is locked.
+ bool IsStringLocked(String* string);
+ // Initializes the data structure underlying LockString.
+ MaybeObject* InitializeStringLocks();
+
// Update the cache with a new number-string pair.
void SetNumberStringCache(Object* number, String* str);
=======================================
--- /branches/bleeding_edge/src/json-parser.h Mon Sep 19 11:36:47 2011
+++ /branches/bleeding_edge/src/json-parser.h Mon Sep 26 04:14:41 2011
@@ -39,43 +39,66 @@
namespace v8 {
namespace internal {
-// A simple json parser.
-template <bool seq_ascii>
+// A simple JSON parser.
+template <typename StringType>
class JsonParser BASE_EMBEDDED {
public:
static Handle<Object> Parse(Handle<String> source) {
- return JsonParser().ParseJson(source);
+ return JsonParser(Handle<StringType>::cast(source)).ParseJson();
}
static const int kEndOfString = -1;
private:
- // Parse a string containing a single JSON value.
- Handle<Object> ParseJson(Handle<String> source);
+ typedef typename StringType::CharType SourceChar;
+
+ explicit JsonParser(Handle<StringType> source)
+ : isolate_(source->GetHeap()->isolate()),
+ source_(source),
+ characters_(NULL),
+ source_length_(source->length()),
+ position_(-1) {
+ InitializeSource();
+ }
+
+
+ // Parse the source string as containing a single JSON value.
+ Handle<Object> ParseJson();
+
+ // Set up the object so GetChar works, in case it needs more than just
+ // the constructor.
+ void InitializeSource();
+
+ inline uc32 GetChar(int position);
+ inline const SourceChar* GetChars();
inline void Advance() {
position_++;
if (position_ >= source_length_) {
c0_ = kEndOfString;
- } else if (seq_ascii) {
- c0_ = seq_source_->SeqAsciiStringGet(position_);
} else {
- c0_ = source_->Get(position_);
+ c0_ = GetChar(position_);
}
}
// The JSON lexical grammar is specified in the ECMAScript 5 standard,
// section 15.12.1.1. The only allowed whitespace characters between
tokens
// are tab, carriage-return, newline and space.
+
+
+ static inline bool IsJsonWhitespace(uc32 ch) {
+ const char* whitespaces = "\x20\x09\x0a\0\0\x0d\0\0";
+ return (static_cast<uc32>(whitespaces[ch & 0x07]) == ch);
+ }
inline void AdvanceSkipWhitespace() {
do {
Advance();
- } while (c0_ == '\t' || c0_ == '\r' || c0_ == '\n' || c0_ == ' ');
+ } while (IsJsonWhitespace(c0_));
}
inline void SkipWhitespace() {
- while (c0_ == '\t' || c0_ == '\r' || c0_ == '\n' || c0_ == ' ') {
+ while (IsJsonWhitespace(c0_)) {
Advance();
}
}
@@ -110,7 +133,7 @@
// 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-ASCII
char.
- template <typename StringType, typename SinkChar>
+ template <typename SinkStringType>
Handle<String> SlowScanJsonString(Handle<String> prefix, int start, int
end);
// A JSON number (production JSONNumber) is a subset of the valid
JavaScript
@@ -152,37 +175,26 @@
static const int kInitialSpecialStringLength = 1024;
-
- private:
- Handle<String> source_;
- int source_length_;
- Handle<SeqAsciiString> seq_source_;
-
Isolate* isolate_;
- uc32 c0_;
+ Handle<StringType> source_;
+ // Used for external strings, to avoid going through the resource on
+ // every access.
+ const SourceChar* characters_;
+ int source_length_;
int position_;
+ uc32 c0_;
};
-template <bool seq_ascii>
-Handle<Object> JsonParser<seq_ascii>::ParseJson(Handle<String> source) {
- isolate_ = source->map()->GetHeap()->isolate();
- FlattenString(source);
- source_ = source;
- source_length_ = source_->length();
-
- // Optimized fast case where we only have ASCII characters.
- if (seq_ascii) {
- seq_source_ = Handle<SeqAsciiString>::cast(source_);
- }
-
- // Set initial position right before the string.
- position_ = -1;
+template <typename StringType>
+Handle<Object> JsonParser<StringType>::ParseJson() {
+ // Initial position is right before the string.
+ ASSERT(position_ == -1);
// Advance to the first character (posibly EOS)
AdvanceSkipWhitespace();
+ // ParseJsonValue also consumes following whitespace.
Handle<Object> result = ParseJsonValue();
if (result.is_null() || c0_ != kEndOfString) {
// Parse failed. Current character is the unexpected token.
-
const char* message;
Factory* factory = isolate()->factory();
Handle<JSArray> array;
@@ -219,7 +231,7 @@
break;
}
- MessageLocation location(factory->NewScript(source),
+ MessageLocation location(factory->NewScript(source_),
position_,
position_ + 1);
Handle<Object> result = factory->NewSyntaxError(message, array);
@@ -231,8 +243,8 @@
// Parse any JSON value.
-template <bool seq_ascii>
-Handle<Object> JsonParser<seq_ascii>::ParseJsonValue() {
+template <typename StringType>
+Handle<Object> JsonParser<StringType>::ParseJsonValue() {
switch (c0_) {
case '"':
return ParseJsonString();
@@ -283,13 +295,13 @@
// Parse a JSON object. Position must be right at '{'.
-template <bool seq_ascii>
-Handle<Object> JsonParser<seq_ascii>::ParseJsonObject() {
+template <typename StringType>
+Handle<Object> JsonParser<StringType>::ParseJsonObject() {
Handle<JSFunction> object_constructor(
isolate()->global_context()->object_function());
Handle<JSObject> json_object =
isolate()->factory()->NewJSObject(object_constructor);
- ASSERT_EQ(c0_, '{');
+ ASSERT_EQ('{', c0_);
AdvanceSkipWhitespace();
if (c0_ != '}') {
@@ -319,8 +331,8 @@
}
// Parse a JSON array. Position must be right at '['.
-template <bool seq_ascii>
-Handle<Object> JsonParser<seq_ascii>::ParseJsonArray() {
+template <typename StringType>
+Handle<Object> JsonParser<StringType>::ParseJsonArray() {
ZoneScope zone_scope(isolate(), DELETE_ON_EXIT);
ZoneList<Handle<Object> > elements(4);
ASSERT_EQ(c0_, '[');
@@ -347,8 +359,8 @@
}
-template <bool seq_ascii>
-Handle<Object> JsonParser<seq_ascii>::ParseJsonNumber() {
+template <typename StringType>
+Handle<Object> JsonParser<StringType>::ParseJsonNumber() {
bool negative = false;
int beg_pos = position_;
if (c0_ == '-') {
@@ -391,24 +403,12 @@
}
int length = position_ - beg_pos;
double number;
- if (seq_ascii) {
- Vector<const char> chars(seq_source_->GetChars() + beg_pos, length);
- number = StringToDouble(isolate()->unicode_cache(),
- chars,
- NO_FLAGS, // Hex, octal or trailing junk.
- OS::nan_value());
- } else {
- Vector<char> buffer = Vector<char>::New(length);
- String::WriteToFlat(*source_, buffer.start(), beg_pos, position_);
- Vector<const char> result =
- Vector<const char>(reinterpret_cast<const char*>(buffer.start()),
- length);
- number = StringToDouble(isolate()->unicode_cache(),
- result,
- NO_FLAGS, // Hex, octal or trailing junk.
- 0.0);
- buffer.Dispose();
- }
+
+ Vector<const SourceChar> chars(GetChars() + beg_pos, length);
+ number = StringToDouble(isolate()->unicode_cache(),
+ chars,
+ NO_FLAGS, // Hex, octal or trailing junk.
+ OS::nan_value());
SkipWhitespace();
return isolate()->factory()->NewNumber(number);
}
@@ -444,15 +444,17 @@
// 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 <bool seq_ascii>
-template <typename StringType, typename SinkChar>
-Handle<String> JsonParser<seq_ascii>::SlowScanJsonString(
+template <typename StringType>
+template <typename SinkStringType>
+Handle<String> JsonParser<StringType>::SlowScanJsonString(
Handle<String> prefix, int start, int end) {
+ typedef typename SinkStringType::CharType SinkChar;
int count = end - start;
int max_length = count + source_length_ - position_;
int length = Min(max_length, Max(kInitialSpecialStringLength, 2 *
count));
- Handle<StringType> seq_str =
NewRawString<StringType>(isolate()->factory(),
- length);
+ Handle<SinkStringType> seq_str =
+ NewRawString<SinkStringType>(isolate()->factory(),
+ length);
// Copy prefix into seq_str.
SinkChar* dest = seq_str->GetChars();
String::WriteToFlat(*prefix, dest, start, end);
@@ -462,7 +464,7 @@
if (c0_ < 0x20) return Handle<String>::null();
if (count >= length) {
// We need to create a longer sequential string for the result.
- return SlowScanJsonString<StringType, SinkChar>(seq_str, 0, count);
+ return SlowScanJsonString<SinkStringType>(seq_str, 0, count);
}
if (c0_ != '\\') {
// If the sink can contain UC16 characters, or source_ contains only
@@ -470,16 +472,16 @@
// character. Otherwise check whether the UC16 source character can
fit
// in the ASCII sink.
if (sizeof(SinkChar) == kUC16Size ||
- seq_ascii ||
+ sizeof(SourceChar) == kCharSize ||
c0_ <= kMaxAsciiCharCode) {
SeqStringSet(seq_str, count++, c0_);
Advance();
} else {
- // StringType is SeqAsciiString and we just read a non-ASCII char.
- return SlowScanJsonString<SeqTwoByteString, uc16>(seq_str, 0,
count);
+ // SinkStringType is SeqAsciiString and we just read a non-ASCII
char.
+ return SlowScanJsonString<SeqTwoByteString>(seq_str, 0, count);
}
} else {
- Advance(); // Advance past the \.
+ Advance(); // Advance past the '\'.
switch (c0_) {
case '"':
case '\\':
@@ -518,9 +520,9 @@
// StringType is SeqAsciiString and we just read a non-ASCII
char.
position_ -= 6; // Rewind position_ to \ in \uxxxx.
Advance();
- return SlowScanJsonString<SeqTwoByteString, uc16>(seq_str,
- 0,
- count);
+ return SlowScanJsonString<SeqTwoByteString>(seq_str,
+ 0,
+ count);
}
}
default:
@@ -532,11 +534,11 @@
// Shrink seq_string length to count.
if (isolate()->heap()->InNewSpace(*seq_str)) {
isolate()->heap()->new_space()->
- template ShrinkStringAtAllocationBoundary<StringType>(
+ template ShrinkStringAtAllocationBoundary<SinkStringType>(
*seq_str, count);
} else {
- int string_size = StringType::SizeFor(count);
- int allocated_string_size = StringType::SizeFor(length);
+ int string_size = SinkStringType::SizeFor(count);
+ int allocated_string_size = SinkStringType::SizeFor(length);
int delta = allocated_string_size - string_size;
Address start_filler_object = seq_str->address() + string_size;
seq_str->set_length(count);
@@ -549,9 +551,9 @@
}
-template <bool seq_ascii>
+template <typename StringType>
template <bool is_symbol>
-Handle<String> JsonParser<seq_ascii>::ScanJsonString() {
+Handle<String> JsonParser<StringType>::ScanJsonString() {
ASSERT_EQ('"', c0_);
Advance();
if (c0_ == '"') {
@@ -564,25 +566,24 @@
// Check for control character (0x00-0x1f) or unterminated string (<0).
if (c0_ < 0x20) return Handle<String>::null();
if (c0_ != '\\') {
- if (seq_ascii || c0_ <= kMaxAsciiCharCode) {
+ if (c0_ <= kMaxAsciiCharCode) {
Advance();
} else {
- return SlowScanJsonString<SeqTwoByteString, uc16>(source_,
- beg_pos,
- position_);
+ return SlowScanJsonString<SeqTwoByteString>(source_,
+ beg_pos,
+ position_);
}
} else {
- return SlowScanJsonString<SeqAsciiString, char>(source_,
- beg_pos,
- position_);
+ return SlowScanJsonString<SeqAsciiString>(source_,
+ beg_pos,
+ position_);
}
} while (c0_ != '"');
int length = position_ - beg_pos;
Handle<String> result;
- if (seq_ascii && is_symbol) {
- result = isolate()->factory()->LookupAsciiSymbol(seq_source_,
- beg_pos,
- length);
+ if (is_symbol && source_->IsSeqAsciiString()) {
+ result = isolate()->factory()->LookupAsciiSymbol(
+ Handle<SeqAsciiString>::cast(source_), beg_pos, length);
} else {
result = isolate()->factory()->NewRawAsciiString(length);
char* dest = SeqAsciiString::cast(*result)->GetChars();
@@ -593,6 +594,74 @@
AdvanceSkipWhitespace();
return result;
}
+
+
+template <typename StringType>
+void JsonParser<StringType>::InitializeSource() { }
+
+
+template <>
+void JsonParser<ExternalAsciiString>::InitializeSource() {
+ characters_ = source_->resource()->data();
+}
+
+
+template <>
+void JsonParser<ExternalTwoByteString>::InitializeSource() {
+ characters_ = source_->resource()->data();
+}
+
+
+template <>
+uc32 JsonParser<SeqAsciiString>::GetChar(int pos) {
+ return static_cast<uc32>(source_->SeqAsciiStringGet(pos));
+}
+
+
+template <>
+uc32 JsonParser<SeqTwoByteString>::GetChar(int pos) {
+ return static_cast<uc32>(source_->SeqTwoByteStringGet(pos));
+}
+
+
+template <>
+uc32 JsonParser<ExternalAsciiString>::GetChar(int pos) {
+ ASSERT(pos >= 0);
+ ASSERT(pos < source_length_);
+ return static_cast<uc32>(characters_[pos]);
+}
+
+
+template <>
+uc32 JsonParser<ExternalTwoByteString>::GetChar(int pos) {
+ ASSERT(pos >= 0);
+ ASSERT(pos < source_length_);
+ return static_cast<uc32>(characters_[pos]);
+}
+
+
+template <>
+const char* JsonParser<SeqAsciiString>::GetChars() {
+ return source_->GetChars();
+}
+
+
+template <>
+const uc16* JsonParser<SeqTwoByteString>::GetChars() {
+ return source_->GetChars();
+}
+
+
+template <>
+const char* JsonParser<ExternalAsciiString>::GetChars() {
+ return characters_;
+}
+
+
+template <>
+const uc16* JsonParser<ExternalTwoByteString>::GetChars() {
+ return characters_;
+}
} } // namespace v8::internal
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Thu Sep 22 10:12:41 2011
+++ /branches/bleeding_edge/src/objects-inl.h Mon Sep 26 04:14:41 2011
@@ -37,6 +37,7 @@
#include "elements.h"
#include "objects.h"
+#include "char-predicates-inl.h"
#include "contexts.h"
#include "conversions-inl.h"
#include "heap.h"
@@ -4194,7 +4195,6 @@
raw_running_hash_(0),
array_index_(0),
is_array_index_(0 < length_ && length_ <= String::kMaxArrayIndexSize),
- is_first_char_(true),
is_valid_(true) { }
@@ -4206,26 +4206,18 @@
void StringHasher::AddCharacter(uc32 c) {
// Use the Jenkins one-at-a-time hash function to update the hash
// for the given character.
- raw_running_hash_ += c;
- raw_running_hash_ += (raw_running_hash_ << 10);
+ raw_running_hash_ = (raw_running_hash_ + c) * 1025;
raw_running_hash_ ^= (raw_running_hash_ >> 6);
- // Incremental array index computation.
if (is_array_index_) {
- if (c < '0' || c > '9') {
+ // Incremental array index computation.
+ unsigned digit = static_cast<unsigned>(c) - '0';
+ if (digit > 9 || array_index_ > 429496729U - ((digit + 2) >> 3)) {
is_array_index_ = false;
} else {
- int d = c - '0';
- if (is_first_char_) {
- is_first_char_ = false;
- if (c == '0' && length_ > 1) {
- is_array_index_ = false;
- return;
- }
- }
- if (array_index_ > 429496729U - ((d + 2) >> 3)) {
+ array_index_ = array_index_ * 10 + digit;
+ // Check for overflows or prefixed zeros (lengths > 0).
+ if (array_index_ == 0 && length_ > 1) {
is_array_index_ = false;
- } else {
- array_index_ = array_index_ * 10 + d;
}
}
}
@@ -4234,8 +4226,7 @@
void StringHasher::AddCharacterNoIndex(uc32 c) {
ASSERT(!is_array_index());
- raw_running_hash_ += c;
- raw_running_hash_ += (raw_running_hash_ << 10);
+ raw_running_hash_ = (raw_running_hash_ + c) * 1025;
raw_running_hash_ ^= (raw_running_hash_ >> 6);
}
=======================================
--- /branches/bleeding_edge/src/objects.cc Fri Sep 23 08:09:00 2011
+++ /branches/bleeding_edge/src/objects.cc Mon Sep 26 04:14:41 2011
@@ -2771,7 +2771,8 @@
attributes);
}
- // Check for accessor in prototype chain removed here in clone.
+ // Unlike SetLocalProperty, we ignore the prototype chain and
+ // any accessors in it.
if (!result.IsFound()) {
// Neither properties nor transitions found.
return AddProperty(name, value, attributes, kNonStrictMode);
@@ -10069,6 +10070,8 @@
ASSERT(length_ >= 0);
ASSERT(from_ + length_ <= string_->length());
StringHasher hasher(length_);
+ AssertNoAllocation no_alloc;
+ const char* chars = string_->GetChars() + from_;
// Very long strings have a trivial hash that doesn't inspect the
// string contents.
@@ -10079,16 +10082,14 @@
// Do the iterative array index computation as long as there is a
// chance this is an array index.
while (i < length_ && hasher.is_array_index()) {
- hasher.AddCharacter(static_cast<uc32>(
- string_->SeqAsciiStringGet(i + from_)));
+ hasher.AddCharacter(static_cast<uc32>(chars[i]));
i++;
}
// Process the remaining characters without updating the array
// index.
while (i < length_) {
- hasher.AddCharacterNoIndex(static_cast<uc32>(
- string_->SeqAsciiStringGet(i + from_)));
+ hasher.AddCharacterNoIndex(static_cast<uc32>(chars[i]));
i++;
}
hash_field_ = hasher.GetHashField();
=======================================
--- /branches/bleeding_edge/src/objects.h Fri Sep 23 08:09:00 2011
+++ /branches/bleeding_edge/src/objects.h Mon Sep 26 04:14:41 2011
@@ -5734,7 +5734,6 @@
uint32_t raw_running_hash_;
uint32_t array_index_;
bool is_array_index_;
- bool is_first_char_;
bool is_valid_;
friend class TwoCharHashTableKey;
};
@@ -6186,15 +6185,25 @@
// Layout description.
static const int kHeaderSize = String::kSize;
+ // Shortcuts for templates that know their string-type exactly.
+ bool IsExternalAsciiString() {
+ return false;
+ }
+ bool IsExternalTwoByteString() {
+ return false;
+ }
+
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SeqString);
};
-// The AsciiString class captures sequential ascii string objects.
-// Each character in the AsciiString is an ascii character.
+// The SeqAsciiString class captures sequential ASCII string objects.
+// Each character in the AsciiString is an ASCII character.
class SeqAsciiString: public SeqString {
public:
+ typedef char CharType;
static const bool kHasAsciiEncoding = true;
// Dispatched behavior.
@@ -6209,6 +6218,15 @@
// Casting
static inline SeqAsciiString* cast(Object* obj);
+ bool IsSeqAsciiString() {
+ ASSERT(this->Object::IsSeqAsciiString());
+ return true;
+ }
+ bool IsSeqTwoByteString() {
+ ASSERT(this->Object::IsSeqAsciiString());
+ return false;
+ }
+
// Garbage collection support. This method is called by the
// garbage collector to compute the actual size of an AsciiString
// instance.
@@ -6238,10 +6256,12 @@
};
-// The TwoByteString class captures sequential unicode string objects.
+// The SeqTwoByteString class captures sequential unicode string objects.
// Each character in the TwoByteString is a two-byte uint16_t.
class SeqTwoByteString: public SeqString {
public:
+ typedef uc16 CharType;
+
static const bool kHasAsciiEncoding = false;
// Dispatched behavior.
@@ -6258,6 +6278,14 @@
// Casting
static inline SeqTwoByteString* cast(Object* obj);
+ bool IsSeqTwoByteString() {
+ ASSERT(this->Object::IsSeqTwoByteString());
+ return true;
+ }
+ bool IsSeqAsciiString() {
+ ASSERT(this->Object::IsSeqTwoByteString());
+ return false;
+ }
// Garbage collection support. This method is called by the
// garbage collector to compute the actual size of a TwoByteString
@@ -6417,6 +6445,14 @@
static const int kSize = kResourceOffset + kPointerSize;
STATIC_CHECK(kResourceOffset == Internals::kStringResourceOffset);
+
+ // Shortcuts for templates that know their string type exactly.
+ bool IsSeqAsciiString() {
+ return false;
+ }
+ bool IsSeqTwoByteString() {
+ return false;
+ }
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ExternalString);
@@ -6427,6 +6463,8 @@
// ASCII string.
class ExternalAsciiString: public ExternalString {
public:
+ typedef char CharType;
+
static const bool kHasAsciiEncoding = true;
typedef v8::String::ExternalAsciiStringResource Resource;
@@ -6440,6 +6478,14 @@
// Casting.
static inline ExternalAsciiString* cast(Object* obj);
+ bool IsExternalAsciiString() {
+ ASSERT(this->Object::IsExternalAsciiString());
+ return true;
+ }
+ bool IsExternalTwoByteString() {
+ ASSERT(this->Object::IsExternalAsciiString());
+ return false;
+ }
// Garbage collection support.
inline void ExternalAsciiStringIterateBody(ObjectVisitor* v);
@@ -6460,10 +6506,11 @@
};
-// The ExternalTwoByteString class is an external string backed by a UTF-16
-// encoded string.
+// The ExternalTwoByteString class is an external string backed by a 16-bit
+// character sequence.
class ExternalTwoByteString: public ExternalString {
public:
+ typedef uc16 CharType;
static const bool kHasAsciiEncoding = false;
typedef v8::String::ExternalStringResource Resource;
@@ -6480,6 +6527,14 @@
// Casting.
static inline ExternalTwoByteString* cast(Object* obj);
+ bool IsExternalTwoByteString() {
+ ASSERT(this->Object::IsExternalTwoByteString());
+ return true;
+ }
+ bool IsExternalAsciiString() {
+ ASSERT(this->Object::IsExternalTwoByteString());
+ return false;
+ }
// Garbage collection support.
inline void ExternalTwoByteStringIterateBody(ObjectVisitor* v);
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri Sep 23 01:00:06 2011
+++ /branches/bleeding_edge/src/runtime.cc Mon Sep 26 04:14:41 2011
@@ -9106,13 +9106,22 @@
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
- source = Handle<String>(source->TryFlattenGetString());
- // Optimized fast case where we only have ascii characters.
+ source = FlattenGetString(source);
+ ASSERT(!source->IsConsString());
+ ASSERT(!source->IsSlicedString());
Handle<Object> result;
- if (source->IsSeqAsciiString()) {
- result = JsonParser<true>::Parse(source);
- } else {
- result = JsonParser<false>::Parse(source);
+ {
+ StringLock lock_representation(source);
+ if (source->IsSeqAsciiString()) {
+ result = JsonParser<SeqAsciiString>::Parse(source);
+ } else if (source->IsExternalTwoByteString()) {
+ result = JsonParser<ExternalTwoByteString>::Parse(source);
+ } else if (source->IsSeqTwoByteString()) {
+ result = JsonParser<SeqTwoByteString>::Parse(source);
+ } else {
+ ASSERT(source->IsExternalAsciiString());
+ result = JsonParser<ExternalAsciiString>::Parse(source);
+ }
}
if (result.is_null()) {
// Syntax error or stack overflow in scanner.
=======================================
--- /branches/bleeding_edge/test/cctest/test-api.cc Wed Sep 21 07:42:50 2011
+++ /branches/bleeding_edge/test/cctest/test-api.cc Mon Sep 26 04:14:41 2011
@@ -33,6 +33,7 @@
#include "isolate.h"
#include "compilation-cache.h"
#include "execution.h"
+#include "handles.h"
#include "snapshot.h"
#include "platform.h"
#include "utils.h"
@@ -15216,3 +15217,223 @@
foreign_context.Dispose();
}
+
+
+
+class SimpleTestResource: public String::ExternalStringResource {
+ public:
+ explicit SimpleTestResource(const uint16_t* data) : data_(data),
length_(0) {
+ while (data[length_] != 0) length_++;
+ }
+ virtual ~SimpleTestResource() { }
+ virtual const uint16_t* data() const { return data_; }
+ virtual size_t length() const { return length_; }
+ private:
+ const uint16_t* data_;
+ int length_;
+};
+
+
+THREADED_TEST(StringLock) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ i::Isolate* isolate = i::Isolate::Current();
+ i::Heap* heap = isolate->heap();
+
+ // ASCII text of test strings.
+ const char* text = "Lorem ipsum dolor sic amet.";
+ const size_t kTextLength = 27;
+ CHECK(strlen(text) == kTextLength);
+ // UC16 version of string.
+ uint16_t uc16_text[kTextLength + 1];
+ for (unsigned i = 0; i <= kTextLength; i++) {
+ uc16_text[i] = static_cast<uint16_t>(text[i]);
+ }
+
+ // Create some different strings and move them to old-space,
+ // so that externalization can succeede.
+ Local<String> s1 = v8_str(text);
+ Local<String> s2 = v8_str(text + 1);
+ Local<String> s3 = v8_str(text + 2);
+ heap->CollectGarbage(i::NEW_SPACE);
+ heap->CollectGarbage(i::NEW_SPACE);
+
+ SimpleTestResource* r1 = new SimpleTestResource(uc16_text);
+ SimpleTestResource* r2 = new SimpleTestResource(uc16_text + 1);
+ SimpleTestResource* r3 = new SimpleTestResource(uc16_text + 2);
+
+ // Check that externalization works.
+ CHECK(!s1->IsExternal());
+ CHECK(s1->MakeExternal(r1));
+ CHECK(s1->IsExternal());
+
+ // Externalize while the string is locked.
+
+ // Use the direct interface to string locking.
+ i::Handle<i::String> s2i(v8::Utils::OpenHandle(*s2));
+ CHECK(!heap->IsStringLocked(*s2i));
+ LockString(s2i); // Use handle version, not Heap::LockString, to allow
GC.
+ CHECK(isolate->heap()->IsStringLocked(*s2i));
+
+ CHECK(!s2->IsExternal());
+ CHECK(!s2->MakeExternal(r2));
+ CHECK(!s2->IsExternal());
+
+ CHECK(heap->IsStringLocked(*s2i));
+ heap->UnlockString(*s2i);
+ CHECK(!heap->IsStringLocked(*s2i));
+
+ CHECK(!s2->IsExternal());
+ CHECK(s2->MakeExternal(r2));
+ CHECK(s2->IsExternal());
+
+ // Use the Handle-based scoped StringLock.
+ i::Handle<i::String> s3i(v8::Utils::OpenHandle(*s3));
+ {
+ CHECK(!heap->IsStringLocked(*s3i));
+ i::StringLock lock(s3i);
+ CHECK(heap->IsStringLocked(*s3i));
+
+ CHECK(!s3->IsExternal());
+ CHECK(!s3->MakeExternal(r3));
+ CHECK(!s3->IsExternal());
+
+ CHECK(heap->IsStringLocked(*s3i));
+ }
+ CHECK(!heap->IsStringLocked(*s3i));
+
+ CHECK(!s3->IsExternal());
+ CHECK(s3->MakeExternal(r3));
+ CHECK(s3->IsExternal());
+}
+
+
+THREADED_TEST(MultiStringLock) {
+ v8::HandleScope scope;
+ LocalContext env;
+ i::Isolate* isolate = i::Isolate::Current();
+ i::Heap* heap = isolate->heap();
+ const char* text = "Lorem ipsum dolor sic amet.";
+
+ const int N = 16; // Must be power of 2.
+ CHECK_GT(strlen(text), static_cast<size_t>(N));
+
+ // Create a bunch of different strings.
+ i::Handle<i::String> strings[N];
+
+ for (int i = 0; i < N; i++) {
+ Local<String> s = v8_str(text + i);
+ strings[i] = v8::Utils::OpenHandle(*s);
+ }
+
+ heap->CollectGarbage(i::NEW_SPACE);
+ heap->CollectGarbage(i::NEW_SPACE);
+
+ // Check that start out are unlocked.
+ for (int i = 0; i < N; i++) {
+ CHECK(!heap->IsStringLocked(*strings[i]));
+ }
+
+ // Lock them, one at a time.
+ for (int i = 0; i < N; i++) {
+ LockString(strings[i]);
+ for (int j = 0; j < N; j++) {
+ if (j <= i) {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ } else {
+ CHECK(!heap->IsStringLocked(*strings[j]));
+ }
+ }
+ }
+
+ // Unlock them in a slightly different order (not same as locking,
+ // nor the reverse order).
+ for (int i = 0; i < N; i++) {
+ int mix_i = i ^ 3;
+ heap->UnlockString(*strings[mix_i]);
+ for (int j = 0; j < N; j++) {
+ int unmix_j = j ^ 3;
+ if (unmix_j <= i) {
+ CHECK(!heap->IsStringLocked(*strings[j]));
+ } else {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ }
+ }
+ }
+}
+
+
+THREADED_TEST(DuplicateStringLock) {
+ v8::HandleScope scope;
+ LocalContext env;
+ i::Isolate* isolate = i::Isolate::Current();
+ i::Heap* heap = isolate->heap();
+ const char* text = "Lorem ipsum dolor sic amet.";
+
+ const int N = 16; // Must be power of 2.
+ CHECK_GT(strlen(text), static_cast<size_t>(N));
+
+ // Create a bunch of different strings.
+ i::Handle<i::String> strings[N];
+
+ for (int i = 0; i < N; i++) {
+ Local<String> s = v8_str(text + i);
+ strings[i] = v8::Utils::OpenHandle(*s);
+ }
+
+ heap->CollectGarbage(i::NEW_SPACE);
+ heap->CollectGarbage(i::NEW_SPACE);
+
+ // Check that strings start out unlocked.
+ for (int i = 0; i < N; i++) {
+ CHECK(!heap->IsStringLocked(*strings[i]));
+ }
+
+ // Lock them, one at a time.
+ for (int i = 0; i < N; i++) {
+ LockString(strings[i]);
+ for (int j = 0; j < N; j++) {
+ if (j <= i) {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ } else {
+ CHECK(!heap->IsStringLocked(*strings[j]));
+ }
+ }
+ }
+
+ // Lock the first half of them a second time.
+ for (int i = 0; i < N / 2; i++) {
+ LockString(strings[i]);
+ for (int j = 0; j < N; j++) {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ }
+ }
+
+ // Unlock them in a slightly different order (not same as locking,
+ // nor the reverse order).
+ for (int i = 0; i < N; i++) {
+ int mix_i = i ^ 3;
+ heap->UnlockString(*strings[mix_i]);
+ for (int j = 0; j < N; j++) {
+ int unmix_j = j ^ 3;
+ if (unmix_j <= i && j >= N / 2) {
+ CHECK(!heap->IsStringLocked(*strings[j]));
+ } else {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ }
+ }
+ }
+
+ // Unlock the first half again.
+ for (int i = 0; i < N / 2; i++) {
+ heap->UnlockString(*strings[i]);
+ for (int j = 0; j < N; j++) {
+ if (j <= i || j >= N/ 2) {
+ CHECK(!heap->IsStringLocked(*strings[j]));
+ } else {
+ CHECK(heap->IsStringLocked(*strings[j]));
+ }
+ }
+ }
+}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev