Commit 7d3711ef authored by lrn@chromium.org's avatar lrn@chromium.org

Avoid (some) symbol lookups at parse time if preparse data is available.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5421 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent e54ad9ee
......@@ -1136,13 +1136,18 @@ ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
ScriptData* ScriptData::New(const char* data, int length) {
// Return an empty ScriptData if the length is obviously invalid.
if (length % sizeof(unsigned) != 0) {
return new i::ScriptDataImpl(i::Vector<unsigned>());
return new i::ScriptDataImpl();
}
// Copy the data to ensure it is properly aligned.
int deserialized_data_length = length / sizeof(unsigned);
// If aligned, don't create a copy of the data.
if (reinterpret_cast<intptr_t>(data) % sizeof(unsigned) == 0) {
return new i::ScriptDataImpl(data, length);
}
// Copy the data to align it.
unsigned* deserialized_data = i::NewArray<unsigned>(deserialized_data_length);
memcpy(deserialized_data, data, length);
i::MemCopy(deserialized_data, data, length);
return new i::ScriptDataImpl(
i::Vector<unsigned>(deserialized_data, deserialized_data_length));
......
This diff is collapsed.
......@@ -72,14 +72,25 @@ class FunctionEntry BASE_EMBEDDED {
backing_[kPropertyCountOffset] = value;
}
int predata_skip() { return backing_[kPredataSkipOffset]; }
void set_predata_skip(int value) {
backing_[kPredataSkipOffset] = value;
int predata_function_skip() { return backing_[kPredataFunctionSkipOffset]; }
void set_predata_function_skip(int value) {
backing_[kPredataFunctionSkipOffset] = value;
}
int predata_symbol_skip() { return backing_[kPredataSymbolSkipOffset]; }
void set_predata_symbol_skip(int value) {
backing_[kPredataSymbolSkipOffset] = value;
}
int symbol_id_skip() { return backing_[kSymbolIdSkipOffset]; }
void set_symbol_id_skip(int value) {
backing_[kSymbolIdSkipOffset] = value;
}
bool is_valid() { return backing_.length() > 0; }
static const int kSize = 5;
static const int kSize = 7;
private:
Vector<unsigned> backing_;
......@@ -87,7 +98,9 @@ class FunctionEntry BASE_EMBEDDED {
static const int kEndPosOffset = 1;
static const int kLiteralCountOffset = 2;
static const int kPropertyCountOffset = 3;
static const int kPredataSkipOffset = 4;
static const int kPredataFunctionSkipOffset = 4;
static const int kPredataSymbolSkipOffset = 5;
static const int kSymbolIdSkipOffset = 6;
};
......@@ -95,12 +108,30 @@ class ScriptDataImpl : public ScriptData {
public:
explicit ScriptDataImpl(Vector<unsigned> store)
: store_(store),
index_(kHeaderSize) { }
function_index_(kHeaderSize),
symbol_id_(0),
owns_store_(true) {
Initialize();
}
void Initialize() {
if (store_.length() >= kHeaderSize) {
// Otherwise we won't satisfy the SanityCheck.
symbol_index_ = kHeaderSize + store_[kFunctionsSizeOffset];
}
}
// Create an empty ScriptDataImpl that is guaranteed to not satisfy
// a SanityCheck.
ScriptDataImpl() : store_(Vector<unsigned>()), owns_store_(false) { }
virtual ~ScriptDataImpl();
virtual int Length();
virtual const char* Data();
virtual bool HasError();
FunctionEntry GetFunctionEntry(int start);
int GetSymbolIdentifier(int start);
void SkipFunctionEntry(int start);
bool SanityCheck();
......@@ -108,36 +139,67 @@ class ScriptDataImpl : public ScriptData {
const char* BuildMessage();
Vector<const char*> BuildArgs();
int symbol_count() {
return (store_.length() > kHeaderSize) ? store_[kSymbolCountOffset] : 0;
}
// The following functions should only be called if SanityCheck has
// returned true.
bool has_error() { return store_[kHasErrorOffset]; }
unsigned magic() { return store_[kMagicOffset]; }
unsigned version() { return store_[kVersionOffset]; }
// Skip forward in the preparser data by the given number
// of unsigned ints.
virtual void Skip(int entries) {
ASSERT(entries >= 0);
ASSERT(entries <= store_.length() - index_);
index_ += entries;
virtual void Skip(int function_entries, int symbol_entries, int symbol_ids) {
ASSERT(function_entries >= 0);
ASSERT(function_entries
<= (static_cast<int>(store_[kFunctionsSizeOffset])
- (function_index_ - kHeaderSize)));
function_index_ += function_entries;
symbol_index_ += symbol_entries;
symbol_id_ += symbol_ids;
}
static const unsigned kMagicNumber = 0xBadDead;
static const unsigned kCurrentVersion = 1;
static const unsigned kCurrentVersion = 2;
static const int kMagicOffset = 0;
static const int kVersionOffset = 1;
static const int kHasErrorOffset = 2;
static const int kSizeOffset = 3;
static const int kHeaderSize = 4;
static const int kFunctionsSizeOffset = 3;
static const int kSymbolCountOffset = 4;
static const int kSizeOffset = 5;
static const int kHeaderSize = 6;
static const int kMessageStartPos = 0;
static const int kMessageEndPos = 1;
static const int kMessageArgCountPos = 2;
static const int kMessageTextPos = 3;
private:
Vector<unsigned> store_;
int index_;
int function_index_;
int symbol_index_;
int symbol_id_;
bool owns_store_;
unsigned Read(int position);
unsigned* ReadAddress(int position);
void FindStart(int position);
ScriptDataImpl(const char* backing_store, int length)
: store_(reinterpret_cast<unsigned*>(const_cast<char*>(backing_store)),
length / sizeof(unsigned)),
function_index_(kHeaderSize),
symbol_id_(0),
owns_store_(false) {
ASSERT_EQ(0, reinterpret_cast<intptr_t>(backing_store) % sizeof(unsigned));
Initialize();
}
// Read strings written by ParserRecorder::WriteString.
static const char* ReadString(unsigned* start, int* chars);
friend class ScriptData;
};
......
......@@ -98,6 +98,8 @@ namespace internal {
SC(total_parse_size, V8.TotalParseSize) \
/* Amount of source code skipped over using preparsing. */ \
SC(total_preparse_skipped, V8.TotalPreparseSkipped) \
/* Number of symbol lookups skipped using preparsing */ \
SC(total_preparse_symbols_skipped, V8.TotalPreparseSymbolSkipped) \
/* Amount of compiled source code. */ \
SC(total_compile_size, V8.TotalCompileSize) \
/* Amount of source code compiled with the old codegen. */ \
......
......@@ -37,6 +37,7 @@
#include "top.h"
#include "utils.h"
#include "cctest.h"
#include "parser.h"
static const bool kLogThreading = true;
......@@ -8624,15 +8625,12 @@ TEST(PreCompileInvalidPreparseDataError) {
v8::ScriptData::PreCompile(script, i::StrLength(script));
CHECK(!sd->HasError());
// ScriptDataImpl private implementation details
const int kUnsignedSize = sizeof(unsigned);
const int kHeaderSize = 4;
const int kFunctionEntrySize = 5;
const int kHeaderSize = i::ScriptDataImpl::kHeaderSize;
const int kFunctionEntrySize = i::FunctionEntry::kSize;
const int kFunctionEntryStartOffset = 0;
const int kFunctionEntryEndOffset = 1;
unsigned* sd_data =
reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
CHECK_EQ(sd->Length(),
(kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize);
// Overwrite function bar's end position with 0.
sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
......
......@@ -31,6 +31,7 @@
#include "token.h"
#include "scanner.h"
#include "parser.h"
#include "utils.h"
#include "execution.h"
......@@ -148,7 +149,7 @@ TEST(ScanHTMLEndComments) {
NULL
};
// Parser needs a stack limit.
// Parser/Scanner needs a stack limit.
int marker;
i::StackGuard::SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
......@@ -160,3 +161,81 @@ TEST(ScanHTMLEndComments) {
delete data;
}
}
class ScriptResource : public v8::String::ExternalAsciiStringResource {
public:
ScriptResource(const char* data, size_t length)
: data_(data), length_(length) { }
const char* data() const { return data_; }
size_t length() const { return length_; }
private:
const char* data_;
size_t length_;
};
TEST(Preparsing) {
v8::HandleScope handles;
v8::Persistent<v8::Context> context = v8::Context::New();
v8::Context::Scope context_scope(context);
int marker;
i::StackGuard::SetStackLimit(
reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
// Source containing functions that might be lazily compiled and all types
// of symbols (string, propertyName, regexp).
const char* source =
"var x = 42;"
"function foo(a) { return function nolazy(b) { return a + b; } }"
"function bar(a) { if (a) return function lazy(b) { return b; } }"
"var z = {'string': 'string literal', bareword: 'propertyName', "
" 42: 'number literal', for: 'keyword as propertyName', "
" f\\u006fr: 'keyword propertyname with escape'};"
"var v = /RegExp Literal/;"
"var w = /RegExp Literal\\u0020With Escape/gin;"
"var y = { get getter() { return 42; }, "
" set setter(v) { this.value = v; }};";
int source_length = strlen(source);
const char* error_source = "var x = y z;";
int error_source_length = strlen(error_source);
v8::ScriptData* preparse =
v8::ScriptData::PreCompile(source, source_length);
CHECK(!preparse->HasError());
bool lazy_flag = i::FLAG_lazy;
{
i::FLAG_lazy = true;
ScriptResource* resource = new ScriptResource(source, source_length);
v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
v8::Script::Compile(script_source, NULL, preparse);
}
{
i::FLAG_lazy = false;
ScriptResource* resource = new ScriptResource(source, source_length);
v8::Local<v8::String> script_source = v8::String::NewExternal(resource);
v8::Script::New(script_source, NULL, preparse, v8::Local<v8::String>());
}
delete preparse;
i::FLAG_lazy = lazy_flag;
// Syntax error.
v8::ScriptData* error_preparse =
v8::ScriptData::PreCompile(error_source, error_source_length);
CHECK(error_preparse->HasError());
i::ScriptDataImpl *pre_impl =
reinterpret_cast<i::ScriptDataImpl*>(error_preparse);
i::Scanner::Location error_location =
pre_impl->MessageLocation();
// Error is at "z" in source, location 10..11.
CHECK_EQ(10, error_location.beg_pos);
CHECK_EQ(11, error_location.end_pos);
// Should not crash.
const char* message = pre_impl->BuildMessage();
i::Vector<const char*> args = pre_impl->BuildArgs();
CHECK_GT(strlen(message), 0);
}
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