blob: b6c99610955b7d0a176021d86edae86d33c20c3c [file] [log] [blame]
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2003-2020 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "config.h"
#include "FunctionPrototype.h"
#include "BuiltinNames.h"
#include "ClonedArguments.h"
#include "FunctionExecutable.h"
#include "IntegrityInlines.h"
#include "JSBoundFunction.h"
#include "JSCInlines.h"
namespace JSC {
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(FunctionPrototype);
const ClassInfo FunctionPrototype::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(FunctionPrototype) };
static JSC_DECLARE_HOST_FUNCTION(functionProtoFuncToString);
static JSC_DECLARE_HOST_FUNCTION(callFunctionPrototype);
static JSC_DECLARE_CUSTOM_GETTER(argumentsGetter);
static JSC_DECLARE_CUSTOM_GETTER(callerGetter);
static JSC_DECLARE_CUSTOM_SETTER(callerAndArgumentsSetter);
// https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object
JSC_DEFINE_HOST_FUNCTION(callFunctionPrototype, (JSGlobalObject*, CallFrame*))
{
return JSValue::encode(jsUndefined());
}
FunctionPrototype::FunctionPrototype(VM& vm, Structure* structure)
: InternalFunction(vm, structure, callFunctionPrototype, nullptr)
{
}
void FunctionPrototype::finishCreation(VM& vm, const String& name)
{
Base::finishCreation(vm, 0, name, PropertyAdditionMode::WithoutStructureTransition);
ASSERT(inherits(vm, info()));
}
void FunctionPrototype::addFunctionProperties(VM& vm, JSGlobalObject* globalObject, JSFunction** callFunction, JSFunction** applyFunction, JSFunction** hasInstanceSymbolFunction)
{
JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().toStringPublicName(), functionProtoFuncToString, static_cast<unsigned>(PropertyAttribute::DontEnum), 0, FunctionToStringIntrinsic);
*applyFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().applyPublicName(), functionPrototypeApplyCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
*callFunction = putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->builtinNames().callPublicName(), functionPrototypeCallCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
putDirectBuiltinFunctionWithoutTransition(vm, globalObject, vm.propertyNames->bind, functionPrototypeBindCodeGenerator(vm), static_cast<unsigned>(PropertyAttribute::DontEnum));
putDirectCustomGetterSetterWithoutTransition(vm, vm.propertyNames->arguments, CustomGetterSetter::create(vm, argumentsGetter, callerAndArgumentsSetter), PropertyAttribute::DontEnum | PropertyAttribute::CustomAccessor);
putDirectCustomGetterSetterWithoutTransition(vm, vm.propertyNames->caller, CustomGetterSetter::create(vm, callerGetter, callerAndArgumentsSetter), PropertyAttribute::DontEnum | PropertyAttribute::CustomAccessor);
*hasInstanceSymbolFunction = JSFunction::create(vm, functionPrototypeSymbolHasInstanceCodeGenerator(vm), globalObject);
putDirectWithoutTransition(vm, vm.propertyNames->hasInstanceSymbol, *hasInstanceSymbolFunction, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
}
JSC_DEFINE_HOST_FUNCTION(functionProtoFuncToString, (JSGlobalObject* globalObject, CallFrame* callFrame))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue thisValue = callFrame->thisValue();
if (thisValue.inherits<JSFunction>(vm)) {
JSFunction* function = jsCast<JSFunction*>(thisValue);
Integrity::auditStructureID(vm, function->structureID());
RELEASE_AND_RETURN(scope, JSValue::encode(function->toString(globalObject)));
}
if (thisValue.inherits<InternalFunction>(vm)) {
InternalFunction* function = jsCast<InternalFunction*>(thisValue);
Integrity::auditStructureID(vm, function->structureID());
RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "function ", function->name(), "() {\n [native code]\n}")));
}
if (thisValue.isObject()) {
JSObject* object = asObject(thisValue);
Integrity::auditStructureID(vm, object->structureID());
if (object->isCallable(vm))
RELEASE_AND_RETURN(scope, JSValue::encode(jsMakeNontrivialString(globalObject, "function ", object->classInfo(vm)->className, "() {\n [native code]\n}")));
}
return throwVMTypeError(globalObject, scope);
}
// https://github.com/claudepache/es-legacy-function-reflection/blob/master/spec.md#isallowedreceiverfunctionforcallerandargumentsfunc-expectedrealm (except step 3)
static ALWAYS_INLINE bool isAllowedReceiverFunctionForCallerAndArguments(JSFunction* function)
{
if (function->isHostOrBuiltinFunction())
return false;
FunctionExecutable* executable = function->jsExecutable();
return !executable->isInStrictContext() && executable->parseMode() == SourceParseMode::NormalFunctionMode && !executable->isClassConstructorFunction();
}
class RetrieveArgumentsFunctor {
public:
RetrieveArgumentsFunctor(VM& vm, JSFunction* functionObj)
: m_vm(vm)
, m_targetCallee(functionObj)
, m_result(jsNull())
{
}
JSValue result() const { return m_result; }
StackVisitor::Status operator()(StackVisitor& visitor) const
{
if (!visitor->callee().isCell())
return StackVisitor::Continue;
JSCell* callee = visitor->callee().asCell();
if (callee != m_targetCallee)
return StackVisitor::Continue;
m_result = JSValue(visitor->createArguments(m_vm));
return StackVisitor::Done;
}
private:
VM& m_vm;
JSObject* m_targetCallee;
mutable JSValue m_result;
};
static JSValue retrieveArguments(VM& vm, CallFrame* callFrame, JSFunction* functionObj)
{
RetrieveArgumentsFunctor functor(vm, functionObj);
if (callFrame)
callFrame->iterate(vm, functor);
return functor.result();
}
// https://github.com/claudepache/es-legacy-function-reflection/blob/master/spec.md#get-functionprototypearguments
JSC_DEFINE_CUSTOM_GETTER(argumentsGetter, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSFunction* thisObj = jsDynamicCast<JSFunction*>(vm, JSValue::decode(thisValue));
if (!thisObj || !isAllowedReceiverFunctionForCallerAndArguments(thisObj))
return throwVMTypeError(globalObject, scope, RestrictedPropertyAccessError);
return JSValue::encode(retrieveArguments(vm, vm.topCallFrame, thisObj));
}
class RetrieveCallerFunctionFunctor {
public:
RetrieveCallerFunctionFunctor(JSFunction* functionObj)
: m_targetCallee(functionObj)
, m_hasFoundFrame(false)
, m_hasSkippedToCallerFrame(false)
, m_result(jsNull())
{
}
JSValue result() const { return m_result; }
StackVisitor::Status operator()(StackVisitor& visitor) const
{
if (!visitor->callee().isCell())
return StackVisitor::Continue;
JSCell* callee = visitor->callee().asCell();
if (callee && (callee->inherits<JSBoundFunction>(callee->vm()) || callee->type() == ProxyObjectType))
return StackVisitor::Continue;
if (!m_hasFoundFrame && callee != m_targetCallee)
return StackVisitor::Continue;
m_hasFoundFrame = true;
if (!m_hasSkippedToCallerFrame) {
m_hasSkippedToCallerFrame = true;
return StackVisitor::Continue;
}
if (callee)
m_result = callee;
return StackVisitor::Done;
}
private:
JSObject* m_targetCallee;
mutable bool m_hasFoundFrame;
mutable bool m_hasSkippedToCallerFrame;
mutable JSValue m_result;
};
static JSValue retrieveCallerFunction(VM& vm, CallFrame* callFrame, JSFunction* functionObj)
{
RetrieveCallerFunctionFunctor functor(functionObj);
if (callFrame)
callFrame->iterate(vm, functor);
return functor.result();
}
// https://github.com/claudepache/es-legacy-function-reflection/blob/master/spec.md#get-functionprototypecaller
JSC_DEFINE_CUSTOM_GETTER(callerGetter, (JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSFunction* thisObj = jsDynamicCast<JSFunction*>(vm, JSValue::decode(thisValue));
if (!thisObj || !isAllowedReceiverFunctionForCallerAndArguments(thisObj))
return throwVMTypeError(globalObject, scope, RestrictedPropertyAccessError);
JSValue caller = retrieveCallerFunction(vm, vm.topCallFrame, thisObj);
if (caller.isNull())
return JSValue::encode(jsNull());
// 11. If caller is not an ECMAScript function object, return null.
JSFunction* function = jsDynamicCast<JSFunction*>(vm, caller);
if (!function || function->isHostOrBuiltinFunction())
return JSValue::encode(jsNull());
// 12. If caller.[[Strict]] is true, return null.
if (function->jsExecutable()->isInStrictContext())
return JSValue::encode(jsNull());
// Prevent bodies (private implementations) of generator / async functions from being exposed.
// They expect to be called by @generatorResume() & friends with certain arguments, and crash otherwise.
// 14. If caller.[[ECMAScriptCode]] is a GeneratorBody, an AsyncFunctionBody, an AsyncGeneratorBody, or an AsyncConciseBody, return null.
SourceParseMode parseMode = function->jsExecutable()->parseMode();
if (isGeneratorParseMode(parseMode) || isAsyncFunctionParseMode(parseMode))
return JSValue::encode(jsNull());
return JSValue::encode(caller);
}
JSC_DEFINE_CUSTOM_SETTER(callerAndArgumentsSetter, (JSGlobalObject* globalObject, EncodedJSValue thisValue, EncodedJSValue, PropertyName))
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSFunction* thisObj = jsDynamicCast<JSFunction*>(vm, JSValue::decode(thisValue));
if (!thisObj || !isAllowedReceiverFunctionForCallerAndArguments(thisObj))
throwTypeError(globalObject, scope, RestrictedPropertyAccessError);
return true;
}
} // namespace JSC