[js weak refs] Migrate the WeakRef parts into the new API

New API is here: https://github.com/tc39/proposal-weakrefs/issues/55

The WeakCell parts stay in the old API, resulting in temporary code duplication
in some parts. Those parts will go away once the WeakCell-related parts are
migrated to the new API (but the spec needs some work first).

BUG=v8:8179

Change-Id: I81ca824a14d830e3c5fa515d5ad7e5f78c10e19d
Reviewed-on: https://chromium-review.googlesource.com/c/1378171
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58264}
This commit is contained in:
Marja Hölttä 2018-12-17 09:15:02 +01:00 committed by Commit Bot
parent 9b42c4c132
commit fef6f8eddc
27 changed files with 225 additions and 422 deletions

View File

@ -4549,9 +4549,6 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
SimpleInstallFunction(isolate(), weak_factory_prototype, "makeCell",
Builtins::kWeakFactoryMakeCell, 2, false);
SimpleInstallFunction(isolate(), weak_factory_prototype, "makeRef",
Builtins::kWeakFactoryMakeRef, 2, false);
SimpleInstallFunction(isolate(), weak_factory_prototype, "cleanupSome",
Builtins::kWeakFactoryCleanupSome, 0, false);
}
@ -4579,6 +4576,7 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
// Create %WeakRefPrototype%
Handle<Map> weak_ref_map =
factory->NewMap(JS_WEAK_REF_TYPE, JSWeakRef::kSize);
DCHECK(weak_ref_map->IsJSObjectMap());
native_context()->set_js_weak_ref_map(*weak_ref_map);
Handle<JSObject> weak_ref_prototype =
@ -4593,6 +4591,23 @@ void Genesis::InitializeGlobal_harmony_weak_refs() {
SimpleInstallFunction(isolate(), weak_ref_prototype, "deref",
Builtins::kWeakRefDeref, 0, false);
// Create %WeakRef%
Handle<String> weak_ref_name = factory->InternalizeUtf8String("WeakRef");
Handle<JSFunction> weak_ref_fun = CreateFunction(
isolate(), weak_ref_name, JS_WEAK_REF_TYPE, JSWeakRef::kSize, 0,
weak_ref_prototype, Builtins::kWeakRefConstructor);
weak_ref_fun->shared()->DontAdaptArguments();
weak_ref_fun->shared()->set_length(1);
// Install the "constructor" property on the prototype.
JSObject::AddProperty(isolate(), weak_ref_prototype,
factory->constructor_string(), weak_ref_fun,
DONT_ENUM);
JSObject::AddProperty(isolate(), global, weak_ref_name, weak_ref_fun,
DONT_ENUM);
}
{

View File

@ -1368,7 +1368,7 @@ namespace internal {
CPP(WeakFactoryCleanupSome) \
CPP(WeakFactoryConstructor) \
CPP(WeakFactoryMakeCell) \
CPP(WeakFactoryMakeRef) \
CPP(WeakRefConstructor) \
CPP(WeakRefDeref)
#ifdef V8_INTL_SUPPORT

View File

@ -77,44 +77,6 @@ BUILTIN(WeakFactoryMakeCell) {
return *weak_cell;
}
BUILTIN(WeakFactoryMakeRef) {
HandleScope scope(isolate);
const char* method_name = "WeakFactory.prototype.makeRef";
CHECK_RECEIVER(JSWeakFactory, weak_factory, method_name);
Handle<Object> target = args.atOrUndefined(isolate, 1);
if (!target->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kWeakRefsMakeRefTargetMustBeObject));
}
Handle<JSReceiver> target_receiver = Handle<JSReceiver>::cast(target);
Handle<Object> holdings = args.atOrUndefined(isolate, 2);
if (target->SameValue(*holdings)) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(
MessageTemplate::kWeakRefsMakeRefTargetAndHoldingsMustNotBeSame));
}
// TODO(marja): Realms.
Handle<Map> weak_ref_map(isolate->native_context()->js_weak_ref_map(),
isolate);
Handle<JSWeakRef> weak_ref =
Handle<JSWeakRef>::cast(isolate->factory()->NewJSObjectFromMap(
weak_ref_map, TENURED, Handle<AllocationSite>::null()));
weak_ref->set_target(*target_receiver);
weak_ref->set_holdings(*holdings);
weak_factory->AddWeakCell(*weak_ref);
isolate->heap()->AddKeepDuringJobTarget(target_receiver);
return *weak_ref;
}
BUILTIN(WeakFactoryCleanupSome) {
HandleScope scope(isolate);
const char* method_name = "WeakFactory.prototype.cleanupSome";
@ -156,6 +118,38 @@ BUILTIN(WeakCellClear) {
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(WeakRefConstructor) {
HandleScope scope(isolate);
Handle<JSFunction> target = args.target();
if (args.new_target()->IsUndefined(isolate)) { // [[Call]]
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kConstructorNotFunction,
handle(target->shared()->Name(), isolate)));
}
// [[Construct]]
Handle<JSReceiver> new_target = Handle<JSReceiver>::cast(args.new_target());
Handle<Object> target_object = args.atOrUndefined(isolate, 1);
if (!target_object->IsJSReceiver()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(
MessageTemplate::kWeakRefsWeakRefConstructorTargetMustBeObject));
}
isolate->heap()->AddKeepDuringJobTarget(
Handle<JSReceiver>::cast(target_object));
// TODO(marja): Realms.
Handle<JSObject> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result,
JSObject::New(target, new_target, Handle<AllocationSite>::null()));
Handle<JSWeakRef> weak_ref = Handle<JSWeakRef>::cast(result);
weak_ref->set_target(*target_object);
return *weak_ref;
}
BUILTIN(WeakRefDeref) {
HandleScope scope(isolate);
CHECK_RECEIVER(JSWeakRef, weak_ref, "WeakRef.prototype.deref");

View File

@ -221,6 +221,28 @@ class ConcurrentMarkingVisitor final
return VisitJSObjectSubclass(map, object);
}
int VisitJSWeakRef(Map map, JSWeakRef weak_ref) {
int size = VisitJSObjectSubclass(map, weak_ref);
if (size == 0) {
return 0;
}
if (weak_ref->target()->IsHeapObject()) {
HeapObject* target = HeapObject::cast(weak_ref->target());
if (marking_state_.IsBlackOrGrey(target)) {
// Record the slot inside the JSWeakRef, since the
// VisitJSObjectSubclass above didn't visit it.
ObjectSlot slot =
HeapObject::RawField(weak_ref, JSWeakRef::kTargetOffset);
MarkCompactCollector::RecordSlot(weak_ref, slot, target);
} else {
// JSWeakRef points to a potentially dead object. We have to process
// them when we know the liveness of the whole transitive closure.
weak_objects_->js_weak_refs.Push(task_id_, weak_ref);
}
}
return size;
}
int VisitJSWeakCell(Map map, JSWeakCell weak_cell) {
int size = VisitJSObjectSubclass(map, weak_cell);
if (size == 0) {
@ -474,7 +496,7 @@ class ConcurrentMarkingVisitor final
void VisitCustomWeakPointers(HeapObject* host, ObjectSlot start,
ObjectSlot end) override {
DCHECK(host->IsJSWeakCell());
DCHECK(host->IsJSWeakCell() || host->IsJSWeakRef());
}
private:
@ -729,6 +751,7 @@ void ConcurrentMarking::Run(int task_id, TaskState* task_state) {
weak_objects_->next_ephemerons.FlushToGlobal(task_id);
weak_objects_->discovered_ephemerons.FlushToGlobal(task_id);
weak_objects_->weak_references.FlushToGlobal(task_id);
weak_objects_->js_weak_refs.FlushToGlobal(task_id);
weak_objects_->js_weak_cells.FlushToGlobal(task_id);
weak_objects_->weak_objects_in_code.FlushToGlobal(task_id);
weak_objects_->bytecode_flushing_candidates.FlushToGlobal(task_id);

View File

@ -195,6 +195,29 @@ int MarkingVisitor<fixed_array_mode, retaining_path_mode,
return size;
}
template <FixedArrayVisitationMode fixed_array_mode,
TraceRetainingPathMode retaining_path_mode, typename MarkingState>
int MarkingVisitor<fixed_array_mode, retaining_path_mode,
MarkingState>::VisitJSWeakRef(Map map, JSWeakRef weak_ref) {
if (weak_ref->target()->IsHeapObject()) {
HeapObject* target = HeapObject::cast(weak_ref->target());
if (marking_state()->IsBlackOrGrey(target)) {
// Record the slot inside the JSWeakRef, since the IterateBody below
// won't visit it.
ObjectSlot slot =
HeapObject::RawField(weak_ref, JSWeakCell::kTargetOffset);
collector_->RecordSlot(weak_ref, slot, target);
} else {
// JSWeakRef points to a potentially dead object. We have to process
// them when we know the liveness of the whole transitive closure.
collector_->AddWeakRef(weak_ref);
}
}
int size = JSWeakRef::BodyDescriptor::SizeOf(map, weak_ref);
JSWeakRef::BodyDescriptor::IterateBody(map, weak_ref, size, this);
return size;
}
template <FixedArrayVisitationMode fixed_array_mode,
TraceRetainingPathMode retaining_path_mode, typename MarkingState>
int MarkingVisitor<fixed_array_mode, retaining_path_mode,

View File

@ -1931,6 +1931,7 @@ void MarkCompactCollector::ClearNonLiveReferences() {
DCHECK(weak_objects_.transition_arrays.IsEmpty());
DCHECK(weak_objects_.weak_references.IsEmpty());
DCHECK(weak_objects_.weak_objects_in_code.IsEmpty());
DCHECK(weak_objects_.js_weak_refs.IsEmpty());
DCHECK(weak_objects_.js_weak_cells.IsEmpty());
DCHECK(weak_objects_.bytecode_flushing_candidates.IsEmpty());
}
@ -2267,6 +2268,20 @@ void MarkCompactCollector::ClearJSWeakCells() {
if (!FLAG_harmony_weak_refs) {
return;
}
JSWeakRef weak_ref;
while (weak_objects_.js_weak_refs.Pop(kMainThread, &weak_ref)) {
// We do not insert cleared weak cells into the list, so the value
// cannot be undefined here.
JSReceiver target = JSReceiver::cast(weak_ref->target());
if (!non_atomic_marking_state()->IsBlackOrGrey(target)) {
weak_ref->set_target(ReadOnlyRoots(isolate()).undefined_value());
} else {
// The value of the JSWeakRef is alive.
ObjectSlot slot =
HeapObject::RawField(weak_ref, JSWeakRef::kTargetOffset);
RecordSlot(weak_ref, slot, target);
}
}
JSWeakCell weak_cell;
while (weak_objects_.js_weak_cells.Pop(kMainThread, &weak_cell)) {
// We do not insert cleared weak cells into the list, so the value
@ -2312,6 +2327,7 @@ void MarkCompactCollector::AbortWeakObjects() {
weak_objects_.discovered_ephemerons.Clear();
weak_objects_.weak_references.Clear();
weak_objects_.weak_objects_in_code.Clear();
weak_objects_.js_weak_refs.Clear();
weak_objects_.js_weak_cells.Clear();
weak_objects_.bytecode_flushing_candidates.Clear();
}

View File

@ -444,6 +444,7 @@ struct WeakObjects {
Worklist<std::pair<HeapObject*, HeapObjectSlot>, 64> weak_references;
Worklist<std::pair<HeapObject*, Code>, 64> weak_objects_in_code;
Worklist<JSWeakRef, 64> js_weak_refs;
Worklist<JSWeakCell, 64> js_weak_cells;
Worklist<SharedFunctionInfo, 64> bytecode_flushing_candidates;
@ -683,6 +684,10 @@ class MarkCompactCollector final : public MarkCompactCollectorBase {
std::make_pair(object, code));
}
void AddWeakRef(JSWeakRef weak_ref) {
weak_objects_.js_weak_refs.Push(kMainThread, weak_ref);
}
void AddWeakCell(JSWeakCell weak_cell) {
weak_objects_.js_weak_cells.Push(kMainThread, weak_cell);
}
@ -951,6 +956,7 @@ class MarkingVisitor final
V8_INLINE int VisitSharedFunctionInfo(Map map, SharedFunctionInfo object);
V8_INLINE int VisitTransitionArray(Map map, TransitionArray object);
V8_INLINE int VisitJSWeakCell(Map map, JSWeakCell object);
V8_INLINE int VisitJSWeakRef(Map map, JSWeakRef object);
// ObjectVisitor implementation.
V8_INLINE void VisitPointer(HeapObject* host, ObjectSlot p) final {

View File

@ -64,8 +64,6 @@ ResultType HeapVisitor<ResultType, ConcreteVisitor>::Visit(Map map,
return visitor->VisitFreeSpace(map, FreeSpace::cast(object));
case kVisitWeakArray:
return visitor->VisitWeakArray(map, object);
case kVisitJSWeakCell:
return visitor->VisitJSWeakCell(map, JSWeakCell::cast(object));
case kVisitorIdCount:
UNREACHABLE();
}

View File

@ -26,6 +26,7 @@ class JSDataView;
class JSRegExp;
class JSTypedArray;
class JSWeakCell;
class JSWeakRef;
class JSWeakCollection;
class NativeContext;
class UncompiledDataWithoutPreParsedScope;
@ -56,7 +57,9 @@ class WasmInstanceObject;
V(JSDataView, JSDataView) \
V(JSObject, JSObject) \
V(JSTypedArray, JSTypedArray) \
V(JSWeakCell, JSWeakCell) \
V(JSWeakCollection, JSWeakCollection) \
V(JSWeakRef, JSWeakRef) \
V(Map, Map) \
V(NativeContext, NativeContext) \
V(Oddball, Oddball*) \

View File

@ -542,8 +542,8 @@ namespace internal {
"WeakFactory.prototype.makeCell: target must be an object") \
T(WeakRefsMakeCellTargetAndHoldingsMustNotBeSame, \
"WeakFactory.prototype.makeCell: target and holdings must not be same") \
T(WeakRefsMakeRefTargetMustBeObject, \
"WeakFactory.prototype.makeRef: target must be an object") \
T(WeakRefsWeakRefConstructorTargetMustBeObject, \
"WeakRef: target must be an object") \
T(WeakRefsMakeRefTargetAndHoldingsMustNotBeSame, \
"WeakFactory.prototype.makeRef: target and holdings must not be same")

View File

@ -237,6 +237,25 @@ class JSWeakCell::BodyDescriptor final : public BodyDescriptorBase {
}
};
class JSWeakRef::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(Map map, HeapObject* obj, int offset) {
return JSObject::BodyDescriptor::IsValidSlot(map, obj, offset);
}
template <typename ObjectVisitor>
static inline void IterateBody(Map map, HeapObject* obj, int object_size,
ObjectVisitor* v) {
IteratePointers(obj, JSReceiver::kPropertiesOrHashOffset, kTargetOffset, v);
IterateCustomWeakPointer(obj, kTargetOffset, v);
IteratePointers(obj, kTargetOffset + kPointerSize, object_size, v);
}
static inline int SizeOf(Map map, HeapObject* object) {
return map->instance_size();
}
};
class SharedFunctionInfo::BodyDescriptor final : public BodyDescriptorBase {
public:
static bool IsValidSlot(Map map, HeapObject* obj, int offset) {
@ -952,8 +971,9 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) {
case JS_FUNCTION_TYPE:
return Op::template apply<JSFunction::BodyDescriptor>(p1, p2, p3, p4);
case JS_WEAK_CELL_TYPE:
case JS_WEAK_REF_TYPE:
return Op::template apply<JSWeakCell::BodyDescriptor>(p1, p2, p3, p4);
case JS_WEAK_REF_TYPE:
return Op::template apply<JSWeakRef::BodyDescriptor>(p1, p2, p3, p4);
case ODDBALL_TYPE:
return Op::template apply<Oddball::BodyDescriptor>(p1, p2, p3, p4);
case JS_PROXY_TYPE:

View File

@ -331,9 +331,11 @@ void HeapObject::HeapObjectVerify(Isolate* isolate) {
isolate);
break;
case JS_WEAK_CELL_TYPE:
case JS_WEAK_REF_TYPE:
JSWeakCell::cast(this)->JSWeakCellVerify(isolate);
break;
case JS_WEAK_REF_TYPE:
JSWeakRef::cast(this)->JSWeakRefVerify(isolate);
break;
case JS_WEAK_FACTORY_TYPE:
JSWeakFactory::cast(this)->JSWeakFactoryVerify(isolate);
break;
@ -1315,6 +1317,12 @@ void JSWeakCell::JSWeakCellVerify(Isolate* isolate) {
CHECK(factory()->IsUndefined(isolate) || factory()->IsJSWeakFactory());
}
void JSWeakRef::JSWeakRefVerify(Isolate* isolate) {
CHECK(IsJSWeakRef());
JSObjectVerify(isolate);
CHECK(target()->IsUndefined(isolate) || target()->IsJSReceiver());
}
void JSWeakFactory::JSWeakFactoryVerify(Isolate* isolate) {
CHECK(IsJSWeakFactory());
JSObjectVerify(isolate);

View File

@ -301,9 +301,11 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
JSMapIterator::cast(this)->JSMapIteratorPrint(os);
break;
case JS_WEAK_CELL_TYPE:
case JS_WEAK_REF_TYPE:
JSWeakCell::cast(this)->JSWeakCellPrint(os);
break;
case JS_WEAK_REF_TYPE:
JSWeakRef::cast(this)->JSWeakRefPrint(os);
break;
case JS_WEAK_FACTORY_TYPE:
JSWeakFactory::cast(this)->JSWeakFactoryPrint(os);
break;
@ -1287,6 +1289,12 @@ void JSWeakCell::JSWeakCellPrint(std::ostream& os) {
JSObjectPrintBody(os, *this);
}
void JSWeakRef::JSWeakRefPrint(std::ostream& os) {
JSObjectPrintHeader(os, *this, "JSWeakRef");
os << "\n - target: " << Brief(target());
JSObjectPrintBody(os, *this);
}
void JSWeakFactory::JSWeakFactoryPrint(std::ostream& os) {
JSObjectPrintHeader(os, *this, "JSWeakFactory");
os << "\n - native_context: " << Brief(native_context());

View File

@ -3309,8 +3309,10 @@ VisitorId Map::GetVisitorId(Map map) {
case JS_SPECIAL_API_OBJECT_TYPE:
return kVisitJSApiObject;
case JS_WEAK_CELL_TYPE:
case JS_WEAK_REF_TYPE:
return kVisitJSWeakRef;
case JS_WEAK_CELL_TYPE:
return kVisitJSWeakCell;
case FILLER_TYPE:

View File

@ -292,8 +292,8 @@ enum InstanceType : uint16_t {
JS_SET_KEY_VALUE_ITERATOR_TYPE,
JS_SET_VALUE_ITERATOR_TYPE,
JS_STRING_ITERATOR_TYPE,
JS_WEAK_CELL_TYPE, // FIRST_JS_WEAK_CELL_TYPE
JS_WEAK_REF_TYPE, // LAST_JS_WEAK_CELL_TYPE
JS_WEAK_CELL_TYPE,
JS_WEAK_REF_TYPE,
JS_WEAK_FACTORY_CLEANUP_ITERATOR_TYPE,
JS_WEAK_FACTORY_TYPE,
JS_WEAK_MAP_TYPE,
@ -382,9 +382,6 @@ enum InstanceType : uint16_t {
FIRST_MAP_ITERATOR_TYPE = JS_MAP_KEY_ITERATOR_TYPE,
LAST_MAP_ITERATOR_TYPE = JS_MAP_VALUE_ITERATOR_TYPE,
FIRST_JS_WEAK_CELL_TYPE = JS_WEAK_CELL_TYPE,
LAST_JS_WEAK_CELL_TYPE = JS_WEAK_REF_TYPE,
};
// This constant is defined outside of the InstanceType enum because the
@ -460,6 +457,7 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
V(JSValue, JS_VALUE_TYPE) \
V(JSWeakFactory, JS_WEAK_FACTORY_TYPE) \
V(JSWeakFactoryCleanupIterator, JS_WEAK_FACTORY_CLEANUP_ITERATOR_TYPE) \
V(JSWeakCell, JS_WEAK_CELL_TYPE) \
V(JSWeakMap, JS_WEAK_MAP_TYPE) \
V(JSWeakRef, JS_WEAK_REF_TYPE) \
V(JSWeakSet, JS_WEAK_SET_TYPE) \
@ -531,7 +529,6 @@ V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
V(HashTable, FIRST_HASH_TABLE_TYPE, LAST_HASH_TABLE_TYPE) \
V(JSMapIterator, FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE) \
V(JSSetIterator, FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE) \
V(JSWeakCell, FIRST_JS_WEAK_CELL_TYPE, LAST_JS_WEAK_CELL_TYPE) \
V(Microtask, FIRST_MICROTASK_TYPE, LAST_MICROTASK_TYPE) \
V(Name, FIRST_NAME_TYPE, LAST_NAME_TYPE) \
V(String, FIRST_STRING_TYPE, LAST_STRING_TYPE) \

View File

@ -18,7 +18,7 @@ namespace v8 {
namespace internal {
OBJECT_CONSTRUCTORS_IMPL(JSWeakCell, JSObject)
OBJECT_CONSTRUCTORS_IMPL(JSWeakRef, JSWeakCell)
OBJECT_CONSTRUCTORS_IMPL(JSWeakRef, JSObject)
OBJECT_CONSTRUCTORS_IMPL(JSWeakFactory, JSObject)
OBJECT_CONSTRUCTORS_IMPL(JSWeakFactoryCleanupIterator, JSObject)
@ -38,6 +38,7 @@ ACCESSORS(JSWeakCell, prev, Object, kPrevOffset)
CAST_ACCESSOR2(JSWeakCell)
CAST_ACCESSOR2(JSWeakRef)
ACCESSORS(JSWeakRef, target, Object, kTargetOffset)
ACCESSORS2(JSWeakFactoryCleanupIterator, factory, JSWeakFactory, kFactoryOffset)
CAST_ACCESSOR2(JSWeakFactoryCleanupIterator)

View File

@ -115,10 +115,20 @@ class JSWeakCell : public JSObject {
OBJECT_CONSTRUCTORS(JSWeakCell, JSObject);
};
class JSWeakRef : public JSWeakCell {
class JSWeakRef : public JSObject {
public:
DECL_PRINTER(JSWeakRef)
DECL_VERIFIER(JSWeakRef)
DECL_CAST2(JSWeakRef)
OBJECT_CONSTRUCTORS(JSWeakRef, JSWeakCell);
DECL_ACCESSORS(target, Object)
static const int kTargetOffset = JSObject::kHeaderSize;
static const int kSize = kTargetOffset + kPointerSize;
class BodyDescriptor;
OBJECT_CONSTRUCTORS(JSWeakRef, JSObject);
};
class WeakFactoryCleanupJobTask : public Microtask {

View File

@ -47,6 +47,7 @@ enum InstanceType : uint16_t;
V(JSObjectFast) \
V(JSTypedArray) \
V(JSWeakCell) \
V(JSWeakRef) \
V(JSWeakCollection) \
V(Map) \
V(NativeContext) \

View File

@ -151,14 +151,10 @@
clear.call(wc);
})();
(function TestMakeRef() {
let wf = new WeakFactory(() => {});
let wr = wf.makeRef({});
let wc = wf.makeCell({});
(function TestWeakRefConstructor() {
let wr = new WeakRef({});
assertEquals(wr.toString(), "[object WeakRef]");
assertNotSame(wr.__proto__, Object.prototype);
assertSame(wr.__proto__.__proto__, wc.__proto__);
assertEquals(wr.holdings, undefined);
let deref_desc = Object.getOwnPropertyDescriptor(wr.__proto__, "deref");
assertEquals(true, deref_desc.configurable);
@ -166,70 +162,36 @@
assertEquals("function", typeof deref_desc.value);
})();
(function TestMakeRefWithHoldings() {
let wf = new WeakFactory(() => {});
let obj = {a: 1};
let holdings = {b: 2};
let wr = wf.makeRef(obj, holdings);
assertSame(wr.holdings, holdings);
(function TestWeakRefConstructorWithNonObject() {
let message = "WeakRef: target must be an object";
assertThrows(() => new WeakRef(), TypeError, message);
assertThrows(() => new WeakRef(1), TypeError, message);
assertThrows(() => new WeakRef(false), TypeError, message);
assertThrows(() => new WeakRef("foo"), TypeError, message);
assertThrows(() => new WeakRef(Symbol()), TypeError, message);
assertThrows(() => new WeakRef(null), TypeError, message);
assertThrows(() => new WeakRef(undefined), TypeError, message);
})();
(function TestMakeRefWithHoldingsSetHoldings() {
let wf = new WeakFactory(() => {});
let obj = {a: 1};
let holdings = {b: 2};
let wr = wf.makeRef(obj, holdings);
assertSame(wr.holdings, holdings);
wr.holdings = 5;
assertSame(wr.holdings, holdings);
(function TestWeakRefConstructorCallAsFunction() {
let caught = false;
let message = "";
try {
let f = WeakRef({});
} catch (e) {
message = e.message;
caught = true;
} finally {
assertTrue(caught);
assertEquals(message, "Constructor WeakRef requires 'new'");
}
})();
(function TestMakeRefWithHoldingsSetHoldingsStrict() {
"use strict";
let wf = new WeakFactory(() => {});
let obj = {a: 1};
let holdings = {b: 2};
let wr = wf.makeRef(obj, holdings);
assertSame(wr.holdings, holdings);
assertThrows(() => { wr.holdings = 5; }, TypeError);
assertSame(wr.holdings, holdings);
})();
(function TestMakeRefWithNonObject() {
let wf = new WeakFactory(() => {});
let message = "WeakFactory.prototype.makeRef: target must be an object";
assertThrows(() => wf.makeRef(), TypeError, message);
assertThrows(() => wf.makeRef(1), TypeError, message);
assertThrows(() => wf.makeRef(false), TypeError, message);
assertThrows(() => wf.makeRef("foo"), TypeError, message);
assertThrows(() => wf.makeRef(Symbol()), TypeError, message);
assertThrows(() => wf.makeRef(null), TypeError, message);
assertThrows(() => wf.makeRef(undefined), TypeError, message);
})();
(function TestMakeRefWithProxy() {
(function TestWeakRefWithProxy() {
let handler = {};
let obj = {};
let proxy = new Proxy(obj, handler);
let wf = new WeakFactory(() => {});
let wr = wf.makeRef(proxy);
})();
(function TestMakeRefTargetAndHoldingsSameValue() {
let wf = new WeakFactory(() => {});
let obj = {a: 1};
// SameValue(target, holdings) not ok
assertThrows(() => wf.makeRef(obj, obj), TypeError,
"WeakFactory.prototype.makeRef: target and holdings must not be same");
let holdings = {a: 1};
let wr = wf.makeRef(obj, holdings);
})();
(function TestMakeRefWithoutWeakFactory() {
assertThrows(() => WeakFactory.prototype.makeRef.call({}, {}), TypeError);
// Does not throw:
let wf = new WeakFactory(() => {});
WeakFactory.prototype.makeRef.call(wf, {});
let wr = new WeakRef(proxy);
})();
(function TestCleanupSomeWithoutWeakFactory() {
@ -243,24 +205,10 @@
(function TestDerefWithoutWeakRef() {
let wf = new WeakFactory(() => {});
let wc = wf.makeCell({});
let wr = wf.makeRef({});
let wr = new WeakRef({});
let deref = Object.getOwnPropertyDescriptor(wr.__proto__, "deref").value;
assertThrows(() => deref.call({}), TypeError);
assertThrows(() => deref.call(wc), TypeError);
// Does not throw:
deref.call(wr);
})();
(function TestWeakRefClearAfterProtoChange() {
let wf = new WeakFactory(() => {});
let wc = wf.makeCell({});
let wr = wf.makeRef({});
// Does not throw:
wr.clear();
wr.__proto__ = {};
assertThrows(() => wr.clear(), TypeError);
let clear = Object.getOwnPropertyDescriptor(wc.__proto__, "clear").value;
// Does not throw:
clear.call(wr);
})();

View File

@ -1,51 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_count = 0;
let cleanup_cells = [];
let cleanup = function(iter) {
for (wc of iter) {
cleanup_cells.push(wc);
}
++cleanup_count;
}
let o = {};
let wf = new WeakFactory(cleanup);
let weak_ref;
(function() {
weak_ref = wf.makeRef(o);
// cleanupSome won't do anything since there are no dirty WeakCells.
wf.cleanupSome();
assertEquals(0, cleanup_count);
})();
// Clear the KeepDuringJob set.
%PerformMicrotaskCheckpoint();
weak_ref.deref();
o = null;
// The WeakRef is not detected as dirty, since the KeepDuringJob set keeps the
// target object alive.
gc();
wf.cleanupSome();
assertEquals(0, cleanup_count);
%PerformMicrotaskCheckpoint();
// Next turn.
// This GC detects the WeakRef as dirty.
gc();
// Clear the WeakRef just before we would've called cleanupSome.
weak_ref.clear();
wf.cleanupSome();
assertEquals(0, cleanup_count);

View File

@ -1,57 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_count = 0;
let cleanup_cells = [];
let cleanup = function(iter) {
for (wc of iter) {
cleanup_cells.push(wc);
}
++cleanup_count;
}
let o = {};
let wf = new WeakFactory(cleanup);
let weak_ref;
(function() {
weak_ref = wf.makeRef(o);
// cleanupSome won't do anything since there are no dirty WeakCells.
wf.cleanupSome();
assertEquals(0, cleanup_count);
})();
// Clear the KeepDuringJob set.
%PerformMicrotaskCheckpoint();
weak_ref.deref();
o = null;
// The WeakRef is not detected as dirty, since the KeepDuringJob set keeps the
// target object alive.
gc();
wf.cleanupSome();
assertEquals(0, cleanup_count);
%PerformMicrotaskCheckpoint();
// Next turn.
// Now the WeakRef can be cleared.
gc();
wf.cleanupSome();
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_cells.length);
assertEquals(weak_ref, cleanup_cells[0]);
// The cleanup task is not executed again since all WeakCells have been
// processed.
%PerformMicrotaskCheckpoint();
// Next turn.
assertEquals(1, cleanup_count);

View File

@ -1,43 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_count = 0;
let cleanup_cells = [];
let cleanup = function(iter) {
for (wc of iter) {
cleanup_cells.push(wc);
}
++cleanup_count;
}
let wf = new WeakFactory(cleanup);
let weak_ref;
(function() {
let o = {};
weak_ref = wf.makeRef(o);
// cleanupSome won't do anything since there are no dirty WeakCells.
wf.cleanupSome();
assertEquals(0, cleanup_count);
})();
// The WeakRef is not detected as dirty, since the KeepDuringJob set keeps the
// target object alive.
gc();
wf.cleanupSome();
assertEquals(0, cleanup_count);
%PerformMicrotaskCheckpoint();
// Next turn.
// Now the WeakRef can be cleared.
gc();
wf.cleanupSome();
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_cells.length);
assertEquals(weak_ref, cleanup_cells[0]);

View File

@ -1,46 +0,0 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_started = false;
let cleanup_succeeded = false;
let cleanup = function(iter) {
cleanup_start = true;
let cells = [];
for (wc of iter) {
cells.push(wc);
}
assertEquals(1, cells.length);
assertEquals(w1, cells[0]);
cleanup_succeeded = true;
}
let wf = new WeakFactory(cleanup);
let wr;
(function() {
let o = { foo: "bar" };
wr = wf.makeRef(o);
})();
// Since the WeakRef was created during this turn, they're not cleared by GC.
gc();
assertNotEquals(undefined, wr.deref());
%PerformMicrotaskCheckpoint();
// New turn.
let o = wr.deref();
assertEquals("bar", o.foo);
wr.clear();
assertEquals(undefined, wr.deref());
let timeout_func1 = function() {
assertFalse(cleanup_started);
assertFalse(cleanup_succeeded);
}
// Assert that the cleanup function won't be called.
setTimeout(timeout_func1, 0);

View File

@ -4,35 +4,18 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_count = 0;
let cleared_cells1 = [];
let cleared_cells2 = [];
let cleanup = function(iter) {
if (cleanup_count == 0) {
for (wc of iter) {
cleared_cells1.push(wc);
}
} else {
assertEquals(1, cleanup_count);
for (wc of iter) {
cleared_cells2.push(wc);
}
}
++cleanup_count;
}
let wf = new WeakFactory(cleanup);
let o1 = {};
let o2 = {};
let wr1;
let wr2;
(function() {
wr1 = wf.makeRef(o1);
wr2 = wf.makeRef(o2);
wr1 = new WeakRef(o1);
wr2 = new WeakRef(o2);
})();
// Since the WeakRefs were created during this turn, they're not cleared by GC.
gc();
(function() {
assertNotEquals(undefined, wr1.deref());
assertNotEquals(undefined, wr2.deref());
@ -41,8 +24,6 @@ gc();
%PerformMicrotaskCheckpoint();
// New turn.
assertEquals(0, cleanup_count);
wr1.deref();
o1 = null;
gc(); // deref makes sure we don't clean up wr1
@ -50,8 +31,6 @@ gc(); // deref makes sure we don't clean up wr1
%PerformMicrotaskCheckpoint();
// New turn.
assertEquals(0, cleanup_count);
wr2.deref();
o2 = null;
gc(); // deref makes sure we don't clean up wr2
@ -59,13 +38,11 @@ gc(); // deref makes sure we don't clean up wr2
%PerformMicrotaskCheckpoint();
// New turn.
assertEquals(1, cleanup_count);
assertEquals(wr1, cleared_cells1[0]);
assertEquals(undefined, wr1.deref());
gc();
%PerformMicrotaskCheckpoint();
// New turn.
assertEquals(2, cleanup_count);
assertEquals(wr2, cleared_cells2[0]);
assertEquals(undefined, wr2.deref());

View File

@ -11,9 +11,8 @@ let cleanup = function(iter) {
for (wc of iter) {
cells.push(wc);
}
assertEquals(2, cells.length);
assertTrue(cells.includes(weak_ref));
assertTrue(cells.includes(weak_cell));
assertEquals(1, cells.length);
assertEquals(weak_cell, cells[0]);
cleanup_called = true;
}
@ -22,8 +21,8 @@ let weak_ref;
let weak_cell;
(function() {
let o = {};
weak_ref = wf.makeRef(o);
weak_cell = wf.makeRef(o);
weak_ref = new WeakRef(o);
weak_cell = wf.makeCell(o);
})();
// Since the WeakRef was created during this turn, it is not cleared by GC. The
@ -43,3 +42,4 @@ gc();
// Next turn.
assertTrue(cleanup_called);
assertEquals(undefined, weak_ref.deref());

View File

@ -4,24 +4,10 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_called = false;
let cleanup = function(iter) {
assertFalse(cleanup_called);
let count = 0;
for (wc of iter) {
++count;
assertEquals(wr, wc);
assertEquals(undefined, wc.deref());
}
assertEquals(1, count);
cleanup_called = true;
}
let wf = new WeakFactory(cleanup);
let wr;
(function() {
let o = {};
wr = wf.makeRef(o);
wr = new WeakRef(o);
// Don't deref here, we want to test that the creation is enough to keep the
// WeakRef alive until the end of the turn.
})();
@ -36,11 +22,6 @@ gc();
%PerformMicrotaskCheckpoint();
// Next turn.
assertFalse(cleanup_called);
gc();
%PerformMicrotaskCheckpoint();
// Next turn.
assertTrue(cleanup_called);
assertEquals(undefined, wr.deref());

View File

@ -4,25 +4,13 @@
// Flags: --harmony-weak-refs --expose-gc --noincremental-marking --allow-natives-syntax
let cleanup_count = 0;
let cleanup_cells = [];
let cleanup = function(iter) {
for (wc of iter) {
assertEquals(undefined, wc.deref());
cleanup_cells.push(wc);
}
++cleanup_count;
}
let wf = new WeakFactory(cleanup);
let wf_control = new WeakFactory(cleanup);
let wr;
let wr_control; // control WeakRef for testing what happens without deref
(function() {
let o1 = {};
wr = wf.makeRef(o1);
wr = new WeakRef(o1);
let o2 = {};
wr_control = wf_control.makeRef(o2);
wr_control = new WeakRef(o2);
})();
let strong = {a: wr.deref(), b: wr_control.deref()};
@ -32,14 +20,6 @@ gc();
%PerformMicrotaskCheckpoint();
// Next turn.
gc();
%PerformMicrotaskCheckpoint();
// Next turn.
// We have a strong reference to the objects, so the WeakRefs are not cleared yet.
assertEquals(0, cleanup_count);
// Call deref inside a closure, trying to avoid accidentally storing a strong
// reference into the object in the stack frame.
(function() {
@ -60,17 +40,6 @@ gc();
%PerformMicrotaskCheckpoint();
// Next turn.
assertEquals(1, cleanup_count);
assertEquals(1, cleanup_cells.length);
assertEquals(wc, cleanup_cells[0]);
gc();
%PerformMicrotaskCheckpoint();
// Next turn.
assertEquals(2, cleanup_count);
assertEquals(2, cleanup_cells.length);
assertEquals(wr, cleanup_cells[1]);
assertEquals(undefined, wr.deref());