Bug 1000780 - Part 5: Self-host Function.prototype.bind. r=jandem

--HG--
extra : rebase_source : 52301e2d50a038040c5d86e8b7b052acd71a9660
This commit is contained in:
Till Schneidereit 2014-12-07 23:39:23 +01:00
parent 851c8ab557
commit 1a6dfabe3f
24 changed files with 513 additions and 365 deletions

299
js/src/builtin/Function.js Normal file
View File

@ -0,0 +1,299 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
// ES6 spec draft 29, 19.2.3.2
function FunctionBind(thisArg, ...boundArgs) {
// Step 1.
var target = this;
// Step 2.
if (!IsCallable(target))
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, 'Function', 'bind', target);
// Step 3 (implicit).
// Steps 4-5.
var F;
var argCount = boundArgs.length;
switch (argCount) {
case 0:
F = bind_bindFunction0(target, thisArg, boundArgs);
break;
case 1:
F = bind_bindFunction1(target, thisArg, boundArgs);
break;
case 2:
F = bind_bindFunction2(target, thisArg, boundArgs);
break;
default:
F = bind_bindFunctionN(target, thisArg, boundArgs);
}
// Steps 6-7.
var targetHasLength = callFunction(std_Object_hasOwnProperty, target, "length");
// Step 8.
var L;
if (targetHasLength) {
// Steps 8.a-b.
var targetLen = target.length;
// Step 8.c.
if (typeof targetLen !== 'number') {
L = 0;
} else {
// Steps d.i-ii.
L = std_Math_max(0, ToInteger(targetLen) - argCount);
}
} else {
// Step 9.
L = 0;
}
// Steps 12-13.
var targetName = target.name;
// Step 14.
if (typeof targetName !== "string")
targetName = "";
// Steps 10-11, 15-16.
_FinishBoundFunctionInit(F, target, L, targetName);
// Ensure that the apply intrinsic has been cloned so it can be baked into
// JIT code.
var funApply = std_Function_apply;
// Step 17.
return F;
}
/**
* bind_bindFunction{0,1,2} are special cases of the generic bind_bindFunctionN
* below. They avoid the need to merge the lists of bound arguments and call
* arguments to the bound function into a new list which is then used in a
* destructuring call of the bound function.
*
* All three of these functions again have special-cases for call argument
* counts between 0 and 5. For calls with 6+ arguments, all - bound and call -
* arguments are copied into an array before invoking the generic call and
* construct helper functions. This avoids having to use rest parameters and
* destructuring in the fast path.
*
* All bind_bindFunction{X} functions have the same signature to enable simple
* reading out of closed-over state by debugging functions.
*/
function bind_bindFunction0(fun, thisArg, boundArgs) {
return function bound() {
var a = arguments;
var constructing = _IsConstructing();
if (constructing) {
switch (a.length) {
case 0:
return new fun();
case 1:
return new fun(a[0]);
case 2:
return new fun(a[0], a[1]);
case 3:
return new fun(a[0], a[1], a[2]);
case 4:
return new fun(a[0], a[1], a[2], a[3]);
case 5:
return new fun(a[0], a[1], a[2], a[3], a[4]);
}
} else {
switch (a.length) {
case 0:
return callContentFunction(fun, thisArg);
case 1:
return callContentFunction(fun, thisArg, a[0]);
case 2:
return callContentFunction(fun, thisArg, a[0], a[1]);
case 3:
return callContentFunction(fun, thisArg, a[0], a[1], a[2]);
case 4:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]);
case 5:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]);
}
}
var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
};
}
function bind_bindFunction1(fun, thisArg, boundArgs) {
var bound1 = boundArgs[0];
return function bound() {
var a = arguments;
var constructing = _IsConstructing();
if (constructing) {
switch (a.length) {
case 0:
return new fun(bound1);
case 1:
return new fun(bound1, a[0]);
case 2:
return new fun(bound1, a[0], a[1]);
case 3:
return new fun(bound1, a[0], a[1], a[2]);
case 4:
return new fun(bound1, a[0], a[1], a[2], a[3]);
case 5:
return new fun(bound1, a[0], a[1], a[2], a[3], a[4]);
}
} else {
switch (a.length) {
case 0:
return callContentFunction(fun, thisArg, bound1);
case 1:
return callContentFunction(fun, thisArg, bound1, a[0]);
case 2:
return callContentFunction(fun, thisArg, bound1, a[0], a[1]);
case 3:
return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2]);
case 4:
return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3]);
case 5:
return callContentFunction(fun, thisArg, bound1, a[0], a[1], a[2], a[3], a[4]);
}
}
var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
};
}
function bind_bindFunction2(fun, thisArg, boundArgs) {
var bound1 = boundArgs[0];
var bound2 = boundArgs[1];
return function bound() {
var a = arguments;
var constructing = _IsConstructing();
if (constructing) {
switch (a.length) {
case 0:
return new fun(bound1, bound2);
case 1:
return new fun(bound1, bound2, a[0]);
case 2:
return new fun(bound1, bound2, a[0], a[1]);
case 3:
return new fun(bound1, bound2, a[0], a[1], a[2]);
case 4:
return new fun(bound1, bound2, a[0], a[1], a[2], a[3]);
case 5:
return new fun(bound1, bound2, a[0], a[1], a[2], a[3], a[4]);
}
} else {
switch (a.length) {
case 0:
return callContentFunction(fun, thisArg, bound1, bound2);
case 1:
return callContentFunction(fun, thisArg, bound1, bound2, a[0]);
case 2:
return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1]);
case 3:
return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2]);
case 4:
return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3]);
case 5:
return callContentFunction(fun, thisArg, bound1, bound2, a[0], a[1], a[2], a[3], a[4]);
}
}
var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
return bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs);
};
}
function bind_bindFunctionN(fun, thisArg, boundArgs) {
assert(boundArgs.length > 2, "Fast paths should be used for few-bound-args cases.");
return function bound() {
if (arguments.length === 0) {
if (_IsConstructing())
return bind_constructFunctionN(fun, boundArgs);
else
return bind_applyFunctionN(fun, thisArg, boundArgs);
}
var callArgs = FUN_APPLY(bind_mapArguments, null, arguments);
return bind_invokeFunctionN(fun, thisArg, _IsConstructing(), boundArgs, callArgs);
};
}
function bind_mapArguments() {
var len = arguments.length;
var args = std_Array(len);
for (var i = 0; i < len; i++)
_DefineDataProperty(args, i, arguments[i]);
return args;
}
function bind_invokeFunctionN(fun, thisArg, constructing, boundArgs, callArgs) {
var boundArgsCount = boundArgs.length;
var callArgsCount = callArgs.length;
var args = std_Array(boundArgsCount + callArgsCount);
for (var i = 0; i < boundArgsCount; i++)
_DefineDataProperty(args, i, boundArgs[i]);
for (var i = 0; i < callArgsCount; i++)
_DefineDataProperty(args, i + boundArgsCount, callArgs[i]);
if (constructing)
return bind_constructFunctionN(fun, args);
return bind_applyFunctionN(fun, thisArg, args);
}
function bind_applyFunctionN(fun, thisArg, a) {
switch (a.length) {
case 0:
return callContentFunction(fun, thisArg);
case 1:
return callContentFunction(fun, thisArg, a[0]);
case 2:
return callContentFunction(fun, thisArg, a[0], a[1]);
case 3:
return callContentFunction(fun, thisArg, a[0], a[1], a[2]);
case 4:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3]);
case 5:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4]);
case 6:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5]);
case 7:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
case 8:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
case 9:
return callContentFunction(fun, thisArg, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
default:
return FUN_APPLY(fun, thisArg, a);
}
}
function bind_constructFunctionN(fun, a) {
switch (a.length) {
case 1:
return new fun(a[0]);
case 2:
return new fun(a[0], a[1]);
case 3:
return new fun(a[0], a[1], a[2]);
case 4:
return new fun(a[0], a[1], a[2], a[3]);
case 5:
return new fun(a[0], a[1], a[2], a[3], a[4]);
case 6:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5]);
case 7:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6]);
case 8:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
case 9:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8]);
case 10:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
case 11:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10]);
case 12:
return new fun(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11]);
default:
assert(a.length !== 0,
"bound function construction without args should be handled by caller");
return _ConstructFunction(fun, a);
}
}

