Bug 1234702 - Part 3: Self-host default derived class constructor. (r=till)
authorEric Faust <efaustbmo@gmail.com>
Wed, 06 Jan 2016 14:26:14 -0800
changeset 278880 10d23a474969b04776bb6200895dc8e64a0dd85e
parent 278879 e7fac5cfd89a3603e132cbb4279b62f14591e551
child 278881 8c69c6036831f6126e64734af669f5c27d47b07f
push id29860
push usercbook@mozilla.com
push dateThu, 07 Jan 2016 10:51:20 +0000
treeherdermozilla-central@e0bcd16e1d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1234702
milestone46.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 1234702 - Part 3: Self-host default derived class constructor. (r=till)
js/src/asmjs/AsmJS.cpp
js/src/builtin/Classes.js
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/moz.build
js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js
js/src/vm/CommonPropertyNames.h
js/src/vm/GlobalObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Runtime.h
js/src/vm/SelfHosting.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -6891,17 +6891,17 @@ ParseFunction(ModuleValidator& m, ParseN
 
     ParseNode* fn = m.parser().handler.newFunctionDefinition();
     if (!fn)
         return false;
 
     // This flows into FunctionBox, so must be tenured.
     RootedFunction fun(m.cx(),
                        NewScriptedFunction(m.cx(), 0, JSFunction::INTERPRETED,
-                                           name, gc::AllocKind::FUNCTION,
+                                           name, /* proto = */ nullptr, gc::AllocKind::FUNCTION,
                                            TenuredObject));
     if (!fun)
         return false;
 
     AsmJSParseContext* outerpc = m.parser().pc;
 
     Directives directives(outerpc);
     FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
@@ -7826,17 +7826,17 @@ HandleDynamicLinkFailure(JSContext* cx, 
 
     uint32_t begin = module.srcBodyStart();  // starts right after 'use asm'
     uint32_t end = module.srcEndBeforeCurly();
     Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
     if (!src)
         return false;
 
     RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
-                                               name, gc::AllocKind::FUNCTION,
+                                               name, /* proto = */ nullptr, gc::AllocKind::FUNCTION,
                                                TenuredObject));
     if (!fun)
         return false;
 
     Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
     if (!formals.reserve(3))
         return false;
 
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/Classes.js
@@ -0,0 +1,11 @@
+// Give a builtin constructor that we can use as the default. When we give
+// it to our newly made class, we will be sure to set it up with the correct name
+// and .prototype, so that everything works properly.
+
+var DefaultDerivedClassConstructor =
+    class extends null {
+        constructor(...args) {
+            super(...allowContentSpread(args));
+        }
+    };
+MakeDefaultConstructor(DefaultDerivedClassConstructor);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4234,16 +4234,17 @@ CompileFunction(JSContext* cx, const Rea
     Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx));
     for (unsigned i = 0; i < nargs; i++) {
         RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i])));
         if (!argAtom || !formals.append(argAtom->asPropertyName()))
             return false;
     }
 
     fun.set(NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, funAtom,
+                                /* proto = */ nullptr,
                                 gc::AllocKind::FUNCTION, TenuredObject,
                                 enclosingDynamicScope));
     if (!fun)
         return false;
 
     // Make sure the static scope chain matches up when we have a
     // non-syntactic scope.
     MOZ_ASSERT_IF(!IsGlobalLexicalScope(enclosingDynamicScope),
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1077,17 +1077,17 @@ js::FunctionToString(JSContext* cx, Hand
         {
             return nullptr;
         }
         if (!lambdaParen && fun->isLambda() && !fun->isArrow() && !out.append(")"))
             return nullptr;
     } else {
         MOZ_ASSERT(!fun->isExprBody());
 
-        if (fun->isNative() && fun->native() == js::DefaultDerivedClassConstructor) {
+        if (fun->infallibleIsDefaultClassConstructor(cx) && fun->isDerivedClassConstructor()) {
             if (!out.append("(...args) {\n    ") ||
                 !out.append("super(...args);\n}"))
             {
                 return nullptr;
             }
         } else {
             if (!out.append("() {\n    "))
                 return nullptr;
@@ -1263,16 +1263,35 @@ js::fun_apply(JSContext* cx, unsigned ar
     // Step 9.
     if (!Invoke(cx, args2))
         return false;
 
     args.rval().set(args2.rval());
     return true;
 }
 
+bool
+JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
+{
+    if (!isSelfHostedBuiltin())
+        return false;
+
+    bool isDefault = false;
+    if (isInterpretedLazy()) {
+        JSAtom* name = &getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).toString()->asAtom();
+        isDefault = name == cx->names().DefaultDerivedClassConstructor;
+    } else {
+        isDefault = nonLazyScript()->isDefaultClassConstructor();
+    }
+
+    MOZ_ASSERT_IF(isDefault, isConstructor());
+    MOZ_ASSERT_IF(isDefault, isClassConstructor());
+    return isDefault;
+}
+
 static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET     = 0;
 static const uint32_t JSSLOT_BOUND_FUNCTION_THIS       = 1;
 static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 2;
 
 static const uint32_t BOUND_FUNCTION_RESERVED_SLOTS = 3;
 
 inline bool
 JSFunction::initBoundFunction(JSContext* cx, HandleObject target, HandleValue thisArg,
@@ -2003,25 +2022,26 @@ js::NewNativeConstructor(ExclusiveContex
     MOZ_ASSERT(flags & JSFunction::NATIVE_CTOR);
     return NewFunctionWithProto(cx, native, nargs, flags, nullptr, atom,
                                 nullptr, allocKind, newKind);
 }
 
 JSFunction*
 js::NewScriptedFunction(ExclusiveContext* cx, unsigned nargs,
                         JSFunction::Flags flags, HandleAtom atom,
+                        HandleObject proto /* = nullptr */,
                         gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
                         NewObjectKind newKind /* = GenericObject */,
                         HandleObject enclosingDynamicScopeArg /* = nullptr */)
 {
     RootedObject enclosingDynamicScope(cx, enclosingDynamicScopeArg);
     if (!enclosingDynamicScope)
         enclosingDynamicScope = &cx->global()->lexicalScope();
     return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingDynamicScope,
-                                atom, nullptr, allocKind, newKind);
+                                atom, proto, allocKind, newKind);
 }
 
 #ifdef DEBUG
 static bool
 NewFunctionScopeIsWellFormed(ExclusiveContext* cx, HandleObject parent)
 {
     // Assert that the parent is null, global, or a debug scope proxy. All
     // other cases of polluting global scope behavior are handled by
@@ -2308,16 +2328,17 @@ js::DefineFunction(JSContext* cx, Handle
     RootedAtom atom(cx, IdToFunctionName(cx, id));
     if (!atom)
         return nullptr;
 
     RootedFunction fun(cx);
     if (!native)
         fun = NewScriptedFunction(cx, nargs,
                                   JSFunction::INTERPRETED_LAZY, atom,
+                                  /* proto = */ nullptr,
                                   allocKind, GenericObject, obj);
     else if (flags & JSFUN_CONSTRUCTOR)
         fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
     else
         fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
 
     if (!fun)
         return nullptr;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -187,16 +187,18 @@ class JSFunction : public js::NativeObje
     bool isExprBody()               const { return flags() & EXPR_BODY; }
     bool hasGuessedAtom()           const { return flags() & HAS_GUESSED_ATOM; }
     bool isLambda()                 const { return flags() & LAMBDA; }
     bool hasRest()                  const { return flags() & HAS_REST; }
     bool isInterpretedLazy()        const { return flags() & INTERPRETED_LAZY; }
     bool hasScript()                const { return flags() & INTERPRETED; }
     bool isBeingParsed()            const { return flags() & BEING_PARSED; }
 
+    bool infallibleIsDefaultClassConstructor(JSContext* cx) const;
+
     // Arrow functions store their lexical new.target in the first extended slot.
     bool isArrow()                  const { return kind() == Arrow; }
     // Every class-constructor is also a method.
     bool isMethod()                 const { return kind() == Method || kind() == ClassConstructor; }
     bool isClassConstructor()       const { return kind() == ClassConstructor; }
 
     bool isGetter()                 const { return kind() == Getter; }
     bool isSetter()                 const { return kind() == Setter; }
@@ -250,16 +252,23 @@ class JSFunction : public js::NativeObje
 
     // Make the function constructible.
     void setIsConstructor() {
         MOZ_ASSERT(!isConstructor());
         MOZ_ASSERT(isSelfHostedBuiltin());
         flags_ |= CONSTRUCTOR;
     }
 
+    void setIsClassConstructor() {
+        MOZ_ASSERT(!isClassConstructor());
+        MOZ_ASSERT(isConstructor());
+
+        setKind(ClassConstructor);
+    }
+
     // Can be called multiple times by the parser.
     void setArgCount(uint16_t nargs) {
         this->nargs_ = nargs;
     }
 
     // Can be called multiple times by the parser.
     void setHasRest() {
         flags_ |= HAS_REST;
@@ -638,17 +647,18 @@ NewNativeConstructor(ExclusiveContext* c
                      NewObjectKind newKind = GenericObject,
                      JSFunction::Flags flags = JSFunction::NATIVE_CTOR);
 
 // Allocate a new scripted function.  If enclosingDynamicScope is null, the
 // global will be used.  In all cases the parent of the resulting object will be
 // the global.
 extern JSFunction*
 NewScriptedFunction(ExclusiveContext* cx, unsigned nargs, JSFunction::Flags flags,
-                    HandleAtom atom, gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
+                    HandleAtom atom, HandleObject proto = nullptr,
+                    gc::AllocKind allocKind = gc::AllocKind::FUNCTION,
                     NewObjectKind newKind = GenericObject,
                     HandleObject enclosingDynamicScope = nullptr);
 
 // By default, if proto is nullptr, Function.prototype is used instead.i
 // If protoHandling is NewFunctionExactProto, and proto is nullptr, the created
 // function will use nullptr as its [[Prototype]] instead. If
 // enclosingDynamicScope is null, the function will have a null environment()
 // (yes, null, not the global).  In all cases, the global will be used as the
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -618,16 +618,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         SelfHosted,
         HasSingleton,
         TreatAsRunOnce,
         HasLazyScript,
         HasNonSyntacticScope,
         HasInnerFunctions,
         NeedsHomeObject,
         IsDerivedClassConstructor,
+        IsDefaultClassConstructor,
     };
 
     uint32_t length, lineno, column, nslots;
     uint32_t natoms, nsrcnotes, i;
     uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes, nyieldoffsets;
     uint32_t prologueLength, version;
     uint32_t funLength = 0;
     uint32_t nTypeSets = 0;
@@ -763,16 +764,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (script->hasNonSyntacticScope())
             scriptBits |= (1 << HasNonSyntacticScope);
         if (script->hasInnerFunctions())
             scriptBits |= (1 << HasInnerFunctions);
         if (script->needsHomeObject())
             scriptBits |= (1 << NeedsHomeObject);
         if (script->isDerivedClassConstructor())
             scriptBits |= (1 << IsDerivedClassConstructor);
+        if (script->isDefaultClassConstructor())
+            scriptBits |= (1 << IsDefaultClassConstructor);
     }
 
     if (!xdr->codeUint32(&prologueLength))
         return false;
     if (!xdr->codeUint32(&version))
         return false;
 
     // To fuse allocations, we need lengths of all embedded arrays early.
@@ -907,16 +910,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << HasNonSyntacticScope))
             script->hasNonSyntacticScope_ = true;
         if (scriptBits & (1 << HasInnerFunctions))
             script->hasInnerFunctions_ = true;
         if (scriptBits & (1 << NeedsHomeObject))
             script->needsHomeObject_ = true;
         if (scriptBits & (1 << IsDerivedClassConstructor))
             script->isDerivedClassConstructor_ = true;
+        if (scriptBits & (1 << IsDefaultClassConstructor))
+            script->isDefaultClassConstructor_ = true;
 
         if (scriptBits & (1 << IsLegacyGenerator)) {
             MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
             script->setGeneratorKind(LegacyGenerator);
         } else if (scriptBits & (1 << IsStarGenerator))
             script->setGeneratorKind(StarGenerator);
     }
 
