}
+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();
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
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()); }
// 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:
virtual ObjectLiteral* AsObjectLiteral() { return this; }
virtual void Accept(AstVisitor* v);
+ virtual bool IsValidJSON();
Handle<FixedArray> constant_properties() const {
return constant_properties_;
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_; }
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);
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);
}
}
+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,
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.
// Compile the function and add it to the cache.
result = MakeFunction(true,
+ false,
false,
script,
Handle<Context>::null(),
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);
// 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);
}
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
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);
}
-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;
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.
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
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 - 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) {
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);
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);
dictionary->SetNextEnumerationIndex(index);
// Allocate new map.
- obj = map()->Copy();
+ obj = map()->CopyDropDescriptors();
if (obj->IsFailure()) return obj;
Map* new_map = Map::cast(obj);
}
-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());
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;
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.
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.
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);
// 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);
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);
// 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,
F(NumberIsFinite, 1) \
\
/* Globals */ \
- F(CompileString, 2) \
+ F(CompileString, 3) \
F(GlobalPrint, 1) \
\
/* Eval */ \
'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);
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;
// 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);
}
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');