예제 #1
0
void CodeGenWorkItem::OnAddToJitQueue()
{
    Assert(!this->isInJitQueue);
    this->isInJitQueue = true;
    VerifyJitMode();

    this->entryPointInfo->SetCodeGenQueued();
    if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_QUEUED()))
    {
        WCHAR displayNameBuffer[256];
        WCHAR* displayName = displayNameBuffer;
        size_t sizeInChars = this->GetDisplayName(displayName, 256);
        if(sizeInChars > 256)
        {
            displayName = HeapNewArray(WCHAR, sizeInChars);
            this->GetDisplayName(displayName, 256);
        }
        JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_QUEUED(
            this->GetFunctionNumber(),
            displayName,
            this->GetScriptContext(),
            this->GetInterpretedCount()));

        if(displayName != displayNameBuffer)
        {
            HeapDeleteArray(sizeInChars, displayName);
        }
    }
}
예제 #2
0
    SnapShot* SnapShot::Parse(LPCWSTR sourceDir, DWORD snapId, ThreadContext* threadContext)
    {
        wchar* snapIdString = HeapNewArrayZ(wchar, 64);
        swprintf_s(snapIdString, 64, _u("%u"), snapId);

        HANDLE snapHandle = threadContext->TTDStreamFunctions.pfGetSnapshotStream(sourceDir, snapIdString, true, false);

        TTD_SNAP_READER snapreader(snapHandle, TTD_COMPRESSED_OUTPUT, threadContext->TTDStreamFunctions.pfReadBytesFromStream, threadContext->TTDStreamFunctions.pfFlushAndCloseStream);
        SnapShot* snap = SnapShot::ParseSnapshotFromFile(&snapreader);

        HeapDeleteArray(64, snapIdString);

        return snap;
    }
예제 #3
0
    void SnapShot::EmitSnapshot(LPCWSTR sourceDir, DWORD snapId, ThreadContext* threadContext) const
    {
        wchar* snapIdString = HeapNewArrayZ(wchar, 64);
        swprintf_s(snapIdString, 64, _u("%u"), snapId);

        HANDLE snapHandle = threadContext->TTDStreamFunctions.pfGetSnapshotStream(sourceDir, snapIdString, false, true);

        TTD_SNAP_WRITER snapwriter(snapHandle, TTD_COMPRESSED_OUTPUT, threadContext->TTDStreamFunctions.pfWriteBytesToStream, threadContext->TTDStreamFunctions.pfFlushAndCloseStream);

        this->EmitSnapshotToFile(&snapwriter, threadContext);
        snapwriter.FlushAndClose();

        HeapDeleteArray(64, snapIdString);
    }
예제 #4
0
파일: Jobs.cpp 프로젝트: dilijev/ChakraCore
    BackgroundJobProcessor::~BackgroundJobProcessor()
    {
        // This should appear to be called from the same thread from which this instance was created
        Assert(IsClosed());

        if (parallelThreadData)
        {
            for (unsigned int i = 0; i < this->threadCount; i++)
            {
                HeapDelete(parallelThreadData[i]);
            }
            HeapDeleteArray(this->maxThreadCount, parallelThreadData);
        }
    }
예제 #5
0
//
// Log loop body load event to VTune 
//
void VTuneChakraProfile::LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopHeader* loopHeader, Js::LoopEntryPointInfo* entryPoint, uint16 loopNumber)
{
#if ENABLE_NATIVE_CODEGEN
    if (isJitProfilingActive)
    {
        iJIT_Method_Load methodInfo;
        memset(&methodInfo, 0, sizeof(iJIT_Method_Load));
        const char16* methodName = body->GetExternalDisplayName();
        size_t methodLength = wcslen(methodName);
        methodLength = min(methodLength, (size_t)UINT_MAX); // Just truncate if it is too big
        size_t length = methodLength * 3 + /* spaces */ 2 + _countof(LoopStr) + /*size of loop number*/ 10 + /*NULL*/ 1;
        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length);
        if(utf8MethodName)
        {
            methodInfo.method_id = iJIT_GetNewMethodID();
            size_t len = utf8::EncodeInto(utf8MethodName, methodName, (charcount_t)methodLength);
            sprintf_s((char*)(utf8MethodName + len), length - len," %s %d", LoopStr, loopNumber + 1);
            methodInfo.method_name = (char*)utf8MethodName;
            methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress();
            methodInfo.method_size = (uint)entryPoint->GetCodeSize();        // Size in memory - Must be exact

            size_t urlLength  = 0;
            utf8char_t* utf8Url = GetUrl(body, &urlLength);
            methodInfo.source_file_name = (char*)utf8Url;

            iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo);
            OUTPUT_TRACE(Js::ProfilerPhase, _u("Loop body load event: %s Loop %d\n"), methodName, loopNumber + 1);

            if(urlLength > 0)
            {
                HeapDeleteArray(urlLength, utf8Url);
            }
            HeapDeleteArray(length, utf8MethodName);
        }
    }
#endif
}
예제 #6
0
void CodeGenWorkItem::OnRemoveFromJitQueue(NativeCodeGenerator* generator)
{
    // This is called from within the lock

    this->isInJitQueue = false;
    this->entryPointInfo->SetCodeGenPending();
    functionBody->GetScriptContext()->GetThreadContext()->UnregisterCodeGenRecyclableData(this->recyclableData);
    this->recyclableData = nullptr;

    if(IS_JS_ETW(EventEnabledJSCRIPT_FUNCTION_JIT_DEQUEUED()))
    {
        WCHAR displayNameBuffer[256];
        WCHAR* displayName = displayNameBuffer;
        size_t sizeInChars = this->GetDisplayName(displayName, 256);
        if(sizeInChars > 256)
        {
            displayName = HeapNewArray(WCHAR, sizeInChars);
            this->GetDisplayName(displayName, 256);
        }
        JS_ETW(EventWriteJSCRIPT_FUNCTION_JIT_DEQUEUED(
            this->GetFunctionNumber(),
            displayName,
            this->GetScriptContext(),
            this->GetInterpretedCount()));

        if(displayName != displayNameBuffer)
        {
            HeapDeleteArray(sizeInChars, displayName);
        }
    }

    if(this->Type() == JsLoopBodyWorkItemType)
    {
        // Go ahead and delete it and let it re-queue if more interpreting of the loop happens
        auto loopBodyWorkItem = static_cast<JsLoopBodyCodeGen*>(this);
        loopBodyWorkItem->loopHeader->ResetInterpreterCount();
        loopBodyWorkItem->GetEntryPoint()->Reset();
        HeapDelete(loopBodyWorkItem);
    }
    else
    {
        Assert(GetJitMode() == ExecutionMode::FullJit); // simple JIT work items are not removed from the queue

        GetFunctionBody()->OnFullJitDequeued(static_cast<Js::FunctionEntryPointInfo *>(GetEntryPoint()));

        // Add it back to the list of available functions to be jitted
        generator->AddWorkItem(this);
    }
}
예제 #7
0
    void SharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
    {
        Unused(maxLength); // WebAssembly only
#if ENABLE_FAST_ARRAYBUFFER
        //AsmJS Virtual Free
        if (this->IsValidVirtualBufferLength(length))
        {
            FreeMemAlloc(buffer);
        }
        else
#endif
        {
            HeapDeleteArray(length, buffer);
        }
    }
예제 #8
0
    void WebAssemblySharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
    {
        if (IsValidVirtualBufferLength(length))
        {
            FreeMemAlloc(buffer);
        }
        else
        {
            HeapDeleteArray(maxLength, buffer);

            AssertOrFailFast(maxLength >= length);
            // JavascriptSharedArrayBuffer::Finalize will only report freeing `length`, we have to take care of the balance
            uint32 additionalSize = maxLength - length;
            if (additionalSize > 0)
            {
                Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
                recycler->ReportExternalMemoryFree(additionalSize);
            }
        }
    }
예제 #9
0
파일: Jobs.cpp 프로젝트: dilijev/ChakraCore
    void BackgroundJobProcessor::InitializeParallelThreadDataForThreadServiceCallBack(AllocationPolicyManager* policyManager)
    {
        //thread is provided by service callback, no need to create thread here. Currently only one thread in service callback supported.
        this->maxThreadCount = 1;
        this->parallelThreadData = HeapNewArrayZ(ParallelThreadData *, this->maxThreadCount);

        this->parallelThreadData[0] = HeapNewNoThrow(ParallelThreadData, policyManager);
        if (this->parallelThreadData[0] == nullptr)
        {
            HeapDeleteArray(this->maxThreadCount, this->parallelThreadData);
            Js::Throw::OutOfMemory();
        }
        this->parallelThreadData[0]->processor = this;
        this->parallelThreadData[0]->isWaitingForJobs = true;
#if DBG_DUMP
        this->parallelThreadData[0]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor");
#endif
        this->threadCount = 1;

        return;
    }
    void JavascriptExternalFunction::PrepareExternalCall(Js::Arguments * args)
    {
        ScriptContext * scriptContext = this->type->GetScriptContext();
        Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException());
        scriptContext->VerifyAlive();

        Assert(scriptContext->GetThreadContext()->IsScriptActive());

        if (args->Info.Count == 0)
        {
            JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined);
        }

        Var &thisVar = args->Values[0];

        Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar);

        this->callCount++;
        if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START()))
        {
            JavascriptFunction* caller = nullptr;

            // Lot the caller function if the call count of the external function pass certain threshold (randomly pick 256)
            // we don't want to call stackwalk too often. The threshold can be adjusted as needed.
            if (callCount >= ETW_MIN_COUNT_FOR_CALLER && ((callCount % ETW_MIN_COUNT_FOR_CALLER) == 0))
            {
                Js::JavascriptStackWalker stackWalker(scriptContext);
                bool foundScriptCaller = false;
                while(stackWalker.GetCaller(&caller))
                {
                    if(caller != nullptr && Js::ScriptFunction::Is(caller))
                    {
                        foundScriptCaller = true;
                        break;
                    }
                }
                if(foundScriptCaller)
                {
                    Var sourceString = caller->EnsureSourceString();
                    Assert(JavascriptString::Is(sourceString));
                    const char16* callerString = Js::JavascriptString::FromVar(sourceString)->GetSz();
                    char16* outString = (char16*)callerString;
                    int length = 0;
                    if (wcschr(callerString, _u('\n')) != NULL || wcschr(callerString, _u('\n')) != NULL)
                    {
                        length = Js::JavascriptString::FromVar(sourceString)->GetLength();
                        outString = HeapNewArray(char16, length+1);
                        int j = 0;
                        for (int i = 0; i < length; i++)
                        {
                            if (callerString[i] != _u('\n') && callerString[i] != L'\r')
                            {
                                outString[j++] = callerString[i];
                            }
                        }
                        outString[j] = L'\0';
                    }
                    JS_ETW(EventWriteJSCRIPT_HOSTING_CALLER_TO_EXTERNAL(scriptContext, this, typeId, outString, callCount));
                    if (outString != callerString)
                    {
                        HeapDeleteArray(length+1, outString);
                    }
#if DBG_DUMP
                    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase))
                    {
                        Output::Print(_u("Large number of Call to trampoline: methodAddr= %p, Object typeid= %d, caller method= %s, callcount= %d\n"),
                            this, typeId, callerString, callCount);
                    }
#endif
                }
            }
            JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START(scriptContext, this, typeId));
#if DBG_DUMP
            if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase))
            {
                Output::Print(_u("Call to trampoline: methodAddr= %p, Object typeid= %d\n"), this, typeId);
            }
