Var JavascriptExternalFunction::ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
    {
        RUNTIME_ARGUMENTS(args, callInfo);
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        // Deferred constructors which are not callable fall back to using the RecyclableObject::DefaultEntryPoint. In order to call
        // this function we have to be inside script so all this and return before doing any of the external call preparation.
        if (externalFunction->nativeMethod == Js::RecyclableObject::DefaultExternalEntryPoint)
        {
            return Js::RecyclableObject::DefaultExternalEntryPoint(function, callInfo, args.Values);
        }

        ScriptContext * scriptContext = externalFunction->type->GetScriptContext();

#ifdef ENABLE_DIRECTCALL_TELEMETRY
        DirectCallTelemetry::AutoLogger logger(scriptContext, externalFunction, &args);
#endif

        externalFunction->PrepareExternalCall(&args);

        Var result = nullptr;
        BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext)
        {
            // Don't do stack probe since BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION does that for us already
            result = externalFunction->nativeMethod(function, callInfo, args.Values);
        }
        END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);

        return externalFunction->FinishExternalCall(result);
    }
    Var JavascriptExternalFunction::HandleRecordReplayExternalFunction_Thunk(Js::JavascriptFunction* function, CallInfo& callInfo, Arguments& args, ScriptContext* scriptContext)
    {
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        Var result = nullptr;

        if(scriptContext->ShouldPerformReplayAction())
        {
            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            scriptContext->GetThreadContext()->TTDLog->ReplayExternalCallEvent(externalFunction, args, &result);
        }
        else
        {
            TTDAssert(scriptContext->ShouldPerformRecordAction(), "Check either record/replay before calling!!!");

            TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog;

            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            TTD::NSLogEvents::EventLogEntry* callEvent = elog->RecordExternalCallEvent(externalFunction, scriptContext->GetThreadContext()->TTDRootNestingCount, args, false);

            BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext)
            {
                // Don't do stack probe since BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION does that for us already
                result = externalFunction->nativeMethod(function, callInfo, args.Values);
            }
            END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);

            //Exceptions should be prohibited so no need to do extra work
            elog->RecordExternalCallEvent_Complete(externalFunction, callEvent, result);
        }

        return result;
    }
    Var JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(Js::RecyclableObject* function, CallInfo& callInfo, Arguments& args, ScriptContext* scriptContext)
    {
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        Var result = nullptr;

        if(scriptContext->ShouldPerformReplayAction())
        {
            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            scriptContext->GetThreadContext()->TTDLog->ReplayExternalCallEvent(externalFunction, args.Info.Count, args.Values, &result);
        }
        else
        {
            AssertMsg(scriptContext->ShouldPerformRecordAction(), "Check either record/replay before calling!!!");

            TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog;

            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            TTD::NSLogEvents::EventLogEntry* callEvent = elog->RecordExternalCallEvent(externalFunction, scriptContext->GetThreadContext()->TTDRootNestingCount, args.Info.Count, args.Values, true);

            BEGIN_LEAVE_SCRIPT(scriptContext)
            {
                result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState);
            }
            END_LEAVE_SCRIPT(scriptContext);

            elog->RecordExternalCallEvent_Complete(externalFunction, callEvent, result);
        }

        return result;
    }
    bool __cdecl JavascriptExternalFunction::DeferredLengthInitializer(DynamicObject * instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
    {
        Js::JavascriptLibrary::InitializeFunction<true>(instance, typeHandler, mode);

        JavascriptExternalFunction* object = static_cast<JavascriptExternalFunction*>(instance);

        object->UndeferLength(instance->GetScriptContext());

        return true;
    }
    Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
    {
        RUNTIME_ARGUMENTS(args, callInfo);
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        externalFunction->PrepareExternalCall(&args);

        ScriptContext * scriptContext = externalFunction->type->GetScriptContext();
        AnalysisAssert(scriptContext);
        Var result = NULL;

        BEGIN_LEAVE_SCRIPT(scriptContext)
        {
            result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState);
        }
        END_LEAVE_SCRIPT(scriptContext);

        if (result != nullptr && !Js::TaggedNumber::Is(result))
        {
            if (!Js::RecyclableObject::Is(result))
            {
                Js::Throw::InternalError();
            }

            Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result);

            // For JSRT, we could get result marshalled in different context.
            bool isJSRT = !(scriptContext->GetThreadContext()->GetIsThreadBound());
            if (!isJSRT && obj->GetScriptContext() != scriptContext)
            {
                Js::Throw::InternalError();
            }
        }


        if (scriptContext->HasRecordedException())
        {
            bool considerPassingToDebugger = false;
            JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger);
            if (recordedException != nullptr)
            {
                // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object.
                if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject())
                {
                    throw Js::ScriptAbortException();
                }
                else
                {
                    JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger);
                }
            }
        }

        return externalFunction->FinishExternalCall(result);
    }
    Var JavascriptExternalFunction::ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
    {
        ARGUMENTS(args, callInfo);
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        ScriptContext * scriptContext = externalFunction->type->GetScriptContext();

#ifdef ENABLE_DIRECTCALL_TELEMETRY
        DirectCallTelemetry::AutoLogger logger(scriptContext, externalFunction, &args);
#endif

        externalFunction->PrepareExternalCall(&args);

#if ENABLE_TTD
        Var result = nullptr;

        if(scriptContext->ShouldPerformRecordOrReplayAction())
        {
            result = JavascriptExternalFunction::HandleRecordReplayExternalFunction_Thunk(externalFunction, callInfo, args, scriptContext);
        }
        else
        {
            BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext)
            {
                // Don't do stack probe since BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION does that for us already
                result = externalFunction->nativeMethod(function, callInfo, args.Values);
            }
            END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);
        }
