Bug 1129313 - Part 2: self-host MapIteratorObject#next(). r=jandem

This commit is contained in:
Till Schneidereit 2015-08-01 00:13:26 +02:00
parent 9441be443e
commit ba57452f87
10 changed files with 176 additions and 100 deletions

View File

@ -624,21 +624,13 @@ function ArrayIncludes(searchElement, fromIndex = 0) {
return false;
}
#define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0
#define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1
#define ARRAY_ITERATOR_SLOT_ITEM_KIND 2
#define ITEM_KIND_VALUE 0
#define ITEM_KIND_KEY_AND_VALUE 1
#define ITEM_KIND_KEY 2
// ES6 draft specification, section 22.1.5.1, version 2013-09-05.
function CreateArrayIteratorAt(obj, kind, n) {
var iteratedObject = ToObject(obj);
var iterator = NewArrayIterator();
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject);
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, n);
UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind);
UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, iteratedObject);
UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, n);
UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_ITEM_KIND, kind);
return iterator;
}
function CreateArrayIterator(obj, kind) {
@ -655,22 +647,22 @@ function ArrayIteratorNext() {
"ArrayIteratorNext");
}
var a = UnsafeGetObjectFromReservedSlot(this, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT);
var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET);
// The index might not be an integer, so we have to do a generic get here.
var index = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX);
var itemKind = UnsafeGetInt32FromReservedSlot(this, ARRAY_ITERATOR_SLOT_ITEM_KIND);
var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
var result = { value: undefined, done: false };
// FIXME: This should be ToLength, which clamps at 2**53. Bug 924058.
if (index >= TO_UINT32(a.length)) {
// When the above is changed to ToLength, use +1/0 here instead
// of MAX_UINT32.
UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
result.done = true;
return result;
}
UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, index + 1);
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1);
if (itemKind === ITEM_KIND_VALUE) {
result.value = a[index];

View File

@ -33,6 +33,59 @@ function MapForEach(callbackfn, thisArg = undefined) {
}
}
var iteratorTemp = { mapIterationResultPair : null };
function MapIteratorNext() {
// Step 1.
var O = this;
// Steps 2-3.
if (!IsObject(O) || !IsMapIterator(O))
return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext");
// Steps 4-5 (implemented in _GetNextMapEntryForIterator).
// Steps 8-9 (omitted).
var mapIterationResultPair = iteratorTemp.mapIterationResultPair;
if (!mapIterationResultPair) {
mapIterationResultPair = iteratorTemp.mapIterationResultPair = NewDenseArray(2);
mapIterationResultPair[0] = null;
mapIterationResultPair[1] = null;
}
var retVal = {value: undefined, done: true};
// Step 10.a, 11.
var done = _GetNextMapEntryForIterator(O, mapIterationResultPair);
if (!done) {
// Steps 10.b-c (omitted).
// Step 6.
var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
var result;
if (itemKind === ITEM_KIND_KEY) {
// Step 10.d.i.
result = mapIterationResultPair[0];
} else if (itemKind === ITEM_KIND_VALUE) {
// Step 10.d.ii.
result = mapIterationResultPair[1];
} else {
// Step 10.d.iii.
assert(itemKind === ITEM_KIND_KEY_AND_VALUE, itemKind);
result = [mapIterationResultPair[0], mapIterationResultPair[1]];
}
mapIterationResultPair[0] = null;
mapIterationResultPair[1] = null;
retVal.value = result;
retVal.done = false;
}
// Steps 7, 12.
return retVal;
}
// ES6 final draft 23.1.2.2.
function MapSpecies() {
// Step 1.

View File

@ -102,25 +102,6 @@ HashableValue::mark(JSTracer* trc) const
namespace {
class MapIteratorObject : public NativeObject
{
public:
static const Class class_;
enum { TargetSlot, KindSlot, RangeSlot, SlotCount };
static const JSFunctionSpec methods[];
static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data,
MapObject::IteratorKind kind);
static bool next(JSContext* cx, unsigned argc, Value* vp);
static void finalize(FreeOp* fop, JSObject* obj);
private:
static inline bool is(HandleValue v);
inline ValueMap::Range* range();
inline MapObject::IteratorKind kind() const;
static bool next_impl(JSContext* cx, CallArgs args);
};
} /* anonymous namespace */
const Class MapIteratorObject::class_ = {
@ -140,14 +121,15 @@ const Class MapIteratorObject::class_ = {
const JSFunctionSpec MapIteratorObject::methods[] = {
JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
JS_FN("next", next, 0, 0),
JS_SELF_HOSTED_FN("next", "MapIteratorNext", 0, 0),
JS_FS_END
};
inline ValueMap::Range*
MapIteratorObject::range()
static inline ValueMap::Range*
MapIteratorObjectRange(NativeObject* obj)
{
return static_cast<ValueMap::Range*>(getSlot(RangeSlot).toPrivate());
MOZ_ASSERT(obj->is<MapIteratorObject>());
return static_cast<ValueMap::Range*>(obj->getSlot(MapIteratorObject::RangeSlot).toPrivate());
}
inline MapObject::IteratorKind
@ -194,75 +176,46 @@ MapIteratorObject::create(JSContext* cx, HandleObject mapobj, ValueMap* data,
return nullptr;
}
iterobj->setSlot(TargetSlot, ObjectValue(*mapobj));
iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
iterobj->setSlot(RangeSlot, PrivateValue(range));
iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
return iterobj;
}
void
MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
{
fop->delete_(obj->as<MapIteratorObject>().range());
fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj)));
}
bool
MapIteratorObject::is(HandleValue v)
MapIteratorObject::next(JSContext* cx, Handle<MapIteratorObject*> mapIterator,
HandleArrayObject resultPairObj)
{
return v.isObject() && v.toObject().hasClass(&class_);
}
bool
MapIteratorObject::next_impl(JSContext* cx, CallArgs args)
{
MapIteratorObject& thisobj = args.thisv().toObject().as<MapIteratorObject>();
ValueMap::Range* range = thisobj.range();
RootedValue value(cx);
bool done;
MOZ_ASSERT(resultPairObj->getDenseInitializedLength() == 2);
ValueMap::Range* range = MapIteratorObjectRange(mapIterator);
if (!range || range->empty()) {
js_delete(range);
thisobj.setReservedSlot(RangeSlot, PrivateValue(nullptr));
value.setUndefined();
done = true;
} else {
switch (thisobj.kind()) {
case MapObject::Keys:
value = range->front().key.get();
break;
case MapObject::Values:
value = range->front().value;
break;
case MapObject::Entries: {
JS::AutoValueArray<2> pair(cx);
pair[0].set(range->front().key.get());
pair[1].set(range->front().value);
JSObject* pairobj = NewDenseCopiedArray(cx, pair.length(), pair.begin());
if (!pairobj)
return false;
value.setObject(*pairobj);
break;
}
}
range->popFront();
done = false;
mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
return true;
}
switch (mapIterator->kind()) {
case MapObject::Keys:
resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get());
break;
RootedObject result(cx, CreateItrResultObject(cx, value, done));
if (!result)
return false;
args.rval().setObject(*result);
case MapObject::Values:
resultPairObj->setDenseElementWithType(cx, 1, range->front().value);
break;
return true;
}
bool
MapIteratorObject::next(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod(cx, is, next_impl, args);
case MapObject::Entries: {
resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get());
resultPairObj->setDenseElementWithType(cx, 1, range->front().value);
break;
}
}
range->popFront();
return false;
}
@ -1455,7 +1408,6 @@ js::InitSetClass(JSContext* cx, HandleObject obj)
}
const JSFunctionSpec selfhosting_collection_iterator_methods[] = {
JS_FN("std_Map_iterator_next", MapIteratorObject::next, 0, 0),
JS_FN("std_Set_iterator_next", SetIteratorObject::next, 0, 0),
JS_FS_END
};

