Commit 190ae2c9 authored by oliver@apple.com's avatar oliver@apple.com

Make LiteralParser non-recursive

Reviewed by Geoff Garen.

Convert LiteralParser from using a simple recursive descent parser
to a hand rolled PDA.  Relatively simple conversion, but required
modifications to MarkedArgumentBuffer to make it more suitable as
a generic marked vector.   I'll refactor and rename MarkedArgumentBuffer
in future as there are many other cases where it will be useful to
have such a class.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@44644 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 6e3aec20
2009-06-12 Oliver Hunt <oliver@apple.com>
Reviewed by Geoff Garen.
Make LiteralParser non-recursive
Convert LiteralParser from using a simple recursive descent parser
to a hand rolled PDA. Relatively simple conversion, but required
modifications to MarkedArgumentBuffer to make it more suitable as
a generic marked vector. I'll refactor and rename MarkedArgumentBuffer
in future as there are many other cases where it will be useful to
have such a class.
* runtime/ArgList.h:
(JSC::MarkedArgumentBuffer::MarkedArgumentBuffer):
(JSC::MarkedArgumentBuffer::append):
(JSC::MarkedArgumentBuffer::removeLast):
(JSC::MarkedArgumentBuffer::last):
* runtime/LiteralParser.cpp:
(JSC::LiteralParser::parse):
* runtime/LiteralParser.h:
(JSC::LiteralParser::LiteralParser):
(JSC::LiteralParser::tryLiteralParse):
(JSC::LiteralParser::):
2009-06-12 David Levin <levin@chromium.org>
Reviewed by NOBODY (build fix for windows).
......@@ -44,7 +44,8 @@ namespace JSC {
// Constructor for a read-write list, to which you may append values.
// FIXME: Remove all clients of this API, then remove this API.
MarkedArgumentBuffer()
: m_markSet(0)
: m_isUsingInlineBuffer(true)
, m_markSet(0)
#ifndef NDEBUG
, m_isReadOnly(false)
#endif
......@@ -57,6 +58,7 @@ namespace JSC {
MarkedArgumentBuffer(Register* buffer, size_t size)
: m_buffer(buffer)
, m_size(size)
, m_isUsingInlineBuffer(true)
, m_markSet(0)
#ifndef NDEBUG
, m_isReadOnly(true)
......@@ -103,7 +105,7 @@ namespace JSC {
{
ASSERT(!m_isReadOnly);
if (m_size < inlineCapacity) {
if (m_isUsingInlineBuffer && m_size < inlineCapacity) {
m_vector.uncheckedAppend(v);
++m_size;
} else {
......@@ -111,9 +113,23 @@ namespace JSC {
// the performance of the fast "just append to inline buffer" case.
slowAppend(v);
++m_size;
m_isUsingInlineBuffer = false;
}
}
void removeLast()
{
ASSERT(m_size);
m_size--;
m_vector.removeLast();
}
JSValue last()
{
ASSERT(m_size);
return m_buffer[m_size - 1].jsValue();
}
iterator begin() { return m_buffer; }
iterator end() { return m_buffer + m_size; }
......@@ -127,6 +143,7 @@ namespace JSC {
Register* m_buffer;
size_t m_size;
bool m_isUsingInlineBuffer;
VectorType m_vector;
ListSet* m_markSet;
......
......@@ -32,22 +32,6 @@
namespace JSC {
class LiteralParser::StackGuard {
public:
StackGuard(LiteralParser* parser)
: m_parser(parser)
{
m_parser->m_depth++;
}
~StackGuard()
{
m_parser->m_depth--;
}
bool isSafe() { return m_parser->m_depth < 10; }
private:
LiteralParser* m_parser;
};
static bool isSafeStringCharacter(UChar c)
{
return (c >= ' ' && c <= 0xff && c != '\\') || c == '\t';
......@@ -197,110 +181,145 @@ LiteralParser::TokenType LiteralParser::Lexer::lexNumber(LiteralParserToken& tok
return TokNumber;
}
JSValue LiteralParser::parseStatement()
JSValue LiteralParser::parse(ParserState initialState)
{
StackGuard guard(this);
if (!guard.isSafe())
return abortParse();
ParserState state = initialState;
MarkedArgumentBuffer objectStack;
JSValue lastValue;
Vector<ParserState, 16> stateStack;
Vector<Identifier, 16> identifierStack;
while (1) {
switch(state) {
startParseArray:
case StartParseArray: {
JSArray* array = constructEmptyArray(m_exec);
objectStack.append(array);
// fallthrough
}
doParseArrayStartExpression:
case DoParseArrayStartExpression: {
if (m_lexer.next() == TokRBracket) {
m_lexer.next();
lastValue = objectStack.last();
objectStack.removeLast();
break;
}
switch (m_lexer.currentToken().type) {
case TokLBracket:
case TokNumber:
case TokString:
return parseExpression();
case TokLParen: {
m_lexer.next();
JSValue result = parseExpression();
if (m_aborted || m_lexer.currentToken().type != TokRParen)
return abortParse();
m_lexer.next();
return result;
}
default:
return abortParse();
}
}
stateStack.append(DoParseArrayEndExpression);
goto startParseExpression;
}
case DoParseArrayEndExpression: {
asArray(objectStack.last())->push(m_exec, lastValue);
if (m_lexer.currentToken().type == TokComma)
goto doParseArrayStartExpression;
JSValue LiteralParser::parseExpression()
{
StackGuard guard(this);
if (!guard.isSafe())
return abortParse();
switch (m_lexer.currentToken().type) {
case TokLBracket:
return parseArray();
case TokLBrace:
return parseObject();
case TokString: {
Lexer::LiteralParserToken stringToken = m_lexer.currentToken();
m_lexer.next();
return jsString(m_exec, UString(stringToken.start + 1, stringToken.end - stringToken.start - 2));
}
case TokNumber: {
Lexer::LiteralParserToken numberToken = m_lexer.currentToken();
m_lexer.next();
return jsNumber(m_exec, UString(numberToken.start, numberToken.end - numberToken.start).toDouble());
}
default:
return JSValue();
}
}
if (m_lexer.currentToken().type != TokRBracket)
return JSValue();
m_lexer.next();
lastValue = objectStack.last();
objectStack.removeLast();
break;
}
startParseObject:
case StartParseObject: {
JSObject* object = constructEmptyObject(m_exec);
objectStack.append(object);
// fallthrough
}
doParseObjectStartExpression:
case DoParseObjectStartExpression: {
TokenType type = m_lexer.next();
if (type == TokString) {
Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
JSValue LiteralParser::parseArray()
{
StackGuard guard(this);
if (!guard.isSafe())
return abortParse();
JSArray* array = constructEmptyArray(m_exec);
while (true) {
m_lexer.next();
JSValue value = parseExpression();
if (m_aborted)
return JSValue();
if (!value)
break;
array->push(m_exec, value);
// Check for colon
if (m_lexer.next() != TokColon)
return JSValue();
if (m_lexer.currentToken().type != TokComma)
break;
}
if (m_lexer.currentToken().type != TokRBracket)
return abortParse();
m_lexer.next();
return array;
}
m_lexer.next();
identifierStack.append(Identifier(m_exec, identifierToken.start + 1, identifierToken.end - identifierToken.start - 2));
stateStack.append(DoParseObjectEndExpression);
goto startParseExpression;
} else if (type != TokRBrace)
return JSValue();
m_lexer.next();
lastValue = objectStack.last();
objectStack.removeLast();
break;
}
case DoParseObjectEndExpression:
{
asObject(objectStack.last())->putDirect(identifierStack.last(), lastValue);
identifierStack.removeLast();
if (m_lexer.currentToken().type == TokComma)
goto doParseObjectStartExpression;
if (m_lexer.currentToken().type != TokRBrace)
return JSValue();
m_lexer.next();
lastValue = objectStack.last();
objectStack.removeLast();
break;
}
startParseExpression:
case StartParseExpression: {
switch (m_lexer.currentToken().type) {
case TokLBracket:
goto startParseArray;
case TokLBrace:
goto startParseObject;
case TokString: {
Lexer::LiteralParserToken stringToken = m_lexer.currentToken();
m_lexer.next();
lastValue = jsString(m_exec, UString(stringToken.start + 1, stringToken.end - stringToken.start - 2));
break;
}
case TokNumber: {
Lexer::LiteralParserToken numberToken = m_lexer.currentToken();
m_lexer.next();
lastValue = jsNumber(m_exec, UString(numberToken.start, numberToken.end - numberToken.start).toDouble());
break;
}
default:
// Error
return JSValue();
}
break;
}
case StartParseStatement: {
switch (m_lexer.currentToken().type) {
case TokLBracket:
case TokNumber:
case TokString:
goto startParseExpression;
JSValue LiteralParser::parseObject()
{
StackGuard guard(this);
if (!guard.isSafe())
return abortParse();
JSObject* object = constructEmptyObject(m_exec);
while (m_lexer.next() == TokString) {
Lexer::LiteralParserToken identifierToken = m_lexer.currentToken();
// Check for colon
if (m_lexer.next() != TokColon)
return abortParse();
m_lexer.next();
JSValue value = parseExpression();
if (!value || m_aborted)
return abortParse();
Identifier ident(m_exec, identifierToken.start + 1, identifierToken.end - identifierToken.start - 2);
object->putDirect(ident, value);
if (m_lexer.currentToken().type != TokComma)
break;
case TokLParen: {
m_lexer.next();
stateStack.append(StartParseStatementEndStatement);
goto startParseExpression;
}
default:
return JSValue();
}
}
case StartParseStatementEndStatement: {
ASSERT(stateStack.isEmpty());
if (m_lexer.currentToken().type != TokRParen)
return JSValue();
if (m_lexer.next() == TokEnd)
return lastValue;
return JSValue();
}
default:
ASSERT_NOT_REACHED();
}
if (stateStack.isEmpty())
return lastValue;
state = stateStack.last();
stateStack.removeLast();
continue;
}
if (m_lexer.currentToken().type != TokRBrace)
return abortParse();
m_lexer.next();
return object;
}
}
......@@ -37,21 +37,22 @@ namespace JSC {
LiteralParser(ExecState* exec, const UString& s)
: m_exec(exec)
, m_lexer(s)
, m_depth(0)
, m_aborted(false)
{
}
JSValue tryLiteralParse()
{
m_lexer.next();
JSValue result = parseStatement();
if (m_aborted || m_lexer.currentToken().type != TokEnd)
JSValue result = parse(StartParseStatement);
if (m_lexer.currentToken().type != TokEnd)
return JSValue();
return result;
}
private:
enum ParserState { StartParseObject, StartParseArray, StartParseExpression,
StartParseStatement, StartParseStatementEndStatement,
DoParseObjectStartExpression, DoParseObjectEndExpression,
DoParseArrayStartExpression, DoParseArrayEndExpression };
enum TokenType { TokLBracket, TokRBracket, TokLBrace, TokRBrace,
TokString, TokIdentifier, TokNumber, TokColon,
TokLParen, TokRParen, TokComma, TokEnd, TokError };
......@@ -91,21 +92,10 @@ namespace JSC {
};
class StackGuard;
JSValue parseStatement();
JSValue parseExpression();
JSValue parseArray();
JSValue parseObject();
JSValue abortParse()
{
m_aborted = true;
return JSValue();
}
JSValue parse(ParserState);
ExecState* m_exec;
LiteralParser::Lexer m_lexer;
int m_depth;
bool m_aborted;
};
}
......
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