#else
        Var result = nullptr;
        BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext)
        {
            // Don't do stack probe since BEGIN_LEAVE_SCRIPT_WITH_EXCEPTION does that for us already
            result = externalFunction->nativeMethod(function, callInfo, args.Values);
        }
        END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);
#endif

        if (result == nullptr)
        {
#pragma warning(push)
#pragma warning(disable:6011) // scriptContext cannot be null here
            result = scriptContext->GetLibrary()->GetUndefined();
#pragma warning(pop)
        }
        else
        {
            result = CrossSite::MarshalVar(scriptContext, result);
        }

        return result;
    }
    void __cdecl JavascriptExternalFunction::DeferredInitializer(DynamicObject* instance, DeferredTypeHandlerBase* typeHandler, DeferredInitializeMode mode)
    {
        JavascriptExternalFunction* object = static_cast<JavascriptExternalFunction*>(instance);
        HRESULT hr = E_FAIL;

        ScriptContext* scriptContext = object->GetScriptContext();
        AnalysisAssert(scriptContext);
        // Don't call the implicit call if disable implicit call
        if (scriptContext->GetThreadContext()->IsDisableImplicitCall())
        {
            scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_External);
            //we will return if we get call further into implicitcalls.
            return;
        }

        if (scriptContext->IsClosed() || scriptContext->IsInvalidatedForHostObjects())
        {
            Js::JavascriptError::MapAndThrowError(scriptContext, E_ACCESSDENIED);
        }
        ThreadContext* threadContext = scriptContext->GetThreadContext();

        typeHandler->Convert(instance, mode, object->typeSlots, object->hasAccessors);

        BEGIN_LEAVE_SCRIPT_INTERNAL(scriptContext)
        {
            ASYNC_HOST_OPERATION_START(threadContext);

            hr = object->initMethod(instance);

            ASYNC_HOST_OPERATION_END(threadContext);
        }
        END_LEAVE_SCRIPT_INTERNAL(scriptContext);

        if (FAILED(hr))
        {
            Js::JavascriptError::MapAndThrowError(scriptContext, hr);
        }

        JavascriptString * functionName = nullptr;
        if (scriptContext->GetConfig()->IsES6FunctionNameEnabled() &&
            object->GetFunctionName(&functionName))
        {
            object->SetPropertyWithAttributes(PropertyIds::name, functionName, PropertyConfigurable, nullptr);
        }

    }
    Var JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(Js::RecyclableObject* function, CallInfo& callInfo, Arguments& args, ScriptContext* scriptContext)
    {
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        Var result = nullptr;

        if(scriptContext->ShouldPerformReplayAction())
        {
            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            scriptContext->GetThreadContext()->TTDLog->ReplayExternalCallEvent(externalFunction, args, &result);
        }
        else
        {
            if (args.Info.Count > USHORT_MAX)
            {
                // Due to compat reasons, stdcall external functions expect a ushort count of args.
                // To support more than this we will need a new API.
                Js::JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgListTooLarge);
            }

            TTDAssert(scriptContext->ShouldPerformRecordAction(), "Check either record/replay before calling!!!");

            TTD::EventLog* elog = scriptContext->GetThreadContext()->TTDLog;

            TTD::TTDNestingDepthAutoAdjuster logPopper(scriptContext->GetThreadContext());
            TTD::NSLogEvents::EventLogEntry* callEvent = elog->RecordExternalCallEvent(externalFunction, scriptContext->GetThreadContext()->TTDRootNestingCount, args, true);

            StdCallJavascriptMethodInfo info = {
                args[0],
                args.HasNewTarget() ? args.GetNewTarget() : args.IsNewCall() ? function : scriptContext->GetLibrary()->GetUndefined(),
                args.IsNewCall()
            };

            BEGIN_LEAVE_SCRIPT(scriptContext)
            {
                result = externalFunction->stdCallNativeMethod(function, args.Values, static_cast<ushort>(args.Info.Count), &info, externalFunction->callbackState);
            }
            END_LEAVE_SCRIPT(scriptContext);

            elog->RecordExternalCallEvent_Complete(externalFunction, callEvent, result);
        }

        return result;
    }
    Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
    {
        ARGUMENTS(args, callInfo);
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        externalFunction->PrepareExternalCall(&args);

        ScriptContext * scriptContext = externalFunction->type->GetScriptContext();
        AnalysisAssert(scriptContext);

        if (args.Info.Count > USHORT_MAX)
        {
            // Due to compat reasons, stdcall external functions expect a ushort count of args.
            // To support more than this we will need a new API.
            Js::JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgListTooLarge);
        }

        Var result = nullptr;
        Assert(callInfo.Count > 0);

        StdCallJavascriptMethodInfo info = {
            args[0],
            args.HasNewTarget() ? args.GetNewTarget() : args.IsNewCall() ? function : scriptContext->GetLibrary()->GetUndefined(),
            args.IsNewCall()
        };

#if ENABLE_TTD
        if(scriptContext->ShouldPerformRecordOrReplayAction())
        {
            result = JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(function, callInfo, args, scriptContext);
        }
        else
        {
            BEGIN_LEAVE_SCRIPT(scriptContext)
            {
                result = externalFunction->stdCallNativeMethod(function, args.Values, static_cast<USHORT>(args.Info.Count), &info, externalFunction->callbackState);
            }
            END_LEAVE_SCRIPT(scriptContext);
        }
#else
        BEGIN_LEAVE_SCRIPT(scriptContext)
        {
            result = externalFunction->stdCallNativeMethod(function, args.Values, static_cast<USHORT>(args.Info.Count), &info, externalFunction->callbackState);
        }
        END_LEAVE_SCRIPT(scriptContext);
#endif

        bool marshallingMayBeNeeded = false;
        if (result != nullptr)
        {
            marshallingMayBeNeeded = Js::RecyclableObject::Is(result);
            if (marshallingMayBeNeeded)
            {
                Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result);

                // For JSRT, we could get result marshalled in different context.
                bool isJSRT = scriptContext->GetThreadContext()->IsJSRT();
                marshallingMayBeNeeded = obj->GetScriptContext() != scriptContext;
                if (!isJSRT && marshallingMayBeNeeded)
                {
                    Js::Throw::InternalError();
                }
            }
        }

        if (scriptContext->HasRecordedException())
        {
            bool considerPassingToDebugger = false;
            JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger);
            if (recordedException != nullptr)
            {
                // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object.
                if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject())
                {
                    throw Js::ScriptAbortException();
                }
                else
                {
                    JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger);
                }
            }
        }

        if (result == nullptr)
        {
            result = scriptContext->GetLibrary()->GetUndefined();
        }
        else if (marshallingMayBeNeeded)
        {
            result = CrossSite::MarshalVar(scriptContext, result);
        }

        return result;
    }
    Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
    {
        ARGUMENTS(args, callInfo);
        JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);

        externalFunction->PrepareExternalCall(&args);

        ScriptContext * scriptContext = externalFunction->type->GetScriptContext();
        AnalysisAssert(scriptContext);
        Var result = NULL;

#if ENABLE_TTD
        if(scriptContext->ShouldPerformRecordOrReplayAction())
        {
            result = JavascriptExternalFunction::HandleRecordReplayExternalFunction_StdThunk(function, callInfo, args, scriptContext);
        }
        else
        {
            BEGIN_LEAVE_SCRIPT(scriptContext)
            {
                result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState);
            }
            END_LEAVE_SCRIPT(scriptContext);
        }
#else
        BEGIN_LEAVE_SCRIPT(scriptContext)
        {
            result = externalFunction->stdCallNativeMethod(function, ((callInfo.Flags & CallFlags_New) != 0), args.Values, args.Info.Count, externalFunction->callbackState);
        }
        END_LEAVE_SCRIPT(scriptContext);
#endif

        if (result != nullptr && !Js::TaggedNumber::Is(result))
        {
            if (!Js::RecyclableObject::Is(result))
            {
                Js::Throw::InternalError();
            }

            Js::RecyclableObject * obj = Js::RecyclableObject::FromVar(result);

            // For JSRT, we could get result marshalled in different context.
            bool isJSRT = scriptContext->GetThreadContext()->IsJSRT();
            if (!isJSRT && obj->GetScriptContext() != scriptContext)
            {
                Js::Throw::InternalError();
            }
        }

        if (scriptContext->HasRecordedException())
        {
            bool considerPassingToDebugger = false;
            JavascriptExceptionObject* recordedException = scriptContext->GetAndClearRecordedException(&considerPassingToDebugger);
            if (recordedException != nullptr)
            {
                // If this is script termination, then throw ScriptAbortExceptio, else throw normal Exception object.
                if (recordedException == scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject())
                {
                    throw Js::ScriptAbortException();
                }
                else
                {
                    JavascriptExceptionOperators::RethrowExceptionObject(recordedException, scriptContext, considerPassingToDebugger);
                }
            }
        }

        if (result == nullptr)
        {
            result = scriptContext->GetLibrary()->GetUndefined();
        }
        else
        {
            result = CrossSite::MarshalVar(scriptContext, result);
        }

        return result;
    }