View File

@ -9,6 +9,7 @@
#include "jsobj.h"
#include "builtin/SelfHostingDefines.h"
#include "vm/Runtime.h"
namespace js {
@ -88,6 +89,13 @@ typedef OrderedHashSet<HashableValue,
class MapObject : public NativeObject {
public:
enum IteratorKind { Keys, Values, Entries };
static_assert(Keys == ITEM_KIND_KEY,
"IteratorKind Keys must match self-hosting define for item kind key.");
static_assert(Values == ITEM_KIND_VALUE,
"IteratorKind Values must match self-hosting define for item kind value.");
static_assert(Entries == ITEM_KIND_KEY_AND_VALUE,
"IteratorKind Entries must match self-hosting define for item kind "
"key-and-value.");
static JSObject* initClass(JSContext* cx, JSObject* obj);
static const Class class_;
@ -146,6 +154,32 @@ class MapObject : public NativeObject {
static bool clear(JSContext* cx, unsigned argc, Value* vp);
};
class MapIteratorObject : public NativeObject
{
public:
static const Class class_;
enum { TargetSlot, RangeSlot, KindSlot, SlotCount };
static_assert(TargetSlot == ITERATOR_SLOT_TARGET,
"TargetSlot must match self-hosting define for iterated object slot.");
static_assert(RangeSlot == ITERATOR_SLOT_RANGE,
"RangeSlot must match self-hosting define for range or index slot.");
static_assert(KindSlot == ITERATOR_SLOT_ITEM_KIND,
"KindSlot must match self-hosting define for item kind slot.");
static const JSFunctionSpec methods[];
static MapIteratorObject* create(JSContext* cx, HandleObject mapobj, ValueMap* data,
MapObject::IteratorKind kind);
static void finalize(FreeOp* fop, JSObject* obj);
static bool next(JSContext* cx, Handle<MapIteratorObject*> mapIterator,
HandleArrayObject resultPairObj);
private:
inline MapObject::IteratorKind kind() const;
};
class SetObject : public NativeObject {
public:
enum IteratorKind { Values, Entries };

View File

@ -13,6 +13,7 @@
#define TO_INT32(x) ((x) | 0)
#define TO_UINT32(x) ((x) >>> 0)
#define IS_UINT32(x) ((x) >>> 0 === (x))
#define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1
// Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice.
#define ARRAY_PUSH(ARRAY, ELEMENT) \
@ -32,4 +33,15 @@
// Stores the private WeakMap slot used for WeakSets
#define WEAKSET_MAP_SLOT 0
#define ITERATOR_SLOT_TARGET 0
// Used for collection iterators.
#define ITERATOR_SLOT_RANGE 1
// Used for list, i.e. Array and String, iterators.
#define ITERATOR_SLOT_NEXT_INDEX 1
#define ITERATOR_SLOT_ITEM_KIND 2
#define ITEM_KIND_KEY 0
#define ITEM_KIND_VALUE 1
#define ITEM_KIND_KEY_AND_VALUE 2
#endif

View File

@ -189,16 +189,13 @@ function String_repeat(count) {
return T;
}
#define STRING_ITERATOR_SLOT_ITERATED_STRING 0
#define STRING_ITERATOR_SLOT_NEXT_INDEX 1
// ES6 draft specification, section 21.1.3.27, version 2013-09-27.
function String_iterator() {
RequireObjectCoercible(this);
var S = ToString(this);
var iterator = NewStringIterator();
UnsafeSetReservedSlot(iterator, STRING_ITERATOR_SLOT_ITERATED_STRING, S);
UnsafeSetReservedSlot(iterator, STRING_ITERATOR_SLOT_NEXT_INDEX, 0);
UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_TARGET, S);
UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, 0);
return iterator;
}
@ -212,11 +209,11 @@ function StringIteratorNext() {
"StringIteratorNext");
}
var S = UnsafeGetStringFromReservedSlot(this, STRING_ITERATOR_SLOT_ITERATED_STRING);
var S = UnsafeGetStringFromReservedSlot(this, ITERATOR_SLOT_TARGET);
// We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in
// SelfHostring.cpp) so our current index can never be anything other than
// an Int32Value.
var index = UnsafeGetInt32FromReservedSlot(this, STRING_ITERATOR_SLOT_NEXT_INDEX);
var index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
var size = S.length;
var result = { value: undefined, done: false };
@ -234,7 +231,7 @@ function StringIteratorNext() {
}
}
UnsafeSetReservedSlot(this, STRING_ITERATOR_SLOT_NEXT_INDEX, index + charCount);
UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + charCount);
result.value = callFunction(std_String_substring, S, index, index + charCount);
return result;

