2011-06-17 Oliver Hunt <oliver@apple.com>
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Jun 2011 19:47:45 +0000 (19:47 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 19 Jun 2011 19:47:45 +0000 (19:47 +0000)
        Reviewed by Gavin Barraclough.

        JSONP is unnecessarily slow
        https://bugs.webkit.org/show_bug.cgi?id=62920

        JSONP has unfortunately become a fairly common idiom online, yet
        it triggers very poor performance in JSC as we end up doing codegen
        for a large number of property accesses that will
           * only be run once, so the vast amount of logic we dump to handle
             caching of accesses is unnecessary.
           * We are doing codegen that is directly proportional to just
             creating the object in the first place.

        This patch extends the use of the literal parser to JSONP-like structures
        in global code, handling a number of different forms I have seen online.
        In an extreme case this improves performance of JSONP by more than 2x
        due to removal of code generation and execution time, and a few optimisations
        that I made to the parser itself.

        * API/JSValueRef.cpp:
        (JSValueMakeFromJSONString):
        * interpreter/Interpreter.cpp:
        (JSC::Interpreter::callEval):
        (JSC::Interpreter::execute):
        * parser/Lexer.cpp:
        (JSC::Lexer::isKeyword):
        * parser/Lexer.h:
        * runtime/JSGlobalObjectFunctions.cpp:
        (JSC::globalFuncEval):
        * runtime/JSONObject.cpp:
        (JSC::JSONProtoFuncParse):
        * runtime/LiteralParser.cpp:
        (JSC::LiteralParser::tryJSONPParse):
        (JSC::LiteralParser::makeIdentifier):
        (JSC::LiteralParser::Lexer::lex):
        (JSC::LiteralParser::Lexer::next):
        (JSC::isSafeStringCharacter):
        (JSC::LiteralParser::Lexer::lexString):
        (JSC::LiteralParser::Lexer::lexNumber):
        (JSC::LiteralParser::parse):
        * runtime/LiteralParser.h:
        (JSC::LiteralParser::LiteralParser):
        (JSC::LiteralParser::tryLiteralParse):
        (JSC::LiteralParser::Lexer::Lexer):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@89219 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/API/JSValueRef.cpp
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/parser/Lexer.cpp
Source/JavaScriptCore/parser/Lexer.h
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Source/JavaScriptCore/runtime/JSONObject.cpp
Source/JavaScriptCore/runtime/LiteralParser.cpp
Source/JavaScriptCore/runtime/LiteralParser.h

index e70534c..18bbd91 100644 (file)
@@ -234,7 +234,8 @@ JSValueRef JSValueMakeFromJSONString(JSContextRef ctx, JSStringRef string)
 {
     ExecState* exec = toJS(ctx);
     APIEntryShim entryShim(exec);
-    LiteralParser parser(exec, string->ustring(), LiteralParser::StrictJSON);
+    UString str = string->ustring();
+    LiteralParser parser(exec, str.characters(), str.length(), LiteralParser::StrictJSON);
     return toRef(exec, parser.tryLiteralParse());
 }
 
index d9b08bc..f0d7296 100644 (file)
@@ -1,3 +1,50 @@
+2011-06-17  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Gavin Barraclough.
+
+        JSONP is unnecessarily slow
+        https://bugs.webkit.org/show_bug.cgi?id=62920
+
+        JSONP has unfortunately become a fairly common idiom online, yet
+        it triggers very poor performance in JSC as we end up doing codegen
+        for a large number of property accesses that will
+           * only be run once, so the vast amount of logic we dump to handle
+             caching of accesses is unnecessary.
+           * We are doing codegen that is directly proportional to just
+             creating the object in the first place.
+
+        This patch extends the use of the literal parser to JSONP-like structures
+        in global code, handling a number of different forms I have seen online.
+        In an extreme case this improves performance of JSONP by more than 2x
+        due to removal of code generation and execution time, and a few optimisations
+        that I made to the parser itself.
+
+        * API/JSValueRef.cpp:
+        (JSValueMakeFromJSONString):
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::callEval):
+        (JSC::Interpreter::execute):
+        * parser/Lexer.cpp:
+        (JSC::Lexer::isKeyword):
+        * parser/Lexer.h:
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncEval):
+        * runtime/JSONObject.cpp:
+        (JSC::JSONProtoFuncParse):
+        * runtime/LiteralParser.cpp:
+        (JSC::LiteralParser::tryJSONPParse):
+        (JSC::LiteralParser::makeIdentifier):
+        (JSC::LiteralParser::Lexer::lex):
+        (JSC::LiteralParser::Lexer::next):
+        (JSC::isSafeStringCharacter):
+        (JSC::LiteralParser::Lexer::lexString):
+        (JSC::LiteralParser::Lexer::lexNumber):
+        (JSC::LiteralParser::parse):
+        * runtime/LiteralParser.h:
+        (JSC::LiteralParser::LiteralParser):
+        (JSC::LiteralParser::tryLiteralParse):
+        (JSC::LiteralParser::Lexer::Lexer):
+
 2011-06-18  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r89184.
index 89276d8..ff2a838 100644 (file)
@@ -396,7 +396,7 @@ NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* r
     if (!codeBlock->isStrictMode()) {
         // FIXME: We can use the preparser in strict mode, we just need additional logic
         // to prevent duplicates.
-        LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON);
+        LiteralParser preparser(callFrame, programSource.characters(), programSource.length(), LiteralParser::NonStrictJSON);
         if (JSValue parsedObject = preparser.tryLiteralParse())
             return parsedObject;
     }
@@ -744,6 +744,74 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, S
         return checkedReturn(throwStackOverflowError(callFrame));
 
     DynamicGlobalObjectScope globalObjectScope(*scopeChain->globalData, scopeChain->globalObject.get());
+    LiteralParser literalParser(callFrame, program->source().data(), program->source().length(), LiteralParser::JSONP);
+    Vector<LiteralParser::JSONPData> JSONPData;
+    if (literalParser.tryJSONPParse(JSONPData)) {
+        JSGlobalObject* globalObject = scopeChain->globalObject.get();
+        JSValue result;
+        for (unsigned entry = 0; entry < JSONPData.size(); entry++) {
+            Vector<LiteralParser::JSONPPathEntry> JSONPPath;
+            JSONPPath.swap(JSONPData[entry].m_path);
+            JSValue JSONPValue = JSONPData[entry].m_value.get();
+            if (JSONPPath.size() == 1 && JSONPPath[0].m_type == LiteralParser::JSONPPathEntryTypeDeclare) {
+                if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) {
+                    PutPropertySlot slot;
+                    globalObject->put(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
+                } else
+                    globalObject->putWithAttributes(callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete);
+                // var declarations return undefined
+                result = jsUndefined();
+                continue;
+            }
+            JSValue baseObject(globalObject);
+            for (unsigned i = 0; i < JSONPPath.size() - 1; i++) {
+                ASSERT(JSONPPath[i].m_type != LiteralParser::JSONPPathEntryTypeDeclare);
+                switch (JSONPPath[i].m_type) {
+                case LiteralParser::JSONPPathEntryTypeDot: {
+                    if (i == 0) {
+                        PropertySlot slot(globalObject);
+                        if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot))
+                            return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName));
+                        baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
+                    } else
+                        baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName);
+                    if (callFrame->hadException())
+                        return jsUndefined();
+                    continue;
+                }
+                case LiteralParser::JSONPPathEntryTypeLookup: {
+                    baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex);
+                    if (callFrame->hadException())
+                        return jsUndefined();
+                    continue;
+                }
+                default:
+                    ASSERT_NOT_REACHED();
+                    return jsUndefined();
+                }
+            }
+            PutPropertySlot slot;
+            switch (JSONPPath.last().m_type) {
+            case LiteralParser::JSONPPathEntryTypeDot: {
+                baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot);
+                if (callFrame->hadException())
+                    return jsUndefined();
+                break;
+            }
+            case LiteralParser::JSONPPathEntryTypeLookup: {
+                baseObject.put(callFrame, JSONPPath.last().m_pathIndex, JSONPValue);
+                if (callFrame->hadException())
+                    return jsUndefined();
+                break;
+            }
+            default:
+                ASSERT_NOT_REACHED();
+                    return jsUndefined();
+            }
+            result = JSONPValue;
+        }
+        return result;
+    }
 
     JSObject* error = program->compile(callFrame, scopeChain);
     if (error)
index 6d2d37a..1666881 100644 (file)
@@ -481,6 +481,11 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer::parseIde
     return IDENT;
 }
 
