BufferOwned
};
- CachedData() : data(NULL), length(0), buffer_policy(BufferNotOwned) {}
+ CachedData()
+ : data(NULL),
+ length(0),
+ rejected(false),
+ buffer_policy(BufferNotOwned) {}
// If buffer_policy is BufferNotOwned, the caller keeps the ownership of
// data and guarantees that it stays alive until the CachedData object is
// which will be called when V8 no longer needs the data.
const uint8_t* data;
int length;
+ bool rejected;
BufferPolicy buffer_policy;
private:
ScriptCompiler::CachedData::CachedData(const uint8_t* data_, int length_,
BufferPolicy buffer_policy_)
- : data(data_), length(length_), buffer_policy(buffer_policy_) {}
+ : data(data_),
+ length(length_),
+ rejected(false),
+ buffer_policy(buffer_policy_) {}
ScriptCompiler::CachedData::~CachedData() {
source->cached_data = new CachedData(
script_data->data(), script_data->length(), CachedData::BufferOwned);
script_data->ReleaseDataOwnership();
+ } else if (options == kConsumeParserCache || options == kConsumeCodeCache) {
+ source->cached_data->rejected = script_data->rejected();
}
delete script_data;
}
ScriptData::ScriptData(const byte* data, int length)
- : owns_data_(false), data_(data), length_(length) {
+ : owns_data_(false), rejected_(false), data_(data), length_(length) {
if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
byte* copy = NewArray<byte>(length);
DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
const byte* data() const { return data_; }
int length() const { return length_; }
+ bool rejected() const { return rejected_; }
+
+ void Reject() { rejected_ = true; }
void AcquireDataOwnership() {
DCHECK(!owns_data_);
}
private:
- bool owns_data_;
+ bool owns_data_ : 1;
+ bool rejected_ : 1;
const byte* data_;
int length_;
bool ParseData::IsSane() {
+ if (!IsAligned(script_data_->length(), sizeof(unsigned))) return false;
// Check that the header data is valid and doesn't specify
// point to positions outside the store.
int data_length = Length();
} else {
DCHECK(info_->cached_data() != NULL);
if (compile_options() == ScriptCompiler::kConsumeParserCache) {
- cached_parse_data_ = new ParseData(*info_->cached_data());
+ cached_parse_data_ = ParseData::FromCachedData(*info_->cached_data());
}
}
}
CompleteParserRecorder recorder;
debug_saved_compile_options_ = compile_options();
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
+ if (produce_cached_parse_data()) {
log_ = &recorder;
- } else if (compile_options() == ScriptCompiler::kConsumeParserCache) {
+ } else if (consume_cached_parse_data()) {
cached_parse_data_->Initialize();
}
}
PrintF(" - took %0.3f ms]\n", ms);
}
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
+ if (produce_cached_parse_data()) {
if (result != NULL) *info_->cached_data() = recorder.GetScriptData();
log_ = NULL;
}
CHECK(materialized_literal_count);
CHECK(expected_property_count);
CHECK(debug_saved_compile_options_ == compile_options());
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
- CHECK(log_);
- }
+ if (produce_cached_parse_data()) CHECK(log_);
int function_block_pos = position();
- if (compile_options() == ScriptCompiler::kConsumeParserCache) {
+ if (consume_cached_parse_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 =
*materialized_literal_count = logger.literals();
*expected_property_count = logger.properties();
scope_->SetStrictMode(logger.strict_mode());
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
+ if (produce_cached_parse_data()) {
DCHECK(log_);
// Position right after terminal '}'.
int body_end = scanner()->location().end_pos;
CompleteParserRecorder recorder;
debug_saved_compile_options_ = compile_options();
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
- log_ = &recorder;
- }
+ if (produce_cached_parse_data()) log_ = &recorder;
DCHECK(info()->source_stream() != NULL);
ExternalStreamingStream stream(info()->source_stream(),
// We cannot internalize on a background thread; a foreground task will take
// care of calling Parser::Internalize just before compilation.
- if (compile_options() == ScriptCompiler::kProduceParserCache) {
+ if (produce_cached_parse_data()) {
if (result != NULL) *info_->cached_data() = recorder.GetScriptData();
log_ = NULL;
}
// Wrapper around ScriptData to provide parser-specific functionality.
class ParseData {
public:
- explicit ParseData(ScriptData* script_data) : script_data_(script_data) {
- CHECK(IsAligned(script_data->length(), sizeof(unsigned)));
- CHECK(IsSane());
+ static ParseData* FromCachedData(ScriptData* cached_data) {
+ ParseData* pd = new ParseData(cached_data);
+ if (pd->IsSane()) return pd;
+ cached_data->Reject();
+ delete pd;
+ return NULL;
}
+
void Initialize();
FunctionEntry GetFunctionEntry(int start);
int FunctionCount();
}
private:
+ explicit ParseData(ScriptData* script_data) : script_data_(script_data) {}
+
bool IsSane();
unsigned Magic();
unsigned Version();
ScriptCompiler::CompileOptions compile_options() const {
return info_->compile_options();
}
+ bool consume_cached_parse_data() const {
+ return compile_options() == ScriptCompiler::kConsumeParserCache &&
+ cached_parse_data_ != NULL;
+ }
+ bool produce_cached_parse_data() const {
+ return compile_options() == ScriptCompiler::kProduceParserCache;
+ }
Scope* DeclarationScope(VariableMode mode) {
return IsLexicalVariableMode(mode)
? scope_ : scope_->DeclarationScope();
MaybeHandle<SharedFunctionInfo> CodeSerializer::Deserialize(
- Isolate* isolate, ScriptData* data, Handle<String> source) {
+ Isolate* isolate, ScriptData* cached_data, Handle<String> source) {
base::ElapsedTimer timer;
if (FLAG_profile_deserialization) timer.Start();
{
HandleScope scope(isolate);
- SerializedCodeData scd(data, *source);
- SnapshotByteSource payload(scd.Payload(), scd.PayloadLength());
+ SerializedCodeData* scd =
+ SerializedCodeData::FromCachedData(cached_data, *source);
+ if (scd == NULL) {
+ if (FLAG_profile_deserialization) PrintF("[Cached code failed check]\n");
+ DCHECK(cached_data->rejected());
+ return MaybeHandle<SharedFunctionInfo>();
+ }
+ SnapshotByteSource payload(scd->Payload(), scd->PayloadLength());
Deserializer deserializer(&payload);
// Eagerly expand string table to avoid allocations during deserialization.
- StringTable::EnsureCapacityForDeserialization(isolate,
- scd.NumInternalizedStrings());
+ StringTable::EnsureCapacityForDeserialization(
+ isolate, scd->NumInternalizedStrings());
// Set reservations.
STATIC_ASSERT(NEW_SPACE == 0);
int current_space = NEW_SPACE;
- Vector<const SerializedCodeData::Reservation> res = scd.Reservations();
+ Vector<const SerializedCodeData::Reservation> res = scd->Reservations();
for (const auto& r : res) {
deserializer.AddReservation(current_space, r.chunk_size());
if (r.is_last_chunk()) current_space++;
DCHECK_EQ(kNumberOfSpaces, current_space);
// Prepare and register list of attached objects.
- Vector<const uint32_t> code_stub_keys = scd.CodeStubKeys();
+ Vector<const uint32_t> code_stub_keys = scd->CodeStubKeys();
Vector<Handle<Object> > attached_objects = Vector<Handle<Object> >::New(
code_stub_keys.length() + kCodeStubsBaseIndex);
attached_objects[kSourceObjectIndex] = source;
if (FLAG_profile_deserialization) {
double ms = timer.Elapsed().InMillisecondsF();
- int length = data->length();
+ int length = cached_data->length();
PrintF("[Deserializing from %d bytes took %0.3f ms]\n", length, ms);
}
Handle<SharedFunctionInfo> result(SharedFunctionInfo::cast(root), isolate);
int SerializedCodeData::CheckSum(String* string) {
- int checksum = Version::Hash();
-#ifdef DEBUG
- uint32_t seed = static_cast<uint32_t>(checksum);
- checksum = static_cast<int>(IteratingStringHasher::Hash(string, seed));
-#endif // DEBUG
- return checksum;
+ return Version::Hash() ^ string->length();
}
} } // namespace v8::internal
Handle<String> source);
MUST_USE_RESULT static MaybeHandle<SharedFunctionInfo> Deserialize(
- Isolate* isolate, ScriptData* data, Handle<String> source);
+ Isolate* isolate, ScriptData* cached_data, Handle<String> source);
static const int kSourceObjectIndex = 0;
static const int kCodeStubsBaseIndex = 1;
class SerializedCodeData {
public:
// Used by when consuming.
- explicit SerializedCodeData(ScriptData* data, String* source)
- : script_data_(data), owns_script_data_(false) {
+ static SerializedCodeData* FromCachedData(ScriptData* cached_data,
+ String* source) {
DisallowHeapAllocation no_gc;
- CHECK(IsSane(source));
+ SerializedCodeData* scd = new SerializedCodeData(cached_data);
+ if (scd->IsSane(source)) return scd;
+ cached_data->Reject();
+ delete scd;
+ return NULL;
}
// Used when producing.
}
private:
+ explicit SerializedCodeData(ScriptData* data)
+ : script_data_(data), owns_script_data_(false) {}
+
void SetHeaderValue(int offset, int value) {
reinterpret_cast<int*>(const_cast<byte*>(script_data_->data()))[offset] =
value;
const char* chunks[] = {chunk1, chunk2, "foo();", NULL};
RunStreamingTest(chunks, v8::ScriptCompiler::StreamedSource::UTF8);
}
+
+
+void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) {
+ const char* garbage = "garbage garbage garbage garbage.";
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(garbage);
+ int length = 16;
+ v8::ScriptCompiler::CachedData* cached_data =
+ new v8::ScriptCompiler::CachedData(data, length);
+ DCHECK(!cached_data->rejected);
+ v8::ScriptOrigin origin(v8_str("origin"));
+ v8::ScriptCompiler::Source source(v8_str("42"), origin, cached_data);
+ v8::Handle<v8::Script> script =
+ v8::ScriptCompiler::Compile(CcTest::isolate(), &source, option);
+ CHECK(cached_data->rejected);
+ CHECK_EQ(42, script->Run()->Int32Value());
+}
+
+
+TEST(InvalidCacheData) {
+ v8::V8::Initialize();
+ v8::HandleScope scope(CcTest::isolate());
+ LocalContext context;
+ TestInvalidCacheData(v8::ScriptCompiler::kConsumeParserCache);
+ TestInvalidCacheData(v8::ScriptCompiler::kConsumeCodeCache);
+}
i::PreParser::PreParseResult result = preparser.PreParseProgram();
CHECK_EQ(i::PreParser::kPreParseSuccess, result);
i::ScriptData* sd = log.GetScriptData();
- i::ParseData pd(sd);
- pd.Initialize();
+ i::ParseData* pd = i::ParseData::FromCachedData(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 = pd.GetFunctionEntry(first_lbrace);
+ i::FunctionEntry entry1 = pd->GetFunctionEntry(first_lbrace);
CHECK(!entry1.is_valid());
int second_function =
int second_lbrace =
second_function + i::StrLength("function () ");
CHECK_EQ('{', program[second_lbrace]);
- i::FunctionEntry entry2 = pd.GetFunctionEntry(second_lbrace);
+ i::FunctionEntry entry2 = pd->GetFunctionEntry(second_lbrace);
CHECK(entry2.is_valid());
CHECK_EQ('}', program[entry2.end_pos() - 1]);
delete sd;
+ delete pd;
}
i::ScriptData* sd = NULL;
info.SetCachedData(&sd, v8::ScriptCompiler::kProduceParserCache);
i::Parser::Parse(&info, true);
- i::ParseData pd(sd);
+ i::ParseData* pd = i::ParseData::FromCachedData(sd);
- if (pd.FunctionCount() != 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, pd.FunctionCount());
+ program, test_cases[i].functions, pd->FunctionCount());
CHECK(false);
}
delete sd;
+ delete pd;
}
}
script = v8::ScriptCompiler::CompileUnbound(
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache);
}
+ CHECK(!cache->rejected);
v8::Local<v8::Value> result = script->BindToCurrentContext()->Run();
CHECK(result->ToString()->Equals(v8_str("abcdef")));
}