View File

@ -44,6 +44,7 @@ var std_String_substring = String_substring;
var std_WeakMap = WeakMap;
// StopIteration is a bare constructor without properties or methods.
var std_StopIteration = StopIteration;
var std_Map_iterator_next = MapIteratorNext;
/********** List specification type **********/

View File

@ -224,6 +224,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
return inlineSubstringKernel(callInfo);
if (native == intrinsic_IsArrayIterator)
return inlineHasClass(callInfo, &ArrayIteratorObject::class_);
if (native == intrinsic_IsMapIterator)
return inlineHasClass(callInfo, &MapIteratorObject::class_);
if (native == intrinsic_IsStringIterator)
return inlineHasClass(callInfo, &StringIteratorObject::class_);

View File

@ -769,6 +769,7 @@ bool intrinsic_IsPackedArray(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsStringIterator(JSContext* cx, unsigned argc, Value* vp);
bool intrinsic_IsArrayBuffer(JSContext* cx, unsigned argc, Value* vp);

View File

@ -20,6 +20,7 @@
#include "selfhosted.out.h"
#include "builtin/Intl.h"
#include "builtin/MapObject.h"
#include "builtin/Object.h"
#include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h"
@ -479,6 +480,32 @@ js::intrinsic_IsArrayIterator(JSContext* cx, unsigned argc, Value* vp)
return true;
}
bool
js::intrinsic_IsMapIterator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(args[0].toObject().is<MapIteratorObject>());
return true;
}
bool
intrinsic_GetNextMapEntryForIterator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].toObject().is<MapIteratorObject>());
MOZ_ASSERT(args[1].isObject());
Rooted<MapIteratorObject*> mapIterator(cx, &args[0].toObject().as<MapIteratorObject>());
RootedArrayObject result(cx, &args[1].toObject().as<ArrayObject>());
args.rval().setBoolean(MapIteratorObject::next(cx, mapIterator, result));
return true;
}
static bool
intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp)
{
@ -1341,6 +1368,11 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("CallArrayIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2,0),
JS_FN("IsMapIterator", intrinsic_IsMapIterator, 1,0),
JS_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 3,0),
JS_FN("CallMapIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<MapIteratorObject>>, 2,0),
JS_FN("NewStringIterator", intrinsic_NewStringIterator, 0,0),
JS_FN("IsStringIterator", intrinsic_IsStringIterator, 1,0),