+bool Lexer::isKeyword(const Identifier& ident)
+{
+    return m_keywordTable.entry(m_globalData, ident);
+}
+
 template <bool shouldBuildStrings> ALWAYS_INLINE bool Lexer::parseString(JSTokenData* tokenData, bool strictMode)
 {
     int stringQuoteCharacter = m_current;
index a9fcfa1..6dfd52e 100644 (file)
@@ -90,7 +90,9 @@ namespace JSC {
         SourceProvider* sourceProvider() const { return m_source->provider(); }
         
         JSTokenType lexExpectIdentifier(JSTokenData*, JSTokenInfo*, unsigned, bool strictMode);
-        
+
+        bool isKeyword(const Identifier&);
+
     private:
         friend class JSGlobalData;
 
index c9c413e..73750ae 100644 (file)
@@ -445,7 +445,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEval(ExecState* exec)
 
     UString s = x.toString(exec);
 
-    LiteralParser preparser(exec, s, LiteralParser::NonStrictJSON);
+    LiteralParser preparser(exec, s.characters(), s.length(), LiteralParser::NonStrictJSON);
     if (JSValue parsedObject = preparser.tryLiteralParse())
         return JSValue::encode(parsedObject);
 
index 8742fdc..8d261a2 100644 (file)
@@ -816,7 +816,7 @@ EncodedJSValue JSC_HOST_CALL JSONProtoFuncParse(ExecState* exec)
         return JSValue::encode(jsNull());
 
     LocalScope scope(exec->globalData());
-    LiteralParser jsonParser(exec, source, LiteralParser::StrictJSON);
+    LiteralParser jsonParser(exec, source.characters(), source.length(), LiteralParser::StrictJSON);
     JSValue unfiltered = jsonParser.tryLiteralParse();
     if (!unfiltered)
         return throwVMError(exec, createSyntaxError(exec, "Unable to parse JSON string"));
index bc2e736..715caae 100644 (file)
@@ -42,7 +42,90 @@ static inline bool isJSONWhiteSpace(const UChar& c)
     return c == ' ' || c == 0x9 || c == 0xA || c == 0xD;
 }
 
-LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
+bool LiteralParser::tryJSONPParse(Vector<JSONPData>& results)
+{
+    if (m_lexer.next() != TokIdentifier)
+        return false;
+    do {
+        Vector<JSONPPathEntry> path;
+        // Unguarded next to start off the lexer
+        Identifier name = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
+        JSONPPathEntry entry;
+        if (name == m_exec->globalData().propertyNames->varKeyword) {
+            if (m_lexer.next() != TokIdentifier)
+                return false;
+            entry.m_type = JSONPPathEntryTypeDeclare;
+            entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
+            path.append(entry);
+        } else {
+            entry.m_type = JSONPPathEntryTypeDot;
+            entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
+            path.append(entry);
+        }
+        if (m_exec->globalData().lexer->isKeyword(entry.m_pathEntryName))
+            return false;
+        TokenType tokenType = m_lexer.next();
+        while (tokenType != TokAssign) {
+            switch (tokenType) {
+            case TokLBracket: {
+                entry.m_type = JSONPPathEntryTypeLookup;
+                if (m_lexer.next() != TokNumber)
+                    return false;
+                double doubleIndex = m_lexer.currentToken().numberToken;
+                int index = (int)doubleIndex;
+                if (index != doubleIndex || index < 0)
+                    return false;
+                entry.m_pathIndex = index;
+                if (m_lexer.next() != TokRBracket)
+                    return false;
+                break;
+            }
+            case TokDot: {
+                entry.m_type = JSONPPathEntryTypeDot;
+                if (m_lexer.next() != TokIdentifier)
+                    return false;
+                entry.m_pathEntryName = Identifier(m_exec, m_lexer.currentToken().start, m_lexer.currentToken().end - m_lexer.currentToken().start);
+                break;
+            }
+            default:
+                return false;
+            }
+            path.append(entry);
+            tokenType = m_lexer.next();
+        }
+        m_lexer.next();
+        results.append(JSONPData());
+        results.last().m_value.set(m_exec->globalData(), parse(StartParseExpression));
+        if (!results.last().m_value)
+            return false;
+        results.last().m_path.swap(path);
+        if (m_lexer.currentToken().type != TokSemi)
+            break;
+        m_lexer.next();
+    } while (m_lexer.currentToken().type == TokIdentifier);
+    return m_lexer.currentToken().type == TokEnd;
+}
+    
+ALWAYS_INLINE const Identifier LiteralParser::makeIdentifier(const UChar* characters, size_t length)
+{
+    if (!length)
+        return m_exec->globalData().propertyNames->emptyIdentifier;
+    if (characters[0] >= MaximumCachableCharacter)
+        return Identifier(&m_exec->globalData(), characters, length);
+
+    if (length == 1) {
+        if (!m_shortIdentifiers[characters[0]].isNull())
+            return m_shortIdentifiers[characters[0]];
+        m_shortIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length);
+        return m_shortIdentifiers[characters[0]];
+    }
+    if (!m_recentIdentifiers[characters[0]].isNull() && Identifier::equal(m_recentIdentifiers[characters[0]].impl(), characters, length))
+        return m_recentIdentifiers[characters[0]];
+    m_recentIdentifiers[characters[0]] = Identifier(&m_exec->globalData(), characters, length);
+    return m_recentIdentifiers[characters[0]];
+}
+
+template <LiteralParser::ParserMode mode> LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
 {
     while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr))
         ++m_ptr;
