Bug 1344469 - Part 1 - Add JSOP_HASOWN to Interpreter and BytecodeEmitter. r=jandem
authorTom Schuster <evilpies@gmail.com>
Thu, 13 Apr 2017 22:17:56 +0200
changeset 352996 7deb7a5cb37053ff542662e365174d1fdb4ed989
parent 352995 57d5c6bdba4cd23485401651bfe856ade573e19e
child 352997 50e8be7ee74875005ac2835e8fb21749ee8e1bc3
push id31655
push userihsiao@mozilla.com
push dateFri, 14 Apr 2017 09:07:09 +0000
treeherdermozilla-central@8f806306fb83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1344469
milestone55.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 1344469 - Part 1 - Add JSOP_HASOWN to Interpreter and BytecodeEmitter. r=jandem
js/src/builtin/Intl.js
js/src/builtin/Object.cpp
js/src/builtin/Object.h
js/src/builtin/Object.js
js/src/builtin/TypedObject.js
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/vm/Interpreter-inl.h
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -374,17 +374,17 @@ function CanonicalizeLanguageTag(locale)
     // technically it's not necessary to adjust case. But for easier processing,
     // and because the canonical form for most subtags is lower case, we start
     // with lower case for all.
     // "Zh-NAN-haNS-bu-variant2-Variant1-u-ca-chinese-t-Zh-laTN-x-PRIVATE" ->
     // "zh-nan-hans-bu-variant2-variant1-u-ca-chinese-t-zh-latn-x-private"
     locale = callFunction(std_String_toLowerCase, locale);
 
     // Handle mappings for complete tags.
-    if (callFunction(std_Object_hasOwnProperty, langTagMappings, locale))
+    if (hasOwn(locale, langTagMappings))
         return langTagMappings[locale];
 
     var subtags = StringSplitString(ToString(locale), "-");
     var i = 0;
 
     // Handle the standard part: All subtags before the first singleton or "x".
     // "zh-nan-hans-bu-variant2-variant1"
     while (i < subtags.length) {
@@ -406,27 +406,27 @@ function CanonicalizeLanguageTag(locale)
                 subtag = callFunction(std_String_toUpperCase, subtag[0]) +
                          callFunction(String_substring, subtag, 1);
             } else if (subtag.length === 2) {
                 // 2-character subtags that are not in initial position are
                 // region codes; they need to be upper case. "bu" -> "BU"
                 subtag = callFunction(std_String_toUpperCase, subtag);
             }
         }