View File

@ -1596,7 +1596,7 @@ function Intl_Collator_compare_get() {
var F = collatorCompareToBind;
// Step 1.b-d.
var bc = callFunction(std_Function_bind, F, this);
var bc = callFunction(FunctionBind, F, this);
internals.boundCompare = bc;
}
@ -2036,7 +2036,7 @@ function Intl_NumberFormat_format_get() {
var F = numberFormatFormatToBind;
// Step 1.b-d.
var bf = callFunction(std_Function_bind, F, this);
var bf = callFunction(FunctionBind, F, this);
internals.boundFormat = bf;
}
// Step 2.
@ -2732,7 +2732,7 @@ function Intl_DateTimeFormat_format_get() {
var F = dateTimeFormatFormatToBind;
// Step 1.b-d.
var bf = callFunction(std_Function_bind, F, this);
var bf = callFunction(FunctionBind, F, this);
internals.boundFormat = bf;
}
@ -2761,7 +2761,7 @@ function Intl_DateTimeFormat_formatToParts_get() {
var F = dateTimeFormatFormatToPartsToBind;
// Step 1.b-d.
var bf = callFunction(std_Function_bind, F, this);
var bf = callFunction(FunctionBind, F, this);
internals.boundFormatToParts = bf;
}

View File

@ -1091,14 +1091,19 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro
return false;
/*
* Define self-hosted functions after setting the intrinsics holder
* (which is needed to define self-hosted functions)
* Define self-hosted functions on Object and Function after setting the
* intrinsics holder (which is needed to define self-hosted functions).
*/
if (!cx->runtime()->isSelfHostingGlobal(global)) {
if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
return false;
if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties))
return false;
RootedObject funProto(cx, global->getOrCreateFunctionPrototype(cx));
if (!funProto)
return false;
if (!JS_DefineFunctions(cx, funProto, function_methods, OnlyDefineLateProperties))
return false;
}
/*

View File

@ -15,6 +15,10 @@
#define IS_UINT32(x) ((x) >>> 0 === (x))
#define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1
// Unforgeable version of Function.prototype.apply.
#define FUN_APPLY(FUN, RECEIVER, ARGS) \
callFunction(std_Function_apply, FUN, RECEIVER, ARGS)
// Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice.
#define ARRAY_PUSH(ARRAY, ELEMENT) \
callFunction(std_Array_push, ARRAY, ELEMENT);
@ -39,6 +43,10 @@
// global. This slot is used only in debug build.
#define HAS_SELFHOSTED_CANONICAL_NAME_SLOT 0
// Stores the length for bound functions, so the .length property doesn't need
// to be resolved eagerly.
#define BOUND_FUN_LENGTH_SLOT 1
// Stores the private WeakMap slot used for WeakSets
#define WEAKSET_MAP_SLOT 0

View File

@ -231,7 +231,7 @@ const Class js::ScalarTypeDescr::class_ = {
const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
{"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
{"array", {nullptr, nullptr}, 0, 0, "ArrayShorthand"},
{"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
JS_FS_END
};

View File

@ -78,6 +78,6 @@ assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]), s(112, 128));
// Various forms of functions.
assertEq(tByteSize(function () {}), s(32, 64));
assertEq(tByteSize(function () {}.bind()), s(96, 128));
assertEq(tByteSize(function () {}.bind()), s(48, 80));
assertEq(tByteSize(() => 1), s(48, 80));
assertEq(tByteSize(Math.sin), s(32, 64));

View File

@ -1985,7 +1985,7 @@ CodeGenerator::visitLambda(LLambda* lir)
emitLambdaInit(output, scopeChain, info);
if (info.flags & JSFunction::EXTENDED) {
MOZ_ASSERT(info.fun->allowSuperProperty());
MOZ_ASSERT(info.fun->allowSuperProperty() || info.fun->isSelfHostedBuiltin());
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));

View File

@ -79,8 +79,6 @@
\
_(ObjectCreate) \
\
_(CallBoundFunction) \
\
_(SimdInt32x4) \
_(SimdFloat32x4) \
_(SimdBool32x4) \

View File

@ -6270,6 +6270,9 @@ IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* new
return magic;
}
if (target->isBoundFunction())
return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
if (target->isDerivedClassConstructor()) {
MOZ_ASSERT(target->isClassConstructor());
return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));

View File

@ -201,10 +201,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
case InlinableNative::ObjectCreate:
return inlineObjectCreate(callInfo);
// Bound function.
case InlinableNative::CallBoundFunction:
return inlineBoundFunction(callInfo, target);
// SIMD natives.
case InlinableNative::SimdInt32x4:
return inlineSimdInt32x4(callInfo, target->native());
@ -2681,73 +2677,6 @@ IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo)
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineBoundFunction(CallInfo& nativeCallInfo, JSFunction* target)
{
trackOptimizationOutcome(TrackedOutcome::CantInlineBound);
if (!target->getBoundFunctionTarget()->is<JSFunction>())
return InliningStatus_NotInlined;
JSFunction* scriptedTarget = &(target->getBoundFunctionTarget()->as<JSFunction>());
// Don't optimize if we're constructing and the callee is not a
// constructor, so that CallKnown does not have to handle this case
// (it should always throw).
if (nativeCallInfo.constructing() && !scriptedTarget->isConstructor())
return InliningStatus_NotInlined;
if (nativeCallInfo.constructing() && nativeCallInfo.getNewTarget() != nativeCallInfo.fun())
return InliningStatus_NotInlined;
if (gc::IsInsideNursery(scriptedTarget))
return InliningStatus_NotInlined;
for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
const Value val = target->getBoundFunctionArgument(i);
if (val.isObject() && gc::IsInsideNursery(&val.toObject()))
return InliningStatus_NotInlined;
if (val.isString() && !val.toString()->isAtom())
return InliningStatus_NotInlined;
}
const Value thisVal = target->getBoundFunctionThis();
if (thisVal.isObject() && gc::IsInsideNursery(&thisVal.toObject()))
return InliningStatus_NotInlined;
if (thisVal.isString() && !thisVal.toString()->isAtom())
return InliningStatus_NotInlined;
size_t argc = target->getBoundFunctionArgumentCount() + nativeCallInfo.argc();
if (argc > ARGS_LENGTH_MAX)
return InliningStatus_NotInlined;
nativeCallInfo.thisArg()->setImplicitlyUsedUnchecked();
CallInfo callInfo(alloc(), nativeCallInfo.constructing());
callInfo.setFun(constant(ObjectValue(*scriptedTarget)));
callInfo.setThis(constant(thisVal));
if (!callInfo.argv().reserve(argc))
return InliningStatus_Error;
for (size_t i = 0; i < target->getBoundFunctionArgumentCount(); i++) {
MConstant* argConst = constant(target->getBoundFunctionArgument(i));
callInfo.argv().infallibleAppend(argConst);
}
for (size_t i = 0; i < nativeCallInfo.argc(); i++)
callInfo.argv().infallibleAppend(nativeCallInfo.getArg(i));
// We only inline when it was not a super-call, so just set the newTarget
// to be the target function, per spec.
if (nativeCallInfo.constructing())
callInfo.setNewTarget(callInfo.fun());
if (!makeCall(scriptedTarget, callInfo))
return InliningStatus_Error;
return InliningStatus_Inlined;
}
IonBuilder::InliningStatus
IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
{

View File

@ -548,12 +548,12 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa
rval.set(MagicValue(JS_IS_CONSTRUCTING));
if (callee->is<JSFunction>()) {
JSFunction* fun = &callee->as<JSFunction>();
RootedFunction fun(cx, &callee->as<JSFunction>());
if (fun->isInterpreted() && fun->isConstructor()) {
JSScript* script = fun->getOrCreateScript(cx);
if (!script || !script->ensureHasTypes(cx))
return false;
if (script->isDerivedClassConstructor()) {
if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
} else {
JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject);

View File

@ -289,7 +289,6 @@ CallJSNativeConstructor(JSContext* cx, Native native, const CallArgs& args)
* - (new Object(Object)) returns the callee.
*/
MOZ_ASSERT_IF(native != js::proxy_Construct &&
native != js::CallOrConstructBoundFunction &&
native != js::IteratorConstructor &&
(!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
args.rval().isObject() && callee != &args.rval().toObject());

View File

@ -29,6 +29,7 @@
#include "builtin/Eval.h"
#include "builtin/Object.h"
#include "builtin/SelfHostingDefines.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/TokenStream.h"
#include "gc/Marking.h"
@ -93,7 +94,6 @@ static bool
AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
{
MOZ_ASSERT(!fun->isBuiltin());
MOZ_ASSERT(!fun->isBoundFunction(), "all bound functions are currently native (ergo builtin)");
for (; !iter.done(); ++iter) {
if (!iter.isFunctionFrame() || iter.isEvalFrame())
@ -477,11 +477,18 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
if (fun->hasResolvedLength())
return true;
uint16_t length;
if (!fun->getLength(cx, &length))
return false;
// Bound functions' length can have values up to MAX_SAFE_INTEGER,
// so they're handled differently from other functions.
if (fun->isBoundFunction()) {
MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
} else {
uint16_t length;
if (!fun->getLength(cx, &length))
return false;
v.setInt32(length);
v.setInt32(length);
}
} else {
if (fun->hasResolvedName())
return true;
@ -1287,74 +1294,63 @@ JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
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,
const Value* args, unsigned argslen)
bool
JSFunction::getLength(JSContext* cx, uint16_t* length)
{
RootedFunction self(cx, this);
/*
* Convert to a dictionary to set the BOUND_FUNCTION flag and increase
* the slot span to cover the arguments and additional slots for the 'this'
* value and arguments count.
*/
if (!self->toDictionaryMode(cx))
JS::RootedFunction self(cx, this);
MOZ_ASSERT(!self->isBoundFunction());
if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
return false;
if (!self->JSObject::setFlags(cx, BaseShape::BOUND_FUNCTION))
return false;
if (!self->setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
return false;
self->setSlot(JSSLOT_BOUND_FUNCTION_TARGET, ObjectValue(*target));
self->setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
self->setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
self->initSlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
self->setJitInfo(&jit::JitInfo_CallBoundFunction);
*length = self->hasScript() ? self->nonLazyScript()->funLength()
: (self->nargs() - self->hasRest());
return true;
}
static const js::Value&
BoundFunctionEnvironmentSlotValue(const JSFunction* fun, uint32_t slotIndex)
{
MOZ_ASSERT(fun->isBoundFunction());
MOZ_ASSERT(fun->environment()->is<CallObject>());
CallObject* callObject = &fun->environment()->as<CallObject>();
return callObject->getSlot(slotIndex);
}
JSObject*
JSFunction::getBoundFunctionTarget() const
{
MOZ_ASSERT(isBoundFunction());
return &getSlot(JSSLOT_BOUND_FUNCTION_TARGET).toObject();
js::Value targetVal = BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_TARGET);
MOZ_ASSERT(IsCallable(targetVal));
return &targetVal.toObject();
}
const js::Value&
JSFunction::getBoundFunctionThis() const
{
MOZ_ASSERT(isBoundFunction());
return BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_THIS);
}
return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
static ArrayObject*
GetBoundFunctionArguments(const JSFunction* boundFun)
{
js::Value argsVal = BoundFunctionEnvironmentSlotValue(boundFun, JSSLOT_BOUND_FUNCTION_ARGS);
return &argsVal.toObject().as<ArrayObject>();
}
const js::Value&
JSFunction::getBoundFunctionArgument(unsigned which) const
JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const
{
MOZ_ASSERT(isBoundFunction());
MOZ_ASSERT(which < getBoundFunctionArgumentCount());
return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which);
RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this));
RootedValue res(cx);
return boundArgs->getDenseElement(which);
}
size_t
JSFunction::getBoundFunctionArgumentCount() const
{
MOZ_ASSERT(isBoundFunction());
return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
return GetBoundFunctionArguments(this)->length();
}
/* static */ bool
@ -1511,9 +1507,14 @@ JSFunction::maybeRelazify(JSRuntime* rt)
return;
// To delazify self-hosted builtins we need the name of the function
// to clone. This name is stored in the first extended slot.
if (isSelfHostedBuiltin() && !isExtended())
// to clone. This name is stored in the first extended slot. Since
// that slot is sometimes also used for other purposes, make sure it
// contains a string.
if (isSelfHostedBuiltin() &&
(!isExtended() || !getExtendedSlot(LAZY_FUNCTION_NAME_SLOT).isString()))
{
return;
}
JSScript* script = nonLazyScript();
@ -1530,73 +1531,6 @@ JSFunction::maybeRelazify(JSRuntime* rt)
}
}
/* ES5 15.3.4.5.1 and 15.3.4.5.2. */
bool
js::CallOrConstructBoundFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction fun(cx, &args.callee().as<JSFunction>());
MOZ_ASSERT(fun->isBoundFunction());
/* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
unsigned boundArgsLen = fun->getBoundFunctionArgumentCount();
uint32_t argsLen = args.length();
if (argsLen + boundArgsLen > ARGS_LENGTH_MAX) {
ReportAllocationOverflow(cx);
return false;
}
/* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
RootedObject target(cx, fun->getBoundFunctionTarget());
/* 15.3.4.5.1 step 2. */
const Value& boundThis = fun->getBoundFunctionThis();
if (args.isConstructing()) {
ConstructArgs cargs(cx);
if (!cargs.init(argsLen + boundArgsLen))
return false;
/* 15.3.4.5.1, 15.3.4.5.2 step 4. */
for (uint32_t i = 0; i < boundArgsLen; i++)
cargs[i].set(fun->getBoundFunctionArgument(i));
for (uint32_t i = 0; i < argsLen; i++)
cargs[boundArgsLen + i].set(args[i]);
RootedValue targetv(cx, ObjectValue(*target));
/* ES6 9.4.1.2 step 5 */
RootedValue newTarget(cx);
if (&args.newTarget().toObject() == fun)
newTarget.set(targetv);
else
newTarget.set(args.newTarget());
return Construct(cx, targetv, cargs, newTarget, args.rval());
}
InvokeArgs invokeArgs(cx);
if (!invokeArgs.init(argsLen + boundArgsLen))
return false;
/* 15.3.4.5.1, 15.3.4.5.2 step 4. */
for (uint32_t i = 0; i < boundArgsLen; i++)
invokeArgs[i].set(fun->getBoundFunctionArgument(i));
for (uint32_t i = 0; i < argsLen; i++)
invokeArgs[boundArgsLen + i].set(args[i]);
/* 15.3.4.5.1, 15.3.4.5.2 step 5. */
invokeArgs.setCallee(ObjectValue(*target));
invokeArgs.setThis(boundThis);
if (!Invoke(cx, invokeArgs))
return false;
args.rval().set(invokeArgs.rval());
return true;
}
static bool
fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
{
@ -1611,132 +1545,6 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static JSFunction*
NewNativeFunctionWithGivenProto(JSContext* cx, Native native, unsigned nargs,
HandleAtom atom, HandleObject proto)
{
return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_FUN, nullptr, atom, proto,
AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto);
}
static JSFunction*
NewNativeConstructorWithGivenProto(JSContext* cx, Native native, unsigned nargs,
HandleAtom atom, HandleObject proto)
{
return NewFunctionWithProto(cx, native, nargs, JSFunction::NATIVE_CTOR, nullptr, atom, proto,
AllocKind::FUNCTION, GenericObject, NewFunctionGivenProto);
}
// ES6 draft rev32 19.2.3.2
bool
js::fun_bind(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
RootedValue thisv(cx, args.thisv());
// Step 2.
if (!IsCallable(thisv)) {
ReportIncompatibleMethod(cx, args, &JSFunction::class_);
return false;
}
// Step 3.
Value* boundArgs = nullptr;
unsigned argslen = 0;
if (args.length() > 1) {
boundArgs = args.array() + 1;
argslen = args.length() - 1;
}
RootedValue thisArg(cx, args.length() >= 1 ? args[0] : UndefinedValue());
RootedObject target(cx, &thisv.toObject());
// This is part of step 4, but we're delaying allocating the function object.
RootedObject proto(cx);
if (!GetPrototype(cx, target, &proto))
return false;
double length = 0.0;
// Try to avoid invoking the resolve hook.
if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedLength()) {
uint16_t len;
if (!target->as<JSFunction>().getLength(cx, &len))
return false;
length = Max(0.0, double(len) - argslen);
} else {
// Steps 5-6.
RootedId id(cx, NameToId(cx->names().length));
bool hasLength;
if (!HasOwnProperty(cx, target, id, &hasLength))
return false;
// Step 7-8.
if (hasLength) {
// a-b.
RootedValue targetLen(cx);
if (!GetProperty(cx, target, target, id, &targetLen))
return false;
// d.
if (targetLen.isNumber())
length = Max(0.0, JS::ToInteger(targetLen.toNumber()) - argslen);
}
}
RootedString name(cx, cx->names().empty);
if (target->is<JSFunction>() && !target->as<JSFunction>().hasResolvedName()) {
if (target->as<JSFunction>().atom())
name = target->as<JSFunction>().atom();
} else {
// Steps 11-12.
RootedValue targetName(cx);
if (!GetProperty(cx, target, target, cx->names().name, &targetName))
return false;
// Step 13.
if (targetName.isString())
name = targetName.toString();
}
// Step 14. Relevant bits from SetFunctionName.
StringBuffer sb(cx);
// Disabled for B2G failures.
// if (!sb.append("bound ") || !sb.append(name))
// return false;
if (!sb.append(name))
return false;
RootedAtom nameAtom(cx, sb.finishAtom());
if (!nameAtom)
return false;
// Step 4.
RootedFunction fun(cx, target->isConstructor() ?
NewNativeConstructorWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto) :
NewNativeFunctionWithGivenProto(cx, CallOrConstructBoundFunction, length, nameAtom, proto));
if (!fun)
return false;
if (!fun->initBoundFunction(cx, target, thisArg, boundArgs, argslen))
return false;
// Steps 9-10. Set length again, because NewNativeFunction/NewNativeConstructor
// sometimes truncates.
if (length != fun->nargs()) {
RootedValue lengthVal(cx, NumberValue(length));
if (!DefineProperty(cx, fun, cx->names().length, lengthVal, nullptr, nullptr,
JSPROP_READONLY))
{
return false;
}
}
// Step 15.
args.rval().setObject(*fun);
return true;
}
/*
* Report "malformed formal parameter" iff no illegal char or similar scanner
* error was already reported.
@ -1755,8 +1563,8 @@ const JSFunctionSpec js::function_methods[] = {
JS_FN(js_toString_str, fun_toString, 0,0),
JS_FN(js_apply_str, fun_apply, 2,0),
JS_FN(js_call_str, fun_call, 1,0),
JS_FN("bind", fun_bind, 1,0),
JS_FN("isGenerator", fun_isGenerator,0,0),
JS_SELF_HOSTED_FN("bind", "FunctionBind", 1,JSPROP_DEFINE_LATE),
JS_FS_END
};

View File

@ -28,6 +28,10 @@ typedef JSNative Native;
struct JSAtomState;
static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 2;
static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 3;
static const uint32_t JSSLOT_BOUND_FUNCTION_ARGS = 4;
class JSFunction : public js::NativeObject
{
public:
@ -48,7 +52,7 @@ class JSFunction : public js::NativeObject
INTERPRETED = 0x0001, /* function has a JSScript and environment. */
CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */
EXTENDED = 0x0004, /* structure is FunctionExtended */
/* 0x0008 unused */
BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */
EXPR_BODY = 0x0010, /* arrow function with expression body or
* expression closure: function(x) x*x */
HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
@ -186,6 +190,7 @@ class JSFunction : public js::NativeObject
bool isExprBody() const { return flags() & EXPR_BODY; }
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
bool isLambda() const { return flags() & LAMBDA; }
bool isBoundFunction() const { return flags() & BOUND_FUN; }
bool hasRest() const { return flags() & HAS_REST; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; }
@ -273,6 +278,11 @@ class JSFunction : public js::NativeObject
flags_ |= HAS_REST;
}
void setIsBoundFunction() {
MOZ_ASSERT(!isBoundFunction());
flags_ |= BOUND_FUN;
}
void setIsSelfHostedBuiltin() {
MOZ_ASSERT(isInterpreted());
MOZ_ASSERT(!isSelfHostedBuiltin());
@ -443,15 +453,7 @@ class JSFunction : public js::NativeObject
return u.i.s.script_;
}
bool getLength(JSContext* cx, uint16_t* length) {
JS::RootedFunction self(cx, this);
if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
return false;
*length = self->hasScript() ? self->nonLazyScript()->funLength()
: (self->nargs() - self->hasRest());
return true;
}
inline bool getLength(JSContext* cx, uint16_t* length);
js::LazyScript* lazyScript() const {
MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
@ -561,12 +563,9 @@ class JSFunction : public js::NativeObject
/* Bound function accessors. */
inline bool initBoundFunction(JSContext* cx, js::HandleObject target, js::HandleValue thisArg,
const js::Value* args, unsigned argslen);
JSObject* getBoundFunctionTarget() const;
const js::Value& getBoundFunctionThis() const;
const js::Value& getBoundFunctionArgument(unsigned which) const;
const js::Value& getBoundFunctionArgument(JSContext* cx, unsigned which) const;
size_t getBoundFunctionArgumentCount() const;
private:
@ -683,9 +682,6 @@ FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
extern bool
fun_toString(JSContext* cx, unsigned argc, Value* vp);
extern bool
fun_bind(JSContext* cx, unsigned argc, Value* vp);
/*
* Function extended with reserved slots for use by various kinds of functions.
* Most functions do not have these extensions, but enough do that efficient
@ -819,10 +815,8 @@ ReportIncompatibleMethod(JSContext* cx, CallReceiver call, const Class* clasp);
extern void
ReportIncompatible(JSContext* cx, CallReceiver call);
bool
CallOrConstructBoundFunction(JSContext*, unsigned, js::Value*);
extern const JSFunctionSpec function_methods[];
extern const JSFunctionSpec function_selfhosted_methods[];
extern bool
fun_apply(JSContext* cx, unsigned argc, Value* vp);

View File

@ -428,7 +428,7 @@ JSObject::nonProxyIsExtensible() const
inline bool
JSObject::isBoundFunction() const
{
return hasAllFlags(js::BaseShape::BOUND_FUNCTION);
return is<JSFunction>() && as<JSFunction>().isBoundFunction();
}
inline bool

View File

@ -3407,8 +3407,17 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle
}
gc::AllocKind allocKind = srcFun->getAllocKind();
RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
JSFunction::INTERPRETED, nullptr, nullptr,
uint16_t flags = srcFun->flags();
if (srcFun->isSelfHostedBuiltin()) {
// Functions in the self-hosting compartment are only extended in
// debug mode. For top-level functions, FUNCTION_EXTENDED gets used by
// the cloning algorithm. Do the same for inner functions here.
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
flags |= JSFunction::Flags::EXTENDED;
}
RootedAtom atom(cx, srcFun->displayAtom());
RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, srcFun->nargs(),
JSFunction::Flags(flags), nullptr, atom,
cloneProto, allocKind, TenuredObject));
if (!clone)
return nullptr;
@ -3420,9 +3429,6 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle
if (!cloneScript)
return nullptr;
clone->setArgCount(srcFun->nargs());
clone->setFlags(srcFun->flags());
clone->initAtom(srcFun->displayAtom());
if (!JSFunction::setTypeForScriptedFunction(cx, clone))
return nullptr;

View File

@ -4352,7 +4352,7 @@ static const JSFunctionSpec string_static_methods[] = {
JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1,0),
JS_SELF_HOSTED_FN("raw", "String_static_raw", 2,0),
JS_SELF_HOSTED_FN("raw", "String_static_raw", 1,0),
JS_SELF_HOSTED_FN("substring", "String_static_substring", 3,0),
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),

View File

@ -696,6 +696,7 @@ selfhosted.inputs = [
'builtin/Classes.js',
'builtin/Date.js',
'builtin/Error.js',
'builtin/Function.js',
'builtin/Generator.js',
'builtin/Intl.js',
'builtin/IntlData.js',

View File

@ -7244,7 +7244,7 @@ DebuggerObject_getBoundArguments(JSContext* cx, unsigned argc, Value* vp)
if (!boundArgs.resize(length))
return false;
for (size_t i = 0; i < length; i++) {
boundArgs[i].set(fun->getBoundFunctionArgument(i));
boundArgs[i].set(fun->getBoundFunctionArgument(cx, i));
if (!dbg->wrapDebuggeeValue(cx, boundArgs[i]))
return false;
}

View File

@ -24,6 +24,7 @@
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
#include "builtin/RegExp.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/SymbolObject.h"
#include "builtin/TypedObject.h"
#include "builtin/WeakMapObject.h"

View File

@ -304,7 +304,7 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
RootedFunction ctor(cx);
if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name,
/* nargs = */ !!derived,
/* nargs = */ 0,
proto, TenuredObject, &ctor))
{
return nullptr;
@ -346,7 +346,9 @@ RunState::maybeCreateThisForConstructor(JSContext* cx)
InvokeState& invoke = *asInvoke();
if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
RootedObject callee(cx, &invoke.args().callee());
if (script()->isDerivedClassConstructor()) {
if (callee->isBoundFunction()) {
invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
} else if (script()->isDerivedClassConstructor()) {
MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
} else {

View File

@ -275,7 +275,7 @@ intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp)
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin());
MOZ_ASSERT(args[1].isObject());
MOZ_ASSERT(args[1].isObjectOrNull());
// Normal .prototype properties aren't enumerable. But for this to clone
// correctly, it must be enumerable.
@ -307,6 +307,64 @@ intrinsic_MakeDefaultConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/*
* Used to mark bound functions as such and make them constructible if the
* target is.
* Also sets the name and correct length, both of which are more costly to
* do in JS.
*/
static bool
intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 4);
MOZ_ASSERT(IsCallable(args[1]));
MOZ_ASSERT(args[2].isNumber());
MOZ_ASSERT(args[3].isString());
RootedFunction bound(cx, &args[0].toObject().as<JSFunction>());
bound->setIsBoundFunction();
RootedObject targetObj(cx, &args[1].toObject());
MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj);
if (targetObj->isConstructor())
bound->setIsConstructor();
// 9.4.1.3 BoundFunctionCreate, steps 2-3,8.
RootedObject proto(cx);
GetPrototype(cx, targetObj, &proto);
if (bound->getProto() != proto) {
if (!SetPrototype(cx, bound, proto))
return false;
}
bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]);
MOZ_ASSERT(!bound->hasGuessedAtom());
RootedAtom name(cx, AtomizeString(cx, args[3].toString()));
if (!name)
return false;
bound->setAtom(name);
args.rval().setUndefined();
return true;
}
static bool
intrinsic_SetPrototype(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].isObject());
MOZ_ASSERT(args[1].isObjectOrNull());
RootedObject obj(cx, &args[0].toObject());
RootedObject proto(cx, args[1].toObjectOrNull());
if (!SetPrototype(cx, obj, proto))
return false;
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
@ -1220,6 +1278,26 @@ intrinsic_LocalTZA(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_ConstructFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
MOZ_ASSERT(args[1].toObject().is<ArrayObject>());
RootedArrayObject argsList(cx, &args[1].toObject().as<ArrayObject>());
uint32_t len = argsList->length();
ConstructArgs constructArgs(cx);
if (!constructArgs.init(len))
return false;
for (uint32_t index = 0; index < len; index++)
constructArgs[index].set(argsList->getDenseElement(index));
return Construct(cx, args[0], constructArgs, args.rval());
}
static bool
intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp)
{
@ -1445,7 +1523,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("std_Date_now", date_now, 0,0),
JS_FN("std_Date_valueOf", date_valueOf, 0,0),
JS_FN("std_Function_bind", fun_bind, 1,0),
JS_FN("std_Function_apply", fun_apply, 2,0),
JS_INLINABLE_FN("std_Math_floor", math_floor, 1,0, MathFloor),
@ -1466,6 +1543,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
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_setPrototypeOf", intrinsic_SetPrototype, 2,0),
JS_FN("std_Object_toString", obj_toString, 0,0),
JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,0),
@ -1512,15 +1590,17 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1,0),
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("MakeConstructible", intrinsic_MakeConstructible, 2,0),
JS_FN("_ConstructFunction", intrinsic_ConstructFunction, 2,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("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0),
JS_FN("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0),
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 4,0),
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0),
@ -2143,7 +2223,6 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
// JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
// aren't any.
MOZ_ASSERT(!sourceFun->isGenerator());
MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
MOZ_ASSERT(targetFun->isExtended());
MOZ_ASSERT(targetFun->isInterpretedLazy());
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
@ -2163,6 +2242,20 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
return false;
MOZ_ASSERT(!targetFun->isInterpretedLazy());
// ...rest args don't count as formal args, but are included in nargs. We don't,
// however, want to introduce a flag "has rest args" in the declaration of
// self-hosted functions, so we fix up the flag and the nargs value here.
// Since the target function might have been cloned and relazified before,
// this only happens if the target function isn't marked as having rest
// args.
MOZ_ASSERT(sourceFun->nargs() - sourceFun->hasRest() ==
targetFun->nargs() - targetFun->hasRest());
MOZ_ASSERT_IF(targetFun->hasRest(), sourceFun->hasRest());
if (sourceFun->hasRest() && !targetFun->hasRest()) {
targetFun->setHasRest();
targetFun->setArgCount(sourceFun->nargs());
}
// The target function might have been relazified after its flags changed.
targetFun->setFlags(targetFun->flags() | sourceFun->flags());
return true;

View File

@ -349,7 +349,7 @@ class BaseShape : public gc::TenuredCell
DELEGATE = 0x8,
NOT_EXTENSIBLE = 0x10,
INDEXED = 0x20,
BOUND_FUNCTION = 0x40,
/* (0x40 is unused) */
HAD_ELEMENTS_ACCESS = 0x80,
WATCHED = 0x100,
ITERATED_SINGLETON = 0x200,

View File

@ -252,7 +252,9 @@ InterpreterFrame::prologue(JSContext* cx)
return false;
if (isConstructing()) {
if (script->isDerivedClassConstructor()) {
if (fun()->isBoundFunction()) {
thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
} else if (script->isDerivedClassConstructor()) {
MOZ_ASSERT(callee().isClassConstructor());
thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
} else if (thisArgument().isPrimitive()) {