@@ -89,9 +172,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
             token.end = ++m_ptr;
             return TokColon;
         case '"':
-            if (m_mode == StrictJSON)
-                return lexString<StrictJSON>(token);
-            return lexString<NonStrictJSON>(token);
+            return lexString<mode, '"'>(token);
         case 't':
             if (m_end - m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') {
                 m_ptr += 4;
@@ -115,7 +196,7 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
                 token.end = m_ptr;
                 return TokNull;
             }
-            break;    
+            break;
         case '-':
         case '0':
         case '1':
@@ -129,27 +210,69 @@ LiteralParser::TokenType LiteralParser::Lexer::lex(LiteralParserToken& token)
         case '9':
             return lexNumber(token);
     }
+    if (m_ptr < m_end) {
+        if (*m_ptr == '.') {
+            token.type = TokDot;
+            token.end = ++m_ptr;
+            return TokDot;
+        }
+        if (*m_ptr == '=') {
+            token.type = TokAssign;
+            token.end = ++m_ptr;
+            return TokAssign;
+        }
+        if (*m_ptr == ';') {
+            token.type = TokSemi;
+            token.end = ++m_ptr;
+            return TokAssign;
+        }
+        if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$') {
+            while (m_ptr < m_end && (isASCIIAlphanumeric(*m_ptr) || *m_ptr == '_' || *m_ptr == '$'))
+                m_ptr++;
+            token.stringToken = token.start;
+            token.stringLength = m_ptr - token.start;
+            token.type = TokIdentifier;
+            token.end = m_ptr;
+            return TokIdentifier;
+        }
+        if (*m_ptr == '\'') {
+            if (mode == StrictJSON)
+                return TokError;
+            return lexString<mode, '\''>(token);
+        }
+    }
     return TokError;
 }
 
-template <LiteralParser::ParserMode mode> static inline bool isSafeStringCharacter(UChar c)
+LiteralParser::TokenType LiteralParser::Lexer::next()
 {
-    return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != '"') || c == '\t';
+    if (m_mode == NonStrictJSON)
+        return lex<NonStrictJSON>(m_currentToken);
+    if (m_mode == JSONP)
+        return lex<JSONP>(m_currentToken);
+    return lex<StrictJSON>(m_currentToken);
+}
+
+template <LiteralParser::ParserMode mode, UChar terminator> static inline bool isSafeStringCharacter(UChar c)
+{
+    return (c >= ' ' && (mode == LiteralParser::StrictJSON || c <= 0xff) && c != '\\' && c != terminator) || c == '\t';
 }
 
 // "inline" is required here to help WINSCW compiler resolve specialized argument in templated functions.
