observers_begin_perform_splice);
INSTALL_NATIVE(JSFunction, "EndPerformSplice",
observers_end_perform_splice);
+ INSTALL_NATIVE(JSFunction, "NativeObjectObserve",
+ native_object_observe);
+ INSTALL_NATIVE(JSFunction, "NativeObjectGetNotifier",
+ native_object_get_notifier);
+ INSTALL_NATIVE(JSFunction, "NativeObjectNotifierPerformChange",
+ native_object_notifier_perform_change);
}
observers_begin_perform_splice) \
V(OBSERVERS_END_SPLICE_INDEX, JSFunction, \
observers_end_perform_splice) \
+ V(NATIVE_OBJECT_OBSERVE_INDEX, JSFunction, \
+ native_object_observe) \
+ V(NATIVE_OBJECT_GET_NOTIFIER_INDEX, JSFunction, \
+ native_object_get_notifier) \
+ V(NATIVE_OBJECT_NOTIFIER_PERFORM_CHANGE, JSFunction, \
+ native_object_notifier_perform_change) \
V(SLOPPY_GENERATOR_FUNCTION_MAP_INDEX, Map, sloppy_generator_function_map) \
V(STRICT_GENERATOR_FUNCTION_MAP_INDEX, Map, strict_generator_function_map) \
V(GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX, Map, \
OBSERVERS_ENQUEUE_SPLICE_INDEX,
OBSERVERS_BEGIN_SPLICE_INDEX,
OBSERVERS_END_SPLICE_INDEX,
+ NATIVE_OBJECT_OBSERVE_INDEX,
+ NATIVE_OBJECT_GET_NOTIFIER_INDEX,
+ NATIVE_OBJECT_NOTIFIER_PERFORM_CHANGE,
SLOPPY_GENERATOR_FUNCTION_MAP_INDEX,
STRICT_GENERATOR_FUNCTION_MAP_INDEX,
GENERATOR_OBJECT_PROTOTYPE_MAP_INDEX,
};
MapWrapper.prototype = {
+ __proto__: null,
get: function(key) {
return %WeakCollectionGet(this.map_, key);
},
if (!AcceptArgIsValid(acceptList))
throw MakeTypeError("observe_accept_invalid");
+ return %NativeObjectObserve(object, callback, acceptList);
+}
+
+function NativeObjectObserve(object, callback, acceptList) {
var objectInfo = ObjectInfoGetOrCreate(object);
ObjectInfoAddObserver(objectInfo, callback, acceptList);
return object;
throw MakeTypeError("called_on_non_object", ["performChange"]);
var objectInfo = ObjectInfoGetFromNotifier(this);
-
if (IS_UNDEFINED(objectInfo))
throw MakeTypeError("observe_notify_non_notifier");
if (!IS_STRING(changeType))
if (!IS_SPEC_FUNCTION(changeFn))
throw MakeTypeError("observe_perform_non_function");
+ return %NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn)
+}
+
+function NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn) {
ObjectInfoAddPerformingType(objectInfo, changeType);
var changeRecord;
if (!%ObjectWasCreatedInCurrentOrigin(object)) return null;
+ return %NativeObjectGetNotifier(object);
+}
+
+function NativeObjectGetNotifier(object) {
var objectInfo = ObjectInfoGetOrCreate(object);
return ObjectInfoGetNotifier(objectInfo);
}
}
+RUNTIME_FUNCTION(Runtime_NativeObjectObserve) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, callback, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, accept, 2);
+
+ Handle<Context> context(object->GetCreationContext(), isolate);
+ Handle<JSFunction> function(context->native_object_observe(), isolate);
+ Handle<Object> call_args[] = { object, callback, accept };
+ Handle<Object> result;
+
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ Execution::Call(isolate, function,
+ handle(context->object_function(), isolate), 3, call_args, true));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NativeObjectGetNotifier) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 1);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
+
+ Handle<Context> context(object->GetCreationContext(), isolate);
+ Handle<JSFunction> function(context->native_object_get_notifier(), isolate);
+ Handle<Object> call_args[] = { object };
+ Handle<Object> result;
+
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ Execution::Call(isolate, function,
+ handle(context->object_function(), isolate), 1, call_args, true));
+ return *result;
+}
+
+
+RUNTIME_FUNCTION(Runtime_NativeObjectNotifierPerformChange) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 3);
+ CONVERT_ARG_HANDLE_CHECKED(JSObject, object_info, 0);
+ CONVERT_ARG_HANDLE_CHECKED(Object, change_type, 1);
+ CONVERT_ARG_HANDLE_CHECKED(Object, change_fn, 2);
+
+ Handle<Context> context(object_info->GetCreationContext(), isolate);
+ Handle<JSFunction> function(context->native_object_notifier_perform_change(),
+ isolate);
+ Handle<Object> call_args[] = { change_type, change_fn };
+ Handle<Object> result;
+
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ Execution::Call(isolate, function, isolate->factory()->undefined_value(),
+ 2, call_args, true));
+ return *result;
+}
+
+
static Object* ArrayConstructorCommon(Isolate* isolate,
Handle<JSFunction> constructor,
Handle<AllocationSite> site,
F(ObservationWeakMapCreate, 0, 1) \
F(ObserverObjectAndRecordHaveSameOrigin, 3, 1) \
F(ObjectWasCreatedInCurrentOrigin, 1, 1) \
+ F(NativeObjectObserve, 3, 1) \
+ F(NativeObjectGetNotifier, 1, 1) \
\
/* Harmony typed arrays */ \
F(ArrayBufferInitialize, 2, 1)\
CHECK(CompileRun("Object.getNotifier(obj)")->IsObject());
}
}
+
+
+static int GetGlobalObjectsCount() {
+ CcTest::heap()->EnsureHeapIsIterable();
+ int count = 0;
+ i::HeapIterator it(CcTest::heap());
+ for (i::HeapObject* object = it.next(); object != NULL; object = it.next())
+ if (object->IsJSGlobalObject()) count++;
+ return count;
+}
+
+
+static void CheckSurvivingGlobalObjectsCount(int expected) {
+ // We need to collect all garbage twice to be sure that everything
+ // has been collected. This is because inline caches are cleared in
+ // the first garbage collection but some of the maps have already
+ // been marked at that point. Therefore some of the maps are not
+ // collected until the second garbage collection.
+ CcTest::heap()->CollectAllGarbage(i::Heap::kNoGCFlags);
+ CcTest::heap()->CollectAllGarbage(i::Heap::kMakeHeapIterableMask);
+ int count = GetGlobalObjectsCount();
+#ifdef DEBUG
+ if (count != expected) CcTest::heap()->TracePathToGlobal();
+#endif
+ CHECK_EQ(expected, count);
+}
+
+
+TEST(DontLeakContextOnObserve) {
+ HandleScope scope(CcTest::isolate());
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo");
+ LocalContext context(CcTest::isolate());
+ context->SetSecurityToken(foo);
+ CompileRun("var obj = {};");
+ Handle<Value> object = CompileRun("obj");
+ {
+ HandleScope scope(CcTest::isolate());
+ LocalContext context2(CcTest::isolate());
+ context2->SetSecurityToken(foo);
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
+ object);
+ CompileRun("function observer() {};"
+ "Object.observe(obj, observer, ['foo', 'bar', 'baz']);"
+ "Object.unobserve(obj, observer);");
+ }
+
+ v8::V8::ContextDisposedNotification();
+ CheckSurvivingGlobalObjectsCount(1);
+}
+
+
+TEST(DontLeakContextOnGetNotifier) {
+ HandleScope scope(CcTest::isolate());
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo");
+ LocalContext context(CcTest::isolate());
+ context->SetSecurityToken(foo);
+ CompileRun("var obj = {};");
+ Handle<Value> object = CompileRun("obj");
+ {
+ HandleScope scope(CcTest::isolate());
+ LocalContext context2(CcTest::isolate());
+ context2->SetSecurityToken(foo);
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
+ object);
+ CompileRun("Object.getNotifier(obj);");
+ }
+
+ v8::V8::ContextDisposedNotification();
+ CheckSurvivingGlobalObjectsCount(1);
+}
+
+
+TEST(DontLeakContextOnNotifierPerformChange) {
+ HandleScope scope(CcTest::isolate());
+ Handle<Value> foo = String::NewFromUtf8(CcTest::isolate(), "foo");
+ LocalContext context(CcTest::isolate());
+ context->SetSecurityToken(foo);
+ CompileRun("var obj = {};");
+ Handle<Value> object = CompileRun("obj");
+ {
+ HandleScope scope(CcTest::isolate());
+ LocalContext context2(CcTest::isolate());
+ context2->SetSecurityToken(foo);
+ context2->Global()->Set(String::NewFromUtf8(CcTest::isolate(), "obj"),
+ object);
+ CompileRun("var n = Object.getNotifier(obj);"
+ "n.performChange('foo', function() {});");
+ }
+
+ v8::V8::ContextDisposedNotification();
+ CheckSurvivingGlobalObjectsCount(1);
+}