Fixed crash bug that occurred when loading a const variable in the presence of eval.

Allowed using with and eval in registered extensions in debug mode by fixing bogus assert.

Fixed the source position for function returns to enable the debugger to break there.


git-svn-id: http://v8.googlecode.com/svn/trunk@1717 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index aeef611..d5c30c7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2009-04-15: Version 1.1.10
+
+        Fixed crash bug that occurred when loading a const variable in the
+        presence of eval.
+
+        Allowed using with and eval in registered extensions in debug mode
+        by fixing bogus assert.
+
+        Fixed the source position for function returns to enable the
+        debugger to break there.
+
+
 2009-04-14: Version 1.1.9
 
         Made the stack traversal code in the profiler robust by avoiding
diff --git a/samples/process.cc b/samples/process.cc
index 6567f08..511e21a 100644
--- a/samples/process.cc
+++ b/samples/process.cc
@@ -27,8 +27,6 @@
 
 #include <v8.h>
 
-// To avoid warnings from <map> on windows we disable exceptions.
-#define _HAS_EXCEPTIONS 0
 #include <string>
 #include <map>
 
diff --git a/src/api.cc b/src/api.cc
index 756f491..9f3024e 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2373,7 +2373,7 @@
 
 
 const char* v8::V8::GetVersion() {
-  return "1.1.9.1";
+  return "1.1.10";
 }
 
 
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 37a7cf2..6fdabc3 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -2446,8 +2446,13 @@
                                                  r1,
                                                  r2,
                                                  &slow));
+        if (potential_slot->var()->mode() == Variable::CONST) {
+          __ cmp(r0, Operand(Factory::the_hole_value()));
+          __ mov(r0, Operand(Factory::undefined_value()), LeaveCC, eq);
+        }
         // There is always control flow to slow from
-        // ContextSlotOperandCheckExtensions.
+        // ContextSlotOperandCheckExtensions so we have to jump around
+        // it.
         done.Jump();
       }
     }
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index 714e5bd..663f235 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -3286,8 +3286,14 @@
                ContextSlotOperandCheckExtensions(potential_slot,
                                                  value,
                                                  &slow));
+        if (potential_slot->var()->mode() == Variable::CONST) {
+          __ cmp(value.reg(), Factory::the_hole_value());
+          done.Branch(not_equal, &value);
+          __ mov(value.reg(), Factory::undefined_value());
+        }
         // There is always control flow to slow from
-        // ContextSlotOperandCheckExtensions.
+        // ContextSlotOperandCheckExtensions so we have to jump around
+        // it.
         done.Jump(&value);
       }
     }