-template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
+template <LiteralParser::ParserMode mode, UChar terminator> inline LiteralParser::TokenType LiteralParser::Lexer::lexString(LiteralParserToken& token)
 {
     ++m_ptr;
-    const UChar* runStart;
+    const UChar* runStart = m_ptr;
     UStringBuilder builder;
     do {
         runStart = m_ptr;
-        while (m_ptr < m_end && isSafeStringCharacter<mode>(*m_ptr))
+        while (m_ptr < m_end && isSafeStringCharacter<mode, terminator>(*m_ptr))
             ++m_ptr;
-        if (runStart < m_ptr)
+        if (builder.length())
             builder.append(runStart, m_ptr - runStart);
-        if ((mode == StrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
+        if ((mode != NonStrictJSON) && m_ptr < m_end && *m_ptr == '\\') {
+            if (builder.isEmpty() && runStart < m_ptr)
+                builder.append(runStart, m_ptr - runStart);
             ++m_ptr;
             if (m_ptr >= m_end)
                 return TokError;
@@ -199,15 +322,28 @@ template <LiteralParser::ParserMode mode> inline LiteralParser::TokenType Litera
                     break;
 
                 default:
+                    if (*m_ptr == '\'' && mode != StrictJSON) {
+                        builder.append('\'');
+                        m_ptr++;
+                        break;
+                    }
                     return TokError;
             }
         }
-    } while ((mode == StrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != '"');
+    } while ((mode != NonStrictJSON) && m_ptr != runStart && (m_ptr < m_end) && *m_ptr != terminator);
 
-    if (m_ptr >= m_end || *m_ptr != '"')
+    if (m_ptr >= m_end || *m_ptr != terminator)
         return TokError;
 
-    token.stringToken = builder.toUString();
+    if (builder.isEmpty()) {
+        token.stringBuffer = UString();
+        token.stringToken = runStart;
+        token.stringLength = m_ptr - runStart;
+    } else {
+        token.stringBuffer = builder.toUString();
+        token.stringToken = token.stringBuffer.characters();
+        token.stringLength = token.stringBuffer.length();
+    }
     token.type = TokString;
     token.end = ++m_ptr;
     return TokString;
@@ -253,6 +389,22 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok
         ++m_ptr;
         while (m_ptr < m_end && isASCIIDigit(*m_ptr))
             ++m_ptr;
+    } else if (m_ptr < m_end && (*m_ptr != 'e' && *m_ptr != 'E') && (m_ptr - token.start) < 10) {
+        int result = 0;
+        token.type = TokNumber;
+        token.end = m_ptr;
+        const UChar* digit = token.start;
+        int negative = 1;
+        if (*digit == '-') {
+            negative = -1;
+            digit++;
+        }
+        
+        while (digit < m_ptr)
+            result = result * 10 + (*digit++) - '0';
+        result *= negative;
+        token.numberToken = result;
+        return TokNumber;
     }
 
     //  ([eE][+-]? [0-9]+)?
@@ -337,7 +489,7 @@ JSValue LiteralParser::parse(ParserState initialState)
                 objectStack.append(object);
 
                 TokenType type = m_lexer.next();
-                if (type == TokString) {
+                if (type == TokString || (m_mode != StrictJSON && type == TokIdentifier)) {
                     Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
 
                     // Check for colon
@@ -345,10 +497,11 @@ JSValue LiteralParser::parse(ParserState initialState)
                         return JSValue();
                     
                     m_lexer.next();
-                    identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
+                    identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
                     stateStack.append(DoParseObjectEndExpression);
                     goto startParseExpression;
-                } else if (type != TokRBrace) 
+                }
+                if (type != TokRBrace) 
                     return JSValue();
                 m_lexer.next();
                 lastValue = objectStack.last();
@@ -358,7 +511,7 @@ JSValue LiteralParser::parse(ParserState initialState)
             doParseObjectStartExpression:
             case DoParseObjectStartExpression: {
                 TokenType type = m_lexer.next();
-                if (type != TokString)
+                if (type != TokString && (m_mode == StrictJSON || type != TokIdentifier))
                     return JSValue();
                 Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
 
@@ -367,7 +520,7 @@ JSValue LiteralParser::parse(ParserState initialState)
                     return JSValue();
 
                 m_lexer.next();
-                identifierStack.append(Identifier(m_exec, identifierToken.stringToken));
+                identifierStack.append(makeIdentifier(identifierToken.stringToken, identifierToken.stringLength));
                 stateStack.append(DoParseObjectEndExpression);
                 goto startParseExpression;
             }
@@ -394,7 +547,7 @@ JSValue LiteralParser::parse(ParserState initialState)
                     case TokString: {
                         Lexer::LiteralParserToken stringToken = m_lexer.currentToken();
                         m_lexer.next();
-                        lastValue = jsString(m_exec, stringToken.stringToken);
+                        lastValue = jsString(m_exec, makeIdentifier(stringToken.stringToken, stringToken.stringLength).ustring());
                         break;
                     }
                     case TokNumber: {
index 6df5d06..85e0311 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef LiteralParser_h
 #define LiteralParser_h
 
+#include "Identifier.h"
 #include "JSGlobalObjectFunctions.h"
 #include "JSValue.h"
 #include "UString.h"
@@ -34,10 +35,10 @@ namespace JSC {
 
     class LiteralParser {
     public:
-        typedef enum { StrictJSON, NonStrictJSON } ParserMode;
-        LiteralParser(ExecState* exec, const UString& s, ParserMode mode)
+        typedef enum { StrictJSON, NonStrictJSON, JSONP } ParserMode;
+        LiteralParser(ExecState* exec, const UChar* characters, unsigned length, ParserMode mode)
             : m_exec(exec)
-            , m_lexer(s, mode)
+            , m_lexer(characters, length, mode)
             , m_mode(mode)
         {
         }
@@ -46,10 +47,32 @@ namespace JSC {
         {
             m_lexer.next();
             JSValue result = parse(m_mode == StrictJSON ? StartParseExpression : StartParseStatement);
+            if (m_lexer.currentToken().type == TokSemi)
+                m_lexer.next();
             if (m_lexer.currentToken().type != TokEnd)
                 return JSValue();
             return result;
         }
+        
+        enum JSONPPathEntryType {
+            JSONPPathEntryTypeDeclare, // var pathEntryName = JSON
+            JSONPPathEntryTypeDot, // <prior entries>.pathEntryName = JSON
+            JSONPPathEntryTypeLookup // <prior entries>[pathIndex] = JSON
+        };
+
+        struct JSONPPathEntry {
+            JSONPPathEntryType m_type;
+            Identifier m_pathEntryName;
+            int m_pathIndex;
+        };
+
+        struct JSONPData {
+            Vector<JSONPPathEntry> m_path;
+            Strong<Unknown> m_value;
+        };
+
+        bool tryJSONPParse(Vector<JSONPData>&);
+
     private:
         enum ParserState { StartParseObject, StartParseArray, StartParseExpression, 
                            StartParseStatement, StartParseStatementEndStatement, 
@@ -58,29 +81,31 @@ namespace JSC {
         enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace, 
                          TokString, TokIdentifier, TokNumber, TokColon, 
                          TokLParen, TokRParen, TokComma, TokTrue, TokFalse,
-                         TokNull, TokEnd, TokError };
-
+                         TokNull, TokEnd, TokDot, TokAssign, TokSemi, TokError };
+        
         class Lexer {
         public:
             struct LiteralParserToken {
                 TokenType type;
                 const UChar* start;
                 const UChar* end;
-                UString stringToken;
-                double numberToken;
+                UString stringBuffer;
+                union {
+                    double numberToken;
+                    struct {
+                        const UChar* stringToken;
+                        int stringLength;
+                    };
+                };
             };
-            Lexer(const UString& s, ParserMode mode)
-                : m_string(s)
-                , m_mode(mode)
-                , m_ptr(s.characters())
-                , m_end(s.characters() + s.length())
+            Lexer(const UChar* characters, unsigned length, ParserMode mode)
+                : m_mode(mode)
+                , m_ptr(characters)
+                , m_end(characters + length)
             {
             }
             
-            TokenType next()
-            {
-                return lex(m_currentToken);
-            }
+            TokenType next();
             
             const LiteralParserToken& currentToken()
             {
@@ -88,9 +113,9 @@ namespace JSC {
             }
             
         private:
-            TokenType lex(LiteralParserToken&);
-            template <ParserMode mode> TokenType lexString(LiteralParserToken&);
-            TokenType lexNumber(LiteralParserToken&);
+            template <ParserMode mode> TokenType lex(LiteralParserToken&);
+            template <ParserMode mode, UChar terminator> ALWAYS_INLINE TokenType lexString(LiteralParserToken&);
+            ALWAYS_INLINE TokenType lexNumber(LiteralParserToken&);
             LiteralParserToken m_currentToken;
             UString m_string;
             ParserMode m_mode;
@@ -104,7 +129,12 @@ namespace JSC {
         ExecState* m_exec;
         LiteralParser::Lexer m_lexer;
         ParserMode m_mode;
+        static unsigned const MaximumCachableCharacter = 128;
+        FixedArray<Identifier, MaximumCachableCharacter> m_shortIdentifiers;
+        FixedArray<Identifier, MaximumCachableCharacter> m_recentIdentifiers;
+        ALWAYS_INLINE const Identifier makeIdentifier(const UChar* characters, size_t length);
     };
+
 }
 
 #endif