diff options
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: |