@@ -3579,16 +3584,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
     dst->hasSingletons_ = src->hasSingletons();
     dst->treatAsRunOnce_ = src->treatAsRunOnce();
     dst->hasInnerFunctions_ = src->hasInnerFunctions();
     dst->isGeneratorExp_ = src->isGeneratorExp();
     dst->setGeneratorKind(src->generatorKind());
     dst->isDerivedClassConstructor_ = src->isDerivedClassConstructor();
     dst->needsHomeObject_ = src->needsHomeObject();
+    dst->isDefaultClassConstructor_ = src->isDefaultClassConstructor();
 
     if (nconsts != 0) {
         HeapValue* vector = Rebase<HeapValue>(dst, src, src->consts()->vector);
         dst->consts()->vector = vector;
         for (unsigned i = 0; i < nconsts; ++i)
             MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
     }
     if (nobjects != 0) {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1183,16 +1183,17 @@ class JSScript : public js::gc::TenuredC
 
     // Script contains inner functions. Used to check if we can relazify the
     // script.
     bool hasInnerFunctions_:1;
 
     bool needsHomeObject_:1;
 
     bool isDerivedClassConstructor_:1;
+    bool isDefaultClassConstructor_:1;
 
     // Add padding so JSScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
   protected:
 #if JS_BITS_PER_WORD == 32
     // No padding currently required.
 #endif
 
@@ -1414,24 +1415,28 @@ class JSScript : public js::gc::TenuredC
         return uninlineable_;
     }
     bool invalidatedIdempotentCache() const {
         return invalidatedIdempotentCache_;
     }
     bool failedLexicalCheck() const {
         return failedLexicalCheck_;
     }
