summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dom/bindings/BindingUtils.cpp2
-rw-r--r--js/public/Class.h2
-rw-r--r--js/public/Proxy.h2
-rw-r--r--js/src/jit-test/tests/basic/bug1380962.js15
-rw-r--r--js/src/jit-test/tests/basic/bug807623.js7
-rw-r--r--js/src/jit-test/tests/proxy/function-toString.js8
-rw-r--r--js/src/jsapi-tests/testXDR.cpp2
-rw-r--r--js/src/jsapi.cpp8
-rw-r--r--js/src/jsapi.h10
-rw-r--r--js/src/jsfriendapi.h2
-rw-r--r--js/src/jsfun.cpp184
-rw-r--r--js/src/jsfun.h38
-rw-r--r--js/src/jsopcode.cpp4
-rw-r--r--js/src/jswrapper.h7
-rw-r--r--js/src/proxy/BaseProxyHandler.cpp7
-rw-r--r--js/src/proxy/CrossCompartmentWrapper.cpp4
-rw-r--r--js/src/proxy/DeadObjectProxy.cpp2
-rw-r--r--js/src/proxy/DeadObjectProxy.h3
-rw-r--r--js/src/proxy/OpaqueCrossCompartmentWrapper.cpp2
-rw-r--r--js/src/proxy/Proxy.cpp10
-rw-r--r--js/src/proxy/Proxy.h2
-rw-r--r--js/src/proxy/ScriptedProxyHandler.cpp8
-rw-r--r--js/src/proxy/ScriptedProxyHandler.h2
-rw-r--r--js/src/proxy/Wrapper.cpp4
-rw-r--r--js/src/shell/js.cpp4
-rw-r--r--js/src/vm/SelfHosting.cpp38
-rw-r--r--js/src/wasm/AsmJS.cpp6
-rw-r--r--js/src/wasm/AsmJS.h2
28 files changed, 210 insertions, 175 deletions
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index 512b9f8173..879db27a49 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -682,7 +682,7 @@ DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
// funToString ObjectOps member for interface objects.
JSString*
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
- unsigned /* indent */)
+ bool /* isToSource */)
{
const js::Class* clasp = js::GetObjectClass(aObject);
MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
diff --git a/js/public/Class.h b/js/public/Class.h
index 1dd03a1821..634e5a281d 100644
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -459,7 +459,7 @@ typedef bool
* that object. A null return value means OOM.
*/
typedef JSString*
-(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, unsigned indent);
+(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, bool isToSource);
/**
* Resolve a lazy property named by id in obj by defining it directly in obj.
diff --git a/js/public/Proxy.h b/js/public/Proxy.h
index 8d24969b20..2453e2e140 100644
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -326,7 +326,7 @@ class JS_FRIEND_API(BaseProxyHandler)
ESClass* cls) const;
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
virtual const char* className(JSContext* cx, HandleObject proxy) const;
- virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
+ virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
virtual void trace(JSTracer* trc, JSObject* proxy) const;
diff --git a/js/src/jit-test/tests/basic/bug1380962.js b/js/src/jit-test/tests/basic/bug1380962.js
new file mode 100644
index 0000000000..66548961d5
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1380962.js
@@ -0,0 +1,15 @@
+// Source string has balanced parentheses even when the source code was discarded.
+
+setDiscardSource(true);
+eval("var f = function() { return 0; };");
+assertEq(f.toSource(), "(function() {\n [sourceless code]\n})");
+// Source string has balanced parentheses even when the source code was discarded.
+
+setDiscardSource(true);
+eval("var f = function() { return 0; };");
+assertEq(f.toSource(), "(function() {\n [sourceless code]\n})");
+// Source string has balanced parentheses even when the source code was discarded.
+
+setDiscardSource(true);
+eval("var f = function() { return 0; };");
+assertEq(f.toSource(), "(function() {\n [sourceless code]\n})");
diff --git a/js/src/jit-test/tests/basic/bug807623.js b/js/src/jit-test/tests/basic/bug807623.js
index ce16ffd417..35c34d3cb3 100644
--- a/js/src/jit-test/tests/basic/bug807623.js
+++ b/js/src/jit-test/tests/basic/bug807623.js
@@ -3,12 +3,7 @@ var functionProxy = new Proxy(function() {}, {});
assertEq(Object.prototype.toString.call(objectProxy), '[object Object]');
assertEq(Object.prototype.toString.call(functionProxy), '[object Function]');
-try {
- Function.prototype.toString.call(functionProxy);
- assertEq(true, false);
-} catch (e) {
- assertEq(!!/incompatible/.exec(e), true);
-}
+
try {
Function.prototype.toString.call(objectProxy);
assertEq(true, false);
diff --git a/js/src/jit-test/tests/proxy/function-toString.js b/js/src/jit-test/tests/proxy/function-toString.js
index cedcf552a4..4aca5c907e 100644
--- a/js/src/jit-test/tests/proxy/function-toString.js
+++ b/js/src/jit-test/tests/proxy/function-toString.js
@@ -1,10 +1,10 @@
load(libdir + 'asserts.js');
-// Function.prototype.toString doesn't accept ES6 proxies.
+var nativeCode = "function () {\n [native code]\n}";
var proxy = new Proxy(function() {}, {});
-assertThrowsInstanceOf(() => Function.prototype.toString.call(proxy), TypeError);
+assertEq(Function.prototype.toString.call(proxy), nativeCode);
var o = Proxy.revocable(function() {}, {});
-assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError);
+assertEq(Function.prototype.toString.call(o.proxy), nativeCode);
o.revoke();
-assertThrowsInstanceOf(() => Function.prototype.toString.call(o.proxy), TypeError);
+assertEq(Function.prototype.toString.call(o.proxy), nativeCode);
diff --git a/js/src/jsapi-tests/testXDR.cpp b/js/src/jsapi-tests/testXDR.cpp
index 5d5fca1a99..3787f596ca 100644
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -115,7 +115,7 @@ BEGIN_TEST(testXDR_source)
CHECK(script);
script = FreezeThaw(cx, script);
CHECK(script);
- JSString* out = JS_DecompileScript(cx, script, "testing", 0);
+ JSString* out = JS_DecompileScript(cx, script);
CHECK(out);
bool equal;
CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 3901a0e63c..d16df26ba7 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4438,7 +4438,7 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
}
JS_PUBLIC_API(JSString*)
-JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigned indent)
+JS_DecompileScript(JSContext* cx, HandleScript script)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
@@ -4447,7 +4447,7 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne
script->ensureNonLazyCanonicalFunction();
RootedFunction fun(cx, script->functionNonDelazifying());
if (fun)
- return JS_DecompileFunction(cx, fun, indent);
+ return JS_DecompileFunction(cx, fun);
bool haveSource = script->scriptSource()->hasSourceData();
if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
return nullptr;
@@ -4456,13 +4456,13 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne
}
JS_PUBLIC_API(JSString*)
-JS_DecompileFunction(JSContext* cx, HandleFunction fun, unsigned indent)
+JS_DecompileFunction(JSContext* cx, HandleFunction fun)
{
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, fun);
- return FunctionToString(cx, fun, !(indent & JS_DONT_PRETTY_PRINT));
+ return FunctionToString(cx, fun, /* isToSource = */ false);
}
MOZ_NEVER_INLINE static bool
diff --git a/js/src/jsapi.h b/js/src/jsapi.h
index 8e70cc152b..0b865ff523 100644
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4200,16 +4200,10 @@ CompileFunction(JSContext* cx, AutoObjectVector& envChain,
} /* namespace JS */
extern JS_PUBLIC_API(JSString*)
-JS_DecompileScript(JSContext* cx, JS::Handle<JSScript*> script, const char* name, unsigned indent);
-
-/*
- * API extension: OR this into indent to avoid pretty-printing the decompiled
- * source resulting from JS_DecompileFunction.
- */
-#define JS_DONT_PRETTY_PRINT ((unsigned)0x8000)
+JS_DecompileScript(JSContext* cx, JS::Handle<JSScript*> script);
extern JS_PUBLIC_API(JSString*)
-JS_DecompileFunction(JSContext* cx, JS::Handle<JSFunction*> fun, unsigned indent);
+JS_DecompileFunction(JSContext* cx, JS::Handle<JSFunction*> fun);
/*
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index 494bcb042a..00da8295ad 100644
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -377,7 +377,7 @@ extern JS_FRIEND_API(bool)
proxy_GetElements(JSContext* cx, JS::HandleObject proxy, uint32_t begin, uint32_t end,
ElementAdder* adder);
extern JS_FRIEND_API(JSString*)
-proxy_FunToString(JSContext* cx, JS::HandleObject proxy, unsigned indent);
+proxy_FunToString(JSContext* cx, JS::HandleObject proxy, bool isToSource);
/**
* A class of objects that return source code on demand.
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index be1d39cd19..fc243fab66 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -339,11 +339,11 @@ 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.
-
+ // 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;
+ return false;
}
args.rval().setUndefined();
@@ -498,9 +498,12 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
if (fun->hasResolvedName())
return true;
+ RootedAtom name(cx);
+ if (!JSFunction::getUnresolvedName(cx, fun, &name))
+ return false;
+
// Don't define an own .name property for unnamed functions.
- JSAtom* name = fun->getUnresolvedName(cx);
- if (name == nullptr)
+ if (!name)
return true;
v.setString(name);
@@ -939,24 +942,24 @@ const Class JSFunction::class_ = {
const Class* const js::FunctionClassPtr = &JSFunction::class_;
JSString*
-js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
+js::FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource)
{
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
return nullptr;
if (IsAsmJSModule(fun))
- return AsmJSModuleToString(cx, fun, !prettyPrint);
+ return AsmJSModuleToString(cx, fun, isToSource);
if (IsAsmJSFunction(fun))
return AsmJSFunctionToString(cx, fun);
if (IsWrappedAsyncFunction(fun)) {
RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
- return FunctionToString(cx, unwrapped, prettyPrint);
+ return FunctionToString(cx, unwrapped, isToSource);
}
if (IsWrappedAsyncGenerator(fun)) {
RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
- return FunctionToString(cx, unwrapped, prettyPrint);
+ return FunctionToString(cx, unwrapped, isToSource);
}
StringBuffer out(cx);
@@ -975,8 +978,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
}
- bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
-
// Default class constructors are self-hosted, but have their source
// objects overridden to refer to the span of the class statement or
// expression. Non-default class constructors are never self-hosted. So,
@@ -984,12 +985,9 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
!fun->isSelfHostedBuiltin());
- // If we're not in pretty mode, put parentheses around lambda functions
- // so that eval returns lambda, not function statement.
- if (haveSource && !prettyPrint && funIsNonArrowLambda) {
- if (!out.append("("))
- return nullptr;
- }
+ // 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 && !script->scriptSource()->hasSourceData() &&
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
@@ -997,85 +995,82 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
return nullptr;
}
- auto AppendPrelude = [&out, &fun]() {
+ if (addParentheses) {
+ if (!out.append('('))
+ return nullptr;
+ }
+
+ if (haveSource) {
+ Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
+ if (!src)
+ return nullptr;
+
+ if (!out.append(src))
+ return nullptr;
+ } else {
if (fun->isAsync()) {
if (!out.append("async "))
- return false;
+ return nullptr;
}
if (!fun->isArrow()) {
if (!out.append("function"))
- return false;
+ return nullptr;
if (fun->isStarGenerator()) {
if (!out.append('*'))
- return false;
+ return nullptr;
}
}
if (fun->explicitName()) {
if (!out.append(' '))
- return false;
+ return nullptr;
+ if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) {
+ if (!out.append(cx->names().boundWithSpace))
+ return nullptr;
+ }
if (!out.append(fun->explicitName()))
- return false;
- }
- return true;
- };
-
- if (haveSource) {
- Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
- if (!src)
- return nullptr;
-
- if (!out.append(src))
- return nullptr;
-
- if (!prettyPrint && funIsNonArrowLambda) {
- if (!out.append(")"))
return nullptr;
}
- } else if (fun->isInterpreted() &&
- (!fun->isSelfHostedBuiltin() ||
- fun->infallibleIsDefaultClassConstructor(cx)))
- {
- // Default class constructors should always haveSource except;
- //
- // 1. Source has been discarded for the whole compartment.
- //
- // 2. The source is marked as "lazy", i.e., retrieved on demand, and
- // the embedding has not provided a hook to retrieve sources.
- MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
- !cx->runtime()->sourceHook ||
- !script->scriptSource()->sourceRetrievable() ||
- fun->compartment()->behaviors().discardSource());
- if (!AppendPrelude() ||
- !out.append("() {\n ") ||
- !out.append("[sourceless code]") ||
- !out.append("\n}"))
+
+ if (fun->isInterpreted() &&
+ (!fun->isSelfHostedBuiltin() ||
+ fun->infallibleIsDefaultClassConstructor(cx)))
{
- return nullptr;
+ // Default class constructors should always haveSource except;
+ //
+ // 1. Source has been discarded for the whole compartment.
+ //
+ // 2. The source is marked as "lazy", i.e., retrieved on demand, and
+ // the embedding has not provided a hook to retrieve sources.
+ MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
+ !cx->runtime()->sourceHook ||
+ !script->scriptSource()->sourceRetrievable() ||
+ fun->compartment()->behaviors().discardSource());
+
+ if (!out.append("() {\n [sourceless code]\n}"))
+ return nullptr;
+ } else {
+ if (!out.append("() {\n [native code]\n}"))
+ return nullptr;
}
- } else {
-
- if (!AppendPrelude() ||
- !out.append("() {\n "))
- return nullptr;
-
- if (!out.append("[native code]"))
- return nullptr;
+ }
- if (!out.append("\n}"))
+ if (addParentheses) {
+ if (!out.append(')'))
return nullptr;
}
+
return out.finishString();
}
JSString*
-fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent)
+fun_toStringHelper(JSContext* cx, HandleObject obj, bool isToSource)
{
if (!obj->is<JSFunction>()) {
if (JSFunToStringOp op = obj->getOpsFunToString())
- return op(cx, obj, indent);
+ return op(cx, obj, isToSource);
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_INCOMPATIBLE_PROTO,
@@ -1084,7 +1079,7 @@ fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent)
}
RootedFunction fun(cx, &obj->as<JSFunction>());
- return FunctionToString(cx, fun, indent != JS_DONT_PRETTY_PRINT);
+ return FunctionToString(cx, fun, isToSource);
}
bool
@@ -1107,16 +1102,11 @@ js::fun_toString(JSContext* cx, unsigned argc, Value* vp)
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(IsFunctionObject(args.calleev()));
- uint32_t indent = 0;
-
- if (args.length() != 0 && !ToUint32(cx, args[0], &indent))
- return false;
-
RootedObject obj(cx, ToObject(cx, args.thisv()));
if (!obj)
return false;
- RootedString str(cx, fun_toStringHelper(cx, obj, indent));
+ JSString* str = fun_toStringHelper(cx, obj, /* isToSource = */ false);
if (!str)
return false;
@@ -1137,12 +1127,12 @@ fun_toSource(JSContext* cx, unsigned argc, Value* vp)
RootedString str(cx);
if (obj->isCallable())
- str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
+ str = fun_toStringHelper(cx, obj, /* isToSource = */ true);
else
str = ObjectToSource(cx, obj);
-
if (!str)
return false;
+
args.rval().setString(str);
return true;
}
@@ -1327,23 +1317,45 @@ JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandle
return true;
}
-JSAtom*
-JSFunction::getUnresolvedName(JSContext* cx)
+/* static */ bool
+JSFunction::getUnresolvedName(JSContext* cx, HandleFunction fun, MutableHandleAtom v)
{
- MOZ_ASSERT(!IsInternalFunctionObject(*this));
- MOZ_ASSERT(!hasResolvedName());
+ MOZ_ASSERT(!IsInternalFunctionObject(*fun));
+ MOZ_ASSERT(!fun->hasResolvedName());
- if (isClassConstructor()) {
+ JSAtom* name = fun->explicitOrCompileTimeName();
+ if (fun->isClassConstructor()) {
// It's impossible to have an empty named class expression. We use
// empty as a sentinel when creating default class constructors.
- MOZ_ASSERT(explicitOrCompileTimeName() != cx->names().empty);
+ MOZ_ASSERT(name != cx->names().empty);
// Unnamed class expressions should not get a .name property at all.
- return explicitOrCompileTimeName();
+ if (name)
+ v.set(name);
+ return true;
+ }
+
+ if (fun->isBoundFunction() && !fun->hasBoundFunctionNamePrefix()) {
+ // Bound functions are never unnamed.
+ MOZ_ASSERT(name);
+
+ if (name->length() > 0) {
+ StringBuffer sb(cx);
+ if (!sb.append(cx->names().boundWithSpace) || !sb.append(name))
+ return false;
+
+ name = sb.finishAtom();
+ if (!name)
+ return false;
+ } else {
+ name = cx->names().boundWithSpace;
+ }
+
+ fun->setPrefixedBoundFunctionName(name);
}
- return explicitOrCompileTimeName() != nullptr ? explicitOrCompileTimeName()
- : cx->names().empty;
+ v.set(name != nullptr ? name : cx->names().empty);
+ return true;
}
static const js::Value&
diff --git a/js/src/jsfun.h b/js/src/jsfun.h
index 38cc832800..481f062080 100644
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -59,6 +59,9 @@ class JSFunction : public js::NativeObject
BOUND_FUN = 0x0008, /* function was created with Function.prototype.bind. */
HAS_GUESSED_ATOM = 0x0020, /* function had no explicit name, but a
name was guessed for it anyway */
+ HAS_BOUND_FUNCTION_NAME_PREFIX = 0x0020, /* bound functions reuse the HAS_GUESSED_ATOM
+ flag to track if atom_ already contains the
+ "bound " function name prefix */
LAMBDA = 0x0040, /* function comes from a FunctionExpression, ArrowFunction, or
Function() call (not a FunctionDeclaration or nonstandard
function-statement) */
@@ -99,8 +102,8 @@ class JSFunction : public js::NativeObject
INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED,
NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME,
- STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA |
- SELF_HOSTED | HAS_COMPILE_TIME_NAME | FUNCTION_KIND_MASK
+ STABLE_ACROSS_CLONES = CONSTRUCTOR | LAMBDA | SELF_HOSTED | HAS_COMPILE_TIME_NAME |
+ FUNCTION_KIND_MASK
};
static_assert((INTERPRETED | INTERPRETED_LAZY) == js::JS_FUNCTION_INTERPRETED_BITS,
@@ -186,10 +189,20 @@ class JSFunction : public js::NativeObject
bool isAsmJSNative() const { return kind() == AsmJS; }
/* Possible attributes of an interpreted function: */
+ bool isBoundFunction() const { return flags() & BOUND_FUN; }
bool hasCompileTimeName() const { return flags() & HAS_COMPILE_TIME_NAME; }
- bool hasGuessedAtom() const { return flags() & HAS_GUESSED_ATOM; }
+ bool hasGuessedAtom() const {
+ static_assert(HAS_GUESSED_ATOM == HAS_BOUND_FUNCTION_NAME_PREFIX,
+ "HAS_GUESSED_ATOM is unused for bound functions");
+ return (flags() & (HAS_GUESSED_ATOM | BOUND_FUN)) == HAS_GUESSED_ATOM;
+ }
+ bool hasBoundFunctionNamePrefix() const {
+ static_assert(HAS_BOUND_FUNCTION_NAME_PREFIX == HAS_GUESSED_ATOM,
+ "HAS_BOUND_FUNCTION_NAME_PREFIX is only used for bound functions");
+ MOZ_ASSERT(isBoundFunction());
+ return flags() & HAS_BOUND_FUNCTION_NAME_PREFIX;
+ }
bool isLambda() const { return flags() & LAMBDA; }
- bool isBoundFunction() const { return flags() & BOUND_FUN; }
bool isInterpretedLazy() const { return flags() & INTERPRETED_LAZY; }
bool hasScript() const { return flags() & INTERPRETED; }
@@ -310,7 +323,8 @@ class JSFunction : public js::NativeObject
static bool getUnresolvedLength(JSContext* cx, js::HandleFunction fun,
js::MutableHandleValue v);
- JSAtom* getUnresolvedName(JSContext* cx);
+ static bool getUnresolvedName(JSContext* cx, js::HandleFunction fun,
+ js::MutableHandleAtom v);
JSAtom* explicitName() const {
return (hasCompileTimeName() || hasGuessedAtom()) ? nullptr : atom_.get();
@@ -346,16 +360,26 @@ class JSFunction : public js::NativeObject
MOZ_ASSERT(atom);
MOZ_ASSERT(!hasCompileTimeName());
MOZ_ASSERT(!hasGuessedAtom());
+ MOZ_ASSERT(!isBoundFunction());
+ MOZ_ASSERT(js::AtomIsMarked(zone(), atom));
atom_ = atom;
flags_ |= HAS_GUESSED_ATOM;
}
void clearGuessedAtom() {
MOZ_ASSERT(hasGuessedAtom());
+ MOZ_ASSERT(!isBoundFunction());
MOZ_ASSERT(atom_);
atom_ = nullptr;
flags_ &= ~HAS_GUESSED_ATOM;
}
+ void setPrefixedBoundFunctionName(JSAtom* atom) {
+ MOZ_ASSERT(!hasBoundFunctionNamePrefix());
+ MOZ_ASSERT(atom);
+ flags_ |= HAS_BOUND_FUNCTION_NAME_PREFIX;
+ atom_ = atom;
+ }
+
/* uint16_t representation bounds number of call object dynamic slots. */
enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) };
@@ -646,7 +670,7 @@ static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
"shadow interface must match actual interface");
extern JSString*
-fun_toStringHelper(JSContext* cx, js::HandleObject obj, unsigned indent);
+fun_toStringHelper(JSContext* cx, js::HandleObject obj, bool isToSource);
namespace js {
@@ -851,7 +875,7 @@ JSFunction::getExtendedSlot(size_t which) const
namespace js {
-JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPring);
+JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
template<XDRMode mode>
bool
diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp
index 982ba8e7f0..30fcd0c87a 100644
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -863,7 +863,7 @@ ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
if (obj.is<JSFunction>()) {
RootedFunction fun(cx, &obj.as<JSFunction>());
- JSString* str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
+ JSString* str = JS_DecompileFunction(cx, fun);
if (!str)
return false;
return bytes->encodeLatin1(cx, str);
@@ -1972,7 +1972,7 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
if (!AppendJSONProperty(buf, "text", NO_COMMA))
return false;
- JSString* str = JS_DecompileScript(cx, script, nullptr, 0);
+ JSString* str = JS_DecompileScript(cx, script);
if (!str || !(str = StringToSource(cx, str)))
return false;
diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h
index b7bfef1c37..3729ba884d 100644
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -115,7 +115,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
JS::IsArrayAnswer* answer) const override;
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
- unsigned indent) const override;
+ bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
RegExpGuard* g) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
@@ -210,7 +210,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
bool* bp) const override;
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
- unsigned indent) const override;
+ bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
@@ -272,7 +272,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap
virtual bool hasInstance(JSContext* cx, HandleObject wrapper,
MutableHandleValue v, bool* bp) const override;
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
- virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
+ virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
+ bool isToSource) const override;
static const OpaqueCrossCompartmentWrapper singleton;
};
diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp
index c433e7ace5..6cd526d059 100644
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -315,12 +315,13 @@ BaseProxyHandler::className(JSContext* cx, HandleObject proxy) const
}
JSString*
-BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
+BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
{
if (proxy->isCallable())
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
- RootedValue v(cx, ObjectValue(*proxy));
- ReportIsNotFunction(cx, v);
+
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
+ js_Function_str, js_toString_str, "object");
return nullptr;
}
diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp
index cb2111778d..e12f96bfa7 100644
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -422,12 +422,12 @@ CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
}
JSString*
-CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const
+CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool isToSource) const
{
RootedString str(cx);
{
AutoCompartment call(cx, wrappedObject(wrapper));
- str = Wrapper::fun_toString(cx, wrapper, indent);
+ str = Wrapper::fun_toString(cx, wrapper, isToSource);
if (!str)
return nullptr;
}
diff --git a/js/src/proxy/DeadObjectProxy.cpp b/js/src/proxy/DeadObjectProxy.cpp
index 95869fa03a..34d9c3b04d 100644
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -135,7 +135,7 @@ DeadObjectProxy::className(JSContext* cx, HandleObject wrapper) const
}
JSString*
-DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
+DeadObjectProxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
{
ReportDead(cx);
return nullptr;
diff --git a/js/src/proxy/DeadObjectProxy.h b/js/src/proxy/DeadObjectProxy.h
index 0715625fec..bedec9c040 100644
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -47,7 +47,8 @@ class DeadObjectProxy : public BaseProxyHandler
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
- virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
+ virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
+ bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
virtual bool isCallable(JSObject* obj) const override;
diff --git a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
index 84b42a7cd5..b63e5cc7ab 100644
--- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
+++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
@@ -191,7 +191,7 @@ OpaqueCrossCompartmentWrapper::className(JSContext* cx,
JSString*
OpaqueCrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject proxy,
- unsigned indent) const
+ bool isToSource) const
{
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
js_Function_str, js_toString_str, "object");
diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp
index 6f91dfb106..d873717248 100644
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -475,7 +475,7 @@ Proxy::className(JSContext* cx, HandleObject proxy)
}
JSString*
-Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
+Proxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource)
{
JS_CHECK_RECURSION(cx, return nullptr);
const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
@@ -483,8 +483,8 @@ Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
BaseProxyHandler::GET, /* mayThrow = */ false);
// Do the safe thing if the policy rejects.
if (!policy.allowed())
- return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
- return handler->fun_toString(cx, proxy, indent);
+ return handler->BaseProxyHandler::fun_toString(cx, proxy, isToSource);
+ return handler->fun_toString(cx, proxy, isToSource);
}
bool
@@ -691,9 +691,9 @@ js::proxy_GetElements(JSContext* cx, HandleObject proxy, uint32_t begin, uint32_
}
JSString*
-js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent)
+js::proxy_FunToString(JSContext* cx, HandleObject proxy, bool isToSource)
{
- return Proxy::fun_toString(cx, proxy, indent);
+ return Proxy::fun_toString(cx, proxy, isToSource);
}
const ClassOps js::ProxyClassOps = {
diff --git a/js/src/proxy/Proxy.h b/js/src/proxy/Proxy.h
index 1b8e368c3b..a1942b25de 100644
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -60,7 +60,7 @@ class Proxy
static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
static const char* className(JSContext* cx, HandleObject proxy);
- static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
+ static JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource);
static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
diff --git a/js/src/proxy/ScriptedProxyHandler.cpp b/js/src/proxy/ScriptedProxyHandler.cpp
index ebbc223827..5a219e2e3f 100644
--- a/js/src/proxy/ScriptedProxyHandler.cpp
+++ b/js/src/proxy/ScriptedProxyHandler.cpp
@@ -1257,11 +1257,11 @@ ScriptedProxyHandler::className(JSContext* cx, HandleObject proxy) const
}
JSString*
-ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
+ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
{
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
- js_Function_str, js_toString_str, "object");
- return nullptr;
+ // The BaseProxyHandler has the desired behavior: Throw for non-callable,
+ // otherwise return [native code].
+ return BaseProxyHandler::fun_toString(cx, proxy, isToSource);
}
bool
diff --git a/js/src/proxy/ScriptedProxyHandler.h b/js/src/proxy/ScriptedProxyHandler.h
index 8ce0119185..e464dc24e8 100644
--- a/js/src/proxy/ScriptedProxyHandler.h
+++ b/js/src/proxy/ScriptedProxyHandler.h
@@ -67,7 +67,7 @@ class ScriptedProxyHandler : public BaseProxyHandler
JS::IsArrayAnswer* answer) const override;
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
- unsigned indent) const override;
+ bool isToSource) const override;
virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
RegExpGuard* g) const override;
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
diff --git a/js/src/proxy/Wrapper.cpp b/js/src/proxy/Wrapper.cpp
index bb69123a59..36cb1317f7 100644
--- a/js/src/proxy/Wrapper.cpp
+++ b/js/src/proxy/Wrapper.cpp
@@ -259,11 +259,11 @@ Wrapper::className(JSContext* cx, HandleObject proxy) const
}
JSString*
-Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
+Wrapper::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
{
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
RootedObject target(cx, proxy->as<ProxyObject>().target());
- return fun_toStringHelper(cx, target, indent);
+ return fun_toStringHelper(cx, target, isToSource);
}
bool
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 755fb73f1a..749e7a79a8 100644
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4577,7 +4577,7 @@ DecompileFunction(JSContext* cx, unsigned argc, Value* vp)
return true;
}
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
- JSString* result = JS_DecompileFunction(cx, fun, 0);
+ JSString* result = JS_DecompileFunction(cx, fun);
if (!result)
return false;
args.rval().setString(result);
@@ -4599,7 +4599,7 @@ DecompileThisScript(JSContext* cx, unsigned argc, Value* vp)
JSAutoCompartment ac(cx, iter.script());
RootedScript script(cx, iter.script());
- JSString* result = JS_DecompileScript(cx, script, "test", 0);
+ JSString* result = JS_DecompileScript(cx, script);
if (!result)
return false;
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index 38785a822c..29c75c2064 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -571,39 +571,31 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, NumberValue(length));
// Try to avoid invoking the resolve hook.
- JSAtom* name = nullptr;
- if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedName())
- name = targetObj->as<JSFunction>().getUnresolvedName(cx);
+ RootedAtom name(cx);
+ if (targetObj->is<JSFunction>() && !targetObj->as<JSFunction>().hasResolvedName()) {
+ if (!JSFunction::getUnresolvedName(cx, targetObj.as<JSFunction>(), &name))
+ return false;
+ }
- RootedString rootedName(cx);
- if (name) {
- rootedName = name;
- } else {
+ // 19.2.3.2 Function.prototype.bind, steps 9-11.
+ if (!name) {
// 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())
- rootedName = targetName.toString();
+ if (targetName.isString() && !targetName.toString()->empty()) {
+ name = AtomizeString(cx, targetName.toString());
+ if (!name)
+ return false;
+ } else {
+ name = cx->names().empty;
+ }
}
- // 19.2.3.2 Function.prototype.bind, step 11 (Inlined SetFunctionName).
MOZ_ASSERT(!bound->hasGuessedAtom());
- if (rootedName && !rootedName->empty()) {
- StringBuffer sb(cx);
- if (!sb.append(cx->names().boundWithSpace) || !sb.append(rootedName))
- return false;
-
- RootedAtom nameAtom(cx, sb.finishAtom());
- if (!nameAtom)
- return false;
-
- bound->setAtom(nameAtom);
- } else {
- bound->setAtom(cx->names().boundWithSpace);
- }
+ bound->setAtom(name);
args.rval().setUndefined();
return true;
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index 8cbbff5370..5fe0f7ea44 100644
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8848,7 +8848,7 @@ js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
// asm.js toString/toSource support
JSString*
-js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
+js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool isToSource)
{
MOZ_ASSERT(IsAsmJSModule(fun));
@@ -8859,7 +8859,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
StringBuffer out(cx);
- if (addParenToLambda && fun->isLambda() && !out.append("("))
+ if (isToSource && fun->isLambda() && !out.append("("))
return nullptr;
bool haveSource = source->hasSourceData();
@@ -8882,7 +8882,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
return nullptr;
}
- if (addParenToLambda && fun->isLambda() && !out.append(")"))
+ if (isToSource && fun->isLambda() && !out.append(")"))
return nullptr;
return out.finishString();
diff --git a/js/src/wasm/AsmJS.h b/js/src/wasm/AsmJS.h
index 15d39991f7..a38b204a8b 100644
--- a/js/src/wasm/AsmJS.h
+++ b/js/src/wasm/AsmJS.h
@@ -76,7 +76,7 @@ extern JSString*
AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
extern JSString*
-AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
+AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool isToSource);
// asm.js heap: