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

Reply via email to