Fix bug with filtering of foreign context maps in the type feedback.

The first attempt did not properly handle keyed loads/stores and
did not check the constructors of the objects in the prototype
chain.

Added two more tests to handle the fixed cases.

BUG=v8:1823
TEST=LeakGlobalObjectViaMapKeyed,LeakGlobalContextViaMapProto
Review URL: http://codereview.chromium.org/8974009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10277 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
fschneider@chromium.org 2011-12-19 12:39:52 +00:00
parent 240e50d6a3
commit 6c0a4f5d45
3 changed files with 117 additions and 24 deletions

View File

@ -85,7 +85,8 @@ bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) {
return code->is_keyed_load_stub() &&
code->ic_state() == MONOMORPHIC &&
Code::ExtractTypeFromFlags(code->flags()) == NORMAL &&
code->FindFirstMap() != NULL;
code->FindFirstMap() != NULL &&
!CanRetainOtherContext(code->FindFirstMap(), *global_context_);
}
return false;
}
@ -111,7 +112,9 @@ bool TypeFeedbackOracle::StoreIsMonomorphicNormal(Expression* expr) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
return code->is_keyed_store_stub() &&
code->ic_state() == MONOMORPHIC &&
Code::ExtractTypeFromFlags(code->flags()) == NORMAL;
Code::ExtractTypeFromFlags(code->flags()) == NORMAL &&
code->FindFirstMap() != NULL &&
!CanRetainOtherContext(code->FindFirstMap(), *global_context_);
}
return false;
}
@ -144,7 +147,9 @@ Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
Map* first_map = code->FindFirstMap();
ASSERT(first_map != NULL);
return Handle<Map>(first_map);
return CanRetainOtherContext(first_map, *global_context_)
? Handle<Map>::null()
: Handle<Map>(first_map);
}
return Handle<Map>::cast(map_or_code);
}
@ -155,7 +160,11 @@ Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Expression* expr) {
Handle<Object> map_or_code = GetInfo(expr->id());
if (map_or_code->IsCode()) {
Handle<Code> code = Handle<Code>::cast(map_or_code);
return Handle<Map>(code->FindFirstMap());
Map* first_map = code->FindFirstMap();
ASSERT(first_map != NULL);
return CanRetainOtherContext(first_map, *global_context_)
? Handle<Map>::null()
: Handle<Map>(first_map);
}
return Handle<Map>::cast(map_or_code);
}
@ -288,7 +297,11 @@ Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) {
if (state != CompareIC::KNOWN_OBJECTS) {
return Handle<Map>::null();
}
return Handle<Map>(code->FindFirstMap());
Map* first_map = code->FindFirstMap();
ASSERT(first_map != NULL);
return CanRetainOtherContext(first_map, *global_context_)
? Handle<Map>::null()
: Handle<Map>(first_map);
}
@ -451,20 +464,23 @@ void TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id,
// retaining objects from different tabs in Chrome via optimized code.
bool TypeFeedbackOracle::CanRetainOtherContext(Map* map,
Context* global_context) {
Object* constructor = map->constructor();
ASSERT(constructor != NULL);
while (!constructor->IsJSFunction()) {
// If the constructor is not null or a JSFunction, we have to
// conservatively assume that it may retain a global context.
if (!constructor->IsNull()) return true;
// If both, constructor and prototype are null, we conclude
// that no global context will be retained by this map.
if (map->prototype()->IsNull()) return false;
map = JSObject::cast(map->prototype())->map();
Object* constructor = NULL;
while (!map->prototype()->IsNull()) {
constructor = map->constructor();
if (!constructor->IsNull()) {
// If the constructor is not null or a JSFunction, we have to
// conservatively assume that it may retain a global context.
if (!constructor->IsJSFunction()) return true;
// Check if the constructor directly references a foreign context.
if (CanRetainOtherContext(JSFunction::cast(constructor),
global_context)) {
return true;
}
}
map = HeapObject::cast(map->prototype())->map();
}
constructor = map->constructor();
if (constructor->IsNull()) return false;
JSFunction* function = JSFunction::cast(constructor);
return CanRetainOtherContext(function, global_context);
}
@ -498,7 +514,10 @@ void TypeFeedbackOracle::CollectKeyedReceiverTypes(unsigned ast_id,
RelocInfo* info = it.rinfo();
Object* object = info->target_object();
if (object->IsMap()) {
AddMapIfMissing(Handle<Map>(Map::cast(object)), types);
Map* map = Map::cast(object);
if (!CanRetainOtherContext(map, *global_context_)) {
AddMapIfMissing(Handle<Map>(map), types);
}
}
}
}

