--- /dev/null
+// 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
// (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 };
// 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--) {
}
-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
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);
-}
allow_natives_syntax_(allow_natives_syntax),
extension_(extension),
pre_data_(pre_data),
- fni_(NULL) {
+ fni_(NULL),
+ stack_overflow_(false) {
}
source->length(),
false,
temp_scope.ContainsLoops());
- } else if (scanner().stack_overflow()) {
+ } else if (stack_overflow_) {
Top::StackOverflow();
}
}
// 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.
// 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));
}
}
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 {
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();
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();
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;
}
// 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);
bool is_pre_parsing_;
ScriptDataImpl* pre_data_;
FuncNameInferrer* fni_;
+ bool stack_overflow_;
};
Handle<String> GetString();
JsonScanner scanner_;
+ bool stack_overflow_;
};
} } // namespace v8::internal
--- /dev/null
+// 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);
+}
// 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.
- if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) {
+ if (token == i::Token::ILLEGAL && stack_overflow_) {
return;
}
i::JavaScriptScanner::Location source_location = scanner_->location();
}
-SourceElements PreParser::ParseSourceElements(int end_token,
- bool* ok) {
+PreParser::SourceElements PreParser::ParseSourceElements(int end_token,
+ bool* ok) {
// SourceElements ::
// (Statement)* <end_token>
}
-Statement PreParser::ParseStatement(bool* ok) {
+PreParser::PreParser::Statement PreParser::ParseStatement(bool* ok) {
// Statement ::
// Block
// VariableStatement
}
-Statement PreParser::ParseFunctionDeclaration(bool* ok) {
+PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
// FunctionDeclaration ::
// 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
Expect(i::Token::FUNCTION, CHECK_OK);
// through the API's extension mechanism. A native function
// declaration is resolved by looking up the function through a
// callback provided by the extension.
-Statement PreParser::ParseNativeDeclaration(bool* ok) {
+PreParser::Statement PreParser::ParseNativeDeclaration(bool* ok) {
Expect(i::Token::NATIVE, CHECK_OK);
Expect(i::Token::FUNCTION, CHECK_OK);
ParseIdentifier(CHECK_OK);
}
-Statement PreParser::ParseBlock(bool* ok) {
+PreParser::Statement PreParser::ParseBlock(bool* ok) {
// Block ::
// '{' Statement* '}'
}
-Statement PreParser::ParseVariableStatement(bool* ok) {
+PreParser::Statement PreParser::ParseVariableStatement(bool* ok) {
// VariableStatement ::
// VariableDeclarations ';'
// *var is untouched; in particular, it is the caller's responsibility
// to initialize it properly. This mechanism is also used for the parsing
// of 'for-in' loops.
-Statement PreParser::ParseVariableDeclarations(bool accept_IN,
- int* num_decl,
- bool* ok) {
+PreParser::Statement PreParser::ParseVariableDeclarations(bool accept_IN,
+ int* num_decl,
+ bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
}
-Statement PreParser::ParseExpressionOrLabelledStatement(
+PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(
bool* ok) {
// ExpressionStatement | LabelledStatement ::
// Expression ';'
}
-Statement PreParser::ParseIfStatement(bool* ok) {
+PreParser::Statement PreParser::ParseIfStatement(bool* ok) {
// IfStatement ::
// 'if' '(' Expression ')' Statement ('else' Statement)?
}
-Statement PreParser::ParseContinueStatement(bool* ok) {
+PreParser::Statement PreParser::ParseContinueStatement(bool* ok) {
// ContinueStatement ::
// 'continue' [no line terminator] Identifier? ';'
}
-Statement PreParser::ParseBreakStatement(bool* ok) {
+PreParser::Statement PreParser::ParseBreakStatement(bool* ok) {
// BreakStatement ::
// 'break' [no line terminator] Identifier? ';'
}
-Statement PreParser::ParseReturnStatement(bool* ok) {
+PreParser::Statement PreParser::ParseReturnStatement(bool* ok) {
// ReturnStatement ::
// 'return' [no line terminator] Expression? ';'
}
-Statement PreParser::ParseWithStatement(bool* ok) {
+PreParser::Statement PreParser::ParseWithStatement(bool* ok) {
// WithStatement ::
// 'with' '(' Expression ')' Statement
Expect(i::Token::WITH, CHECK_OK);
}
-Statement PreParser::ParseSwitchStatement(bool* ok) {
+PreParser::Statement PreParser::ParseSwitchStatement(bool* ok) {
// SwitchStatement ::
// 'switch' '(' Expression ')' '{' CaseClause* '}'
}
-Statement PreParser::ParseDoWhileStatement(bool* ok) {
+PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) {
// DoStatement ::
// 'do' Statement 'while' '(' Expression ')' ';'
}
-Statement PreParser::ParseWhileStatement(bool* ok) {
+PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
// WhileStatement ::
// 'while' '(' Expression ')' Statement
}
-Statement PreParser::ParseForStatement(bool* ok) {
+PreParser::Statement PreParser::ParseForStatement(bool* ok) {
// ForStatement ::
// 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
}
-Statement PreParser::ParseThrowStatement(bool* ok) {
+PreParser::Statement PreParser::ParseThrowStatement(bool* ok) {
// ThrowStatement ::
// 'throw' [no line terminator] Expression ';'
}
-Statement PreParser::ParseTryStatement(bool* ok) {
+PreParser::Statement PreParser::ParseTryStatement(bool* ok) {
// TryStatement ::
// 'try' Block Catch
// 'try' Block Finally
}
-Statement PreParser::ParseDebuggerStatement(bool* ok) {
+PreParser::Statement PreParser::ParseDebuggerStatement(bool* ok) {
// In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
// contexts this is used as a statement which invokes the debugger as if a
// break point is present.
// Precedence = 1
-Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
+PreParser::Expression PreParser::ParseExpression(bool accept_IN, bool* ok) {
// Expression ::
// AssignmentExpression
// Expression ',' AssignmentExpression
// Precedence = 2
-Expression PreParser::ParseAssignmentExpression(bool accept_IN,
- bool* ok) {
+PreParser::Expression PreParser::ParseAssignmentExpression(bool accept_IN,
+ bool* ok) {
// AssignmentExpression ::
// ConditionalExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
// Precedence = 3
-Expression PreParser::ParseConditionalExpression(bool accept_IN,
- bool* ok) {
+PreParser::Expression PreParser::ParseConditionalExpression(bool accept_IN,
+ bool* ok) {
// ConditionalExpression ::
// LogicalOrExpression
// LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression
// Precedence >= 4
-Expression PreParser::ParseBinaryExpression(int prec,
- bool accept_IN,
- bool* ok) {
+PreParser::Expression PreParser::ParseBinaryExpression(int prec,
+ bool accept_IN,
+ bool* ok) {
Expression result = ParseUnaryExpression(CHECK_OK);
for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
// prec1 >= 4
}
-Expression PreParser::ParseUnaryExpression(bool* ok) {
+PreParser::Expression PreParser::ParseUnaryExpression(bool* ok) {
// UnaryExpression ::
// PostfixExpression
// 'delete' UnaryExpression
}
-Expression PreParser::ParsePostfixExpression(bool* ok) {
+PreParser::Expression PreParser::ParsePostfixExpression(bool* ok) {
// PostfixExpression ::
// LeftHandSideExpression ('++' | '--')?
}
-Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
+PreParser::Expression PreParser::ParseLeftHandSideExpression(bool* ok) {
// LeftHandSideExpression ::
// (NewExpression | MemberExpression) ...
}
-Expression PreParser::ParseNewExpression(bool* ok) {
+PreParser::Expression PreParser::ParseNewExpression(bool* ok) {
// NewExpression ::
// ('new')+ MemberExpression
}
-Expression PreParser::ParseMemberExpression(bool* ok) {
+PreParser::Expression PreParser::ParseMemberExpression(bool* ok) {
return ParseMemberWithNewPrefixesExpression(0, ok);
}
-Expression PreParser::ParseMemberWithNewPrefixesExpression(
+PreParser::Expression PreParser::ParseMemberWithNewPrefixesExpression(
unsigned new_count, bool* ok) {
// MemberExpression ::
// (PrimaryExpression | FunctionLiteral)
}
-Expression PreParser::ParsePrimaryExpression(bool* ok) {
+PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
// PrimaryExpression ::
// 'this'
// 'null'
}
-Expression PreParser::ParseArrayLiteral(bool* ok) {
+PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
Expect(i::Token::LBRACK, CHECK_OK);
}
-Expression PreParser::ParseObjectLiteral(bool* ok) {
+PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) {
// ObjectLiteral ::
// '{' (
// ((IdentifierName | String | Number) ':' AssignmentExpression)
}
-Expression PreParser::ParseRegExpLiteral(bool seen_equal,
- bool* ok) {
+PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal,
+ bool* ok) {
if (!scanner_->ScanRegExpPattern(seen_equal)) {
Next();
i::JavaScriptScanner::Location location = scanner_->location();
}
-Arguments PreParser::ParseArguments(bool* ok) {
+PreParser::Arguments PreParser::ParseArguments(bool* ok) {
// Arguments ::
// '(' (AssignmentExpression)*[','] ')'
}
-Expression PreParser::ParseFunctionLiteral(bool* ok) {
+PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) {
// Function ::
// '(' FormalParameterList? ')' '{' FunctionBody '}'
}
-Expression PreParser::ParseV8Intrinsic(bool* ok) {
+PreParser::Expression PreParser::ParseV8Intrinsic(bool* ok) {
// CallRuntime ::
// '%' Identifier Arguments
}
-Identifier PreParser::GetIdentifierSymbol() {
+PreParser::Identifier PreParser::GetIdentifierSymbol() {
const char* literal_chars = scanner_->literal_string();
int literal_length = scanner_->literal_length();
int identifier_pos = scanner_->location().beg_pos;
}
-Expression PreParser::GetStringSymbol() {
+PreParser::Expression PreParser::GetStringSymbol() {
const char* literal_chars = scanner_->literal_string();
int literal_length = scanner_->literal_length();
}
-Identifier PreParser::ParseIdentifier(bool* ok) {
+PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
Expect(i::Token::IDENTIFIER, ok);
if (!*ok) return kUnknownIdentifier;
return GetIdentifierSymbol();
}
-Identifier PreParser::ParseIdentifierName(bool* ok) {
+PreParser::Identifier PreParser::ParseIdentifierName(bool* ok) {
i::Token::Value next = Next();
if (i::Token::IsKeyword(next)) {
int pos = scanner_->location().beg_pos;
// is 'get' or 'set'. The reason for not using ParseIdentifier and
// checking on the output is that this involves heap allocation which
// we can't do during preparsing.
-Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
- bool* is_set,
- bool* ok) {
+PreParser::Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get,
+ bool* is_set,
+ bool* ok) {
Expect(i::Token::IDENTIFIER, CHECK_OK);
if (scanner_->literal_length() == 3) {
const char* token = scanner_->literal_string();
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:
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)
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);
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) {
i::JavaScriptScanner* scanner_;
i::ParserRecorder* log_;
Scope* scope_;
+ uintptr_t stack_limit_;
+ bool stack_overflow_;
bool allow_lazy_;
};
} } // v8::preparser
// ----------------------------------------------------------------------------
// Scanner
-Scanner::Scanner() : source_(NULL), stack_overflow_(false) {}
+Scanner::Scanner() : source_(NULL) {}
uc32 Scanner::ScanHexEscape(uc32 c, int length) {
return Vector<const char>(next_literal_string(), next_literal_length());
}
- bool stack_overflow() { return stack_overflow_; }
-
static const int kCharacterLookaheadBufferSize = 1;
protected:
// 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_;
};
}
-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,
// 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;
}
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,
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());
}
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);
+}