+    bool isDefaultClassConstructor() const {
+        return isDefaultClassConstructor_;
+    }
 
     void setFailedBoundsCheck() { failedBoundsCheck_ = true; }
     void setFailedShapeGuard() { failedShapeGuard_ = true; }
     void setHadFrequentBailouts() { hadFrequentBailouts_ = true; }
     void setHadOverflowBailout() { hadOverflowBailout_ = true; }
     void setUninlineable() { uninlineable_ = true; }
     void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; }
     void setFailedLexicalCheck() { failedLexicalCheck_ = true; }
+    void setIsDefaultClassConstructor() { isDefaultClassConstructor_ = true; }
 
     bool hasScriptCounts() const { return hasScriptCounts_; }
 
     bool hasFreezeConstraints() const { return hasFreezeConstraints_; }
     void setHasFreezeConstraints() { hasFreezeConstraints_ = true; }
 
     bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; }
     void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; }
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -686,16 +686,17 @@ GENERATED_FILES += ['selfhosted.out.h']
 selfhosted = GENERATED_FILES['selfhosted.out.h']
 selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
 selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
+    'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
     'builtin/Generator.js',
     'builtin/Intl.js',
     'builtin/IntlData.js',
     'builtin/Iterator.js',
     'builtin/Map.js',
     'builtin/Module.js',
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Class/defaultConstructorDerivedSpread.js
@@ -0,0 +1,19 @@
+/* Make sure that the default derived class constructor has the required spread semantics.
+ *
+ * Test credit André Bargull
+ */
+
+Array.prototype[Symbol.iterator] = function*() { yield 1; yield 2; };
+
+class Base {
+    constructor(a, b) {
+        assertEq(a, 1);
+        assertEq(b, 2);
+    }
+};
+class Derived extends Base {};
+
+new Derived();
+
+if (typeof reportCompare === 'function')
+    reportCompare(0,0,"OK");
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -61,16 +61,17 @@
     macro(DateTimeFormat, DateTimeFormat, "DateTimeFormat") \
     macro(DateTimeFormatFormatGet, DateTimeFormatFormatGet, "Intl_DateTimeFormat_format_get") \
     macro(DateTimeFormatFormatToPartsGet, DateTimeFormatFormatToPartsGet, "Intl_DateTimeFormat_formatToParts_get") \
     macro(day, day, "day") \
     macro(dayperiod, dayperiod, "dayperiod") \
     macro(decodeURI, decodeURI, "decodeURI") \
     macro(decodeURIComponent, decodeURIComponent, "decodeURIComponent") \
     macro(default_, default_, "default") \
