summaryrefslogtreecommitdiffstats
path: root/js/src/vm/JSFunction.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/vm/JSFunction.cpp2527
1 files changed, 2527 insertions, 0 deletions
diff --git a/js/src/vm/JSFunction.cpp b/js/src/vm/JSFunction.cpp
new file mode 100644
index 0000000000..03b6aa6c9a
--- /dev/null
+++ b/js/src/vm/JSFunction.cpp
@@ -0,0 +1,2527 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * 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/. */
+
+/*
+ * JS function support.
+ */
+
+#include "vm/JSFunction-inl.h"
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Range.h"
+#include "mozilla/Utf8.h"
+
+#include <algorithm>
+#include <iterator>
+#include <string.h>
+
+#include "jsapi.h"
+#include "jstypes.h"
+
+#include "builtin/Array.h"
+#include "builtin/BigInt.h"
+#include "builtin/Eval.h"
+#include "builtin/Object.h"
+#include "builtin/SelfHostingDefines.h"
+#include "builtin/Symbol.h"
+#include "frontend/BytecodeCompilation.h"
+#include "frontend/BytecodeCompiler.h"
+#include "frontend/TokenStream.h"
+#include "gc/Marking.h"
+#include "gc/Policy.h"
+#include "jit/InlinableNatives.h"
+#include "jit/Ion.h"
+#include "js/CallNonGenericMethod.h"
+#include "js/CompileOptions.h"
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
+#include "js/friend/StackLimits.h" // js::CheckRecursionLimit
+#include "js/PropertySpec.h"
+#include "js/Proxy.h"
+#include "js/SourceText.h"
+#include "js/StableStringChars.h"
+#include "js/Wrapper.h"
+#include "util/StringBuffer.h"
+#include "util/Text.h"
+#include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
+#include "vm/BooleanObject.h"
+#include "vm/FunctionFlags.h" // js::FunctionFlags
+#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/JSAtom.h"
+#include "vm/JSContext.h"
+#include "vm/JSObject.h"
+#include "vm/JSScript.h"
+#include "vm/NumberObject.h"
+#include "vm/PlainObject.h" // js::PlainObject
+#include "vm/SelfHosting.h"
+#include "vm/Shape.h"
+#include "vm/SharedImmutableStringsCache.h"
+#include "vm/StringObject.h"
+#include "vm/WrapperObject.h"
+#include "vm/Xdr.h"
+#include "wasm/AsmJS.h"
+
+#include "debugger/DebugAPI-inl.h"
+#include "vm/FrameIter-inl.h" // js::FrameIter::unaliasedForEachActual
+#include "vm/Interpreter-inl.h"
+#include "vm/JSScript-inl.h"
+#include "vm/Stack-inl.h"
+
+using namespace js;
+
+using mozilla::CheckedInt;
+using mozilla::Maybe;
+using mozilla::Some;
+using mozilla::Utf8Unit;
+
+using JS::AutoStableStringChars;
+using JS::CompileOptions;
+using JS::SourceOwnership;
+using JS::SourceText;
+
+static bool fun_enumerate(JSContext* cx, HandleObject obj) {
+ MOZ_ASSERT(obj->is<JSFunction>());
+
+ RootedId id(cx);
+ bool found;
+
+ if (!obj->isBoundFunction() && !obj->as<JSFunction>().isArrow()) {
+ id = NameToId(cx->names().prototype);
+ if (!HasOwnProperty(cx, obj, id, &found)) {
+ return false;
+ }
+ }
+
+ if (!obj->as<JSFunction>().hasResolvedLength()) {
+ id = NameToId(cx->names().length);
+ if (!HasOwnProperty(cx, obj, id, &found)) {
+ return false;
+ }
+ }
+
+ if (!obj->as<JSFunction>().hasResolvedName()) {
+ id = NameToId(cx->names().name);
+ if (!HasOwnProperty(cx, obj, id, &found)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IsFunction(HandleValue v) {
+ return v.isObject() && v.toObject().is<JSFunction>();
+}
+
+static bool AdvanceToActiveCallLinear(JSContext* cx,
+ NonBuiltinScriptFrameIter& iter,
+ HandleFunction fun) {
+ MOZ_ASSERT(!fun->isBuiltin());
+
+ for (; !iter.done(); ++iter) {
+ if (!iter.isFunctionFrame()) {
+ continue;
+ }
+ if (iter.matchCallee(cx, fun)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void js::ThrowTypeErrorBehavior(JSContext* cx) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_THROW_TYPE_ERROR);
+}
+
+static bool IsSloppyNormalFunction(JSFunction* fun) {
+ // FunctionDeclaration or FunctionExpression in sloppy mode.
+ if (fun->kind() == FunctionFlags::NormalFunction) {
+ if (fun->isBuiltin() || fun->isBoundFunction()) {
+ return false;
+ }
+
+ if (fun->isGenerator() || fun->isAsync()) {
+ return false;
+ }
+
+ MOZ_ASSERT(fun->isInterpreted());
+ return !fun->strict();
+ }
+
+ // Or asm.js function in sloppy mode.
+ if (fun->kind() == FunctionFlags::AsmJS) {
+ return !IsAsmJSStrictModeModuleOrFunction(fun);
+ }
+
+ return false;
+}
+
+// Beware: this function can be invoked on *any* function! That includes
+// natives, strict mode functions, bound functions, arrow functions,
+// self-hosted functions and constructors, asm.js functions, functions with
+// destructuring arguments and/or a rest argument, and probably a few more I
+// forgot. Turn back and save yourself while you still can. It's too late for
+// me.
+static bool ArgumentsRestrictions(JSContext* cx, HandleFunction fun) {
+ // Throw unless the function is a sloppy, normal function.
+ // TODO (bug 1057208): ensure semantics are correct for all possible
+ // pairings of callee/caller.
+ if (!IsSloppyNormalFunction(fun)) {
+ ThrowTypeErrorBehavior(cx);
+ return false;
+ }
+
+ return true;
+}
+
+bool ArgumentsGetterImpl(JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(IsFunction(args.thisv()));
+
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+ if (!ArgumentsRestrictions(cx, fun)) {
+ return false;
+ }
+
+ // Return null if this function wasn't found on the stack.
+ NonBuiltinScriptFrameIter iter(cx);
+ if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
+ args.rval().setNull();
+ return true;
+ }
+
+ Rooted<ArgumentsObject*> argsobj(cx,
+ ArgumentsObject::createUnexpected(cx, iter));
+ if (!argsobj) {
+ return false;
+ }
+
+#ifndef JS_CODEGEN_NONE
+ // Disabling compiling of this script in IonMonkey. IonMonkey doesn't
+ // guarantee |f.arguments| can be fully recovered, so we try to mitigate
+ // observing this behavior by detecting its use early.
+ JSScript* script = iter.script();
+ jit::ForbidCompilation(cx, script);
+#endif
+
+ args.rval().setObject(*argsobj);
+ return true;
+}
+
+static bool ArgumentsGetter(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsFunction, ArgumentsGetterImpl>(cx, args);
+}
+
+bool ArgumentsSetterImpl(JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(IsFunction(args.thisv()));
+
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+ if (!ArgumentsRestrictions(cx, fun)) {
+ return false;
+ }
+
+ // If the function passes the gauntlet, return |undefined|.
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool ArgumentsSetter(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsFunction, ArgumentsSetterImpl>(cx, args);
+}
+
+// Beware: this function can be invoked on *any* function! That includes
+// natives, strict mode functions, bound functions, arrow functions,
+// self-hosted functions and constructors, asm.js functions, functions with
+// destructuring arguments and/or a rest argument, and probably a few more I
+// forgot. Turn back and save yourself while you still can. It's too late for
+// me.
+static bool CallerRestrictions(JSContext* cx, HandleFunction fun) {
+ // Throw unless the function is a sloppy, normal function.
+ // TODO (bug 1057208): ensure semantics are correct for all possible
+ // pairings of callee/caller.
+ if (!IsSloppyNormalFunction(fun)) {
+ ThrowTypeErrorBehavior(cx);
+ return false;
+ }
+
+ return true;
+}
+
+bool CallerGetterImpl(JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(IsFunction(args.thisv()));
+
+ // Beware! This function can be invoked on *any* function! It can't
+ // assume it'll never be invoked on natives, strict mode functions, bound
+ // functions, or anything else that ordinarily has immutable .caller
+ // defined with [[ThrowTypeError]].
+ RootedFunction fun(cx, &args.thisv().toObject().as<JSFunction>());
+ if (!CallerRestrictions(cx, fun)) {
+ return false;
+ }
+
+ // Also return null if this function wasn't found on the stack.
+ NonBuiltinScriptFrameIter iter(cx);
+ if (!AdvanceToActiveCallLinear(cx, iter, fun)) {
+ args.rval().setNull();
+ return true;
+ }
+
+ ++iter;
+ while (!iter.done() && iter.isEvalFrame()) {
+ ++iter;
+ }
+
+ if (iter.done() || !iter.isFunctionFrame()) {
+ args.rval().setNull();
+ return true;
+ }
+
+ RootedObject caller(cx, iter.callee(cx));
+ if (!cx->compartment()->wrap(cx, &caller)) {
+ return false;
+ }
+
+ // Censor the caller if we don't have full access to it. If we do, but the
+ // caller is a function with strict mode code, throw a TypeError per ES5.
+ // If we pass these checks, we can return the computed caller.
+ {
+ JSObject* callerObj = CheckedUnwrapStatic(caller);
+ if (!callerObj) {
+ args.rval().setNull();
+ return true;
+ }
+
+ if (JS_IsDeadWrapper(callerObj)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_DEAD_OBJECT);
+ return false;
+ }
+
+ JSFunction* callerFun = &callerObj->as<JSFunction>();
+ MOZ_ASSERT(!callerFun->isBuiltin(),
+ "non-builtin iterator returned a builtin?");
+
+ if (callerFun->strict() || callerFun->isAsync() ||
+ callerFun->isGenerator()) {
+ args.rval().setNull();
+ return true;
+ }
+ }
+
+ args.rval().setObject(*caller);
+ return true;
+}
+
+static bool CallerGetter(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsFunction, CallerGetterImpl>(cx, args);
+}
+
+bool CallerSetterImpl(JSContext* cx, const CallArgs& args) {
+ MOZ_ASSERT(IsFunction(args.thisv()));
+
+ // We just have to return |undefined|, but first we call CallerGetterImpl
+ // because we need the same strict-mode and security checks.
+
+ if (!CallerGetterImpl(cx, args)) {
+ return false;
+ }
+
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool CallerSetter(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CallNonGenericMethod<IsFunction, CallerSetterImpl>(cx, args);
+}
+
+static const JSPropertySpec function_properties[] = {
+ JS_PSGS("arguments", ArgumentsGetter, ArgumentsSetter, 0),
+ JS_PSGS("caller", CallerGetter, CallerSetter, 0), JS_PS_END};
+
+static bool ResolveInterpretedFunctionPrototype(JSContext* cx,
+ HandleFunction fun,
+ HandleId id) {
+ MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative());
+ MOZ_ASSERT(id == NameToId(cx->names().prototype));
+
+ // Assert that fun is not a compiler-created function object, which
+ // must never leak to script or embedding code and then be mutated.
+ // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above.
+ MOZ_ASSERT(!IsInternalFunctionObject(*fun));
+ MOZ_ASSERT(!fun->isBoundFunction());
+
+ // Make the prototype object an instance of Object with the same parent as
+ // the function object itself, unless the function is an ES6 generator. In
+ // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
+ // the GeneratorObjectPrototype singleton.
+ bool isGenerator = fun->isGenerator();
+ Rooted<GlobalObject*> global(cx, &fun->global());
+ RootedObject objProto(cx);
+ if (isGenerator && fun->isAsync()) {
+ objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
+ } else if (isGenerator) {
+ objProto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global);
+ } else {
+ objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
+ }
+ if (!objProto) {
+ return false;
+ }
+
+ RootedPlainObject proto(
+ cx, NewTenuredObjectWithGivenProto<PlainObject>(cx, objProto));
+ if (!proto) {
+ return false;
+ }
+
+ // Per ES5 13.2 the prototype's .constructor property is configurable,
+ // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft,
+ // section 15.19.3, the .prototype of a generator function does not link
+ // back with a .constructor.
+ if (!isGenerator) {
+ RootedValue objVal(cx, ObjectValue(*fun));
+ if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0)) {
+ return false;
+ }
+ }
+
+ // Per ES5 15.3.5.2 a user-defined function's .prototype property is
+ // initially non-configurable, non-enumerable, and writable.
+ RootedValue protoVal(cx, ObjectValue(*proto));
+ return DefineDataProperty(cx, fun, id, protoVal,
+ JSPROP_PERMANENT | JSPROP_RESOLVING);
+}
+
+bool JSFunction::needsPrototypeProperty() {
+ /*
+ * Built-in functions do not have a .prototype property per ECMA-262,
+ * or (Object.prototype, Function.prototype, etc.) have that property
+ * created eagerly.
+ *
+ * ES5 15.3.4.5: bound functions don't have a prototype property. The
+ * isBuiltin() test covers this case because bound functions are self-hosted
+ * (scripted) built-ins.
+ *
+ * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors.
+ * Generators are not constructors, but they have a .prototype property
+ * anyway, according to errata to ES6. See bug 1191486.
+ *
+ * Thus all of the following don't get a .prototype property:
+ * - Methods (that are not class-constructors or generators)
+ * - Arrow functions
+ * - Function.prototype
+ * - Async functions
+ */
+ return !isBuiltin() && (isConstructor() || isGenerator());
+}
+
+bool JSFunction::hasNonConfigurablePrototypeDataProperty() {
+ if (!isBuiltin()) {
+ return needsPrototypeProperty();
+ }
+
+ if (isSelfHostedBuiltin()) {
+ // Self-hosted constructors other than bound functions have a
+ // non-configurable .prototype data property. See the MakeConstructible
+ // intrinsic.
+ if (!isConstructor() || isBoundFunction()) {
+ return false;
+ }
+#ifdef DEBUG
+ PropertyName* prototypeName =
+ runtimeFromMainThread()->commonNames->prototype;
+ Shape* shape = lookupPure(prototypeName);
+ MOZ_ASSERT(shape);
+ MOZ_ASSERT(shape->isDataProperty());
+ MOZ_ASSERT(!shape->configurable());
+#endif
+ return true;
+ }
+
+ if (!isConstructor()) {
+ // We probably don't have a .prototype property. Avoid the lookup below.
+ return false;
+ }
+
+ PropertyName* prototypeName = runtimeFromMainThread()->commonNames->prototype;
+ Shape* shape = lookupPure(prototypeName);
+ return shape && shape->isDataProperty() && !shape->configurable();
+}
+
+static bool fun_mayResolve(const JSAtomState& names, jsid id, JSObject*) {
+ if (!JSID_IS_ATOM(id)) {
+ return false;
+ }
+
+ JSAtom* atom = JSID_TO_ATOM(id);
+ return atom == names.prototype || atom == names.length || atom == names.name;
+}
+
+static bool fun_resolve(JSContext* cx, HandleObject obj, HandleId id,
+ bool* resolvedp) {
+ if (!JSID_IS_ATOM(id)) {
+ return true;
+ }
+
+ RootedFunction fun(cx, &obj->as<JSFunction>());
+
+ if (JSID_IS_ATOM(id, cx->names().prototype)) {
+ if (!fun->needsPrototypeProperty()) {
+ return true;
+ }
+
+ if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) {
+ return false;
+ }
+
+ *resolvedp = true;
+ return true;
+ }
+
+ bool isLength = JSID_IS_ATOM(id, cx->names().length);
+ if (isLength || JSID_IS_ATOM(id, cx->names().name)) {
+ MOZ_ASSERT(!IsInternalFunctionObject(*obj));
+
+ RootedValue v(cx);
+
+ // Since f.length and f.name are configurable, they could be resolved
+ // and then deleted:
+ // function f(x) {}
+ // assertEq(f.length, 1);
+ // delete f.length;
+ // assertEq(f.name, "f");
+ // delete f.name;
+ // Afterwards, asking for f.length or f.name again will cause this
+ // resolve hook to run again. Defining the property again the second
+ // time through would be a bug.
+ // assertEq(f.length, 0); // gets Function.prototype.length!
+ // assertEq(f.name, ""); // gets Function.prototype.name!
+ // We use the RESOLVED_LENGTH and RESOLVED_NAME flags as a hack to prevent
+ // this bug.
+ if (isLength) {
+ if (fun->hasResolvedLength()) {
+ return true;
+ }
+
+ if (!JSFunction::getUnresolvedLength(cx, fun, &v)) {
+ return false;
+ }
+ } else {
+ if (fun->hasResolvedName()) {
+ return true;
+ }
+
+ if (!JSFunction::getUnresolvedName(cx, fun, &v)) {
+ return false;
+ }
+ }
+
+ if (!NativeDefineDataProperty(cx, fun, id, v,
+ JSPROP_READONLY | JSPROP_RESOLVING)) {
+ return false;
+ }
+
+ if (isLength) {
+ fun->setResolvedLength();
+ } else {
+ fun->setResolvedName();
+ }
+
+ *resolvedp = true;
+ return true;
+ }
+
+ return true;
+}
+
+template <XDRMode mode>
+XDRResult js::XDRInterpretedFunction(XDRState<mode>* xdr,
+ HandleScope enclosingScope,
+ HandleScriptSourceObject sourceObject,
+ MutableHandleFunction objp) {
+ enum FirstWordFlag {
+ HasAtom = 1 << 0,
+ IsGenerator = 1 << 1,
+ IsAsync = 1 << 2,
+ IsLazy = 1 << 3,
+ };
+
+ /* NB: Keep this in sync with CloneInnerInterpretedFunction. */
+
+ JSContext* cx = xdr->cx();
+
+ uint8_t xdrFlags = 0; /* bitmask of FirstWordFlag */
+
+ uint16_t nargs = 0;
+ uint16_t flags = 0;
+
+ RootedFunction fun(cx);
+ RootedAtom atom(cx);
+ RootedScript script(cx);
+ Rooted<BaseScript*> lazy(cx);
+
+ if (mode == XDR_ENCODE) {
+ fun = objp;
+ if (!fun->isInterpreted() || fun->isBoundFunction()) {
+ return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
+ }
+
+ if (fun->isGenerator()) {
+ xdrFlags |= IsGenerator;
+ }
+ if (fun->isAsync()) {
+ xdrFlags |= IsAsync;
+ }
+
+ if (fun->hasBytecode()) {
+ // Encode the script.
+ script = fun->nonLazyScript();
+ } else {
+ // Encode a lazy script.
+ xdrFlags |= IsLazy;
+ lazy = fun->baseScript();
+ }
+
+ if (fun->displayAtom()) {
+ xdrFlags |= HasAtom;
+ }
+
+ nargs = fun->nargs();
+ flags = FunctionFlags::clearMutableflags(fun->flags()).toRaw();
+
+ atom = fun->displayAtom();
+ }
+
+ // Everything added below can substituted by the non-lazy-script version of
+ // this function later.
+ js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
+
+ MOZ_TRY(xdr->codeUint8(&xdrFlags));
+
+ MOZ_TRY(xdr->codeUint16(&nargs));
+ MOZ_TRY(xdr->codeUint16(&flags));
+
+ if (xdrFlags & HasAtom) {
+ MOZ_TRY(XDRAtom(xdr, &atom));
+ }
+
+ if (mode == XDR_DECODE) {
+ GeneratorKind generatorKind = (xdrFlags & IsGenerator)
+ ? GeneratorKind::Generator
+ : GeneratorKind::NotGenerator;
+ FunctionAsyncKind asyncKind = (xdrFlags & IsAsync)
+ ? FunctionAsyncKind::AsyncFunction
+ : FunctionAsyncKind::SyncFunction;
+
+ RootedObject proto(cx);
+ if (!GetFunctionPrototype(cx, generatorKind, asyncKind, &proto)) {
+ return xdr->fail(JS::TranscodeResult_Throw);
+ }
+
+ gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
+ if (flags & FunctionFlags::EXTENDED) {
+ allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+ }
+
+ // Sanity check the flags. We should have cleared the mutable flags already
+ // and we do not support self-hosted-lazy, bound or wasm functions.
+ constexpr uint16_t UnsupportedFlags =
+ FunctionFlags::MUTABLE_FLAGS | FunctionFlags::SELFHOSTLAZY |
+ FunctionFlags::BOUND_FUN | FunctionFlags::WASM_JIT_ENTRY;
+ if ((flags & UnsupportedFlags) != 0) {
+ return xdr->fail(JS::TranscodeResult_Failure_BadDecode);
+ }
+
+ fun = NewFunctionWithProto(cx, nullptr, nargs, FunctionFlags(flags),
+ nullptr, atom, proto, allocKind, TenuredObject);
+ if (!fun) {
+ return xdr->fail(JS::TranscodeResult_Throw);
+ }
+ objp.set(fun);
+ }
+
+ if (xdrFlags & IsLazy) {
+ MOZ_TRY(XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy));
+ } else {
+ MOZ_TRY(XDRScript(xdr, enclosingScope, sourceObject, fun, &script));
+ }
+
+ // Verify marker at end of function to detect buffer trunction.
+ MOZ_TRY(xdr->codeMarker(0x9E35CA1F));
+
+ return Ok();
+}
+
+template XDRResult js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*,
+ HandleScope,
+ HandleScriptSourceObject,
+ MutableHandleFunction);
+
+template XDRResult js::XDRInterpretedFunction(XDRState<XDR_DECODE>*,
+ HandleScope,
+ HandleScriptSourceObject,
+ MutableHandleFunction);
+
+/* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */
+static bool fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() < 1) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ /* Step 1. */
+ HandleValue func = args.thisv();
+
+ // Primitives are non-callable and will always return false from
+ // OrdinaryHasInstance.
+ if (!func.isObject()) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ RootedObject obj(cx, &func.toObject());
+
+ /* Step 2. */
+ bool result;
+ if (!OrdinaryHasInstance(cx, obj, args[0], &result)) {
+ return false;
+ }
+
+ args.rval().setBoolean(result);
+ return true;
+}
+
+/*
+ * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance
+ */
+bool JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v,
+ bool* bp) {
+ AssertHeapIsIdle();
+ cx->check(objArg, v);
+
+ RootedObject obj(cx, objArg);
+
+ /* Step 1. */
+ if (!obj->isCallable()) {
+ *bp = false;
+ return true;
+ }
+
+ /* Step 2. */
+ if (obj->is<JSFunction>() && obj->isBoundFunction()) {
+ /* Steps 2a-b. */
+ if (!CheckRecursionLimit(cx)) {
+ return false;
+ }
+ obj = obj->as<JSFunction>().getBoundFunctionTarget();
+ return InstanceofOperator(cx, obj, v, bp);
+ }
+
+ /* Step 3. */
+ if (!v.isObject()) {
+ *bp = false;
+ return true;
+ }
+
+ /* Step 4. */
+ RootedValue pval(cx);
+ if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval)) {
+ return false;
+ }
+
+ /* Step 5. */
+ if (pval.isPrimitive()) {
+ /*
+ * Throw a runtime error if instanceof is called on a function that
+ * has a non-object as its .prototype value.
+ */
+ RootedValue val(cx, ObjectValue(*obj));
+ ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, val, nullptr);
+ return false;
+ }
+
+ /* Step 6. */
+ RootedObject pobj(cx, &pval.toObject());
+ bool isPrototype;
+ if (!IsPrototypeOf(cx, pobj, &v.toObject(), &isPrototype)) {
+ return false;
+ }
+ *bp = isPrototype;
+ return true;
+}
+
+inline void JSFunction::trace(JSTracer* trc) {
+ if (isExtended()) {
+ TraceRange(trc, std::size(toExtended()->extendedSlots),
+ (GCPtrValue*)toExtended()->extendedSlots, "nativeReserved");
+ }
+
+ TraceNullableEdge(trc, &atom_, "atom");
+
+ if (isInterpreted()) {
+ // Functions can be be marked as interpreted despite having no script
+ // yet at some points when parsing, and can be lazy with no lazy script
+ // for self-hosted code.
+ if (isIncomplete()) {
+ MOZ_ASSERT(u.scripted.s.script_ == nullptr);
+ } else if (hasBaseScript()) {
+ BaseScript* script = u.scripted.s.script_;
+ TraceManuallyBarrieredEdge(trc, &script, "script");
+ // Self-hosted scripts are shared with workers but are never
+ // relocated. Skip unnecessary writes to prevent the possible data race.
+ if (u.scripted.s.script_ != script) {
+ u.scripted.s.script_ = script;
+ }
+ }
+ // NOTE: The u.scripted.s.selfHostedLazy_ does not point to GC things.
+
+ if (u.scripted.env_) {
+ TraceManuallyBarrieredEdge(trc, &u.scripted.env_, "fun_environment");
+ }
+ }
+}
+
+static void fun_trace(JSTracer* trc, JSObject* obj) {
+ obj->as<JSFunction>().trace(trc);
+}
+
+static JSObject* CreateFunctionConstructor(JSContext* cx, JSProtoKey key) {
+ Rooted<GlobalObject*> global(cx, cx->global());
+ RootedObject functionProto(
+ cx, &global->getPrototype(JSProto_Function).toObject());
+
+ RootedObject functionCtor(
+ cx, NewFunctionWithProto(
+ cx, Function, 1, FunctionFlags::NATIVE_CTOR, nullptr,
+ HandlePropertyName(cx->names().Function), functionProto,
+ gc::AllocKind::FUNCTION, TenuredObject));
+ if (!functionCtor) {
+ return nullptr;
+ }
+
+ return functionCtor;
+}
+
+static bool FunctionPrototype(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setUndefined();
+ return true;
+}
+
+static JSObject* CreateFunctionPrototype(JSContext* cx, JSProtoKey key) {
+ Rooted<GlobalObject*> self(cx, cx->global());
+
+ RootedObject objectProto(cx, &self->getPrototype(JSProto_Object).toObject());
+
+ return NewFunctionWithProto(
+ cx, FunctionPrototype, 0, FunctionFlags::NATIVE_FUN, nullptr,
+ HandlePropertyName(cx->names().empty), objectProto,
+ gc::AllocKind::FUNCTION, TenuredObject);
+}
+
+JSString* js::FunctionToStringCache::lookup(BaseScript* script) const {
+ for (size_t i = 0; i < NumEntries; i++) {
+ if (entries_[i].script == script) {
+ return entries_[i].string;
+ }
+ }
+ return nullptr;
+}
+
+void js::FunctionToStringCache::put(BaseScript* script, JSString* string) {
+ for (size_t i = NumEntries - 1; i > 0; i--) {
+ entries_[i] = entries_[i - 1];
+ }
+
+ entries_[0].set(script, string);
+}
+
+JSString* js::FunctionToString(JSContext* cx, HandleFunction fun,
+ bool isToSource) {
+ if (IsAsmJSModule(fun)) {
+ return AsmJSModuleToString(cx, fun, isToSource);
+ }
+ if (IsAsmJSFunction(fun)) {
+ return AsmJSFunctionToString(cx, fun);
+ }
+
+ // Self-hosted built-ins should not expose their source code.
+ bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
+
+ // If we're in toSource mode, put parentheses around lambda functions so
+ // that eval returns lambda, not function statement.
+ bool addParentheses =
+ haveSource && isToSource && (fun->isLambda() && !fun->isArrow());
+
+ if (haveSource) {
+ if (!ScriptSource::loadSource(cx, fun->baseScript()->scriptSource(),
+ &haveSource)) {
+ return nullptr;
+ }
+ }
+
+ // Fast path for the common case, to avoid StringBuffer overhead.
+ if (!addParentheses && haveSource) {
+ FunctionToStringCache& cache = cx->zone()->functionToStringCache();
+ if (JSString* str = cache.lookup(fun->baseScript())) {
+ return str;
+ }
+
+ BaseScript* script = fun->baseScript();
+ size_t start = script->toStringStart();
+ size_t end = script->toStringEnd();
+ JSString* str =
+ (end - start <= ScriptSource::SourceDeflateLimit)
+ ? script->scriptSource()->substring(cx, start, end)
+ : script->scriptSource()->substringDontDeflate(cx, start, end);
+ if (!str) {
+ return nullptr;
+ }
+
+ cache.put(fun->baseScript(), str);
+ return str;
+ }
+
+ JSStringBuilder out(cx);
+ if (addParentheses) {
+ if (!out.append('(')) {
+ return nullptr;
+ }
+ }
+
+ if (haveSource) {
+ if (!fun->baseScript()->appendSourceDataForToString(cx, out)) {
+ return nullptr;
+ }
+ } else if (!isToSource) {
+ // For the toString() output the source representation must match
+ // NativeFunction when no source text is available.
+ //
+ // NativeFunction:
+ // function PropertyName[~Yield,~Await]opt (
+ // FormalParameters[~Yield,~Await] ) { [native code] }
+ //
+ // Additionally, if |fun| is a well-known intrinsic object and is not
+ // identified as an anonymous function, the portion of the returned
+ // string that would be matched by IdentifierName must be the initial
+ // value of the name property of |fun|.
+
+ auto hasGetterOrSetterPrefix = [](JSAtom* name) {
+ auto hasGetterOrSetterPrefix = [](const auto* chars) {
+ return (chars[0] == 'g' || chars[0] == 's') && chars[1] == 'e' &&
+ chars[2] == 't' && chars[3] == ' ';
+ };
+
+ JS::AutoCheckCannotGC nogc;
+ return name->length() >= 4 &&
+ (name->hasLatin1Chars()
+ ? hasGetterOrSetterPrefix(name->latin1Chars(nogc))
+ : hasGetterOrSetterPrefix(name->twoByteChars(nogc)));
+ };
+
+ if (!out.append("function")) {
+ return nullptr;
+ }
+
+ // We don't want to fully parse the function's name here because of
+ // performance reasons, so only append the name if we're confident it
+ // can be matched as the 'PropertyName' grammar production.
+ if (fun->explicitName() && !fun->isBoundFunction() &&
+ (fun->kind() == FunctionFlags::NormalFunction ||
+ fun->kind() == FunctionFlags::ClassConstructor)) {
+ if (!out.append(' ')) {
+ return nullptr;
+ }
+
+ // Built-in getters or setters are classified as normal
+ // functions, strip any leading "get " or "set " if present.
+ JSAtom* name = fun->explicitName();
+ size_t offset = hasGetterOrSetterPrefix(name) ? 4 : 0;
+ if (!out.appendSubstring(name, offset, name->length() - offset)) {
+ return nullptr;
+ }
+ }
+
+ if (!out.append("() {\n [native code]\n}")) {
+ return nullptr;
+ }
+ } else {
+ if (fun->isAsync()) {
+ if (!out.append("async ")) {
+ return nullptr;
+ }
+ }
+
+ if (!fun->isArrow()) {
+ if (!out.append("function")) {
+ return nullptr;
+ }
+
+ if (fun->isGenerator()) {
+ if (!out.append('*')) {
+ return nullptr;
+ }
+ }
+ }
+
+ if (fun->explicitName()) {
+ if (!out.append(' ')) {
+ return nullptr;
+ }
+
+ if (fun->isBoundFunction()) {
+ JSLinearString* boundName = JSFunction::getBoundFunctionName(cx, fun);
+ if (!boundName || !out.append(boundName)) {
+ return nullptr;
+ }
+ } else {
+ if (!out.append(fun->explicitName())) {
+ return nullptr;
+ }
+ }
+ }
+
+ if (!out.append("() {\n [native code]\n}")) {
+ return nullptr;
+ }
+ }
+
+ if (addParentheses) {
+ if (!out.append(')')) {
+ return nullptr;
+ }
+ }
+
+ return out.finishString();
+}
+
+JSString* fun_toStringHelper(JSContext* cx, HandleObject obj, bool isToSource) {
+ if (!obj->is<JSFunction>()) {
+ if (JSFunToStringOp op = obj->getOpsFunToString()) {
+ return op(cx, obj, isToSource);
+ }
+
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_INCOMPATIBLE_PROTO, js_Function_str,
+ js_toString_str, "object");
+ return nullptr;
+ }
+
+ return FunctionToString(cx, obj.as<JSFunction>(), isToSource);
+}
+
+bool js::fun_toString(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(IsFunctionObject(args.calleev()));
+
+ RootedObject obj(cx, ToObject(cx, args.thisv()));
+ if (!obj) {
+ return false;
+ }
+
+ JSString* str = fun_toStringHelper(cx, obj, /* isToSource = */ false);
+ if (!str) {
+ return false;
+ }
+
+ args.rval().setString(str);
+ return true;
+}
+
+static bool fun_toSource(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(IsFunctionObject(args.calleev()));
+
+ RootedObject obj(cx, ToObject(cx, args.thisv()));
+ if (!obj) {
+ return false;
+ }
+
+ RootedString str(cx);
+ if (obj->isCallable()) {
+ str = fun_toStringHelper(cx, obj, /* isToSource = */ true);
+ } else {
+ str = ObjectToSource(cx, obj);
+ }
+ if (!str) {
+ return false;
+ }
+
+ args.rval().setString(str);
+ return true;
+}
+
+bool js::fun_call(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ HandleValue func = args.thisv();
+
+ // We don't need to do this -- Call would do it for us -- but the error
+ // message is *much* better if we do this here. (Without this,
+ // JSDVG_SEARCH_STACK tries to decompile |func| as if it were |this| in
+ // the scripted caller's frame -- so for example
+ //
+ // Function.prototype.call.call({});
+ //
+ // would identify |{}| as |this| as being the result of evaluating
+ // |Function.prototype.call| and would conclude, "Function.prototype.call
+ // is not a function". Grotesque.)
+ if (!IsCallable(func)) {
+ ReportIncompatibleMethod(cx, args, &JSFunction::class_);
+ return false;
+ }
+
+ size_t argCount = args.length();
+ if (argCount > 0) {
+ argCount--; // strip off provided |this|
+ }
+
+ InvokeArgs iargs(cx);
+ if (!iargs.init(cx, argCount)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < argCount; i++) {
+ iargs[i].set(args[i + 1]);
+ }
+
+ return Call(cx, func, args.get(0), iargs, args.rval());
+}
+
+// ES5 15.3.4.3
+bool js::fun_apply(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Step 1.
+ //
+ // Note that we must check callability here, not at actual call time,
+ // because extracting argument values from the provided arraylike might
+ // have side effects or throw an exception.
+ HandleValue fval = args.thisv();
+ if (!IsCallable(fval)) {
+ ReportIncompatibleMethod(cx, args, &JSFunction::class_);
+ return false;
+ }
+
+ // Step 2.
+ if (args.length() < 2 || args[1].isNullOrUndefined()) {
+ return fun_call(cx, (args.length() > 0) ? 1 : 0, vp);
+ }
+
+ InvokeArgs args2(cx);
+
+ // A JS_OPTIMIZED_ARGUMENTS magic value means that 'arguments' flows into
+ // this apply call from a scripted caller and, as an optimization, we've
+ // avoided creating it since apply can simply pull the argument values from
+ // the calling frame (which we must do now).
+ if (args[1].isMagic(JS_OPTIMIZED_ARGUMENTS)) {
+ // Step 3-6.
+ ScriptFrameIter iter(cx);
+ MOZ_ASSERT(iter.numActualArgs() <= ARGS_LENGTH_MAX);
+ if (!args2.init(cx, iter.numActualArgs())) {
+ return false;
+ }
+
+ // Steps 7-8.
+ iter.unaliasedForEachActual(cx, CopyTo(args2.array()));
+ } else {
+ // Step 3.
+ if (!args[1].isObject()) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_BAD_APPLY_ARGS, js_apply_str);
+ return false;
+ }
+
+ // Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
+ // original version of ES5).
+ RootedObject aobj(cx, &args[1].toObject());
+ uint32_t length;
+ if (!GetLengthProperty(cx, aobj, &length)) {
+ return false;
+ }
+
+ // Step 6.
+ if (!args2.init(cx, length)) {
+ return false;
+ }
+
+ MOZ_ASSERT(length <= ARGS_LENGTH_MAX);
+
+ // Steps 7-8.
+ if (!GetElements(cx, aobj, length, args2.array())) {
+ return false;
+ }
+ }
+
+ // Step 9.
+ return Call(cx, fval, args[0], args2, args.rval());
+}
+
+static const JSFunctionSpec function_methods[] = {
+ JS_FN(js_toSource_str, fun_toSource, 0, 0),
+ 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_SELF_HOSTED_FN("bind", "FunctionBind", 2, 0),
+ JS_SYM_FN(hasInstance, fun_symbolHasInstance, 1,
+ JSPROP_READONLY | JSPROP_PERMANENT),
+ JS_FS_END};
+
+static const JSClassOps JSFunctionClassOps = {
+ nullptr, // addProperty
+ nullptr, // delProperty
+ fun_enumerate, // enumerate
+ nullptr, // newEnumerate
+ fun_resolve, // resolve
+ fun_mayResolve, // mayResolve
+ nullptr, // finalize
+ nullptr, // call
+ nullptr, // hasInstance
+ nullptr, // construct
+ fun_trace, // trace
+};
+
+static const ClassSpec JSFunctionClassSpec = {
+ CreateFunctionConstructor, CreateFunctionPrototype, nullptr, nullptr,
+ function_methods, function_properties};
+
+const JSClass JSFunction::class_ = {js_Function_str,
+ JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
+ &JSFunctionClassOps, &JSFunctionClassSpec};
+
+const JSClass* const js::FunctionClassPtr = &JSFunction::class_;
+
+bool JSFunction::isDerivedClassConstructor() const {
+ bool derived = hasBaseScript() && baseScript()->isDerivedClassConstructor();
+ MOZ_ASSERT_IF(derived, isClassConstructor());
+ return derived;
+}
+
+bool JSFunction::isFieldInitializer() const {
+ bool derived = hasBaseScript() && baseScript()->isFieldInitializer();
+ MOZ_ASSERT_IF(derived, isMethod());
+ return derived;
+}
+
+/* static */
+bool JSFunction::getLength(JSContext* cx, HandleFunction fun,
+ uint16_t* length) {
+ MOZ_ASSERT(!fun->isBoundFunction());
+
+ if (fun->isNative()) {
+ *length = fun->nargs();
+ return true;
+ }
+
+ JSScript* script = getOrCreateScript(cx, fun);
+ if (!script) {
+ return false;
+ }
+
+ *length = script->funLength();
+ return true;
+}
+
+/* static */
+bool JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun,
+ MutableHandleValue v) {
+ MOZ_ASSERT(!IsInternalFunctionObject(*fun));
+ MOZ_ASSERT(!fun->hasResolvedLength());
+
+ // Bound functions' length can have values up to MAX_SAFE_INTEGER, so
+ // they're handled differently from other functions.
+ if (fun->isBoundFunction()) {
+ constexpr auto lengthSlot = FunctionExtended::BOUND_FUNCTION_LENGTH_SLOT;
+ MOZ_ASSERT(fun->getExtendedSlot(lengthSlot).isNumber());
+ v.set(fun->getExtendedSlot(lengthSlot));
+ return true;
+ }
+
+ uint16_t length;
+ if (!JSFunction::getLength(cx, fun, &length)) {
+ return false;
+ }
+
+ v.setInt32(length);
+ return true;
+}
+
+JSAtom* JSFunction::infallibleGetUnresolvedName(JSContext* cx) {
+ MOZ_ASSERT(!IsInternalFunctionObject(*this));
+ MOZ_ASSERT(!hasResolvedName());
+
+ if (JSAtom* name = explicitOrInferredName()) {
+ return name;
+ }
+
+ return cx->names().empty;
+}
+
+/* static */
+bool JSFunction::getUnresolvedName(JSContext* cx, HandleFunction fun,
+ MutableHandleValue v) {
+ if (fun->isBoundFunction()) {
+ JSLinearString* name = JSFunction::getBoundFunctionName(cx, fun);
+ if (!name) {
+ return false;
+ }
+
+ v.setString(name);
+ return true;
+ }
+
+ v.setString(fun->infallibleGetUnresolvedName(cx));
+ return true;
+}
+
+/* static */
+JSLinearString* JSFunction::getBoundFunctionName(JSContext* cx,
+ HandleFunction fun) {
+ MOZ_ASSERT(fun->isBoundFunction());
+ JSAtom* name = fun->explicitName();
+
+ // Bound functions are never unnamed.
+ MOZ_ASSERT(name);
+
+ // If the bound function prefix is present, return the name as is.
+ if (fun->hasBoundFunctionNamePrefix()) {
+ return name;
+ }
+
+ // Otherwise return "bound " * (number of bound function targets) + name.
+ size_t boundTargets = 0;
+ for (JSFunction* boundFn = fun; boundFn->isBoundFunction();) {
+ boundTargets++;
+
+ JSObject* target = boundFn->getBoundFunctionTarget();
+ if (!target->is<JSFunction>()) {
+ break;
+ }
+ boundFn = &target->as<JSFunction>();
+ }
+
+ // |function /*unnamed*/ (){...}.bind()| is a common case, handle it here.
+ if (name->empty() && boundTargets == 1) {
+ return cx->names().boundWithSpace;
+ }
+
+ static constexpr char boundWithSpaceChars[] = "bound ";
+ static constexpr size_t boundWithSpaceCharsLength =
+ js_strlen(boundWithSpaceChars);
+ MOZ_ASSERT(
+ StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
+
+ JSStringBuilder sb(cx);
+ if (name->hasTwoByteChars() && !sb.ensureTwoByteChars()) {
+ return nullptr;
+ }
+
+ CheckedInt<size_t> len(boundTargets);
+ len *= boundWithSpaceCharsLength;
+ len += name->length();
+ if (!len.isValid()) {
+ ReportAllocationOverflow(cx);
+ return nullptr;
+ }
+ if (!sb.reserve(len.value())) {
+ return nullptr;
+ }
+
+ while (boundTargets--) {
+ sb.infallibleAppend(boundWithSpaceChars, boundWithSpaceCharsLength);
+ }
+ sb.infallibleAppendSubstring(name, 0, name->length());
+
+ return sb.finishString();
+}
+
+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 {
+ js::Value targetVal =
+ BoundFunctionEnvironmentSlotValue(this, BoundFunctionEnvTargetSlot);
+ MOZ_ASSERT(IsCallable(targetVal));
+ return &targetVal.toObject();
+}
+
+const js::Value& JSFunction::getBoundFunctionThis() const {
+ return BoundFunctionEnvironmentSlotValue(this, BoundFunctionEnvThisSlot);
+}
+
+static ArrayObject* GetBoundFunctionArguments(const JSFunction* boundFun) {
+ js::Value argsVal =
+ BoundFunctionEnvironmentSlotValue(boundFun, BoundFunctionEnvArgsSlot);
+ return &argsVal.toObject().as<ArrayObject>();
+}
+
+const js::Value& JSFunction::getBoundFunctionArgument(unsigned which) const {
+ MOZ_ASSERT(which < getBoundFunctionArgumentCount());
+ return GetBoundFunctionArguments(this)->getDenseElement(which);
+}
+
+size_t JSFunction::getBoundFunctionArgumentCount() const {
+ return GetBoundFunctionArguments(this)->length();
+}
+
+static JSAtom* AppendBoundFunctionPrefix(JSContext* cx, JSString* str) {
+ static constexpr char boundWithSpaceChars[] = "bound ";
+ MOZ_ASSERT(
+ StringEqualsAscii(cx->names().boundWithSpace, boundWithSpaceChars));
+
+ StringBuffer sb(cx);
+ if (!sb.append(boundWithSpaceChars) || !sb.append(str)) {
+ return nullptr;
+ }
+ return sb.finishAtom();
+}
+
+/* static */
+bool JSFunction::finishBoundFunctionInit(JSContext* cx, HandleFunction bound,
+ HandleObject targetObj,
+ int32_t argCount) {
+ bound->setIsBoundFunction();
+ MOZ_ASSERT(bound->getBoundFunctionTarget() == targetObj);
+
+ // 9.4.1.3 BoundFunctionCreate, steps 1, 3-5, 8-12 (Already performed).
+
+ // 9.4.1.3 BoundFunctionCreate, step 6.
+ if (targetObj->isConstructor()) {
+ bound->setIsConstructor();
+ }
+
+ // 9.4.1.3 BoundFunctionCreate, step 2.
+ RootedObject proto(cx);
+ if (!GetPrototype(cx, targetObj, &proto)) {
+ return false;
+ }
+
+ // 9.4.1.3 BoundFunctionCreate, step 7.
+ if (bound->staticPrototype() != proto) {
+ if (!SetPrototype(cx, bound, proto)) {
+ return false;
+ }
+ }
+
+ double length = 0.0;
+
+ // Try to avoid invoking the resolve hook.
+ if (targetObj->is<JSFunction>() &&
+ !targetObj->as<JSFunction>().hasResolvedLength()) {
+ RootedValue targetLength(cx);
+ if (!JSFunction::getUnresolvedLength(cx, targetObj.as<JSFunction>(),
+ &targetLength)) {
+ return false;
+ }
+
+ length = std::max(0.0, targetLength.toNumber() - argCount);
+ } else {
+ // 19.2.3.2 Function.prototype.bind, step 5.
+ bool hasLength;
+ RootedId idRoot(cx, NameToId(cx->names().length));
+ if (!HasOwnProperty(cx, targetObj, idRoot, &hasLength)) {
+ return false;
+ }
+
+ // 19.2.3.2 Function.prototype.bind, step 6.
+ if (hasLength) {
+ RootedValue targetLength(cx);
+ if (!GetProperty(cx, targetObj, targetObj, idRoot, &targetLength)) {
+ return false;
+ }
+
+ if (targetLength.isNumber()) {
+ length =
+ std::max(0.0, JS::ToInteger(targetLength.toNumber()) - argCount);
+ }
+ }
+
+ // 19.2.3.2 Function.prototype.bind, step 7 (implicit).
+ }
+
+ // 19.2.3.2 Function.prototype.bind, step 8.
+ bound->setExtendedSlot(FunctionExtended::BOUND_FUNCTION_LENGTH_SLOT,
+ NumberValue(length));
+
+ MOZ_ASSERT(!bound->hasGuessedAtom());
+
+ // Try to avoid invoking the resolve hook.
+ if (targetObj->is<JSFunction>() &&
+ !targetObj->as<JSFunction>().hasResolvedName()) {
+ JSFunction* targetFn = &targetObj->as<JSFunction>();
+
+ // If the target is a bound function with a prefixed name, we can't
+ // lazily compute the full name in getBoundFunctionName(), therefore
+ // we need to append the bound function name prefix here.
+ if (targetFn->isBoundFunction() && targetFn->hasBoundFunctionNamePrefix()) {
+ JSAtom* name = AppendBoundFunctionPrefix(cx, targetFn->explicitName());
+ if (!name) {
+ return false;
+ }
+ bound->setPrefixedBoundFunctionName(name);
+ } else {
+ JSAtom* name = targetFn->infallibleGetUnresolvedName(cx);
+ MOZ_ASSERT(name);
+
+ bound->setAtom(name);
+ }
+ } else {
+ // 19.2.3.2 Function.prototype.bind, step 9.
+ RootedValue targetName(cx);
+ if (!GetProperty(cx, targetObj, targetObj, cx->names().name, &targetName)) {
+ return false;
+ }
+
+ // 19.2.3.2 Function.prototype.bind, step 10.
+ if (!targetName.isString()) {
+ targetName.setString(cx->names().empty);
+ }
+
+ // If the target itself is a bound function (with a resolved name), we
+ // can't compute the full name in getBoundFunctionName() based only on
+ // the number of bound target functions, therefore we need to store
+ // the complete prefixed name here.
+ if (targetObj->is<JSFunction>() &&
+ targetObj->as<JSFunction>().isBoundFunction()) {
+ JSAtom* name = AppendBoundFunctionPrefix(cx, targetName.toString());
+ if (!name) {
+ return false;
+ }
+ bound->setPrefixedBoundFunctionName(name);
+ } else {
+ JSAtom* name = AtomizeString(cx, targetName.toString());
+ if (!name) {
+ return false;
+ }
+ bound->setAtom(name);
+ }
+ }
+
+ return true;
+}
+
+template <typename Unit>
+bool DelazifyCanonicalScriptedFunctionImpl(JSContext* cx, HandleFunction fun,
+ Handle<BaseScript*> lazy,
+ ScriptSource* ss) {
+ MOZ_ASSERT(!lazy->hasBytecode(), "Script is already compiled!");
+ MOZ_ASSERT(lazy->function() == fun);
+
+ AutoIncrementalTimer timer(cx->realm()->timers.delazificationTime);
+
+ size_t sourceStart = lazy->sourceStart();
+ size_t sourceLength = lazy->sourceEnd() - lazy->sourceStart();
+
+ {
+ MOZ_ASSERT(ss->hasSourceText());
+
+ // Parse and compile the script from source.
+ UncompressedSourceCache::AutoHoldEntry holder;
+
+ MOZ_ASSERT(ss->hasSourceType<Unit>());
+
+ ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, sourceStart,
+ sourceLength);
+ if (!units.get()) {
+ return false;
+ }
+
+ JS::CompileOptions options(cx);
+ frontend::FillCompileOptionsForLazyFunction(options, lazy);
+
+ Rooted<frontend::CompilationStencil> stencil(
+ cx, frontend::CompilationStencil(cx, options));
+ stencil.get().input.initFromLazy(lazy);
+
+ if (!frontend::CompileLazyFunctionToStencil(cx, stencil.get(), lazy,
+ units.get(), sourceLength)) {
+ // The frontend shouldn't fail after linking the function and the
+ // non-lazy script together.
+ MOZ_ASSERT(fun->baseScript() == lazy);
+ MOZ_ASSERT(lazy->isReadyForDelazification());
+ return false;
+ }
+
+ if (!frontend::InstantiateStencilsForDelazify(cx, stencil.get())) {
+ // The frontend shouldn't fail after linking the function and the
+ // non-lazy script together.
+ MOZ_ASSERT(fun->baseScript() == lazy);
+ MOZ_ASSERT(lazy->isReadyForDelazification());
+ return false;
+ }
+
+ if (ss->hasEncoder()) {
+ // NOTE: Currently we rely on the UseOffThreadParseGlobal to decide which
+ // format to use for incremental encoding.
+ bool useStencilXDR = !js::UseOffThreadParseGlobal();
+ if (useStencilXDR) {
+ if (!ss->xdrEncodeFunctionStencil(cx, stencil.get())) {
+ return false;
+ }
+ } else {
+ // XDR the newly delazified function.
+ RootedScriptSourceObject sourceObject(
+ cx, fun->nonLazyScript()->sourceObject());
+ if (!ss->xdrEncodeFunction(cx, fun, sourceObject)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool DelazifyCanonicalScriptedFunction(JSContext* cx,
+ HandleFunction fun) {
+ Rooted<BaseScript*> lazy(cx, fun->baseScript());
+ ScriptSource* ss = lazy->scriptSource();
+
+ if (ss->hasSourceType<Utf8Unit>()) {
+ // UTF-8 source text.
+ return DelazifyCanonicalScriptedFunctionImpl<Utf8Unit>(cx, fun, lazy, ss);
+ }
+
+ MOZ_ASSERT(ss->hasSourceType<char16_t>());
+
+ // UTF-16 source text.
+ return DelazifyCanonicalScriptedFunctionImpl<char16_t>(cx, fun, lazy, ss);
+}
+
+/* static */
+bool JSFunction::delazifyLazilyInterpretedFunction(JSContext* cx,
+ HandleFunction fun) {
+ MOZ_ASSERT(fun->hasBaseScript());
+ MOZ_ASSERT(cx->compartment() == fun->compartment());
+
+ // The function must be same-compartment but might be cross-realm. Make sure
+ // the script is created in the function's realm.
+ AutoRealm ar(cx, fun);
+
+ Rooted<BaseScript*> lazy(cx, fun->baseScript());
+ RootedFunction canonicalFun(cx, lazy->function());
+
+ // If this function is non-canonical, then use the canonical function first
+ // to get the delazified script. This may result in calling this method
+ // again on the canonical function. This ensures the canonical function is
+ // always non-lazy if any of the clones are non-lazy.
+ if (fun != canonicalFun) {
+ JSScript* script = JSFunction::getOrCreateScript(cx, canonicalFun);
+ if (!script) {
+ return false;
+ }
+
+ // Delazifying the canonical function should naturally make us non-lazy
+ // because we share a BaseScript with the canonical function.
+ MOZ_ASSERT(fun->hasBytecode());
+ return true;
+ }
+
+ // Finally, compile the script if it really doesn't exist.
+ return DelazifyCanonicalScriptedFunction(cx, fun);
+}
+
+/* static */
+bool JSFunction::delazifySelfHostedLazyFunction(JSContext* cx,
+ js::HandleFunction fun) {
+ MOZ_ASSERT(cx->compartment() == fun->compartment());
+
+ // The function must be same-compartment but might be cross-realm. Make sure
+ // the script is created in the function's realm.
+ AutoRealm ar(cx, fun);
+
+ /* Lazily cloned self-hosted script. */
+ MOZ_ASSERT(fun->isSelfHostedBuiltin());
+ Rooted<PropertyName*> funName(cx, GetClonedSelfHostedFunctionName(fun));
+ if (!funName) {
+ return false;
+ }
+ return cx->runtime()->cloneSelfHostedFunctionScript(cx, funName, fun);
+}
+
+void JSFunction::maybeRelazify(JSRuntime* rt) {
+ MOZ_ASSERT(!isIncomplete(), "Cannot relazify incomplete functions");
+
+ // Don't relazify functions in compartments that are active.
+ Realm* realm = this->realm();
+ if (!rt->allowRelazificationForTesting) {
+ if (realm->compartment()->gcState.hasEnteredRealm) {
+ return;
+ }
+
+ MOZ_ASSERT(!realm->hasBeenEnteredIgnoringJit());
+ }
+
+ // The caller should have checked we're not in the self-hosting zone (it's
+ // shared with worker runtimes so relazifying functions in it will race).
+ MOZ_ASSERT(!realm->isSelfHostingRealm());
+
+ // Don't relazify if the realm is being debugged. The debugger side-tables
+ // such as the set of active breakpoints require bytecode to exist.
+ if (realm->isDebuggee()) {
+ return;
+ }
+
+ // Don't relazify if we are collecting coverage so that we do not lose count
+ // information.
+ if (coverage::IsLCovEnabled()) {
+ return;
+ }
+
+ // Check the script's eligibility.
+ JSScript* script = nonLazyScript();
+ if (!script->allowRelazify()) {
+ return;
+ }
+ MOZ_ASSERT(script->isRelazifiable());
+
+ // There must not be any JIT code attached since the relazification process
+ // does not know how to discard it. In general, the GC should discard most JIT
+ // code before attempting relazification.
+ if (script->hasJitScript()) {
+ return;
+ }
+
+ if (isSelfHostedBuiltin()) {
+ gc::PreWriteBarrier(script);
+ initSelfHostedLazyScript(&rt->selfHostedLazyScript.ref());
+ } else {
+ script->relazify(rt);
+ }
+}
+
+js::GeneratorKind JSFunction::clonedSelfHostedGeneratorKind() const {
+ MOZ_ASSERT(hasSelfHostedLazyScript());
+
+ // This is a lazy clone of a self-hosted builtin. It has no BaseScript, and
+ // `this->flags_` does not contain the generator kind. Consult the
+ // implementation in the self-hosting realm, which has a BaseScript.
+ MOZ_RELEASE_ASSERT(isExtended());
+ PropertyName* name = GetClonedSelfHostedFunctionName(this);
+ return runtimeFromMainThread()->getSelfHostedFunctionGeneratorKind(name);
+}
+
+// ES2018 draft rev 2aea8f3e617b49df06414eb062ab44fad87661d3
+// 19.2.1.1.1 CreateDynamicFunction( constructor, newTarget, kind, args )
+static bool CreateDynamicFunction(JSContext* cx, const CallArgs& args,
+ GeneratorKind generatorKind,
+ FunctionAsyncKind asyncKind) {
+ using namespace frontend;
+
+ // Steps 1-5.
+ bool isGenerator = generatorKind == GeneratorKind::Generator;
+ bool isAsync = asyncKind == FunctionAsyncKind::AsyncFunction;
+
+ RootedScript maybeScript(cx);
+ const char* filename;
+ unsigned lineno;
+ bool mutedErrors;
+ uint32_t pcOffset;
+ DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno,
+ &pcOffset, &mutedErrors);
+
+ const char* introductionType = "Function";
+ if (isAsync) {
+ if (isGenerator) {
+ introductionType = "AsyncGenerator";
+ } else {
+ introductionType = "AsyncFunction";
+ }
+ } else if (isGenerator) {
+ introductionType = "GeneratorFunction";
+ }
+
+ const char* introducerFilename = filename;
+ if (maybeScript && maybeScript->scriptSource()->introducerFilename()) {
+ introducerFilename = maybeScript->scriptSource()->introducerFilename();
+ }
+
+ CompileOptions options(cx);
+ options.setMutedErrors(mutedErrors)
+ .setFileAndLine(filename, 1)
+ .setNoScriptRval(false)
+ .setIntroductionInfo(introducerFilename, introductionType, lineno,
+ maybeScript, pcOffset)
+ .setScriptOrModule(maybeScript);
+
+ JSStringBuilder sb(cx);
+
+ if (isAsync) {
+ if (!sb.append("async ")) {
+ return false;
+ }
+ }
+ if (!sb.append("function")) {
+ return false;
+ }
+ if (isGenerator) {
+ if (!sb.append('*')) {
+ return false;
+ }
+ }
+
+ if (!sb.append(" anonymous(")) {
+ return false;
+ }
+
+ if (args.length() > 1) {
+ RootedString str(cx);
+
+ // Steps 10, 14.d.
+ unsigned n = args.length() - 1;
+
+ for (unsigned i = 0; i < n; i++) {
+ // Steps 14.a-b, 14.d.i-ii.
+ str = ToString<CanGC>(cx, args[i]);
+ if (!str) {
+ return false;
+ }
+
+ // Steps 14.b, 14.d.iii.
+ if (!sb.append(str)) {
+ return false;
+ }
+
+ if (i < args.length() - 2) {
+ // Step 14.d.iii.
+ if (!sb.append(',')) {
+ return false;
+ }
+ }
+ }
+ }
+
+ if (!sb.append('\n')) {
+ return false;
+ }
+
+ // Remember the position of ")".
+ Maybe<uint32_t> parameterListEnd = Some(uint32_t(sb.length()));
+ MOZ_ASSERT(FunctionConstructorMedialSigils[0] == ')');
+
+ if (!sb.append(FunctionConstructorMedialSigils)) {
+ return false;
+ }
+
+ if (args.length() > 0) {
+ // Steps 13, 14.e, 15.
+ RootedString body(cx, ToString<CanGC>(cx, args[args.length() - 1]));
+ if (!body || !sb.append(body)) {
+ return false;
+ }
+ }
+
+ if (!sb.append(FunctionConstructorFinalBrace)) {
+ return false;
+ }
+
+ // The parser only accepts two byte strings.
+ if (!sb.ensureTwoByteChars()) {
+ return false;
+ }
+
+ RootedString functionText(cx, sb.finishString());
+ if (!functionText) {
+ return false;
+ }
+
+ // Block this call if security callbacks forbid it.
+ Handle<GlobalObject*> global = cx->global();
+ if (!GlobalObject::isRuntimeCodeGenEnabled(cx, functionText, global)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_CSP_BLOCKED_FUNCTION);
+ return false;
+ }
+
+ // Steps 7.a-b, 8.a-b, 9.a-b, 16-28.
+ AutoStableStringChars stableChars(cx);
+ if (!stableChars.initTwoByte(cx, functionText)) {
+ return false;
+ }
+
+ mozilla::Range<const char16_t> chars = stableChars.twoByteRange();
+ SourceOwnership ownership = stableChars.maybeGiveOwnershipToCaller()
+ ? SourceOwnership::TakeOwnership
+ : SourceOwnership::Borrowed;
+ SourceText<char16_t> srcBuf;
+ if (!srcBuf.init(cx, chars.begin().get(), chars.length(), ownership)) {
+ return false;
+ }
+
+ FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Expression;
+
+ RootedFunction fun(cx);
+ JSProtoKey protoKey;
+ if (isAsync) {
+ if (isGenerator) {
+ fun = CompileStandaloneAsyncGenerator(cx, options, srcBuf,
+ parameterListEnd, syntaxKind);
+ protoKey = JSProto_AsyncGeneratorFunction;
+ } else {
+ fun = CompileStandaloneAsyncFunction(cx, options, srcBuf,
+ parameterListEnd, syntaxKind);
+ protoKey = JSProto_AsyncFunction;
+ }
+ } else {
+ if (isGenerator) {
+ fun = CompileStandaloneGenerator(cx, options, srcBuf, parameterListEnd,
+ syntaxKind);
+ protoKey = JSProto_GeneratorFunction;
+ } else {
+ fun = CompileStandaloneFunction(cx, options, srcBuf, parameterListEnd,
+ syntaxKind);
+ protoKey = JSProto_Function;
+ }
+ }
+ if (!fun) {
+ return false;
+ }
+
+ if (fun->isInterpreted()) {
+ fun->initEnvironment(&cx->global()->lexicalEnvironment());
+ }
+
+ // Steps 6, 29.
+ RootedObject proto(cx);
+ if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
+ return false;
+ }
+
+ // Steps 7.d, 8.d (implicit).
+ // Call SetPrototype if an explicit prototype was given.
+ if (proto && !SetPrototype(cx, fun, proto)) {
+ return false;
+ }
+
+ // Step 38.
+ args.rval().setObject(*fun);
+ return true;
+}
+
+bool js::Function(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CreateDynamicFunction(cx, args, GeneratorKind::NotGenerator,
+ FunctionAsyncKind::SyncFunction);
+}
+
+bool js::Generator(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CreateDynamicFunction(cx, args, GeneratorKind::Generator,
+ FunctionAsyncKind::SyncFunction);
+}
+
+bool js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CreateDynamicFunction(cx, args, GeneratorKind::NotGenerator,
+ FunctionAsyncKind::AsyncFunction);
+}
+
+bool js::AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return CreateDynamicFunction(cx, args, GeneratorKind::Generator,
+ FunctionAsyncKind::AsyncFunction);
+}
+
+bool JSFunction::isBuiltinFunctionConstructor() {
+ return maybeNative() == Function || maybeNative() == Generator;
+}
+
+bool JSFunction::needsExtraBodyVarEnvironment() const {
+ if (isNative()) {
+ return false;
+ }
+
+ if (!nonLazyScript()->functionHasExtraBodyVarScope()) {
+ return false;
+ }
+
+ return nonLazyScript()->functionExtraBodyVarScope()->hasEnvironment();
+}
+
+bool JSFunction::needsNamedLambdaEnvironment() const {
+ if (!isNamedLambda()) {
+ return false;
+ }
+
+ LexicalScope* scope = nonLazyScript()->maybeNamedLambdaScope();
+ if (!scope) {
+ return false;
+ }
+
+ return scope->hasEnvironment();
+}
+
+bool JSFunction::needsCallObject() const {
+ if (isNative()) {
+ return false;
+ }
+
+ MOZ_ASSERT(hasBytecode());
+
+ // Note: this should be kept in sync with
+ // FunctionBox::needsCallObjectRegardlessOfBindings().
+ MOZ_ASSERT_IF(
+ baseScript()->funHasExtensibleScope() || isGenerator() || isAsync(),
+ nonLazyScript()->bodyScope()->hasEnvironment());
+
+ return nonLazyScript()->bodyScope()->hasEnvironment();
+}
+
+JSFunction* js::NewScriptedFunction(
+ JSContext* cx, unsigned nargs, FunctionFlags flags, HandleAtom atom,
+ HandleObject proto /* = nullptr */,
+ gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
+ NewObjectKind newKind /* = GenericObject */,
+ HandleObject enclosingEnvArg /* = nullptr */) {
+ RootedObject enclosingEnv(cx, enclosingEnvArg);
+ if (!enclosingEnv) {
+ enclosingEnv = &cx->global()->lexicalEnvironment();
+ }
+ return NewFunctionWithProto(cx, nullptr, nargs, flags, enclosingEnv, atom,
+ proto, allocKind, newKind);
+}
+
+#ifdef DEBUG
+static JSObject* SkipEnvironmentObjects(JSObject* env) {
+ if (!env) {
+ return nullptr;
+ }
+ while (env->is<EnvironmentObject>()) {
+ env = &env->as<EnvironmentObject>().enclosingEnvironment();
+ }
+ return env;
+}
+
+static bool NewFunctionEnvironmentIsWellFormed(JSContext* cx,
+ HandleObject env) {
+ // Assert that the terminating environment is null, global, or a debug
+ // scope proxy. All other cases of polluting global scope behavior are
+ // handled by EnvironmentObjects (viz. non-syntactic DynamicWithObject and
+ // NonSyntacticVariablesObject).
+ RootedObject terminatingEnv(cx, SkipEnvironmentObjects(env));
+ return !terminatingEnv || terminatingEnv == cx->global() ||
+ terminatingEnv->is<DebugEnvironmentProxy>();
+}
+#endif
+
+JSFunction* js::NewFunctionWithProto(
+ JSContext* cx, Native native, unsigned nargs, FunctionFlags flags,
+ HandleObject enclosingEnv, HandleAtom atom, HandleObject proto,
+ gc::AllocKind allocKind /* = AllocKind::FUNCTION */,
+ NewObjectKind newKind /* = GenericObject */) {
+ MOZ_ASSERT(allocKind == gc::AllocKind::FUNCTION ||
+ allocKind == gc::AllocKind::FUNCTION_EXTENDED);
+ MOZ_ASSERT_IF(native, !enclosingEnv);
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
+
+ // NOTE: Keep this in sync with `CreateFunctionFast` in Stencil.cpp
+
+ JSFunction* fun =
+ NewObjectWithClassProto<JSFunction>(cx, proto, allocKind, newKind);
+ if (!fun) {
+ return nullptr;
+ }
+
+ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
+ flags.setIsExtended();
+ }
+
+ // Disallow flags that require special union arms to be initialized.
+ MOZ_ASSERT(!flags.hasSelfHostedLazyScript());
+ MOZ_ASSERT(!flags.isWasmWithJitEntry());
+
+ /* Initialize all function members. */
+ fun->setArgCount(uint16_t(nargs));
+ fun->setFlags(flags);
+ if (fun->isInterpreted()) {
+ fun->initScript(nullptr);
+ fun->initEnvironment(enclosingEnv);
+ } else {
+ MOZ_ASSERT(fun->isNative());
+ fun->initNative(native, nullptr);
+ }
+ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
+ fun->initializeExtended();
+ }
+ fun->initAtom(atom);
+
+ return fun;
+}
+
+bool js::GetFunctionPrototype(JSContext* cx, js::GeneratorKind generatorKind,
+ js::FunctionAsyncKind asyncKind,
+ js::MutableHandleObject proto) {
+ // Self-hosted functions have null [[Prototype]]. This allows self-hosting to
+ // support generators, despite this loop in the builtin object graph:
+ // - %Generator%.prototype.[[Prototype]] is Iterator.prototype;
+ // - Iterator.prototype has self-hosted methods (iterator helpers).
+ if (cx->realm()->isSelfHostingRealm()) {
+ proto.set(nullptr);
+ return true;
+ }
+
+ if (generatorKind == js::GeneratorKind::NotGenerator) {
+ if (asyncKind == js::FunctionAsyncKind::SyncFunction) {
+ proto.set(nullptr);
+ return true;
+ }
+
+ proto.set(
+ GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
+ } else {
+ if (asyncKind == js::FunctionAsyncKind::SyncFunction) {
+ proto.set(GlobalObject::getOrCreateGeneratorFunctionPrototype(
+ cx, cx->global()));
+ } else {
+ proto.set(GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()));
+ }
+ }
+ return !!proto;
+}
+
+bool js::CanReuseScriptForClone(JS::Realm* realm, HandleFunction fun,
+ HandleObject newEnclosingEnv) {
+ MOZ_ASSERT(fun->isInterpreted());
+
+ if (realm != fun->realm()) {
+ return false;
+ }
+
+ if (newEnclosingEnv->is<GlobalObject>()) {
+ return true;
+ }
+
+ // Don't need to clone the script if newEnclosingEnv is a syntactic scope,
+ // since in that case we have some actual scope objects on our scope chain and
+ // whatnot; whoever put them there should be responsible for setting our
+ // script's flags appropriately. We hit this case for JSOp::Lambda, for
+ // example.
+ if (IsSyntacticEnvironment(newEnclosingEnv)) {
+ return true;
+ }
+
+ // We need to clone the script if we're not already marked as having a
+ // non-syntactic scope. The HasNonSyntacticScope flag is not computed for lazy
+ // scripts so fallback to checking the scope chain.
+ BaseScript* script = fun->baseScript();
+ return script->hasNonSyntacticScope() ||
+ script->enclosingScope()->hasOnChain(ScopeKind::NonSyntactic);
+}
+
+static inline JSFunction* NewFunctionClone(JSContext* cx, HandleFunction fun,
+ NewObjectKind newKind,
+ gc::AllocKind allocKind,
+ HandleObject proto) {
+ RootedObject cloneProto(cx, proto);
+ if (!proto) {
+ if (!GetFunctionPrototype(cx, fun->generatorKind(), fun->asyncKind(),
+ &cloneProto)) {
+ return nullptr;
+ }
+ }
+
+ RootedFunction clone(cx);
+ clone =
+ NewObjectWithClassProto<JSFunction>(cx, cloneProto, allocKind, newKind);
+ if (!clone) {
+ return nullptr;
+ }
+
+ constexpr uint16_t NonCloneableFlags = FunctionFlags::EXTENDED |
+ FunctionFlags::RESOLVED_LENGTH |
+ FunctionFlags::RESOLVED_NAME;
+
+ FunctionFlags flags = fun->flags();
+ flags.clearFlags(NonCloneableFlags);
+
+ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
+ flags.setIsExtended();
+ }
+
+ clone->setArgCount(fun->nargs());
+ clone->setFlags(flags);
+
+ JSAtom* atom = fun->displayAtom();
+ if (atom) {
+ cx->markAtom(atom);
+ }
+ clone->initAtom(atom);
+
+ if (allocKind == gc::AllocKind::FUNCTION_EXTENDED) {
+ if (fun->isExtended() && fun->compartment() == cx->compartment()) {
+ for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++) {
+ clone->initExtendedSlot(i, fun->getExtendedSlot(i));
+ }
+ } else {
+ clone->initializeExtended();
+ }
+ }
+
+ return clone;
+}
+
+JSFunction* js::CloneFunctionReuseScript(JSContext* cx, HandleFunction fun,
+ HandleObject enclosingEnv,
+ gc::AllocKind allocKind,
+ HandleObject proto) {
+ MOZ_ASSERT(cx->realm() == fun->realm());
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
+ MOZ_ASSERT(fun->isInterpreted());
+ MOZ_ASSERT(!fun->isBoundFunction());
+ MOZ_ASSERT(CanReuseScriptForClone(cx->realm(), fun, enclosingEnv));
+
+ NewObjectKind newKind = GenericObject;
+ RootedFunction clone(cx,
+ NewFunctionClone(cx, fun, newKind, allocKind, proto));
+ if (!clone) {
+ return nullptr;
+ }
+
+ if (fun->hasBaseScript()) {
+ BaseScript* base = fun->baseScript();
+ clone->initScript(base);
+ clone->initEnvironment(enclosingEnv);
+ } else {
+ MOZ_ASSERT(fun->hasSelfHostedLazyScript());
+ SelfHostedLazyScript* lazy = fun->selfHostedLazyScript();
+ clone->initSelfHostedLazyScript(lazy);
+ clone->initEnvironment(enclosingEnv);
+ }
+
+ return clone;
+}
+
+JSFunction* js::CloneFunctionAndScript(JSContext* cx, HandleFunction fun,
+ HandleObject enclosingEnv,
+ HandleScope newScope,
+ Handle<ScriptSourceObject*> sourceObject,
+ gc::AllocKind allocKind,
+ HandleObject proto /* = nullptr */) {
+ MOZ_ASSERT(NewFunctionEnvironmentIsWellFormed(cx, enclosingEnv));
+ MOZ_ASSERT(fun->isInterpreted());
+ MOZ_ASSERT(!fun->isBoundFunction());
+
+ JSScript::AutoDelazify funScript(cx, fun);
+ if (!funScript) {
+ return nullptr;
+ }
+
+ RootedFunction clone(
+ cx, NewFunctionClone(cx, fun, TenuredObject, allocKind, proto));
+ if (!clone) {
+ return nullptr;
+ }
+
+ clone->initScript(nullptr);
+ clone->initEnvironment(enclosingEnv);
+
+ /*
+ * Across compartments or if we have to introduce a non-syntactic scope we
+ * have to clone the script for interpreted functions. Cross-compartment
+ * cloning only happens via JSAPI (JS::CloneFunctionObject) which
+ * dynamically ensures that 'script' has no enclosing lexical scope (only
+ * the global scope or other non-lexical scope).
+ */
+#ifdef DEBUG
+ RootedObject terminatingEnv(cx, enclosingEnv);
+ while (IsSyntacticEnvironment(terminatingEnv)) {
+ terminatingEnv = terminatingEnv->enclosingEnvironment();
+ }
+ MOZ_ASSERT_IF(!terminatingEnv->is<GlobalObject>(),
+ newScope->hasOnChain(ScopeKind::NonSyntactic));
+#endif
+
+ RootedScript script(cx, fun->nonLazyScript());
+ MOZ_ASSERT(script->realm() == fun->realm());
+ MOZ_ASSERT(cx->compartment() == clone->compartment(),
+ "Otherwise we could relazify clone below!");
+
+ RootedScript clonedScript(
+ cx, CloneScriptIntoFunction(cx, newScope, clone, script, sourceObject));
+ if (!clonedScript) {
+ return nullptr;
+ }
+ DebugAPI::onNewScript(cx, clonedScript);
+
+ return clone;
+}
+
+JSFunction* js::CloneAsmJSModuleFunction(JSContext* cx, HandleFunction fun) {
+ MOZ_ASSERT(fun->isNative());
+ MOZ_ASSERT(IsAsmJSModule(fun));
+ MOZ_ASSERT(fun->isExtended());
+ MOZ_ASSERT(cx->compartment() == fun->compartment());
+
+ JSFunction* clone =
+ NewFunctionClone(cx, fun, GenericObject, gc::AllocKind::FUNCTION_EXTENDED,
+ /* proto = */ nullptr);
+ if (!clone) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(fun->native() == InstantiateAsmJS);
+ MOZ_ASSERT(!fun->hasJitInfo());
+ clone->initNative(InstantiateAsmJS, nullptr);
+
+ return clone;
+}
+
+JSFunction* js::CloneSelfHostingIntrinsic(JSContext* cx, HandleFunction fun) {
+ MOZ_ASSERT(fun->isNative());
+ MOZ_ASSERT(fun->realm()->isSelfHostingRealm());
+ MOZ_ASSERT(!fun->isExtended());
+ MOZ_ASSERT(cx->compartment() != fun->compartment());
+
+ JSFunction* clone =
+ NewFunctionClone(cx, fun, TenuredObject, gc::AllocKind::FUNCTION,
+ /* proto = */ nullptr);
+ if (!clone) {
+ return nullptr;
+ }
+
+ clone->initNative(fun->native(),
+ fun->hasJitInfo() ? fun->jitInfo() : nullptr);
+ return clone;
+}
+
+static JSAtom* SymbolToFunctionName(JSContext* cx, JS::Symbol* symbol,
+ FunctionPrefixKind prefixKind) {
+ // Step 4.a.
+ JSAtom* desc = symbol->description();
+
+ // Step 4.b, no prefix fastpath.
+ if (!desc && prefixKind == FunctionPrefixKind::None) {
+ return cx->names().empty;
+ }
+
+ // Step 5 (reordered).
+ StringBuffer sb(cx);
+ if (prefixKind == FunctionPrefixKind::Get) {
+ if (!sb.append("get ")) {
+ return nullptr;
+ }
+ } else if (prefixKind == FunctionPrefixKind::Set) {
+ if (!sb.append("set ")) {
+ return nullptr;
+ }
+ }
+
+ // Step 4.b.
+ if (desc) {
+ // Note: Private symbols are wedged in, as implementation wise they're
+ // PrivateNameSymbols with a the source level name as a description
+ // i.e. obj.#f desugars to obj.[PrivateNameSymbol("#f")], however
+ // they don't use the symbol naming, but rather property naming.
+ if (symbol->isPrivateName()) {
+ if (!sb.append(desc)) {
+ return nullptr;
+ }
+ } else {
+ // Step 4.c.
+ if (!sb.append('[') || !sb.append(desc) || !sb.append(']')) {
+ return nullptr;
+ }
+ }
+ }
+ return sb.finishAtom();
+}
+
+static JSAtom* NameToFunctionName(JSContext* cx, HandleValue name,
+ FunctionPrefixKind prefixKind) {
+ MOZ_ASSERT(name.isString() || name.isNumeric());
+
+ if (prefixKind == FunctionPrefixKind::None) {
+ return ToAtom<CanGC>(cx, name);
+ }
+
+ JSString* nameStr = ToString(cx, name);
+ if (!nameStr) {
+ return nullptr;
+ }
+
+ StringBuffer sb(cx);
+ if (prefixKind == FunctionPrefixKind::Get) {
+ if (!sb.append("get ")) {
+ return nullptr;
+ }
+ } else {
+ if (!sb.append("set ")) {
+ return nullptr;
+ }
+ }
+ if (!sb.append(nameStr)) {
+ return nullptr;
+ }
+ return sb.finishAtom();
+}
+
+/*
+ * Return an atom for use as the name of a builtin method with the given
+ * property id.
+ *
+ * Function names are always strings. If id is the well-known @@iterator
+ * symbol, this returns "[Symbol.iterator]". If a prefix is supplied the final
+ * name is |prefix + " " + name|.
+ *
+ * Implements steps 3-5 of 9.2.11 SetFunctionName in ES2016.
+ */
+JSAtom* js::IdToFunctionName(
+ JSContext* cx, HandleId id,
+ FunctionPrefixKind prefixKind /* = FunctionPrefixKind::None */) {
+ MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id) || JSID_IS_INT(id));
+
+ // No prefix fastpath.
+ if (JSID_IS_ATOM(id) && prefixKind == FunctionPrefixKind::None) {
+ return JSID_TO_ATOM(id);
+ }
+
+ // Step 3 (implicit).
+
+ // Step 4.
+ if (JSID_IS_SYMBOL(id)) {
+ return SymbolToFunctionName(cx, JSID_TO_SYMBOL(id), prefixKind);
+ }
+
+ // Step 5.
+ RootedValue idv(cx, IdToValue(id));
+ return NameToFunctionName(cx, idv, prefixKind);
+}
+
+bool js::SetFunctionName(JSContext* cx, HandleFunction fun, HandleValue name,
+ FunctionPrefixKind prefixKind) {
+ MOZ_ASSERT(name.isString() || name.isSymbol() || name.isNumeric());
+
+ // `fun` is a newly created function, so it can't already have an inferred
+ // name.
+ MOZ_ASSERT(!fun->hasInferredName());
+
+ // Anonymous functions should neither have an own 'name' property nor a
+ // resolved name at this point.
+ MOZ_ASSERT(!fun->containsPure(cx->names().name));
+ MOZ_ASSERT(!fun->hasResolvedName());
+
+ JSAtom* funName = name.isSymbol()
+ ? SymbolToFunctionName(cx, name.toSymbol(), prefixKind)
+ : NameToFunctionName(cx, name, prefixKind);
+ if (!funName) {
+ return false;
+ }
+
+ fun->setInferredName(funName);
+
+ return true;
+}
+
+JSFunction* js::DefineFunction(
+ JSContext* cx, HandleObject obj, HandleId id, Native native, unsigned nargs,
+ unsigned flags, gc::AllocKind allocKind /* = AllocKind::FUNCTION */) {
+ RootedAtom atom(cx, IdToFunctionName(cx, id));
+ if (!atom) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(native);
+
+ RootedFunction fun(cx);
+ if (flags & JSFUN_CONSTRUCTOR) {
+ fun = NewNativeConstructor(cx, native, nargs, atom, allocKind);
+ } else {
+ fun = NewNativeFunction(cx, native, nargs, atom, allocKind);
+ }
+
+ if (!fun) {
+ return nullptr;
+ }
+
+ RootedValue funVal(cx, ObjectValue(*fun));
+ if (!DefineDataProperty(cx, obj, id, funVal, flags & ~JSFUN_FLAGS_MASK)) {
+ return nullptr;
+ }
+
+ return fun;
+}
+
+void js::ReportIncompatibleMethod(JSContext* cx, const CallArgs& args,
+ const JSClass* clasp) {
+ RootedValue thisv(cx, args.thisv());
+
+#ifdef DEBUG
+ switch (thisv.type()) {
+ case ValueType::Object:
+ MOZ_ASSERT(thisv.toObject().getClass() != clasp ||
+ !thisv.toObject().isNative() ||
+ !thisv.toObject().staticPrototype() ||
+ thisv.toObject().staticPrototype()->getClass() != clasp);
+ break;
+ case ValueType::String:
+ MOZ_ASSERT(clasp != &StringObject::class_);
+ break;
+ case ValueType::Double:
+ case ValueType::Int32:
+ MOZ_ASSERT(clasp != &NumberObject::class_);
+ break;
+ case ValueType::Boolean:
+ MOZ_ASSERT(clasp != &BooleanObject::class_);
+ break;
+ case ValueType::Symbol:
+ MOZ_ASSERT(clasp != &SymbolObject::class_);
+ break;
+ case ValueType::BigInt:
+ MOZ_ASSERT(clasp != &BigIntObject::class_);
+ break;
+ case ValueType::Undefined:
+ case ValueType::Null:
+ break;
+ case ValueType::Magic:
+ case ValueType::PrivateGCThing:
+ MOZ_CRASH("unexpected type");
+ }
+#endif
+
+ if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
+ UniqueChars funNameBytes;
+ if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
+ JSMSG_INCOMPATIBLE_PROTO, clasp->name, funName,
+ InformalValueTypeName(thisv));
+ }
+ }
+}
+
+void js::ReportIncompatible(JSContext* cx, const CallArgs& args) {
+ if (JSFunction* fun = ReportIfNotFunction(cx, args.calleev())) {
+ UniqueChars funNameBytes;
+ if (const char* funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
+ JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
+ JSMSG_INCOMPATIBLE_METHOD, funName, "method",
+ InformalValueTypeName(args.thisv()));
+ }
+ }
+}
+
+namespace JS {
+namespace detail {
+
+JS_PUBLIC_API void CheckIsValidConstructible(const Value& calleev) {
+ MOZ_ASSERT(calleev.toObject().isConstructor());
+}
+
+} // namespace detail
+} // namespace JS