Commit 938d88e1 authored by lrn@chromium.org's avatar lrn@chromium.org

Separate JSON parsing from the JavaScript parser.

Switch JSON parsing to creating the value directly instead of createing
code to create the value.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5715 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent a8f27144
......@@ -152,10 +152,8 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
script->set_context_data((*i::Top::global_context())->data());
#ifdef ENABLE_DEBUGGER_SUPPORT
if (info->is_eval() || info->is_json()) {
Script::CompilationType compilation_type = info->is_json()
? Script::COMPILATION_TYPE_JSON
: Script::COMPILATION_TYPE_EVAL;
if (info->is_eval()) {
Script::CompilationType compilation_type = Script::COMPILATION_TYPE_EVAL;
script->set_compilation_type(Smi::FromInt(compilation_type));
// For eval scripts add information on the function from which eval was
// called.
......@@ -323,13 +321,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
Handle<Context> context,
bool is_global,
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.
bool is_json = (validate == VALIDATE_JSON);
bool is_global) {
int source_length = source->length();
Counters::total_eval_size.Increment(source_length);
Counters::total_compile_size.Increment(source_length);
......@@ -338,13 +330,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
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. If we're evaluating json
// we bypass the cache since we can't be sure a potential value in the
// cache has been validated.
// the compiler and add the result to the cache.
Handle<SharedFunctionInfo> result;
if (!is_json) {
result = CompilationCache::LookupEval(source, context, is_global);
}
result = CompilationCache::LookupEval(source, context, is_global);
if (result.is_null()) {
// Create a script object describing the script to be compiled.
......@@ -352,12 +340,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
CompilationInfo info(script);
info.MarkAsEval();
if (is_global) info.MarkAsGlobal();
if (is_json) info.MarkAsJson();
info.SetCallingContext(context);
result = MakeFunctionInfo(&info);
if (!result.is_null() && !is_json) {
// For json it's unlikely that we'll ever see exactly the same string
// again so we don't use the compilation cache.
if (!result.is_null()) {
CompilationCache::PutEval(source, context, is_global, result);
}
}
......
......@@ -49,7 +49,6 @@ class CompilationInfo BASE_EMBEDDED {
bool is_lazy() const { return (flags_ & IsLazy::mask()) != 0; }
bool is_eval() const { return (flags_ & IsEval::mask()) != 0; }
bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; }
bool is_json() const { return (flags_ & IsJson::mask()) != 0; }
bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; }
FunctionLiteral* function() const { return function_; }
Scope* scope() const { return scope_; }
......@@ -69,10 +68,6 @@ class CompilationInfo BASE_EMBEDDED {
ASSERT(!is_lazy());
flags_ |= IsGlobal::encode(true);
}
void MarkAsJson() {
ASSERT(!is_lazy());
flags_ |= IsJson::encode(true);
}
void MarkAsInLoop() {
ASSERT(is_lazy());
flags_ |= IsInLoop::encode(true);
......@@ -108,16 +103,15 @@ class CompilationInfo BASE_EMBEDDED {
// Flags that can be set for eager compilation.
class IsEval: public BitField<bool, 1, 1> {};
class IsGlobal: public BitField<bool, 2, 1> {};
class IsJson: public BitField<bool, 3, 1> {};
// Flags that can be set for lazy compilation.
class IsInLoop: public BitField<bool, 4, 1> {};
class IsInLoop: public BitField<bool, 3, 1> {};
unsigned flags_;
// Fields filled in by the compilation pipeline.
// AST filled in by the parser.
FunctionLiteral* function_;
// The scope of the function literal as a convenience. Set to indidicate
// The scope of the function literal as a convenience. Set to indicate
// that scopes have been analyzed.
Scope* scope_;
// The compiled code.
......@@ -153,8 +147,6 @@ class CompilationInfo BASE_EMBEDDED {
class Compiler : public AllStatic {
public:
enum ValidationState { DONT_VALIDATE_JSON, VALIDATE_JSON };
// All routines return a JSFunction.
// If an error occurs an exception is raised and
// the return handle contains NULL.
......@@ -172,8 +164,7 @@ class Compiler : public AllStatic {
// Compile a String source within a context for Eval.
static Handle<SharedFunctionInfo> CompileEval(Handle<String> source,
Handle<Context> context,
bool is_global,
ValidationState validation);
bool is_global);
// Compile from function info (used for lazy compilation). Returns true on
// success and false if the compilation resulted in a stack overflow.
......
......@@ -1301,7 +1301,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
try {
try {
// Convert the JSON string to an object.
request = %CompileString('(' + json_request + ')', false)();
request = %CompileString('(' + json_request + ')')();
// Create an initial response.
response = this.createResponse(request);
......
......@@ -29,8 +29,7 @@ var $JSON = global.JSON;
function ParseJSONUnfiltered(text) {
var s = $String(text);
var f = %CompileString(s, true);
return f();
return %ParseJson(s);
}
function Revive(holder, name, reviver) {
......
......@@ -3409,8 +3409,7 @@ class Script: public Struct {
// Script compilation types.
enum CompilationType {
COMPILATION_TYPE_HOST = 0,
COMPILATION_TYPE_EVAL = 1,
COMPILATION_TYPE_JSON = 2
COMPILATION_TYPE_EVAL = 1
};
// [source]: the script source.
......
This diff is collapsed.
......@@ -218,7 +218,6 @@ class Parser {
FunctionLiteral* ParseProgram(Handle<String> source,
bool in_global_context);
FunctionLiteral* ParseLazy(Handle<SharedFunctionInfo> info);
FunctionLiteral* ParseJson(Handle<String> source);
// The minimum number of contiguous assignment that will
// be treated as an initialization block. Benchmarks show that
......@@ -411,29 +410,6 @@ class Parser {
Handle<String> type,
Vector< Handle<Object> > arguments);
// JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5
// specification section 15.12.1 (and appendix A.8).
// The grammar is given section 15.12.1.2 (and appendix A.8.2).
// Parse JSON input as a single JSON value.
Expression* ParseJson(bool* ok);
// Parse a single JSON value from input (grammar production JSONValue).
// A JSON value is either a (double-quoted) string literal, a number literal,
// one of "true", "false", or "null", or an object or array literal.
Expression* ParseJsonValue(bool* ok);
// Parse a JSON object literal (grammar production JSONObject).
// An object literal is a squiggly-braced and comma separated sequence
// (possibly empty) of key/value pairs, where the key is a JSON string
// literal, the value is a JSON value, and the two are spearated by a colon.
// A JavaScript object also allows numbers and identifiers as keys.
Expression* ParseJsonObject(bool* ok);
// Parses a JSON array literal (grammar production JSONArray). An array
// literal is a square-bracketed and comma separated sequence (possibly empty)
// of JSON values.
// A JavaScript array allows leaving out values from the sequence.
Expression* ParseJsonArray(bool* ok);
friend class Target;
friend class TargetScope;
friend class LexicalScope;
......@@ -472,6 +448,49 @@ class CompileTimeValue: public AllStatic {
};
// JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5
// specification section 15.12.1 (and appendix A.8).
// The grammar is given section 15.12.1.2 (and appendix A.8.2).
class JsonParser BASE_EMBEDDED {
public:
// Parse JSON input as a single JSON value.
// Returns null handle and sets exception if parsing failed.
static Handle<Object> Parse(Handle<String> source) {
return JsonParser().ParseJson(source);
}
private:
JsonParser() { }
~JsonParser() { }
// Parse a string containing a single JSON value.
Handle<Object> ParseJson(Handle<String>);
// Parse a single JSON value from input (grammar production JSONValue).
// A JSON value is either a (double-quoted) string literal, a number literal,
// one of "true", "false", or "null", or an object or array literal.
Handle<Object> ParseJsonValue();
// Parse a JSON object literal (grammar production JSONObject).
// An object literal is a squiggly-braced and comma separated sequence
// (possibly empty) of key/value pairs, where the key is a JSON string
// literal, the value is a JSON value, and the two are separated by a colon.
// A JSON array dosn't allow numbers and identifiers as keys, like a
// JavaScript array.
Handle<Object> ParseJsonObject();
// Parses a JSON array literal (grammar production JSONArray). An array
// literal is a square-bracketed and comma separated sequence (possibly empty)
// of JSON values.
// A JSON array doesn't allow leaving out values from the sequence, nor does
// it allow a terminal comma, like a JavaScript array does.
Handle<Object> ParseJsonArray();
// Mark that a parsing error has happened at the current token, and
// return a null handle. Primarily for readability.
Handle<Object> ReportUnexpectedToken() { return Handle<Object>::null(); }
// Converts the currently parsed literal to a JavaScript String.
Handle<String> GetString();
Scanner scanner_;
};
} } // namespace v8::internal
#endif // V8_PARSER_H_
......@@ -7129,20 +7129,31 @@ static MaybeObject* Runtime_GlobalReceiver(Arguments args) {
}
static MaybeObject* Runtime_ParseJson(Arguments args) {
HandleScope scope;
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
Handle<Object> result = JsonParser::Parse(source);
if (result.is_null()) {
// Syntax error or stack overflow in scanner.
ASSERT(Top::has_pending_exception());
return Failure::Exception();
}
return *result;
}
static MaybeObject* Runtime_CompileString(Arguments args) {
HandleScope scope;
ASSERT_EQ(2, args.length());
ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
CONVERT_ARG_CHECKED(Oddball, is_json, 1)
// 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<SharedFunctionInfo> shared = Compiler::CompileEval(source,
context,
true,
validate);
true);
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
......@@ -7157,8 +7168,7 @@ static ObjectPair CompileGlobalEval(Handle<String> source,
Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
source,
Handle<Context>(Top::context()),
Top::context()->IsGlobalContext(),
Compiler::DONT_VALIDATE_JSON);
Top::context()->IsGlobalContext());
if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
shared,
......@@ -9370,8 +9380,7 @@ static MaybeObject* Runtime_DebugEvaluate(Arguments args) {
Handle<SharedFunctionInfo> shared =
Compiler::CompileEval(function_source,
context,
context->IsGlobalContext(),
Compiler::DONT_VALIDATE_JSON);
context->IsGlobalContext());
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> compiled_function =
Factory::NewFunctionFromSharedFunctionInfo(shared, context);
......@@ -9442,8 +9451,7 @@ static MaybeObject* Runtime_DebugEvaluateGlobal(Arguments args) {
Handle<SharedFunctionInfo> shared =
Compiler::CompileEval(source,
context,
true,
Compiler::DONT_VALIDATE_JSON);
true);
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> compiled_function =
Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
......
......@@ -164,6 +164,9 @@ namespace internal {
F(RegExpConstructResult, 3, 1) \
F(RegExpCloneResult, 1, 1) \
\
/* JSON */ \
F(ParseJson, 1, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \
F(StringIndexOf, 3, 1) \
......@@ -222,7 +225,7 @@ namespace internal {
/* Numbers */ \
\
/* Globals */ \
F(CompileString, 2, 1) \
F(CompileString, 1, 1) \
F(GlobalPrint, 1, 1) \
\
/* Eval */ \
......
......@@ -296,6 +296,9 @@ class Scanner {
// Returns the next token.
Token::Value Next();
// Returns the current token again.
Token::Value current_token() { return current_.token; }
// One token look-ahead (past the token returned by Next()).
Token::Value peek() const { return next_.token; }
......
......@@ -140,7 +140,7 @@ function GlobalEval(x) {
'be the global object from which eval originated');
}
var f = %CompileString(x, false);
var f = %CompileString(x);
if (!IS_FUNCTION(f)) return f;
return f.call(this);
......@@ -151,7 +151,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), false);
var f = %CompileString(ToString(expr));
f.call(%GlobalReceiver(global));
}
return null;
......@@ -1177,7 +1177,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, false)();
var f = %CompileString(source)();
%FunctionSetName(f, "anonymous");
return %SetNewFunctionAttributes(f);
}
......
......@@ -36,7 +36,6 @@ var current_source = ''; // Current source being compiled.
var source_count = 0; // Total number of scources compiled.
var host_compilations = 0; // Number of scources compiled through the API.
var eval_compilations = 0; // Number of scources compiled through eval.
var json_compilations = 0; // Number of scources compiled through JSON.parse.
function compileSource(source) {
......@@ -62,9 +61,6 @@ function listener(event, exec_state, event_data, data) {
case Debug.ScriptCompilationType.Eval:
eval_compilations++;
break;
case Debug.ScriptCompilationType.JSON:
json_compilations++;
break;
}
}
......@@ -74,13 +70,6 @@ function listener(event, exec_state, event_data, data) {
// For source with 'eval' there will be compile events with substrings
// as well as with with the exact source.
assertTrue(current_source.indexOf(event_data.script().source()) >= 0);
} else if (current_source.indexOf('JSON.parse') == 0) {
// For JSON the JSON source will be in parentheses.
var s = event_data.script().source();
if (s[0] == '(') {
s = s.substring(1, s.length - 2);
}
assertTrue(current_source.indexOf(s) >= 0);
} else {
// For source without 'eval' there will be a compile events with the
// exact source.
......@@ -113,7 +102,7 @@ source_count++; // Using eval causes additional compilation event.
compileSource('eval("eval(\'(function(){return a;})\')")');
source_count += 2; // Using eval causes additional compilation event.
compileSource('JSON.parse(\'{"a":1,"b":2}\')');
source_count++; // Using JSON.parse causes additional compilation event.
// Using JSON.parse does not causes additional compilation events.
compileSource('x=1; //@ sourceURL=myscript.js');
// Make sure that the debug event listener was invoked.
......@@ -123,10 +112,9 @@ assertFalse(exception, "exception in listener")
assertEquals(before_compile_count, after_compile_count);
// Check the actual number of events (no compilation through the API as all
// source compiled through eval except for one JSON.parse call).
// source compiled through eval).
assertEquals(source_count, after_compile_count);
assertEquals(0, host_compilations);
assertEquals(source_count - 1, eval_compilations);
assertEquals(1, json_compilations);
assertEquals(source_count, eval_compilations);
Debug.setListener(null);
......@@ -83,12 +83,10 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type,
// Test the script mirror for different functions.
testScriptMirror(function(){}, 'mirror-script.js', 100, 2, 0);
testScriptMirror(function(){}, 'mirror-script.js', 98, 2, 0);
testScriptMirror(Math.sin, 'native math.js', -1, 0, 0);
testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87);
testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88);
testScriptMirror(%CompileString('{"a":1,"b":2}', true), null, 1, 2, 2, '{"a":1,"b":2}');
testScriptMirror(%CompileString('{"a":1,\n "b":2}', true), null, 2, 2, 2, '{"a":1,\n "b":2}');
// Test taking slices of source.
var mirror = debug.MakeMirror(eval('(function(){\n 1;\n})')).script();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment