mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 578216 - Make eval(json-like string) fast. r=sayrer.
This commit is contained in:
parent
5a3ae5c418
commit
7ba153876d
@ -76,6 +76,7 @@
|
||||
#include "jsstr.h"
|
||||
#include "jstracer.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "json.h"
|
||||
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
@ -1129,6 +1130,28 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
|
||||
|
||||
JSString *str = argv[0].toString();
|
||||
JSScript *script = NULL;
|
||||
|
||||
const jschar *chars;
|
||||
size_t length;
|
||||
str->getCharsAndLength(chars, length);
|
||||
|
||||
/*
|
||||
* If the eval string starts with '(' and ends with ')', it may be JSON.
|
||||
* Try the JSON parser first because it's much faster. If the eval string
|
||||
* isn't JSON, JSON parsing will probably fail quickly, so little time
|
||||
* will be lost.
|
||||
*/
|
||||
if (length > 2 && chars[0] == '(' && chars[length-1] == ')') {
|
||||
JSONParser *jp = js_BeginJSONParse(cx, vp, /* suppressErrors = */true);
|
||||
JSBool ok = jp != NULL;
|
||||
if (ok) {
|
||||
/* Run JSON-parser on string inside ( and ). */
|
||||
ok = js_ConsumeJSONText(cx, jp, chars+1, length-2);
|
||||
ok &= js_FinishJSONParse(cx, jp, NullValue());
|
||||
if (ok)
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache local eval scripts indexed by source qualified by scope.
|
||||
@ -1216,7 +1239,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
|
||||
script = Compiler::compileScript(cx, scopeobj, callerFrame,
|
||||
principals,
|
||||
TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT,
|
||||
str->chars(), str->length(),
|
||||
chars, length,
|
||||
NULL, file, line, str, staticLevel);
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
@ -77,7 +77,7 @@ struct JSONParser
|
||||
{
|
||||
JSONParser(JSContext *cx)
|
||||
: hexChar(), numHex(), statep(), stateStack(), rootVal(), objectStack(),
|
||||
objectKey(cx), buffer(cx)
|
||||
objectKey(cx), buffer(cx), suppressErrors(false)
|
||||
{}
|
||||
|
||||
/* Used while handling \uNNNN in strings */
|
||||
@ -90,6 +90,7 @@ struct JSONParser
|
||||
JSObject *objectStack;
|
||||
js::Vector<jschar, 8> objectKey;
|
||||
js::Vector<jschar, 8> buffer;
|
||||
bool suppressErrors;
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -655,6 +656,14 @@ Walk(JSContext *cx, jsid id, JSObject *holder, const Value &reviver, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
JSONParseError(JSContext *cx, JSONParser *jp)
|
||||
{
|
||||
if (!jp->suppressErrors)
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
static bool
|
||||
Revive(JSContext *cx, const Value &reviver, Value *vp)
|
||||
{
|
||||
@ -673,7 +682,7 @@ Revive(JSContext *cx, const Value &reviver, Value *vp)
|
||||
}
|
||||
|
||||
JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, Value *rootVal)
|
||||
js_BeginJSONParse(JSContext *cx, Value *rootVal, bool suppressErrors /*= true*/)
|
||||
{
|
||||
if (!cx)
|
||||
return NULL;
|
||||
@ -693,6 +702,7 @@ js_BeginJSONParse(JSContext *cx, Value *rootVal)
|
||||
jp->statep = jp->stateStack;
|
||||
*jp->statep = JSON_PARSE_STATE_INIT;
|
||||
jp->rootVal = rootVal;
|
||||
jp->suppressErrors = suppressErrors;
|
||||
|
||||
return jp;
|
||||
|
||||
@ -734,10 +744,8 @@ js_FinishJSONParse(JSContext *cx, JSONParser *jp, const Value &reviver)
|
||||
if (!early_ok)
|
||||
return false;
|
||||
|
||||
if (!ok) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return false;
|
||||
}
|
||||
if (!ok)
|
||||
return !!JSONParseError(cx, jp);
|
||||
|
||||
if (reviver.isObject() && reviver.toObject().isCallable())
|
||||
ok = Revive(cx, reviver, vp);
|
||||
@ -750,15 +758,13 @@ PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
|
||||
{
|
||||
if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
|
||||
// extra input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
jp->statep++;
|
||||
if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) {
|
||||
// too deep
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
*jp->statep = state;
|
||||
@ -772,8 +778,7 @@ PopState(JSContext *cx, JSONParser *jp)
|
||||
jp->statep--;
|
||||
if (jp->statep < jp->stateStack) {
|
||||
jp->statep = jp->stateStack;
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
if (*jp->statep == JSON_PARSE_STATE_INIT)
|
||||
@ -811,10 +816,8 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
|
||||
jsuint len;
|
||||
if (!js_GetLengthProperty(cx, jp->objectStack, &len))
|
||||
return JS_FALSE;
|
||||
if (len >= JSON_MAX_DEPTH) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (len >= JSON_MAX_DEPTH)
|
||||
return JSONParseError(cx, jp);
|
||||
|
||||
AutoObjectRooter tvr(cx, obj);
|
||||
Value v = ObjectOrNullValue(obj);
|
||||
@ -917,8 +920,7 @@ HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
return JS_FALSE;
|
||||
if (ep != buf + len) {
|
||||
// bad number input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
return PushPrimitive(cx, jp, DoubleValue(val));
|
||||
@ -941,8 +943,7 @@ HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
TokenKind tt = js_CheckKeyword(buf, len);
|
||||
if (tt != TOK_PRIMARY) {
|
||||
// bad keyword
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
if (buf[0] == 'n') {
|
||||
@ -952,8 +953,7 @@ HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
|
||||
} else if (buf[0] == 'f') {
|
||||
keyword.setBoolean(false);
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
return PushPrimitive(cx, jp, keyword);
|
||||
@ -1006,10 +1006,8 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (!PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
|
||||
if (*jp->statep != JSON_PARSE_STATE_ARRAY) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (*jp->statep != JSON_PARSE_STATE_ARRAY)
|
||||
return JSONParseError(cx, jp);
|
||||
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
@ -1019,8 +1017,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
|
||||
if (c == '}') {
|
||||
// we should only find these in OBJECT_KEY state
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
if (c == '"') {
|
||||
@ -1053,8 +1050,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1066,12 +1062,11 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_ARRAY :
|
||||
case JSON_PARSE_STATE_ARRAY:
|
||||
if (c == ']') {
|
||||
if (!CloseArray(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
@ -1079,12 +1074,11 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
|
||||
return JS_FALSE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR :
|
||||
case JSON_PARSE_STATE_OBJECT_PAIR:
|
||||
if (c == '"') {
|
||||
// we want to be waiting for a : when the string has been read
|
||||
*jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
|
||||
@ -1095,8 +1089,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp))
|
||||
return JS_FALSE;
|
||||
} else if (c == ']' || !JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1104,8 +1097,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
if (c == ':') {
|
||||
*jp->statep = JSON_PARSE_STATE_VALUE;
|
||||
} else if (!JS_ISXMLSPACE(c)) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1126,8 +1118,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
} else if (c < 31) {
|
||||
// The JSON lexical grammer does not allow a JSONStringCharacter to be
|
||||
// any of the Unicode characters U+0000 thru U+001F (control characters).
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
} else {
|
||||
if (!jp->buffer.append(c))
|
||||
return JS_FALSE;
|
||||
@ -1152,8 +1143,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
*jp->statep = JSON_PARSE_STATE_STRING_HEX;
|
||||
continue;
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1170,8 +1160,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
} else if (('A' <= c) && (c <= 'F')) {
|
||||
jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
|
||||
if (++(jp->numHex) == 4) {
|
||||
@ -1215,8 +1204,7 @@ js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len
|
||||
case JSON_PARSE_STATE_FINISHED:
|
||||
if (!JS_ISXMLSPACE(c)) {
|
||||
// extra input
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
|
||||
return JS_FALSE;
|
||||
return JSONParseError(cx, jp);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -111,7 +111,7 @@ enum JSONDataType {
|
||||
struct JSONParser;
|
||||
|
||||
extern JSONParser *
|
||||
js_BeginJSONParse(JSContext *cx, js::Value *rootVal);
|
||||
js_BeginJSONParse(JSContext *cx, js::Value *rootVal, bool suppressErrors = false);
|
||||
|
||||
extern JSBool
|
||||
js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len);
|
||||
|
Loading…
Reference in New Issue
Block a user