summaryrefslogtreecommitdiffstats
path: root/js/src/jsfun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jsfun.cpp')
-rw-r--r--js/src/jsfun.cpp349
1 files changed, 156 insertions, 193 deletions
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index bcb0da80b..9edf238ef 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -129,6 +129,11 @@ IsFunctionInStrictMode(JSFunction* fun)
return IsAsmJSStrictModeModuleOrFunction(fun);
}
+static bool
+IsNewerTypeFunction(JSFunction* fun) {
+ return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod();
+}
+
// 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
@@ -142,7 +147,9 @@ ArgumentsRestrictions(JSContext* cx, HandleFunction fun)
// a strict mode function, or a bound function.
// TODO (bug 1057208): ensure semantics are correct for all possible
// pairings of callee/caller.
- if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) {
+ if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
+ fun->isBoundFunction() || IsNewerTypeFunction(fun))
+ {
ThrowTypeErrorBehavior(cx);
return false;
}
@@ -229,7 +236,9 @@ CallerRestrictions(JSContext* cx, HandleFunction fun)
// a strict mode function, or a bound function.
// TODO (bug 1057208): ensure semantics are correct for all possible
// pairings of callee/caller.
- if (fun->isBuiltin() || IsFunctionInStrictMode(fun) || fun->isBoundFunction()) {
+ if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
+ fun->isBoundFunction() || IsNewerTypeFunction(fun))
+ {
ThrowTypeErrorBehavior(cx);
return false;
}
@@ -275,6 +284,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
}
RootedObject caller(cx, iter.callee(cx));
+ if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync())
+ caller = GetWrappedAsyncFunction(&caller->as<JSFunction>());
if (!cx->compartment()->wrap(cx, &caller))
return false;
@@ -288,7 +299,15 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
return true;
}
+ if (JS_IsDeadWrapper(callerObj)) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_DEAD_OBJECT);
+ return false;
+ }
+
JSFunction* callerFun = &callerObj->as<JSFunction>();
+ if (IsWrappedAsyncFunction(callerFun))
+ callerFun = GetUnwrappedAsyncFunction(callerFun);
MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
if (callerFun->strict()) {
@@ -314,54 +333,14 @@ CallerSetterImpl(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;
-
- // Return |undefined| unless an error must be thrown.
- args.rval().setUndefined();
-
- // We can almost just return |undefined| here -- but if the caller function
- // was strict mode code, we still have to throw a TypeError. This requires
- // computing the caller, checking that no security boundaries are crossed,
- // and throwing a TypeError if the resulting caller is strict.
-
- NonBuiltinScriptFrameIter iter(cx);
- if (!AdvanceToActiveCallLinear(cx, iter, fun))
- return true;
-
- ++iter;
- while (!iter.done() && iter.isEvalFrame())
- ++iter;
-
- if (iter.done() || !iter.isFunctionFrame())
- return true;
-
- RootedObject caller(cx, iter.callee(cx));
- if (!cx->compartment()->wrap(cx, &caller)) {
- cx->clearPendingException();
- return true;
- }
-
- // If we don't have full access to the caller, or the caller is not strict,
- // return undefined. Otherwise throw a TypeError.
- JSObject* callerObj = CheckedUnwrap(caller);
- if (!callerObj)
- return true;
-
- JSFunction* callerFun = &callerObj->as<JSFunction>();
- MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?");
-
- if (callerFun->strict()) {
- JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
- JSMSG_CALLER_IS_STRICT);
- return false;
+ // 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;
}
@@ -400,7 +379,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId
if (isStarGenerator)
objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
else
- objProto = fun->global().getOrCreateObjectPrototype(cx);
+ objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
if (!objProto)
return false;
@@ -497,7 +476,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
if (fun->hasResolvedLength())
return true;
- if (!fun->getUnresolvedLength(cx, &v))
+ if (!JSFunction::getUnresolvedLength(cx, fun, &v))
return false;
} else {
if (fun->hasResolvedName())
@@ -690,7 +669,7 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp)
}
/*
- * ES6 (4-25-16) 7.3.19 OrdinaryHasInstance
+ * ES6 7.3.19 OrdinaryHasInstance
*/
bool
JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp)
@@ -707,7 +686,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
if (obj->is<JSFunction>() && obj->isBoundFunction()) {
/* Steps 2a-b. */
obj = obj->as<JSFunction>().getBoundFunctionTarget();
- return InstanceOfOperator(cx, obj, v, bp);
+ return InstanceofOperator(cx, obj, v, bp);
}
/* Step 3. */
@@ -716,12 +695,12 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
return true;
}
- /* Step 4. */
+ /* Step 4-5. */
RootedValue pval(cx);
if (!GetProperty(cx, obj, obj, cx->names().prototype, &pval))
return false;
- /* Step 5. */
+ /* Step 6. */
if (pval.isPrimitive()) {
/*
* Throw a runtime error if instanceof is called on a function that
@@ -732,7 +711,7 @@ JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool*
return false;
}
- /* Step 6. */
+ /* Step 7. */
RootedObject pobj(cx, &pval.toObject());
bool isDelegate;
if (!IsDelegate(cx, pobj, v, &isDelegate))
@@ -815,8 +794,10 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedFunction functionProto(cx, &functionProto_->as<JSFunction>());
- const char* rawSource = "() {\n}";
+ const char* rawSource = "function () {\n}";
size_t sourceLen = strlen(rawSource);
+ size_t begin = 9;
+ MOZ_ASSERT(rawSource[begin] == '(');
mozilla::UniquePtr<char16_t[], JS::FreePolicy> source(InflateString(cx, rawSource, &sourceLen));
if (!source)
return nullptr;
@@ -838,13 +819,15 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
RootedScript script(cx, JSScript::Create(cx,
options,
sourceObject,
+ begin,
+ ss->length(),
0,
ss->length()));
if (!script || !JSScript::initFunctionPrototype(cx, script, functionProto))
return nullptr;
functionProto->initScript(script);
- ObjectGroup* protoGroup = functionProto->getGroup(cx);
+ ObjectGroup* protoGroup = JSObject::getGroup(cx, functionProto);
if (!protoGroup)
return nullptr;
@@ -879,6 +862,28 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key)
if (!throwTypeError || !PreventExtensions(cx, throwTypeError))
return nullptr;
+ // The "length" property of %ThrowTypeError% is non-configurable, adjust
+ // the default property attributes accordingly.
+ Rooted<PropertyDescriptor> nonConfigurableDesc(cx);
+ nonConfigurableDesc.setAttributes(JSPROP_PERMANENT | JSPROP_IGNORE_READONLY |
+ JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_VALUE);
+
+ RootedId lengthId(cx, NameToId(cx->names().length));
+ ObjectOpResult lengthResult;
+ if (!NativeDefineProperty(cx, throwTypeError, lengthId, nonConfigurableDesc, lengthResult))
+ return nullptr;
+ MOZ_ASSERT(lengthResult);
+
+ // Non-standard: Also change "name" to non-configurable. ECMAScript defines
+ // %ThrowTypeError% as an anonymous function, i.e. it shouldn't actually
+ // get an own "name" property. To be consistent with other built-in,
+ // anonymous functions, we don't delete %ThrowTypeError%'s "name" property.
+ RootedId nameId(cx, NameToId(cx->names().name));
+ ObjectOpResult nameResult;
+ if (!NativeDefineProperty(cx, throwTypeError, nameId, nonConfigurableDesc, nameResult))
+ return nullptr;
+ MOZ_ASSERT(nameResult);
+
self->setThrowTypeError(throwTypeError);
return functionProto;
@@ -917,80 +922,10 @@ const Class JSFunction::class_ = {
const Class* const js::FunctionClassPtr = &JSFunction::class_;
-/* Find the body of a function (not including braces). */
-bool
-js::FindBody(JSContext* cx, HandleFunction fun, HandleLinearString src, size_t* bodyStart,
- size_t* bodyEnd)
-{
- // We don't need principals, since those are only used for error reporting.
- CompileOptions options(cx);
- options.setFileAndLine("internal-findBody", 0);
-
- // For asm.js/wasm modules, there's no script.
- if (fun->hasScript())
- options.setVersion(fun->nonLazyScript()->getVersion());
-
- AutoKeepAtoms keepAtoms(cx->perThreadData);
-
- AutoStableStringChars stableChars(cx);
- if (!stableChars.initTwoByte(cx, src))
- return false;
-
- const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
- TokenStream ts(cx, options, srcChars.begin().get(), srcChars.length(), nullptr);
- int nest = 0;
- bool onward = true;
- // Skip arguments list.
- do {
- TokenKind tt;
- if (!ts.getToken(&tt))
- return false;
- switch (tt) {
- case TOK_NAME:
- case TOK_YIELD:
- if (nest == 0)
- onward = false;
- break;
- case TOK_LP:
- nest++;
- break;
- case TOK_RP:
- if (--nest == 0)
- onward = false;
- break;
- default:
- break;
- }
- } while (onward);
- TokenKind tt;
- if (!ts.getToken(&tt))
- return false;
- if (tt == TOK_ARROW) {
- if (!ts.getToken(&tt))
- return false;
- }
- bool braced = tt == TOK_LC;
- MOZ_ASSERT_IF(fun->isExprBody(), !braced);
- *bodyStart = ts.currentToken().pos.begin;
- if (braced)
- *bodyStart += 1;
- mozilla::RangedPtr<const char16_t> end = srcChars.end();
- if (end[-1] == '}') {
- end--;
- } else {
- MOZ_ASSERT(!braced);
- for (; unicode::IsSpaceOrBOM2(end[-1]); end--)
- ;
- }
- *bodyEnd = end - srcChars.begin();
- MOZ_ASSERT(*bodyStart <= *bodyEnd);
- return true;
-}
-
JSString*
js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
{
- if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
+ if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
return nullptr;
if (IsAsmJSModule(fun))
@@ -1019,80 +954,97 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
}
}
- if (fun->isAsync()) {
- if (!out.append("async "))
- return nullptr;
- }
+ bool funIsNonArrowLambda = fun->isLambda() && !fun->isArrow();
- bool funIsMethodOrNonArrowLambda = (fun->isLambda() && !fun->isArrow()) || fun->isMethod() ||
- fun->isGetter() || fun->isSetter();
- bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin();
+ // 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,
+ // all class constructors always have source.
+ bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
+ !fun->isSelfHostedBuiltin());
- // If we're not in pretty mode, put parentheses around lambda functions and methods.
- if (haveSource && !prettyPrint && funIsMethodOrNonArrowLambda) {
+ // 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 (!fun->isArrow()) {
- bool ok;
- if (fun->isStarGenerator() && !fun->isAsync())
- ok = out.append("function* ");
- else
- ok = out.append("function ");
- if (!ok)
- return nullptr;
- }
- if (fun->explicitName()) {
- if (!out.append(fun->explicitName()))
- return nullptr;
- }
if (haveSource && !script->scriptSource()->hasSourceData() &&
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
{
return nullptr;
}
+
+ auto AppendPrelude = [&out, &fun]() {
+ if (fun->isAsync()) {
+ if (!out.append("async "))
+ return false;
+ }
+
+ if (!fun->isArrow()) {
+ if (!out.append("function"))
+ return false;
+
+ if (fun->isStarGenerator()) {
+ if (!out.append('*'))
+ return false;
+ }
+ }
+
+ if (fun->explicitName()) {
+ if (!out.append(' '))
+ return false;
+ if (!out.append(fun->explicitName()))
+ return false;
+ }
+ return true;
+ };
+
if (haveSource) {
- Rooted<JSFlatString*> src(cx, script->sourceData(cx));
+ Rooted<JSFlatString*> src(cx, JSScript::sourceDataForToString(cx, script));
if (!src)
return nullptr;
if (!out.append(src))
return nullptr;
- if (!prettyPrint && funIsMethodOrNonArrowLambda) {
+ if (!prettyPrint && funIsNonArrowLambda) {
if (!out.append(")"))
return nullptr;
}
- } else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
- if (!out.append("() {\n ") ||
+ } 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}"))
{
return nullptr;
}
} else {
- MOZ_ASSERT(!fun->isExprBody());
- bool derived = fun->infallibleIsDefaultClassConstructor(cx);
- if (derived && fun->isDerivedClassConstructor()) {
- if (!out.append("(...args) {\n ") ||
- !out.append("super(...args);\n}"))
- {
- return nullptr;
- }
- } else {
- if (!out.append("() {\n "))
- return nullptr;
+ if (!AppendPrelude() ||
+ !out.append("() {\n "))
+ return nullptr;
- if (!derived) {
- if (!out.append("[native code]"))
- return nullptr;
- }
+ if (!out.append("[native code]"))
+ return nullptr;
- if (!out.append("\n}"))
- return nullptr;
- }
+ if (!out.append("\n}"))
+ return nullptr;
}
return out.finishString();
}
@@ -1321,34 +1273,33 @@ JSFunction::isDerivedClassConstructor()
return derived;
}
-bool
-JSFunction::getLength(JSContext* cx, uint16_t* length)
+/* static */ bool
+JSFunction::getLength(JSContext* cx, HandleFunction fun, uint16_t* length)
{
- JS::RootedFunction self(cx, this);
- MOZ_ASSERT(!self->isBoundFunction());
- if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
+ MOZ_ASSERT(!fun->isBoundFunction());
+ if (fun->isInterpretedLazy() && !getOrCreateScript(cx, fun))
return false;
- *length = self->isNative() ? self->nargs() : self->nonLazyScript()->funLength();
+ *length = fun->isNative() ? fun->nargs() : fun->nonLazyScript()->funLength();
return true;
}
-bool
-JSFunction::getUnresolvedLength(JSContext* cx, MutableHandleValue v)
+/* static */ bool
+JSFunction::getUnresolvedLength(JSContext* cx, HandleFunction fun, MutableHandleValue v)
{
- MOZ_ASSERT(!IsInternalFunctionObject(*this));
- MOZ_ASSERT(!hasResolvedLength());
+ 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 (isBoundFunction()) {
- MOZ_ASSERT(getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
- v.set(getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
+ if (fun->isBoundFunction()) {
+ MOZ_ASSERT(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT).isNumber());
+ v.set(fun->getExtendedSlot(BOUND_FUN_LENGTH_SLOT));
return true;
}
uint16_t length;
- if (!getLength(cx, &length))
+ if (!JSFunction::getLength(cx, fun, &length))
return false;
v.setInt32(length);
@@ -1405,13 +1356,11 @@ GetBoundFunctionArguments(const JSFunction* boundFun)
}
const js::Value&
-JSFunction::getBoundFunctionArgument(JSContext* cx, unsigned which) const
+JSFunction::getBoundFunctionArgument(unsigned which) const
{
MOZ_ASSERT(which < getBoundFunctionArgumentCount());
- RootedArrayObject boundArgs(cx, GetBoundFunctionArguments(this));
- RootedValue res(cx);
- return boundArgs->getDenseElement(which);
+ return GetBoundFunctionArguments(this)->getDenseElement(which);
}
size_t
@@ -1448,7 +1397,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
}
if (fun != lazy->functionNonDelazifying()) {
- if (!lazy->functionDelazifying(cx))
+ if (!LazyScript::functionDelazifying(cx, lazy))
return false;
script = lazy->functionNonDelazifying()->nonLazyScript();
if (!script)
@@ -1669,7 +1618,18 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
StringBuffer sb(cx);
- if (!sb.append('('))
+ if (isAsync) {
+ if (!sb.append("async "))
+ return false;
+ }
+ if (!sb.append("function"))
+ return false;
+ if (isStarGenerator && !isAsync) {
+ if (!sb.append('*'))
+ return false;
+ }
+
+ if (!sb.append(" anonymous("))
return false;
if (args.length() > 1) {
@@ -1690,12 +1650,15 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
if (i < args.length() - 2) {
// Step 9.d.iii.
- if (!sb.append(", "))
+ 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] == ')');