-        if (callFunction(std_Object_hasOwnProperty, langSubtagMappings, subtag)) {
+        if (hasOwn(subtag, langSubtagMappings)) {
             // Replace deprecated subtags with their preferred values.
             // "BU" -> "MM"
             // This has to come after we capitalize region codes because
             // otherwise some language and region codes could be confused.
             // For example, "in" is an obsolete language code for Indonesian,
             // but "IN" is the country code for India.
             // Note that the script generating langSubtagMappings makes sure
             // that no regular subtag mapping will replace an extlang code.
             subtag = langSubtagMappings[subtag];
-        } else if (callFunction(std_Object_hasOwnProperty, extlangMappings, subtag)) {
+        } else if (hasOwn(subtag, extlangMappings)) {
             // Replace deprecated extlang subtags with their preferred values,
             // and remove the preceding subtag if it's a redundant prefix.
             // "zh-nan" -> "nan"
             // Note that the script generating extlangMappings makes sure that
             // no extlang mapping will replace a normal language code.
             subtag = extlangMappings[subtag].preferred;
             if (i === 1 && extlangMappings[subtag].prefix === subtags[0]) {
                 callFunction(std_Array_shift, subtags);
@@ -505,21 +505,20 @@ function ValidateAndCanonicalizeLanguage
             ThrowRangeError(JSMSG_INVALID_LANGUAGE_TAG, locale);
         assert(IsStructurallyValidLanguageTag(locale), "2*3ALPHA is a valid language tag");
 
         // The language subtag is canonicalized to lower case.
         locale = callFunction(std_String_toLowerCase, locale);
 
         // langTagMappings doesn't contain any 2*3ALPHA keys, so we don't need
         // to check for possible replacements in this map.
-        assert(!callFunction(std_Object_hasOwnProperty, langTagMappings, locale),
-               "langTagMappings contains no 2*3ALPHA mappings");
+        assert(!hasOwn(locale, langTagMappings), "langTagMappings contains no 2*3ALPHA mappings");
 
         // Replace deprecated subtags with their preferred values.
-        locale = callFunction(std_Object_hasOwnProperty, langSubtagMappings, locale)
+        locale = hasOwn(locale, langSubtagMappings)
                  ? langSubtagMappings[locale]
                  : locale;
         assert(locale === CanonicalizeLanguageTag(locale), "expected same canonicalization");
 
         return locale;
     }
 
     if (!IsStructurallyValidLanguageTag(locale))
@@ -602,17 +601,17 @@ function DefaultLocaleIgnoringAvailableL
     } else {
         candidate = CanonicalizeLanguageTag(runtimeDefaultLocale);
 
         // The default locale must be in [[availableLocales]], and that list
         // must not contain any locales with Unicode extension sequences, so
         // remove any present in the candidate.
         candidate = removeUnicodeExtensions(candidate);
 
-        if (callFunction(std_Object_hasOwnProperty, oldStyleLanguageTagMappings, candidate))
+        if (hasOwn(candidate, oldStyleLanguageTagMappings))
             candidate = oldStyleLanguageTagMappings[candidate];
     }
 
     // Cache the candidate locale until the runtime default locale changes.
     localeCandidateCache.candidateDefaultLocale = candidate;
     localeCandidateCache.runtimeDefaultLocale = runtimeDefaultLocale;
 
     assert(IsStructurallyValidLanguageTag(candidate),
@@ -1365,26 +1364,24 @@ function maybeInternalProperties(interna
 function getIntlObjectInternals(obj) {
     assert(IsObject(obj), "getIntlObjectInternals called with non-Object");
     assert(IsCollator(obj) || IsDateTimeFormat(obj) || IsNumberFormat(obj) || IsPluralRules(obj),
            "getIntlObjectInternals called with non-Intl object");
 
     var internals = UnsafeGetReservedSlot(obj, INTL_INTERNALS_OBJECT_SLOT);
 
     assert(IsObject(internals), "internals not an object");
-    assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
+    assert(hasOwn("type", internals), "missing type");
     assert((internals.type === "Collator" && IsCollator(obj)) ||
            (internals.type === "DateTimeFormat" && IsDateTimeFormat(obj)) ||
            (internals.type === "NumberFormat" && IsNumberFormat(obj)) ||
            (internals.type === "PluralRules" && IsPluralRules(obj)),
            "type must match the object's class");
-    assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"),
-           "missing lazyData");
-    assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"),
-           "missing internalProps");
+    assert(hasOwn("lazyData", internals), "missing lazyData");
+    assert(hasOwn("internalProps", internals), "missing internalProps");
 
     return internals;
 }
 
 
 /**
  * Get the internal properties of known-Intl object |obj|.  For use only by
  * C++ code that knows what it's doing!
@@ -2114,17 +2111,17 @@ function InitializeNumberFormat(numberFo
 function getCurrencyDigitsRE() {
     return internalIntlRegExps.currencyDigitsRE ||
            (internalIntlRegExps.currencyDigitsRE = RegExpCreate("^[A-Z]{3}$"));
 }
 function CurrencyDigits(currency) {
     assert(typeof currency === "string", "CurrencyDigits");
     assert(regexp_test_no_statics(getCurrencyDigitsRE(), currency), "CurrencyDigits");
 
-    if (callFunction(std_Object_hasOwnProperty, currencyDigits, currency))
+    if (hasOwn(currency, currencyDigits))
         return currencyDigits[currency];
     return 2;
 }
 
 
 /**
  * Returns the subset of the given locale list for which this locale list has a
  * matching (possibly fallback) locale. Locales appear in the same order in the
@@ -2264,17 +2261,17 @@ function Intl_NumberFormat_resolvedOptio
     var optionalProperties = [
         "currency",
         "currencyDisplay",
         "minimumSignificantDigits",
         "maximumSignificantDigits"
     ];
     for (var i = 0; i < optionalProperties.length; i++) {
         var p = optionalProperties[i];
-        if (callFunction(std_Object_hasOwnProperty, internals, p))
+        if (hasOwn(p, internals))
             _DefineDataProperty(result, p, internals[p]);
     }
     return result;
 }
 
 
 /********** Intl.DateTimeFormat **********/
 
@@ -3055,17 +3052,17 @@ function resolveICUPattern(pattern, resu
                 else if (count === 4)
                     value = "long";
                 else
                     value = "narrow";
                 break;
             default:
                 // skip other pattern characters and literal text
             }
-            if (callFunction(std_Object_hasOwnProperty, icuPatternCharToComponent, c))
+            if (hasOwn(c, icuPatternCharToComponent))
                 _DefineDataProperty(result, icuPatternCharToComponent[c], value);
             if (c === "h" || c === "K")
                 _DefineDataProperty(result, "hour12", true);
             else if (c === "H" || c === "k")
                 _DefineDataProperty(result, "hour12", false);
         }
     }
 }
