[modules] Implement @@iterator on namespace objects.

As part of this, introduce a new JSObject for iterating over the elements of a
FixedArray.

R=adamk@chromium.org,bmeurer@chromium.org
TBR=ulan@chromium.org
BUG=v8:1569

Review-Url: https://codereview.chromium.org/2407423002
Cr-Commit-Position: refs/heads/master@{#40265}
This commit is contained in:
neis 2016-10-13 06:34:50 -07:00 committed by Commit bot
parent 6e3af1070b
commit dafe6867f3
18 changed files with 233 additions and 33 deletions

View File

@ -209,6 +209,7 @@ AstType::bitset AstBitsetType::Lub(i::Map* map) {
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_FIXED_ARRAY_ITERATOR_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type.

View File

@ -2161,18 +2161,27 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Handle<Map> map =
factory->NewMap(JS_MODULE_NAMESPACE_TYPE, JSModuleNamespace::kSize);
Map::SetPrototype(map, isolate->factory()->null_value());
Map::EnsureDescriptorSlack(map, 2);
native_context()->set_js_module_namespace_map(*map);
// Install @@toStringTag.
PropertyAttributes attribs =
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
DataConstantDescriptor d(factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("Module"),
attribs);
Map::EnsureDescriptorSlack(map, 1);
map->AppendDescriptor(&d);
{ // Install @@toStringTag.
PropertyAttributes attribs =
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY);
DataConstantDescriptor d(factory->to_string_tag_symbol(),
factory->NewStringFromAsciiChecked("Module"),
attribs);
map->AppendDescriptor(&d);
}
// TODO(neis): Implement and install @@iterator.
{ // Install @@iterator.
Handle<JSFunction> iterator = SimpleCreateFunction(
isolate, factory->NewStringFromAsciiChecked("[Symbol.iterator]"),
Builtins::kModuleNamespaceIterator, 0, true);
iterator->shared()->set_native(true);
// TODO(neis): Is this really supposed to be writable?
DataConstantDescriptor d(factory->iterator_symbol(), iterator, DONT_ENUM);
map->AppendDescriptor(&d);
}
}
{ // -- I t e r a t o r R e s u l t
@ -2782,6 +2791,25 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
*generator_function_function);
}
{ // -- F i x e d A r r a y I t e r a t o r
int size = JSFixedArrayIterator::kHeaderSize +
JSFixedArrayIterator::kInObjectPropertyCount * kPointerSize;
Handle<Map> map = factory->NewMap(JS_FIXED_ARRAY_ITERATOR_TYPE, size);
Map::SetPrototype(map, iterator_prototype);
Map::EnsureDescriptorSlack(map,
JSFixedArrayIterator::kInObjectPropertyCount);
map->SetInObjectProperties(JSFixedArrayIterator::kInObjectPropertyCount);
map->SetConstructor(native_context->object_function());
{ // next
DataDescriptor d(factory->next_string(), JSFixedArrayIterator::kNextIndex,
DONT_ENUM, Representation::Tagged());
map->AppendDescriptor(&d);
}
native_context->set_fixed_array_iterator_map(*map);
}
{ // -- S e t I t e r a t o r
Handle<JSObject> set_iterator_prototype =
isolate->factory()->NewJSObject(isolate->object_function(), TENURED);

View File

@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins.h"
#include "src/builtins/builtins-utils.h"
#include "src/builtins/builtins.h"
#include "src/frames-inl.h"
namespace v8 {
namespace internal {
@ -13,5 +14,55 @@ void Builtins::Generate_IteratorPrototypeIterator(
assembler->Return(assembler->Parameter(0));
}
BUILTIN(ModuleNamespaceIterator) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
Handle<Object> receiver = args.at<Object>(0);
if (!receiver->IsJSModuleNamespace()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->iterator_symbol(), receiver));
}
auto ns = Handle<JSModuleNamespace>::cast(receiver);
Handle<FixedArray> names =
KeyAccumulator::GetKeys(ns, KeyCollectionMode::kOwnOnly, SKIP_SYMBOLS)
.ToHandleChecked();
return *isolate->factory()->NewJSFixedArrayIterator(names);
}
BUILTIN(FixedArrayIteratorNext) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
Handle<Object> receiver = args.at<Object>(0);
// It is an error if this function is called on anything other than the
// particular iterator object for which the function was created.
if (!receiver->IsJSFixedArrayIterator() ||
Handle<JSFixedArrayIterator>::cast(receiver)->initial_next() !=
*args.target<JSFunction>()) {
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver,
isolate->factory()->next_string(), receiver));
}
auto iterator = Handle<JSFixedArrayIterator>::cast(receiver);
Handle<Object> value;
bool done;
int index = iterator->index();
if (index < iterator->array()->length()) {
value = handle(iterator->array()->get(index), isolate);
done = false;
iterator->set_index(index + 1);
} else {
value = isolate->factory()->undefined_value();
done = true;
}
return *isolate->factory()->NewJSIteratorResult(value, done);
}
} // namespace internal
} // namespace v8

