зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1000780 - Part 5: Self-host Function.prototype.bind. r=jandem
--HG-- extra : rebase_source : 52301e2d50a038040c5d86e8b7b052acd71a9660
This commit is contained in:
Родитель
851c8ab557
Коммит
1a6dfabe3f
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -79,8 +79,6 @@
|
|||
\
|
||||
_(ObjectCreate) \
|
||||
\
|
||||
_(CallBoundFunction) \
|
||||
\
|
||||
_(SimdInt32x4) \
|
||||
_(SimdFloat32x4) \
|
||||
_(SimdBool32x4) \
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
294
js/src/jsfun.cpp
294
js/src/jsfun.cpp
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче