//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Copyright (c) 2022 ChakraCore Project Contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
#include "EngineInterfaceObject.h"
#include "JsBuiltInEngineInterfaceExtensionObject.h"
#include "Types/DeferredTypeHandler.h"

#ifdef ENABLE_JS_BUILTINS
#include "ByteCode/ByteCodeSerializer.h"
#include "errstr.h"
#include "ByteCode/ByteCodeDumper.h"

#define IfFailAssertMsgAndThrowHr(op, msg) \
    if (FAILED(hr=(op))) \
    { \
    AssertMsg(false, msg); \
    JavascriptError::MapAndThrowError(scriptContext, hr); \
    } \

enum class IsTypeStatic : bool
{
    prototype = false,
    constructor = true,
    object = true
};

namespace Js
{

    JsBuiltInEngineInterfaceExtensionObject::JsBuiltInEngineInterfaceExtensionObject(ScriptContext * scriptContext) :
        EngineExtensionObjectBase(EngineInterfaceExtensionKind_JsBuiltIn, scriptContext)
    {
    }

    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterFunction));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterChakraLibraryFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterChakraLibraryFunction));

    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_GetLength(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_GetLength));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_GetIteratorPrototype(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_GetIteratorPrototype));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_InitInternalProperties(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_InitInternalProperties));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToLengthFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToLengthFunction));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToIntegerFunction(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToIntegerFunction));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ArraySpeciesCreate(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ArraySpeciesCreate));
    NoProfileFunctionInfo JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ArrayCreateDataPropertyOrThrow(FORCE_NO_WRITE_BARRIER_TAG(JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ArrayCreateDataPropertyOrThrow));

    void JsBuiltInEngineInterfaceExtensionObject::Initialize()
    {
        if (wasInitialized)
        {
            return;
        }

        JavascriptLibrary* library = scriptContext->GetLibrary();
        DynamicObject* commonObject = library->GetEngineInterfaceObject()->GetCommonNativeInterfaces();
        if (scriptContext->IsJsBuiltInEnabled())
        {
            Assert(library->GetEngineInterfaceObject() != nullptr);
            builtInNativeInterfaces = DynamicObject::New(library->GetRecycler(),
                DynamicType::New(scriptContext, TypeIds_Object, commonObject, nullptr,
                    DeferredTypeHandler<InitializeJsBuiltInNativeInterfaces>::GetDefaultInstance()));
            library->AddMember(library->GetEngineInterfaceObject(), Js::PropertyIds::JsBuiltIn, builtInNativeInterfaces);
        }

        wasInitialized = true;
    }

    void JsBuiltInEngineInterfaceExtensionObject::InitializePrototypes(ScriptContext * scriptContext)
    {
        JavascriptLibrary* library = scriptContext->GetLibrary();
        JavascriptString * methodName = JavascriptString::NewWithSz(_u("ArrayIterator"), scriptContext);
        auto arrayIterator = JavascriptOperators::GetProperty(library->GetChakraLib(), JavascriptOperators::GetPropertyId(methodName, scriptContext), scriptContext);
        library->arrayIteratorPrototype = VarTo<DynamicObject>(JavascriptOperators::GetProperty(VarTo<DynamicObject>(arrayIterator), PropertyIds::prototype, scriptContext));
        library->arrayIteratorPrototypeBuiltinNextFunction = VarTo<JavascriptFunction>(JavascriptOperators::GetProperty(library->arrayIteratorPrototype, PropertyIds::next, scriptContext));
    }

    void JsBuiltInEngineInterfaceExtensionObject::InjectJsBuiltInLibraryCode(ScriptContext * scriptContext, JsBuiltInFile file)
    {
        JavascriptExceptionObject *pExceptionObject = nullptr;
        FunctionBody* jsBuiltInByteCode = nullptr;

        switch (file)
        {
            #define jsBuiltInByteCodeCase(class, type, obj) \
            case (JsBuiltInFile::class##_##type): \
            { \
                if (jsBuiltIn##class##_##type##Bytecode != nullptr) \
                    return; \
                EnsureSourceInfo(); \
                uint32 flags = fscrJsBuiltIn | (CONFIG_FLAG(CreateFunctionProxy) && !scriptContext->IsProfiling() ? fscrAllowFunctionProxy : 0); \
                SRCINFO* hsi = sourceInfo; \
                Js::ByteCodeSerializer::DeserializeFromBuffer(scriptContext, flags, (LPCUTF8)nullptr, hsi, (byte*)js::Library_Bytecode_##class##_##type, nullptr, &jsBuiltIn##class##_##type##Bytecode); \
                jsBuiltInByteCode = jsBuiltIn##class##_##type##Bytecode; \
                break; \
            }
            JsBuiltIns(jsBuiltInByteCodeCase)
            #undef jsBuiltInByteCodeCase
        }
        this->SetHasBytecode();

        try {
            AssertOrFailFast(jsBuiltInByteCode != nullptr);

            Js::ScriptFunction *functionGlobal = scriptContext->GetLibrary()->CreateScriptFunction(jsBuiltInByteCode);

            // If we are in the debugger and doing a GetProperty of arguments, then Js Builtins code will be injected on-demand
            // Since it is a cross site call, we marshall not only functionGlobal but also its entire prototype chain
            // The prototype of functionGlobal will be shared by a normal Js function,
            // so marshalling will inadvertently transition the entrypoint of the prototype to a crosssite entrypoint
            // So we set the prototype to null here
            functionGlobal->SetPrototype(scriptContext->GetLibrary()->nullValue);

#ifdef ENABLE_SCRIPT_PROFILING
            // If we are profiling, we need to register the script to the profiler callback, so the script compiled event will be sent.
            if (scriptContext->IsProfiling())
            {
                scriptContext->RegisterScript(functionGlobal->GetFunctionProxy());
            }
#endif

#ifdef ENABLE_SCRIPT_DEBUGGING
            // Mark we are profiling library code already, so that any initialization library code called here won't be reported to profiler.
            // Also tell the debugger not to record events during intialization so that we don't leak information about initialization.
            AutoInitLibraryCodeScope autoInitLibraryCodeScope(scriptContext);
#endif

            Js::Var args[] = { scriptContext->GetLibrary()->GetUndefined(), scriptContext->GetLibrary()->GetEngineInterfaceObject() };
            Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args));

#if ENABLE_JS_REENTRANCY_CHECK
            // Create a Reentrancy lock to make sure we correctly restore the lock at the end of BuiltIns initialization
            JsReentLock lock(scriptContext->GetThreadContext());
            // Clear ReentrancyLock bit as initialization code doesn't have any side effect
            scriptContext->GetThreadContext()->SetNoJsReentrancy(false);
#endif
            // specify which set of BuiltIns are currently being loaded
            current = file;

            // Clear disable implicit call bit as initialization code doesn't have any side effect
            {
                ThreadContext::AutoRestoreImplicitFlags autoRestoreImplicitFlags(scriptContext->GetThreadContext(), scriptContext->GetThreadContext()->GetImplicitCallFlags(), scriptContext->GetThreadContext()->GetDisableImplicitFlags());
                scriptContext->GetThreadContext()->ClearDisableImplicitFlags();
                JavascriptFunction::CallRootFunctionInScript(functionGlobal, Js::Arguments(callInfo, args));

                Js::ScriptFunction *functionBuiltins = scriptContext->GetLibrary()->CreateScriptFunction(jsBuiltInByteCode->GetNestedFunctionForExecution(0));
                functionBuiltins->SetPrototype(scriptContext->GetLibrary()->nullValue);
                JavascriptFunction::CallRootFunctionInScript(functionBuiltins, Js::Arguments(callInfo, args));
            }

            if (file == JsBuiltInFile::Array_prototype)
            {
                InitializePrototypes(scriptContext);
            }
#if DBG_DUMP
            if (PHASE_DUMP(Js::ByteCodePhase, functionGlobal->GetFunctionProxy()) && Js::Configuration::Global.flags.Verbose)
            {
                DumpByteCode();
            }
#endif
        }
        catch (const JavascriptException& err)
        {
            pExceptionObject = err.GetAndClear();
        }

        if (pExceptionObject)
        {
            switch (file)
            {
                #define clearJsBuiltInByteCodeCase(class, type, obj) \
            case (JsBuiltInFile::class##_##type): \
                jsBuiltIn##class##_##type##Bytecode = nullptr;
                    break;
            JsBuiltIns(clearJsBuiltInByteCodeCase)
            #undef clearJsBuiltInByteCodeCase
            }
            jsBuiltInByteCode = nullptr;
            if (pExceptionObject == ThreadContext::GetContextForCurrentThread()->GetPendingSOErrorObject())
            {
                JavascriptExceptionOperators::DoThrowCheckClone(pExceptionObject, scriptContext);
            }
            Js::Throw::FatalJsBuiltInError();
        }
    }

    bool JsBuiltInEngineInterfaceExtensionObject::InitializeJsBuiltInNativeInterfaces(DynamicObject * builtInNativeInterfaces, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
    {
        int initSlotCapacity = 5; // for register{ChakraLibrary}Function, POSITIVE_INFINITY, NEGATIVE_INFINITY, and GetIteratorPrototype

        typeHandler->Convert(builtInNativeInterfaces, mode, initSlotCapacity);

        ScriptContext* scriptContext = builtInNativeInterfaces->GetScriptContext();
        JavascriptLibrary* library = scriptContext->GetLibrary();

        library->AddMember(builtInNativeInterfaces, PropertyIds::POSITIVE_INFINITY, library->GetPositiveInfinite());
        library->AddMember(builtInNativeInterfaces, PropertyIds::NEGATIVE_INFINITY, library->GetNegativeInfinite());

        library->AddFunctionToLibraryObject(builtInNativeInterfaces, Js::PropertyIds::registerChakraLibraryFunction, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterChakraLibraryFunction, 2);
        library->AddFunctionToLibraryObject(builtInNativeInterfaces, Js::PropertyIds::registerFunction, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_RegisterFunction, 2);
        library->AddFunctionToLibraryObject(builtInNativeInterfaces, Js::PropertyIds::GetIteratorPrototype, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_GetIteratorPrototype, 1);

        builtInNativeInterfaces->SetHasNoEnumerableProperties(true);
        return true;
    }

#if DBG
    void JsBuiltInEngineInterfaceExtensionObject::DumpByteCode(JsBuiltInFile file)
    {
        Output::Print(_u("Dumping JS Built Ins Byte Code:\n"));
        switch (file)
        {
            #define fileCase(class, type, obj) \
            case class##_##type: \
                Assert(this->jsBuiltIn##class##_##type##Bytecode != nullptr); \
                Js::ByteCodeDumper::DumpRecursively(this->jsBuiltIn##class##_##type##Bytecode); \
                break;
            JsBuiltIns(fileCase)
            #undef fileCase
        }
    }

    void JsBuiltInEngineInterfaceExtensionObject::DumpByteCode()
    {
        Output::Print(_u("Dumping JS Built Ins Byte Code:\n"));
        #define dumpOne(class, type, obj) \
        if (this->jsBuiltIn##class##_##type##Bytecode != nullptr) \
        { \
            DumpByteCode(JsBuiltInFile::class##_##type); \
        }
        JsBuiltIns(dumpOne)
        #undef dumpOne
    }
#endif // DBG

    void JsBuiltInEngineInterfaceExtensionObject::EnsureSourceInfo()
    {
        if (sourceInfo == nullptr)
        {
            SourceContextInfo* sourceContextInfo = RecyclerNewStructZ(scriptContext->GetRecycler(), SourceContextInfo);
            sourceContextInfo->dwHostSourceContext = Js::Constants::JsBuiltInSourceContext;
            sourceContextInfo->isHostDynamicDocument = true;
            sourceContextInfo->sourceContextId = Js::Constants::JsBuiltInSourceContextId;

            Assert(sourceContextInfo != nullptr);

            SRCINFO si;
            memset(&si, 0, sizeof(si));
            si.sourceContextInfo = sourceContextInfo;
            sourceInfo = scriptContext->AddHostSrcInfo(&si);
        }
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterChakraLibraryFunction(RecyclableObject* function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);

        AssertOrFailFast(args.Info.Count >= 3 && VarIs<JavascriptString>(args.Values[1]) && VarIs<ScriptFunction>(args.Values[2]));

        JavascriptLibrary * library = scriptContext->GetLibrary();

        JavascriptString* methodName = UnsafeVarTo<JavascriptString>(args.Values[1]);

        // chakra library functions, since they aren't public, can be constructors (__chakraLibrary.ArrayIterator is one)
        ScriptFunction* func = EngineInterfaceObject::CreateLibraryCodeScriptFunction(
            UnsafeVarTo<ScriptFunction>(args.Values[2]),
            methodName,
            true /* isConstructor */,
            true /* isJsBuiltIn */,
            false /* isPublic */
        );

        PropertyIds functionIdentifier = JavascriptOperators::GetPropertyId(methodName, scriptContext);

        library->AddMember(library->GetChakraLib(), functionIdentifier, func);

        //Don't need to return anything
        return library->GetUndefined();
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_RegisterFunction(RecyclableObject* function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);

        AssertOrFailFast(args.Info.Count == 3 && VarIs<ScriptFunction>(args.Values[2]));

        JavascriptLibrary * library = scriptContext->GetLibrary();

        // process function name and get property ID
        JavascriptString* methodString = VarTo<JavascriptString>(args.Values[1]);
        PropertyRecord* methodPropertyRecord = nullptr;
        methodString->GetPropertyRecord((PropertyRecord const **) &methodPropertyRecord, false);
        PropertyId methodPropID = methodPropertyRecord->GetPropertyId();

        DynamicObject *installTarget = nullptr;
        bool isStatic = false;
        PropertyString *classPropString = nullptr;
        JavascriptString *fullName = nullptr;
        JavascriptString *dot = library->GetDotString();
        JsBuiltInFile current = (static_cast<JsBuiltInEngineInterfaceExtensionObject*>(scriptContext->GetLibrary()
            ->GetEngineInterfaceObject()->GetEngineExtension(EngineInterfaceExtensionKind_JsBuiltIn)))->current;

        // determine what object this function is being added too
        switch (current)
        {
            #define file(class, type, obj) \
            case class##_##type: \
                isStatic = static_cast<bool>(IsTypeStatic::##type); \
                installTarget = library->Get##obj##(); \
                classPropString = scriptContext->GetPropertyString(PropertyIds::class); \
                break;
            JsBuiltIns(file)
        }

        Assert(methodString && classPropString && installTarget && methodPropID != PropertyIds::_none);

        if (isStatic)
        {
            fullName = JavascriptString::Concat3(classPropString, dot, methodString);
        }
        else
        {
            JavascriptString *dotPrototypeDot = JavascriptString::Concat3(dot, scriptContext->GetPropertyString(PropertyIds::prototype), dot);
            fullName = JavascriptString::Concat3(classPropString, dotPrototypeDot, methodString);
        }

        ScriptFunction *func = EngineInterfaceObject::CreateLibraryCodeScriptFunction(
            UnsafeVarTo<ScriptFunction>(args.Values[2]),
            fullName,
            false /* isConstructor */,
            true /* isJsBuiltIn */,
            true /* isPublic */
        );

        library->AddMember(installTarget, methodPropID, func);

        // Make function available to other internal facilities that need it
        // applicable for specific functions only - this may need review upon moving other functions into JsBuiltins
        if (current == JsBuiltInFile::Array_prototype)
        {
            switch (methodPropID)
            {
                case PropertyIds::entries:
                    library->arrayPrototypeEntriesFunction = func;
                    break;
                case PropertyIds::values:
                    library->arrayPrototypeValuesFunction = func;
                    library->AddMember(installTarget, PropertyIds::_symbolIterator, func);
                    break;
                case PropertyIds::keys:
                    library->arrayPrototypeKeysFunction = func;
                    break;
                case PropertyIds::forEach:
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInArray_prototype_forEach, func);
                    break;
                case PropertyIds::filter:
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInArray_prototype_filter, func);
                    break;
                case PropertyIds::indexOf:
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInArray_prototype_indexOf, func);
                    break;
                case PropertyIds::reduce:
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInArray_prototype_reduce, func);
                    break;
            }
        }
        else if (current == JsBuiltInFile::Math_object)
        {
            switch (methodPropID)
            {
                case PropertyIds::max:
                    library->mathMax = func;
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInMath_object_max, func);
                    break;
                case PropertyIds::min:
                    library->mathMin = func;
                    library->AddMember(scriptContext->GetLibrary()->GetEngineInterfaceObject()->GetCommonNativeInterfaces(), PropertyIds::builtInMath_object_min, func);
                    break;
            }
        }
        //Don't need to return anything
        return library->GetUndefined();
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToLengthFunction(RecyclableObject * function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
        Assert(args.Info.Count == 2);
        return JavascriptNumber::ToVar(JavascriptConversion::ToLength(args.Values[1], scriptContext), scriptContext);
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ToIntegerFunction(RecyclableObject * function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
        Assert(args.Info.Count == 2);

        Var value = args.Values[1];
        if (JavascriptOperators::IsUndefinedOrNull(value))
        {
            return TaggedInt::ToVarUnchecked(0);
        }
        else if (TaggedInt::Is(value))
        {
            return value;
        }

        return JavascriptNumber::ToVarIntCheck(JavascriptConversion::ToInteger(value, scriptContext), scriptContext);
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_GetLength(RecyclableObject *function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
        Assert(callInfo.Count == 2);

        int64 length;
        Var iterable = args.Values[1];

        TypedArrayBase *typedArrayBase = nullptr;
        Assert(!JavascriptArray::IsNonES5Array(iterable));

        if (VarIs<TypedArrayBase>(iterable))
        {
            typedArrayBase = VarTo<TypedArrayBase>(iterable);
            if (typedArrayBase->IsDetachedBuffer())
            {
                JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray);
            }

            length = typedArrayBase->GetLength();
        }
        else
        {
            length = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(iterable, scriptContext), scriptContext);
        }

        return JavascriptNumber::ToVar(length, scriptContext);
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_GetIteratorPrototype(RecyclableObject *function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);

        Assert(callInfo.Count == 1);

        return scriptContext->GetLibrary()->iteratorPrototype;
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_InitInternalProperties(RecyclableObject *function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);

        DynamicObject* obj = VarTo<DynamicObject>(args.Values[1]);
        unsigned propCount = TaggedInt::ToUInt32(args.Values[2]);

        Assert(callInfo.Count == 3 + propCount);

        for (unsigned i = 0; i < propCount; i++)
        {
            JavascriptString *propName = VarTo<JavascriptString>(args.Values[i + 3]);
            obj->SetPropertyWithAttributes(JavascriptOperators::GetPropertyId(propName, scriptContext), scriptContext->GetLibrary()->GetNull(), PropertyWritable, nullptr);
        }

        return obj;
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ArraySpeciesCreate(RecyclableObject *function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
        AssertOrFailFast(args.Info.Count == 3);

        int64 length64 = JavascriptConversion::ToLength(args.Values[2], scriptContext);
        if (length64 > UINT_MAX)
        {
            JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect);
        }

        uint32 length = static_cast<uint32>(length64);

        bool isBuiltinArrayCtor = true;
        RecyclableObject * newObj = JavascriptArray::ArraySpeciesCreate(args.Values[1], length, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor);
        nullptr;

        if (newObj == nullptr)
        {
            newObj = scriptContext->GetLibrary()->CreateArray(length);
        }
        else
        {
#if ENABLE_COPYONACCESS_ARRAY
            JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(newObj);
#endif
        }

        return newObj;
    }

    Var JsBuiltInEngineInterfaceExtensionObject::EntryJsBuiltIn_Internal_ArrayCreateDataPropertyOrThrow(RecyclableObject *function, CallInfo callInfo, ...)
    {
        EngineInterfaceObject_CommonFunctionProlog(function, callInfo);
        AssertOrFailFast(args.Info.Count == 4);

        RecyclableObject * obj = VarTo<RecyclableObject>(args.Values[1]);
        double index = JavascriptConversion::ToInteger(args.Values[2], scriptContext);
        AssertOrFailFast(index >= 0);
        JavascriptArray::BigIndex bigIndex(static_cast<uint64>(index));
        Var item = args.Values[3];

        JavascriptArray::CreateDataPropertyOrThrow(obj, bigIndex, item, scriptContext);
        return scriptContext->GetLibrary()->GetTrue();
    }
}
#endif // ENABLE_JS_BUILTINS
