Hereâs the method.
Var JavascriptFunction::EntryCall(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
RUNTIME_ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();
Assert(!(callInfo.Flags & CallFlags_New));
///
/// Check Argument[0] has internal [[Call]] property
/// If not, throw TypeError
///
if (args.Info.Count == 0 || !JavascriptConversion::IsCallable(args[0]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.call"));
}
RecyclableObject *pFunc = RecyclableObject::FromVar(args[0]);
if (args.Info.Count == 1)
{
args.Values[0] = scriptContext->GetLibrary()->GetUndefined();
}
else
{
///
/// Remove function object from the arguments and pass the rest
///
for (uint i = 0; i < args.Info.Count - 1; ++i)
{
args.Values[i] = args.Values[i + 1];
}
args.Info.Count = args.Info.Count - 1;
}
///
/// Call the [[Call]] method on the function object
///
return JavascriptFunction::CallFunction<true>(pFunc, pFunc->GetEntryPoint(), args);
}
Chakra uses the first value of âargs.Valuesâ as âthisâ and âargs.Info.Count - 1â as the length of the arguments. So âargs.Info.Countâ must always be 1 or greater.
But the problem is that the method decrements âargs.Info.Countâ by one without considering the flag âCallFlags_ExtraArgâ. If the flag is set, the value of âargs.Info.Countâ will be decremented again in the next routine(ArgumentReader::AdjustArguments) because the last value of the arguments is not used as an actual argument. Therefore, the value of âargs.Info.Countâ becomes 0.
function f() {
print(arguments);
}
let call = new Proxy(Function.prototype.call, {}); // proxy calls set the flag
call.call(f);
function f() {
print(arguments);
}
let call = new Proxy(Function.prototype.call, {}); // proxy calls set the flag
call.call(f);