- When cloning maps to set the 'lazy loading' bit remember to clone
authorchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 24 Apr 2009 12:45:29 +0000 (12:45 +0000)
committerchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 24 Apr 2009 12:45:29 +0000 (12:45 +0000)
  the properties as well.  This fixes some failing tests.
- Moved json parsing into native code.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1789 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

16 files changed:
src/ast.cc
src/ast.h
src/bootstrapper.cc
src/compiler.cc
src/compiler.h
src/debug-delay.js
src/factory.cc
src/factory.h
src/handles.cc
src/json-delay.js
src/objects.cc
src/objects.h
src/runtime.cc
src/runtime.h
src/v8natives.js
test/mjsunit/json.js

index 1a6010a00903930ec6dc392741a3bac53b882f5d..d19e3b3e0410601bcca8d0a0bdef59bc068729f4 100644 (file)
@@ -152,6 +152,27 @@ ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) {
 }
 
 
+bool ObjectLiteral::IsValidJSON() {
+  int length = properties()->length();
+  for (int i = 0; i < length; i++) {
+    Property* prop = properties()->at(i);
+    if (!prop->value()->IsValidJSON())
+      return false;
+  }
+  return true;
+}
+
+
+bool ArrayLiteral::IsValidJSON() {
+  int length = values()->length();
+  for (int i = 0; i < length; i++) {
+    if (!values()->at(i)->IsValidJSON())
+      return false;
+  }
+  return true;
+}
+
+
 void TargetCollector::AddTarget(BreakTarget* target) {
   // Add the label to the collector, but discard duplicates.
   int length = targets_->length();
index b496816f7e42b7bb9dfb0bc4d47442bf24be8713..6a2f671052b0ed0f26397e272b06861b2d467a50 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -155,6 +155,7 @@ class Expression: public Node {
  public:
   virtual Expression* AsExpression()  { return this; }
 
+  virtual bool IsValidJSON() { return false; }
   virtual bool IsValidLeftHandSide() { return false; }
 
   // Mark the expression as being compiled as an expression
@@ -625,6 +626,8 @@ class Literal: public Expression {
     return handle_.is_identical_to(other->handle_);
   }
 
+  virtual bool IsValidJSON() { return true; }
+
   // Identity testers.
   bool IsNull() const { return handle_.is_identical_to(Factory::null_value()); }
   bool IsTrue() const { return handle_.is_identical_to(Factory::true_value()); }
@@ -653,6 +656,8 @@ class MaterializedLiteral: public Expression {
   // constants and simple object and array literals.
   bool is_simple() const { return is_simple_; }
 
+  virtual bool IsValidJSON() { return true; }
+
   int depth() const { return depth_; }
 
  private:
@@ -704,6 +709,7 @@ class ObjectLiteral: public MaterializedLiteral {
 
   virtual ObjectLiteral* AsObjectLiteral() { return this; }
   virtual void Accept(AstVisitor* v);
+  virtual bool IsValidJSON();
 
   Handle<FixedArray> constant_properties() const {
     return constant_properties_;
@@ -751,6 +757,7 @@ class ArrayLiteral: public MaterializedLiteral {
 
   virtual void Accept(AstVisitor* v);
   virtual ArrayLiteral* AsArrayLiteral() { return this; }
+  virtual bool IsValidJSON();
 
   Handle<FixedArray> literals() const { return literals_; }
   ZoneList<Expression*>* values() const { return values_; }
index 31d8c63ea82f2dceafb284e5726a0079788fe604..c434207618f2c4abb967081ceddac6dfbe5d0a58 100644 (file)
@@ -530,7 +530,7 @@ void Genesis::CreateRoots(v8::Handle<v8::ObjectTemplate> global_template,
     global_context()->function_instance_map()->set_prototype(*empty_function);
 
     // Allocate the function map first and then patch the prototype later
-    Handle<Map> empty_fm = Factory::CopyMap(fm);
+    Handle<Map> empty_fm = Factory::CopyMapDropDescriptors(fm);
     empty_fm->set_instance_descriptors(*function_map_descriptors);
     empty_fm->set_prototype(global_context()->object_function()->prototype());
     empty_function->set_map(*empty_fm);
@@ -1433,7 +1433,7 @@ void Genesis::MakeFunctionInstancePrototypeWritable() {
 
   Handle<DescriptorArray> function_map_descriptors =
       ComputeFunctionInstanceDescriptor(false, true);
-  Handle<Map> fm = Factory::CopyMap(Top::function_map());
+  Handle<Map> fm = Factory::CopyMapDropDescriptors(Top::function_map());
   fm->set_instance_descriptors(*function_map_descriptors);
   Top::context()->global_context()->set_function_map(*fm);
 }
index 62e838e6c9d60b3b976698a7afa137c2d8626e74..c16b938886ee7238860649c45e568de4d376ada9 100644 (file)
@@ -80,8 +80,20 @@ static Handle<Code> MakeCode(FunctionLiteral* literal,
 }
 
 
+static bool IsValidJSON(FunctionLiteral* lit) {
+  if (!lit->body()->length() == 1)
+    return false;
+  Statement* stmt = lit->body()->at(0);
+  if (stmt->AsExpressionStatement() == NULL)
+    return false;
+  Expression *expr = stmt->AsExpressionStatement()->expression();
+  return expr->IsValidJSON();
+}
+
+
 static Handle<JSFunction> MakeFunction(bool is_global,
                                        bool is_eval,
+                                       bool is_json,
                                        Handle<Script> script,
                                        Handle<Context> context,
                                        v8::Extension* extension,
@@ -109,6 +121,19 @@ static Handle<JSFunction> MakeFunction(bool is_global,
     return Handle<JSFunction>::null();
   }
 
+  // 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)) {
+    HandleScope scope;
+    Handle<JSArray> args = Factory::NewJSArray(1);
+    Handle<Object> source(script->source());
+    SetElement(args, 0, source);
+    Handle<Object> result = Factory::NewSyntaxError("invalid_json", args);
+    Top::Throw(*result, NULL);
+    return Handle<JSFunction>::null();
+  }
+
   // Measure how long it takes to do the compilation; only take the
   // rest of the function into account to avoid overlap with the
   // parsing statistics.
@@ -214,6 +239,7 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
 
     // Compile the function and add it to the cache.
     result = MakeFunction(true,
+                          false,
                           false,
                           script,
                           Handle<Context>::null(),
@@ -237,7 +263,8 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
 Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
                                          Handle<Context> context,
                                          int line_offset,
-                                         bool is_global) {
+                                         bool is_global,
+                                         bool is_json) {
   int source_length = source->length();
   Counters::total_eval_size.Increment(source_length);
   Counters::total_compile_size.Increment(source_length);
@@ -256,7 +283,13 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
     // Create a script object describing the script to be compiled.
     Handle<Script> script = Factory::NewScript(source);
     script->set_line_offset(Smi::FromInt(line_offset));
-    result = MakeFunction(is_global, true, script, context, NULL, NULL);
+    result = MakeFunction(is_global,
+                          true,
+                          is_json,
+                          script,
+                          context,
+                          NULL,
+                          NULL);
     if (!result.is_null()) {
       CompilationCache::PutEval(source, context, entry, result);
     }
index 4f589b7eeac1f504c045bca6056bfd844850bafc..8abe130d839eeb889053d58b944cb1bda646b55d 100644 (file)
@@ -60,7 +60,8 @@ class Compiler : public AllStatic {
   static Handle<JSFunction> CompileEval(Handle<String> source,
                                         Handle<Context> context,
                                         int line_offset,
-                                        bool is_global);
+                                        bool is_global,
+                                        bool is_json);
 
   // Compile from function info (used for lazy compilation). Returns
   // true on success and false if the compilation resulted in a stack
index 6cb5e7f579eaea54747ade47a5a321a5e3819c8f..961f304edbcf837021fa6fd3a34ceb4b369eaa12 100644 (file)
@@ -1133,7 +1133,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
   try {
     try {
       // Convert the JSON string to an object.
-      request = %CompileString('(' + json_request + ')', 0)();
+      request = %CompileString('(' + json_request + ')', 0, false)();
 
       // Create an initial response.
       response = this.createResponse(request);
index c849ab71e94bc1c6e36a1c4b82f739e91a7bca55..c6b1d1715249007c59e41414ad7d3bebf2d12db3 100644 (file)
@@ -208,14 +208,14 @@ Handle<JSObject> Factory::NewFunctionPrototype(Handle<JSFunction> function) {
 }
 
 
-Handle<Map> Factory::CopyMap(Handle<Map> src) {
-  CALL_HEAP_FUNCTION(src->Copy(), Map);
+Handle<Map> Factory::CopyMapDropDescriptors(Handle<Map> src) {
+  CALL_HEAP_FUNCTION(src->CopyDropDescriptors(), Map);
 }
 
 
 Handle<Map> Factory::CopyMap(Handle<Map> src,
                              int extra_inobject_properties) {
-  Handle<Map> copy = CopyMap(src);
+  Handle<Map> copy = CopyMapDropDescriptors(src);
   // Check that we do not overflow the instance size when adding the
   // extra inobject properties.
   int instance_size_delta = extra_inobject_properties * kPointerSize;
index 54f2089f7466a35e366597c64d23a2e1fe683062..6ac2706ec18a2d3f9a886bd2a2e41ca0f60f89ab 100644 (file)
@@ -153,7 +153,7 @@ class Factory : public AllStatic {
 
   static Handle<JSObject> NewFunctionPrototype(Handle<JSFunction> function);
 
-  static Handle<Map> CopyMap(Handle<Map> map);
+  static Handle<Map> CopyMapDropDescriptors(Handle<Map> map);
 
   // Copy the map adding more inobject properties if possible without
   // overflowing the instance size.
index 6cd5006135ba70778f1b8a26026c588cc47aea22..773483d609f37a48a91cce0d5bb1bab4c5196ef1 100644 (file)
@@ -700,7 +700,7 @@ void SetupLazy(Handle<JSObject> obj,
   arr->set(2, *function_context);  // Set function context to this
   arr->set(3, obj->map()->constructor());  // Remember the constructor
   Handle<Map> old_map(obj->map());
-  Handle<Map> new_map = Factory::CopyMap(old_map);
+  Handle<Map> new_map = Factory::CopyMapDropTransitions(old_map);
   obj->set_map(*new_map);
   new_map->set_needs_loading(true);
   // Store the lazy loading info in the constructor field.  We'll
index 3640d7cd87dd8c065c7705cfb8fc522eba784b52..90150c6f17e8d3b3217164e9a009189a0ca2de56 100644 (file)
 
 var $JSON = global.JSON;
 
-function IsValidJSON(s) {
-  // All empty whitespace is not valid.
-  if (/^\s*$/.test(s))
-    return false;
-  // This is taken from http://www.json.org/json2.js which is released to the
-  // public domain.
-
-  var backslashesRe = /\\["\\\/bfnrtu]/g;
-  var simpleValuesRe =
-      /"[^"\\\n\r\x00-\x1f\x7f-\x9f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
-  var openBracketsRe = /(?:^|:|,)(?:[\s]*\[)+/g;
-  var remainderRe = /^[\],:{}\s]*$/;
-
-  return remainderRe.test(s.replace(backslashesRe, '@').
-      replace(simpleValuesRe, ']').
-      replace(openBracketsRe, ''));  
-}
-
 function ParseJSONUnfiltered(text) {
   var s = $String(text);
-  if (IsValidJSON(s)) {
-    try {
-      return global.eval('(' + s + ')');
-    } catch (e) {
-      // ignore exceptions
-    }
-  }
-  throw MakeSyntaxError('invalid_json', [s]);
+  var f = %CompileString("(" + text + ")", -1, true);
+  return f();
 }
 
 function Revive(holder, name, reviver) {
index 9471ecdafd95397d47fd6c3b499299744bf3fd33..55fc971ed5ea390fe3e859e8b583d0ccea4aa495 100644 (file)
@@ -1158,7 +1158,7 @@ Object* JSObject::AddFastProperty(String* name,
          (index - map()->inobject_properties()) < properties()->length() ||
          map()->unused_property_fields() == 0);
   // Allocate a new map for the object.
-  Object* r = map()->Copy();
+  Object* r = map()->CopyDropDescriptors();
   if (r->IsFailure()) return r;
   Map* new_map = Map::cast(r);
   if (allow_map_transition) {
@@ -1203,7 +1203,7 @@ Object* JSObject::AddConstantFunctionProperty(String* name,
   if (new_descriptors->IsFailure()) return new_descriptors;
 
   // Allocate a new map for the object.
-  Object* new_map = map()->Copy();
+  Object* new_map = map()->CopyDropDescriptors();
   if (new_map->IsFailure()) return new_map;
 
   DescriptorArray* descriptors = DescriptorArray::cast(new_descriptors);
@@ -1361,7 +1361,7 @@ Object* JSObject::ConvertDescriptorToField(String* name,
       DescriptorArray::cast(descriptors_unchecked);
 
   // Make a new map for the object.
-  Object* new_map_unchecked = map()->Copy();
+  Object* new_map_unchecked = map()->CopyDropDescriptors();
   if (new_map_unchecked->IsFailure()) return new_map_unchecked;
   Map* new_map = Map::cast(new_map_unchecked);
   new_map->set_instance_descriptors(new_descriptors);
@@ -2032,7 +2032,7 @@ Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode) {
   dictionary->SetNextEnumerationIndex(index);
 
   // Allocate new map.
-  obj = map()->Copy();
+  obj = map()->CopyDropDescriptors();
   if (obj->IsFailure()) return obj;
   Map* new_map = Map::cast(obj);
 
@@ -2704,12 +2704,16 @@ Object* JSObject::SlowReverseLookup(Object* value) {
 }
 
 
-Object* Map::Copy() {
+Object* Map::CopyDropDescriptors() {
   Object* result = Heap::AllocateMap(instance_type(), instance_size());
   if (result->IsFailure()) return result;
   Map::cast(result)->set_prototype(prototype());
   Map::cast(result)->set_constructor(constructor());
   // Don't copy descriptors, so map transitions always remain a forest.
+  // If we retained the same descriptors we would have two maps
+  // pointing to the same transition which is bad because the garbage
+  // collector relies on being able to reverse pointers from transitions
+  // to maps.  If properties need to be retained use CopyDropTransitions.
   Map::cast(result)->set_instance_descriptors(Heap::empty_descriptor_array());
   // Please note instance_type and instance_size are set when allocated.
   Map::cast(result)->set_inobject_properties(inobject_properties());
@@ -2722,7 +2726,7 @@ Object* Map::Copy() {
 
 
 Object* Map::CopyDropTransitions() {
-  Object* new_map = Copy();
+  Object* new_map = CopyDropDescriptors();
   if (new_map->IsFailure()) return new_map;
   Object* descriptors = instance_descriptors()->RemoveTransitions();
   if (descriptors->IsFailure()) return descriptors;
@@ -7154,7 +7158,7 @@ Object* Dictionary::TransformPropertiesToFastFor(JSObject* obj,
 
   descriptors->Sort();
   // Allocate new map.
-  Object* new_map = obj->map()->Copy();
+  Object* new_map = obj->map()->CopyDropDescriptors();
   if (new_map->IsFailure()) return new_map;
 
   // Transform the object.
index d17aeeae67e8a479ac3999f089f7f7d7e3cfae47..8e765917aea9bf1db8227aeda494dc04c06b8e82 100644 (file)
@@ -2495,7 +2495,7 @@ class Map: public HeapObject {
   DECL_ACCESSORS(code_cache, FixedArray)
 
   // Returns a copy of the map.
-  Object* Copy();
+  Object* CopyDropDescriptors();
 
   // Returns a copy of the map, with all transitions dropped from the
   // instance descriptors.
index 2b350a5748fcefe0d6a44a0db6fe55eed33f2e9a..a1e23b4aabae1c4549aa70dbc92b1eb2aa28cfc0 100644 (file)
@@ -4824,14 +4824,18 @@ static Object* Runtime_GlobalReceiver(Arguments args) {
 
 static Object* Runtime_CompileString(Arguments args) {
   HandleScope scope;
-  ASSERT(args.length() == 2);
+  ASSERT_EQ(3, args.length());
   CONVERT_ARG_CHECKED(String, source, 0);
   CONVERT_ARG_CHECKED(Smi, line_offset, 1);
+  CONVERT_ARG_CHECKED(Oddball, is_json, 2)
 
   // Compile source string in the global context.
   Handle<Context> context(Top::context()->global_context());
-  Handle<JSFunction> boilerplate =
-      Compiler::CompileEval(source, context, line_offset->value(), true);
+  Handle<JSFunction> boilerplate = Compiler::CompileEval(source,
+                                                         context,
+                                                         line_offset->value(),
+                                                         true,
+                                                         is_json->IsTrue());
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> fun =
       Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -4856,7 +4860,7 @@ static Object* CompileDirectEval(Handle<String> source) {
 
   // Compile source string in the current context.
   Handle<JSFunction> boilerplate =
-      Compiler::CompileEval(source, context, 0, is_global);
+      Compiler::CompileEval(source, context, 0, is_global, false);
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> fun =
     Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -6479,7 +6483,8 @@ static Object* Runtime_DebugEvaluate(Arguments args) {
       Compiler::CompileEval(function_source,
                             context,
                             0,
-                            context->IsGlobalContext());
+                            context->IsGlobalContext(),
+                            false);
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> compiled_function =
       Factory::NewFunctionFromBoilerplate(boilerplate, context);
@@ -6537,7 +6542,11 @@ static Object* Runtime_DebugEvaluateGlobal(Arguments args) {
 
   // Compile the source to be evaluated.
   Handle<JSFunction> boilerplate =
-      Handle<JSFunction>(Compiler::CompileEval(source, context, 0, true));
+      Handle<JSFunction>(Compiler::CompileEval(source,
+                                               context,
+                                               0,
+                                               true,
+                                               false));
   if (boilerplate.is_null()) return Failure::Exception();
   Handle<JSFunction> compiled_function =
       Handle<JSFunction>(Factory::NewFunctionFromBoilerplate(boilerplate,
index 6bd19f65c75a71048eec90224c1aab059d5d40e2..b29ce70c61b078d076e34223357cbc8735d3b1f2 100644 (file)
@@ -194,7 +194,7 @@ namespace v8 { namespace internal {
   F(NumberIsFinite, 1) \
   \
   /* Globals */ \
-  F(CompileString, 2) \
+  F(CompileString, 3) \
   F(GlobalPrint, 1) \
   \
   /* Eval */ \
index 9ed6558473e92e206ebc3d41415b3446c444e671..29a24b476700d4c4bdc4594547bd1e8c254ed584 100644 (file)
@@ -110,7 +110,7 @@ function GlobalEval(x) {
                          'be the global object from which eval originated');
   }
   
-  var f = %CompileString(x, 0);
+  var f = %CompileString(x, 0, false);
   if (!IS_FUNCTION(f)) return f;
 
   return f.call(this);
@@ -121,7 +121,7 @@ function GlobalEval(x) {
 function GlobalExecScript(expr, lang) {
   // NOTE: We don't care about the character casing.
   if (!lang || /javascript/i.test(lang)) {
-    var f = %CompileString(ToString(expr), 0);
+    var f = %CompileString(ToString(expr), 0, false);
     f.call(%GlobalReceiver(global));
   }
   return null;
@@ -540,7 +540,7 @@ function NewFunction(arg1) {  // length == 1
 
   // The call to SetNewFunctionAttributes will ensure the prototype
   // property of the resulting function is enumerable (ECMA262, 15.3.5.2).
-  var f = %CompileString(source, -1)();
+  var f = %CompileString(source, -1, false)();
   %FunctionSetName(f, "anonymous");
   return %SetNewFunctionAttributes(f);
 }
index ce7d0781f2816cd6d90bfd1f976ef1ea56c234c3..47582643c6ab3a93fb323bea927637851f31ba61 100644 (file)
@@ -136,9 +136,11 @@ function TestInvalid(str) {
   assertThrows(function () { JSON.parse(str); }, SyntaxError);
 }
 
-TestInvalid('"abc\x00def"');
-TestInvalid('"abc\x10def"');
-TestInvalid('"abc\x1fdef"');
+TestInvalid('abcdef');
+TestInvalid('isNaN()');
+TestInvalid('{"x": [1, 2, deepObject]}');
+TestInvalid('[1, [2, [deepObject], 3], 4]');
+TestInvalid('function () { return 0; }');
 
 TestInvalid("[1, 2");
 TestInvalid('{"x": 3');