#endif
        }

        Js::RecyclableObject* directHostObject = nullptr;
        switch(typeId)
        {
        case TypeIds_Integer:
#if FLOATVAR
        case TypeIds_Number:
#endif // FLOATVAR
            Assert(!Js::RecyclableObject::Is(thisVar));
            break;
        default:
            {
                Assert(Js::RecyclableObject::Is(thisVar));

                ScriptContext* scriptContextThisVar = Js::RecyclableObject::FromVar(thisVar)->GetScriptContext();
                // We need to verify "this" pointer is active as well. The problem is that DOM prototype functions are
                // the same across multiple frames, and caller can do function.call(closedthis)
                Assert(!scriptContext->GetThreadContext()->IsDisableImplicitException());
                scriptContextThisVar->VerifyAlive();

                // translate direct host for fastDOM.
                switch(typeId)
                {
                case Js::TypeIds_GlobalObject:
                    {
                        Js::GlobalObject* srcGlobalObject = static_cast<Js::GlobalObject*>(thisVar);
                        directHostObject = srcGlobalObject->GetDirectHostObject();
                        // For jsrt, direct host object can be null. If thats the case don't change it.
                        if (directHostObject != nullptr)
                        {
                            thisVar = directHostObject;
                        }

                    }
                    break;
                case Js::TypeIds_Undefined:
                case Js::TypeIds_Null:
                    {
                        // Call to DOM function with this as "undefined" or "null"
                        // This should be converted to Global object
                        Js::GlobalObject* srcGlobalObject = scriptContextThisVar->GetGlobalObject() ;
                        directHostObject = srcGlobalObject->GetDirectHostObject();
                        // For jsrt, direct host object can be null. If thats the case don't change it.
                        if (directHostObject != nullptr)
                        {
                            thisVar = directHostObject;
                        }
                    }
                    break;
                }
            }
            break;
        }
    }
예제 #11
0
//
// Log JIT method native load event to VTune 
//
void VTuneChakraProfile::LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::FunctionEntryPointInfo* entryPoint)
{
#if ENABLE_NATIVE_CODEGEN
    if (isJitProfilingActive)
    {
        iJIT_Method_Load methodInfo;
        memset(&methodInfo, 0, sizeof(iJIT_Method_Load));
        const char16* methodName = body->GetExternalDisplayName();
        // Append function line number info to method name so that VTune can distinguish between polymorphic methods
        char16 methodNameBuffer[_MAX_PATH];
        ULONG lineNumber = body->GetLineNumber();
        char16 numberBuffer[20];
        _ltow_s(lineNumber, numberBuffer, 10);
        wcscpy_s(methodNameBuffer, methodName);
        if (entryPoint->GetJitMode() == ExecutionMode::SimpleJit)
        {
            wcscat_s(methodNameBuffer, _u(" Simple"));
        }
        wcscat_s(methodNameBuffer, _u(" {line:"));
        wcscat_s(methodNameBuffer, numberBuffer);
        wcscat_s(methodNameBuffer, _u("}"));

        size_t methodLength = wcslen(methodNameBuffer);
        Assert(methodLength < _MAX_PATH);
        size_t length = methodLength * 3 + 1;
        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length);
        if (utf8MethodName)
        {
            methodInfo.method_id = iJIT_GetNewMethodID();
            utf8::EncodeIntoAndNullTerminate(utf8MethodName, methodNameBuffer, (charcount_t)methodLength);
            methodInfo.method_name = (char*)utf8MethodName;
            methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress();
            methodInfo.method_size = (uint)entryPoint->GetCodeSize();        // Size in memory - Must be exact

            LineNumberInfo numberInfo[1];

            uint lineCount = (entryPoint->GetNativeOffsetMapCount()) * 2 + 1; // may need to record both .begin and .end for all elements
            LineNumberInfo* pLineInfo = HeapNewNoThrowArray(LineNumberInfo, lineCount);

            if (pLineInfo == NULL || Js::Configuration::Global.flags.DisableVTuneSourceLineInfo)
            {
                // resort to original implementation, attribute all samples to first line
                numberInfo[0].LineNumber = lineNumber;
                numberInfo[0].Offset = 0;
                methodInfo.line_number_size = 1;
                methodInfo.line_number_table = numberInfo;
            }
            else
            {
                int size = entryPoint->PopulateLineInfo(pLineInfo, body);
                methodInfo.line_number_size = size;
                methodInfo.line_number_table = pLineInfo;
            }

            size_t urlLength = 0;
            utf8char_t* utf8Url = GetUrl(body, &urlLength);
            methodInfo.source_file_name = (char*)utf8Url;
            OUTPUT_TRACE(Js::ProfilerPhase, _u("Method load event: %s\n"), methodNameBuffer);
            iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED, &methodInfo);

            HeapDeleteArray(lineCount, pLineInfo);

            if (urlLength > 0)
            {
                HeapDeleteArray(urlLength, utf8Url);
            }

            HeapDeleteArray(length, utf8MethodName);
        }
    }
#endif
}
예제 #12
0
파일: Jobs.cpp 프로젝트: dilijev/ChakraCore
    void BackgroundJobProcessor::InitializeParallelThreadData(AllocationPolicyManager* policyManager, bool disableParallelThreads)
    {
        if (!disableParallelThreads)
        {
            InitializeThreadCount();
        }
        else
        {
            this->maxThreadCount = 1;
        }

        Assert(this->maxThreadCount >= 1);
        this->parallelThreadData = HeapNewArrayZ(ParallelThreadData*, this->maxThreadCount);

        for (uint i = 0; i < this->maxThreadCount; i++)
        {
            this->parallelThreadData[i] = HeapNewNoThrow(ParallelThreadData, policyManager);

            if (this->parallelThreadData[i] == nullptr)
            {
                if (i == 0)
                {
                    HeapDeleteArray(this->maxThreadCount, this->parallelThreadData);
                    Js::Throw::OutOfMemory();
                }
                // At least one thread is created, continue
                break;
            }

            this->parallelThreadData[i]->processor = this;
            // Make sure to create the thread suspended so the thread handle can be assigned before the thread starts running
            this->parallelThreadData[i]->threadHandle = reinterpret_cast<HANDLE>(PlatformAgnostic::Thread::Create(0, &StaticThreadProc, this->parallelThreadData[i], PlatformAgnostic::Thread::ThreadInitCreateSuspended));
            if (!this->parallelThreadData[i]->threadHandle)
            {
                HeapDelete(parallelThreadData[i]);
                parallelThreadData[i] = nullptr;
                if (i == 0)
                {
                    Js::Throw::OutOfMemory();
                }
                // At least one thread is created, continue
                break;
            }

            if (ResumeThread(this->parallelThreadData[i]->threadHandle) == static_cast<DWORD>(-1))
            {
                CloseHandle(this->parallelThreadData[i]->threadHandle);
                HeapDelete(parallelThreadData[i]);
                this->parallelThreadData[i] = nullptr;

                if (i == 0)
                {
                    Js::Throw::OutOfMemory();
                }
                // At least one thread is created, continue
                break;
            }

            this->threadCount++;

            // Wait for the thread to fully start. This is necessary because Close may be called before the thread starts and if
            // Close is called while holding the loader lock during DLL_THREAD_DETACH, the thread may be stuck waiting for the
            // loader lock for DLL_THREAD_ATTACH to start up, and Close would then end up waiting forever, causing a deadlock.
            WaitWithThreadForThreadStartedOrClosingEvent(this->parallelThreadData[i]);
            this->parallelThreadData[i]->threadStartedOrClosing.Reset(); // after this, the event will be used to wait for the thread to close

#if DBG_DUMP
            if (i < (sizeof(DebugThreadNames) / sizeof(DebugThreadNames[i])))
            {
                this->parallelThreadData[i]->backgroundPageAllocator.debugName = DebugThreadNames[i];
            }
            else
            {
                this->parallelThreadData[i]->backgroundPageAllocator.debugName = _u("BackgroundJobProcessor thread");
            }
#endif
        }

        Assert(this->threadCount >= 1);
     }