View File

@ -666,7 +666,10 @@ namespace internal {
/* ES6 section 22.2.3.3 get %TypedArray%.prototype.byteOffset */ \
TFJ(TypedArrayPrototypeByteOffset, 1) \
/* ES6 section 22.2.3.18 get %TypedArray%.prototype.length */ \
TFJ(TypedArrayPrototypeLength, 1)
TFJ(TypedArrayPrototypeLength, 1) \
\
CPP(ModuleNamespaceIterator) \
CPP(FixedArrayIteratorNext)
#define IGNORE_BUILTIN(...)

View File

@ -204,6 +204,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_FIXED_ARRAY_ITERATOR_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type.

View File

@ -210,6 +210,7 @@ enum ContextLookupFlags {
V(SECURITY_TOKEN_INDEX, Object, security_token) \
V(SELF_WEAK_CELL_INDEX, WeakCell, self_weak_cell) \
V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \
V(FIXED_ARRAY_ITERATOR_MAP_INDEX, Map, fixed_array_iterator_map) \
V(SHARED_ARRAY_BUFFER_FUN_INDEX, JSFunction, shared_array_buffer_fun) \
V(SLOPPY_ARGUMENTS_MAP_INDEX, Map, sloppy_arguments_map) \
V(SLOPPY_FUNCTION_MAP_INDEX, Map, sloppy_function_map) \

View File

@ -2625,5 +2625,26 @@ void Factory::SetStrictFunctionInstanceDescriptor(Handle<Map> map,
}
}
Handle<JSFixedArrayIterator> Factory::NewJSFixedArrayIterator(
Handle<FixedArray> array) {
// Create the "next" function (must be unique per iterator object).
Handle<Code> code(
isolate()->builtins()->builtin(Builtins::kFixedArrayIteratorNext));
// TODO(neis): Don't create a new SharedFunctionInfo each time.
Handle<JSFunction> next = isolate()->factory()->NewFunctionWithoutPrototype(
isolate()->factory()->next_string(), code, false);
next->shared()->set_native(true);
// Create the iterator.
Handle<Map> map(isolate()->native_context()->fixed_array_iterator_map());
Handle<JSFixedArrayIterator> iterator =
Handle<JSFixedArrayIterator>::cast(NewJSObjectFromMap(map));
iterator->set_initial_next(*next);
iterator->set_array(*array);
iterator->set_index(0);
iterator->InObjectPropertyAtPut(JSFixedArrayIterator::kNextIndex, *next);
return iterator;
}
} // namespace internal
} // namespace v8

View File