diff --git a/src/codegen.cc b/src/codegen.cc
index a3c55d4..edc498d 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -571,7 +571,7 @@
 
 void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
   if (FLAG_debug_info) {
-    int pos = fun->start_position();
+    int pos = fun->end_position();
     if (pos != RelocInfo::kNoPosition) {
       masm()->RecordStatementPosition(pos);
       masm()->RecordPosition(pos);
diff --git a/src/d8-posix.cc b/src/d8-posix.cc
index d7ea2ec..c2dc531 100644
--- a/src/d8-posix.cc
+++ b/src/d8-posix.cc
@@ -105,17 +105,17 @@
 // Returns false on timeout, true on data ready.
 static bool WaitOnFD(int fd,
                      int read_timeout,
-                     int* total_timeout,
+                     int total_timeout,
                      struct timeval& start_time) {
   fd_set readfds, writefds, exceptfds;
   struct timeval timeout;
-  if (*total_timeout != -1) {
+  int gone = 0;
+  if (total_timeout != -1) {
     struct timeval time_now;
     gettimeofday(&time_now, NULL);
     int seconds = time_now.tv_sec - start_time.tv_sec;
-    int gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
-    if (gone >= *total_timeout) return false;
-    *total_timeout -= gone;
+    gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
+    if (gone >= total_timeout) return false;
   }
   FD_ZERO(&readfds);
   FD_ZERO(&writefds);
@@ -123,8 +123,8 @@
   FD_SET(fd, &readfds);
   FD_SET(fd, &exceptfds);
   if (read_timeout == -1 ||
-      (*total_timeout != -1 && *total_timeout < read_timeout)) {
-    read_timeout = *total_timeout;
+      (total_timeout != -1 && total_timeout - gone < read_timeout)) {
+    read_timeout = total_timeout - gone;
   }
   timeout.tv_usec = (read_timeout % 1000) * 1000;
   timeout.tv_sec = read_timeout / 1000;
@@ -306,7 +306,7 @@
 static Handle<Value> GetStdout(int child_fd,
                                struct timeval& start_time,
                                int read_timeout,
-                               int* total_timeout) {
+                               int total_timeout) {
   Handle<String> accumulator = String::Empty();
   const char* source = "function(a, b) { return a + b; }";
   Handle<Value> cons_as_obj(Script::Compile(String::New(source))->Run());
@@ -332,7 +332,7 @@
                       read_timeout,
                       total_timeout,
                       start_time) ||
-            (TimeIsOut(start_time, *total_timeout))) {
+            (TimeIsOut(start_time, total_timeout))) {
           return ThrowException(String::New("Timed out waiting for output"));
         }
         continue;
@@ -502,7 +502,7 @@
   Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD],
                                         start_time,
                                         read_timeout,
-                                        &total_timeout);
+                                        total_timeout);
   if (accumulator->IsUndefined()) {
     kill(pid, SIGINT);  // On timeout, kill the subprocess.
     return accumulator;
diff --git a/src/func-name-inferrer.h b/src/func-name-inferrer.h
index 4e49285..9dcf7c5 100644
--- a/src/func-name-inferrer.h
+++ b/src/func-name-inferrer.h
@@ -70,7 +70,8 @@
 
   void SetFuncToInfer(FunctionLiteral* func_to_infer) {
     if (IsOpen()) {
-      ASSERT(func_to_infer_ == NULL);
+      // If we encounter another function literal after already having
+      // encountered one, the second one replaces the first.
       func_to_infer_ = func_to_infer;
     }
   }
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
index 916bc27..9c9d713 100644
--- a/src/mirror-delay.js
+++ b/src/mirror-delay.js
@@ -1715,8 +1715,8 @@
   // Collect the JSON property/value pairs in an array.
   var content = new Array();
 
-  // Add the handle for value mirrors.
-  if (mirror.isValue()) {
+  // Add the mirror handle.
+  if (mirror.isValue() || mirror.isScript()) {
     content.push(MakeJSONPair_('handle', NumberToJSON_(mirror.handle())));
   }
 
@@ -1771,10 +1771,11 @@
       break;
 
     case SCRIPT_TYPE:
-      // Script is represented by name and source attributes.
+      // Script is represented by id, name and source attributes.
       if (mirror.name()) {
         content.push(MakeJSONPair_('name', StringToJSON_(mirror.name())));
       }
+      content.push(MakeJSONPair_('id', NumberToJSON_(mirror.id())));
       content.push(MakeJSONPair_('lineOffset',
                                  NumberToJSON_(mirror.lineOffset())));
       content.push(MakeJSONPair_('columnOffset',
@@ -1908,7 +1909,12 @@
   content.push(MakeJSONPair_('index', NumberToJSON_(mirror.index())));
   content.push(MakeJSONPair_('receiver',
                              this.serializeReference(mirror.receiver())));
-  content.push(MakeJSONPair_('func', this.serializeReference(mirror.func())));
+  var func = mirror.func();
+  content.push(MakeJSONPair_('func', this.serializeReference(func)));
+  if (func.script()) {
+    content.push(MakeJSONPair_('script',
+                               this.serializeReference(func.script())));
+  }
   content.push(MakeJSONPair_('constructCall',
                              BooleanToJSON_(mirror.isConstructCall())));
   content.push(MakeJSONPair_('debuggerFrame',
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 40a9019..4891f37 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -25,8 +25,6 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-// To avoid warnings from <map> on windows we disable exceptions.
-#define _HAS_EXCEPTIONS 0
 #include <signal.h>
 #include <string>
 #include <map>
diff --git a/src/parser.cc b/src/parser.cc
index fc0ca4d..9db10cf 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -2092,7 +2092,7 @@
   // code. If 'with' statements were allowed, the simplified setup of
   // the runtime context chain would allow access to properties in the
   // global object from within a 'with' statement.
-  ASSERT(!Bootstrapper::IsActive());
+  ASSERT(extension_ != NULL || !Bootstrapper::IsActive());
 
   Expect(Token::WITH, CHECK_OK);
   Expect(Token::LPAREN, CHECK_OK);
@@ -2761,7 +2761,7 @@
             if (var == NULL) {
               // We do not allow direct calls to 'eval' in our internal
               // JS files. Use builtin functions instead.
-              ASSERT(!Bootstrapper::IsActive());
+              ASSERT(extension_ != NULL || !Bootstrapper::IsActive());
               top_scope_->RecordEvalCall();
               is_potentially_direct_eval = true;
             }
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index 5cfc62c..b58000a 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -646,7 +646,7 @@
 
 void AstPrinter::PrintIndented(const char* txt) {
   for (int i = 0; i < indent_; i++) {
-    Print(".   ");
+    Print(". ");
   }
   Print(txt);
 }
@@ -732,7 +732,7 @@
   if (scope->num_parameters() > 0) {
     IndentedScope indent("PARAMS");
     for (int i = 0; i < scope->num_parameters(); i++) {
-      PrintLiteralWithModeIndented("VAR ", scope->parameter(i),
+      PrintLiteralWithModeIndented("VAR", scope->parameter(i),
                                    scope->parameter(i)->name(),
                                    scope->parameter(i)->type());
     }
@@ -1024,7 +1024,7 @@
   Visit(node->obj());
   Literal* literal = node->key()->AsLiteral();
   if (literal != NULL && literal->handle()->IsSymbol()) {
-    PrintLiteralIndented("LITERAL", literal->handle(), false);
+    PrintLiteralIndented("NAME", literal->handle(), false);
   } else {
     PrintIndentedVisit("KEY", node->key());
   }
diff --git a/src/virtual-frame-arm.h b/src/virtual-frame-arm.h
index f15eec2..af3c08c 100644
--- a/src/virtual-frame-arm.h
+++ b/src/virtual-frame-arm.h
@@ -85,7 +85,7 @@
   }
 
   bool is_used(Register reg) {
-    return is_used(reg.code()) != kIllegalIndex;
+    return is_used(reg.code());
   }
 
   // Add extra in-memory elements to the top of the frame to match an actual
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index d899e8d..23e57d5 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -2495,6 +2495,44 @@
 }
 
 
+static const char* kEvalExtensionSource =
+  "function UseEval() {"
+  "  var x = 42;"
+  "  return eval('x');"
+  "}";
+
+
+THREADED_TEST(UseEvalFromExtension) {
+  v8::HandleScope handle_scope;
+  v8::RegisterExtension(new Extension("evaltest", kEvalExtensionSource));
+  const char* extension_names[] = { "evaltest" };
+  v8::ExtensionConfiguration extensions(1, extension_names);
+  v8::Handle<Context> context = Context::New(&extensions);
+  Context::Scope lock(context);
+  v8::Handle<Value> result = Script::Compile(v8_str("UseEval()"))->Run();
+  CHECK_EQ(result, v8::Integer::New(42));
+}
+
+
+static const char* kWithExtensionSource =
+  "function UseWith() {"
+  "  var x = 42;"
+  "  with({x:87}) { return x; }"
+  "}";
+
+
+THREADED_TEST(UseWithFromExtension) {
+  v8::HandleScope handle_scope;
+  v8::RegisterExtension(new Extension("withtest", kWithExtensionSource));
+  const char* extension_names[] = { "withtest" };
+  v8::ExtensionConfiguration extensions(1, extension_names);
+  v8::Handle<Context> context = Context::New(&extensions);
+  Context::Scope lock(context);
+  v8::Handle<Value> result = Script::Compile(v8_str("UseWith()"))->Run();
+  CHECK_EQ(result, v8::Integer::New(87));
+}
+
+
 THREADED_TEST(AutoExtensions) {
   v8::HandleScope handle_scope;
   Extension* extension = new Extension("autotest", kSimpleExtensionSource);
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 5926a8b..03ef70d 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -498,7 +498,7 @@
 // ---
 
 
-// Source for The JavaScript function which picks out the function name on the
+// Source for The JavaScript function which picks out the function name of the
 // top frame.
 const char* frame_function_name_source =
     "function frame_function_name(exec_state) {"
@@ -507,6 +507,24 @@
 v8::Local<v8::Function> frame_function_name;
 
 
+// Source for The JavaScript function which picks out the source line for the
+// top frame.
+const char* frame_source_line_source =
+    "function frame_source_line(exec_state) {"
+    "  return exec_state.frame(0).sourceLine();"
+    "}";
+v8::Local<v8::Function> frame_source_line;
+
+
+// Source for The JavaScript function which picks out the source column for the
+// top frame.
+const char* frame_source_column_source =
+    "function frame_source_column(exec_state) {"
+    "  return exec_state.frame(0).sourceColumn();"
+    "}";
+v8::Local<v8::Function> frame_source_column;
+
+
 // Source for The JavaScript function which returns the number of frames.
 static const char* frame_count_source =
     "function frame_count(exec_state) {"
@@ -518,6 +536,10 @@
 // Global variable to store the last function hit - used by some tests.
 char last_function_hit[80];
 
+// Global variables to store the last source position - used by some tests.
+int last_source_line = -1;
+int last_source_column = -1;
+
 // Debug event handler which counts the break points which have been hit.
 int break_point_hit_count = 0;
 static void DebugEventBreakPointHitCount(v8::DebugEvent event,
@@ -544,6 +566,26 @@
         function_name->WriteAscii(last_function_hit);
       }
     }
+
+    if (!frame_source_line.IsEmpty()) {
+      // Get the source line.
+      const int argc = 1;
+      v8::Handle<v8::Value> argv[argc] = { exec_state };
+      v8::Handle<v8::Value> result = frame_source_line->Call(exec_state,
+                                                             argc, argv);
+      CHECK(result->IsNumber());
+      last_source_line = result->Int32Value();
+    }
+
+    if (!frame_source_column.IsEmpty()) {
+      // Get the source column.
+      const int argc = 1;
+      v8::Handle<v8::Value> argv[argc] = { exec_state };
+      v8::Handle<v8::Value> result = frame_source_column->Call(exec_state,
+                                                               argc, argv);
+      CHECK(result->IsNumber());
+      last_source_column = result->Int32Value();
+    }
   }
 }
 
@@ -994,6 +1036,17 @@
   break_point_hit_count = 0;
   v8::HandleScope scope;
   DebugLocalContext env;
+
+  // Create a functions for checking the source line and column when hitting
+  // a break point.
+  frame_source_line = CompileFunction(&env,
+                                      frame_source_line_source,
+                                      "frame_source_line");
+  frame_source_column = CompileFunction(&env,
+                                        frame_source_column_source,
+                                        "frame_source_column");
+
+
   v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
                                    v8::Undefined());
   v8::Script::Compile(v8::String::New("function foo(){}"))->Run();
@@ -1008,8 +1061,12 @@
   int bp = SetBreakPoint(foo, 0);
   foo->Call(env->Global(), 0, NULL);
   CHECK_EQ(1, break_point_hit_count);
+  CHECK_EQ(0, last_source_line);
+  CHECK_EQ(16, last_source_column);
   foo->Call(env->Global(), 0, NULL);
   CHECK_EQ(2, break_point_hit_count);
+  CHECK_EQ(0, last_source_line);
+  CHECK_EQ(16, last_source_column);
 
   // Run without breakpoints.
   ClearBreakPoint(bp);
@@ -3663,15 +3720,6 @@
 }
 
 
-// Source for a JavaScript function which returns the source line for the top
-// frame.
-static const char* frame_source_line_source =
-    "function frame_source_line(exec_state) {"
-    "  return exec_state.frame(0).sourceLine();"
-    "}";
-v8::Handle<v8::Function> frame_source_line;
-
-
 // Source for a JavaScript function which returns the data parameter of a
 // function called in the context of the debugger. If no data parameter is
 // passed it throws an exception.
diff --git a/test/mjsunit/const-declaration.js b/test/mjsunit/const-declaration.js
new file mode 100644
index 0000000..48c0cf2
--- /dev/null
+++ b/test/mjsunit/const-declaration.js
@@ -0,0 +1,172 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test handling of const variables in various settings.
+
+(function () {
+  function f() {
+    function g() {
+      x = 42;  //  should be ignored
+      return x;  // force x into context
+    }
+    x = 43;  // should be ignored
+    assertEquals(undefined, g());
+    x = 44;  // should be ignored
+    const x = 0;
+    x = 45;  // should be ignored
+    assertEquals(0, g());
+  }
+  f();
+})();
+
+
+(function () {
+  function f() {
+    function g() {
+      with ({foo: 0}) {
+        x = 42;  //  should be ignored
+        return x;  // force x into context
+      }
+    }
+    x = 43;  // should be ignored
+    assertEquals(undefined, g());
+    x = 44;  // should be ignored
+    const x = 0;
+    x = 45;  // should be ignored
+    assertEquals(0, g());
+  }
+  f();
+})();
+
+
+(function () {
+  function f() {
+    function g(s) {
+      eval(s);
+      return x;  // force x into context
+    }
+    x = 43;  // should be ignored
+    assertEquals(undefined, g("x = 42;"));
+    x = 44;  // should be ignored
+    const x = 0;
+    x = 45;  // should be ignored
+    assertEquals(0, g("x = 46;"));
+  }
+  f();
+})();
+
+
+(function () {
+  function f() {
+    function g(s) {
+      with ({foo: 0}) {
+        eval(s);
+        return x;  // force x into context
+      }
+    }
+    x = 43;  // should be ignored
+    assertEquals(undefined, g("x = 42;"));
+    x = 44;  // should be ignored
+    const x = 0;
+    x = 45;  // should be ignored
+    assertEquals(0, g("x = 46;"));
+  }
+  f();
+})();
+
+
+(function () {
+  function f(s) {
+    function g() {
+      x = 42;  // assign to global x, or to const x
+      return x;
+    }
+    x = 43;  // declare global x
+    assertEquals(42, g());
+    x = 44;  // assign to global x
+    eval(s);
+    x = 45;  // should be ignored (assign to const x)
+    assertEquals(0, g());
+  }
+  f("const x = 0;");
+})();
+
+
+(function () {
+  function f(s) {
+    function g() {
+      with ({foo: 0}) {
+        x = 42;  // assign to global x, or to const x
+        return x;
+      }
+    }
+    x = 43;  // declare global x
+    assertEquals(42, g());
+    x = 44;  // assign to global x
+    eval(s);
+    x = 45;  // should be ignored (assign to const x)
+    assertEquals(0, g());
+  }
+  f("const x = 0;");
+})();
+
+
+(function () {
+  function f(s) {
+    function g(s) {
+      eval(s);
+      return x;
+    }
+    x = 43;  // declare global x
+    assertEquals(42, g("x = 42;"));
+    x = 44;  // assign to global x
+    eval(s);
+    x = 45;  // should be ignored (assign to const x)
+    assertEquals(0, g("x = 46;"));
+  }
+  f("const x = 0;");
+})();
+
+
+(function () {
+  function f(s) {
+    function g(s) {
+      with ({foo: 0}) {
+        eval(s);
+        return x;
+      }
+    }
+    x = 43;  // declare global x
+    assertEquals(42, g("x = 42;"));
+    x = 44;  // assign to global x
+    eval(s);
+    x = 45;  // should be ignored (assign to const x)
+    assertEquals(0, g("x = 46;"));
+  }
+  f("const x = 0;");
+})();
+
diff --git a/test/mjsunit/override-read-only-property.js b/test/mjsunit/override-read-only-property.js
new file mode 100644
index 0000000..b8fa501
--- /dev/null
+++ b/test/mjsunit/override-read-only-property.js
@@ -0,0 +1,64 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// According to ECMA-262, sections 8.6.2.2 and 8.6.2.3 you're not
+// allowed to override read-only properties, not even if the read-only
+// property is in the prototype chain.
+//
+// However, for compatibility with WebKit/JSC, we allow the overriding
+// of read-only properties in prototype chains.
+
+function F() {};
+F.prototype = Number;
+
+var original_number_max = Number.MAX_VALUE;
+
+// Assignment to a property which does not exist on the object itself,
+// but is read-only in a prototype takes effect.
+var f = new F();
+assertEquals(original_number_max, f.MAX_VALUE);
+f.MAX_VALUE = 42;
+assertEquals(42, f.MAX_VALUE);
+
+// Assignment to a property which does not exist on the object itself,
+// but is read-only in a prototype takes effect.
+f = new F();
+with (f) {
+  MAX_VALUE = 42;
+}
+assertEquals(42, f.MAX_VALUE);
+
+// Assignment to read-only property on the object itself is ignored.
+Number.MAX_VALUE = 42;
+assertEquals(original_number_max, Number.MAX_VALUE);
+
+// G should be read-only on the global object and the assignment is
+// ignored.
+(function G() {
+  eval("G = 42;");
+  assertTrue(typeof G === 'function');
+})();
diff --git a/test/mjsunit/regress/regress-312.js b/test/mjsunit/regress/regress-312.js
new file mode 100644
index 0000000..0fb8c21
--- /dev/null
+++ b/test/mjsunit/regress/regress-312.js
@@ -0,0 +1,31 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Should not trigger debug ASSERT.
+// See http://code.google.com/p/v8/issues/detail?id=312
+
+var o = { f: "x" ? function () {} : function () {} };
diff --git a/test/mjsunit/testcfg.py b/test/mjsunit/testcfg.py
index f65365d..9c7e028 100644
--- a/test/mjsunit/testcfg.py
+++ b/test/mjsunit/testcfg.py
@@ -32,6 +32,7 @@
 
 
 FLAGS_PATTERN = re.compile(r"//\s+Flags:(.*)")
+FILES_PATTERN = re.compile(r"//\s+Files:(.*)")
 
 
 class MjsunitTestCase(test.TestCase):
@@ -54,6 +55,12 @@
     flags_match = FLAGS_PATTERN.search(source)
     if flags_match:
       result += flags_match.group(1).strip().split()
+    files_match = FILES_PATTERN.search(source);
+    additional_files = []
+    if files_match:
+      additional_files += files_match.group(1).strip().split()
+    for a_file in additional_files:
+      result.append(join(dirname(self.config.root), '..', a_file))
     framework = join(dirname(self.config.root), 'mjsunit', 'mjsunit.js')
     result += [framework, self.file]
     return result
@@ -76,7 +83,8 @@
     mjsunit = [current_path + [t] for t in self.Ls(self.root)]
     regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))]
     bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
-    all_tests = mjsunit + regress + bugs
+    tools = [current_path + ['tools', t] for t in self.Ls(join(self.root, 'tools'))]
+    all_tests = mjsunit + regress + bugs + tools
     result = []
     for test in all_tests:
       if self.Contains(path, test):
diff --git a/test/mjsunit/tools/splaytree.js b/test/mjsunit/tools/splaytree.js
new file mode 100644
index 0000000..3beba0b
--- /dev/null
+++ b/test/mjsunit/tools/splaytree.js
@@ -0,0 +1,166 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load the Splay tree implementation from <project root>/tools.
+// Files: tools/splaytree.js
+
+
+(function testIsEmpty() {
+  var tree = new goog.structs.SplayTree();
+  assertTrue(tree.isEmpty());
+  tree.insert(0, 'value');
+  assertFalse(tree.isEmpty());
+})();
+
+
+(function testExportValues() {
+  var tree = new goog.structs.SplayTree();
+  assertArrayEquals([], tree.exportValues());
+  tree.insert(0, 'value');
+  assertArrayEquals(['value'], tree.exportValues());
+  tree.insert(0, 'value');
+  assertArrayEquals(['value'], tree.exportValues());
+})();
+
+
+function createSampleTree() {
+  // Creates the following tree:
+  //           50
+  //          /  \
+  //         30  60
+  //        /  \   \
+  //       10  40  90
+  //         \    /  \
+  //         20  70 100
+  //        /      \
+  //       15      80
+  //
+  // We can't use the 'insert' method because it also uses 'splay_'.
+  return { key: 50, value: 50,
+      left: { key: 30, value: 30,
+              left: { key: 10, value: 10, left: null,
+                      right: { key: 20, value: 20,
+                               left: { key: 15, value: 15,
+                                       left: null, right: null },
+                               right: null } },
+              right: { key: 40, value: 40, left: null, right: null } },
+      right: { key: 60, value: 60, left: null,
+               right: { key: 90, value: 90,
+                        left: { key: 70, value: 70, left: null,
+                                right: { key: 80, value: 80,
+                                         left: null, right: null } },
+                        right: { key: 100, value: 100,
+                                 left: null, right: null } } } };
+};
+
+
+(function testSplay() {
+  var tree = new goog.structs.SplayTree();
+  tree.root_ = createSampleTree();
+  assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+                    tree.exportValues());
+  tree.splay_(50);
+  assertArrayEquals(['50', '30', '60', '10', '40', '90', '20', '70', '100', '15', '80'],
+                    tree.exportValues());
+  tree.splay_(80);
+  assertArrayEquals(['80', '60', '90', '50', '70', '100', '30', '10', '40', '20', '15'],
+                    tree.exportValues());
+})();
+
+
+(function testInsert() {
+  var tree = new goog.structs.SplayTree();
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  assertArrayEquals(['left', 'root'], tree.exportValues());
+  tree.insert(7, 'right');
+  assertArrayEquals(['right', 'root', 'left'], tree.exportValues());
+})();
+
+
+(function testFind() {
+  var tree = new goog.structs.SplayTree();
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('root', tree.find(5).value);
+  assertEquals('left', tree.find(3).value);
+  assertEquals('right', tree.find(7).value);
+  assertEquals(null, tree.find(0));
+  assertEquals(null, tree.find(100));
+  assertEquals(null, tree.find(-100));
+})();
+
+
+(function testFindMin() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findMin());
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('left', tree.findMin().value);
+})();
+
+
+(function testFindMax() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findMax());
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('right', tree.findMax().value);
+})();
+
+
+(function testFindGreatestLessThan() {
+  var tree = new goog.structs.SplayTree();
+  assertEquals(null, tree.findGreatestLessThan(10));
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertEquals('right', tree.findGreatestLessThan(10).value);
+  assertEquals('right', tree.findGreatestLessThan(7).value);
+  assertEquals('root', tree.findGreatestLessThan(6).value);
+  assertEquals('left', tree.findGreatestLessThan(4).value);
+  assertEquals(null, tree.findGreatestLessThan(2));
+})();
+
+
+(function testRemove() {
+  var tree = new goog.structs.SplayTree();
+  assertThrows('tree.remove(5)');
+  tree.insert(5, 'root');
+  tree.insert(3, 'left');
+  tree.insert(7, 'right');
+  assertThrows('tree.remove(1)');
+  assertThrows('tree.remove(6)');
+  assertThrows('tree.remove(10)');
+  assertEquals('root', tree.remove(5).value);
+  assertEquals('left', tree.remove(3).value);
+  assertEquals('right', tree.remove(7).value);
+  assertArrayEquals([], tree.exportValues());
+})();
diff --git a/tools/splaytree.js b/tools/splaytree.js
new file mode 100644
index 0000000..3045456
--- /dev/null
+++ b/tools/splaytree.js
@@ -0,0 +1,322 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// A namespace stub. It will become more clear how to declare it properly
+// during integration of this script into Dev Tools.
+goog = { structs: {} };
+
+
+/**
+ * Constructs a Splay tree.  A splay tree is a self-balancing binary
+ * search tree with the additional property that recently accessed
+ * elements are quick to access again. It performs basic operations
+ * such as insertion, look-up and removal in O(log(n)) amortized time.
+ *
+ * @constructor
+ */
+goog.structs.SplayTree = function() {
+};
+
+
+/**
+ * Pointer to the root node of the tree.
+ *
+ * @type {goog.structs.SplayTree.Node}
+ * @private
+ */
+goog.structs.SplayTree.prototype.root_ = null;
+
+
+/**
+ * @return {boolean} Whether the tree is empty.
+ */
+goog.structs.SplayTree.prototype.isEmpty = function() {
+  return !this.root_;
+};
+
+
+
+/**
+ * Inserts a node into the tree with the specified key and value if
+ * the tree does not already contain a node with the specified key. If
+ * the value is inserted, it becomes the root of the tree.
+ *
+ * @param {number} key Key to insert into the tree.
+ * @param {*} value Value to insert into the tree.
+ */
+goog.structs.SplayTree.prototype.insert = function(key, value) {
+  if (this.isEmpty()) {
+    this.root_ = new goog.structs.SplayTree.Node(key, value);
+    return;
+  }
+  // Splay on the key to move the last node on the search path for
+  // the key to the root of the tree.
+  this.splay_(key);
+  if (this.root_.key == key) {
+    return;
+  }
+  var node = new goog.structs.SplayTree.Node(key, value);
+  if (key > this.root_.key) {
+    node.left = this.root_;
+    node.right = this.root_.right;
+    this.root_.right = null;
+  } else {
+    node.right = this.root_;
+    node.left = this.root_.left;
+    this.root_.left = null;
+  }
+  this.root_ = node;
+};
+
+
+
+/**
+ * Removes a node with the specified key from the tree if the tree
+ * contains a node with this key. The removed node is returned. If the
+ * key is not found, an exception is thrown.
+ *
+ * @param {number} key Key to find and remove from the tree.
+ * @return {goog.structs.SplayTree.Node} The removed node.
+ */
+goog.structs.SplayTree.prototype.remove = function(key) {
+  if (this.isEmpty()) {
+    throw Error('Key not found: ' + key);
+  }
+  this.splay_(key);
+  if (this.root_.key != key) {
+    throw Error('Key not found: ' + key);
+  }
+  var removed = this.root_;
+  if (!this.root_.left) {
+    this.root_ = this.root_.right;
+  } else {
+    var right = this.root_.right;
+    this.root_ = this.root_.left;
+    // Splay to make sure that the new root has an empty right child.
+    this.splay_(key);
+    // Insert the original right child as the right child of the new
+    // root.
+    this.root_.right = right;
+  }
+  return removed;
+};
+
+
+/**
+ * Returns the node having the specified key or null if the tree doesn't contain
+ * a node with the specified key.
+ *
+ * @param {number} key Key to find in the tree.
+ * @return {goog.structs.SplayTree.Node} Node having the specified key.
+ */
+goog.structs.SplayTree.prototype.find = function(key) {
+  if (this.isEmpty()) {
+    return null;
+  }
+  this.splay_(key);
+  return this.root_.key == key ? this.root_ : null;
+};
+
+
+/**
+ * @return {goog.structs.SplayTree.Node} Node having the minimum key value.
+ */
+goog.structs.SplayTree.prototype.findMin = function() {
+  if (this.isEmpty()) {
+    return null;
+  }
+  var current = this.root_;
+  while (current.left) {
+    current = current.left;
+  }
+  return current;
+};
+
+
+/**
+ * @return {goog.structs.SplayTree.Node} Node having the maximum key value.
+ */
+goog.structs.SplayTree.prototype.findMax = function(opt_startNode) {
+  if (this.isEmpty()) {
+    return null;
+  }
+  var current = opt_startNode || this.root_;
+  while (current.right) {
+    current = current.right;
+  }
+  return current;
+};
+
+
+/**
+ * @return {goog.structs.SplayTree.Node} Node having the maximum key value that
+ *     is less or equal to the specified key value.
+ */
+goog.structs.SplayTree.prototype.findGreatestLessThan = function(key) {
+  if (this.isEmpty()) {
+    return null;
+  }
+  // Splay on the key to move the node with the given key or the last
+  // node on the search path to the top of the tree.
+  this.splay_(key);
+  // Now the result is either the root node or the greatest node in
+  // the left subtree.
+  if (this.root_.key <= key) {
+    return this.root_;
+  } else if (this.root_.left) {
+    return this.findMax(this.root_.left);
+  } else {
+    return null;
+  }
+};
+
+
+/**
+ * @return {Array<*>} An array containing all the values of tree's nodes.
+ */
+goog.structs.SplayTree.prototype.exportValues = function() {
+  var result = [];
+  this.traverse_(function(node) { result.push(node.value); });
+  return result;
+};
+
+
+/**
+ * Perform the splay operation for the given key. Moves the node with
+ * the given key to the top of the tree.  If no node has the given
+ * key, the last node on the search path is moved to the top of the
+ * tree. This is the simplified top-down splaying algorithm from:
+ * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
+ *
+ * @param {number} key Key to splay the tree on.
+ * @private
+ */
+goog.structs.SplayTree.prototype.splay_ = function(key) {
+  if (this.isEmpty()) {
+    return;
+  }
+  // Create a dummy node.  The use of the dummy node is a bit
+  // counter-intuitive: The right child of the dummy node will hold
+  // the L tree of the algorithm.  The left child of the dummy node
+  // will hold the R tree of the algorithm.  Using a dummy node, left
+  // and right will always be nodes and we avoid special cases.
+  var dummy, left, right;
+  dummy = left = right = new goog.structs.SplayTree.Node(null, null);
+  var current = this.root_;
+  while (true) {
+    if (key < current.key) {
+      if (!current.left) {
+        break;
+      }
+      if (key < current.left.key) {
+        // Rotate right.
+        var tmp = current.left;
+        current.left = tmp.right;
+        tmp.right = current;
+        current = tmp;
+        if (!current.left) {
+          break;
+        }
+      }
+      // Link right.
+      right.left = current;
+      right = current;
+      current = current.left;
+    } else if (key > current.key) {
+      if (!current.right) {
+        break;
+      }
+      if (key > current.right.key) {
+        // Rotate left.
+        var tmp = current.right;
+        current.right = tmp.left;
+        tmp.left = current;
+        current = tmp;
+        if (!current.right) {
+          break;
+        }
+      }
+      // Link left.
+      left.right = current;
+      left = current;
+      current = current.right;
+    } else {
+      break;
+    }
+  }
+  // Assemble.
+  left.right = current.left;
+  right.left = current.right;
+  current.left = dummy.right;
+  current.right = dummy.left;
+  this.root_ = current;
+};
+
+
+/**
+ * Performs a preorder traversal of the tree.
+ *
+ * @param {function(goog.structs.SplayTree.Node)} f Visitor function.
+ * @private
+ */
+goog.structs.SplayTree.prototype.traverse_ = function(f) {
+  var nodesToVisit = [this.root_];
+  while (nodesToVisit.length > 0) {
+    var node = nodesToVisit.shift();
+    if (node == null) {
+      continue;
+    }
+    f(node);
+    nodesToVisit.push(node.left);
+    nodesToVisit.push(node.right);
+  }
+};
+
+
+/**
+ * Constructs a Splay tree node.
+ *
+ * @param {number} key Key.
+ * @param {*} value Value.
+ */
+goog.structs.SplayTree.Node = function(key, value) {
+  this.key = key;
+  this.value = value;
+};
+
+
+/**
+ * @type {goog.structs.SplayTree.Node}
+ */
+goog.structs.SplayTree.Node.prototype.left = null;
+
+
+/**
+ * @type {goog.structs.SplayTree.Node}
+ */
+goog.structs.SplayTree.Node.prototype.right = null;