예제 #13
0
    Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, const T (&laneValues)[N], 
        CallInfo* callInfo, ScriptContext* scriptContext) const
    {
        Assert(args);
        Assert(N == 4 || N == 8 || N == 16);
        if (typeDescriptor == TypeIds_SIMDBool8x16 ||
            typeDescriptor == TypeIds_SIMDBool16x8 ||
            typeDescriptor == TypeIds_SIMDBool32x4)
        {
            return ToString(scriptContext);   //Boolean types does not have toLocaleString.
        }

        // Creating a new arguments list for the JavascriptNumber generated from each lane.The optional SIMDToLocaleString Args are
        //added to this argument list. 
        Var* newArgs = HeapNewArray(Var, numArgs);
        switch (numArgs)
        {
        case 1:
            break;
        case 2:
            newArgs[1] = args[1];
            break;
        case 3:
            newArgs[1] = args[1];
            newArgs[2] = args[2];
            break;
        default:
            Assert(UNREACHED);
        }
        //Locale specifc seperator?? 
        JavascriptString *seperator = JavascriptString::NewWithSz(_u(", "), scriptContext);
        uint idx = 0;
        Var laneVar = nullptr;
        BEGIN_TEMP_ALLOCATOR(tempAllocator, scriptContext, _u("fromCodePoint"));
        char16* stringBuffer = AnewArray(tempAllocator, char16, SIMD_STRING_BUFFER_MAX);
        JavascriptString *result = nullptr;

        swprintf_s(stringBuffer, 1024, typeString);
        result = JavascriptString::NewCopySzFromArena(stringBuffer, scriptContext, scriptContext->GeneralAllocator());

        if (typeDescriptor == TypeIds_SIMDFloat32x4)
        {
            for (; idx < numLanes - 1; ++idx)
            {
                laneVar = JavascriptNumber::ToVarWithCheck(laneValues[idx], scriptContext);
                newArgs[0] = laneVar;
                JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext);
                result = JavascriptString::Concat(result, laneValue);
                result = JavascriptString::Concat(result, seperator);
            }
            laneVar = JavascriptNumber::ToVarWithCheck(laneValues[idx], scriptContext);
            newArgs[0] = laneVar;
            result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext));
        }
        else if (typeDescriptor == TypeIds_SIMDInt8x16 || typeDescriptor == TypeIds_SIMDInt16x8 || typeDescriptor == TypeIds_SIMDInt32x4)
        {
            for (; idx < numLanes - 1; ++idx)
            {
                laneVar = JavascriptNumber::ToVar(static_cast<int>(laneValues[idx]), scriptContext); 
                newArgs[0] = laneVar;
                JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext);
                result = JavascriptString::Concat(result, laneValue);
                result = JavascriptString::Concat(result, seperator);
            }
            laneVar = JavascriptNumber::ToVar(static_cast<int>(laneValues[idx]), scriptContext);
            newArgs[0] = laneVar;
            result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext));
        }
        else
        {
            Assert((typeDescriptor == TypeIds_SIMDUint8x16 || typeDescriptor == TypeIds_SIMDUint16x8 || typeDescriptor == TypeIds_SIMDUint32x4));
            for (; idx < numLanes - 1; ++idx)
            {
                laneVar = JavascriptNumber::ToVar(static_cast<uint>(laneValues[idx]), scriptContext); 
                newArgs[0] = laneVar;
                JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext);
                result = JavascriptString::Concat(result, laneValue);
                result = JavascriptString::Concat(result, seperator);
            }
            laneVar = JavascriptNumber::ToVar(static_cast<uint>(laneValues[idx]), scriptContext);
            newArgs[0] = laneVar;
            result = JavascriptString::Concat(result, JavascriptNumber::ToLocaleStringIntl(newArgs, *callInfo, scriptContext));
        }
        HeapDeleteArray(numArgs, newArgs);
        END_TEMP_ALLOCATOR(tempAllocator, scriptContext);
        return JavascriptString::Concat(result, JavascriptString::NewWithSz(_u(")"), scriptContext));
    }