Commit 9c5053bf authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Re-enable Set and WeakSet constructor fast path.

- Add Map, WeakMap, Set, and WeakSet initial prototype maps to native context.
- Set and WeakSet constructors check whether prototype map differs from initial
  before choosing the fast path.

Bug: chromium:798026
Change-Id: I5f9cc2463f89e17f06a66b565c625fce133d01fb
Reviewed-on: https://chromium-review.googlesource.com/853698
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50691}
parent b3970791
......@@ -3265,6 +3265,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
BuiltinFunctionId::kMapSize);
SimpleInstallFunction(prototype, "values", Builtins::kMapPrototypeValues, 0,
true);
native_context()->set_initial_map_prototype_map(prototype->map());
InstallSpeciesGetter(js_map_fun);
}
......@@ -3316,6 +3319,9 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
JSObject::AddProperty(prototype, factory->keys_string(), values, DONT_ENUM);
JSObject::AddProperty(prototype, factory->iterator_symbol(), values,
DONT_ENUM);
native_context()->set_initial_set_prototype_map(prototype->map());
InstallSpeciesGetter(js_set_fun);
}
......@@ -3382,13 +3388,16 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kWeakMapPrototypeDelete, 1, true);
SimpleInstallFunction(prototype, "get", Builtins::kWeakMapGet, 1, true);
SimpleInstallFunction(prototype, "has", Builtins::kWeakMapHas, 1, true);
SimpleInstallFunction(prototype, "set", Builtins::kWeakMapPrototypeSet, 2,
true);
Handle<JSFunction> weakmap_set = SimpleInstallFunction(
prototype, "set", Builtins::kWeakMapPrototypeSet, 2, true);
native_context()->set_weakmap_set(*weakmap_set);
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("WeakMap"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
native_context()->set_initial_weakmap_prototype_map(prototype->map());
}
{ // -- W e a k S e t
......@@ -3410,13 +3419,16 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(prototype, "delete",
Builtins::kWeakSetPrototypeDelete, 1, true);
SimpleInstallFunction(prototype, "has", Builtins::kWeakSetHas, 1, true);
SimpleInstallFunction(prototype, "add", Builtins::kWeakSetPrototypeAdd, 1,
true);
Handle<JSFunction> weakset_add = SimpleInstallFunction(
prototype, "add", Builtins::kWeakSetPrototypeAdd, 1, true);
native_context()->set_weakset_add(*weakset_add);
JSObject::AddProperty(
prototype, factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("WeakSet"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
native_context()->set_initial_weakset_prototype_map(prototype->map());
}
{ // -- P r o x y
......
......@@ -289,12 +289,8 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithSpread(
&if_runtime);
// Check that the map of the initial array iterator hasn't changed.
Node* native_context = LoadNativeContext(context);
Node* arr_it_proto_map = LoadMap(CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)));
Node* initial_map = LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX);
GotoIfNot(WordEqual(arr_it_proto_map, initial_map), &if_runtime);
TNode<Context> native_context = LoadNativeContext(context);
GotoIfNot(HasInitialArrayIteratorPrototypeMap(native_context), &if_runtime);
Node* kind = LoadMapElementsKind(spread_map);
......
......@@ -26,13 +26,13 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
virtual ~BaseCollectionsAssembler() {}
protected:
enum Variant { kMap, kSet };
enum Variant { kMap, kSet, kWeakMap, kWeakSet };
// Adds an entry to a collection. For Maps, properly handles extracting the
// key and value from the entry (see LoadKeyValue()).
TNode<Object> AddConstructorEntry(Variant variant, TNode<Context> context,
TNode<Object> collection,
TNode<Object> add_function,
TNode<JSFunction> add_function,
TNode<Object> key_value,
Label* if_exception = nullptr,
TVariable<Object>* var_exception = nullptr);
......@@ -49,6 +49,7 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
// JS array (see CodeStubAssembler::BranchIfFastJSArray()).
void AddConstructorEntriesFromFastJSArray(Variant variant,
TNode<Context> context,
TNode<Context> native_context,
TNode<Object> collection,
TNode<JSArray> fast_jsarray);
......@@ -61,8 +62,7 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
// Constructs a collection instance. Choosing a fast path when possible.
TNode<Object> AllocateJSCollection(TNode<Context> context,
TNode<Context> native_context,
int constructor_function_index,
TNode<JSFunction> constructor,
TNode<Object> new_target);
// Fast path for constructing a collection instance if the constructor
......@@ -72,7 +72,7 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
// Fallback for constructing a collection instance if the constructor function
// has been modified.
TNode<Object> AllocateJSCollectionSlow(TNode<Context> context,
TNode<HeapObject> constructor,
TNode<JSFunction> constructor,
TNode<Object> new_target);
// Allocates the backing store for a collection.
......@@ -81,15 +81,26 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
// Main entry point for a collection constructor builtin.
void GenerateConstructor(Variant variant,
const int constructor_function_index,
Handle<String> constructor_function_name,
int collection_tableoffset);
Handle<String> constructor_function_name);
// Retrieves the collection function that adds an entry. `set` for Maps and
// `add` for Sets.
TNode<Object> GetAddFunction(Variant variant, TNode<Context> context,
TNode<JSFunction> GetAddFunction(Variant variant, TNode<Context> context,
TNode<Object> collection);
// Retrieves the collection constructor function.
TNode<JSFunction> GetConstructor(Variant variant,
TNode<Context> native_context);
// Retrieves the initial collection function that adds an entry. Should only
// be called when it is certain that a collection prototype's map hasn't been
// changed.
TNode<JSFunction> GetInitialAddFunction(Variant variant,
TNode<Context> native_context);
// Retrieves the offset to access the backing table from the collection.
int GetTableOffset(Variant variant);
// Estimates the number of entries the collection will have after adding the
// entries passed in the constructor. AllocateTable() can use this to avoid
// the time of growing/rehashing when adding the constructor entries.
......@@ -98,6 +109,11 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
void GotoIfNotJSReceiver(Node* const obj, Label* if_not_receiver);
// Determines whether the collection's prototype has been modified.
TNode<BoolT> HasInitialCollectionPrototype(Variant variant,
TNode<Context> native_context,
TNode<Object> collection);
// Loads an element from a fixed array. If the element is the hole, returns
// `undefined`.
TNode<Object> LoadAndNormalizeFixedArrayElement(TNode<Object> elements,
......@@ -118,10 +134,10 @@ class BaseCollectionsAssembler : public CodeStubAssembler {
TNode<Object> BaseCollectionsAssembler::AddConstructorEntry(
Variant variant, TNode<Context> context, TNode<Object> collection,
TNode<Object> add_function, TNode<Object> key_value, Label* if_exception,
TVariable<Object>* var_exception) {
TNode<JSFunction> add_function, TNode<Object> key_value,
Label* if_exception, TVariable<Object>* var_exception) {
CSA_ASSERT(this, Word32BinaryNot(IsTheHole(key_value)));
if (variant == kMap) {
if (variant == kMap || variant == kWeakMap) {
Label exit(this), if_notobject(this, Label::kDeferred);
GotoIfNotJSReceiver(key_value, &if_notobject);
......@@ -149,8 +165,8 @@ TNode<Object> BaseCollectionsAssembler::AddConstructorEntry(
BIND(&exit);
return add_call;
} else { // variant == kSet
DCHECK(variant == kSet);
} else {
DCHECK(variant == kSet || variant == kWeakSet);
return UncheckedCast<Object>(CallJS(CodeFactory::Call(isolate()), context,
add_function, collection, key_value));
}
......@@ -163,27 +179,41 @@ void BaseCollectionsAssembler::AddConstructorEntries(
Label exit(this), slow_loop(this, Label::kDeferred);
GotoIf(IsNullOrUndefined(initial_entries), &exit);
// TODO(mvstanton): Re-enable the fast path when a fix is found for
// crbug.com/798026.
// TODO(pwong): Re-enable the fast path for Map/WeakMap when a fix for
// handling key/value access side-effects is found.
if (variant == kSet || variant == kWeakSet) {
GotoIfNot(is_fast_jsarray, &slow_loop);
{
CSA_ASSERT(this, IsFastJSArray(initial_entries, context));
GotoIfNot(
HasInitialCollectionPrototype(variant, native_context, collection),
&slow_loop);
AddConstructorEntriesFromFastJSArray(
variant, context, native_context, collection,
UncheckedCast<JSArray>(initial_entries));
Goto(&exit);
}
BIND(&slow_loop);
}
AddConstructorEntriesFromIterable(variant, context, native_context,
collection, initial_entries);
Goto(&exit);
}
BIND(&exit);
}
void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
Variant variant, TNode<Context> context, TNode<Object> collection,
TNode<JSArray> fast_jsarray) {
Variant variant, TNode<Context> context, TNode<Context> native_context,
TNode<Object> collection, TNode<JSArray> fast_jsarray) {
TNode<FixedArrayBase> elements = LoadElements(fast_jsarray);
TNode<Int32T> elements_kind = LoadMapElementsKind(LoadMap(fast_jsarray));
TNode<IntPtrT> length = SmiUntag(LoadFastJSArrayLength(fast_jsarray));
TNode<Object> add_func = GetAddFunction(variant, context, collection);
TNode<JSFunction> add_func = GetInitialAddFunction(variant, native_context);
CSA_ASSERT(this, IsFastJSArray(fast_jsarray, context));
CSA_ASSERT(this, IsFastElementsKind(elements_kind));
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(length, IntPtrConstant(0)));
CSA_ASSERT(
this, HasInitialCollectionPrototype(variant, native_context, collection));
Label exit(this), if_doubles(this), if_smiorobjects(this);
Branch(IsFastSmiOrTaggedElementsKind(elements_kind), &if_smiorobjects,
......@@ -195,6 +225,11 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
elements, UncheckedCast<IntPtrT>(index));
AddConstructorEntry(variant, context, collection, add_func, element);
};
// Instead of using the slower iteration protocol to iterate over the
// elements, a fast loop is used. This assumes that adding an element
// to the collection does not call user code that could mutate the elements
// or collection.
BuildFastLoop(IntPtrConstant(0), length, set_entry, 1,
ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
Goto(&exit);
......@@ -203,7 +238,7 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
{
// A Map constructor requires entries to be arrays (ex. [key, value]),
// so a FixedDoubleArray can never succeed.
if (variant == kMap) {
if (variant == kMap || variant == kWeakMap) {
TNode<Float64T> element =
UncheckedCast<Float64T>(LoadFixedDoubleArrayElement(
elements, IntPtrConstant(0), MachineType::Float64(), 0,
......@@ -211,6 +246,7 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromFastJSArray(
ThrowTypeError(context, MessageTemplate::kIteratorValueNotAnObject,
AllocateHeapNumberWithValue(element));
} else {
DCHECK(variant == kSet || variant == kWeakSet);
auto set_entry = [&](Node* index) {
TNode<Object> entry = LoadAndNormalizeFixedDoubleArrayElement(
elements, UncheckedCast<IntPtrT>(index));
......@@ -230,7 +266,7 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
Label exit(this), loop(this), if_exception(this, Label::kDeferred);
CSA_ASSERT(this, Word32BinaryNot(IsNullOrUndefined(iterable)));
TNode<Object> add_func = GetAddFunction(variant, context, collection);
TNode<JSFunction> add_func = GetAddFunction(variant, context, collection);
IteratorBuiltinsAssembler iterator_assembler(this->state());
IteratorRecord iterator = iterator_assembler.GetIterator(context, iterable);
......@@ -262,10 +298,8 @@ void BaseCollectionsAssembler::AddConstructorEntriesFromIterable(
}
TNode<Object> BaseCollectionsAssembler::AllocateJSCollection(
TNode<Context> context, TNode<Context> native_context,
int constructor_function_index, TNode<Object> new_target) {
TNode<HeapObject> constructor =
CAST(LoadContextElement(native_context, constructor_function_index));
TNode<Context> context, TNode<JSFunction> constructor,
TNode<Object> new_target) {
TNode<BoolT> is_target_unmodified = WordEqual(constructor, new_target);
return Select<Object>(is_target_unmodified,
......@@ -286,7 +320,7 @@ TNode<Object> BaseCollectionsAssembler::AllocateJSCollectionFast(
}
TNode<Object> BaseCollectionsAssembler::AllocateJSCollectionSlow(
TNode<Context> context, TNode<HeapObject> constructor,
TNode<Context> context, TNode<JSFunction> constructor,
TNode<Object> new_target) {
ConstructorBuiltinsAssembler constructor_assembler(this->state());
return CAST(constructor_assembler.EmitFastNewObject(context, constructor,
......@@ -294,8 +328,7 @@ TNode<Object> BaseCollectionsAssembler::AllocateJSCollectionSlow(
}
void BaseCollectionsAssembler::GenerateConstructor(
Variant variant, const int constructor_function_index,
Handle<String> constructor_function_name, int collection_tableoffset) {
Variant variant, Handle<String> constructor_function_name) {
const int kIterableArg = 0;
CodeStubArguments args(
this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
......@@ -306,15 +339,18 @@ void BaseCollectionsAssembler::GenerateConstructor(
Label if_undefined(this, Label::kDeferred);
GotoIf(IsUndefined(new_target), &if_undefined);
TNode<BoolT> is_fast_jsarray = IsFastJSArray(iterable, context);
TNode<Context> native_context = LoadNativeContext(context);
TNode<BoolT> array_iterator_unchanged =
HasInitialArrayIteratorPrototypeMap(native_context);
TNode<BoolT> is_fast_jsarray = UncheckedCast<BoolT>(
Word32And(IsFastJSArray(iterable, context), array_iterator_unchanged));
TNode<IntPtrT> at_least_space_for =
EstimatedInitialSize(iterable, is_fast_jsarray);
TNode<Context> native_context = LoadNativeContext(context);
TNode<Object> collection = AllocateJSCollection(
context, native_context, constructor_function_index, new_target);
context, GetConstructor(variant, native_context), new_target);
TNode<Object> table = AllocateTable(variant, context, at_least_space_for);
StoreObjectField(collection, collection_tableoffset, table);
StoreObjectField(collection, GetTableOffset(variant), table);
AddConstructorEntries(variant, context, native_context, collection, iterable,
is_fast_jsarray);
Return(collection);
......@@ -324,12 +360,9 @@ void BaseCollectionsAssembler::GenerateConstructor(
HeapConstant(constructor_function_name));
}
TNode<Object> BaseCollectionsAssembler::GetAddFunction(
TNode<JSFunction> BaseCollectionsAssembler::GetAddFunction(
Variant variant, TNode<Context> context, TNode<Object> collection) {
// TODO(pwong): Consider calling the builtin directly when the prototype is
// unmodified. This will require tracking WeakMap/WeakSet prototypes on the
// native context.
Handle<String> add_func_name = variant == kMap
Handle<String> add_func_name = (variant == kMap || variant == kWeakMap)
? isolate()->factory()->set_string()
: isolate()->factory()->add_string();
TNode<Object> add_func =
......@@ -345,7 +378,61 @@ TNode<Object> BaseCollectionsAssembler::GetAddFunction(
HeapConstant(add_func_name), collection);
BIND(&exit);
return add_func;
return CAST(add_func);
}
TNode<JSFunction> BaseCollectionsAssembler::GetConstructor(
Variant variant, TNode<Context> native_context) {
int index;
switch (variant) {
case kMap:
index = Context::JS_MAP_FUN_INDEX;
break;
case kSet:
index = Context::JS_SET_FUN_INDEX;
break;
case kWeakMap:
index = Context::JS_WEAK_MAP_FUN_INDEX;
break;
case kWeakSet:
index = Context::JS_WEAK_SET_FUN_INDEX;
break;
}
return CAST(LoadContextElement(native_context, index));
}
TNode<JSFunction> BaseCollectionsAssembler::GetInitialAddFunction(
Variant variant, TNode<Context> native_context) {
int index;
switch (variant) {
case kMap:
index = Context::MAP_SET_INDEX;
break;
case kSet:
index = Context::SET_ADD_INDEX;
break;
case kWeakMap:
index = Context::WEAKMAP_SET_INDEX;
break;
case kWeakSet:
index = Context::WEAKSET_ADD_INDEX;
break;
}
return CAST(LoadContextElement(native_context, index));
}
int BaseCollectionsAssembler::GetTableOffset(Variant variant) {
switch (variant) {
case kMap:
return JSMap::kTableOffset;
case kSet:
return JSSet::kTableOffset;
case kWeakMap:
return JSWeakMap::kTableOffset;
case kWeakSet:
return JSWeakSet::kTableOffset;
}
UNREACHABLE();
}
TNode<IntPtrT> BaseCollectionsAssembler::EstimatedInitialSize(
......@@ -362,6 +449,31 @@ void BaseCollectionsAssembler::GotoIfNotJSReceiver(Node* const obj,
GotoIfNot(IsJSReceiver(obj), if_not_receiver);
}
TNode<BoolT> BaseCollectionsAssembler::HasInitialCollectionPrototype(
Variant variant, TNode<Context> native_context, TNode<Object> collection) {
int initial_prototype_index;
switch (variant) {
case kMap:
initial_prototype_index = Context::INITIAL_MAP_PROTOTYPE_MAP_INDEX;
break;
case kSet:
initial_prototype_index = Context::INITIAL_SET_PROTOTYPE_MAP_INDEX;
break;
case kWeakMap:
initial_prototype_index = Context::INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX;
break;
case kWeakSet:
initial_prototype_index = Context::INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX;
break;
}
TNode<Map> initial_prototype_map =
CAST(LoadContextElement(native_context, initial_prototype_index));
TNode<Map> collection_proto_map =
LoadMap(CAST(LoadMapPrototype(LoadMap(CAST(collection)))));
return WordEqual(collection_proto_map, initial_prototype_map);
}
TNode<Object> BaseCollectionsAssembler::LoadAndNormalizeFixedArrayElement(
TNode<Object> elements, TNode<IntPtrT> index) {
TNode<Object> element = CAST(LoadFixedArrayElement(elements, index));
......@@ -672,18 +784,17 @@ Node* CollectionsBuiltinsAssembler::AllocateJSCollectionIterator(
TNode<Object> CollectionsBuiltinsAssembler::AllocateTable(
Variant variant, TNode<Context> context,
TNode<IntPtrT> at_least_space_for) {
return CAST(variant == kMap ? AllocateOrderedHashTable<OrderedHashMap>()
return CAST((variant == kMap || variant == kWeakMap)
? AllocateOrderedHashTable<OrderedHashMap>()
: AllocateOrderedHashTable<OrderedHashSet>());
}
TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) {
GenerateConstructor(kMap, Context::JS_MAP_FUN_INDEX,
isolate()->factory()->Map_string(), JSMap::kTableOffset);
GenerateConstructor(kMap, isolate()->factory()->Map_string());
}
TF_BUILTIN(SetConstructor, CollectionsBuiltinsAssembler) {
GenerateConstructor(kSet, Context::JS_SET_FUN_INDEX,
isolate()->factory()->Set_string(), JSSet::kTableOffset);
GenerateConstructor(kSet, isolate()->factory()->Set_string());
}
Node* CollectionsBuiltinsAssembler::CallGetOrCreateHashRaw(Node* const key) {
......@@ -2222,15 +2333,11 @@ TNode<IntPtrT> WeakCollectionsBuiltinsAssembler::ValueIndexFromKeyIndex(
}
TF_BUILTIN(WeakMapConstructor, WeakCollectionsBuiltinsAssembler) {
GenerateConstructor(kMap, Context::JS_WEAK_MAP_FUN_INDEX,
isolate()->factory()->WeakMap_string(),
JSWeakMap::kTableOffset);
GenerateConstructor(kWeakMap, isolate()->factory()->WeakMap_string());
}
TF_BUILTIN(WeakSetConstructor, WeakCollectionsBuiltinsAssembler) {
GenerateConstructor(kSet, Context::JS_WEAK_SET_FUN_INDEX,
isolate()->factory()->WeakSet_string(),
JSWeakSet::kTableOffset);
GenerateConstructor(kWeakSet, isolate()->factory()->WeakSet_string());
}
TF_BUILTIN(WeakMapLookupHashIndex, WeakCollectionsBuiltinsAssembler) {
......
......@@ -1265,6 +1265,15 @@ Node* CodeStubAssembler::HasInstanceType(Node* object,
return InstanceTypeEqual(LoadInstanceType(object), instance_type);
}
TNode<BoolT> CodeStubAssembler::HasInitialArrayIteratorPrototypeMap(
TNode<Context> native_context) {
TNode<Map> arr_it_proto_map = LoadMap(CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_INDEX)));
TNode<Map> initial_map = CAST(LoadContextElement(
native_context, Context::INITIAL_ARRAY_ITERATOR_PROTOTYPE_MAP_INDEX));
return WordEqual(arr_it_proto_map, initial_map);
}
Node* CodeStubAssembler::DoesntHaveInstanceType(Node* object,
InstanceType instance_type) {
return Word32NotEqual(LoadInstanceType(object), Int32Constant(instance_type));
......
......@@ -497,6 +497,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<Int32T> LoadInstanceType(SloppyTNode<HeapObject> object);
// Compare the instance the type of the object against the provided one.
Node* HasInstanceType(Node* object, InstanceType type);
// Determines whether the Array Iterator's prototype has changed.
TNode<BoolT> HasInitialArrayIteratorPrototypeMap(
TNode<Context> native_context);
Node* DoesntHaveInstanceType(Node* object, InstanceType type);
Node* TaggedDoesntHaveInstanceType(Node* any_tagged, InstanceType type);
// Load the properties backing store of a JSObject.
......
......@@ -121,7 +121,10 @@ enum ContextLookupFlags {
V(WASM_COMPILE_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_compile_error_function) \
V(WASM_LINK_ERROR_FUNCTION_INDEX, JSFunction, wasm_link_error_function) \
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, wasm_runtime_error_function)
V(WASM_RUNTIME_ERROR_FUNCTION_INDEX, JSFunction, \
wasm_runtime_error_function) \
V(WEAKMAP_SET_INDEX, JSFunction, weakmap_set) \
V(WEAKSET_ADD_INDEX, JSFunction, weakset_add)
#define NATIVE_CONTEXT_JS_ARRAY_ITERATOR_MAPS(V) \
V(TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX, Map, typed_array_key_iterator_map) \
......@@ -266,8 +269,12 @@ enum ContextLookupFlags {
V(INITIAL_ASYNC_GENERATOR_PROTOTYPE_INDEX, JSObject, \
initial_async_generator_prototype) \
V(INITIAL_ITERATOR_PROTOTYPE_INDEX, JSObject, initial_iterator_prototype) \
V(INITIAL_MAP_PROTOTYPE_MAP_INDEX, Map, initial_map_prototype_map) \
V(INITIAL_OBJECT_PROTOTYPE_INDEX, JSObject, initial_object_prototype) \
V(INITIAL_SET_PROTOTYPE_MAP_INDEX, Map, initial_set_prototype_map) \
V(INITIAL_STRING_PROTOTYPE_INDEX, JSObject, initial_string_prototype) \
V(INITIAL_WEAKMAP_PROTOTYPE_MAP_INDEX, Map, initial_weakmap_prototype_map) \
V(INITIAL_WEAKSET_PROTOTYPE_MAP_INDEX, Map, initial_weakset_prototype_map) \
V(INT16_ARRAY_FUN_INDEX, JSFunction, int16_array_fun) \
V(INT32_ARRAY_FUN_INDEX, JSFunction, int32_array_fun) \
V(INT8_ARRAY_FUN_INDEX, JSFunction, int8_array_fun) \
......
// 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: --allow-natives-syntax --opt
function TestSetWithModifiedIterator(ctor) {
const k1 = {};
const k2 = {};
const entries = [k1, k2];
const arrayIteratorProto = Object.getPrototypeOf(entries[Symbol.iterator]());
const originalNext = arrayIteratorProto.next;
let callCount = 0;
arrayIteratorProto.next = function() {
callCount++;
return originalNext.call(this);
};
const set = new ctor(entries);
assertEquals(3, callCount); // +1 for iterator done
if('size' in set) assertEquals(2, set.size);
assertTrue(set.has(k1));
assertTrue(set.has(k2));
arrayIteratorProto.next = originalNext;
}
TestSetWithModifiedIterator(Set);
TestSetWithModifiedIterator(Set);
TestSetWithModifiedIterator(Set);
%OptimizeFunctionOnNextCall(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(Set);
assertOptimized(TestSetWithModifiedIterator);
%DeoptimizeFunction(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(WeakSet);
TestSetWithModifiedIterator(WeakSet);
TestSetWithModifiedIterator(WeakSet);
%OptimizeFunctionOnNextCall(TestSetWithModifiedIterator);
TestSetWithModifiedIterator(WeakSet);
assertOptimized(TestSetWithModifiedIterator);
%DeoptimizeFunction(TestSetWithModifiedIterator);
function TestMapWithModifiedIterator(ctor) {
const k1 = {};
const k2 = {};
const entries = [[k1, 1], [k2, 2]];
const arrayIteratorProto = Object.getPrototypeOf(entries[Symbol.iterator]());
const originalNext = arrayIteratorProto.next;
let callCount = 0;
arrayIteratorProto.next = function() {
callCount++;
return originalNext.call(this);
};
const set = new ctor(entries);
assertEquals(3, callCount); // +1 for iterator done
if('size' in set) assertEquals(2, set.size);
assertEquals(1, set.get(k1));
assertEquals(2, set.get(k2));
arrayIteratorProto.next = originalNext;
}
TestMapWithModifiedIterator(Map);
TestMapWithModifiedIterator(Map);
TestMapWithModifiedIterator(Map);
%OptimizeFunctionOnNextCall(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(Map);
assertOptimized(TestMapWithModifiedIterator);
%DeoptimizeFunction(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(WeakMap);
TestMapWithModifiedIterator(WeakMap);
TestMapWithModifiedIterator(WeakMap);
%OptimizeFunctionOnNextCall(TestMapWithModifiedIterator);
TestMapWithModifiedIterator(WeakMap);
assertOptimized(TestMapWithModifiedIterator);
%DeoptimizeFunction(TestMapWithModifiedIterator);
// 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: --allow-natives-syntax --opt
function TestSetPrototypeModified(ctor) {
const originalPrototypeAdd = ctor.prototype.add;
const k1 = {};
const k2 = {};
const entries = [k1, k2];
let addCount = 0;
ctor.prototype.add = function(value) {
addCount++;
originalPrototypeAdd.call(this, value);
entries.length = 1;
};
const set = new ctor(entries);
assertEquals(1, addCount);
assertTrue(set.has(k1));
assertFalse(set.has(k2));
ctor.prototype.add = originalPrototypeAdd;
}
TestSetPrototypeModified(Set);
TestSetPrototypeModified(Set);
TestSetPrototypeModified(Set);
%OptimizeFunctionOnNextCall(TestSetPrototypeModified);
TestSetPrototypeModified(Set);
assertOptimized(TestSetPrototypeModified);
%DeoptimizeFunction(TestSetPrototypeModified);
TestSetPrototypeModified(WeakSet);
TestSetPrototypeModified(WeakSet);
TestSetPrototypeModified(WeakSet);
%OptimizeFunctionOnNextCall(TestSetPrototypeModified);
TestSetPrototypeModified(WeakSet);
assertOptimized(TestSetPrototypeModified);
%DeoptimizeFunction(TestSetPrototypeModified);
function TestMapPrototypeModified(ctor) {
const originalPrototypeSet = ctor.prototype.set;
const k1 = {};
const k2 = {};
const entries = [[k1, 1], [k2, 2]];
let setCount = 0;
ctor.prototype.set = function(key, value) {
setCount++;
originalPrototypeSet.call(this, key, value);
entries.length = 1;
};
const map = new ctor(entries);
assertEquals(1, setCount);
assertTrue(map.has(k1));
assertFalse(map.has(k2));
ctor.prototype.set = originalPrototypeSet;
}
TestMapPrototypeModified(Map);
TestMapPrototypeModified(Map);
TestMapPrototypeModified(Map);
%OptimizeFunctionOnNextCall(TestMapPrototypeModified);
TestMapPrototypeModified(Map);
assertOptimized(TestMapPrototypeModified);
%DeoptimizeFunction(TestMapPrototypeModified);
TestMapPrototypeModified(WeakMap);
TestMapPrototypeModified(WeakMap);
TestMapPrototypeModified(WeakMap);
%OptimizeFunctionOnNextCall(TestMapPrototypeModified);
TestMapPrototypeModified(WeakMap);
assertOptimized(TestMapPrototypeModified);
// 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: --allow-natives-syntax --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
let callCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
input.length = 2;
return k1;
}
}),
[k2, 2],
Object.defineProperty([, 3], "0", {
get() {
callCount++;
return k3;
}
})
];
const col = new ctor(input);
assertEquals(0, callCount);
if ('size' in col) assertEquals(2, col.size);
assertEquals(col.get(k1), 1);
assertEquals(col.get(k2), 2);
assertFalse(col.has(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// 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: --allow-natives-syntax --opt
function TestMapConstructorEntrySideEffect(ctor) {
const originalPrototypeSet = ctor.prototype.set;
const k1 = {};
const k2 = {};
let callCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify continuation retains original set function
ctor.prototype.set = () => {
callCount++;
};
return k1;
}
}),
[k2, 2]
];
const col = new ctor(input);
assertEquals(0, callCount);
if ('size' in col) assertEquals(2, col.size);
assertTrue(col.has(k1));
assertTrue(col.has(k2));
const col2 = new ctor(input);
assertEquals(2, callCount);
if ('size' in col) assertEquals(0, col2.size);
assertFalse(col2.has(k1));
assertFalse(col2.has(k2));
ctor.prototype.set = originalPrototypeSet;
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// 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: --allow-natives-syntax --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify continuation accesses properly accesses subsequent entries
Object.defineProperty(input, "1", {
get: () => [k3, 3]
});
return k1;
}
}),
[k2, 2]
];
const col = new ctor(input);
if ('size' in col) assertEquals(2, col.size);
assertTrue(col.has(k1));
assertFalse(col.has(k2));
assertTrue(col.has(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
// 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: --allow-natives-syntax --opt
function TestMapConstructorEntrySideEffect(ctor) {
const k1 = {};
const k2 = {};
const k3 = {};
let firstEntryCallCount = 0;
let lastEntryCallCount = 0;
const input = [
Object.defineProperty([, 1], "0", {
get() {
// Verify handling of a non-Smi array length
input.length = 2 ** 32 - 2;
firstEntryCallCount++;
return k1;
}
}),
[k2, 2],
Object.defineProperty([k3, ], "1", {
get() {
input.length = 1;
lastEntryCallCount++;
return 3;
}
})
];
const col = new ctor(input);
assertEquals(1, firstEntryCallCount,);
assertEquals(1, lastEntryCallCount);
if ('size' in col) assertEquals(3, col.size);
assertEquals(1, col.get(k1));
assertEquals(2, col.get(k2));
assertEquals(3, col.get(k3));
}
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
TestMapConstructorEntrySideEffect(Map);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(Map);
assertOptimized(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
TestMapConstructorEntrySideEffect(WeakMap);
%OptimizeFunctionOnNextCall(TestMapConstructorEntrySideEffect);
TestMapConstructorEntrySideEffect(WeakMap);
assertOptimized(TestMapConstructorEntrySideEffect);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment