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:
parent
500b76d5be
commit
339bc81390
@ -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
|
||||
|
58
src/api.cc
58
src/api.cc
@ -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);
|
||||
|
@ -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)),
|
||||
|
@ -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_
|
||||
|
@ -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)"],
|
||||
|
@ -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);
|
||||
|
260
src/parser.cc
260
src/parser.cc
@ -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);
|
||||
|
101
src/parser.h
101
src/parser.h
@ -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_;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
Loading…
Reference in New Issue
Block a user