Refactor ScriptData class for cached compile data.

R=marja@chromium.org, vogelheim@chromium.org

Review URL: https://codereview.chromium.org/376223002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22314 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
yangguo@chromium.org 2014-07-10 10:28:05 +00:00
parent 500b76d5be
commit 339bc81390
17 changed files with 369 additions and 581 deletions

View File

@ -1087,6 +1087,12 @@ class V8_EXPORT ScriptCompiler {
/**
* Compiles the specified script (context-independent).
* Cached data as part of the source object can be optionally produced to be
* consumed later to speed up compilation of identical source scripts.
*
* Note that when producing cached data, the source must point to NULL for
* cached data. When consuming cached data, the cached data must have been
* produced by the same version of V8.
*
* \param source Script source code.
* \return Compiled script object (context independent; for running it must be

View File

@ -1699,43 +1699,19 @@ Local<UnboundScript> ScriptCompiler::CompileUnbound(
Isolate* v8_isolate,
Source* source,
CompileOptions options) {
i::ScriptData* script_data_impl = NULL;
i::ScriptData* script_data = NULL;
i::CachedDataMode cached_data_mode = i::NO_CACHED_DATA;
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ON_BAILOUT(isolate, "v8::ScriptCompiler::CompileUnbound()",
return Local<UnboundScript>());
if (options & kProduceDataToCache) {
cached_data_mode = i::PRODUCE_CACHED_DATA;
ASSERT(source->cached_data == NULL);
if (source->cached_data) {
// Asked to produce cached data even though there is some already -> not
// good. Fail the compilation.
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> result = isolate->factory()->NewSyntaxError(
"invalid_cached_data", isolate->factory()->NewJSArray(0));
isolate->Throw(*result);
isolate->ReportPendingMessages();
has_pending_exception = true;
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
}
CHECK(source->cached_data == NULL);
} else if (source->cached_data) {
cached_data_mode = i::CONSUME_CACHED_DATA;
// ScriptData takes care of aligning, in case the data is not aligned
// correctly.
script_data_impl = i::ScriptData::New(
reinterpret_cast<const char*>(source->cached_data->data),
// ScriptData takes care of pointer-aligning the data.
script_data = new i::ScriptData(source->cached_data->data,
source->cached_data->length);
// If the cached data is not valid, fail the compilation.
if (script_data_impl == NULL || !script_data_impl->SanityCheck()) {
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::Object> result = isolate->factory()->NewSyntaxError(
"invalid_cached_data", isolate->factory()->NewJSArray(0));
isolate->Throw(*result);
isolate->ReportPendingMessages();
delete script_data_impl;
has_pending_exception = true;
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
}
}
i::Handle<i::String> str = Utils::OpenHandle(*(source->source_string));
@ -1763,36 +1739,28 @@ Local<UnboundScript> ScriptCompiler::CompileUnbound(
source->resource_is_shared_cross_origin == v8::True(v8_isolate);
}
EXCEPTION_PREAMBLE(isolate);
i::Handle<i::SharedFunctionInfo> result =
i::Compiler::CompileScript(str,
name_obj,
line_offset,
column_offset,
is_shared_cross_origin,
isolate->global_context(),
NULL,
&script_data_impl,
cached_data_mode,
i::Handle<i::SharedFunctionInfo> result = i::Compiler::CompileScript(
str, name_obj, line_offset, column_offset, is_shared_cross_origin,
isolate->global_context(), NULL, &script_data, cached_data_mode,
i::NOT_NATIVES_CODE);
has_pending_exception = result.is_null();
if (has_pending_exception && cached_data_mode == i::CONSUME_CACHED_DATA) {
// This case won't happen during normal operation; we have compiled
// successfully and produced cached data, and but the second compilation
// of the same source code fails.
delete script_data_impl;
script_data_impl = NULL;
delete script_data;
script_data = NULL;
}
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
raw_result = *result;
if ((options & kProduceDataToCache) && script_data_impl != NULL) {
if ((options & kProduceDataToCache) && script_data != NULL) {
// script_data_impl now contains the data that was generated. source will
// take the ownership.
source->cached_data = new CachedData(
reinterpret_cast<const uint8_t*>(script_data_impl->Data()),
script_data_impl->Length(), CachedData::BufferOwned);
script_data_impl->owns_store_ = false;
script_data->data(), script_data->length(), CachedData::BufferOwned);
script_data->ReleaseDataOwnership();
}
delete script_data_impl;
delete script_data;
}
i::Handle<i::SharedFunctionInfo> result(raw_result, isolate);
return ToApiHandle<UnboundScript>(result);

View File

@ -31,6 +31,18 @@ namespace v8 {
namespace internal {
ScriptData::ScriptData(const byte* data, int length)
: owns_data_(false), data_(data), length_(length) {
if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
byte* copy = NewArray<byte>(length);
ASSERT(IsAligned(reinterpret_cast<intptr_t>(data_), kPointerAlignment));
CopyBytes(copy, data, length);
data_ = copy;
AcquireDataOwnership();
}
}
CompilationInfo::CompilationInfo(Handle<Script> script,
Zone* zone)
: flags_(StrictModeField::encode(SLOPPY)),

View File

@ -13,7 +13,6 @@ namespace v8 {
namespace internal {
class AstValueFactory;
class ScriptData;
class HydrogenCodeStub;
// ParseRestriction is used to restrict the set of valid statements in a
@ -35,6 +34,36 @@ struct OffsetRange {
int to;
};
class ScriptData {
public:
ScriptData(const byte* data, int length);
~ScriptData() {
if (owns_data_) DeleteArray(data_);
}
const byte* data() const { return data_; }
int length() const { return length_; }
void AcquireDataOwnership() {
ASSERT(!owns_data_);
owns_data_ = true;
}
void ReleaseDataOwnership() {
ASSERT(owns_data_);
owns_data_ = false;
}
private:
bool owns_data_;
const byte* data_;
int length_;
DISALLOW_COPY_AND_ASSIGN(ScriptData);
};
// CompilationInfo encapsulates some information known at compile time. It
// is constructed based on the resources available at compile-time.
class CompilationInfo {
@ -722,7 +751,6 @@ class CompilationPhase BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(CompilationPhase);
};
} } // namespace v8::internal
#endif // V8_COMPILER_H_

View File

@ -137,8 +137,6 @@ var kMessages = {
array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
illegal_access: ["Illegal access"],
invalid_cached_data_function: ["Invalid cached data for function ", "%0"],
invalid_cached_data: ["Invalid cached data"],
strict_mode_with: ["Strict mode code may not include a with statement"],
strict_eval_arguments: ["Unexpected eval or arguments in strict mode"],
too_many_arguments: ["Too many arguments in function call (only 65535 allowed)"],

View File

@ -27,8 +27,8 @@ using namespace v8;
class Compressor {
public:
virtual ~Compressor() {}
virtual bool Compress(i::Vector<char> input) = 0;
virtual i::Vector<char>* output() = 0;
virtual bool Compress(i::Vector<i::byte> input) = 0;
virtual i::Vector<i::byte>* output() = 0;
};
@ -63,9 +63,9 @@ class SnapshotWriter {
startup_blob_file_ = GetFileDescriptorOrDie(startup_blob_file);
}
void WriteSnapshot(const i::List<char>& snapshot_data,
void WriteSnapshot(const i::List<i::byte>& snapshot_data,
const i::Serializer& serializer,
const i::List<char>& context_snapshot_data,
const i::List<i::byte>& context_snapshot_data,
const i::Serializer& context_serializer) const {
WriteSnapshotFile(snapshot_data, serializer,
context_snapshot_data, context_serializer);
@ -74,14 +74,14 @@ class SnapshotWriter {
}
private:
void MaybeWriteStartupBlob(const i::List<char>& snapshot_data,
void MaybeWriteStartupBlob(const i::List<i::byte>& snapshot_data,
const i::Serializer& serializer,
const i::List<char>& context_snapshot_data,
const i::List<i::byte>& context_snapshot_data,
const i::Serializer& context_serializer) const {
if (!startup_blob_file_)
return;
i::List<char> startup_blob;
i::List<i::byte> startup_blob;
i::ListSnapshotSink sink(&startup_blob);
int spaces[] = {
@ -89,13 +89,12 @@ class SnapshotWriter {
i::MAP_SPACE, i::CELL_SPACE, i::PROPERTY_CELL_SPACE
};
i::byte* snapshot_bytes = reinterpret_cast<i::byte*>(snapshot_data.begin());
i::byte* snapshot_bytes = snapshot_data.begin();
sink.PutBlob(snapshot_bytes, snapshot_data.length(), "snapshot");
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
sink.PutInt(serializer.CurrentAllocationAddress(spaces[i]), "spaces");
i::byte* context_bytes =
reinterpret_cast<i::byte*>(context_snapshot_data.begin());
i::byte* context_bytes = context_snapshot_data.begin();
sink.PutBlob(context_bytes, context_snapshot_data.length(), "context");
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
sink.PutInt(context_serializer.CurrentAllocationAddress(spaces[i]),
@ -109,9 +108,9 @@ class SnapshotWriter {
}
}
void WriteSnapshotFile(const i::List<char>& snapshot_data,
void WriteSnapshotFile(const i::List<i::byte>& snapshot_data,
const i::Serializer& serializer,
const i::List<char>& context_snapshot_data,
const i::List<i::byte>& context_snapshot_data,
const i::Serializer& context_serializer) const {
WriteFilePrefix();
WriteData("", snapshot_data, raw_file_);
@ -135,11 +134,10 @@ class SnapshotWriter {
fprintf(fp_, "} // namespace v8\n");
}
void WriteData(const char* prefix,
const i::List<char>& source_data,
void WriteData(const char* prefix, const i::List<i::byte>& source_data,
FILE* raw_file) const {
const i::List <char>* data_to_be_written = NULL;
i::List<char> compressed_data;
const i::List<i::byte>* data_to_be_written = NULL;
i::List<i::byte> compressed_data;
if (!compressor_) {
data_to_be_written = &source_data;
} else if (compressor_->Compress(source_data.ToVector())) {
@ -155,7 +153,7 @@ class SnapshotWriter {
WriteData(prefix, source_data, data_to_be_written);
}
void MaybeWriteRawFile(const i::List<char>* data, FILE* raw_file) const {
void MaybeWriteRawFile(const i::List<i::byte>* data, FILE* raw_file) const {
if (!data || !raw_file)
return;
@ -170,9 +168,8 @@ class SnapshotWriter {
}
}
void WriteData(const char* prefix,
const i::List<char>& source_data,
const i::List<char>* data_to_be_written) const {
void WriteData(const char* prefix, const i::List<i::byte>& source_data,
const i::List<i::byte>* data_to_be_written) const {
fprintf(fp_, "const byte Snapshot::%sdata_[] = {\n", prefix);
WriteSnapshotData(data_to_be_written);
fprintf(fp_, "};\n");
@ -209,7 +206,7 @@ class SnapshotWriter {
prefix, name, ser.CurrentAllocationAddress(space));
}
void WriteSnapshotData(const i::List<char>* data) const {
void WriteSnapshotData(const i::List<i::byte>* data) const {
for (int i = 0; i < data->length(); i++) {
if ((i & 0x1f) == 0x1f)
fprintf(fp_, "\n");
@ -405,12 +402,12 @@ int main(int argc, char** argv) {
// This results in a somewhat smaller snapshot, probably because it gets
// rid of some things that are cached between garbage collections.
i::List<char> snapshot_data;
i::List<i::byte> snapshot_data;
i::ListSnapshotSink snapshot_sink(&snapshot_data);
i::StartupSerializer ser(internal_isolate, &snapshot_sink);
ser.SerializeStrongReferences();
i::List<char> context_data;
i::List<i::byte> context_data;
i::ListSnapshotSink contex_sink(&context_data);
i::PartialSerializer context_ser(internal_isolate, &ser, &contex_sink);
context_ser.Serialize(&raw_context);

View File

@ -182,147 +182,86 @@ void RegExpBuilder::AddQuantifierToAtom(
}
ScriptData* ScriptData::New(const char* data, int length, bool owns_store) {
// The length is obviously invalid.
if (length % sizeof(unsigned) != 0) {
return NULL;
}
int deserialized_data_length = length / sizeof(unsigned);
unsigned* deserialized_data;
owns_store =
owns_store || reinterpret_cast<intptr_t>(data) % sizeof(unsigned) != 0;
if (owns_store) {
// Copy the data to align it.
deserialized_data = i::NewArray<unsigned>(deserialized_data_length);
i::CopyBytes(reinterpret_cast<char*>(deserialized_data),
data, static_cast<size_t>(length));
} else {
// If aligned, don't create a copy of the data.
deserialized_data = reinterpret_cast<unsigned*>(const_cast<char*>(data));
}
return new ScriptData(
Vector<unsigned>(deserialized_data, deserialized_data_length),
owns_store);
}
FunctionEntry ScriptData::GetFunctionEntry(int start) {
FunctionEntry ParseData::GetFunctionEntry(int start) {
// The current pre-data entry must be a FunctionEntry with the given
// start position.
if ((function_index_ + FunctionEntry::kSize <= store_.length())
&& (static_cast<int>(store_[function_index_]) == start)) {
if ((function_index_ + FunctionEntry::kSize <= Length()) &&
(static_cast<int>(Data()[function_index_]) == start)) {
int index = function_index_;
function_index_ += FunctionEntry::kSize;
return FunctionEntry(store_.SubVector(index,
index + FunctionEntry::kSize));
Vector<unsigned> subvector(&(Data()[index]), FunctionEntry::kSize);
return FunctionEntry(subvector);
}
return FunctionEntry();
}
int ScriptData::GetSymbolIdentifier() {
return ReadNumber(&symbol_data_);
int ParseData::FunctionCount() {
int functions_size = FunctionsSize();
if (functions_size < 0) return 0;
if (functions_size % FunctionEntry::kSize != 0) return 0;
return functions_size / FunctionEntry::kSize;
}
bool ScriptData::SanityCheck() {
bool ParseData::IsSane() {
// Check that the header data is valid and doesn't specify
// point to positions outside the store.
if (store_.length() < PreparseDataConstants::kHeaderSize) return false;
if (magic() != PreparseDataConstants::kMagicNumber) return false;
if (version() != PreparseDataConstants::kCurrentVersion) return false;
if (has_error()) {
// Extra sane sanity check for error message encoding.
if (store_.length() <= PreparseDataConstants::kHeaderSize
+ PreparseDataConstants::kMessageTextPos) {
return false;
}
if (Read(PreparseDataConstants::kMessageStartPos) >
Read(PreparseDataConstants::kMessageEndPos)) {
return false;
}
unsigned arg_count = Read(PreparseDataConstants::kMessageArgCountPos);
int pos = PreparseDataConstants::kMessageTextPos;
for (unsigned int i = 0; i <= arg_count; i++) {
if (store_.length() <= PreparseDataConstants::kHeaderSize + pos) {
return false;
}
int length = static_cast<int>(Read(pos));
if (length < 0) return false;
pos += 1 + length;
}
if (store_.length() < PreparseDataConstants::kHeaderSize + pos) {
return false;
}
return true;
}
int data_length = Length();
if (data_length < PreparseDataConstants::kHeaderSize) return false;
if (Magic() != PreparseDataConstants::kMagicNumber) return false;
if (Version() != PreparseDataConstants::kCurrentVersion) return false;
if (HasError()) return false;
// Check that the space allocated for function entries is sane.
int functions_size =
static_cast<int>(store_[PreparseDataConstants::kFunctionsSizeOffset]);
int functions_size = FunctionsSize();
if (functions_size < 0) return false;
if (functions_size % FunctionEntry::kSize != 0) return false;
// Check that the total size has room for header and function entries.
int minimum_size =
PreparseDataConstants::kHeaderSize + functions_size;
if (store_.length() < minimum_size) return false;
if (data_length < minimum_size) return false;
return true;
}
const char* ScriptData::ReadString(unsigned* start, int* chars) {
int length = start[0];
char* result = NewArray<char>(length + 1);
for (int i = 0; i < length; i++) {
result[i] = start[i + 1];
void ParseData::Initialize() {
// Prepares state for use.
int data_length = Length();
if (data_length >= PreparseDataConstants::kHeaderSize) {
function_index_ = PreparseDataConstants::kHeaderSize;
}
result[length] = '\0';
if (chars != NULL) *chars = length;
return result;
}
Scanner::Location ScriptData::MessageLocation() const {
int beg_pos = Read(PreparseDataConstants::kMessageStartPos);
int end_pos = Read(PreparseDataConstants::kMessageEndPos);
return Scanner::Location(beg_pos, end_pos);
bool ParseData::HasError() {
return Data()[PreparseDataConstants::kHasErrorOffset];
}
bool ScriptData::IsReferenceError() const {
return Read(PreparseDataConstants::kIsReferenceErrorPos);
unsigned ParseData::Magic() {
return Data()[PreparseDataConstants::kMagicOffset];
}
const char* ScriptData::BuildMessage() const {
unsigned* start = ReadAddress(PreparseDataConstants::kMessageTextPos);
return ReadString(start, NULL);
unsigned ParseData::Version() {
return Data()[PreparseDataConstants::kVersionOffset];
}
const char* ScriptData::BuildArg() const {
int arg_count = Read(PreparseDataConstants::kMessageArgCountPos);
ASSERT(arg_count == 0 || arg_count == 1);
if (arg_count == 0) {
return NULL;
int ParseData::FunctionsSize() {
return static_cast<int>(Data()[PreparseDataConstants::kFunctionsSizeOffset]);
}
void Parser::SetCachedData() {
if (cached_data_mode() == NO_CACHED_DATA) {
cached_parse_data_ = NULL;
} else {
ASSERT(info_->cached_data() != NULL);
if (cached_data_mode() == CONSUME_CACHED_DATA) {
cached_parse_data_ = new ParseData(*info_->cached_data());
}
}
// Position after text found by skipping past length field and
// length field content words.
int pos = PreparseDataConstants::kMessageTextPos + 1
+ Read(PreparseDataConstants::kMessageTextPos);
int count = 0;
return ReadString(ReadAddress(pos), &count);
}
unsigned ScriptData::Read(int position) const {
return store_[PreparseDataConstants::kHeaderSize + position];
}
unsigned* ScriptData::ReadAddress(int position) const {
return &store_[PreparseDataConstants::kHeaderSize + position];
}
@ -757,18 +696,14 @@ FunctionLiteral* ParserTraits::ParseFunctionLiteral(
Parser::Parser(CompilationInfo* info)
: ParserBase<ParserTraits>(&scanner_,
info->isolate()->stack_guard()->real_climit(),
info->extension(),
NULL,
info->zone(),
this),
info->extension(), NULL, info->zone(), this),
isolate_(info->isolate()),
script_(info->script()),
scanner_(isolate_->unicode_cache()),
reusable_preparser_(NULL),
original_scope_(NULL),
target_stack_(NULL),
cached_data_(NULL),
cached_data_mode_(NO_CACHED_DATA),
cached_parse_data_(NULL),
ast_value_factory_(NULL),
info_(info),
has_pending_error_(false),
@ -805,10 +740,10 @@ FunctionLiteral* Parser::ParseProgram() {
// Initialize parser state.
CompleteParserRecorder recorder;
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
log_ = &recorder;
} else if (cached_data_mode_ == CONSUME_CACHED_DATA) {
(*cached_data_)->Initialize();
} else if (cached_data_mode() == CONSUME_CACHED_DATA) {
cached_parse_data_->Initialize();
}
source = String::Flatten(source);
@ -840,11 +775,8 @@ FunctionLiteral* Parser::ParseProgram() {
}
PrintF(" - took %0.3f ms]\n", ms);
}
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
if (result != NULL) {
Vector<unsigned> store = recorder.ExtractData();
*cached_data_ = new ScriptData(store);
}
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
if (result != NULL) *info_->cached_data() = recorder.GetScriptData();
log_ = NULL;
}
return result;
@ -3292,12 +3224,6 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
}
void Parser::ReportInvalidCachedData(const AstRawString* name, bool* ok) {
ParserTraits::ReportMessage("invalid_cached_data_function", name);
*ok = false;
}
bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
if (expression->IsLiteral()) return true;
MaterializedLiteral* lit = expression->AsMaterializedLiteral();
@ -3614,19 +3540,15 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
int* expected_property_count,
bool* ok) {
int function_block_pos = position();
if (cached_data_mode_ == CONSUME_CACHED_DATA) {
if (cached_data_mode() == CONSUME_CACHED_DATA) {
// If we have cached data, we use it to skip parsing the function body. The
// data contains the information we need to construct the lazy function.
FunctionEntry entry =
(*cached_data())->GetFunctionEntry(function_block_pos);
if (entry.is_valid()) {
if (entry.end_pos() <= function_block_pos) {
cached_parse_data_->GetFunctionEntry(function_block_pos);
// Check that cached data is valid.
CHECK(entry.is_valid());
// End position greater than end of stream is safe, and hard to check.
ReportInvalidCachedData(function_name, ok);
if (!*ok) {
return;
}
}
CHECK(entry.end_pos() > function_block_pos);
scanner()->SeekForward(entry.end_pos() - 1);
scope_->set_end_position(entry.end_pos());
@ -3639,12 +3561,6 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
*materialized_literal_count = entry.literal_count();
*expected_property_count = entry.property_count();
scope_->SetStrictMode(entry.strict_mode());
} else {
// This case happens when we have preparse data but it doesn't contain an
// entry for the function. Fail the compilation.
ReportInvalidCachedData(function_name, ok);
return;
}
} else {
// With no cached data, we partially parse the function, without building an
// AST. This gathers the data needed to build a lazy function.
@ -3674,7 +3590,7 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
*materialized_literal_count = logger.literals();
*expected_property_count = logger.properties();
scope_->SetStrictMode(logger.strict_mode());
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
ASSERT(log_);
// Position right after terminal '}'.
int body_end = scanner()->location().end_pos;
@ -4766,70 +4682,6 @@ RegExpTree* RegExpParser::ParseCharacterClass() {
// ----------------------------------------------------------------------------
// The Parser interface.
ScriptData::~ScriptData() {
if (owns_store_) store_.Dispose();
}
int ScriptData::Length() {
return store_.length() * sizeof(unsigned);
}
const char* ScriptData::Data() {
return reinterpret_cast<const char*>(store_.start());
}
bool ScriptData::HasError() {
return has_error();
}
void ScriptData::Initialize() {
// Prepares state for use.
if (store_.length() >= PreparseDataConstants::kHeaderSize) {
function_index_ = PreparseDataConstants::kHeaderSize;
int symbol_data_offset = PreparseDataConstants::kHeaderSize
+ store_[PreparseDataConstants::kFunctionsSizeOffset];
if (store_.length() > symbol_data_offset) {
symbol_data_ = reinterpret_cast<byte*>(&store_[symbol_data_offset]);
} else {
// Partial preparse causes no symbol information.
symbol_data_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
}
symbol_data_end_ = reinterpret_cast<byte*>(&store_[0] + store_.length());
}
}
int ScriptData::ReadNumber(byte** source) {
// Reads a number from symbol_data_ in base 128. The most significant
// bit marks that there are more digits.
// If the first byte is 0x80 (kNumberTerminator), it would normally
// represent a leading zero. Since that is useless, and therefore won't
// appear as the first digit of any actual value, it is used to
// mark the end of the input stream.
byte* data = *source;
if (data >= symbol_data_end_) return -1;
byte input = *data;
if (input == PreparseDataConstants::kNumberTerminator) {
// End of stream marker.
return -1;
}
int result = input & 0x7f;
data++;
while ((input & 0x80u) != 0) {
if (data >= symbol_data_end_) return -1;
input = *data;
result = (result << 7) | (input & 0x7f);
data++;
}
*source = data;
return result;
}
bool RegExpParser::ParseRegExp(FlatStringReader* input,
bool multiline,
RegExpCompileData* result,
@ -4876,7 +4728,7 @@ bool Parser::Parse() {
result = ParseProgram();
}
} else {
SetCachedData(info()->cached_data(), info()->cached_data_mode());
SetCachedData();
result = ParseProgram();
}
info()->SetFunction(result);

View File

@ -59,73 +59,39 @@ class FunctionEntry BASE_EMBEDDED {
};
class ScriptData {
// Wrapper around ScriptData to provide parser-specific functionality.
class ParseData {
public:
explicit ScriptData(Vector<unsigned> store)
: store_(store),
owns_store_(true) { }
ScriptData(Vector<unsigned> store, bool owns_store)
: store_(store),
owns_store_(owns_store) { }
// The created ScriptData won't take ownership of the data. If the alignment
// is not correct, this will copy the data (and the created ScriptData will
// take ownership of the copy).
static ScriptData* New(const char* data, int length, bool owns_store = false);
virtual ~ScriptData();
virtual int Length();
virtual const char* Data();
virtual bool HasError();
void Initialize();
void ReadNextSymbolPosition();
FunctionEntry GetFunctionEntry(int start);
int GetSymbolIdentifier();
bool SanityCheck();
Scanner::Location MessageLocation() const;
bool IsReferenceError() const;
const char* BuildMessage() const;
const char* BuildArg() const;
int function_count() {
int functions_size =
static_cast<int>(store_[PreparseDataConstants::kFunctionsSizeOffset]);
if (functions_size < 0) return 0;
if (functions_size % FunctionEntry::kSize != 0) return 0;
return functions_size / FunctionEntry::kSize;
explicit ParseData(ScriptData* script_data) : script_data_(script_data) {
CHECK(IsAligned(script_data->length(), sizeof(unsigned)));
CHECK(IsSane());
}
void Initialize();
FunctionEntry GetFunctionEntry(int start);
int FunctionCount();
bool HasError();
unsigned* Data() { // Writable data as unsigned int array.
return reinterpret_cast<unsigned*>(const_cast<byte*>(script_data_->data()));
}
// The following functions should only be called if SanityCheck has
// returned true.
bool has_error() { return store_[PreparseDataConstants::kHasErrorOffset]; }
unsigned magic() { return store_[PreparseDataConstants::kMagicOffset]; }
unsigned version() { return store_[PreparseDataConstants::kVersionOffset]; }
private:
// Disable copying and assigning; because of owns_store they won't be correct.
ScriptData(const ScriptData&);
ScriptData& operator=(const ScriptData&);
bool IsSane();
unsigned Magic();
unsigned Version();
int FunctionsSize();
int Length() const {
// Script data length is already checked to be a multiple of unsigned size.
return script_data_->length() / sizeof(unsigned);
}
friend class v8::ScriptCompiler;
Vector<unsigned> store_;
unsigned char* symbol_data_;
unsigned char* symbol_data_end_;
ScriptData* script_data_;
int function_index_;
bool owns_store_;
unsigned Read(int position) const;
unsigned* ReadAddress(int position) const;
// Reads a number from the current symbols
int ReadNumber(byte** source);
// Read strings written by ParserRecorder::WriteString.
static const char* ReadString(unsigned* start, int* chars);
DISALLOW_COPY_AND_ASSIGN(ParseData);
};
// ----------------------------------------------------------------------------
// REGEXP PARSING
@ -646,23 +612,10 @@ class Parser : public ParserBase<ParserTraits> {
FunctionLiteral* DoParseProgram(CompilationInfo* info,
Handle<String> source);
// Report syntax error
void ReportInvalidCachedData(const AstRawString* name, bool* ok);
void SetCachedData(ScriptData** data,
CachedDataMode cached_data_mode) {
cached_data_mode_ = cached_data_mode;
if (cached_data_mode == NO_CACHED_DATA) {
cached_data_ = NULL;
} else {
ASSERT(data != NULL);
cached_data_ = data;
}
}
void SetCachedData();
bool inside_with() const { return scope_->inside_with(); }
ScriptData** cached_data() const { return cached_data_; }
CachedDataMode cached_data_mode() const { return cached_data_mode_; }
CachedDataMode cached_data_mode() const { return info_->cached_data_mode(); }
Scope* DeclarationScope(VariableMode mode) {
return IsLexicalVariableMode(mode)
? scope_ : scope_->DeclarationScope();
@ -809,7 +762,7 @@ class Parser : public ParserBase<ParserTraits> {
PreParser* reusable_preparser_;
Scope* original_scope_; // for ES5 function declarations in sloppy eval
Target* target_stack_; // for break, continue statements
ScriptData** cached_data_;
ParseData* cached_parse_data_;
CachedDataMode cached_data_mode_;
AstValueFactory* ast_value_factory_;

View File

@ -4,6 +4,7 @@
#include "include/v8stdint.h"
#include "src/base/logging.h"
#include "src/compiler.h"
#include "src/globals.h"
#include "src/hashmap.h"
#include "src/preparse-data.h"
@ -34,7 +35,7 @@ void CompleteParserRecorder::LogMessage(int start_pos,
const char* message,
const char* arg_opt,
bool is_reference_error) {
if (has_error()) return;
if (HasError()) return;
preamble_[PreparseDataConstants::kHasErrorOffset] = true;
function_store_.Reset();
STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0);
@ -59,17 +60,21 @@ void CompleteParserRecorder::WriteString(Vector<const char> str) {
}
Vector<unsigned> CompleteParserRecorder::ExtractData() {
ScriptData* CompleteParserRecorder::GetScriptData() {
int function_size = function_store_.size();
int total_size = PreparseDataConstants::kHeaderSize + function_size;
Vector<unsigned> data = Vector<unsigned>::New(total_size);
unsigned* data = NewArray<unsigned>(total_size);
preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size;
MemCopy(data.start(), preamble_, sizeof(preamble_));
MemCopy(data, preamble_, sizeof(preamble_));
if (function_size > 0) {
function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize,
total_size));
function_store_.WriteTo(Vector<unsigned>(
data + PreparseDataConstants::kHeaderSize, function_size));
}
return data;
ASSERT(IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment));
ScriptData* result = new ScriptData(reinterpret_cast<byte*>(data),
total_size * sizeof(unsigned));
result->AcquireDataOwnership();
return result;
}

View File

@ -13,6 +13,8 @@
namespace v8 {
namespace internal {
class ScriptData;
// Abstract interface for preparse data recorder.
class ParserRecorder {
@ -149,13 +151,17 @@ class CompleteParserRecorder : public ParserRecorder {
const char* message,
const char* argument_opt,
bool is_reference_error_);
Vector<unsigned> ExtractData();
ScriptData* GetScriptData();
private:
bool has_error() {
bool HasError() {
return static_cast<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]);
}
Vector<unsigned> ErrorMessageData() {
ASSERT(HasError());
return function_store_.ToVector();
}
private:
void WriteString(Vector<const char> str);
// Write a non-negative number to the symbol store.

View File

@ -1799,13 +1799,12 @@ int Serializer::SpaceAreaSize(int space) {
}
void Serializer::PadByte() { sink_->Put(kNop, "Padding"); }
void Serializer::Pad() {
// The non-branching GetInt will read up to 3 bytes too far, so we need
// to pad the snapshot to make sure we don't read over the end.
for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) PadByte();
for (unsigned i = 0; i < sizeof(int32_t) - 1; i++) {
sink_->Put(kNop, "Padding");
}
}
@ -1817,33 +1816,16 @@ void Serializer::InitializeCodeAddressMap() {
ScriptData* CodeSerializer::Serialize(Handle<SharedFunctionInfo> info) {
// Serialize code object.
List<char> payload;
ListSnapshotSink listsink(&payload);
CodeSerializer ser(info->GetIsolate(), &listsink);
List<byte> payload;
ListSnapshotSink list_sink(&payload);
CodeSerializer cs(info->GetIsolate(), &list_sink);
DisallowHeapAllocation no_gc;
Object** location = Handle<Object>::cast(info).location();
ser.VisitPointer(location);
ser.Pad();
cs.VisitPointer(location);
cs.Pad();
// Allocate storage. The payload length may not be aligned. Round up.
// TODO(yangguo) replace ScriptData with a more generic super class.
int payload_length = payload.length();
int raw_length = payload_length / sizeof(unsigned) + kHeaderSize;
if (!IsAligned(payload_length, sizeof(unsigned))) raw_length++;
unsigned* raw_data = i::NewArray<unsigned>(raw_length);
char* payload_data = reinterpret_cast<char*>(raw_data + kHeaderSize);
// Write header.
raw_data[kVersionHashOffset] = Version::Hash();
raw_data[kPayloadLengthOffset] = payload_length;
STATIC_ASSERT(NEW_SPACE == 0);
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
raw_data[kReservationsOffset + i] = ser.CurrentAllocationAddress(i);
}
CopyBytes(payload_data, payload.begin(), static_cast<size_t>(payload_length));
return new ScriptData(Vector<unsigned>(raw_data, raw_length), true);
SerializedCodeData data(&payload, &cs);
return data.GetScriptData();
}
@ -1892,20 +1874,13 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
Object* CodeSerializer::Deserialize(Isolate* isolate, ScriptData* data) {
const unsigned* raw_data = reinterpret_cast<const unsigned*>(data->Data());
CHECK_EQ(Version::Hash(), raw_data[kVersionHashOffset]);
int payload_length = raw_data[kPayloadLengthOffset];
const byte* payload_data =
reinterpret_cast<const byte*>(raw_data + kHeaderSize);
ASSERT_LE(payload_length, data->Length() - kHeaderSize);
SnapshotByteSource payload(payload_data, payload_length);
SerializedCodeData scd(data);
SnapshotByteSource payload(scd.Payload(), scd.PayloadLength());
Deserializer deserializer(&payload);
STATIC_ASSERT(NEW_SPACE == 0);
// TODO(yangguo) what happens if remaining new space is too small?
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
deserializer.set_reservation(
i, raw_data[CodeSerializer::kReservationsOffset + i]);
deserializer.set_reservation(i, scd.GetReservation(i));
}
Object* root;
deserializer.DeserializePartial(isolate, &root);
@ -1913,4 +1888,27 @@ Object* CodeSerializer::Deserialize(Isolate* isolate, ScriptData* data) {
ASSERT(root->IsSharedFunctionInfo());
return root;
}
SerializedCodeData::SerializedCodeData(List<byte>* payload, CodeSerializer* cs)
: owns_script_data_(true) {
int data_length = payload->length() + kHeaderEntries * kIntSize;
byte* data = NewArray<byte>(data_length);
ASSERT(IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment));
CopyBytes(data + kHeaderEntries * kIntSize, payload->begin(),
static_cast<size_t>(payload->length()));
script_data_ = new ScriptData(data, data_length);
script_data_->AcquireDataOwnership();
SetHeaderValue(kVersionHashOffset, Version::Hash());
STATIC_ASSERT(NEW_SPACE == 0);
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
SetHeaderValue(kReservationsOffset + i, cs->CurrentAllocationAddress(i));
}
}
bool SerializedCodeData::IsSane() {
return GetHeaderValue(kVersionHashOffset) == Version::Hash() &&
PayloadLength() >= SharedFunctionInfo::kSize;
}
} } // namespace v8::internal

View File

@ -5,10 +5,10 @@
#ifndef V8_SERIALIZE_H_
#define V8_SERIALIZE_H_
#include "src/compiler.h"
#include "src/hashmap.h"
#include "src/heap-profiler.h"
#include "src/isolate.h"
#include "src/parser.h"
#include "src/snapshot-source-sink.h"
namespace v8 {
@ -470,7 +470,6 @@ class Serializer : public SerializerDeserializer {
SerializationAddressMapper address_mapper_;
intptr_t root_index_wave_front_;
void Pad();
void PadByte();
friend class ObjectSerializer;
friend class Deserializer;
@ -576,6 +575,67 @@ class CodeSerializer : public Serializer {
static const int kPayloadLengthOffset = 1;
static const int kReservationsOffset = 2;
};
// Wrapper around ScriptData to provide code-serializer-specific functionality.
class SerializedCodeData {
public:
// Used by when consuming.
explicit SerializedCodeData(ScriptData* data)
: script_data_(data), owns_script_data_(false) {
CHECK(IsSane());
}
// Used when producing.
SerializedCodeData(List<byte>* payload, CodeSerializer* cs);
~SerializedCodeData() {
if (owns_script_data_) delete script_data_;
}
// Return ScriptData object and relinquish ownership over it to the caller.
ScriptData* GetScriptData() {
ScriptData* result = script_data_;
script_data_ = NULL;
ASSERT(owns_script_data_);
owns_script_data_ = false;
return result;
}
const byte* Payload() const {
return script_data_->data() + kHeaderEntries * kIntSize;
}
int PayloadLength() const {
return script_data_->length() - kHeaderEntries * kIntSize;
}
int GetReservation(int space) const {
return GetHeaderValue(kReservationsOffset + space);
}
private:
void SetHeaderValue(int offset, int value) {
reinterpret_cast<int*>(const_cast<byte*>(script_data_->data()))[offset] =
value;
}
int GetHeaderValue(int offset) const {
return reinterpret_cast<const int*>(script_data_->data())[offset];
}
bool IsSane();
// The data header consists of int-sized entries:
// [0] version hash
// [1..7] reservation sizes for spaces from NEW_SPACE to PROPERTY_CELL_SPACE.
static const int kVersionHashOffset = 0;
static const int kReservationsOffset = 1;
static const int kHeaderEntries = 8;
ScriptData* script_data_;
bool owns_script_data_;
};
} } // namespace v8::internal
#endif // V8_SERIALIZE_H_

View File

@ -92,9 +92,9 @@ bool SnapshotByteSource::GetBlob(const byte** data, int* number_of_bytes) {
}
void DebugSnapshotSink::Put(int byte, const char* description) {
PrintF("%24s: %x\n", description, byte);
sink_->Put(byte, description);
void DebugSnapshotSink::Put(byte b, const char* description) {
PrintF("%24s: %x\n", description, b);
sink_->Put(b, description);
}
} // namespace v8::internal

View File

@ -71,9 +71,10 @@ class SnapshotByteSource V8_FINAL {
class SnapshotByteSink {
public:
virtual ~SnapshotByteSink() { }
virtual void Put(int byte, const char* description) = 0;
virtual void PutSection(int byte, const char* description) {
Put(byte, description);
virtual void Put(byte b, const char* description) = 0;
virtual void PutSection(int b, const char* description) {
ASSERT_LE(b, kMaxUInt8);
Put(static_cast<byte>(b), description);
}
void PutInt(uintptr_t integer, const char* description);
void PutRaw(byte* data, int number_of_bytes, const char* description);
@ -86,7 +87,7 @@ class DummySnapshotSink : public SnapshotByteSink {
public:
DummySnapshotSink() : length_(0) {}
virtual ~DummySnapshotSink() {}
virtual void Put(int byte, const char* description) { length_++; }
virtual void Put(byte b, const char* description) { length_++; }
virtual int Position() { return length_; }
private:
@ -98,7 +99,7 @@ class DummySnapshotSink : public SnapshotByteSink {
class DebugSnapshotSink : public SnapshotByteSink {
public:
explicit DebugSnapshotSink(SnapshotByteSink* chained) : sink_(chained) {}
virtual void Put(int byte, const char* description) V8_OVERRIDE;
virtual void Put(byte b, const char* description) V8_OVERRIDE;
virtual int Position() V8_OVERRIDE { return sink_->Position(); }
private:
@ -108,14 +109,14 @@ class DebugSnapshotSink : public SnapshotByteSink {
class ListSnapshotSink : public i::SnapshotByteSink {
public:
explicit ListSnapshotSink(i::List<char>* data) : data_(data) {}
virtual void Put(int byte, const char* description) V8_OVERRIDE {
data_->Add(byte);
explicit ListSnapshotSink(i::List<byte>* data) : data_(data) {}
virtual void Put(byte b, const char* description) V8_OVERRIDE {
data_->Add(b);
}
virtual int Position() V8_OVERRIDE { return data_->length(); }
private:
i::List<char>* data_;
i::List<byte>* data_;
};
} // namespace v8::internal

View File

@ -14814,134 +14814,21 @@ TEST(PreCompileSerialization) {
v8::ScriptCompiler::kProduceDataToCache);
// Serialize.
const v8::ScriptCompiler::CachedData* cd = source.GetCachedData();
char* serialized_data = i::NewArray<char>(cd->length);
i::byte* serialized_data = i::NewArray<i::byte>(cd->length);
i::MemCopy(serialized_data, cd->data, cd->length);
// Deserialize.
i::ScriptData* deserialized = i::ScriptData::New(serialized_data, cd->length);
i::ScriptData* deserialized = new i::ScriptData(serialized_data, cd->length);
// Verify that the original is the same as the deserialized.
CHECK_EQ(cd->length, deserialized->Length());
CHECK_EQ(0, memcmp(cd->data, deserialized->Data(), cd->length));
CHECK_EQ(cd->length, deserialized->length());
CHECK_EQ(0, memcmp(cd->data, deserialized->data(), cd->length));
delete deserialized;
i::DeleteArray(serialized_data);
}
// Attempts to deserialize bad data.
TEST(PreCompileDeserializationError) {
v8::V8::Initialize();
const char* data = "DONT CARE";
int invalid_size = 3;
i::ScriptData* sd = i::ScriptData::New(data, invalid_size);
CHECK_EQ(NULL, sd);
}
TEST(CompileWithInvalidCachedData) {
v8::V8::Initialize();
v8::Isolate* isolate = CcTest::isolate();
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
i::FLAG_min_preparse_length = 0;
const char* script = "function foo(){ return 5;}\n"
"function bar(){ return 6 + 7;} foo();";
v8::ScriptCompiler::Source source(v8_str(script));
v8::ScriptCompiler::Compile(isolate, &source,
v8::ScriptCompiler::kProduceDataToCache);
// source owns its cached data. Create a ScriptData based on it. The user
// never needs to create ScriptDatas any more; we only need it here because we
// want to modify the data before passing it back.
const v8::ScriptCompiler::CachedData* cd = source.GetCachedData();
// ScriptData does not take ownership of the buffers passed to it.
i::ScriptData* sd =
i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length);
CHECK(!sd->HasError());
// ScriptData private implementation details
const int kHeaderSize = i::PreparseDataConstants::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()));
// Overwrite function bar's end position with 0.
sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
v8::TryCatch try_catch;
// Make the script slightly different so that we don't hit the compilation
// cache. Don't change the lenghts of tokens.
const char* script2 = "function foo(){ return 6;}\n"
"function bar(){ return 6 + 7;} foo();";
v8::ScriptCompiler::Source source2(
v8_str(script2),
// CachedData doesn't take ownership of the buffers, Source takes
// ownership of CachedData.
new v8::ScriptCompiler::CachedData(
reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length()));
Local<v8::UnboundScript> compiled_script =
v8::ScriptCompiler::CompileUnbound(isolate, &source2);
CHECK(try_catch.HasCaught());
{
String::Utf8Value exception_value(try_catch.Message()->Get());
CHECK_EQ("Uncaught SyntaxError: Invalid cached data for function bar",
*exception_value);
}
try_catch.Reset();
delete sd;
// Overwrite function bar's start position with 200. The function entry will
// not be found when searching for it by position, and the compilation fails.
// ScriptData does not take ownership of the buffers passed to it.
sd = i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length);
sd_data = reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
200;
const char* script3 = "function foo(){ return 7;}\n"
"function bar(){ return 6 + 7;} foo();";
v8::ScriptCompiler::Source source3(
v8_str(script3),
new v8::ScriptCompiler::CachedData(
reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length()));
compiled_script =
v8::ScriptCompiler::CompileUnbound(isolate, &source3);
CHECK(try_catch.HasCaught());
{
String::Utf8Value exception_value(try_catch.Message()->Get());
CHECK_EQ("Uncaught SyntaxError: Invalid cached data for function bar",
*exception_value);
}
CHECK(compiled_script.IsEmpty());
try_catch.Reset();
delete sd;
// Try passing in cached data which is obviously invalid (wrong length).
sd = i::ScriptData::New(reinterpret_cast<const char*>(cd->data), cd->length);
const char* script4 =
"function foo(){ return 8;}\n"
"function bar(){ return 6 + 7;} foo();";
v8::ScriptCompiler::Source source4(
v8_str(script4),
new v8::ScriptCompiler::CachedData(
reinterpret_cast<const uint8_t*>(sd->Data()), sd->Length() - 1));
compiled_script =
v8::ScriptCompiler::CompileUnbound(isolate, &source4);
CHECK(try_catch.HasCaught());
{
String::Utf8Value exception_value(try_catch.Message()->Get());
CHECK_EQ("Uncaught SyntaxError: Invalid cached data",
*exception_value);
}
CHECK(compiled_script.IsEmpty());
delete sd;
}
// This tests that we do not allow dictionary load/call inline caches
// to use functions that have not yet been compiled. The potential
// problem of loading a function that has not yet been compiled can

View File

@ -157,8 +157,7 @@ TEST(ScanHTMLEndComments) {
preparser.set_allow_lazy(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
CHECK(!data.has_error());
CHECK(!log.HasError());
}
for (int i = 0; fail_tests[i]; i++) {
@ -173,8 +172,7 @@ TEST(ScanHTMLEndComments) {
i::PreParser::PreParseResult result = preparser.PreParseProgram();
// Even in the case of a syntax error, kPreParseSuccess is returned.
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
CHECK(data.has_error());
CHECK(log.HasError());
}
}
@ -306,8 +304,7 @@ TEST(StandAlonePreParser) {
preparser.set_allow_natives_syntax(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
CHECK(!data.has_error());
CHECK(!log.HasError());
}
}
@ -339,9 +336,7 @@ TEST(StandAlonePreParserNoNatives) {
preparser.set_allow_lazy(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
// Data contains syntax error.
CHECK(data.has_error());
CHECK(log.HasError());
}
}
@ -407,8 +402,7 @@ TEST(RegressChromium62639) {
i::PreParser::PreParseResult result = preparser.PreParseProgram();
// Even in the case of a syntax error, kPreParseSuccess is returned.
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
CHECK(data.has_error());
CHECK(log.HasError());
}
@ -438,15 +432,15 @@ TEST(Regress928) {
preparser.set_allow_lazy(true);
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData data(log.ExtractData());
CHECK(!data.has_error());
data.Initialize();
i::ScriptData* sd = log.GetScriptData();
i::ParseData pd(sd);
pd.Initialize();
int first_function =
static_cast<int>(strstr(program, "function") - program);
int first_lbrace = first_function + i::StrLength("function () ");
CHECK_EQ('{', program[first_lbrace]);
i::FunctionEntry entry1 = data.GetFunctionEntry(first_lbrace);
i::FunctionEntry entry1 = pd.GetFunctionEntry(first_lbrace);
CHECK(!entry1.is_valid());
int second_function =
@ -454,9 +448,10 @@ TEST(Regress928) {
int second_lbrace =
second_function + i::StrLength("function () ");
CHECK_EQ('{', program[second_lbrace]);
i::FunctionEntry entry2 = data.GetFunctionEntry(second_lbrace);
i::FunctionEntry entry2 = pd.GetFunctionEntry(second_lbrace);
CHECK(entry2.is_valid());
CHECK_EQ('}', program[entry2.end_pos() - 1]);
delete sd;
}
@ -1134,20 +1129,41 @@ TEST(ScopePositions) {
}
i::Handle<i::String> FormatMessage(i::ScriptData* data) {
const char* ReadString(unsigned* start) {
int length = start[0];
char* result = i::NewArray<char>(length + 1);
for (int i = 0; i < length; i++) {
result[i] = start[i + 1];
}
result[length] = '\0';
return result;
}
i::Handle<i::String> FormatMessage(i::Vector<unsigned> data) {
i::Isolate* isolate = CcTest::i_isolate();
i::Factory* factory = isolate->factory();
const char* message = data->BuildMessage();
const char* message =
ReadString(&data[i::PreparseDataConstants::kMessageTextPos]);
i::Handle<i::String> format = v8::Utils::OpenHandle(
*v8::String::NewFromUtf8(CcTest::isolate(), message));
const char* arg = data->BuildArg();
i::Handle<i::JSArray> args_array = factory->NewJSArray(arg == NULL ? 0 : 1);
if (arg != NULL) {
i::JSArray::SetElement(
args_array, 0, v8::Utils::OpenHandle(*v8::String::NewFromUtf8(
CcTest::isolate(), arg)),
int arg_count = data[i::PreparseDataConstants::kMessageArgCountPos];
const char* arg = NULL;
i::Handle<i::JSArray> args_array;
if (arg_count == 1) {
// Position after text found by skipping past length field and
// length field content words.
int pos = i::PreparseDataConstants::kMessageTextPos + 1 +
data[i::PreparseDataConstants::kMessageTextPos];
arg = ReadString(&data[pos]);
args_array = factory->NewJSArray(1);
i::JSArray::SetElement(args_array, 0, v8::Utils::OpenHandle(*v8_str(arg)),
NONE, i::SLOPPY).Check();
} else {
CHECK_EQ(0, arg_count);
args_array = factory->NewJSArray(0);
}
i::Handle<i::JSObject> builtins(isolate->js_builtins_object());
i::Handle<i::Object> format_fun = i::Object::GetProperty(
isolate, builtins, "FormatMessage").ToHandleChecked();
@ -1157,6 +1173,7 @@ i::Handle<i::String> FormatMessage(i::ScriptData* data) {
CHECK(result->IsString());
i::DeleteArray(message);
i::DeleteArray(arg);
data.Dispose();
return i::Handle<i::String>::cast(result);
}
@ -1211,7 +1228,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
}
i::ScriptData data(log.ExtractData());
bool preparse_error = log.HasError();
// Parse the data
i::FunctionLiteral* function;
@ -1246,7 +1264,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
CHECK(false);
}
if (!data.has_error()) {
if (!preparse_error) {
v8::base::OS::Print(
"Parser failed on:\n"
"\t%s\n"
@ -1257,7 +1275,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
CHECK(false);
}
// Check that preparser and parser produce the same error.
i::Handle<i::String> preparser_message = FormatMessage(&data);
i::Handle<i::String> preparser_message =
FormatMessage(log.ErrorMessageData());
if (!i::String::Equals(message_string, preparser_message)) {
v8::base::OS::Print(
"Expected parser and preparser to produce the same error on:\n"
@ -1270,14 +1289,14 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
preparser_message->ToCString().get());
CHECK(false);
}
} else if (data.has_error()) {
} else if (preparse_error) {
v8::base::OS::Print(
"Preparser failed on:\n"
"\t%s\n"
"with error:\n"
"\t%s\n"
"However, the parser succeeded",
source->ToCString().get(), FormatMessage(&data)->ToCString().get());
source->ToCString().get(), FormatMessage(log.ErrorMessageData()));
CHECK(false);
} else if (result == kError) {
v8::base::OS::Print(
@ -2165,22 +2184,20 @@ TEST(DontRegressPreParserDataSizes) {
factory->NewStringFromUtf8(i::CStrVector(program)).ToHandleChecked();
i::Handle<i::Script> script = factory->NewScript(source);
i::CompilationInfoWithZone info(script);
i::ScriptData* data = NULL;
info.SetCachedData(&data, i::PRODUCE_CACHED_DATA);
i::ScriptData* sd = NULL;
info.SetCachedData(&sd, i::PRODUCE_CACHED_DATA);
i::Parser::Parse(&info, true);
CHECK(data);
CHECK(!data->HasError());
i::ParseData pd(sd);
if (data->function_count() != test_cases[i].functions) {
if (pd.FunctionCount() != test_cases[i].functions) {
v8::base::OS::Print(
"Expected preparse data for program:\n"
"\t%s\n"
"to contain %d functions, however, received %d functions.\n",
program, test_cases[i].functions,
data->function_count());
program, test_cases[i].functions, pd.FunctionCount());
CHECK(false);
}
delete data;
delete sd;
}
}

View File

@ -177,9 +177,9 @@ class FileByteSink : public SnapshotByteSink {
fclose(fp_);
}
}
virtual void Put(int byte, const char* description) {
virtual void Put(byte b, const char* description) {
if (fp_ != NULL) {
fputc(byte, fp_);
fputc(b, fp_);
}
}
virtual int Position() {