View File

@ -52,10 +52,6 @@ test-profile-generator/RecordStackTraceAtStartProfiling: PASS || FAIL
# We do not yet shrink weak maps after they have been emptied by the GC
test-weakmaps/Shrinking: FAIL
# TODO(1823): Fails without snapshot. Temporarily disabled until fixed.
test-heap/LeakGlobalContextViaMap: SKIP
test-heap/LeakGlobalContextViaFunction: SKIP
##############################################################################
[ $arch == arm ]

View File

@ -1333,6 +1333,7 @@ static int NumberOfGlobalObjects() {
// Test that we don't embed maps from foreign contexts into
// optimized code.
TEST(LeakGlobalContextViaMap) {
i::FLAG_allow_natives_syntax = true;
v8::HandleScope outer_scope;
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
@ -1349,7 +1350,8 @@ TEST(LeakGlobalContextViaMap) {
ctx2->Global()->Set(v8_str("o"), v);
v8::Local<v8::Value> res = CompileRun(
"function f() { return o.x; }"
"for (var i = 0; i < 1000000; ++i) f();"
"for (var i = 0; i < 10; ++i) f();"
"%OptimizeFunctionOnNextCall(f);"
"f();");
CHECK_EQ(42, res->Int32Value());
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
@ -1368,6 +1370,7 @@ TEST(LeakGlobalContextViaMap) {
// Test that we don't embed functions from foreign contexts into
// optimized code.
TEST(LeakGlobalContextViaFunction) {
i::FLAG_allow_natives_syntax = true;
v8::HandleScope outer_scope;
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
@ -1384,7 +1387,8 @@ TEST(LeakGlobalContextViaFunction) {
ctx2->Global()->Set(v8_str("o"), v);
v8::Local<v8::Value> res = CompileRun(
"function f(x) { return x(); }"
"for (var i = 0; i < 1000000; ++i) f(o);"
"for (var i = 0; i < 10; ++i) f(o);"
"%OptimizeFunctionOnNextCall(f);"
"f(o);");
CHECK_EQ(42, res->Int32Value());
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
@ -1398,3 +1402,77 @@ TEST(LeakGlobalContextViaFunction) {
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(0, NumberOfGlobalObjects());
}
TEST(LeakGlobalContextViaMapKeyed) {
i::FLAG_allow_natives_syntax = true;
v8::HandleScope outer_scope;
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
ctx1->Enter();
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(4, NumberOfGlobalObjects());
{
v8::HandleScope inner_scope;
CompileRun("var v = [42, 43]");
v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
ctx2->Enter();
ctx2->Global()->Set(v8_str("o"), v);
v8::Local<v8::Value> res = CompileRun(
"function f() { return o[0]; }"
"for (var i = 0; i < 10; ++i) f();"
"%OptimizeFunctionOnNextCall(f);"
"f();");
CHECK_EQ(42, res->Int32Value());
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
ctx2->Exit();
ctx1->Exit();
ctx1.Dispose();
}
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(2, NumberOfGlobalObjects());
ctx2.Dispose();
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(0, NumberOfGlobalObjects());
}
TEST(LeakGlobalContextViaMapProto) {
i::FLAG_allow_natives_syntax = true;
v8::HandleScope outer_scope;
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
ctx1->Enter();
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(4, NumberOfGlobalObjects());
{
v8::HandleScope inner_scope;
CompileRun("var v = { y: 42}");
v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
ctx2->Enter();
ctx2->Global()->Set(v8_str("o"), v);
v8::Local<v8::Value> res = CompileRun(
"function f() {"
" var p = {x: 42};"
" p.__proto__ = o;"
" return p.x;"
"}"
"for (var i = 0; i < 10; ++i) f();"
"%OptimizeFunctionOnNextCall(f);"
"f();");
CHECK_EQ(42, res->Int32Value());
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
ctx2->Exit();
ctx1->Exit();
ctx1.Dispose();
}
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(2, NumberOfGlobalObjects());
ctx2.Dispose();
HEAP->CollectAllAvailableGarbage();
CHECK_EQ(0, NumberOfGlobalObjects());
}