Push version 1.3.4 to trunk.

Added a readline() command to the d8 shell.

Fixed bug in json parsing.

Added idle notification to the API and reduced memory on idle notifications.

Review URL: http://codereview.chromium.org/164475

git-svn-id: http://v8.googlecode.com/svn/trunk@2682 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index e786802..4bfd8d5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2009-08-13: Version 1.3.4
+
+        Added a readline() command to the d8 shell.
+
+        Fixed bug in json parsing.
+
+        Added idle notification to the API and reduced memory on idle
+        notifications.
+
+
 2009-08-12: Version 1.3.3
 
         Fix issue 417: incorrect %t placeholder expansion.
diff --git a/include/v8.h b/include/v8.h
index 23c4425..d8de00c 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -2201,6 +2201,14 @@
    */
   static bool Dispose();
 
+
+  /**
+   * Optional notification that the embedder is idle.
+   * V8 uses the notification to reduce memory footprint.
+   * \param is_high_priority tells whether the embedder is high priority.
+   */
+  static void IdleNotification(bool is_high_priority);
+
  private:
   V8();
 
diff --git a/src/api.cc b/src/api.cc
index d02a2e4..0dc4fd7 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2558,6 +2558,10 @@
 }
 
 
+void  v8::V8::IdleNotification(bool is_high_priority) {
+  i::V8::IdleNotification(is_high_priority);
+}
+
 const char* v8::V8::GetVersion() {
   static v8::internal::EmbeddedVector<char, 128> buffer;
   v8::internal::Version::GetString(buffer);
@@ -2589,12 +2593,8 @@
   i::Handle<i::Context> env;
   {
     ENTER_V8;
-#if defined(ANDROID)
-    // On mobile devices, full GC is expensive.
-#else
     // Give the heap a chance to cleanup if we've disposed contexts.
     i::Heap::CollectAllGarbageIfContextDisposed();
-#endif
     v8::Handle<ObjectTemplate> proxy_template = global_template;
     i::Handle<i::FunctionTemplateInfo> proxy_constructor;
     i::Handle<i::FunctionTemplateInfo> global_constructor;
diff --git a/src/compiler.cc b/src/compiler.cc
index f0d97fe..5607f29 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -102,7 +102,7 @@
 
 
 static bool IsValidJSON(FunctionLiteral* lit) {
-  if (!lit->body()->length() == 1)
+  if (lit->body()->length() != 1)
     return false;
   Statement* stmt = lit->body()->at(0);
   if (stmt->AsExpressionStatement() == NULL)
@@ -114,7 +114,7 @@
 
 static Handle<JSFunction> MakeFunction(bool is_global,
                                        bool is_eval,
-                                       bool is_json,
+                                       Compiler::ValidationState validate,
                                        Handle<Script> script,
                                        Handle<Context> context,
                                        v8::Extension* extension,
@@ -129,6 +129,7 @@
   script->set_context_data((*i::Top::global_context())->data());
 
 #ifdef ENABLE_DEBUGGER_SUPPORT
+  bool is_json = (validate == Compiler::VALIDATE_JSON);
   if (is_eval || is_json) {
     script->set_compilation_type(
         is_json ? Smi::FromInt(Script::COMPILATION_TYPE_JSON) :
@@ -162,7 +163,7 @@
   // When parsing JSON we do an ordinary parse and then afterwards
   // check the AST to ensure it was well-formed.  If not we give a
   // syntax error.
-  if (is_json && !IsValidJSON(lit)) {
+  if (validate == Compiler::VALIDATE_JSON && !IsValidJSON(lit)) {
     HandleScope scope;
     Handle<JSArray> args = Factory::NewJSArray(1);
     Handle<Object> source(script->source());
@@ -282,7 +283,7 @@
     // Compile the function and add it to the cache.
     result = MakeFunction(true,
                           false,
-                          false,
+                          DONT_VALIDATE_JSON,
                           script,
                           Handle<Context>::null(),
                           extension,
@@ -305,7 +306,11 @@
 Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
                                          Handle<Context> context,
                                          bool is_global,
-                                         bool is_json) {
+                                         ValidationState validate) {
+  // Note that if validation is required then no path through this
+  // function is allowed to return a value without validating that
+  // the input is legal json.
+
   int source_length = source->length();
   Counters::total_eval_size.Increment(source_length);
   Counters::total_compile_size.Increment(source_length);
@@ -314,20 +319,26 @@
   VMState state(COMPILER);
 
   // Do a lookup in the compilation cache; if the entry is not there,
-  // invoke the compiler and add the result to the cache.
-  Handle<JSFunction> result =
-      CompilationCache::LookupEval(source, context, is_global);
+  // invoke the compiler and add the result to the cache.  If we're
+  // evaluating json we bypass the cache since we can't be sure a
+  // potential value in the cache has been validated.
+  Handle<JSFunction> result;
+  if (validate == DONT_VALIDATE_JSON)
+    result = CompilationCache::LookupEval(source, context, is_global);
+
   if (result.is_null()) {
     // Create a script object describing the script to be compiled.
     Handle<Script> script = Factory::NewScript(source);
     result = MakeFunction(is_global,
                           true,
-                          is_json,
+                          validate,
                           script,
                           context,
                           NULL,
                           NULL);
-    if (!result.is_null()) {
+    if (!result.is_null() && validate != VALIDATE_JSON) {
+      // For json it's unlikely that we'll ever see exactly the same
+      // string again so we don't use the compilation cache.
       CompilationCache::PutEval(source, context, is_global, result);
     }
   }
diff --git a/src/compiler.h b/src/compiler.h
index 9f02a8d..579970b 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -48,6 +48,8 @@
 
 class Compiler : public AllStatic {
  public:
+  enum ValidationState { VALIDATE_JSON, DONT_VALIDATE_JSON };
+
   // All routines return a JSFunction.
   // If an error occurs an exception is raised and
   // the return handle contains NULL.
@@ -63,7 +65,7 @@
   static Handle<JSFunction> CompileEval(Handle<String> source,
                                         Handle<Context> context,
                                         bool is_global,
-                                        bool is_json);
+                                        ValidationState validation);
 
   // Compile from function info (used for lazy compilation). Returns
   // true on success and false if the compilation resulted in a stack
diff --git a/src/d8.cc b/src/d8.cc
index be3615b..7082280 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -167,9 +167,6 @@
 
 
 Handle<Value> Shell::Read(const Arguments& args) {
-  if (args.Length() != 1) {
-    return ThrowException(String::New("Bad parameters"));
-  }
   String::Utf8Value file(args[0]);
   if (*file == NULL) {
     return ThrowException(String::New("Error loading file"));
@@ -182,6 +179,19 @@
 }
 
 
+Handle<Value> Shell::ReadLine(const Arguments& args) {
+  char line_buf[256];
+  if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
+    return ThrowException(String::New("Error reading line"));
+  }
+  int len = strlen(line_buf);
+  if (line_buf[len - 1] == '\n') {
+    --len;
+  }
+  return String::New(line_buf, len);
+}
+
+
 Handle<Value> Shell::Load(const Arguments& args) {
   for (int i = 0; i < args.Length(); i++) {
     HandleScope handle_scope;
@@ -404,6 +414,8 @@
   global_template->Set(String::New("print"), FunctionTemplate::New(Print));
   global_template->Set(String::New("write"), FunctionTemplate::New(Write));
   global_template->Set(String::New("read"), FunctionTemplate::New(Read));
+  global_template->Set(String::New("readline"),
+                       FunctionTemplate::New(ReadLine));
   global_template->Set(String::New("load"), FunctionTemplate::New(Load));
   global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
   global_template->Set(String::New("version"), FunctionTemplate::New(Version));
@@ -596,6 +608,8 @@
                        FunctionTemplate::New(Shell::Write));
   global_template->Set(String::New("read"),
                        FunctionTemplate::New(Shell::Read));
+  global_template->Set(String::New("readline"),
+                       FunctionTemplate::New(Shell::ReadLine));
   global_template->Set(String::New("load"),
                        FunctionTemplate::New(Shell::Load));
   global_template->Set(String::New("yield"),
diff --git a/src/d8.h b/src/d8.h
index 1bcc879..c93ea46 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -143,6 +143,7 @@
   static Handle<Value> Quit(const Arguments& args);
   static Handle<Value> Version(const Arguments& args);
   static Handle<Value> Read(const Arguments& args);
+  static Handle<Value> ReadLine(const Arguments& args);
   static Handle<Value> Load(const Arguments& args);
   // The OS object on the global object contains methods for performing
   // operating system calls:
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 3df11f7..c05feb4 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -161,6 +161,9 @@
 DEFINE_bool(collect_maps, true,
             "garbage collect maps from which no objects can be reached")
 
+// v8.cc
+DEFINE_bool(use_idle_notification, true,
+            "Use idle notification to reduce memory footprint.")
 // ic.cc
 DEFINE_bool(use_ic, true, "use inline caching")
 
diff --git a/src/heap.cc b/src/heap.cc
index 8ea1136..f8d22a2 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -425,6 +425,20 @@
 }
 
 
+void Heap::EnsureFromSpaceIsCommitted() {
+  if (new_space_.CommitFromSpaceIfNeeded()) return;
+
+  // Committing memory to from space failed.
+  // Try shrinking and try again.
+  Shrink();
+  if (new_space_.CommitFromSpaceIfNeeded()) return;
+
+  // Committing memory to from space failed again.
+  // Memory is exhausted and we will die.
+  V8::FatalProcessOutOfMemory("Committing semi space failed.");
+}
+
+
 void Heap::PerformGarbageCollection(AllocationSpace space,
                                     GarbageCollector collector,
                                     GCTracer* tracer) {
@@ -433,7 +447,7 @@
     ASSERT(!allocation_allowed_);
     global_gc_prologue_callback_();
   }
-
+  EnsureFromSpaceIsCommitted();
   if (collector == MARK_COMPACTOR) {
     MarkCompact(tracer);
 
diff --git a/src/heap.h b/src/heap.h
index 30522dc..179f9af 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -280,6 +280,9 @@
     return new_space_.allocation_limit_address();
   }
 
+  // Uncommit unused semi space.
+  static bool UncommitFromSpace() { return new_space_.UncommitFromSpace(); }
+
 #ifdef ENABLE_HEAP_PROTECTION
   // Protect/unprotect the heap by marking all spaces read-only/writable.
   static void Protect();
@@ -794,6 +797,9 @@
   // Rebuild remembered set in old and map spaces.
   static void RebuildRSets();
 
+  // Commits from space if it is uncommitted.
+  static void EnsureFromSpaceIsCommitted();
+
   //
   // Support for the API.
   //
diff --git a/src/messages.js b/src/messages.js
index fd505ff..8328fe5 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -28,88 +28,36 @@
 
 // -------------------------------------------------------------------
 
-const kVowelSounds = {a: true, e: true, i: true, o: true, u: true, y: true};
-const kCapitalVowelSounds = {a: true, e: true, i: true, o: true, u: true,
-    h: true, f: true, l: true, m: true, n: true, r: true, s: true, x: true,
-    y: true};
+// Lazily initialized.
+var kVowelSounds = 0;
+var kCapitalVowelSounds = 0;
+
 
 function GetInstanceName(cons) {
   if (cons.length == 0) {
     return "";
   }
   var first = %StringToLowerCase(StringCharAt.call(cons, 0));
-  var mapping = kVowelSounds;
+  if (kVowelSounds === 0) {
+    kVowelSounds = {a: true, e: true, i: true, o: true, u: true, y: true};
+    kCapitalVowelSounds = {a: true, e: true, i: true, o: true, u: true, h: true,
+        f: true, l: true, m: true, n: true, r: true, s: true, x: true, y: true};
+  }
+  var vowel_mapping = kVowelSounds;
   if (cons.length > 1 && (StringCharAt.call(cons, 0) != first)) {
     // First char is upper case
     var second = %StringToLowerCase(StringCharAt.call(cons, 1));
     // Second char is upper case
-    if (StringCharAt.call(cons, 1) != second)
-      mapping = kCapitalVowelSounds;
+    if (StringCharAt.call(cons, 1) != second) {
+      vowel_mapping = kCapitalVowelSounds;
+    }
   }
-  var s = mapping[first] ? "an " : "a ";
+  var s = vowel_mapping[first] ? "an " : "a ";
   return s + cons;
 }
 
 
-const kMessages = {
-  // Error
-  cyclic_proto:                 "Cyclic __proto__ value",
-  // TypeError
-  unexpected_token:             "Unexpected token %0",
-  unexpected_token_number:      "Unexpected number",
-  unexpected_token_string:      "Unexpected string",
-  unexpected_token_identifier:  "Unexpected identifier",
-  unexpected_eos:               "Unexpected end of input",
-  malformed_regexp:             "Invalid regular expression: /%0/: %1",
-  unterminated_regexp:          "Invalid regular expression: missing /",
-  regexp_flags:                 "Cannot supply flags when constructing one RegExp from another",
-  invalid_lhs_in_assignment:    "Invalid left-hand side in assignment",
-  invalid_lhs_in_for_in:        "Invalid left-hand side in for-in",
-  invalid_lhs_in_postfix_op:    "Invalid left-hand side expression in postfix operation",
-  invalid_lhs_in_prefix_op:     "Invalid left-hand side expression in prefix operation",
-  multiple_defaults_in_switch:  "More than one default clause in switch statement",
-  newline_after_throw:          "Illegal newline after throw",
-  redeclaration:                "%0 '%1' has already been declared",
-  no_catch_or_finally:          "Missing catch or finally after try",
-  unknown_label:                "Undefined label '%0'",
-  uncaught_exception:           "Uncaught %0",
-  stack_trace:                  "Stack Trace:\n%0",
-  called_non_callable:          "%0 is not a function",
-  undefined_method:             "Object %1 has no method '%0'",
-  property_not_function:        "Property '%0' of object %1 is not a function",
-  cannot_convert_to_primitive:  "Cannot convert object to primitive value",
-  not_constructor:              "%0 is not a constructor",
-  not_defined:                  "%0 is not defined",
-  non_object_property_load:     "Cannot read property '%0' of %1",
-  non_object_property_store:    "Cannot set property '%0' of %1",
-  non_object_property_call:     "Cannot call method '%0' of %1",
-  with_expression:              "%0 has no properties",
-  illegal_invocation:           "Illegal invocation",
-  no_setter_in_callback:        "Cannot set property %0 of %1 which has only a getter",
-  apply_non_function:           "Function.prototype.apply was called on %0, which is a %1 and not a function",
-  apply_wrong_args:             "Function.prototype.apply: Arguments list has wrong type",
-  invalid_in_operator_use:      "Cannot use 'in' operator to search for '%0' in %1",
-  instanceof_function_expected: "Expecting a function in instanceof check, but got %0",
-  instanceof_nonobject_proto:   "Function has non-object prototype '%0' in instanceof check",
-  null_to_object:               "Cannot convert null to object",
-  reduce_no_initial:            "Reduce of empty array with no initial value",
-  // RangeError
-  invalid_array_length:         "Invalid array length",
-  stack_overflow:               "Maximum call stack size exceeded",
-  apply_overflow:               "Function.prototype.apply cannot support %0 arguments",
-  // SyntaxError
-  unable_to_parse:              "Parse error",
-  duplicate_regexp_flag:        "Duplicate RegExp flag %0",
-  invalid_regexp:               "Invalid RegExp pattern /%0/",
-  illegal_break:                "Illegal break statement",
-  illegal_continue:             "Illegal continue statement",
-  illegal_return:               "Illegal return statement",
-  error_loading_debugger:       "Error loading debugger %0",
-  no_input_to_regexp:           "No input to %0",
-  result_not_primitive:         "Result of %0 must be a primitive, was %1",
-  invalid_json:                 "String '%0' is not valid JSON",
-  circular_structure:           "Converting circular structure to JSON"
-};
+var kMessages = 0;
 
 
 function FormatString(format, args) {
@@ -161,6 +109,67 @@
 
 // Helper functions; called from the runtime system.
 function FormatMessage(message) {
+  if (kMessages === 0) {
+    kMessages = {
+      // Error
+      cyclic_proto:                 "Cyclic __proto__ value",
+      // TypeError
+      unexpected_token:             "Unexpected token %0",
+      unexpected_token_number:      "Unexpected number",
+      unexpected_token_string:      "Unexpected string",
+      unexpected_token_identifier:  "Unexpected identifier",
+      unexpected_eos:               "Unexpected end of input",
+      malformed_regexp:             "Invalid regular expression: /%0/: %1",
+      unterminated_regexp:          "Invalid regular expression: missing /",
+      regexp_flags:                 "Cannot supply flags when constructing one RegExp from another",
+      invalid_lhs_in_assignment:    "Invalid left-hand side in assignment",
+      invalid_lhs_in_for_in:        "Invalid left-hand side in for-in",
+      invalid_lhs_in_postfix_op:    "Invalid left-hand side expression in postfix operation",
+      invalid_lhs_in_prefix_op:     "Invalid left-hand side expression in prefix operation",
+      multiple_defaults_in_switch:  "More than one default clause in switch statement",
+      newline_after_throw:          "Illegal newline after throw",
+      redeclaration:                "%0 '%1' has already been declared",
+      no_catch_or_finally:          "Missing catch or finally after try",
+      unknown_label:                "Undefined label '%0'",
+      uncaught_exception:           "Uncaught %0",
+      stack_trace:                  "Stack Trace:\n%0",
+      called_non_callable:          "%0 is not a function",
+      undefined_method:             "Object %1 has no method '%0'",
+      property_not_function:        "Property '%0' of object %1 is not a function",
+      cannot_convert_to_primitive:  "Cannot convert object to primitive value",
+      not_constructor:              "%0 is not a constructor",
+      not_defined:                  "%0 is not defined",
+      non_object_property_load:     "Cannot read property '%0' of %1",
+      non_object_property_store:    "Cannot set property '%0' of %1",
+      non_object_property_call:     "Cannot call method '%0' of %1",
+      with_expression:              "%0 has no properties",
+      illegal_invocation:           "Illegal invocation",
+      no_setter_in_callback:        "Cannot set property %0 of %1 which has only a getter",
+      apply_non_function:           "Function.prototype.apply was called on %0, which is a %1 and not a function",
+      apply_wrong_args:             "Function.prototype.apply: Arguments list has wrong type",
+      invalid_in_operator_use:      "Cannot use 'in' operator to search for '%0' in %1",
+      instanceof_function_expected: "Expecting a function in instanceof check, but got %0",
+      instanceof_nonobject_proto:   "Function has non-object prototype '%0' in instanceof check",
+      null_to_object:               "Cannot convert null to object",
+      reduce_no_initial:            "Reduce of empty array with no initial value",
+      // RangeError
+      invalid_array_length:         "Invalid array length",
+      stack_overflow:               "Maximum call stack size exceeded",
+      apply_overflow:               "Function.prototype.apply cannot support %0 arguments",
+      // SyntaxError
+      unable_to_parse:              "Parse error",
+      duplicate_regexp_flag:        "Duplicate RegExp flag %0",
+      invalid_regexp:               "Invalid RegExp pattern /%0/",
+      illegal_break:                "Illegal break statement",
+      illegal_continue:             "Illegal continue statement",
+      illegal_return:               "Illegal return statement",
+      error_loading_debugger:       "Error loading debugger %0",
+      no_input_to_regexp:           "No input to %0",
+      result_not_primitive:         "Result of %0 must be a primitive, was %1",
+      invalid_json:                 "String '%0' is not valid JSON",
+      circular_structure:           "Converting circular structure to JSON"
+    };
+  }
   var format = kMessages[message.type];
   if (!format) return "<unknown message " + message.type + ">";
   return FormatString(format, message.args);
diff --git a/src/runtime.cc b/src/runtime.cc
index 56e9f85..0da4be8 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -4973,10 +4973,12 @@
 
   // Compile source string in the global context.
   Handle<Context> context(Top::context()->global_context());
+  Compiler::ValidationState validate = (is_json->IsTrue())
+    ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
   Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
                                                          context,
                                                          true,
-                                                         is_json->IsTrue());
+                                                         validate);
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> fun =
       Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -5000,8 +5002,11 @@
   bool is_global = context->IsGlobalContext();
 
   // Compile source string in the current context.
-  Handle<JSFunction> boilerplate =
-      Compiler::CompileEval(source, context, is_global, false);
+  Handle<JSFunction> boilerplate = Compiler::CompileEval(
+      source,
+      context,
+      is_global,
+      Compiler::DONT_VALIDATE_JSON);
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> fun =
     Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -7043,7 +7048,7 @@
       Compiler::CompileEval(function_source,
                             context,
                             context->IsGlobalContext(),
-                            false);
+                            Compiler::DONT_VALIDATE_JSON);
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> compiled_function =
       Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -7111,7 +7116,7 @@
       Handle<JSFunction>(Compiler::CompileEval(source,
                                                context,
                                                true,
-                                               false));
+                                               Compiler::DONT_VALIDATE_JSON));
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> compiled_function =
       Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
diff --git a/src/spaces.cc b/src/spaces.cc
index 6283bba..9f266cb 100644
--- a/src/spaces.cc
+++ b/src/spaces.cc
@@ -340,6 +340,17 @@
   return true;
 }
 
+bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
+  ASSERT(start != NULL);
+  ASSERT(size > 0);
+  ASSERT(initial_chunk_ != NULL);
+  ASSERT(InInitialChunk(start));
+  ASSERT(InInitialChunk(start + size - 1));
+
+  if (!initial_chunk_->Uncommit(start, size)) return false;
+  Counters::memory_allocated.Decrement(size);
+  return true;
+}
 
 Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk,
                                               PagedSpace* owner) {
@@ -1039,6 +1050,26 @@
 #endif
 
 
+bool SemiSpace::Commit() {
+  ASSERT(!is_committed());
+  if (!MemoryAllocator::CommitBlock(start_, capacity_, executable())) {
+    return false;
+  }
+  committed_ = true;
+  return true;
+}
+
+
+bool SemiSpace::Uncommit() {
+  ASSERT(is_committed());
+  if (!MemoryAllocator::UncommitBlock(start_, capacity_)) {
+    return false;
+  }
+  committed_ = false;
+  return true;
+}
+
+
 // -----------------------------------------------------------------------------
 // SemiSpace implementation
 
@@ -1053,18 +1084,15 @@
   // addresses.
   capacity_ = initial_capacity;
   maximum_capacity_ = maximum_capacity;
-
-  if (!MemoryAllocator::CommitBlock(start, capacity_, executable())) {
-    return false;
-  }
+  committed_ = false;
 
   start_ = start;
   address_mask_ = ~(maximum_capacity - 1);
   object_mask_ = address_mask_ | kHeapObjectTag;
   object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
-
   age_mark_ = start_;
-  return true;
+
+  return Commit();
 }
 
 
@@ -1076,7 +1104,7 @@
 
 bool SemiSpace::Grow() {
   // Commit 50% extra space but only up to maximum capacity.
-  int extra = capacity_/2;
+  int extra = RoundUp(capacity_ / 2, OS::AllocateAlignment());
   if (capacity_ + extra > maximum_capacity_) {
     extra = maximum_capacity_ - capacity_;
   }
diff --git a/src/spaces.h b/src/spaces.h
index 4df3388..4760a42 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -367,6 +367,13 @@
   // and false otherwise.
   static bool CommitBlock(Address start, size_t size, Executability executable);
 
+
+  // Uncommit a contiguous block of memory [start..(start+size)[.
+  // start is not NULL, the size is greater than zero, and the
+  // block is contained in the initial chunk.  Returns true if it succeeded
+  // and false otherwise.
+  static bool UncommitBlock(Address start, size_t size);
+
   // Attempts to allocate the requested (non-zero) number of pages from the
   // OS.  Fewer pages might be allocated than requested. If it fails to
   // allocate memory for the OS or cannot allocate a single page, this
@@ -1035,6 +1042,10 @@
     return 0;
   }
 
+  bool is_committed() { return committed_; }
+  bool Commit();
+  bool Uncommit();
+
 #ifdef DEBUG
   virtual void Print();
   virtual void Verify();
@@ -1058,6 +1069,8 @@
   uintptr_t object_mask_;
   uintptr_t object_expected_;
 
+  bool committed_;
+
  public:
   TRACK_MEMORY("SemiSpace")
 };
@@ -1250,6 +1263,17 @@
   void RecordPromotion(HeapObject* obj);
 #endif
 
+  // Return whether the operation succeded.
+  bool CommitFromSpaceIfNeeded() {
+    if (from_space_.is_committed()) return true;
+    return from_space_.Commit();
+  }
+
+  bool UncommitFromSpace() {
+    if (!from_space_.is_committed()) return true;
+    return from_space_.Uncommit();
+  }
+
  private:
   // The current and maximum capacities of a semispace.
   int capacity_;
diff --git a/src/uri.js b/src/uri.js
index fe659aa..0dfe765 100644
--- a/src/uri.js
+++ b/src/uri.js
@@ -39,6 +39,10 @@
 
 
 function URIEncodeOctets(octets, result, index) {
+  if (hexCharCodeArray === 0) {
+    hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+                        65, 66, 67, 68, 69, 70];
+  }
   index = URIAddEncodedOctetToBuffer(octets[0], result, index);
   if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
   if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
@@ -316,11 +320,9 @@
 }
 
 
-const hexCharArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-                      "A", "B", "C", "D", "E", "F"];
-
-const hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
-                          65, 66, 67, 68, 69, 70];
+// Lazily initialized.
+var hexCharArray = 0;
+var hexCharCodeArray = 0;
 
 
 function HexValueOf(c) {
@@ -341,6 +343,10 @@
 // 64 -> 0040, 62234 -> F31A.
 function CharCodeToHex4Str(cc) {
   var r = "";
+  if (hexCharArray === 0) {
+    hexCharArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+                    "A", "B", "C", "D", "E", "F"];
+  }
   for (var i = 0; i < 4; ++i) {
     var c = hexCharArray[cc & 0x0F];
     r = c + r;
diff --git a/src/v8.cc b/src/v8.cc
index 72f74aa..00e0e6e 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -156,6 +156,15 @@
   return (hi << 16) + (lo & 0xFFFF);
 }
 
+void V8::IdleNotification(bool is_high_priority) {
+  if (!FLAG_use_idle_notification) return;
+  // Ignore high priority instances of V8.
+  if (is_high_priority) return;
+
+  // Uncommit unused memory in new space.
+  Heap::UncommitFromSpace();
+}
+
 
 Smi* V8::RandomPositiveSmi() {
   uint32_t random = Random();
diff --git a/src/v8.h b/src/v8.h
index 2cfce3d..1ca3245 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -99,6 +99,9 @@
   static uint32_t Random();
   static Smi* RandomPositiveSmi();
 
+  // Idle notification directly from the API.
+  static void IdleNotification(bool is_high_priority);
+
  private:
   // True if engine is currently running
   static bool is_running_;
diff --git a/src/version.cc b/src/version.cc
index b141120..7bc52a8 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
 // cannot be changed without changing the SCons build script.
 #define MAJOR_VERSION     1
 #define MINOR_VERSION     3
-#define BUILD_NUMBER      3
+#define BUILD_NUMBER      4
 #define PATCH_LEVEL       0
 #define CANDIDATE_VERSION false
 
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 58c5b47..b3df929 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -5258,6 +5258,58 @@
       }
       break;
 
+    case Token::SHL:
+      if (reversed) {
+        Result constant_operand(value);
+        LikelySmiBinaryOperation(op, &constant_operand, operand,
+                                 overwrite_mode);
+      } else {
+        // Only the least significant 5 bits of the shift value are used.
+        // In the slow case, this masking is done inside the runtime call.
+        int shift_value = int_value & 0x1f;
+        operand->ToRegister();
+        if (shift_value == 0) {
+          // Spill operand so it can be overwritten in the slow case.
+          frame_->Spill(operand->reg());
+          DeferredInlineSmiOperation* deferred =
+              new DeferredInlineSmiOperation(op,
+                                             operand->reg(),
+                                             operand->reg(),
+                                             smi_value,
+                                             overwrite_mode);
+          __ testl(operand->reg(), Immediate(kSmiTagMask));
+          deferred->Branch(not_zero);
+          deferred->BindExit();
+          frame_->Push(operand);
+        } else {
+          // Use a fresh temporary for nonzero shift values.
+          Result answer = allocator()->Allocate();
+          ASSERT(answer.is_valid());
+          DeferredInlineSmiOperation* deferred =
+              new DeferredInlineSmiOperation(op,
+                                             answer.reg(),
+                                             operand->reg(),
+                                             smi_value,
+                                             overwrite_mode);
+          __ testl(operand->reg(), Immediate(kSmiTagMask));
+          deferred->Branch(not_zero);
+          __ movl(answer.reg(), operand->reg());
+          ASSERT(kSmiTag == 0);  // adjust code if not the case
+          // We do no shifts, only the Smi conversion, if shift_value is 1.
+          if (shift_value > 1) {
+            __ shll(answer.reg(), Immediate(shift_value - 1));
+          }
+          // Convert int result to Smi, checking that it is in int range.
+          ASSERT(kSmiTagSize == 1);  // adjust code if not the case
+          __ addl(answer.reg(), answer.reg());
+          deferred->Branch(overflow);
+          deferred->BindExit();
+          operand->Unuse();
+          frame_->Push(&answer);
+        }
+      }
+      break;
+
     case Token::BIT_OR:
     case Token::BIT_XOR:
     case Token::BIT_AND: {
@@ -6013,6 +6065,8 @@
         __ testl(key.reg(),
                  Immediate(static_cast<uint32_t>(kSmiTagMask | 0x80000000U)));
         deferred->Branch(not_zero);
+        // Ensure that the smi is zero-extended.  This is not guaranteed.
+        __ movl(key.reg(), key.reg());
 
         // Check that the receiver is not a smi.
         __ testl(receiver.reg(), Immediate(kSmiTagMask));
@@ -7172,14 +7226,14 @@
   __ jmp(&done);
 
   __ bind(&load_smi_1);
-  __ sar(kScratchRegister, Immediate(kSmiTagSize));
+  __ sarl(kScratchRegister, Immediate(kSmiTagSize));
   __ push(kScratchRegister);
   __ fild_s(Operand(rsp, 0));
   __ pop(kScratchRegister);
   __ jmp(&done_load_1);
 
   __ bind(&load_smi_2);
-  __ sar(kScratchRegister, Immediate(kSmiTagSize));
+  __ sarl(kScratchRegister, Immediate(kSmiTagSize));
   __ push(kScratchRegister);
   __ fild_s(Operand(rsp, 0));
   __ pop(kScratchRegister);
@@ -7534,7 +7588,7 @@
         __ j(negative, &non_smi_result);
       }
       // Tag smi result and return.
-      ASSERT(kSmiTagSize == times_2);  // adjust code if not the case
+      ASSERT(kSmiTagSize == 1);  // adjust code if not the case
       __ lea(rax, Operand(rax, rax, times_1, kSmiTag));
       __ ret(2 * kPointerSize);
 
diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc
index cc8365c..d8d6dbb 100644
--- a/src/x64/disasm-x64.cc
+++ b/src/x64/disasm-x64.cc
@@ -105,7 +105,6 @@
 static ByteMnemonic zero_operands_instr[] = {
   { 0xC3, UNSET_OP_ORDER, "ret" },
   { 0xC9, UNSET_OP_ORDER, "leave" },
-  { 0x90, UNSET_OP_ORDER, "nop" },
   { 0xF4, UNSET_OP_ORDER, "hlt" },
   { 0xCC, UNSET_OP_ORDER, "int3" },
   { 0x60, UNSET_OP_ORDER, "pushad" },
@@ -1425,7 +1424,7 @@
           default:
             UNREACHABLE();
         }
-        AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"ux",
+        AppendToBuffer("test%c rax,0x%"V8_PTR_PREFIX"x",
                        operand_size_code(),
                        value);
         break;
diff --git a/test/mjsunit/json.js b/test/mjsunit/json.js
index 4758264..bf44f78 100644
--- a/test/mjsunit/json.js
+++ b/test/mjsunit/json.js
@@ -195,3 +195,13 @@
 
 assertEquals(undefined, JSON.stringify(undefined));
 assertEquals(undefined, JSON.stringify(function () { }));
+
+function checkIllegal(str) {
+  assertThrows(function () { JSON.parse(str); }, SyntaxError);
+}
+
+checkIllegal('1); throw "foo"; (1');
+
+var x = 0;
+eval("(1); x++; (1)");
+checkIllegal('1); x++; (1');