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).
|
* 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.
|
* \param source Script source code.
|
||||||
* \return Compiled script object (context independent; for running it must be
|
* \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,
|
Isolate* v8_isolate,
|
||||||
Source* source,
|
Source* source,
|
||||||
CompileOptions options) {
|
CompileOptions options) {
|
||||||
i::ScriptData* script_data_impl = NULL;
|
i::ScriptData* script_data = NULL;
|
||||||
i::CachedDataMode cached_data_mode = i::NO_CACHED_DATA;
|
i::CachedDataMode cached_data_mode = i::NO_CACHED_DATA;
|
||||||
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
||||||
ON_BAILOUT(isolate, "v8::ScriptCompiler::CompileUnbound()",
|
ON_BAILOUT(isolate, "v8::ScriptCompiler::CompileUnbound()",
|
||||||
return Local<UnboundScript>());
|
return Local<UnboundScript>());
|
||||||
if (options & kProduceDataToCache) {
|
if (options & kProduceDataToCache) {
|
||||||
cached_data_mode = i::PRODUCE_CACHED_DATA;
|
cached_data_mode = i::PRODUCE_CACHED_DATA;
|
||||||
ASSERT(source->cached_data == NULL);
|
CHECK(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>());
|
|
||||||
}
|
|
||||||
} else if (source->cached_data) {
|
} else if (source->cached_data) {
|
||||||
cached_data_mode = i::CONSUME_CACHED_DATA;
|
cached_data_mode = i::CONSUME_CACHED_DATA;
|
||||||
// ScriptData takes care of aligning, in case the data is not aligned
|
// ScriptData takes care of pointer-aligning the data.
|
||||||
// correctly.
|
script_data = new i::ScriptData(source->cached_data->data,
|
||||||
script_data_impl = i::ScriptData::New(
|
|
||||||
reinterpret_cast<const char*>(source->cached_data->data),
|
|
||||||
source->cached_data->length);
|
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));
|
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);
|
source->resource_is_shared_cross_origin == v8::True(v8_isolate);
|
||||||
}
|
}
|
||||||
EXCEPTION_PREAMBLE(isolate);
|
EXCEPTION_PREAMBLE(isolate);
|
||||||
i::Handle<i::SharedFunctionInfo> result =
|
i::Handle<i::SharedFunctionInfo> result = i::Compiler::CompileScript(
|
||||||
i::Compiler::CompileScript(str,
|
str, name_obj, line_offset, column_offset, is_shared_cross_origin,
|
||||||
name_obj,
|
isolate->global_context(), NULL, &script_data, cached_data_mode,
|
||||||
line_offset,
|
|
||||||
column_offset,
|
|
||||||
is_shared_cross_origin,
|
|
||||||
isolate->global_context(),
|
|
||||||
NULL,
|
|
||||||
&script_data_impl,
|
|
||||||
cached_data_mode,
|
|
||||||
i::NOT_NATIVES_CODE);
|
i::NOT_NATIVES_CODE);
|
||||||
has_pending_exception = result.is_null();
|
has_pending_exception = result.is_null();
|
||||||
if (has_pending_exception && cached_data_mode == i::CONSUME_CACHED_DATA) {
|
if (has_pending_exception && cached_data_mode == i::CONSUME_CACHED_DATA) {
|
||||||
// This case won't happen during normal operation; we have compiled
|
// This case won't happen during normal operation; we have compiled
|
||||||
// successfully and produced cached data, and but the second compilation
|
// successfully and produced cached data, and but the second compilation
|
||||||
// of the same source code fails.
|
// of the same source code fails.
|
||||||
delete script_data_impl;
|
delete script_data;
|
||||||
script_data_impl = NULL;
|
script_data = NULL;
|
||||||
}
|
}
|
||||||
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
|
EXCEPTION_BAILOUT_CHECK(isolate, Local<UnboundScript>());
|
||||||
raw_result = *result;
|
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
|
// script_data_impl now contains the data that was generated. source will
|
||||||
// take the ownership.
|
// take the ownership.
|
||||||
source->cached_data = new CachedData(
|
source->cached_data = new CachedData(
|
||||||
reinterpret_cast<const uint8_t*>(script_data_impl->Data()),
|
script_data->data(), script_data->length(), CachedData::BufferOwned);
|
||||||
script_data_impl->Length(), CachedData::BufferOwned);
|
script_data->ReleaseDataOwnership();
|
||||||
script_data_impl->owns_store_ = false;
|
|
||||||
}
|
}
|
||||||
delete script_data_impl;
|
delete script_data;
|
||||||
}
|
}
|
||||||
i::Handle<i::SharedFunctionInfo> result(raw_result, isolate);
|
i::Handle<i::SharedFunctionInfo> result(raw_result, isolate);
|
||||||
return ToApiHandle<UnboundScript>(result);
|
return ToApiHandle<UnboundScript>(result);
|
||||||
|
@ -31,6 +31,18 @@ namespace v8 {
|
|||||||
namespace internal {
|
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,
|
CompilationInfo::CompilationInfo(Handle<Script> script,
|
||||||
Zone* zone)
|
Zone* zone)
|
||||||
: flags_(StrictModeField::encode(SLOPPY)),
|
: flags_(StrictModeField::encode(SLOPPY)),
|
||||||
|
@ -13,7 +13,6 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class AstValueFactory;
|
class AstValueFactory;
|
||||||
class ScriptData;
|
|
||||||
class HydrogenCodeStub;
|
class HydrogenCodeStub;
|
||||||
|
|
||||||
// ParseRestriction is used to restrict the set of valid statements in a
|
// ParseRestriction is used to restrict the set of valid statements in a
|
||||||
@ -35,6 +34,36 @@ struct OffsetRange {
|
|||||||
int to;
|
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
|
// CompilationInfo encapsulates some information known at compile time. It
|
||||||
// is constructed based on the resources available at compile-time.
|
// is constructed based on the resources available at compile-time.
|
||||||
class CompilationInfo {
|
class CompilationInfo {
|
||||||
@ -722,7 +751,6 @@ class CompilationPhase BASE_EMBEDDED {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(CompilationPhase);
|
DISALLOW_COPY_AND_ASSIGN(CompilationPhase);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
|
||||||
#endif // V8_COMPILER_H_
|
#endif // V8_COMPILER_H_
|
||||||
|
@ -137,8 +137,6 @@ var kMessages = {
|
|||||||
array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
|
array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"],
|
||||||
object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
|
object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"],
|
||||||
illegal_access: ["Illegal access"],
|
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_mode_with: ["Strict mode code may not include a with statement"],
|
||||||
strict_eval_arguments: ["Unexpected eval or arguments in strict mode"],
|
strict_eval_arguments: ["Unexpected eval or arguments in strict mode"],
|
||||||
too_many_arguments: ["Too many arguments in function call (only 65535 allowed)"],
|
too_many_arguments: ["Too many arguments in function call (only 65535 allowed)"],
|
||||||
|
@ -27,8 +27,8 @@ using namespace v8;
|
|||||||
class Compressor {
|
class Compressor {
|
||||||
public:
|
public:
|
||||||
virtual ~Compressor() {}
|
virtual ~Compressor() {}
|
||||||
virtual bool Compress(i::Vector<char> input) = 0;
|
virtual bool Compress(i::Vector<i::byte> input) = 0;
|
||||||
virtual i::Vector<char>* output() = 0;
|
virtual i::Vector<i::byte>* output() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -63,9 +63,9 @@ class SnapshotWriter {
|
|||||||
startup_blob_file_ = GetFileDescriptorOrDie(startup_blob_file);
|
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::Serializer& serializer,
|
||||||
const i::List<char>& context_snapshot_data,
|
const i::List<i::byte>& context_snapshot_data,
|
||||||
const i::Serializer& context_serializer) const {
|
const i::Serializer& context_serializer) const {
|
||||||
WriteSnapshotFile(snapshot_data, serializer,
|
WriteSnapshotFile(snapshot_data, serializer,
|
||||||
context_snapshot_data, context_serializer);
|
context_snapshot_data, context_serializer);
|
||||||
@ -74,14 +74,14 @@ class SnapshotWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MaybeWriteStartupBlob(const i::List<char>& snapshot_data,
|
void MaybeWriteStartupBlob(const i::List<i::byte>& snapshot_data,
|
||||||
const i::Serializer& serializer,
|
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 {
|
const i::Serializer& context_serializer) const {
|
||||||
if (!startup_blob_file_)
|
if (!startup_blob_file_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
i::List<char> startup_blob;
|
i::List<i::byte> startup_blob;
|
||||||
i::ListSnapshotSink sink(&startup_blob);
|
i::ListSnapshotSink sink(&startup_blob);
|
||||||
|
|
||||||
int spaces[] = {
|
int spaces[] = {
|
||||||
@ -89,13 +89,12 @@ class SnapshotWriter {
|
|||||||
i::MAP_SPACE, i::CELL_SPACE, i::PROPERTY_CELL_SPACE
|
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");
|
sink.PutBlob(snapshot_bytes, snapshot_data.length(), "snapshot");
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
|
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
|
||||||
sink.PutInt(serializer.CurrentAllocationAddress(spaces[i]), "spaces");
|
sink.PutInt(serializer.CurrentAllocationAddress(spaces[i]), "spaces");
|
||||||
|
|
||||||
i::byte* context_bytes =
|
i::byte* context_bytes = context_snapshot_data.begin();
|
||||||
reinterpret_cast<i::byte*>(context_snapshot_data.begin());
|
|
||||||
sink.PutBlob(context_bytes, context_snapshot_data.length(), "context");
|
sink.PutBlob(context_bytes, context_snapshot_data.length(), "context");
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
|
for (size_t i = 0; i < ARRAY_SIZE(spaces); ++i)
|
||||||
sink.PutInt(context_serializer.CurrentAllocationAddress(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::Serializer& serializer,
|
||||||
const i::List<char>& context_snapshot_data,
|
const i::List<i::byte>& context_snapshot_data,
|
||||||
const i::Serializer& context_serializer) const {
|
const i::Serializer& context_serializer) const {
|
||||||
WriteFilePrefix();
|
WriteFilePrefix();
|
||||||
WriteData("", snapshot_data, raw_file_);
|
WriteData("", snapshot_data, raw_file_);
|
||||||
@ -135,11 +134,10 @@ class SnapshotWriter {
|
|||||||
fprintf(fp_, "} // namespace v8\n");
|
fprintf(fp_, "} // namespace v8\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteData(const char* prefix,
|
void WriteData(const char* prefix, const i::List<i::byte>& source_data,
|
||||||
const i::List<char>& source_data,
|
|
||||||
FILE* raw_file) const {
|
FILE* raw_file) const {
|
||||||
const i::List <char>* data_to_be_written = NULL;
|
const i::List<i::byte>* data_to_be_written = NULL;
|
||||||
i::List<char> compressed_data;
|
i::List<i::byte> compressed_data;
|
||||||
if (!compressor_) {
|
if (!compressor_) {
|
||||||
data_to_be_written = &source_data;
|
data_to_be_written = &source_data;
|
||||||
} else if (compressor_->Compress(source_data.ToVector())) {
|
} else if (compressor_->Compress(source_data.ToVector())) {
|
||||||
@ -155,7 +153,7 @@ class SnapshotWriter {
|
|||||||
WriteData(prefix, source_data, data_to_be_written);
|
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)
|
if (!data || !raw_file)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -170,9 +168,8 @@ class SnapshotWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteData(const char* prefix,
|
void WriteData(const char* prefix, const i::List<i::byte>& source_data,
|
||||||
const i::List<char>& source_data,
|
const i::List<i::byte>* data_to_be_written) const {
|
||||||
const i::List<char>* data_to_be_written) const {
|
|
||||||
fprintf(fp_, "const byte Snapshot::%sdata_[] = {\n", prefix);
|
fprintf(fp_, "const byte Snapshot::%sdata_[] = {\n", prefix);
|
||||||
WriteSnapshotData(data_to_be_written);
|
WriteSnapshotData(data_to_be_written);
|
||||||
fprintf(fp_, "};\n");
|
fprintf(fp_, "};\n");
|
||||||
@ -209,7 +206,7 @@ class SnapshotWriter {
|
|||||||
prefix, name, ser.CurrentAllocationAddress(space));
|
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++) {
|
for (int i = 0; i < data->length(); i++) {
|
||||||
if ((i & 0x1f) == 0x1f)
|
if ((i & 0x1f) == 0x1f)
|
||||||
fprintf(fp_, "\n");
|
fprintf(fp_, "\n");
|
||||||
@ -405,12 +402,12 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
// This results in a somewhat smaller snapshot, probably because it gets
|
// This results in a somewhat smaller snapshot, probably because it gets
|
||||||
// rid of some things that are cached between garbage collections.
|
// 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::ListSnapshotSink snapshot_sink(&snapshot_data);
|
||||||
i::StartupSerializer ser(internal_isolate, &snapshot_sink);
|
i::StartupSerializer ser(internal_isolate, &snapshot_sink);
|
||||||
ser.SerializeStrongReferences();
|
ser.SerializeStrongReferences();
|
||||||
|
|
||||||
i::List<char> context_data;
|
i::List<i::byte> context_data;
|
||||||
i::ListSnapshotSink contex_sink(&context_data);
|
i::ListSnapshotSink contex_sink(&context_data);
|
||||||
i::PartialSerializer context_ser(internal_isolate, &ser, &contex_sink);
|
i::PartialSerializer context_ser(internal_isolate, &ser, &contex_sink);
|
||||||
context_ser.Serialize(&raw_context);
|
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) {
|
FunctionEntry ParseData::GetFunctionEntry(int start) {
|
||||||
// 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) {
|
|
||||||
// The current pre-data entry must be a FunctionEntry with the given
|
// The current pre-data entry must be a FunctionEntry with the given
|
||||||
// start position.
|
// start position.
|
||||||
if ((function_index_ + FunctionEntry::kSize <= store_.length())
|
if ((function_index_ + FunctionEntry::kSize <= Length()) &&
|
||||||
&& (static_cast<int>(store_[function_index_]) == start)) {
|
(static_cast<int>(Data()[function_index_]) == start)) {
|
||||||
int index = function_index_;
|
int index = function_index_;
|
||||||
function_index_ += FunctionEntry::kSize;
|
function_index_ += FunctionEntry::kSize;
|
||||||
return FunctionEntry(store_.SubVector(index,
|
Vector<unsigned> subvector(&(Data()[index]), FunctionEntry::kSize);
|
||||||
index + FunctionEntry::kSize));
|
return FunctionEntry(subvector);
|
||||||
}
|
}
|
||||||
return FunctionEntry();
|
return FunctionEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int ScriptData::GetSymbolIdentifier() {
|
int ParseData::FunctionCount() {
|
||||||
return ReadNumber(&symbol_data_);
|
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
|
// Check that the header data is valid and doesn't specify
|
||||||
// point to positions outside the store.
|
// point to positions outside the store.
|
||||||
if (store_.length() < PreparseDataConstants::kHeaderSize) return false;
|
int data_length = Length();
|
||||||
if (magic() != PreparseDataConstants::kMagicNumber) return false;
|
if (data_length < PreparseDataConstants::kHeaderSize) return false;
|
||||||
if (version() != PreparseDataConstants::kCurrentVersion) return false;
|
if (Magic() != PreparseDataConstants::kMagicNumber) return false;
|
||||||
if (has_error()) {
|
if (Version() != PreparseDataConstants::kCurrentVersion) return false;
|
||||||
// Extra sane sanity check for error message encoding.
|
if (HasError()) return false;
|
||||||
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;
|
|
||||||
}
|
|
||||||
// Check that the space allocated for function entries is sane.
|
// Check that the space allocated for function entries is sane.
|
||||||
int functions_size =
|
int functions_size = FunctionsSize();
|
||||||
static_cast<int>(store_[PreparseDataConstants::kFunctionsSizeOffset]);
|
|
||||||
if (functions_size < 0) return false;
|
if (functions_size < 0) return false;
|
||||||
if (functions_size % FunctionEntry::kSize != 0) return false;
|
if (functions_size % FunctionEntry::kSize != 0) return false;
|
||||||
// Check that the total size has room for header and function entries.
|
// Check that the total size has room for header and function entries.
|
||||||
int minimum_size =
|
int minimum_size =
|
||||||
PreparseDataConstants::kHeaderSize + functions_size;
|
PreparseDataConstants::kHeaderSize + functions_size;
|
||||||
if (store_.length() < minimum_size) return false;
|
if (data_length < minimum_size) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ParseData::Initialize() {
|
||||||
const char* ScriptData::ReadString(unsigned* start, int* chars) {
|
// Prepares state for use.
|
||||||
int length = start[0];
|
int data_length = Length();
|
||||||
char* result = NewArray<char>(length + 1);
|
if (data_length >= PreparseDataConstants::kHeaderSize) {
|
||||||
for (int i = 0; i < length; i++) {
|
function_index_ = PreparseDataConstants::kHeaderSize;
|
||||||
result[i] = start[i + 1];
|
|
||||||
}
|
}
|
||||||
result[length] = '\0';
|
|
||||||
if (chars != NULL) *chars = length;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Scanner::Location ScriptData::MessageLocation() const {
|
bool ParseData::HasError() {
|
||||||
int beg_pos = Read(PreparseDataConstants::kMessageStartPos);
|
return Data()[PreparseDataConstants::kHasErrorOffset];
|
||||||
int end_pos = Read(PreparseDataConstants::kMessageEndPos);
|
|
||||||
return Scanner::Location(beg_pos, end_pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ScriptData::IsReferenceError() const {
|
unsigned ParseData::Magic() {
|
||||||
return Read(PreparseDataConstants::kIsReferenceErrorPos);
|
return Data()[PreparseDataConstants::kMagicOffset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* ScriptData::BuildMessage() const {
|
unsigned ParseData::Version() {
|
||||||
unsigned* start = ReadAddress(PreparseDataConstants::kMessageTextPos);
|
return Data()[PreparseDataConstants::kVersionOffset];
|
||||||
return ReadString(start, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const char* ScriptData::BuildArg() const {
|
int ParseData::FunctionsSize() {
|
||||||
int arg_count = Read(PreparseDataConstants::kMessageArgCountPos);
|
return static_cast<int>(Data()[PreparseDataConstants::kFunctionsSizeOffset]);
|
||||||
ASSERT(arg_count == 0 || arg_count == 1);
|
}
|
||||||
if (arg_count == 0) {
|
|
||||||
return NULL;
|
|
||||||
|
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)
|
Parser::Parser(CompilationInfo* info)
|
||||||
: ParserBase<ParserTraits>(&scanner_,
|
: ParserBase<ParserTraits>(&scanner_,
|
||||||
info->isolate()->stack_guard()->real_climit(),
|
info->isolate()->stack_guard()->real_climit(),
|
||||||
info->extension(),
|
info->extension(), NULL, info->zone(), this),
|
||||||
NULL,
|
|
||||||
info->zone(),
|
|
||||||
this),
|
|
||||||
isolate_(info->isolate()),
|
isolate_(info->isolate()),
|
||||||
script_(info->script()),
|
script_(info->script()),
|
||||||
scanner_(isolate_->unicode_cache()),
|
scanner_(isolate_->unicode_cache()),
|
||||||
reusable_preparser_(NULL),
|
reusable_preparser_(NULL),
|
||||||
original_scope_(NULL),
|
original_scope_(NULL),
|
||||||
target_stack_(NULL),
|
target_stack_(NULL),
|
||||||
cached_data_(NULL),
|
cached_parse_data_(NULL),
|
||||||
cached_data_mode_(NO_CACHED_DATA),
|
|
||||||
ast_value_factory_(NULL),
|
ast_value_factory_(NULL),
|
||||||
info_(info),
|
info_(info),
|
||||||
has_pending_error_(false),
|
has_pending_error_(false),
|
||||||
@ -805,10 +740,10 @@ FunctionLiteral* Parser::ParseProgram() {
|
|||||||
|
|
||||||
// Initialize parser state.
|
// Initialize parser state.
|
||||||
CompleteParserRecorder recorder;
|
CompleteParserRecorder recorder;
|
||||||
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
||||||
log_ = &recorder;
|
log_ = &recorder;
|
||||||
} else if (cached_data_mode_ == CONSUME_CACHED_DATA) {
|
} else if (cached_data_mode() == CONSUME_CACHED_DATA) {
|
||||||
(*cached_data_)->Initialize();
|
cached_parse_data_->Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
source = String::Flatten(source);
|
source = String::Flatten(source);
|
||||||
@ -840,11 +775,8 @@ FunctionLiteral* Parser::ParseProgram() {
|
|||||||
}
|
}
|
||||||
PrintF(" - took %0.3f ms]\n", ms);
|
PrintF(" - took %0.3f ms]\n", ms);
|
||||||
}
|
}
|
||||||
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
||||||
if (result != NULL) {
|
if (result != NULL) *info_->cached_data() = recorder.GetScriptData();
|
||||||
Vector<unsigned> store = recorder.ExtractData();
|
|
||||||
*cached_data_ = new ScriptData(store);
|
|
||||||
}
|
|
||||||
log_ = NULL;
|
log_ = NULL;
|
||||||
}
|
}
|
||||||
return result;
|
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) {
|
bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
|
||||||
if (expression->IsLiteral()) return true;
|
if (expression->IsLiteral()) return true;
|
||||||
MaterializedLiteral* lit = expression->AsMaterializedLiteral();
|
MaterializedLiteral* lit = expression->AsMaterializedLiteral();
|
||||||
@ -3614,19 +3540,15 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
|
|||||||
int* expected_property_count,
|
int* expected_property_count,
|
||||||
bool* ok) {
|
bool* ok) {
|
||||||
int function_block_pos = position();
|
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
|
// 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.
|
// data contains the information we need to construct the lazy function.
|
||||||
FunctionEntry entry =
|
FunctionEntry entry =
|
||||||
(*cached_data())->GetFunctionEntry(function_block_pos);
|
cached_parse_data_->GetFunctionEntry(function_block_pos);
|
||||||
if (entry.is_valid()) {
|
// Check that cached data is valid.
|
||||||
if (entry.end_pos() <= function_block_pos) {
|
CHECK(entry.is_valid());
|
||||||
// End position greater than end of stream is safe, and hard to check.
|
// End position greater than end of stream is safe, and hard to check.
|
||||||
ReportInvalidCachedData(function_name, ok);
|
CHECK(entry.end_pos() > function_block_pos);
|
||||||
if (!*ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scanner()->SeekForward(entry.end_pos() - 1);
|
scanner()->SeekForward(entry.end_pos() - 1);
|
||||||
|
|
||||||
scope_->set_end_position(entry.end_pos());
|
scope_->set_end_position(entry.end_pos());
|
||||||
@ -3639,12 +3561,6 @@ void Parser::SkipLazyFunctionBody(const AstRawString* function_name,
|
|||||||
*materialized_literal_count = entry.literal_count();
|
*materialized_literal_count = entry.literal_count();
|
||||||
*expected_property_count = entry.property_count();
|
*expected_property_count = entry.property_count();
|
||||||
scope_->SetStrictMode(entry.strict_mode());
|
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 {
|
} else {
|
||||||
// With no cached data, we partially parse the function, without building an
|
// With no cached data, we partially parse the function, without building an
|
||||||
// AST. This gathers the data needed to build a lazy function.
|
// 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();
|
*materialized_literal_count = logger.literals();
|
||||||
*expected_property_count = logger.properties();
|
*expected_property_count = logger.properties();
|
||||||
scope_->SetStrictMode(logger.strict_mode());
|
scope_->SetStrictMode(logger.strict_mode());
|
||||||
if (cached_data_mode_ == PRODUCE_CACHED_DATA) {
|
if (cached_data_mode() == PRODUCE_CACHED_DATA) {
|
||||||
ASSERT(log_);
|
ASSERT(log_);
|
||||||
// Position right after terminal '}'.
|
// Position right after terminal '}'.
|
||||||
int body_end = scanner()->location().end_pos;
|
int body_end = scanner()->location().end_pos;
|
||||||
@ -4766,70 +4682,6 @@ RegExpTree* RegExpParser::ParseCharacterClass() {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// The Parser interface.
|
// 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 RegExpParser::ParseRegExp(FlatStringReader* input,
|
||||||
bool multiline,
|
bool multiline,
|
||||||
RegExpCompileData* result,
|
RegExpCompileData* result,
|
||||||
@ -4876,7 +4728,7 @@ bool Parser::Parse() {
|
|||||||
result = ParseProgram();
|
result = ParseProgram();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
SetCachedData(info()->cached_data(), info()->cached_data_mode());
|
SetCachedData();
|
||||||
result = ParseProgram();
|
result = ParseProgram();
|
||||||
}
|
}
|
||||||
info()->SetFunction(result);
|
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:
|
public:
|
||||||
explicit ScriptData(Vector<unsigned> store)
|
explicit ParseData(ScriptData* script_data) : script_data_(script_data) {
|
||||||
: store_(store),
|
CHECK(IsAligned(script_data->length(), sizeof(unsigned)));
|
||||||
owns_store_(true) { }
|
CHECK(IsSane());
|
||||||
|
}
|
||||||
ScriptData(Vector<unsigned> store, bool owns_store)
|
void Initialize();
|
||||||
: store_(store),
|
FunctionEntry GetFunctionEntry(int start);
|
||||||
owns_store_(owns_store) { }
|
int FunctionCount();
|
||||||
|
|
||||||
// The created ScriptData won't take ownership of the data. If the alignment
|
bool HasError();
|
||||||
// is not correct, this will copy the data (and the created ScriptData will
|
|
||||||
// take ownership of the copy).
|
unsigned* Data() { // Writable data as unsigned int array.
|
||||||
static ScriptData* New(const char* data, int length, bool owns_store = false);
|
return reinterpret_cast<unsigned*>(const_cast<byte*>(script_data_->data()));
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
// 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:
|
private:
|
||||||
// Disable copying and assigning; because of owns_store they won't be correct.
|
bool IsSane();
|
||||||
ScriptData(const ScriptData&);
|
unsigned Magic();
|
||||||
ScriptData& operator=(const ScriptData&);
|
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;
|
ScriptData* script_data_;
|
||||||
Vector<unsigned> store_;
|
|
||||||
unsigned char* symbol_data_;
|
|
||||||
unsigned char* symbol_data_end_;
|
|
||||||
int function_index_;
|
int function_index_;
|
||||||
bool owns_store_;
|
|
||||||
|
|
||||||
unsigned Read(int position) const;
|
DISALLOW_COPY_AND_ASSIGN(ParseData);
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// REGEXP PARSING
|
// REGEXP PARSING
|
||||||
|
|
||||||
@ -646,23 +612,10 @@ class Parser : public ParserBase<ParserTraits> {
|
|||||||
FunctionLiteral* DoParseProgram(CompilationInfo* info,
|
FunctionLiteral* DoParseProgram(CompilationInfo* info,
|
||||||
Handle<String> source);
|
Handle<String> source);
|
||||||
|
|
||||||
// Report syntax error
|
void SetCachedData();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inside_with() const { return scope_->inside_with(); }
|
bool inside_with() const { return scope_->inside_with(); }
|
||||||
ScriptData** cached_data() const { return cached_data_; }
|
CachedDataMode cached_data_mode() const { return info_->cached_data_mode(); }
|
||||||
CachedDataMode cached_data_mode() const { return cached_data_mode_; }
|
|
||||||
Scope* DeclarationScope(VariableMode mode) {
|
Scope* DeclarationScope(VariableMode mode) {
|
||||||
return IsLexicalVariableMode(mode)
|
return IsLexicalVariableMode(mode)
|
||||||
? scope_ : scope_->DeclarationScope();
|
? scope_ : scope_->DeclarationScope();
|
||||||
@ -809,7 +762,7 @@ class Parser : public ParserBase<ParserTraits> {
|
|||||||
PreParser* reusable_preparser_;
|
PreParser* reusable_preparser_;
|
||||||
Scope* original_scope_; // for ES5 function declarations in sloppy eval
|
Scope* original_scope_; // for ES5 function declarations in sloppy eval
|
||||||
Target* target_stack_; // for break, continue statements
|
Target* target_stack_; // for break, continue statements
|
||||||
ScriptData** cached_data_;
|
ParseData* cached_parse_data_;
|
||||||
CachedDataMode cached_data_mode_;
|
CachedDataMode cached_data_mode_;
|
||||||
AstValueFactory* ast_value_factory_;
|
AstValueFactory* ast_value_factory_;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "include/v8stdint.h"
|
#include "include/v8stdint.h"
|
||||||
#include "src/base/logging.h"
|
#include "src/base/logging.h"
|
||||||
|
#include "src/compiler.h"
|
||||||
#include "src/globals.h"
|
#include "src/globals.h"
|
||||||
#include "src/hashmap.h"
|
#include "src/hashmap.h"
|
||||||
#include "src/preparse-data.h"
|
#include "src/preparse-data.h"
|
||||||
@ -34,7 +35,7 @@ void CompleteParserRecorder::LogMessage(int start_pos,
|
|||||||
const char* message,
|
const char* message,
|
||||||
const char* arg_opt,
|
const char* arg_opt,
|
||||||
bool is_reference_error) {
|
bool is_reference_error) {
|
||||||
if (has_error()) return;
|
if (HasError()) return;
|
||||||
preamble_[PreparseDataConstants::kHasErrorOffset] = true;
|
preamble_[PreparseDataConstants::kHasErrorOffset] = true;
|
||||||
function_store_.Reset();
|
function_store_.Reset();
|
||||||
STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0);
|
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 function_size = function_store_.size();
|
||||||
int total_size = PreparseDataConstants::kHeaderSize + function_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;
|
preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size;
|
||||||
MemCopy(data.start(), preamble_, sizeof(preamble_));
|
MemCopy(data, preamble_, sizeof(preamble_));
|
||||||
if (function_size > 0) {
|
if (function_size > 0) {
|
||||||
function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize,
|
function_store_.WriteTo(Vector<unsigned>(
|
||||||
total_size));
|
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 v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
class ScriptData;
|
||||||
|
|
||||||
|
|
||||||
// Abstract interface for preparse data recorder.
|
// Abstract interface for preparse data recorder.
|
||||||
class ParserRecorder {
|
class ParserRecorder {
|
||||||
@ -149,13 +151,17 @@ class CompleteParserRecorder : public ParserRecorder {
|
|||||||
const char* message,
|
const char* message,
|
||||||
const char* argument_opt,
|
const char* argument_opt,
|
||||||
bool is_reference_error_);
|
bool is_reference_error_);
|
||||||
Vector<unsigned> ExtractData();
|
ScriptData* GetScriptData();
|
||||||
|
|
||||||
private:
|
bool HasError() {
|
||||||
bool has_error() {
|
|
||||||
return static_cast<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]);
|
return static_cast<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]);
|
||||||
}
|
}
|
||||||
|
Vector<unsigned> ErrorMessageData() {
|
||||||
|
ASSERT(HasError());
|
||||||
|
return function_store_.ToVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
void WriteString(Vector<const char> str);
|
void WriteString(Vector<const char> str);
|
||||||
|
|
||||||
// Write a non-negative number to the symbol store.
|
// 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() {
|
void Serializer::Pad() {
|
||||||
// The non-branching GetInt will read up to 3 bytes too far, so we need
|
// 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.
|
// 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) {
|
ScriptData* CodeSerializer::Serialize(Handle<SharedFunctionInfo> info) {
|
||||||
// Serialize code object.
|
// Serialize code object.
|
||||||
List<char> payload;
|
List<byte> payload;
|
||||||
ListSnapshotSink listsink(&payload);
|
ListSnapshotSink list_sink(&payload);
|
||||||
CodeSerializer ser(info->GetIsolate(), &listsink);
|
CodeSerializer cs(info->GetIsolate(), &list_sink);
|
||||||
DisallowHeapAllocation no_gc;
|
DisallowHeapAllocation no_gc;
|
||||||
Object** location = Handle<Object>::cast(info).location();
|
Object** location = Handle<Object>::cast(info).location();
|
||||||
ser.VisitPointer(location);
|
cs.VisitPointer(location);
|
||||||
ser.Pad();
|
cs.Pad();
|
||||||
|
|
||||||
// Allocate storage. The payload length may not be aligned. Round up.
|
SerializedCodeData data(&payload, &cs);
|
||||||
// TODO(yangguo) replace ScriptData with a more generic super class.
|
return data.GetScriptData();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1892,20 +1874,13 @@ void CodeSerializer::SerializeObject(Object* o, HowToCode how_to_code,
|
|||||||
|
|
||||||
|
|
||||||
Object* CodeSerializer::Deserialize(Isolate* isolate, ScriptData* data) {
|
Object* CodeSerializer::Deserialize(Isolate* isolate, ScriptData* data) {
|
||||||
const unsigned* raw_data = reinterpret_cast<const unsigned*>(data->Data());
|
SerializedCodeData scd(data);
|
||||||
CHECK_EQ(Version::Hash(), raw_data[kVersionHashOffset]);
|
SnapshotByteSource payload(scd.Payload(), scd.PayloadLength());
|
||||||
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);
|
|
||||||
Deserializer deserializer(&payload);
|
Deserializer deserializer(&payload);
|
||||||
STATIC_ASSERT(NEW_SPACE == 0);
|
STATIC_ASSERT(NEW_SPACE == 0);
|
||||||
// TODO(yangguo) what happens if remaining new space is too small?
|
// TODO(yangguo) what happens if remaining new space is too small?
|
||||||
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
|
for (int i = NEW_SPACE; i <= PROPERTY_CELL_SPACE; i++) {
|
||||||
deserializer.set_reservation(
|
deserializer.set_reservation(i, scd.GetReservation(i));
|
||||||
i, raw_data[CodeSerializer::kReservationsOffset + i]);
|
|
||||||
}
|
}
|
||||||
Object* root;
|
Object* root;
|
||||||
deserializer.DeserializePartial(isolate, &root);
|
deserializer.DeserializePartial(isolate, &root);
|
||||||
@ -1913,4 +1888,27 @@ Object* CodeSerializer::Deserialize(Isolate* isolate, ScriptData* data) {
|
|||||||
ASSERT(root->IsSharedFunctionInfo());
|
ASSERT(root->IsSharedFunctionInfo());
|
||||||
return root;
|
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
|
} } // namespace v8::internal
|
||||||
|
@ -5,10 +5,10 @@
|
|||||||
#ifndef V8_SERIALIZE_H_
|
#ifndef V8_SERIALIZE_H_
|
||||||
#define V8_SERIALIZE_H_
|
#define V8_SERIALIZE_H_
|
||||||
|
|
||||||
|
#include "src/compiler.h"
|
||||||
#include "src/hashmap.h"
|
#include "src/hashmap.h"
|
||||||
#include "src/heap-profiler.h"
|
#include "src/heap-profiler.h"
|
||||||
#include "src/isolate.h"
|
#include "src/isolate.h"
|
||||||
#include "src/parser.h"
|
|
||||||
#include "src/snapshot-source-sink.h"
|
#include "src/snapshot-source-sink.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -470,7 +470,6 @@ class Serializer : public SerializerDeserializer {
|
|||||||
SerializationAddressMapper address_mapper_;
|
SerializationAddressMapper address_mapper_;
|
||||||
intptr_t root_index_wave_front_;
|
intptr_t root_index_wave_front_;
|
||||||
void Pad();
|
void Pad();
|
||||||
void PadByte();
|
|
||||||
|
|
||||||
friend class ObjectSerializer;
|
friend class ObjectSerializer;
|
||||||
friend class Deserializer;
|
friend class Deserializer;
|
||||||
@ -576,6 +575,67 @@ class CodeSerializer : public Serializer {
|
|||||||
static const int kPayloadLengthOffset = 1;
|
static const int kPayloadLengthOffset = 1;
|
||||||
static const int kReservationsOffset = 2;
|
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
|
} } // namespace v8::internal
|
||||||
|
|
||||||
#endif // V8_SERIALIZE_H_
|
#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) {
|
void DebugSnapshotSink::Put(byte b, const char* description) {
|
||||||
PrintF("%24s: %x\n", description, byte);
|
PrintF("%24s: %x\n", description, b);
|
||||||
sink_->Put(byte, description);
|
sink_->Put(b, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace v8::internal
|
} // namespace v8::internal
|
||||||
|
@ -71,9 +71,10 @@ class SnapshotByteSource V8_FINAL {
|
|||||||
class SnapshotByteSink {
|
class SnapshotByteSink {
|
||||||
public:
|
public:
|
||||||
virtual ~SnapshotByteSink() { }
|
virtual ~SnapshotByteSink() { }
|
||||||
virtual void Put(int byte, const char* description) = 0;
|
virtual void Put(byte b, const char* description) = 0;
|
||||||
virtual void PutSection(int byte, const char* description) {
|
virtual void PutSection(int b, const char* description) {
|
||||||
Put(byte, description);
|
ASSERT_LE(b, kMaxUInt8);
|
||||||
|
Put(static_cast<byte>(b), description);
|
||||||
}
|
}
|
||||||
void PutInt(uintptr_t integer, const char* description);
|
void PutInt(uintptr_t integer, const char* description);
|
||||||
void PutRaw(byte* data, int number_of_bytes, const char* description);
|
void PutRaw(byte* data, int number_of_bytes, const char* description);
|
||||||
@ -86,7 +87,7 @@ class DummySnapshotSink : public SnapshotByteSink {
|
|||||||
public:
|
public:
|
||||||
DummySnapshotSink() : length_(0) {}
|
DummySnapshotSink() : length_(0) {}
|
||||||
virtual ~DummySnapshotSink() {}
|
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_; }
|
virtual int Position() { return length_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -98,7 +99,7 @@ class DummySnapshotSink : public SnapshotByteSink {
|
|||||||
class DebugSnapshotSink : public SnapshotByteSink {
|
class DebugSnapshotSink : public SnapshotByteSink {
|
||||||
public:
|
public:
|
||||||
explicit DebugSnapshotSink(SnapshotByteSink* chained) : sink_(chained) {}
|
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(); }
|
virtual int Position() V8_OVERRIDE { return sink_->Position(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -108,14 +109,14 @@ class DebugSnapshotSink : public SnapshotByteSink {
|
|||||||
|
|
||||||
class ListSnapshotSink : public i::SnapshotByteSink {
|
class ListSnapshotSink : public i::SnapshotByteSink {
|
||||||
public:
|
public:
|
||||||
explicit ListSnapshotSink(i::List<char>* data) : data_(data) {}
|
explicit ListSnapshotSink(i::List<byte>* data) : data_(data) {}
|
||||||
virtual void Put(int byte, const char* description) V8_OVERRIDE {
|
virtual void Put(byte b, const char* description) V8_OVERRIDE {
|
||||||
data_->Add(byte);
|
data_->Add(b);
|
||||||
}
|
}
|
||||||
virtual int Position() V8_OVERRIDE { return data_->length(); }
|
virtual int Position() V8_OVERRIDE { return data_->length(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
i::List<char>* data_;
|
i::List<byte>* data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace v8::internal
|
} // namespace v8::internal
|
||||||
|
@ -14814,134 +14814,21 @@ TEST(PreCompileSerialization) {
|
|||||||
v8::ScriptCompiler::kProduceDataToCache);
|
v8::ScriptCompiler::kProduceDataToCache);
|
||||||
// Serialize.
|
// Serialize.
|
||||||
const v8::ScriptCompiler::CachedData* cd = source.GetCachedData();
|
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);
|
i::MemCopy(serialized_data, cd->data, cd->length);
|
||||||
|
|
||||||
// Deserialize.
|
// 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.
|
// Verify that the original is the same as the deserialized.
|
||||||
CHECK_EQ(cd->length, deserialized->Length());
|
CHECK_EQ(cd->length, deserialized->length());
|
||||||
CHECK_EQ(0, memcmp(cd->data, deserialized->Data(), cd->length));
|
CHECK_EQ(0, memcmp(cd->data, deserialized->data(), cd->length));
|
||||||
|
|
||||||
delete deserialized;
|
delete deserialized;
|
||||||
i::DeleteArray(serialized_data);
|
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
|
// This tests that we do not allow dictionary load/call inline caches
|
||||||
// to use functions that have not yet been compiled. The potential
|
// to use functions that have not yet been compiled. The potential
|
||||||
// problem of loading a function that has not yet been compiled can
|
// problem of loading a function that has not yet been compiled can
|
||||||
|
@ -157,8 +157,7 @@ TEST(ScanHTMLEndComments) {
|
|||||||
preparser.set_allow_lazy(true);
|
preparser.set_allow_lazy(true);
|
||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
CHECK(!log.HasError());
|
||||||
CHECK(!data.has_error());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; fail_tests[i]; i++) {
|
for (int i = 0; fail_tests[i]; i++) {
|
||||||
@ -173,8 +172,7 @@ TEST(ScanHTMLEndComments) {
|
|||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
// Even in the case of a syntax error, kPreParseSuccess is returned.
|
// Even in the case of a syntax error, kPreParseSuccess is returned.
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
CHECK(log.HasError());
|
||||||
CHECK(data.has_error());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,8 +304,7 @@ TEST(StandAlonePreParser) {
|
|||||||
preparser.set_allow_natives_syntax(true);
|
preparser.set_allow_natives_syntax(true);
|
||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
CHECK(!log.HasError());
|
||||||
CHECK(!data.has_error());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,9 +336,7 @@ TEST(StandAlonePreParserNoNatives) {
|
|||||||
preparser.set_allow_lazy(true);
|
preparser.set_allow_lazy(true);
|
||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
CHECK(log.HasError());
|
||||||
// Data contains syntax error.
|
|
||||||
CHECK(data.has_error());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,8 +402,7 @@ TEST(RegressChromium62639) {
|
|||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
// Even in the case of a syntax error, kPreParseSuccess is returned.
|
// Even in the case of a syntax error, kPreParseSuccess is returned.
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
CHECK(log.HasError());
|
||||||
CHECK(data.has_error());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -438,15 +432,15 @@ TEST(Regress928) {
|
|||||||
preparser.set_allow_lazy(true);
|
preparser.set_allow_lazy(true);
|
||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
i::ScriptData data(log.ExtractData());
|
i::ScriptData* sd = log.GetScriptData();
|
||||||
CHECK(!data.has_error());
|
i::ParseData pd(sd);
|
||||||
data.Initialize();
|
pd.Initialize();
|
||||||
|
|
||||||
int first_function =
|
int first_function =
|
||||||
static_cast<int>(strstr(program, "function") - program);
|
static_cast<int>(strstr(program, "function") - program);
|
||||||
int first_lbrace = first_function + i::StrLength("function () ");
|
int first_lbrace = first_function + i::StrLength("function () ");
|
||||||
CHECK_EQ('{', program[first_lbrace]);
|
CHECK_EQ('{', program[first_lbrace]);
|
||||||
i::FunctionEntry entry1 = data.GetFunctionEntry(first_lbrace);
|
i::FunctionEntry entry1 = pd.GetFunctionEntry(first_lbrace);
|
||||||
CHECK(!entry1.is_valid());
|
CHECK(!entry1.is_valid());
|
||||||
|
|
||||||
int second_function =
|
int second_function =
|
||||||
@ -454,9 +448,10 @@ TEST(Regress928) {
|
|||||||
int second_lbrace =
|
int second_lbrace =
|
||||||
second_function + i::StrLength("function () ");
|
second_function + i::StrLength("function () ");
|
||||||
CHECK_EQ('{', program[second_lbrace]);
|
CHECK_EQ('{', program[second_lbrace]);
|
||||||
i::FunctionEntry entry2 = data.GetFunctionEntry(second_lbrace);
|
i::FunctionEntry entry2 = pd.GetFunctionEntry(second_lbrace);
|
||||||
CHECK(entry2.is_valid());
|
CHECK(entry2.is_valid());
|
||||||
CHECK_EQ('}', program[entry2.end_pos() - 1]);
|
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::Isolate* isolate = CcTest::i_isolate();
|
||||||
i::Factory* factory = isolate->factory();
|
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(
|
i::Handle<i::String> format = v8::Utils::OpenHandle(
|
||||||
*v8::String::NewFromUtf8(CcTest::isolate(), message));
|
*v8::String::NewFromUtf8(CcTest::isolate(), message));
|
||||||
const char* arg = data->BuildArg();
|
int arg_count = data[i::PreparseDataConstants::kMessageArgCountPos];
|
||||||
i::Handle<i::JSArray> args_array = factory->NewJSArray(arg == NULL ? 0 : 1);
|
const char* arg = NULL;
|
||||||
if (arg != NULL) {
|
i::Handle<i::JSArray> args_array;
|
||||||
i::JSArray::SetElement(
|
if (arg_count == 1) {
|
||||||
args_array, 0, v8::Utils::OpenHandle(*v8::String::NewFromUtf8(
|
// Position after text found by skipping past length field and
|
||||||
CcTest::isolate(), arg)),
|
// 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();
|
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::JSObject> builtins(isolate->js_builtins_object());
|
||||||
i::Handle<i::Object> format_fun = i::Object::GetProperty(
|
i::Handle<i::Object> format_fun = i::Object::GetProperty(
|
||||||
isolate, builtins, "FormatMessage").ToHandleChecked();
|
isolate, builtins, "FormatMessage").ToHandleChecked();
|
||||||
@ -1157,6 +1173,7 @@ i::Handle<i::String> FormatMessage(i::ScriptData* data) {
|
|||||||
CHECK(result->IsString());
|
CHECK(result->IsString());
|
||||||
i::DeleteArray(message);
|
i::DeleteArray(message);
|
||||||
i::DeleteArray(arg);
|
i::DeleteArray(arg);
|
||||||
|
data.Dispose();
|
||||||
return i::Handle<i::String>::cast(result);
|
return i::Handle<i::String>::cast(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1211,7 +1228,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
|||||||
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
i::PreParser::PreParseResult result = preparser.PreParseProgram();
|
||||||
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
|
||||||
}
|
}
|
||||||
i::ScriptData data(log.ExtractData());
|
|
||||||
|
bool preparse_error = log.HasError();
|
||||||
|
|
||||||
// Parse the data
|
// Parse the data
|
||||||
i::FunctionLiteral* function;
|
i::FunctionLiteral* function;
|
||||||
@ -1246,7 +1264,7 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
|||||||
CHECK(false);
|
CHECK(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data.has_error()) {
|
if (!preparse_error) {
|
||||||
v8::base::OS::Print(
|
v8::base::OS::Print(
|
||||||
"Parser failed on:\n"
|
"Parser failed on:\n"
|
||||||
"\t%s\n"
|
"\t%s\n"
|
||||||
@ -1257,7 +1275,8 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
|
|||||||
CHECK(false);
|
CHECK(false);
|
||||||
}
|
}
|
||||||
// Check that preparser and parser produce the same error.
|
// 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)) {
|
if (!i::String::Equals(message_string, preparser_message)) {
|
||||||
v8::base::OS::Print(
|
v8::base::OS::Print(
|
||||||
"Expected parser and preparser to produce the same error on:\n"
|
"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());
|
preparser_message->ToCString().get());
|
||||||
CHECK(false);
|
CHECK(false);
|
||||||
}
|
}
|
||||||
} else if (data.has_error()) {
|
} else if (preparse_error) {
|
||||||
v8::base::OS::Print(
|
v8::base::OS::Print(
|
||||||
"Preparser failed on:\n"
|
"Preparser failed on:\n"
|
||||||
"\t%s\n"
|
"\t%s\n"
|
||||||
"with error:\n"
|
"with error:\n"
|
||||||
"\t%s\n"
|
"\t%s\n"
|
||||||
"However, the parser succeeded",
|
"However, the parser succeeded",
|
||||||
source->ToCString().get(), FormatMessage(&data)->ToCString().get());
|
source->ToCString().get(), FormatMessage(log.ErrorMessageData()));
|
||||||
CHECK(false);
|
CHECK(false);
|
||||||
} else if (result == kError) {
|
} else if (result == kError) {
|
||||||
v8::base::OS::Print(
|
v8::base::OS::Print(
|
||||||
@ -2165,22 +2184,20 @@ TEST(DontRegressPreParserDataSizes) {
|
|||||||
factory->NewStringFromUtf8(i::CStrVector(program)).ToHandleChecked();
|
factory->NewStringFromUtf8(i::CStrVector(program)).ToHandleChecked();
|
||||||
i::Handle<i::Script> script = factory->NewScript(source);
|
i::Handle<i::Script> script = factory->NewScript(source);
|
||||||
i::CompilationInfoWithZone info(script);
|
i::CompilationInfoWithZone info(script);
|
||||||
i::ScriptData* data = NULL;
|
i::ScriptData* sd = NULL;
|
||||||
info.SetCachedData(&data, i::PRODUCE_CACHED_DATA);
|
info.SetCachedData(&sd, i::PRODUCE_CACHED_DATA);
|
||||||
i::Parser::Parse(&info, true);
|
i::Parser::Parse(&info, true);
|
||||||
CHECK(data);
|
i::ParseData pd(sd);
|
||||||
CHECK(!data->HasError());
|
|
||||||
|
|
||||||
if (data->function_count() != test_cases[i].functions) {
|
if (pd.FunctionCount() != test_cases[i].functions) {
|
||||||
v8::base::OS::Print(
|
v8::base::OS::Print(
|
||||||
"Expected preparse data for program:\n"
|
"Expected preparse data for program:\n"
|
||||||
"\t%s\n"
|
"\t%s\n"
|
||||||
"to contain %d functions, however, received %d functions.\n",
|
"to contain %d functions, however, received %d functions.\n",
|
||||||
program, test_cases[i].functions,
|
program, test_cases[i].functions, pd.FunctionCount());
|
||||||
data->function_count());
|
|
||||||
CHECK(false);
|
CHECK(false);
|
||||||
}
|
}
|
||||||
delete data;
|
delete sd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +177,9 @@ class FileByteSink : public SnapshotByteSink {
|
|||||||
fclose(fp_);
|
fclose(fp_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void Put(int byte, const char* description) {
|
virtual void Put(byte b, const char* description) {
|
||||||
if (fp_ != NULL) {
|
if (fp_ != NULL) {
|
||||||
fputc(byte, fp_);
|
fputc(b, fp_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual int Position() {
|
virtual int Position() {
|
||||||
|
Loading…
Reference in New Issue
Block a user