зеркало из 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;
|
var F = collatorCompareToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Step 1.b-d.
|
||||||
var bc = callFunction(std_Function_bind, F, this);
|
var bc = callFunction(FunctionBind, F, this);
|
||||||
internals.boundCompare = bc;
|
internals.boundCompare = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2036,7 +2036,7 @@ function Intl_NumberFormat_format_get() {
|
||||||
var F = numberFormatFormatToBind;
|
var F = numberFormatFormatToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Step 1.b-d.
|
||||||
var bf = callFunction(std_Function_bind, F, this);
|
var bf = callFunction(FunctionBind, F, this);
|
||||||
internals.boundFormat = bf;
|
internals.boundFormat = bf;
|
||||||
}
|
}
|
||||||
// Step 2.
|
// Step 2.
|
||||||
|
@ -2732,7 +2732,7 @@ function Intl_DateTimeFormat_format_get() {
|
||||||
var F = dateTimeFormatFormatToBind;
|
var F = dateTimeFormatFormatToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Step 1.b-d.
|
||||||
var bf = callFunction(std_Function_bind, F, this);
|
var bf = callFunction(FunctionBind, F, this);
|
||||||
internals.boundFormat = bf;
|
internals.boundFormat = bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2761,7 +2761,7 @@ function Intl_DateTimeFormat_formatToParts_get() {
|
||||||
var F = dateTimeFormatFormatToPartsToBind;
|
var F = dateTimeFormatFormatToPartsToBind;
|
||||||
|
|
||||||
// Step 1.b-d.
|
// Step 1.b-d.
|
||||||
var bf = callFunction(std_Function_bind, F, this);
|
var bf = callFunction(FunctionBind, F, this);
|
||||||
internals.boundFormatToParts = bf;
|
internals.boundFormatToParts = bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1091,14 +1091,19 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Define self-hosted functions after setting the intrinsics holder
|
* Define self-hosted functions on Object and Function after setting the
|
||||||
* (which is needed to define self-hosted functions)
|
* intrinsics holder (which is needed to define self-hosted functions).
|
||||||
*/
|
*/
|
||||||
if (!cx->runtime()->isSelfHostingGlobal(global)) {
|
if (!cx->runtime()->isSelfHostingGlobal(global)) {
|
||||||
if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
|
if (!JS_DefineFunctions(cx, ctor, object_static_methods, OnlyDefineLateProperties))
|
||||||
return false;
|
return false;
|
||||||
if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties))
|
if (!JS_DefineFunctions(cx, proto, object_methods, OnlyDefineLateProperties))
|
||||||
return false;
|
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 IS_UINT32(x) ((x) >>> 0 === (x))
|
||||||
#define MAX_NUMERIC_INDEX 0x1fffffffffffff // == Math.pow(2, 53) - 1
|
#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.
|
// Unforgeable versions of ARRAY.push(ELEMENT) and ARRAY.slice.
|
||||||
#define ARRAY_PUSH(ARRAY, ELEMENT) \
|
#define ARRAY_PUSH(ARRAY, ELEMENT) \
|
||||||
callFunction(std_Array_push, ARRAY, ELEMENT);
|
callFunction(std_Array_push, ARRAY, ELEMENT);
|
||||||
|
@ -39,6 +43,10 @@
|
||||||
// global. This slot is used only in debug build.
|
// global. This slot is used only in debug build.
|
||||||
#define HAS_SELFHOSTED_CANONICAL_NAME_SLOT 0
|
#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
|
// Stores the private WeakMap slot used for WeakSets
|
||||||
#define WEAKSET_MAP_SLOT 0
|
#define WEAKSET_MAP_SLOT 0
|
||||||
|
|
||||||
|
|
|
@ -231,7 +231,7 @@ const Class js::ScalarTypeDescr::class_ = {
|
||||||
|
|
||||||
const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
|
const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
|
||||||
JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
|
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"},
|
{"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,6 +78,6 @@ assertEq(tByteSize([1, 2, 3, 4, 5, 6, 7, 8]), s(112, 128));
|
||||||
|
|
||||||
// Various forms of functions.
|
// Various forms of functions.
|
||||||
assertEq(tByteSize(function () {}), s(32, 64));
|
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(() => 1), s(48, 80));
|
||||||
assertEq(tByteSize(Math.sin), s(32, 64));
|
assertEq(tByteSize(Math.sin), s(32, 64));
|
||||||
|
|
|
@ -1985,7 +1985,7 @@ CodeGenerator::visitLambda(LLambda* lir)
|
||||||
emitLambdaInit(output, scopeChain, info);
|
emitLambdaInit(output, scopeChain, info);
|
||||||
|
|
||||||
if (info.flags & JSFunction::EXTENDED) {
|
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");
|
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(0)));
|
||||||
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||||
|
|
|
@ -79,8 +79,6 @@
|
||||||
\
|
\
|
||||||
_(ObjectCreate) \
|
_(ObjectCreate) \
|
||||||
\
|
\
|
||||||
_(CallBoundFunction) \
|
|
||||||
\
|
|
||||||
_(SimdInt32x4) \
|
_(SimdInt32x4) \
|
||||||
_(SimdFloat32x4) \
|
_(SimdFloat32x4) \
|
||||||
_(SimdBool32x4) \
|
_(SimdBool32x4) \
|
||||||
|
|
|
@ -6270,6 +6270,9 @@ IonBuilder::createThis(JSFunction* target, MDefinition* callee, MDefinition* new
|
||||||
return magic;
|
return magic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target->isBoundFunction())
|
||||||
|
return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
||||||
|
|
||||||
if (target->isDerivedClassConstructor()) {
|
if (target->isDerivedClassConstructor()) {
|
||||||
MOZ_ASSERT(target->isClassConstructor());
|
MOZ_ASSERT(target->isClassConstructor());
|
||||||
return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
return constant(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
||||||
|
|
|
@ -201,10 +201,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||||
case InlinableNative::ObjectCreate:
|
case InlinableNative::ObjectCreate:
|
||||||
return inlineObjectCreate(callInfo);
|
return inlineObjectCreate(callInfo);
|
||||||
|
|
||||||
// Bound function.
|
|
||||||
case InlinableNative::CallBoundFunction:
|
|
||||||
return inlineBoundFunction(callInfo, target);
|
|
||||||
|
|
||||||
// SIMD natives.
|
// SIMD natives.
|
||||||
case InlinableNative::SimdInt32x4:
|
case InlinableNative::SimdInt32x4:
|
||||||
return inlineSimdInt32x4(callInfo, target->native());
|
return inlineSimdInt32x4(callInfo, target->native());
|
||||||
|
@ -2681,73 +2677,6 @@ IonBuilder::inlineAssertRecoveredOnBailout(CallInfo& callInfo)
|
||||||
return InliningStatus_Inlined;
|
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::InliningStatus
|
||||||
IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
|
IonBuilder::inlineAtomicsCompareExchange(CallInfo& callInfo)
|
||||||
{
|
{
|
||||||
|
|
|
@ -548,12 +548,12 @@ CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget, MutableHa
|
||||||
rval.set(MagicValue(JS_IS_CONSTRUCTING));
|
rval.set(MagicValue(JS_IS_CONSTRUCTING));
|
||||||
|
|
||||||
if (callee->is<JSFunction>()) {
|
if (callee->is<JSFunction>()) {
|
||||||
JSFunction* fun = &callee->as<JSFunction>();
|
RootedFunction fun(cx, &callee->as<JSFunction>());
|
||||||
if (fun->isInterpreted() && fun->isConstructor()) {
|
if (fun->isInterpreted() && fun->isConstructor()) {
|
||||||
JSScript* script = fun->getOrCreateScript(cx);
|
JSScript* script = fun->getOrCreateScript(cx);
|
||||||
if (!script || !script->ensureHasTypes(cx))
|
if (!script || !script->ensureHasTypes(cx))
|
||||||
return false;
|
return false;
|
||||||
if (script->isDerivedClassConstructor()) {
|
if (fun->isBoundFunction() || script->isDerivedClassConstructor()) {
|
||||||
rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
rval.set(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
||||||
} else {
|
} else {
|
||||||
JSObject* thisObj = CreateThisForFunction(cx, callee, newTarget, GenericObject);
|
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.
|
* - (new Object(Object)) returns the callee.
|
||||||
*/
|
*/
|
||||||
MOZ_ASSERT_IF(native != js::proxy_Construct &&
|
MOZ_ASSERT_IF(native != js::proxy_Construct &&
|
||||||
native != js::CallOrConstructBoundFunction &&
|
|
||||||
native != js::IteratorConstructor &&
|
native != js::IteratorConstructor &&
|
||||||
(!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
|
(!callee->is<JSFunction>() || callee->as<JSFunction>().native() != obj_construct),
|
||||||
args.rval().isObject() && callee != &args.rval().toObject());
|
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/Eval.h"
|
||||||
#include "builtin/Object.h"
|
#include "builtin/Object.h"
|
||||||
|
#include "builtin/SelfHostingDefines.h"
|
||||||
#include "frontend/BytecodeCompiler.h"
|
#include "frontend/BytecodeCompiler.h"
|
||||||
#include "frontend/TokenStream.h"
|
#include "frontend/TokenStream.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
|
@ -93,7 +94,6 @@ static bool
|
||||||
AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
|
AdvanceToActiveCallLinear(JSContext* cx, NonBuiltinScriptFrameIter& iter, HandleFunction fun)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!fun->isBuiltin());
|
MOZ_ASSERT(!fun->isBuiltin());
|
||||||
MOZ_ASSERT(!fun->isBoundFunction(), "all bound functions are currently native (ergo builtin)");
|
|
||||||
|
|
||||||
for (; !iter.done(); ++iter) {
|
for (; !iter.done(); ++iter) {
|
||||||
if (!iter.isFunctionFrame() || iter.isEvalFrame())
|
if (!iter.isFunctionFrame() || iter.isEvalFrame())
|
||||||
|
@ -477,11 +477,18 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
|
||||||
if (fun->hasResolvedLength())
|
if (fun->hasResolvedLength())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uint16_t length;
|
// Bound functions' length can have values up to MAX_SAFE_INTEGER,
|
||||||
if (!fun->getLength(cx, &length))
|
// so they're handled differently from other functions.
|
||||||
return false;
|
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 {
|
} else {
|
||||||
if (fun->hasResolvedName())
|
if (fun->hasResolvedName())
|
||||||
return true;
|
return true;
|
||||||
|
@ -1287,74 +1294,63 @@ JSFunction::infallibleIsDefaultClassConstructor(JSContext* cx) const
|
||||||
return isDefault;
|
return isDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t JSSLOT_BOUND_FUNCTION_TARGET = 0;
|
bool
|
||||||
static const uint32_t JSSLOT_BOUND_FUNCTION_THIS = 1;
|
JSFunction::getLength(JSContext* cx, uint16_t* length)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
RootedFunction self(cx, this);
|
JS::RootedFunction self(cx, this);
|
||||||
|
MOZ_ASSERT(!self->isBoundFunction());
|
||||||
/*
|
if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
|
||||||
* 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))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!self->JSObject::setFlags(cx, BaseShape::BOUND_FUNCTION))
|
*length = self->hasScript() ? self->nonLazyScript()->funLength()
|
||||||
return false;
|
: (self->nargs() - self->hasRest());
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return true;
|
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*
|
JSObject*
|
||||||
JSFunction::getBoundFunctionTarget() const
|
JSFunction::getBoundFunctionTarget() const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(isBoundFunction());
|
js::Value targetVal = BoundFunctionEnvironmentSlotValue(this, JSSLOT_BOUND_FUNCTION_TARGET);
|
||||||
|
MOZ_ASSERT(IsCallable(targetVal));
|
||||||
return &getSlot(JSSLOT_BOUND_FUNCTION_TARGET).toObject();
|
return &targetVal.toObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
const js::Value&
|
const js::Value&
|
||||||
JSFunction::getBoundFunctionThis() const
|
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&
|
const js::Value&
|
||||||
JSFunction::getBoundFunctionArgument(unsigned which) const
|
JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(isBoundFunction());
|
|
||||||
MOZ_ASSERT(which < getBoundFunctionArgumentCount());
|
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
|
size_t
|
||||||
JSFunction::getBoundFunctionArgumentCount() const
|
JSFunction::getBoundFunctionArgumentCount() const
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(isBoundFunction());
|
return GetBoundFunctionArguments(this)->length();
|
||||||
|
|
||||||
return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ bool
|
/* static */ bool
|
||||||
|
@ -1511,9 +1507,14 @@ JSFunction::maybeRelazify(JSRuntime* rt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// To delazify self-hosted builtins we need the name of the function
|
// To delazify self-hosted builtins we need the name of the function
|
||||||
// to clone. This name is stored in the first extended slot.
|
// to clone. This name is stored in the first extended slot. Since
|
||||||
if (isSelfHostedBuiltin() && !isExtended())
|
// 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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
JSScript* script = nonLazyScript();
|
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
|
static bool
|
||||||
fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
|
fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
@ -1611,132 +1545,6 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
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
|
* Report "malformed formal parameter" iff no illegal char or similar scanner
|
||||||
* error was already reported.
|
* 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_toString_str, fun_toString, 0,0),
|
||||||
JS_FN(js_apply_str, fun_apply, 2,0),
|
JS_FN(js_apply_str, fun_apply, 2,0),
|
||||||
JS_FN(js_call_str, fun_call, 1,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_FN("isGenerator", fun_isGenerator,0,0),
|
||||||
|
JS_SELF_HOSTED_FN("bind", "FunctionBind", 1,JSPROP_DEFINE_LATE),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,10 @@ typedef JSNative Native;
|
||||||
|
|
||||||
struct JSAtomState;
|
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
|
class JSFunction : public js::NativeObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -48,7 +52,7 @@ class JSFunction : public js::NativeObject
|
||||||
INTERPRETED = 0x0001, /* function has a JSScript and environment. */
|
INTERPRETED = 0x0001, /* function has a JSScript and environment. */
|
||||||
CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */
|
CONSTRUCTOR = 0x0002, /* function that can be called as a constructor */
|
||||||
EXTENDED = 0x0004, /* structure is FunctionExtended */
|
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
|
EXPR_BODY = 0x0010, /* arrow function with expression body or
|
||||||
* expression closure: function(x) x*x */
|
* expression closure: function(x) x*x */
|
||||||
HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
|
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 isExprBody() const { return flags() & EXPR_BODY; }
|
||||||
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
|
bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
|
||||||
bool isLambda() const { return flags() & LAMBDA; }
|
bool isLambda() const { return flags() & LAMBDA; }
|
||||||
|
bool isBoundFunction() const { return flags() & BOUND_FUN; }
|
||||||
bool hasRest() const { return flags() & HAS_REST; }
|
bool hasRest() const { return flags() & HAS_REST; }
|
||||||
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
|
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
|
||||||
bool hasScript() const { return flags() & INTERPRETED; }
|
bool hasScript() const { return flags() & INTERPRETED; }
|
||||||
|
@ -273,6 +278,11 @@ class JSFunction : public js::NativeObject
|
||||||
flags_ |= HAS_REST;
|
flags_ |= HAS_REST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setIsBoundFunction() {
|
||||||
|
MOZ_ASSERT(!isBoundFunction());
|
||||||
|
flags_ |= BOUND_FUN;
|
||||||
|
}
|
||||||
|
|
||||||
void setIsSelfHostedBuiltin() {
|
void setIsSelfHostedBuiltin() {
|
||||||
MOZ_ASSERT(isInterpreted());
|
MOZ_ASSERT(isInterpreted());
|
||||||
MOZ_ASSERT(!isSelfHostedBuiltin());
|
MOZ_ASSERT(!isSelfHostedBuiltin());
|
||||||
|
@ -443,15 +453,7 @@ class JSFunction : public js::NativeObject
|
||||||
return u.i.s.script_;
|
return u.i.s.script_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getLength(JSContext* cx, uint16_t* length) {
|
inline 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
js::LazyScript* lazyScript() const {
|
js::LazyScript* lazyScript() const {
|
||||||
MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
|
MOZ_ASSERT(isInterpretedLazy() && u.i.s.lazy_);
|
||||||
|
@ -561,12 +563,9 @@ class JSFunction : public js::NativeObject
|
||||||
|
|
||||||
/* Bound function accessors. */
|
/* Bound function accessors. */
|
||||||
|
|
||||||
inline bool initBoundFunction(JSContext* cx, js::HandleObject target, js::HandleValue thisArg,
|
|
||||||
const js::Value* args, unsigned argslen);
|
|
||||||
|
|
||||||
JSObject* getBoundFunctionTarget() const;
|
JSObject* getBoundFunctionTarget() const;
|
||||||
const js::Value& getBoundFunctionThis() 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;
|
size_t getBoundFunctionArgumentCount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -683,9 +682,6 @@ FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
|
||||||
extern bool
|
extern bool
|
||||||
fun_toString(JSContext* cx, unsigned argc, Value* vp);
|
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.
|
* Function extended with reserved slots for use by various kinds of functions.
|
||||||
* Most functions do not have these extensions, but enough do that efficient
|
* 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
|
extern void
|
||||||
ReportIncompatible(JSContext* cx, CallReceiver call);
|
ReportIncompatible(JSContext* cx, CallReceiver call);
|
||||||
|
|
||||||
bool
|
|
||||||
CallOrConstructBoundFunction(JSContext*, unsigned, js::Value*);
|
|
||||||
|
|
||||||
extern const JSFunctionSpec function_methods[];
|
extern const JSFunctionSpec function_methods[];
|
||||||
|
extern const JSFunctionSpec function_selfhosted_methods[];
|
||||||
|
|
||||||
extern bool
|
extern bool
|
||||||
fun_apply(JSContext* cx, unsigned argc, Value* vp);
|
fun_apply(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
|
@ -428,7 +428,7 @@ JSObject::nonProxyIsExtensible() const
|
||||||
inline bool
|
inline bool
|
||||||
JSObject::isBoundFunction() const
|
JSObject::isBoundFunction() const
|
||||||
{
|
{
|
||||||
return hasAllFlags(js::BaseShape::BOUND_FUNCTION);
|
return is<JSFunction>() && as<JSFunction>().isBoundFunction();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
@ -3407,8 +3407,17 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
gc::AllocKind allocKind = srcFun->getAllocKind();
|
gc::AllocKind allocKind = srcFun->getAllocKind();
|
||||||
RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
|
uint16_t flags = srcFun->flags();
|
||||||
JSFunction::INTERPRETED, nullptr, nullptr,
|
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));
|
cloneProto, allocKind, TenuredObject));
|
||||||
if (!clone)
|
if (!clone)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -3420,9 +3429,6 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, Handle
|
||||||
if (!cloneScript)
|
if (!cloneScript)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
clone->setArgCount(srcFun->nargs());
|
|
||||||
clone->setFlags(srcFun->flags());
|
|
||||||
clone->initAtom(srcFun->displayAtom());
|
|
||||||
if (!JSFunction::setTypeForScriptedFunction(cx, clone))
|
if (!JSFunction::setTypeForScriptedFunction(cx, clone))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -4352,7 +4352,7 @@ static const JSFunctionSpec string_static_methods[] = {
|
||||||
JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
|
JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
|
||||||
|
|
||||||
JS_SELF_HOSTED_FN("fromCodePoint", "String_static_fromCodePoint", 1,0),
|
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("substring", "String_static_substring", 3,0),
|
||||||
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
|
JS_SELF_HOSTED_FN("substr", "String_static_substr", 3,0),
|
||||||
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),
|
JS_SELF_HOSTED_FN("slice", "String_static_slice", 3,0),
|
||||||
|
|
|
@ -696,6 +696,7 @@ selfhosted.inputs = [
|
||||||
'builtin/Classes.js',
|
'builtin/Classes.js',
|
||||||
'builtin/Date.js',
|
'builtin/Date.js',
|
||||||
'builtin/Error.js',
|
'builtin/Error.js',
|
||||||
|
'builtin/Function.js',
|
||||||
'builtin/Generator.js',
|
'builtin/Generator.js',
|
||||||
'builtin/Intl.js',
|
'builtin/Intl.js',
|
||||||
'builtin/IntlData.js',
|
'builtin/IntlData.js',
|
||||||
|
|
|
@ -7244,7 +7244,7 @@ DebuggerObject_getBoundArguments(JSContext* cx, unsigned argc, Value* vp)
|
||||||
if (!boundArgs.resize(length))
|
if (!boundArgs.resize(length))
|
||||||
return false;
|
return false;
|
||||||
for (size_t i = 0; i < length; i++) {
|
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]))
|
if (!dbg->wrapDebuggeeValue(cx, boundArgs[i]))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "builtin/ModuleObject.h"
|
#include "builtin/ModuleObject.h"
|
||||||
#include "builtin/Object.h"
|
#include "builtin/Object.h"
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
|
#include "builtin/SelfHostingDefines.h"
|
||||||
#include "builtin/SymbolObject.h"
|
#include "builtin/SymbolObject.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
#include "builtin/WeakMapObject.h"
|
#include "builtin/WeakMapObject.h"
|
||||||
|
|
|
@ -304,7 +304,7 @@ MakeDefaultConstructor(JSContext* cx, JSOp op, JSAtom* atom, HandleObject proto)
|
||||||
|
|
||||||
RootedFunction ctor(cx);
|
RootedFunction ctor(cx);
|
||||||
if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name,
|
if (!cx->runtime()->createLazySelfHostedFunctionClone(cx, selfHostedName, name,
|
||||||
/* nargs = */ !!derived,
|
/* nargs = */ 0,
|
||||||
proto, TenuredObject, &ctor))
|
proto, TenuredObject, &ctor))
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -346,7 +346,9 @@ RunState::maybeCreateThisForConstructor(JSContext* cx)
|
||||||
InvokeState& invoke = *asInvoke();
|
InvokeState& invoke = *asInvoke();
|
||||||
if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
|
if (invoke.constructing() && invoke.args().thisv().isPrimitive()) {
|
||||||
RootedObject callee(cx, &invoke.args().callee());
|
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());
|
MOZ_ASSERT(callee->as<JSFunction>().isClassConstructor());
|
||||||
invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
invoke.args().setThis(MagicValue(JS_UNINITIALIZED_LEXICAL));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -275,7 +275,7 @@ intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp)
|
||||||
MOZ_ASSERT(args[0].isObject());
|
MOZ_ASSERT(args[0].isObject());
|
||||||
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
|
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
|
||||||
MOZ_ASSERT(args[0].toObject().as<JSFunction>().isSelfHostedBuiltin());
|
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
|
// Normal .prototype properties aren't enumerable. But for this to clone
|
||||||
// correctly, it must be enumerable.
|
// correctly, it must be enumerable.
|
||||||
|
@ -307,6 +307,64 @@ intrinsic_MakeDefaultConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
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
|
* Used to decompile values in the nearest non-builtin stack frame, falling
|
||||||
* back to decompiling in the current frame. Helpful for printing higher-order
|
* 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;
|
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
|
static bool
|
||||||
intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp)
|
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_now", date_now, 0,0),
|
||||||
JS_FN("std_Date_valueOf", date_valueOf, 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_FN("std_Function_apply", fun_apply, 2,0),
|
||||||
|
|
||||||
JS_INLINABLE_FN("std_Math_floor", math_floor, 1,0, MathFloor),
|
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_getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
|
||||||
JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
|
JS_FN("std_Object_getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor, 2,0),
|
||||||
JS_FN("std_Object_hasOwnProperty", obj_hasOwnProperty, 1,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_Object_toString", obj_toString, 0,0),
|
||||||
|
|
||||||
JS_FN("std_Reflect_getPrototypeOf", Reflect_getPrototypeOf, 1,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_FN("ToPropertyKey", intrinsic_ToPropertyKey, 1,0),
|
||||||
JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1,0, IntrinsicIsCallable),
|
JS_INLINABLE_FN("IsCallable", intrinsic_IsCallable, 1,0, IntrinsicIsCallable),
|
||||||
JS_FN("IsConstructor", intrinsic_IsConstructor, 1,0),
|
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("ThrowRangeError", intrinsic_ThrowRangeError, 4,0),
|
||||||
JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0),
|
JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0),
|
||||||
JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0),
|
JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0),
|
||||||
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,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("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0),
|
||||||
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
|
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
|
||||||
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
|
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
|
||||||
|
JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 4,0),
|
||||||
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
|
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
|
||||||
JS_FN("LocalTZA", intrinsic_LocalTZA, 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
|
// JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
|
||||||
// aren't any.
|
// aren't any.
|
||||||
MOZ_ASSERT(!sourceFun->isGenerator());
|
MOZ_ASSERT(!sourceFun->isGenerator());
|
||||||
MOZ_ASSERT(sourceFun->nargs() == targetFun->nargs());
|
|
||||||
MOZ_ASSERT(targetFun->isExtended());
|
MOZ_ASSERT(targetFun->isExtended());
|
||||||
MOZ_ASSERT(targetFun->isInterpretedLazy());
|
MOZ_ASSERT(targetFun->isInterpretedLazy());
|
||||||
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
|
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
|
||||||
|
@ -2163,6 +2242,20 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
|
||||||
return false;
|
return false;
|
||||||
MOZ_ASSERT(!targetFun->isInterpretedLazy());
|
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.
|
// The target function might have been relazified after its flags changed.
|
||||||
targetFun->setFlags(targetFun->flags() | sourceFun->flags());
|
targetFun->setFlags(targetFun->flags() | sourceFun->flags());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -349,7 +349,7 @@ class BaseShape : public gc::TenuredCell
|
||||||
DELEGATE = 0x8,
|
DELEGATE = 0x8,
|
||||||
NOT_EXTENSIBLE = 0x10,
|
NOT_EXTENSIBLE = 0x10,
|
||||||
INDEXED = 0x20,
|
INDEXED = 0x20,
|
||||||
BOUND_FUNCTION = 0x40,
|
/* (0x40 is unused) */
|
||||||
HAD_ELEMENTS_ACCESS = 0x80,
|
HAD_ELEMENTS_ACCESS = 0x80,
|
||||||
WATCHED = 0x100,
|
WATCHED = 0x100,
|
||||||
ITERATED_SINGLETON = 0x200,
|
ITERATED_SINGLETON = 0x200,
|
||||||
|
|
|
@ -252,7 +252,9 @@ InterpreterFrame::prologue(JSContext* cx)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (isConstructing()) {
|
if (isConstructing()) {
|
||||||
if (script->isDerivedClassConstructor()) {
|
if (fun()->isBoundFunction()) {
|
||||||
|
thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
|
||||||
|
} else if (script->isDerivedClassConstructor()) {
|
||||||
MOZ_ASSERT(callee().isClassConstructor());
|
MOZ_ASSERT(callee().isClassConstructor());
|
||||||
thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
|
thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
|
||||||
} else if (thisArgument().isPrimitive()) {
|
} else if (thisArgument().isPrimitive()) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче