Commit 84f2f5ee authored by lrn@chromium.org's avatar lrn@chromium.org

Preparser extracted into separate files that can be compiled to a library.

No scons target yet.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5899 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d96f46c8
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef PREPARSER_H
#define PREPARSER_H
#include "v8stdint.h"
#ifdef _WIN32
// Setup for Windows DLL export/import. When building the V8 DLL the
// BUILDING_V8_SHARED needs to be defined. When building a program which uses
// the V8 DLL USING_V8_SHARED needs to be defined. When either building the V8
// static library or building a program which uses the V8 static library neither
// BUILDING_V8_SHARED nor USING_V8_SHARED should be defined.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
#endif
#ifdef BUILDING_V8_SHARED
#define V8EXPORT __declspec(dllexport)
#elif USING_V8_SHARED
#define V8EXPORT __declspec(dllimport)
#else
#define V8EXPORT
#endif // BUILDING_V8_SHARED
#else // _WIN32
// Setup for Linux shared library export. There is no need to distinguish
// between building or using the V8 shared library, but we should not
// export symbols when we are building a static library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define V8EXPORT __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define V8EXPORT
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
namespace v8 {
class PreParserData {
public:
PreParserData(size_t size, const uint8_t* data)
: data_(data), size_(size) { }
// Create a PreParserData value where stack_overflow reports true.
static PreParserData StackOverflow() { return PreParserData(NULL, 0); }
// Whether the pre-parser stopped due to a stack overflow.
// If this is the case, size() and data() should not be used.
bool stack_overflow() { return size_ == 0u; }
// The size of the data in bytes.
size_t size() const { return size_; }
// Pointer to the data.
const uint8_t* data() const { return data_; }
private:
const uint8_t* const data_;
const size_t size_;
};
// Interface for a stream of Unicode characters.
class UnicodeInputStream {
public:
virtual ~UnicodeInputStream();
// Returns the next Unicode code-point in the input, or a negative value when
// there is no more input in the stream.
virtual int32_t Next() = 0;
// Pushes a read character back into the stream, so that it will be the next
// to be read by Advance(). The character pushed back must be the most
// recently read character that hasn't already been pushed back (i.e., if
// pushing back more than one character, they must occur in the opposite order
// of the one they were read in).
virtual void PushBack(int32_t ch) = 0;
};
// Preparse a JavaScript program. The source code is provided as a
// UnicodeInputStream. The max_stack_size limits the amount of stack
// space that the preparser is allowed to use. If the preparser uses
// more stack space than the limit provided, the result's stack_overflow()
// method will return true. Otherwise the result contains preparser
// data that can be used by the V8 parser to speed up parsing.
PreParserData V8EXPORT Preparse(UnicodeInputStream* input,
size_t max_stack_size);
} // namespace v8.
#endif // PREPARSER_H
......@@ -25,17 +25,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include <stdarg.h>
#include "../include/v8stdint.h"
#include "globals.h"
#include "checks.h"
#include "allocation.h"
#include "utils.h"
#include "list.h"
#include "smart-pointer.h"
#include "scanner-base.h"
#include "preparse-data.h"
#include "preparser.h"
#include "../include/v8-preparser.h"
#include "unicode-inl.h"
enum ResultCode { kSuccess = 0, kErrorReading = 1, kErrorWriting = 2 };
......@@ -45,78 +39,66 @@ namespace internal {
// THIS FILE IS PROOF-OF-CONCEPT ONLY.
// The final goal is a stand-alone preparser library.
// UTF16Buffer based on an UTF-8 string in memory.
class UTF8UTF16Buffer : public UTF16Buffer {
class UTF8InputStream : public v8::UnicodeInputStream {
public:
UTF8UTF16Buffer(uint8_t* buffer, size_t length)
: UTF16Buffer(),
buffer_(buffer),
UTF8InputStream(uint8_t* buffer, size_t length)
: buffer_(buffer),
offset_(0),
pos_(0),
end_offset_(static_cast<int>(length)) { }
virtual void PushBack(uc32 ch) {
virtual ~UTF8InputStream() { }
virtual void PushBack(int32_t ch) {
// Pushback assumes that the character pushed back is the
// one that was most recently read, and jumps back in the
// UTF-8 stream by the length of that character's encoding.
offset_ -= unibrow::Utf8::Length(ch);
pos_--;
#ifdef DEBUG
int tmp = 0;
ASSERT_EQ(ch, unibrow::Utf8::ValueOf(buffer_ + offset_,
end_offset_ - offset_,
&tmp);
if (static_cast<unsigned>(ch) <= unibrow::Utf8::kMaxOneByteChar) {
if (ch != buffer_[offset_]) {
fprintf(stderr, "Invalid pushback: '%c'.", ch);
exit(1);
}
} else {
unsigned tmp = 0;
if (static_cast<unibrow::uchar>(ch) !=
unibrow::Utf8::CalculateValue(buffer_ + offset_,
end_offset_ - offset_,
&tmp)) {
fprintf(stderr, "Invalid pushback: 0x%x.", ch);
exit(1);
}
}
#endif
}
virtual uc32 Advance() {
virtual int32_t Next() {
if (offset_ == end_offset_) return -1;
uint8_t first_char = buffer_[offset_];
if (first_char <= unibrow::Utf8::kMaxOneByteChar) {
pos_++;
offset_++;
return static_cast<uc32>(first_char);
return static_cast<int32_t>(first_char);
}
unibrow::uchar codepoint =
unibrow::Utf8::CalculateValue(buffer_ + offset_,
end_offset_ - offset_,
&offset_);
pos_++;
return static_cast<uc32>(codepoint);
}
virtual void SeekForward(int pos) {
while (pos_ < pos) {
uint8_t first_byte = buffer_[offset_++];
while (first_byte & 0x80u && offset_ < end_offset_) {
offset_++;
first_byte <<= 1;
}
pos_++;
}
return static_cast<int32_t>(codepoint);
}
private:
const uint8_t* buffer_;
unsigned offset_;
unsigned pos_;
unsigned end_offset_;
};
class StandAloneJavaScriptScanner : public JavaScriptScanner {
public:
void Initialize(UTF16Buffer* source) {
source_ = source;
literal_flags_ = kLiteralString | kLiteralIdentifier;
Init();
// Skip initial whitespace allowing HTML comment ends just like
// after a newline and scan first token.
has_line_terminator_before_next_ = true;
SkipWhiteSpace();
Scan();
}
};
// Write a number to dest in network byte order.
void WriteUInt32(FILE* dest, uint32_t value, bool* ok) {
for (int i = 3; i >= 0; i--) {
......@@ -150,56 +132,55 @@ bool ReadBuffer(FILE* source, void* buffer, size_t length) {
}
bool WriteBuffer(FILE* dest, void* buffer, size_t length) {
bool WriteBuffer(FILE* dest, const void* buffer, size_t length) {
size_t actually_written = fwrite(buffer, 1, length, dest);
return (actually_written == length);
}
template <typename T>
class ScopedPointer {
public:
explicit ScopedPointer(T* pointer) : pointer_(pointer) {}
~ScopedPointer() { delete[] pointer_; }
T& operator[](int index) { return pointer_[index]; }
T* operator*() { return pointer_ ;}
private:
T* pointer_;
};
// Preparse stdin and output result on stdout.
int PreParseIO() {
fprintf(stderr, "LOG: Enter parsing loop\n");
bool ok = true;
uint32_t length = ReadUInt32(stdin, &ok);
if (!ok) return kErrorReading;
SmartPointer<byte> buffer(NewArray<byte>(length));
ScopedPointer<uint8_t> buffer(new uint8_t[length]);
if (!ReadBuffer(stdin, *buffer, length)) {
return kErrorReading;
}
UTF8UTF16Buffer input_buffer(*buffer, static_cast<size_t>(length));
StandAloneJavaScriptScanner scanner;
scanner.Initialize(&input_buffer);
CompleteParserRecorder recorder;
preparser::PreParser preparser;
if (!preparser.PreParseProgram(&scanner, &recorder, true)) {
if (scanner.stack_overflow()) {
// Report stack overflow error/no-preparser-data.
WriteUInt32(stdout, 0, &ok);
if (!ok) return kErrorWriting;
return 0;
}
UTF8InputStream input_buffer(*buffer, static_cast<size_t>(length));
v8::PreParserData data =
v8::Preparse(&input_buffer, 64 * sizeof(void*)); // NOLINT
if (data.stack_overflow()) {
// Report stack overflow error/no-preparser-data.
WriteUInt32(stdout, 0, &ok);
if (!ok) return kErrorWriting;
return 0;
}
Vector<unsigned> pre_data = recorder.ExtractData();
uint32_t size = static_cast<uint32_t>(pre_data.length() * sizeof(uint32_t));
uint32_t size = data.size();
WriteUInt32(stdout, size, &ok);
if (!ok) return kErrorWriting;
if (!WriteBuffer(stdout,
reinterpret_cast<byte*>(pre_data.start()),
size)) {
if (!WriteBuffer(stdout, data.data(), size)) {
return kErrorWriting;
}
return 0;
}
// Functions declared by allocation.h
void FatalProcessOutOfMemory(const char* location) {
V8_Fatal("", 0, location);
}
bool EnableSlowAsserts() { return true; }
} } // namespace v8::internal
......@@ -211,17 +192,3 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "EXIT: Failure %d\n", status);
return EXIT_FAILURE;
}
// Fatal error handling declared by checks.h.
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...) {
fflush(stdout);
fflush(stderr);
va_list arguments;
va_start(arguments, format);
vfprintf(stderr, format, arguments);
va_end(arguments);
fputs("\n#\n\n", stderr);
exit(EXIT_FAILURE);
}
......@@ -593,7 +593,8 @@ Parser::Parser(Handle<Script> script,
allow_natives_syntax_(allow_natives_syntax),
extension_(extension),
pre_data_(pre_data),
fni_(NULL) {
fni_(NULL),
stack_overflow_(false) {
}
......@@ -643,7 +644,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
source->length(),
false,
temp_scope.ContainsLoops());
} else if (scanner().stack_overflow()) {
} else if (stack_overflow_) {
Top::StackOverflow();
}
}
......@@ -693,7 +694,7 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) {
// Make sure the results agree.
ASSERT(ok == (result != NULL));
// The only errors should be stack overflows.
ASSERT(ok || scanner_.stack_overflow());
ASSERT(ok || stack_overflow_);
}
// Make sure the target stack is empty.
......@@ -2590,25 +2591,24 @@ void Parser::ReportUnexpectedToken(Token::Value token) {
// We don't report stack overflows here, to avoid increasing the
// stack depth even further. Instead we report it after parsing is
// over, in ParseProgram/ParseJson.
if (token == Token::ILLEGAL && scanner().stack_overflow())
return;
if (token == Token::ILLEGAL && stack_overflow_) return;
// Four of the tokens are treated specially
switch (token) {
case Token::EOS:
return ReportMessage("unexpected_eos", Vector<const char*>::empty());
case Token::NUMBER:
return ReportMessage("unexpected_token_number",
Vector<const char*>::empty());
case Token::STRING:
return ReportMessage("unexpected_token_string",
Vector<const char*>::empty());
case Token::IDENTIFIER:
return ReportMessage("unexpected_token_identifier",
Vector<const char*>::empty());
default:
const char* name = Token::String(token);
ASSERT(name != NULL);
ReportMessage("unexpected_token", Vector<const char*>(&name, 1));
case Token::EOS:
return ReportMessage("unexpected_eos", Vector<const char*>::empty());
case Token::NUMBER:
return ReportMessage("unexpected_token_number",
Vector<const char*>::empty());
case Token::STRING:
return ReportMessage("unexpected_token_string",
Vector<const char*>::empty());
case Token::IDENTIFIER:
return ReportMessage("unexpected_token_identifier",
Vector<const char*>::empty());
default:
const char* name = Token::String(token);
ASSERT(name != NULL);
ReportMessage("unexpected_token", Vector<const char*>(&name, 1));
}
}
......@@ -3498,9 +3498,10 @@ Expression* Parser::NewThrowError(Handle<String> constructor,
Handle<Object> JsonParser::ParseJson(Handle<String> source) {
source->TryFlatten();
scanner_.Initialize(source);
stack_overflow_ = false;
Handle<Object> result = ParseJsonValue();
if (result.is_null() || scanner_.Next() != Token::EOS) {
if (scanner_.stack_overflow()) {
if (stack_overflow_) {
// Scanner failed.
Top::StackOverflow();
} else {
......@@ -3598,6 +3599,10 @@ Handle<Object> JsonParser::ParseJsonObject() {
if (scanner_.peek() == Token::RBRACE) {
scanner_.Next();
} else {
if (StackLimitCheck().HasOverflowed()) {
stack_overflow_ = true;
return Handle<Object>::null();
}
do {
if (scanner_.Next() != Token::STRING) {
return ReportUnexpectedToken();
......@@ -3632,6 +3637,10 @@ Handle<Object> JsonParser::ParseJsonArray() {
if (token == Token::RBRACK) {
scanner_.Next();
} else {
if (StackLimitCheck().HasOverflowed()) {
stack_overflow_ = true;
return Handle<Object>::null();
}
do {
Handle<Object> element = ParseJsonValue();
if (element.is_null()) return Handle<Object>::null();
......@@ -4531,8 +4540,11 @@ static ScriptDataImpl* DoPreParse(Handle<String> source,
int literal_flags) {
V8JavaScriptScanner scanner;
scanner.Initialize(source, stream, literal_flags);
preparser::PreParser preparser;
if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) {
intptr_t stack_limit = StackGuard::real_climit();
if (!preparser::PreParser::PreParseProgram(&scanner,
recorder,
allow_lazy,
stack_limit)) {
Top::StackOverflow();
return NULL;
}
......
......@@ -546,8 +546,25 @@ class Parser {
// Magical syntax support.
Expression* ParseV8Intrinsic(bool* ok);
INLINE(Token::Value peek()) { return scanner_.peek(); }
INLINE(Token::Value Next()) { return scanner_.NextCheckStack(); }
INLINE(Token::Value peek()) {
if (stack_overflow_) return Token::ILLEGAL;
return scanner_.peek();
}
INLINE(Token::Value Next()) {
// BUG 1215673: Find a thread safe way to set a stack limit in
// pre-parse mode. Otherwise, we cannot safely pre-parse from other
// threads.
if (stack_overflow_) {
return Token::ILLEGAL;
}
if (StackLimitCheck().HasOverflowed()) {
// Any further calls to Next or peek will return the illegal token.
stack_overflow_ = true;
}
return scanner_.Next();
}
INLINE(void Consume(Token::Value token));
void Expect(Token::Value token, bool* ok);
bool Check(Token::Value token);
......@@ -639,6 +656,7 @@ class Parser {
bool is_pre_parsing_;
ScriptDataImpl* pre_data_;
FuncNameInferrer* fni_;
bool stack_overflow_;
};
......@@ -718,6 +736,7 @@ class JsonParser BASE_EMBEDDED {
Handle<String> GetString();
JsonScanner scanner_;
bool stack_overflow_;
};
} } // namespace v8::internal
......
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/v8-preparser.h"
#include "globals.h"
#include "checks.h"
#include "allocation.h"
#include "utils.h"
#include "list.h"
#include "scanner-base.h"
#include "preparse-data.h"
#include "preparser.h"
namespace v8 {
namespace internal {
// UTF16Buffer based on a v8::UnicodeInputStream.
class InputStreamUTF16Buffer : public UTF16Buffer {
public:
explicit InputStreamUTF16Buffer(UnicodeInputStream* stream)
: UTF16Buffer(),
stream_(stream) { }
virtual ~InputStreamUTF16Buffer() { }
virtual void PushBack(uc32 ch) {
stream_->PushBack(ch);
pos_--;
}
virtual uc32 Advance() {
uc32 result = stream_->Next();
if (result >= 0) pos_++;
return result;
}
virtual void SeekForward(int pos) {
// Seeking in the input is not used by preparsing.
// It's only used by the real parser based on preparser data.
UNIMPLEMENTED();
}
private:
v8::UnicodeInputStream* const stream_;
};
class StandAloneJavaScriptScanner : public JavaScriptScanner {
public:
void Initialize(UTF16Buffer* source) {
source_ = source;
literal_flags_ = kLiteralString | kLiteralIdentifier;
Init();
// Skip initial whitespace allowing HTML comment ends just like
// after a newline and scan first token.
has_line_terminator_before_next_ = true;
SkipWhiteSpace();
Scan();
}
};
// Functions declared by allocation.h
void FatalProcessOutOfMemory(const char* reason) {
V8_Fatal(__FILE__, __LINE__, reason);
}
bool EnableSlowAsserts() { return true; }
} // namespace internal.
UnicodeInputStream::~UnicodeInputStream() { }
PreParserData Preparse(UnicodeInputStream* input, size_t max_stack) {
internal::InputStreamUTF16Buffer buffer(input);
uintptr_t stack_limit = reinterpret_cast<uintptr_t>(&buffer) - max_stack;
internal::StandAloneJavaScriptScanner scanner;
scanner.Initialize(&buffer);
internal::CompleteParserRecorder recorder;
preparser::PreParser::PreParseResult result =
preparser::PreParser::PreParseProgram(&scanner,
&recorder,
true,
stack_limit);
if (result == preparser::PreParser::kPreParseStackOverflow) {
return PreParserData::StackOverflow();
}
internal::Vector<unsigned> pre_data = recorder.ExtractData();
size_t size = pre_data.length() * sizeof(pre_data[0]);
unsigned char* data = reinterpret_cast<unsigned char*>(pre_data.start());
return PreParserData(size, data);
}
} // namespace v8.
// Used by ASSERT macros and other immediate exits.
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...) {
exit(EXIT_FAILURE);
}
This diff is collapsed.
......@@ -46,56 +46,24 @@ namespace preparser {
namespace i = v8::internal;
enum StatementType {
kUnknownStatement
};
enum ExpressionType {
kUnknownExpression,
kIdentifierExpression, // Used to detect labels.
kThisExpression,
kThisPropertyExpression
};
enum IdentifierType {
kUnknownIdentifier
};
enum SourceElementTypes {
kUnknownSourceElements
};
typedef int SourceElements;
typedef int Expression;
typedef int Statement;
typedef int Identifier;
typedef int Arguments;
class PreParser {
public:
PreParser() : scope_(NULL), allow_lazy_(true) { }
enum PreParseResult {
kPreParseStackOverflow,
kPreParseSuccess
};
~PreParser() { }
// Pre-parse the program from the character stream; returns true on
// success (even if parsing failed, the pre-parse data successfully
// captured the syntax error), and false if a stack-overflow happened
// during parsing.
bool PreParseProgram(i::JavaScriptScanner* scanner,
i::ParserRecorder* log,
bool allow_lazy) {
allow_lazy_ = allow_lazy;
scanner_ = scanner;
log_ = log;
Scope top_scope(&scope_, kTopLevelScope);
bool ok = true;
ParseSourceElements(i::Token::EOS, &ok);
bool stack_overflow = scanner_->stack_overflow();
if (!ok && !stack_overflow) {
ReportUnexpectedToken(scanner_->current_token());
}
return !stack_overflow;
static PreParseResult PreParseProgram(i::JavaScriptScanner* scanner,
i::ParserRecorder* log,
bool allow_lazy,
uintptr_t stack_limit) {
return PreParser(scanner, log, stack_limit, allow_lazy).PreParse();
}
private:
......@@ -104,6 +72,38 @@ class PreParser {
kFunctionScope
};
// Types that allow us to recognize simple this-property assignments.
// A simple this-property assignment is a statement on the form
// "this.propertyName = {primitive constant or function parameter name);"
// where propertyName isn't "__proto__".
// The result is only relevant if the function body contains only
// simple this-property assignments.
enum StatementType {
kUnknownStatement
};
enum ExpressionType {
kUnknownExpression,
kIdentifierExpression, // Used to detect labels.
kThisExpression,
kThisPropertyExpression
};
enum IdentifierType {
kUnknownIdentifier
};
enum SourceElementTypes {
kUnknownSourceElements
};
typedef int SourceElements;
typedef int Expression;
typedef int Statement;
typedef int Identifier;
typedef int Arguments;
class Scope {
public:
Scope(Scope** variable, ScopeType type)
......@@ -134,12 +134,30 @@ class PreParser {
int with_nesting_count_;
};
// Types that allow us to recognize simple this-property assignments.
// A simple this-property assignment is a statement on the form
// "this.propertyName = {primitive constant or function parameter name);"
// where propertyName isn't "__proto__".
// The result is only relevant if the function body contains only
// simple this-property assignments.
// Private constructor only used in PreParseProgram.
PreParser(i::JavaScriptScanner* scanner,
i::ParserRecorder* log,
uintptr_t stack_limit,
bool allow_lazy)
: scanner_(scanner),
log_(log),
scope_(NULL),
stack_limit_(stack_limit),
stack_overflow_(false),
allow_lazy_(true) { }
// Preparse the program. Only called in PreParseProgram after creating
// the instance.
PreParseResult PreParse() {
Scope top_scope(&scope_, kTopLevelScope);
bool ok = true;
ParseSourceElements(i::Token::EOS, &ok);
if (stack_overflow_) return kPreParseStackOverflow;
if (!ok) {
ReportUnexpectedToken(scanner_->current_token());
}
return kPreParseSuccess;
}
// Report syntax error
void ReportUnexpectedToken(i::Token::Value token);
......@@ -202,16 +220,26 @@ class PreParser {
unsigned int HexDigitValue(char digit);
Expression GetStringSymbol();
i::Token::Value peek() {
if (stack_overflow_) return i::Token::ILLEGAL;
return scanner_->peek();
}
i::Token::Value peek() { return scanner_->peek(); }
i::Token::Value Next() {
i::Token::Value next = scanner_->Next();
return next;
if (stack_overflow_) return i::Token::ILLEGAL;
{
int marker;
if (reinterpret_cast<uintptr_t>(&marker) < stack_limit_) {
// Further calls to peek/Next will return illegal token.
// The current one will still be returned. It might already
// have been seen using peek.
stack_overflow_ = true;
}
}
return scanner_->Next();
}
void Consume(i::Token::Value token) {
Next();
}
void Consume(i::Token::Value token) { Next(); }
void Expect(i::Token::Value token, bool* ok) {
if (Next() != token) {
......@@ -234,6 +262,8 @@ class PreParser {
i::JavaScriptScanner* scanner_;
i::ParserRecorder* log_;
Scope* scope_;
uintptr_t stack_limit_;
bool stack_overflow_;
bool allow_lazy_;
};
} } // v8::preparser
......
......@@ -92,7 +92,7 @@ bool ScannerConstants::IsIdentifier(unibrow::CharacterStream* buffer) {
// ----------------------------------------------------------------------------
// Scanner
Scanner::Scanner() : source_(NULL), stack_overflow_(false) {}
Scanner::Scanner() : source_(NULL) {}
uc32 Scanner::ScanHexEscape(uc32 c, int length) {
......
......@@ -228,8 +228,6 @@ class Scanner {
return Vector<const char>(next_literal_string(), next_literal_length());
}
bool stack_overflow() { return stack_overflow_; }
static const int kCharacterLookaheadBufferSize = 1;
protected:
......@@ -316,8 +314,6 @@ class Scanner {
// using '\x00'-terminated UTF-8 encoding. Handles allocation internally.
LiteralCollector literal_buffer_;
bool stack_overflow_;
// One Unicode character look-ahead; c0_ < 0 at the end of the input.
uc32 c0_;
};
......
......@@ -164,22 +164,6 @@ void V8JavaScriptScanner::Initialize(Handle<String> source,
}
Token::Value V8JavaScriptScanner::NextCheckStack() {
// BUG 1215673: Find a thread safe way to set a stack limit in
// pre-parse mode. Otherwise, we cannot safely pre-parse from other
// threads.
StackLimitCheck check;
if (check.HasOverflowed()) {
stack_overflow_ = true;
current_ = next_;
next_.token = Token::ILLEGAL;
return current_.token;
} else {
return Next();
}
}
UTF16Buffer* StreamInitializer::Init(Handle<String> source,
unibrow::CharacterStream* stream,
int start_position,
......@@ -236,13 +220,7 @@ Token::Value JsonScanner::Next() {
// threads.
current_ = next_;
// Check for stack-overflow before returning any tokens.
StackLimitCheck check;
if (check.HasOverflowed()) {
stack_overflow_ = true;
next_.token = Token::ILLEGAL;
} else {
ScanJson();
}
ScanJson();
return current_.token;
}
......
......@@ -105,8 +105,6 @@ class V8JavaScriptScanner : public JavaScriptScanner {
public:
V8JavaScriptScanner() {}
Token::Value NextCheckStack();
// Initialize the Scanner to scan source.
void Initialize(Handle<String> source, int literal_flags = kAllLiterals);
void Initialize(Handle<String> source,
......
......@@ -257,15 +257,20 @@ TEST(StandAlonePreParser) {
NULL
};
uintptr_t stack_limit = i::StackGuard::real_climit();
for (int i = 0; programs[i]; i++) {
const char* program = programs[i];
unibrow::Utf8InputBuffer<256> stream(program, strlen(program));
i::CompleteParserRecorder log;
i::V8JavaScriptScanner scanner;
scanner.Initialize(i::Handle<i::String>::null(), &stream);
v8::preparser::PreParser preparser;
bool result = preparser.PreParseProgram(&scanner, &log, true);
CHECK(result);
v8::preparser::PreParser::PreParseResult result =
v8::preparser::PreParser::PreParseProgram(&scanner,
&log,
true,
stack_limit);
CHECK_EQ(v8::preparser::PreParser::kPreParseSuccess, result);
i::ScriptDataImpl data(log.ExtractData());
CHECK(!data.has_error());
}
......@@ -327,3 +332,31 @@ TEST(Regress928) {
CHECK_EQ('}', program[entry2.end_pos() - 1]);
delete data;
}
TEST(PreParseOverflow) {
int marker;
i::StackGuard::SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
size_t kProgramSize = 1024 * 1024;
i::SmartPointer<char> program(
reinterpret_cast<char*>(malloc(kProgramSize + 1)));
memset(*program, '(', kProgramSize);
program[kProgramSize] = '\0';
uintptr_t stack_limit = i::StackGuard::real_climit();
unibrow::Utf8InputBuffer<256> stream(*program, strlen(*program));
i::CompleteParserRecorder log;
i::V8JavaScriptScanner scanner;
scanner.Initialize(i::Handle<i::String>::null(), &stream);
v8::preparser::PreParser::PreParseResult result =
v8::preparser::PreParser::PreParseProgram(&scanner,
&log,
true,
stack_limit);
CHECK_EQ(v8::preparser::PreParser::kPreParseStackOverflow, result);
}
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