+    macro(DefaultDerivedClassConstructor, DefaultDerivedClassConstructor, "DefaultDerivedClassConstructor") \
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(displayURL, displayURL, "displayURL") \
     macro(done, done, "done") \
     macro(dotGenerator, dotGenerator, ".generator") \
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -682,18 +682,22 @@ GlobalObject::getSelfHostedFunction(JSCo
         // might be neither "selfHostedName" nor "name". In that case, its
         // canonical name must've been set using the `_SetCanonicalName`
         // intrinsic.
         cx->runtime()->assertSelfHostedFunctionHasCanonicalName(cx, selfHostedName);
         return true;
     }
 
     RootedFunction fun(cx);
-    if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, nargs, &fun))
+    if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, nargs,
+                                                          /* proto = */ nullptr,
+                                                          SingletonObject, &fun))
+    {
         return false;
+    }
     funVal.setObject(*fun);
 
     return GlobalObject::addIntrinsicValue(cx, global, selfHostedName, funVal);
 }
 
 /* static */ bool
 GlobalObject::addIntrinsicValue(JSContext* cx, Handle<GlobalObject*> global,
                                 HandlePropertyName name, HandleValue value)
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -286,24 +286,42 @@ SetPropertyOperation(JSContext* cx, JSOp
     // to obj's [[Set]] internal method. See bug 603201.
     RootedValue receiver(cx, ObjectValue(*obj));
     ObjectOpResult result;
     return SetProperty(cx, obj, id, rval, receiver, result) &&
            result.checkStrictErrorOrWarning(cx, obj, id, op == JSOP_STRICTSETPROP);
 }
 
 static JSFunction*
-MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
-{
-    bool derived = op == JSOP_DERIVEDCONSTRUCTOR;
-    MOZ_ASSERT(derived == !!proto);
-
+MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom)
+{
+    RootedAtom name(cx, atom == cx->names().empty ? nullptr : atom);
+    JSNative native = DefaultClassConstructor;
+    return NewFunctionWithProto(cx, native, 0, JSFunction::NATIVE_CLASS_CTOR, nullptr, name, nullptr);
+}
+
+static JSFunction*
+MakeDerivedDefaultConstructor(JSContext* cx, JSAtom* atom, HandleObject proto)
+{
+    RootedPropertyName selfHostedName(cx, cx->names().DefaultDerivedClassConstructor);
     RootedAtom name(cx, atom == cx->names().empty ? nullptr : atom);
-    JSNative native = derived ? DefaultDerivedClassConstructor : DefaultClassConstructor;
-    return NewFunctionWithProto(cx, native, 0, JSFunction::NATIVE_CLASS_CTOR, nullptr, name, proto);
+
+    RootedFunction ctor(cx);
+    if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name, /* nargs = */ 1,
+                                                          proto, TenuredObject, &ctor))
+    {
+        return nullptr;
+    }
+
+    ctor->setIsConstructor();
+    ctor->setIsClassConstructor();
+
+    MOZ_ASSERT(ctor->infallibleIsDefaultClassConstructor(cx));
+
+    return ctor;
 }
 
 bool
 js::ReportIsNotFunction(JSContext* cx, HandleValue v, int numToSkip, MaybeConstruct construct)
 {
     unsigned error = construct ? JSMSG_NOT_CONSTRUCTOR : JSMSG_NOT_FUNCTION;
     int spIndex = numToSkip >= 0 ? -(numToSkip + 1) : JSDVG_SEARCH_STACK;
 
@@ -3879,29 +3897,27 @@ CASE(JSOP_SUPERFUN)
 }
 END_CASE(JSOP_SUPERFUN)
 
 CASE(JSOP_DERIVEDCONSTRUCTOR)
 {
     MOZ_ASSERT(REGS.sp[-1].isObject());
     ReservedRooted<JSObject*> proto(&rootObject0, &REGS.sp[-1].toObject());
 
-    JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
-                                                     proto);
+    JSFunction* constructor = MakeDerivedDefaultConstructor(cx, script->getAtom(REGS.pc), proto);
     if (!constructor)
         goto error;
 
     REGS.sp[-1].setObject(*constructor);
 }
 END_CASE(JSOP_DERIVEDCONSTRUCTOR)
 
 CASE(JSOP_CLASSCONSTRUCTOR)
 {
-    JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc),
-                                                     nullptr);
+    JSFunction* constructor = MakeDefaultConstructor(cx, JSOp(*REGS.pc), script->getAtom(REGS.pc));
     if (!constructor)
         goto error;
     PUSH_OBJECT(*constructor);
 }
 END_CASE(JSOP_CLASSCONSTRUCTOR)
 
 CASE(JSOP_CHECKOBJCOERCIBLE)
 {
@@ -4810,42 +4826,16 @@ js::DefaultClassConstructor(JSContext* c
     JSObject* obj = CreateThis(cx, &PlainObject::class_, newTarget);
     if (!obj)
         return false;
 
     args.rval().set(ObjectValue(*obj));
     return true;
 }
 
-bool
-js::DefaultDerivedClassConstructor(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (!args.isConstructing()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CANT_CALL_CLASS_CONSTRUCTOR);
-        return false;
-    }
-
-    RootedObject fun(cx, &args.callee());
-    RootedObject superFun(cx);
-    if (!GetPrototype(cx, fun, &superFun))
-        return false;
-
-    RootedValue fval(cx, ObjectOrNullValue(superFun));
-    if (!IsConstructor(fval)) {
-        ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval, nullptr);
-        return false;
-    }
-
-    ConstructArgs constArgs(cx);
-    if (!FillArgumentsFromArraylike(cx, constArgs, args))
-        return false;
-    return Construct(cx, fval, constArgs, args.newTarget(), args.rval());
-}
-
 void
 js::ReportRuntimeRedeclaration(JSContext* cx, HandlePropertyName name,
                                frontend::Definition::Kind declKind)
 {
     JSAutoByteString printable;
     if (AtomToPrintableString(cx, name, &printable)) {
         // We cannot distinguish 'var' declarations from manually defined,
         // non-configurable global properties.
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -984,16 +984,18 @@ struct JSRuntime : public JS::shadow::Ru
     void markSelfHostingGlobal(JSTracer* trc);
     bool isSelfHostingGlobal(JSObject* global) {
         return global == selfHostingGlobal_;
     }
     bool isSelfHostingCompartment(JSCompartment* comp) const;
     bool isSelfHostingZone(const JS::Zone* zone) const;
     bool createLazySelfHostedFunctionClone(JSContext* cx, js::HandlePropertyName selfHostedName,
                                            js::HandleAtom name, unsigned nargs,
+                                           js::HandleObject proto,
+                                           js::NewObjectKind newKind,
                                            js::MutableHandleFunction fun);
     bool cloneSelfHostedFunctionScript(JSContext* cx, js::Handle<js::PropertyName*> name,
                                        js::Handle<JSFunction*> targetFun);
     bool cloneSelfHostedValue(JSContext* cx, js::Handle<js::PropertyName*> name,
                               js::MutableHandleValue vp);
     void assertSelfHostedFunctionHasCanonicalName(JSContext* cx, js::HandlePropertyName name);
 
     //-------------------------------------------------------------------------
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -287,16 +287,31 @@ intrinsic_MakeConstructible(JSContext* c
         return false;
     }
 
     ctor->as<JSFunction>().setIsConstructor();
     args.rval().setUndefined();
     return true;
 }
 
+static bool
+intrinsic_MakeDefaultConstructor(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+    MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin());
+
+    RootedFunction ctor(cx, &args[0].toObject().as<JSFunction>());
+
+    ctor->nonLazyScript()->setIsDefaultClassConstructor();
+
+    args.rval().setUndefined();
+    return true;
+}
+
 /*
  * Used to decompile values in the nearest non-builtin stack frame, falling
  * back to decompiling in the current frame. Helpful for printing higher-order
  * function arguments.
  *
  * The user must supply the argument number of the value in question; it
  * _cannot_ be automatically determined.
  */
