Bug 1129313 - Part 2: self-host MapIteratorObject#next(). r=jandem
authorTill Schneidereit <till@tillschneidereit.net>
Sat, 01 Aug 2015 00:13:26 +0200
changeset 287414 51d2109c72dcb27394e043c0390bdc982c2771de
parent 287413 91cb27a1be1e3aff0bad157083de17137f38eb8d
child 287415 71d335f1a4a3fe6aa24b933eb35e8b6592f2dae7
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1129313
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1129313 - Part 2: self-host MapIteratorObject#next(). r=jandem
js/src/builtin/Array.js
js/src/builtin/Map.js
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/builtin/SelfHostingDefines.h
js/src/builtin/String.js
js/src/builtin/Utilities.js
js/src/jit/MCallOptimize.cpp
js/src/jscntxt.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -619,63 +619,55 @@ function ArrayIncludes(searchElement, fr
         // Step d.
         k++;
     }
 
     // Step 11.
     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) {
     return CreateArrayIteratorAt(obj, kind, 0);
 }
 
 function ArrayIteratorIdentity() {
     return this;
 }
 
 function ArrayIteratorNext() {
     if (!IsObject(this) || !IsArrayIterator(this)) {
         return callFunction(CallArrayIteratorMethodIfWrapped, this,
                             "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];
         return result;
     }
 
     if (itemKind === ITEM_KIND_KEY_AND_VALUE) {
         var pair = NewDenseArray(2);
--- a/js/src/builtin/Map.js
+++ b/js/src/builtin/Map.js
@@ -28,13 +28,66 @@ function MapForEach(callbackfn, thisArg 
         var result = callFunction(std_Map_iterator_next, entries);
         if (result.done)
             break;
         var entry = result.value;
         callFunction(callbackfn, thisArg, entry[1], entry[0], M);
     }
 }
 
+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.
     return this;
 }
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -97,35 +97,16 @@ HashableValue::mark(JSTracer* trc) const
     return hv;
 }
 
 
 /*** MapIterator *********************************************************************************/
 
 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_ = {
     "Map Iterator",
     JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount),
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -135,24 +116,25 @@ const Class MapIteratorObject::class_ = 
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* convert */
     MapIteratorObject::finalize
 };
 
 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
 MapIteratorObject::kind() const
 {
     int32_t i = getSlot(KindSlot).toInt32();
     MOZ_ASSERT(i == MapObject::Keys || i == MapObject::Values || i == MapObject::Entries);
     return MapObject::IteratorKind(i);
@@ -189,85 +171,56 @@ MapIteratorObject::create(JSContext* cx,
         return nullptr;
 
     MapIteratorObject* iterobj = NewObjectWithGivenProto<MapIteratorObject>(cx, proto);
     if (!iterobj) {
         js_delete(range);
         return nullptr;
     }
     iterobj->setSlot(TargetSlot, ObjectValue(*mapobj));
+    iterobj->setSlot(RangeSlot, PrivateValue(range));
     iterobj->setSlot(KindSlot, Int32Value(int32_t(kind)));
-    iterobj->setSlot(RangeSlot, PrivateValue(range));
     return iterobj;
 }
 
 void
 MapIteratorObject::finalize(FreeOp* fop, JSObject* obj)
 {
-    fop->delete_(obj->as<MapIteratorObject>().range());
-}
-
-bool
-MapIteratorObject::is(HandleValue v)
-{
-    return v.isObject() && v.toObject().hasClass(&class_);
+    fop->delete_(MapIteratorObjectRange(static_cast<NativeObject*>(obj)));
 }
 
 bool
-MapIteratorObject::next_impl(JSContext* cx, CallArgs args)
+MapIteratorObject::next(JSContext* cx, Handle<MapIteratorObject*> mapIterator,
+                        HandleArrayObject resultPairObj)
 {
-    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);
+        mapIterator->setReservedSlot(RangeSlot, PrivateValue(nullptr));
+        return true;
+    }
+    switch (mapIterator->kind()) {
+      case MapObject::Keys:
+        resultPairObj->setDenseElementWithType(cx, 0, range->front().key.get());
+        break;
 
-            JSObject* pairobj = NewDenseCopiedArray(cx, pair.length(), pair.begin());
-            if (!pairobj)
-                return false;
-            value.setObject(*pairobj);
-            break;
-          }
-        }
-        range->popFront();
-        done = false;
-    }
+      case MapObject::Values:
+        resultPairObj->setDenseElementWithType(cx, 1, range->front().value);
+        break;
 
-    RootedObject result(cx, CreateItrResultObject(cx, value, done));
-    if (!result)
-        return false;
-    args.rval().setObject(*result);
-
-    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;
 }
 
 
 /*** Map *****************************************************************************************/
 
 const Class MapObject::class_ = {
     "Map",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
@@ -1450,17 +1403,16 @@ SetObject::clear(JSContext* cx, unsigned
 
 JSObject*
 js::InitSetClass(JSContext* cx, HandleObject obj)
 {
     return SetObject::initClass(cx, 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
 };
 
 bool
 js::InitSelfHostingCollectionIteratorFunctions(JSContext* cx, HandleObject obj)
 {
     return JS_DefineFunctions(cx, obj, selfhosting_collection_iterator_methods);
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_MapObject_h
 #define builtin_MapObject_h
 
 #include "jsobj.h"
 
+#include "builtin/SelfHostingDefines.h"
 #include "vm/Runtime.h"
 
 namespace js {
 
 /*
  * Comparing two ropes for equality can fail. The js::HashTable template
  * requires infallible hash() and match() operations. Therefore we require
  * all values to be converted to hashable form before being used as a key
@@ -83,16 +84,23 @@ typedef OrderedHashMap<HashableValue,
 
 typedef OrderedHashSet<HashableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueSet;
 
 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_;
 
     static bool getKeysAndValuesInterleaved(JSContext* cx, HandleObject obj,
                                             JS::AutoValueVector* entries);
     static bool entries(JSContext* cx, unsigned argc, Value* vp);
     static bool has(JSContext* cx, unsigned argc, Value* vp);
@@ -141,16 +149,42 @@ class MapObject : public NativeObject {
     static bool keys(JSContext* cx, unsigned argc, Value* vp);
     static bool values_impl(JSContext* cx, CallArgs args);
     static bool values(JSContext* cx, unsigned argc, Value* vp);
     static bool entries_impl(JSContext* cx, CallArgs args);
     static bool clear_impl(JSContext* cx, CallArgs args);
     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 };
     static JSObject* initClass(JSContext* cx, JSObject* obj);
     static const Class class_;
 
     static bool keys(JSContext *cx, HandleObject obj, JS::AutoValueVector *keys);
     static bool values(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -8,16 +8,17 @@
 
 #ifndef builtin_SelfHostingDefines_h
 #define builtin_SelfHostingDefines_h
 
 // Utility macros.
 #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) \
   callFunction(std_Array_push, ARRAY, ELEMENT);
 #define ARRAY_SLICE(ARRAY, ELEMENT) \
   callFunction(std_Array_slice, ARRAY, ELEMENT);
 
 // Property descriptor attributes.
@@ -27,9 +28,20 @@
 
 #define ATTR_NONENUMERABLE      0x08
 #define ATTR_NONCONFIGURABLE    0x10
 #define ATTR_NONWRITABLE        0x20
 
 // 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
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -184,44 +184,41 @@ function String_repeat(count) {
         if (n)
             S += S;
         else
             break;
     }
     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;
 }
 
 function StringIteratorIdentity() {
     return this;
 }
 
 function StringIteratorNext() {
     if (!IsObject(this) || !IsStringIterator(this)) {
         return callFunction(CallStringIteratorMethodIfWrapped, this,
                             "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 };
 
     if (index >= size) {
         result.done = true;
         return result;
     }
 
@@ -229,17 +226,17 @@ function StringIteratorNext() {
     var first = callFunction(std_String_charCodeAt, S, index);
     if (first >= 0xD800 && first <= 0xDBFF && index + 1 < size) {
         var second = callFunction(std_String_charCodeAt, S, index + 1);
         if (second >= 0xDC00 && second <= 0xDFFF) {
             charCount = 2;
         }
     }
 
-    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;
 }
 
 /**
  * Compare this String against that String, using the locale and collation
  * options provided.
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -39,16 +39,17 @@
 // The few items below here are either self-hosted or installing them under a
 // std_Foo name would require ugly contortions, so they just get aliased here.
 var std_Array_indexOf = ArrayIndexOf;
 var std_String_substring = String_substring;
 // WeakMap is a bare constructor without properties or methods.
 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 **********/
 
 
 /* Spec: ECMAScript Language Specification, 5.1 edition, 8.8 */
 function List() {
     this.length = 0;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -219,16 +219,18 @@ IonBuilder::inlineNativeCall(CallInfo& c
     if (native == intrinsic_ToString)
         return inlineToString(callInfo);
     if (native == intrinsic_IsConstructing)
         return inlineIsConstructing(callInfo);
     if (native == intrinsic_SubstringKernel)
         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_);
 
     // TypedArray intrinsics.
     if (native == intrinsic_IsTypedArray)
         return inlineIsTypedArray(callInfo);
     if (native == intrinsic_IsPossiblyWrappedTypedArray)
         return inlineIsPossiblyWrappedTypedArray(callInfo);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -764,16 +764,17 @@ bool intrinsic_UnsafeGetReservedSlot(JSC
 bool intrinsic_UnsafeGetObjectFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_UnsafeGetInt32FromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_UnsafeGetStringFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_UnsafeGetBooleanFromReservedSlot(JSContext* cx, unsigned argc, Value* vp);
 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);
 
 bool intrinsic_IsTypedArray(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_TypedArrayBuffer(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_TypedArrayByteOffset(JSContext* cx, unsigned argc, Value* vp);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -15,16 +15,17 @@
 #include "jsdate.h"
 #include "jsfriendapi.h"
 #include "jshashutil.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 #include "selfhosted.out.h"
 
 #include "builtin/Intl.h"
+#include "builtin/MapObject.h"
 #include "builtin/Object.h"
 #include "builtin/Reflect.h"
 #include "builtin/SelfHostingDefines.h"
 #include "builtin/SIMD.h"
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "vm/Compression.h"
@@ -474,16 +475,42 @@ js::intrinsic_IsArrayIterator(JSContext*
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isObject());
 
     args.rval().setBoolean(args[0].toObject().is<ArrayIteratorObject>());
     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)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 0);
 
     RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
     if (!proto)
@@ -1336,16 +1363,21 @@ static const JSFunctionSpec intrinsic_fu
 
     JS_FN("GetIteratorPrototype",    intrinsic_GetIteratorPrototype,    0,0),
 
     JS_FN("NewArrayIterator",        intrinsic_NewArrayIterator,        0,0),
     JS_FN("IsArrayIterator",         intrinsic_IsArrayIterator,         1,0),
     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),
     JS_FN("CallStringIteratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<StringIteratorObject>>,     2,0),
 
     JS_FN("IsStarGeneratorObject",   intrinsic_IsStarGeneratorObject,   1,0),
     JS_FN("StarGeneratorObjectIsClosed", intrinsic_StarGeneratorObjectIsClosed, 1,0),