@ -530,6 +530,9 @@ class Factory final {
Handle<JSMapIterator> NewJSMapIterator();
Handle<JSSetIterator> NewJSSetIterator();
Handle<JSFixedArrayIterator> NewJSFixedArrayIterator(
Handle<FixedArray> array);
// Allocates a bound function.
MaybeHandle<JSBoundFunction> NewJSBoundFunction(
Handle<JSReceiver> target_function, Handle<Object> bound_this,

View File

@ -107,6 +107,7 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
case JS_ARGUMENTS_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_FIXED_ARRAY_ITERATOR_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_VALUE_TYPE:
case JS_DATE_TYPE:

View File

@ -469,6 +469,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3) {
case JS_DATE_TYPE:
case JS_ARRAY_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_FIXED_ARRAY_ITERATOR_TYPE:
case JS_TYPED_ARRAY_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_SET_TYPE:

View File

@ -143,6 +143,9 @@ void HeapObject::HeapObjectVerify() {
case JS_MODULE_NAMESPACE_TYPE:
JSModuleNamespace::cast(this)->JSModuleNamespaceVerify();
break;
case JS_FIXED_ARRAY_ITERATOR_TYPE:
JSFixedArrayIterator::cast(this)->JSFixedArrayIteratorVerify();
break;
case JS_SET_TYPE:
JSSet::cast(this)->JSSetVerify();
break;
@ -943,6 +946,16 @@ void JSModuleNamespace::JSModuleNamespaceVerify() {
module()->ObjectVerify();
}
void JSFixedArrayIterator::JSFixedArrayIteratorVerify() {
CHECK(IsJSFixedArrayIterator());
CHECK(array()->IsFixedArray());
VerifySmiField(kIndexOffset);
CHECK(initial_next()->IsJSFunction());
CHECK_LE(index(), array()->length());
}
void Module::ModuleVerify() {
Isolate* isolate = GetIsolate();
CHECK(IsModule());

View File

@ -702,6 +702,7 @@ TYPE_CHECKER(FixedDoubleArray, FIXED_DOUBLE_ARRAY_TYPE)
TYPE_CHECKER(WeakFixedArray, FIXED_ARRAY_TYPE)
TYPE_CHECKER(TransitionArray, TRANSITION_ARRAY_TYPE)
TYPE_CHECKER(JSStringIterator, JS_STRING_ITERATOR_TYPE)
TYPE_CHECKER(JSFixedArrayIterator, JS_FIXED_ARRAY_ITERATOR_TYPE)
bool HeapObject::IsJSWeakCollection() const {
return IsJSWeakMap() || IsJSWeakSet();
@ -2114,6 +2115,8 @@ int JSObject::GetHeaderSize(InstanceType type) {
return JSObject::kHeaderSize;
case JS_STRING_ITERATOR_TYPE:
return JSStringIterator::kSize;
case JS_FIXED_ARRAY_ITERATOR_TYPE:
return JSFixedArrayIterator::kHeaderSize;
default:
UNREACHABLE();
return 0;
@ -3283,6 +3286,7 @@ CAST_ACCESSOR(JSMap)
CAST_ACCESSOR(JSMapIterator)
CAST_ACCESSOR(JSMessageObject)
CAST_ACCESSOR(JSModuleNamespace)
CAST_ACCESSOR(JSFixedArrayIterator)
CAST_ACCESSOR(JSObject)
CAST_ACCESSOR(JSProxy)
CAST_ACCESSOR(JSReceiver)
@ -5725,6 +5729,10 @@ ACCESSORS(ContextExtension, extension, Object, kExtensionOffset)
ACCESSORS(JSModuleNamespace, module, Module, kModuleOffset)
ACCESSORS(JSFixedArrayIterator, array, FixedArray, kArrayOffset)
SMI_ACCESSORS(JSFixedArrayIterator, index, kIndexOffset)
ACCESSORS(JSFixedArrayIterator, initial_next, JSFunction, kNextOffset)
ACCESSORS(Module, code, Object, kCodeOffset)
ACCESSORS(Module, exports, ObjectHashTable, kExportsOffset)
ACCESSORS(Module, module_namespace, HeapObject, kModuleNamespaceOffset)

View File

@ -193,6 +193,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
case JS_TYPED_ARRAY_TYPE:
JSTypedArray::cast(this)->JSTypedArrayPrint(os);
break;
case JS_FIXED_ARRAY_ITERATOR_TYPE:
JSFixedArrayIterator::cast(this)->JSFixedArrayIteratorPrint(os);
break;
case JS_DATA_VIEW_TYPE:
JSDataView::cast(this)->JSDataViewPrint(os);
break;
@ -949,6 +952,14 @@ void JSTypedArray::JSTypedArrayPrint(std::ostream& os) { // NOLINT
JSObjectPrintBody(os, this, !WasNeutered());
}
void JSFixedArrayIterator::JSFixedArrayIteratorPrint(
std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSFixedArrayIterator");
os << "\n - array = " << Brief(array());
os << "\n - index = " << index();
os << "\n - initial_next = " << Brief(initial_next());
JSObjectPrintBody(os, this);
}
void JSDataView::JSDataViewPrint(std::ostream& os) { // NOLINT
JSObjectPrintHeader(os, this, "JSDataView");

View File

@ -72,6 +72,7 @@
// - JSDate
// - JSMessageObject
// - JSModuleNamespace
// - JSFixedArrayIterator
// - JSProxy
// - FixedArrayBase
// - ByteArray
@ -421,6 +422,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
V(JS_GENERATOR_OBJECT_TYPE) \
V(JS_MODULE_NAMESPACE_TYPE) \
V(JS_FIXED_ARRAY_ITERATOR_TYPE) \
V(JS_GLOBAL_OBJECT_TYPE) \
V(JS_GLOBAL_PROXY_TYPE) \
V(JS_API_OBJECT_TYPE) \
@ -727,6 +729,7 @@ enum InstanceType {
JS_CONTEXT_EXTENSION_OBJECT_TYPE,
JS_GENERATOR_OBJECT_TYPE,
JS_MODULE_NAMESPACE_TYPE,
JS_FIXED_ARRAY_ITERATOR_TYPE,
JS_ARRAY_TYPE,
JS_ARRAY_BUFFER_TYPE,
JS_TYPED_ARRAY_TYPE,
@ -982,6 +985,7 @@ template <class C> inline bool Is(Object* obj);
V(JSContextExtensionObject) \
V(JSGeneratorObject) \
V(JSModuleNamespace) \
V(JSFixedArrayIterator) \
V(Map) \
V(DescriptorArray) \
V(FrameArray) \
@ -10455,6 +10459,37 @@ class JSStringIterator : public JSObject {
DISALLOW_IMPLICIT_CONSTRUCTORS(JSStringIterator);
};
// A JS iterator over the elements of a FixedArray.
// This corresponds to ListIterator in ecma262/#sec-createlistiterator.
class JSFixedArrayIterator : public JSObject {
public:
DECLARE_CAST(JSFixedArrayIterator)
DECLARE_PRINTER(JSFixedArrayIterator)
DECLARE_VERIFIER(JSFixedArrayIterator)
// The array over which the iterator iterates.
DECL_ACCESSORS(array, FixedArray)
// The index of the array element that will be returned next.
DECL_INT_ACCESSORS(index)
// The initial value of the object's "next" property.
DECL_ACCESSORS(initial_next, JSFunction)
static const int kArrayOffset = JSObject::kHeaderSize;
static const int kIndexOffset = kArrayOffset + kPointerSize;
static const int kNextOffset = kIndexOffset + kPointerSize;
static const int kHeaderSize = kNextOffset + kPointerSize;
enum InObjectPropertyIndex {
kNextIndex,
kInObjectPropertyCount // Dummy.
};
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSFixedArrayIterator);
};
// OrderedHashTableIterator is an iterator that iterates over the keys and
// values of an OrderedHashTable.
//

View File

@ -78,7 +78,7 @@ bytecodes: [
/* 15 S> */ B(LdrUndefined), R(0),
B(CreateArrayLiteral), U8(0), U8(0), U8(9),
B(Star), R(1),
B(CallJSRuntime), U8(146), R(0), U8(2),
B(CallJSRuntime), U8(147), R(0), U8(2),
/* 44 S> */ B(Return),
]
constant pool: [

View File

@ -22,9 +22,8 @@ assertSame(null, Reflect.getPrototypeOf(foo));
assertTrue(Reflect.setPrototypeOf(foo, null));
assertFalse(Reflect.setPrototypeOf(foo, {}));
assertSame(null, Reflect.getPrototypeOf(foo));
// TODO(neis): The next one should include @@iterator at the end.
assertEquals(
["bla", "foo_again", "yo", Symbol.toStringTag],
["bla", "foo_again", "yo", Symbol.toStringTag, Symbol.iterator],
Reflect.ownKeys(foo));
// Its "yo" property.
@ -52,13 +51,39 @@ assertEquals(
{value: "Module", configurable: true, writable: false, enumerable: false},
Reflect.getOwnPropertyDescriptor(foo, Symbol.toStringTag));
// TODO(neis): Its @@iterator property.
// assertTrue(Reflect.has(foo, Symbol.iterator));
// assertEquals("function", typeof Reflect.get(foo, Symbol.iterator));
// assertEquals(["bla", "yo"], [...foo]);
// assertThrows(() => (42, foo[Symbol.iterator])(), TypeError);
// assertSame(foo[Symbol.iterator]().__proto__,
// ([][Symbol.iterator]()).__proto__.__proto__);
// Its @@iterator property.
assertTrue(Reflect.has(foo, Symbol.iterator));
assertEquals("function", typeof Reflect.get(foo, Symbol.iterator));
assertEquals("[Symbol.iterator]", foo[Symbol.iterator].name);
assertEquals(0, foo[Symbol.iterator].length);
assertSame(Function.prototype, foo[Symbol.iterator].__proto__);
assertEquals(
{value: foo[Symbol.iterator],
configurable: true, writable: true, enumerable: false},
Reflect.getOwnPropertyDescriptor(foo, Symbol.iterator));
assertEquals(["bla", "foo_again", "yo"], [...foo]);
assertThrows(() => (42, foo[Symbol.iterator])(), TypeError);
{
let it = foo[Symbol.iterator]();
assertSame(it.__proto__, ([][Symbol.iterator]()).__proto__.__proto__);
assertEquals(["next"], Reflect.ownKeys(it));
assertEquals(
{value: it.next, configurable: true, writable: true, enumerable: false},
Reflect.getOwnPropertyDescriptor(it, "next"));
assertEquals("function", typeof it.next);
assertEquals("next", it.next.name);
assertEquals(0, it.next.length);
assertSame(Function.prototype, it.next.__proto__);
assertFalse(it === foo[Symbol.iterator]());
assertFalse(it.next === foo[Symbol.iterator]().next);
assertThrows(() => (42, it.next)(), TypeError);
assertThrows(() => it.next.call(foo[Symbol.iterator]()), TypeError);
let next = it.next;
assertEquals(42, (it.next = 42, it.next));
assertEquals(43, (it.bla = 43, it.bla));
assertTrue(delete it.next);
assertThrows(() => next.call(foo[Symbol.iterator]()), TypeError);
}
// TODO(neis): Clarify spec w.r.t. other symbols.
@ -74,7 +99,7 @@ for (let key of nonexistant) {
}
// The actual star import that we are testing. Namespace imports are
// initialized before evaluation
// initialized before evaluation.
import * as foo from "modules-namespace1.js";
// There can be only one namespace object.

View File

@ -4,11 +4,11 @@
//
// MODULE
// TODO(neis): Symbol.iterator
assertEquals(
["b", "c", "get_a", "ns2", "set_a", "zzz", Symbol.toStringTag],
["b", "c", "get_a", "ns2", "set_a", "zzz",
Symbol.toStringTag, Symbol.iterator],
Reflect.ownKeys(ns));
// assertEquals(["b", "c", "get_a", "ns2", "set_a", "zzz"], [...ns]);
assertEquals(["b", "c", "get_a", "ns2", "set_a", "zzz"], [...ns]);
import * as foo from "modules-skip-1.js";
assertSame(foo.a, ns.b);

View File

@ -644,15 +644,12 @@
# v8:5487
'language/module-code/namespace/internals/get-own-property-str-found-uninit': [FAIL],
# @@iterator on namespace objects, to be implemented
'language/module-code/namespace/internals/define-own-property': [FAIL],
'language/module-code/namespace/internals/get-own-property-sym': [FAIL],
'language/module-code/namespace/internals/get-sym-found': [FAIL],
'language/module-code/namespace/internals/has-property-sym-found': [FAIL],
'language/module-code/namespace/internals/own-property-keys-binding-types': [FAIL],
'language/module-code/namespace/internals/own-property-keys-sort': [FAIL],
# v8:5401
'language/module-code/namespace/internals/set': [FAIL],
'language/module-code/namespace/Symbol.iterator/*': [FAIL],
'language/module-code/namespace/internals/define-own-property': [FAIL],
# https://github.com/tc39/test262/issues/773
'language/module-code/namespace/Symbol.iterator/values-binding-types_': [SKIP],
}], # variant == ignition or variant == ignition_staging or variant == ignition_turbofan
]