@@ -1498,16 +1513,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_INLINABLE_FN("IsCallable",    intrinsic_IsCallable,              1,0, IntrinsicIsCallable),
     JS_FN("IsConstructor",           intrinsic_IsConstructor,           1,0),
     JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("ThrowRangeError",         intrinsic_ThrowRangeError,         4,0),
     JS_FN("ThrowTypeError",          intrinsic_ThrowTypeError,          4,0),
     JS_FN("ThrowSyntaxError",        intrinsic_ThrowSyntaxError,        4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
+    JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("LocalTZA",                intrinsic_LocalTZA,                0,0),
 
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
     JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel,       3,0,
@@ -1991,16 +2007,17 @@ CloneObject(JSContext* cx, HandleNativeO
             MOZ_CRASH("SelfHosted cloning cannot handle cyclic object graphs.");
     }
 #endif
 
     RootedObject clone(cx);
     if (selfHostedObject->is<JSFunction>()) {
         RootedFunction selfHostedFunction(cx, &selfHostedObject->as<JSFunction>());
         bool hasName = selfHostedFunction->atom() != nullptr;
+
         // Arrow functions use the first extended slot for their lexical |this| value.
         MOZ_ASSERT(!selfHostedFunction->isArrow());
         js::gc::AllocKind kind = hasName
                                  ? gc::AllocKind::FUNCTION_EXTENDED
                                  : selfHostedFunction->getAllocKind();
         MOZ_ASSERT(!CanReuseScriptForClone(cx->compartment(), selfHostedFunction, cx->global()));
         Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
         RootedObject staticGlobalLexical(cx, &globalLexical->staticBlock());
@@ -2036,16 +2053,17 @@ CloneObject(JSContext* cx, HandleNativeO
     } else {
         MOZ_ASSERT(selfHostedObject->isNative());
         clone = NewObjectWithGivenProto(cx, selfHostedObject->getClass(), nullptr,
                                         selfHostedObject->asTenured().getAllocKind(),
                                         SingletonObject);
     }
     if (!clone)
         return nullptr;
+
     if (!CloneProperties(cx, selfHostedObject, clone))
         return nullptr;
     return clone;
 }
 
 static bool
 CloneValue(JSContext* cx, HandleValue selfHostedValue, MutableHandleValue vp)
 {
@@ -2076,30 +2094,33 @@ CloneValue(JSContext* cx, HandleValue se
         MOZ_CRASH("Self-hosting CloneValue can't clone given value.");
     }
     return true;
 }
 
 bool
 JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName selfHostedName,
                                              HandleAtom name, unsigned nargs,
+                                             HandleObject proto, NewObjectKind newKind,
                                              MutableHandleFunction fun)
 {
+    MOZ_ASSERT(newKind != GenericObject);
+
     RootedAtom funName(cx, name);
     JSFunction* selfHostedFun = getUnclonedSelfHostedFunction(cx, selfHostedName);
     if (!selfHostedFun)
         return false;
 
     if (!selfHostedFun->hasGuessedAtom() && selfHostedFun->atom() != selfHostedName) {
         MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean());
         funName = selfHostedFun->atom();
     }
 
     fun.set(NewScriptedFunction(cx, nargs, JSFunction::INTERPRETED_LAZY,
-                                funName, gc::AllocKind::FUNCTION_EXTENDED, SingletonObject));
+                                funName, proto, gc::AllocKind::FUNCTION_EXTENDED, newKind));
     if (!fun)
         return false;
     fun->setIsSelfHostedBuiltin();
     fun->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(selfHostedName));
     return true;
 }
 
 bool