@@ -3290,17 +3287,17 @@ function Intl_PluralRules_resolvedOption
 
     var optionalProperties = [
         "minimumSignificantDigits",
         "maximumSignificantDigits"
     ];
 
     for (var i = 0; i < optionalProperties.length; i++) {
         var p = optionalProperties[i];
-        if (callFunction(std_Object_hasOwnProperty, internals, p))
+        if (hasOwn(p, internals))
             _DefineDataProperty(result, p, internals[p]);
     }
     return result;
 }
 
 
 /********** Intl **********/
 
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -680,60 +680,16 @@ obj_unwatch(JSContext* cx, unsigned argc
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
-/* ECMA 15.2.4.5. */
-bool
-js::obj_hasOwnProperty(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    HandleValue idValue = args.get(0);
-
-    // As an optimization, provide a fast path when rooting is not necessary and
-    // we can safely retrieve the object's shape.
-
-    /* Step 1, 2. */
-    jsid id;
-    if (args.thisv().isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
-        JSObject* obj = &args.thisv().toObject();
-        PropertyResult prop;
-        if (obj->isNative() &&
-            NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
-        {
-            args.rval().setBoolean(prop.isFound());
-            return true;
-        }
-    }
-
-    /* Step 1. */
-    RootedId idRoot(cx);
-    if (!ToPropertyKey(cx, idValue, &idRoot))
-        return false;
-
-    /* Step 2. */
-    RootedObject obj(cx, ToObject(cx, args.thisv()));
-    if (!obj)
-        return false;
-
-    /* Step 3. */
-    bool found;
-    if (!HasOwnProperty(cx, obj, idRoot, &found))
-        return false;
-
-    /* Step 4,5. */
-    args.rval().setBoolean(found);
-    return true;
-}
-
 /* ES5 15.2.4.6. */
 static bool
 obj_isPrototypeOf(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     if (args.length() < 1 || !args[0].isObject()) {
@@ -1315,17 +1271,17 @@ static const JSFunctionSpec object_metho
 #endif
     JS_FN(js_toString_str,             obj_toString,                0,0),
     JS_SELF_HOSTED_FN(js_toLocaleString_str, "Object_toLocaleString", 0, 0),
     JS_SELF_HOSTED_FN(js_valueOf_str,  "Object_valueOf",            0,0),
 #if JS_HAS_OBJ_WATCHPOINT
     JS_FN(js_watch_str,                obj_watch,                   2,0),
     JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
 #endif
-    JS_FN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0),
+    JS_SELF_HOSTED_FN(js_hasOwnProperty_str, "Object_hasOwnProperty", 1,0),
     JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
     JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
 #if JS_OLD_GETTER_SETTER_METHODS
     JS_SELF_HOSTED_FN(js_defineGetter_str, "ObjectDefineGetter",    2,0),
     JS_SELF_HOSTED_FN(js_defineSetter_str, "ObjectDefineSetter",    2,0),
     JS_SELF_HOSTED_FN(js_lookupGetter_str, "ObjectLookupGetter",    1,0),
     JS_SELF_HOSTED_FN(js_lookupSetter_str, "ObjectLookupSetter",    1,0),
 #endif
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -43,18 +43,16 @@ MOZ_MUST_USE bool
 obj_getOwnPropertyNames(JSContext* cx, unsigned argc, JS::Value* vp);
 
 MOZ_MUST_USE bool
 obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 MOZ_MUST_USE bool
 obj_getPrototypeOf(JSContext* cx, unsigned argc, JS::Value* vp);
 
-MOZ_MUST_USE bool
-obj_hasOwnProperty(JSContext* cx, unsigned argc, JS::Value* vp);
 
 MOZ_MUST_USE bool
 obj_isExtensible(JSContext* cx, unsigned argc, JS::Value* vp);
 
 MOZ_MUST_USE bool
 obj_toString(JSContext* cx, unsigned argc, JS::Value* vp);
 
 // Exposed so SelfHosting.cpp can use it in the OwnPropertyKeys intrinsic
--- a/js/src/builtin/Object.js
+++ b/js/src/builtin/Object.js
@@ -88,16 +88,23 @@ function Object_toLocaleString() {
 }
 
 // ES 2017 draft bb96899bb0d9ef9be08164a26efae2ee5f25e875 19.1.3.7
 function Object_valueOf() {
     // Step 1.
     return ToObject(this);
 }
 
+// ES 2018 draft 19.1.3.2
+function Object_hasOwnProperty(V) {
+    // Implement hasOwnProperty as a pseudo function that becomes a JSOp
+    // to easier add an inline cache for this.
+    return hasOwn(V, this);
+}
+
 // ES7 draft (2016 March 8) B.2.2.3
 function ObjectDefineSetter(name, setter) {
     // Step 1.
     var object = ToObject(this);
 
     // Step 2.
     if (!IsCallable(setter))
         ThrowTypeError(JSMSG_BAD_GETTER_OR_SETTER, "setter");
@@ -155,17 +162,17 @@ function ObjectLookupSetter(name) {
 
     do {
         // Step 3.a.
         var desc = std_Object_getOwnPropertyDescriptor(object, key);
 
         // Step 3.b.
         if (desc) {
             // Step.b.i.
-            if (callFunction(std_Object_hasOwnProperty, desc, "set"))
+            if (hasOwn("set", desc))
                 return desc.set;
 
             // Step.b.ii.
             return undefined;
         }
 
         // Step 3.c.
         object = std_Reflect_getPrototypeOf(object);
@@ -184,17 +191,17 @@ function ObjectLookupGetter(name) {
 
     do {
         // Step 3.a.
         var desc = std_Object_getOwnPropertyDescriptor(object, key);
 
         // Step 3.b.
         if (desc) {
             // Step.b.i.
-            if (callFunction(std_Object_hasOwnProperty, desc, "get"))
+            if (hasOwn("get", desc))
                 return desc.get;
 
             // Step.b.ii.
             return undefined;
         }
 
         // Step 3.c.
         object = std_Reflect_getPrototypeOf(object);
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -23,21 +23,16 @@
     TO_INT32(UnsafeGetInt32FromReservedSlot(obj, JS_DESCR_SLOT_ARRAY_LENGTH))
 #define DESCR_STRUCT_FIELD_NAMES(obj) \
     UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_NAMES)
 #define DESCR_STRUCT_FIELD_TYPES(obj) \
     UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_TYPES)
 #define DESCR_STRUCT_FIELD_OFFSETS(obj) \
     UnsafeGetObjectFromReservedSlot(obj, JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS)
 
-// Other
-
-#define HAS_PROPERTY(obj, prop) \
-    callFunction(std_Object_hasOwnProperty, obj, prop)
-
 ///////////////////////////////////////////////////////////////////////////
 // Getting values
 //
 // The methods in this section read from the memory pointed at
 // by `this` and produce JS values. This process is called *reification*
 // in the spec.
 
 // Reifies the value referenced by the pointer, meaning that it
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9165,20 +9165,38 @@ BytecodeEmitter::emitSelfHostedDefineDat
 
     ParseNode* valNode = idNode->pn_next;
     if (!emitTree(valNode))
         return false;
 
     // This will leave the object on the stack instead of pushing |undefined|,
     // but that's fine because the self-hosted code doesn't use the return
     // value.
-    if (!emit1(JSOP_INITELEM))
-        return false;
-
-    return true;
+    return emit1(JSOP_INITELEM);
+}
+
+bool
+BytecodeEmitter::emitSelfHostedHasOwn(ParseNode* pn)
+{
+    if (pn->pn_count != 3) {
+        reportError(pn, JSMSG_MORE_ARGS_NEEDED, "hasOwn", "2", "");
+        return false;
+    }
+
+    ParseNode* funNode = pn->pn_head;  // The hasOwn node.
+
+    ParseNode* idNode = funNode->pn_next;
+    if (!emitTree(idNode))
+        return false;
+
+    ParseNode* objNode = idNode->pn_next;
+    if (!emitTree(objNode))
+        return false;
+
+    return emit1(JSOP_HASOWN);
 }
 
 bool
 BytecodeEmitter::isRestParameter(ParseNode* pn)
 {
     if (!sc->isFunctionBox())
         return false;
 
@@ -9291,16 +9309,18 @@ BytecodeEmitter::emitCallOrNew(ParseNode
             if (pn2->name() == cx->names().resumeGenerator)
                 return emitSelfHostedResumeGenerator(pn);
             if (pn2->name() == cx->names().forceInterpreter)
                 return emitSelfHostedForceInterpreter(pn);
             if (pn2->name() == cx->names().allowContentIter)
                 return emitSelfHostedAllowContentIter(pn);
             if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
                 return emitSelfHostedDefineDataProperty(pn);
+            if (pn2->name() == cx->names().hasOwn)
+                return emitSelfHostedHasOwn(pn);
             // Fall through.
         }
         if (!emitGetName(pn2, callop))
             return false;
         break;
       case PNK_DOT:
         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
         if (pn2->as<PropertyAccess>().isSuper()) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -756,16 +756,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     MOZ_MUST_USE bool emitOptimizeSpread(ParseNode* arg0, JumpList* jmp, bool* emitted);
 
     MOZ_MUST_USE bool emitCallOrNew(ParseNode* pn, ValueUsage valueUsage = ValueUsage::WantValue);
     MOZ_MUST_USE bool emitSelfHostedCallFunction(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedResumeGenerator(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedForceInterpreter(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedAllowContentIter(ParseNode* pn);
     MOZ_MUST_USE bool emitSelfHostedDefineDataProperty(ParseNode* pn);
+    MOZ_MUST_USE bool emitSelfHostedHasOwn(ParseNode* pn);
 
     MOZ_MUST_USE bool emitComprehensionFor(ParseNode* compFor);
     MOZ_MUST_USE bool emitComprehensionForIn(ParseNode* pn);
     MOZ_MUST_USE bool emitComprehensionForInOrOfVariables(ParseNode* pn, bool* lexicalScope);
     MOZ_MUST_USE bool emitComprehensionForOf(ParseNode* pn);
 
     MOZ_MUST_USE bool emitDo(ParseNode* pn);
     MOZ_MUST_USE bool emitWhile(ParseNode* pn);
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -253,16 +253,49 @@ GetEnvironmentName(JSContext* cx, Handle
     RootedObject obj(cx), pobj(cx);
     if (!LookupName(cx, name, envChain, &obj, &pobj, &prop))
         return false;
 
     return FetchName<mode>(cx, obj, pobj, name, prop, vp);
 }
 
 inline bool
+HasOwnProperty(JSContext* cx, HandleValue val, HandleValue idValue, bool* result)
+{
+
+    // As an optimization, provide a fast path when rooting is not necessary and
+    // we can safely retrieve the object's shape.
+    jsid id;
+    if (val.isObject() && ValueToId<NoGC>(cx, idValue, &id)) {
+        JSObject* obj = &val.toObject();
+        PropertyResult prop;
+        if (obj->isNative() &&
+            NativeLookupOwnProperty<NoGC>(cx, &obj->as<NativeObject>(), id, &prop))
+        {
+            *result = prop.isFound();
+            return true;
+        }
+    }
+
+    // Step 1.
+    RootedId key(cx);
+    if (!ToPropertyKey(cx, idValue, &key))
+        return false;
+
+    // Step 2.
+    RootedObject obj(cx, ToObject(cx, val));
+    if (!obj)
+        return false;
+
+    // Step 3.
+    return HasOwnProperty(cx, obj, key, result);
+}
+
+
+inline bool
 GetIntrinsicOperation(JSContext* cx, jsbytecode* pc, MutableHandleValue vp)
 {
     RootedPropertyName name(cx, cx->currentScript()->getName(pc));
     return GlobalObject::getIntrinsicValue(cx, cx->global(), name, vp);
 }
 
 inline bool
 SetIntrinsicOperation(JSContext* cx, JSScript* script, jsbytecode* pc, HandleValue val)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1918,17 +1918,16 @@ CASE(EnableInterruptsPseudoOpcode)
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED211)
 CASE(JSOP_UNUSED220)
 CASE(JSOP_UNUSED221)
 CASE(JSOP_UNUSED222)
 CASE(JSOP_UNUSED223)
 CASE(JSOP_CONDSWITCH)
 {
     MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
     ADVANCE_AND_DISPATCH(1);
@@ -2166,16 +2165,30 @@ CASE(JSOP_IN)
             goto error;
     }
     TRY_BRANCH_AFTER_COND(found, 2);
     REGS.sp--;
     REGS.sp[-1].setBoolean(found);
 }
 END_CASE(JSOP_IN)
 
+CASE(JSOP_HASOWN)
+{
+    HandleValue val = REGS.stackHandleAt(-1);
+    HandleValue idval = REGS.stackHandleAt(-2);
+
+    bool found;
+    if (!HasOwnProperty(cx, val, idval, &found))
+        goto error;
+
+    REGS.sp--;
+    REGS.sp[-1].setBoolean(found);
+}
+END_CASE(JSOP_HASOWN)
+
 CASE(JSOP_ITER)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 1);
     uint8_t flags = GET_UINT8(REGS.pc);
     HandleValue val = REGS.stackHandleAt(-1);
     ReservedRooted<JSObject*> iter(&rootObject0);
     iter.set(ValueToIterator(cx, flags, val));
     if (!iter)
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2156,17 +2156,27 @@ 1234567890123456789012345678901234567890
      * Pops the iterator from the top of the stack, and create async iterator
      * from it and push the async iterator back onto the stack.
      *   Category: Statements
      *   Type: Generator
      *   Operands:
      *   Stack: iter => asynciter
      */ \
     macro(JSOP_TOASYNCITER,   210, "toasynciter",  NULL,  1,  1,  1,  JOF_BYTE) \
-    macro(JSOP_UNUSED211,     211, "unused211",    NULL,  1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top two values 'id' and 'obj' from the stack, then pushes
+     * obj.hasOwnProperty(id)
+     *
+     * Note that 'obj' is the top value.
+     *   Category: Other
+     *   Type:
+     *   Operands:
+     *   Stack: id, obj => (obj.hasOwnProperty(id))
+     */ \
+    macro(JSOP_HASOWN,        211, "hasown",     NULL,    1,  2,  1, JOF_BYTE) \
     /*
      * Initializes generator frame, creates a generator and pushes it on the
      * stack.
      *   Category: Statements
      *   Type: Generator
      *   Operands:
      *   Stack: => generator
      */ \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2275,17 +2275,16 @@ static const JSFunctionSpec intrinsic_fu
 
     JS_FN("std_Number_valueOf",                  num_valueOf,                  0,0),
 
     JS_INLINABLE_FN("std_Object_create",         obj_create,                   2, 0, ObjectCreate),
     JS_FN("std_Object_propertyIsEnumerable",     obj_propertyIsEnumerable,     1,0),
     JS_FN("std_Object_defineProperty",           obj_defineProperty,           3,0),
     JS_FN("std_Object_getOwnPropertyNames",      obj_getOwnPropertyNames,      1,0),
     JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
-    JS_FN("std_Object_hasOwnProperty",           obj_hasOwnProperty,           1,0),
     JS_FN("std_Object_toString",                 obj_toString,                 0,0),
 
     JS_FN("std_Reflect_getPrototypeOf",          Reflect_getPrototypeOf,       1,0),
     JS_FN("std_Reflect_isExtensible",            Reflect_isExtensible,         1,0),
 
     JS_FN("std_Set_has",                         SetObject::has,               1,0),
     JS_FN("std_Set_iterator",                    SetObject::values,            0,0),