Просмотр исходного кода

[1.6>master] [MERGE #3137 @leirocks] implement $262 for Atomics test262 tests

Merge pull request #3137 from leirocks:$262_1.6

also exposes SharedArrayBuffer APIs for JSRT
and couple fixes for Atomics dealing with the index input argument
all Atomics test262 tests passed
Lei Shi 8 лет назад
Родитель
Сommit
d3b6d103bb

+ 4 - 1
bin/ChakraCore/ChakraCore.def

@@ -2,7 +2,6 @@ LIBRARY ChakraCore
 
 EXPORTS
 #include "JsrtCommonExports.inc"
-
 JsDiagEvaluate
 JsDiagGetBreakOnException
 JsDiagGetBreakpoints
@@ -52,3 +51,7 @@ JsModuleEvaluation
 JsSetModuleHostInfo
 JsGetModuleHostInfo
 JsInitializeJITServer
+
+JsCreateSharedArrayBufferWithSharedContent
+JsGetSharedArrayBufferContent
+JsReleaseSharedArrayBufferContentHandle

+ 29 - 0
bin/ch/262.js

@@ -0,0 +1,29 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+LR"====(
+
+var $262 = {
+  agent: {
+    start: function (src) {
+      WScript.LoadScript(        
+        `
+        $262 = {
+          agent:{
+            receiveBroadcast: function(callback){ WScript.ReceiveBroadcast(callback); },
+            report: function(value){ WScript.Report(value); },
+            leaving: function(){ WScript.Leaving(); }
+          }
+        };
+        ${src}
+        `, 'crossthread');
+    },
+    broadcast: function (sab) { WScript.Broadcast(sab); },
+    sleep: function (timeout) { WScript.Sleep(timeout); },
+    getReport: function () { return WScript.GetReport(); },
+  }
+}
+
+)===="

+ 1 - 0
bin/ch/CMakeLists.txt

@@ -6,6 +6,7 @@ set(ch_source_files
   Helpers.cpp
   HostConfigFlags.cpp
   WScriptJsrt.cpp
+  RuntimeThreadData.cpp
   )
 
 if (STATIC_LIBRARY)

+ 6 - 0
bin/ch/ChakraRtInterface.cpp

@@ -86,6 +86,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
     m_jsApiHooks.pfJsrtSetProperty = (JsAPIHooks::JsrtSetPropertyPtr)GetChakraCoreSymbol(library, "JsSetProperty");
     m_jsApiHooks.pfJsrtGetGlobalObject = (JsAPIHooks::JsrtGetGlobalObjectPtr)GetChakraCoreSymbol(library, "JsGetGlobalObject");
     m_jsApiHooks.pfJsrtGetUndefinedValue = (JsAPIHooks::JsrtGetUndefinedValuePtr)GetChakraCoreSymbol(library, "JsGetUndefinedValue");
+    m_jsApiHooks.pfJsrtGetNullValue = (JsAPIHooks::JsrtGetUndefinedValuePtr)GetChakraCoreSymbol(library, "JsGetNullValue");
     m_jsApiHooks.pfJsrtGetTrueValue = (JsAPIHooks::JsrtGetUndefinedValuePtr)GetChakraCoreSymbol(library, "JsGetTrueValue");
     m_jsApiHooks.pfJsrtGetFalseValue = (JsAPIHooks::JsrtGetUndefinedValuePtr)GetChakraCoreSymbol(library, "JsGetFalseValue");
     m_jsApiHooks.pfJsrtConvertValueToString = (JsAPIHooks::JsrtConvertValueToStringPtr)GetChakraCoreSymbol(library, "JsConvertValueToString");
@@ -99,8 +100,12 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
     m_jsApiHooks.pfJsrtNumberToInt = (JsAPIHooks::JsrtNumberToIntPtr)GetChakraCoreSymbol(library, "JsNumberToInt");
     m_jsApiHooks.pfJsrtDoubleToNumber = (JsAPIHooks::JsrtDoubleToNumberPtr)GetChakraCoreSymbol(library, "JsDoubleToNumber");
     m_jsApiHooks.pfJsrtGetExternalData = (JsAPIHooks::JsrtGetExternalDataPtr)GetChakraCoreSymbol(library, "JsGetExternalData");
+    m_jsApiHooks.pfJsrtSetExternalData = (JsAPIHooks::JsrtSetExternalDataPtr)GetChakraCoreSymbol(library, "JsSetExternalData");
     m_jsApiHooks.pfJsrtCreateArray = (JsAPIHooks::JsrtCreateArrayPtr)GetChakraCoreSymbol(library, "JsCreateArray");
     m_jsApiHooks.pfJsrtCreateArrayBuffer = (JsAPIHooks::JsrtCreateArrayBufferPtr)GetChakraCoreSymbol(library, "JsCreateArrayBuffer");
+    m_jsApiHooks.pfJsrtCreateSharedArrayBufferWithSharedContent = (JsAPIHooks::JsrtCreateSharedArrayBufferWithSharedContentPtr)GetChakraCoreSymbol(library, "JsCreateSharedArrayBufferWithSharedContent");
+    m_jsApiHooks.pfJsrtGetSharedArrayBufferContent = (JsAPIHooks::JsrtGetSharedArrayBufferContentPtr)GetChakraCoreSymbol(library, "JsGetSharedArrayBufferContent");
+    m_jsApiHooks.pfJsrtReleaseSharedArrayBufferContentHandle = (JsAPIHooks::JsrtReleaseSharedArrayBufferContentHandlePtr)GetChakraCoreSymbol(library, "JsReleaseSharedArrayBufferContentHandle");
     m_jsApiHooks.pfJsrtGetArrayBufferStorage = (JsAPIHooks::JsrtGetArrayBufferStoragePtr)GetChakraCoreSymbol(library, "JsGetArrayBufferStorage");
     m_jsApiHooks.pfJsrtHasException = (JsAPIHooks::JsrtHasExceptionPtr)GetChakraCoreSymbol(library, "JsHasException");
     m_jsApiHooks.pfJsrtSetException = (JsAPIHooks::JsrtSetExceptionPtr)GetChakraCoreSymbol(library, "JsSetException");
@@ -140,6 +145,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
     m_jsApiHooks.pfJsrtSerialize = (JsAPIHooks::JsrtSerialize)GetChakraCoreSymbol(library, "JsSerialize");
     m_jsApiHooks.pfJsrtRunSerialized = (JsAPIHooks::JsrtRunSerialized)GetChakraCoreSymbol(library, "JsRunSerialized");
     m_jsApiHooks.pfJsrtCreateString = (JsAPIHooks::JsrtCreateString)GetChakraCoreSymbol(library, "JsCreateString");
+    m_jsApiHooks.pfJsrtCreateStringUtf16 = (JsAPIHooks::JsrtCreateStringUtf16)GetChakraCoreSymbol(library, "JsCreateStringUtf16");
     m_jsApiHooks.pfJsrtCopyString = (JsAPIHooks::JsrtCopyString)GetChakraCoreSymbol(library, "JsCopyString");
     m_jsApiHooks.pfJsrtCreatePropertyId = (JsAPIHooks::JsrtCreatePropertyId)GetChakraCoreSymbol(library, "JsCreatePropertyId");
     m_jsApiHooks.pfJsrtCreateExternalArrayBuffer = (JsAPIHooks::JsrtCreateExternalArrayBuffer)GetChakraCoreSymbol(library, "JsCreateExternalArrayBuffer");

+ 18 - 0
bin/ch/ChakraRtInterface.h

@@ -36,8 +36,12 @@ struct JsAPIHooks
     typedef JsErrorCode (WINAPI *JsrtNumberToIntPtr)(JsValueRef value, int *intValue);
     typedef JsErrorCode (WINAPI *JsrtDoubleToNumberPtr)(double doubleValue, JsValueRef* value);
     typedef JsErrorCode (WINAPI *JsrtGetExternalDataPtr)(JsValueRef object, void **data);
+    typedef JsErrorCode (WINAPI *JsrtSetExternalDataPtr)(JsValueRef object, void *data);
     typedef JsErrorCode (WINAPI *JsrtCreateArrayPtr)(unsigned int length, JsValueRef *result);
     typedef JsErrorCode (WINAPI *JsrtCreateArrayBufferPtr)(unsigned int byteLength, JsValueRef *result);
+    typedef JsErrorCode (WINAPI *JsrtCreateSharedArrayBufferWithSharedContentPtr)(JsSharedArrayBufferContentHandle sharedContent, JsValueRef *result);
+    typedef JsErrorCode (WINAPI *JsrtGetSharedArrayBufferContentPtr)(JsValueRef sharedArrayBuffer, JsSharedArrayBufferContentHandle *sharedContents);
+    typedef JsErrorCode (WINAPI *JsrtReleaseSharedArrayBufferContentHandlePtr)(JsSharedArrayBufferContentHandle sharedContent);
     typedef JsErrorCode (WINAPI *JsrtGetArrayBufferStoragePtr)(JsValueRef instance, BYTE **buffer, unsigned int *bufferLength);
     typedef JsErrorCode (WINAPI *JsrtCreateErrorPtr)(JsValueRef message, JsValueRef *error);
     typedef JsErrorCode (WINAPI *JsrtHasExceptionPtr)(bool *hasException);
@@ -75,6 +79,8 @@ struct JsAPIHooks
     typedef JsErrorCode(WINAPI *JsrtRunSerialized)(JsValueRef buffer, JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, JsValueRef sourceUrl, JsValueRef * result);
     typedef JsErrorCode(WINAPI *JsrtCopyString)(JsValueRef value, char* buffer, size_t bufferSize, size_t* written);
     typedef JsErrorCode(WINAPI *JsrtCreateString)(const char *content, size_t length, JsValueRef *value);
+    typedef JsErrorCode(WINAPI *JsrtCreateStringUtf16)(const uint16_t *content, size_t length, JsValueRef *value);
+    
     typedef JsErrorCode(WINAPI *JsrtCreateExternalArrayBuffer)(void *data, unsigned int byteLength, JsFinalizeCallback finalizeCallback, void *callbackState, JsValueRef *result);
     typedef JsErrorCode(WINAPI *JsrtCreatePropertyId)(const char *name, size_t length, JsPropertyIdRef *propertyId);
 
@@ -107,6 +113,7 @@ struct JsAPIHooks
     JsrtSetPropertyPtr pfJsrtSetProperty;
     JsrtGetGlobalObjectPtr pfJsrtGetGlobalObject;
     JsrtGetUndefinedValuePtr pfJsrtGetUndefinedValue;
+    JsrtGetUndefinedValuePtr pfJsrtGetNullValue;
     JsrtGetUndefinedValuePtr pfJsrtGetTrueValue;
     JsrtGetUndefinedValuePtr pfJsrtGetFalseValue;
     JsrtConvertValueToStringPtr pfJsrtConvertValueToString;
@@ -125,8 +132,12 @@ struct JsAPIHooks
     JsrtNumberToIntPtr pfJsrtNumberToInt;
     JsrtDoubleToNumberPtr pfJsrtDoubleToNumber;
     JsrtGetExternalDataPtr pfJsrtGetExternalData;
+    JsrtSetExternalDataPtr pfJsrtSetExternalData;
     JsrtCreateArrayPtr pfJsrtCreateArray;
     JsrtCreateArrayBufferPtr pfJsrtCreateArrayBuffer;
+    JsrtCreateSharedArrayBufferWithSharedContentPtr pfJsrtCreateSharedArrayBufferWithSharedContent;
+    JsrtGetSharedArrayBufferContentPtr pfJsrtGetSharedArrayBufferContent;    
+    JsrtReleaseSharedArrayBufferContentHandlePtr pfJsrtReleaseSharedArrayBufferContentHandle;
     JsrtGetArrayBufferStoragePtr pfJsrtGetArrayBufferStorage;
     JsrtCreateErrorPtr pfJsrtCreateError;
     JsrtHasExceptionPtr pfJsrtHasException;
@@ -162,6 +173,7 @@ struct JsAPIHooks
     JsrtSerialize pfJsrtSerialize;
     JsrtRunSerialized pfJsrtRunSerialized;
     JsrtCreateString pfJsrtCreateString;
+    JsrtCreateStringUtf16 pfJsrtCreateStringUtf16;
     JsrtCopyString pfJsrtCopyString;
     JsrtCreatePropertyId pfJsrtCreatePropertyId;
     JsrtCreateExternalArrayBuffer pfJsrtCreateExternalArrayBuffer;
@@ -306,6 +318,7 @@ public:
     static JsErrorCode WINAPI JsSetProperty(JsValueRef object, JsPropertyIdRef property, JsValueRef value, bool useStrictRules) { return HOOK_JS_API(SetProperty(object, property, value, useStrictRules)); }
     static JsErrorCode WINAPI JsGetGlobalObject(JsValueRef *globalObject) { return HOOK_JS_API(GetGlobalObject(globalObject)); }
     static JsErrorCode WINAPI JsGetUndefinedValue(JsValueRef *globalObject) { return HOOK_JS_API(GetUndefinedValue(globalObject)); }
+    static JsErrorCode WINAPI JsGetNullValue(JsValueRef *globalObject) { return HOOK_JS_API(GetNullValue(globalObject)); }
     static JsErrorCode WINAPI JsGetTrueValue(JsValueRef *globalObject) { return HOOK_JS_API(GetTrueValue(globalObject)); }
     static JsErrorCode WINAPI JsGetFalseValue(JsValueRef *globalObject) { return HOOK_JS_API(GetFalseValue(globalObject)); }
     static JsErrorCode WINAPI JsConvertValueToString(JsValueRef value, JsValueRef *stringValue) { return HOOK_JS_API(ConvertValueToString(value, stringValue)); }
@@ -319,8 +332,12 @@ public:
     static JsErrorCode WINAPI JsNumberToInt(JsValueRef value, int* intValue) { return HOOK_JS_API(NumberToInt(value, intValue)); }
     static JsErrorCode WINAPI JsDoubleToNumber(double doubleValue, JsValueRef* value) { return HOOK_JS_API(DoubleToNumber(doubleValue, value)); }
     static JsErrorCode WINAPI JsGetExternalData(JsValueRef object, void **data) { return HOOK_JS_API(GetExternalData(object, data)); }
+    static JsErrorCode WINAPI JsSetExternalData(JsValueRef object, void *data)  { return HOOK_JS_API(SetExternalData(object, data)); }
     static JsErrorCode WINAPI JsCreateArray(unsigned int length, JsValueRef *result) { return HOOK_JS_API(CreateArray(length, result)); }
     static JsErrorCode WINAPI JsCreateArrayBuffer(unsigned int byteLength, JsValueRef *result) { return HOOK_JS_API(CreateArrayBuffer(byteLength, result)); }
+    static JsErrorCode WINAPI JsCreateSharedArrayBufferWithSharedContent(JsSharedArrayBufferContentHandle sharedContent, JsValueRef *result) { return HOOK_JS_API(CreateSharedArrayBufferWithSharedContent(sharedContent, result)); }
+    static JsErrorCode WINAPI JsGetSharedArrayBufferContent(JsValueRef sharedArrayBuffer, JsSharedArrayBufferContentHandle *sharedContents) { return HOOK_JS_API(GetSharedArrayBufferContent(sharedArrayBuffer, sharedContents)); }
+    static JsErrorCode WINAPI JsReleaseSharedArrayBufferContentHandle(JsSharedArrayBufferContentHandle sharedContent) { return HOOK_JS_API(ReleaseSharedArrayBufferContentHandle(sharedContent)); }
     static JsErrorCode WINAPI JsGetArrayBufferStorage(JsValueRef instance, BYTE **buffer, unsigned int *bufferLength) { return HOOK_JS_API(GetArrayBufferStorage(instance, buffer, bufferLength)); }
     static JsErrorCode WINAPI JsCreateError(JsValueRef message, JsValueRef *error) { return HOOK_JS_API(CreateError(message, error)); }
     static JsErrorCode WINAPI JsHasException(bool *hasException) { return HOOK_JS_API(HasException(hasException)); }
@@ -381,6 +398,7 @@ public:
     static JsErrorCode WINAPI JsRunSerialized(JsValueRef buffer, JsSerializedLoadScriptCallback scriptLoadCallback, JsSourceContext sourceContext, JsValueRef sourceUrl, JsValueRef * result) { return HOOK_JS_API(RunSerialized(buffer, scriptLoadCallback, sourceContext, sourceUrl, result)); }
     static JsErrorCode WINAPI JsCopyString(JsValueRef value, char* buffer, size_t bufferSize, size_t* written) { return HOOK_JS_API(CopyString(value, buffer, bufferSize, written)); }
     static JsErrorCode WINAPI JsCreateString(const char *content, size_t length, JsValueRef *value) { return HOOK_JS_API(CreateString(content, length, value)); }
+    static JsErrorCode WINAPI JsCreateStringUtf16(const uint16_t *content, size_t length, JsValueRef *value) { return HOOK_JS_API(CreateStringUtf16(content, length, value)); }
     static JsErrorCode WINAPI JsCreatePropertyId(const char *name, size_t length, JsPropertyIdRef *propertyId) { return HOOK_JS_API(CreatePropertyId(name, length, propertyId)); }
     static JsErrorCode WINAPI JsCreateExternalArrayBuffer(void *data, unsigned int byteLength, JsFinalizeCallback finalizeCallback, void *callbackState, JsValueRef *result)  { return HOOK_JS_API(CreateExternalArrayBuffer(data, byteLength, finalizeCallback, callbackState, result)); }
 };

+ 1 - 0
bin/ch/HostConfigFlagsList.h

@@ -12,5 +12,6 @@ FLAG(BSTR, Serialized,                      "If source is UTF8, deserializes fro
 FLAG(bool, OOPJIT,                          "Run JIT in a separate process", false)
 FLAG(bool, EnsureCloseJITServer,            "JIT process will be force closed when ch is terminated", true)
 FLAG(bool, AsyncModuleLoad,                 "Silence host error output for module load failures to enable promise testing", false)
+FLAG(bool, $262,                            "load $262 harness", false)
 #undef FLAG
 #endif

+ 153 - 0
bin/ch/RuntimeThreadData.cpp

@@ -0,0 +1,153 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#include "stdafx.h"
+
+#ifndef _WIN32
+HANDLE CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCWSTR lpName)
+{
+    // xplat-todo: implement this in PAL
+    Assert(false);
+    return INVALID_HANDLE_VALUE;
+}
+BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount)
+{
+    // xplat-todo: implement this in PAL
+    Assert(false);
+    return FALSE;
+}
+
+#endif
+
+void RuntimeThreadLocalData::Initialize(RuntimeThreadData* threadData)
+{
+    this->threadData = threadData;
+}
+
+void RuntimeThreadLocalData::Uninitialize()
+{
+    if (threadData)
+    {
+        delete threadData;
+        threadData = nullptr;
+    }
+}
+
+
+THREAD_LOCAL RuntimeThreadLocalData threadLocalData;
+
+RuntimeThreadLocalData& GetRuntimeThreadLocalData()
+{
+    return threadLocalData;
+}
+
+RuntimeThreadData::RuntimeThreadData()
+{
+    this->hevntInitialScriptCompleted = CreateEvent(NULL, TRUE, FALSE, NULL);
+    this->hevntReceivedBroadcast = CreateEvent(NULL, FALSE, FALSE, NULL);
+    this->hevntShutdown = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+    this->sharedContent = nullptr;
+    this->receiveBroadcastCallbackFunc = nullptr;
+
+    this->leaving = false;
+
+    InitializeCriticalSection(&csReportQ);
+
+}
+
+RuntimeThreadData::~RuntimeThreadData()
+{
+    CloseHandle(this->hevntInitialScriptCompleted);
+    CloseHandle(this->hevntReceivedBroadcast);
+    CloseHandle(this->hevntShutdown);
+    DeleteCriticalSection(&csReportQ);
+}
+
+DWORD RuntimeThreadData::ThreadProc()
+{
+    JsValueRef scriptSource;
+    JsValueRef fname;
+    const char* fullPath = "agent source";
+    HRESULT hr = S_OK;
+
+    threadLocalData.Initialize(this);
+
+    IfJsErrorFailLog(ChakraRTInterface::JsCreateRuntime(JsRuntimeAttributeNone, nullptr, &runtime));
+    IfJsErrorFailLog(ChakraRTInterface::JsCreateContext(runtime, &context));
+    IfJsErrorFailLog(ChakraRTInterface::JsSetCurrentContext(context));
+
+
+    if (!WScriptJsrt::Initialize())
+    {
+        IfFailGo(E_FAIL);
+    }
+
+
+    IfJsErrorFailLog(ChakraRTInterface::JsCreateExternalArrayBuffer((void*)this->initialSource.c_str(),
+        (unsigned int)this->initialSource.size(), nullptr, nullptr, &scriptSource));
+
+
+    ChakraRTInterface::JsCreateString(fullPath, strlen(fullPath), &fname);
+
+    ChakraRTInterface::JsRun(scriptSource, WScriptJsrt::GetNextSourceContext(), fname, JsParseScriptAttributeNone, nullptr);
+
+    SetEvent(this->parent->hevntInitialScriptCompleted);
+
+    // loop waiting for work;
+
+    while (true)
+    {
+        HANDLE handles[] = { this->hevntReceivedBroadcast, this->hevntShutdown };
+        DWORD waitRet = WaitForMultipleObjects(_countof(handles), handles, false, INFINITE);
+
+        if (waitRet == WAIT_OBJECT_0)
+        {
+            JsValueRef args[3];
+            ChakraRTInterface::JsGetGlobalObject(&args[0]);
+            ChakraRTInterface::JsCreateSharedArrayBufferWithSharedContent(this->parent->sharedContent, &args[1]);
+            ChakraRTInterface::JsDoubleToNumber(1, &args[2]);
+
+            // notify the parent we received the data
+            ReleaseSemaphore(this->parent->hSemaphore, 1, NULL);
+
+            if (this->receiveBroadcastCallbackFunc)
+            {
+                ChakraRTInterface::JsCallFunction(this->receiveBroadcastCallbackFunc, args, 3, nullptr);
+            }
+        }
+
+        if (waitRet == WAIT_OBJECT_0 + 1 || this->leaving)
+        {
+            WScriptJsrt::Uninitialize();
+
+            if (this->receiveBroadcastCallbackFunc)
+            {
+                ChakraRTInterface::JsRelease(this->receiveBroadcastCallbackFunc, nullptr);
+            }
+            ChakraRTInterface::JsSetCurrentContext(nullptr);
+            ChakraRTInterface::JsDisposeRuntime(runtime);
+
+            if (this->parent->hSemaphore != INVALID_HANDLE_VALUE)
+            {
+                ReleaseSemaphore(this->parent->hSemaphore, 1, NULL);
+            }
+
+            threadLocalData.Uninitialize();
+            return 0;
+        }
+        else if (waitRet != WAIT_OBJECT_0)
+        {
+            Assert(false);
+            break;
+        }
+    }
+
+Error:
+
+    ChakraRTInterface::JsSetCurrentContext(nullptr);
+    ChakraRTInterface::JsDisposeRuntime(runtime);
+    threadLocalData.Uninitialize();
+    return 0;
+}

+ 50 - 0
bin/ch/RuntimeThreadData.h

@@ -0,0 +1,50 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#pragma once
+#include <list>
+
+class RuntimeThreadData
+{
+public:
+    RuntimeThreadData();
+    ~RuntimeThreadData();
+    HANDLE hevntInitialScriptCompleted;
+    HANDLE hevntReceivedBroadcast;
+    HANDLE hevntShutdown;
+    HANDLE hSemaphore;
+    JsSharedArrayBufferContentHandle sharedContent;
+    JsValueRef receiveBroadcastCallbackFunc;
+
+
+    JsRuntimeHandle runtime;
+    JsContextRef context;
+
+
+    std::string initialSource;
+
+    RuntimeThreadData* parent;
+    
+    std::list<RuntimeThreadData*> children;
+
+    CRITICAL_SECTION csReportQ;
+    std::list<std::string> reportQ;
+
+    bool leaving;
+
+
+    DWORD ThreadProc();
+
+};
+
+struct RuntimeThreadLocalData
+{
+    // can't use ctor/dtor because it's not supported in VS2012
+    // error C2483: 'threadLocalData' : object with constructor or destructor cannot be declared 'thread' 
+    void Initialize(RuntimeThreadData* threadData);
+    void Uninitialize();
+    RuntimeThreadData* threadData;
+};
+
+RuntimeThreadLocalData& GetRuntimeThreadLocalData();

+ 234 - 1
bin/ch/WScriptJsrt.cpp

@@ -370,6 +370,7 @@ JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileConten
     return JsNoError;
 }
 
+
 JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName,
     LPCSTR fileContent, LPCSTR scriptInjectType, bool isSourceModule, JsFinalizeCallback finalizeCallback)
 {
@@ -473,6 +474,29 @@ JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName,
         // Set the context back to the old one
         ChakraRTInterface::JsSetCurrentContext(currentContext);
     }
+    else if (strcmp(scriptInjectType, "crossthread") == 0)
+    {
+        if (GetRuntimeThreadLocalData().threadData == nullptr) 
+        {
+            GetRuntimeThreadLocalData().threadData = new RuntimeThreadData();
+        }
+
+        RuntimeThreadData* child = new RuntimeThreadData();
+        child->initialSource = fileContent;
+        GetRuntimeThreadLocalData().threadData->children.push_back(child);
+        child->parent = GetRuntimeThreadLocalData().threadData;
+                
+        // TODO: need to add a switch in case we don't need to wait for 
+        // child initial script completion
+        ResetEvent(GetRuntimeThreadLocalData().threadData->hevntInitialScriptCompleted);
+
+        ::CreateThread(NULL, NULL, [](void* param) -> DWORD
+        {
+            return ((RuntimeThreadData*)param)->ThreadProc();
+        }, (void*)child, NULL, NULL);
+
+        WaitForSingleObject(GetRuntimeThreadLocalData().threadData->hevntInitialScriptCompleted, INFINITE);
+    }
     else
     {
         errorCode = JsErrorInvalidArgument;
@@ -886,6 +910,30 @@ bool WScriptJsrt::Initialize()
 
     IfJsrtErrorFail(InitializeModuleCallbacks(), false);
 
+    if (HostConfigFlags::flags.$262)
+    {
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Broadcast", BroadcastCallback));
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "ReceiveBroadcast", ReceiveBroadcastCallback));
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Report", ReportCallback));
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "GetReport", GetReportCallback));
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Leaving", LeavingCallback));
+        IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Sleep", SleepCallback));
+
+
+        // OSX does build does not support $262 as filename
+        const wchar_t $262[] =
+            #include "262.js"
+            ;
+
+        JsValueRef $262ScriptRef;
+        IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateStringUtf16((uint16_t*)$262, _countof($262), &$262ScriptRef));
+
+        JsValueRef fname;
+        IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsCreateString("$262", strlen("$262"), &fname));
+        IfJsrtErrorFailLogAndRetFalse(ChakraRTInterface::JsRun($262ScriptRef, WScriptJsrt::GetNextSourceContext(), fname, JsParseScriptAttributeNone, nullptr));
+
+    }
+
 Error:
     return hr == S_OK;
 }
@@ -908,6 +956,31 @@ bool WScriptJsrt::Uninitialize()
     // "operator delete" / global HeapAllocator::Instance. Clear it manually here
     // to avoid worrying about global destructor order.
     moduleRecordMap.clear();
+
+    if (GetRuntimeThreadLocalData().threadData && !GetRuntimeThreadLocalData().threadData->children.empty())
+    {
+        LONG count = (LONG)GetRuntimeThreadLocalData().threadData->children.size();
+        GetRuntimeThreadLocalData().threadData->hSemaphore = CreateSemaphore(NULL, 0, count, NULL);
+        
+        //Clang does not support "for each" yet
+        for(auto i = GetRuntimeThreadLocalData().threadData->children.begin(); i!= GetRuntimeThreadLocalData().threadData->children.end(); i++)
+        {
+            auto child = *i;
+            if (child->leaving)
+            {
+                ReleaseSemaphore(GetRuntimeThreadLocalData().threadData->hSemaphore, 1, NULL);
+            }
+            else
+            {
+                SetEvent(child->hevntShutdown);
+            }
+        }
+
+        WaitForSingleObject(GetRuntimeThreadLocalData().threadData->hSemaphore, INFINITE);
+        CloseHandle(GetRuntimeThreadLocalData().threadData->hSemaphore);
+        GetRuntimeThreadLocalData().threadData->hSemaphore = INVALID_HANDLE_VALUE;
+    }
+
     return true;
 }
 
@@ -1037,6 +1110,166 @@ Error:
     return returnValue;
 }
 
+JsValueRef __stdcall WScriptJsrt::BroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue));
+
+    if (argumentCount > 1)
+    {
+        if (GetRuntimeThreadLocalData().threadData)
+        {
+            ChakraRTInterface::JsGetSharedArrayBufferContent(arguments[1], &GetRuntimeThreadLocalData().threadData->sharedContent);
+
+            LONG count = (LONG)GetRuntimeThreadLocalData().threadData->children.size();
+            GetRuntimeThreadLocalData().threadData->hSemaphore = CreateSemaphore(NULL, 0, count, NULL);
+            //Clang does not support "for each" yet
+            for (auto i = GetRuntimeThreadLocalData().threadData->children.begin(); i != GetRuntimeThreadLocalData().threadData->children.end(); i++)
+            {
+                auto child = *i;
+                SetEvent(child->hevntReceivedBroadcast);
+            }
+
+            WaitForSingleObject(GetRuntimeThreadLocalData().threadData->hSemaphore, INFINITE);
+            CloseHandle(GetRuntimeThreadLocalData().threadData->hSemaphore);
+            GetRuntimeThreadLocalData().threadData->hSemaphore = INVALID_HANDLE_VALUE;
+
+
+            ChakraRTInterface::JsReleaseSharedArrayBufferContentHandle(GetRuntimeThreadLocalData().threadData->sharedContent);
+        }
+    }
+
+Error:
+    return returnValue;
+}
+
+JsValueRef __stdcall WScriptJsrt::ReceiveBroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue));
+
+    if (argumentCount > 1)
+    {
+        if (GetRuntimeThreadLocalData().threadData)
+        {
+            if (GetRuntimeThreadLocalData().threadData->receiveBroadcastCallbackFunc)
+            {
+                ChakraRTInterface::JsRelease(GetRuntimeThreadLocalData().threadData->receiveBroadcastCallbackFunc, nullptr);
+            }
+            GetRuntimeThreadLocalData().threadData->receiveBroadcastCallbackFunc = arguments[1];
+            ChakraRTInterface::JsAddRef(GetRuntimeThreadLocalData().threadData->receiveBroadcastCallbackFunc, nullptr);
+        }
+    }
+
+Error:
+    return returnValue;
+}
+
+JsValueRef __stdcall WScriptJsrt::ReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue));
+
+    if (argumentCount > 1)
+    {
+        JsValueRef stringRef;
+        ChakraRTInterface::JsConvertValueToString(arguments[1], &stringRef);
+
+        AutoString autoStr(stringRef);
+
+        if (autoStr.GetError() == JsNoError)
+        {
+            std::string str(autoStr.GetString());
+
+            if (GetRuntimeThreadLocalData().threadData && GetRuntimeThreadLocalData().threadData->parent)
+            {
+                EnterCriticalSection(&GetRuntimeThreadLocalData().threadData->parent->csReportQ);
+                GetRuntimeThreadLocalData().threadData->parent->reportQ.push_back(str);
+                LeaveCriticalSection(&GetRuntimeThreadLocalData().threadData->parent->csReportQ);
+            }
+        }
+    }
+
+Error:
+    return returnValue;
+}
+
+JsValueRef __stdcall WScriptJsrt::GetReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetNullValue(&returnValue));
+
+    if (argumentCount > 0)
+    {
+        if (GetRuntimeThreadLocalData().threadData)
+        {
+            EnterCriticalSection(&GetRuntimeThreadLocalData().threadData->csReportQ);
+
+            if (GetRuntimeThreadLocalData().threadData->reportQ.size() > 0)
+            {
+                auto str = GetRuntimeThreadLocalData().threadData->reportQ.front();
+                GetRuntimeThreadLocalData().threadData->reportQ.pop_front();
+                ChakraRTInterface::JsCreateString(str.c_str(), str.size(), &returnValue);
+            }
+            LeaveCriticalSection(&GetRuntimeThreadLocalData().threadData->csReportQ);
+        }
+    }
+
+Error:
+    return returnValue;
+}
+
+JsValueRef __stdcall WScriptJsrt::LeavingCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue));
+
+    if (argumentCount > 0)
+    {
+        if (GetRuntimeThreadLocalData().threadData)
+        {
+            GetRuntimeThreadLocalData().threadData->leaving = true;
+        }
+    }
+
+Error:
+    return returnValue;
+}
+
+JsValueRef __stdcall WScriptJsrt::SleepCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
+{
+    HRESULT hr = E_FAIL;
+    JsValueRef returnValue = JS_INVALID_REFERENCE;
+    JsErrorCode errorCode = JsNoError;
+
+    IfJsrtErrorSetGo(ChakraRTInterface::JsGetUndefinedValue(&returnValue));
+
+    if (argumentCount > 1)
+    {
+        double timeout = 0.0;
+        ChakraRTInterface::JsNumberToDouble(arguments[1], &timeout);
+        Sleep((DWORD)timeout);
+    }
+
+Error:
+    return returnValue;
+}
+
 bool WScriptJsrt::PrintException(LPCSTR fileName, JsErrorCode jsErrorCode)
 {
     LPCWSTR errorTypeString = ConvertErrorCodeToMessage(jsErrorCode);
@@ -1348,4 +1581,4 @@ void WScriptJsrt::PromiseContinuationCallback(JsValueRef task, void *callbackSta
 
     WScriptJsrt::CallbackMessage *msg = new WScriptJsrt::CallbackMessage(0, task);
     messageQueue->InsertSorted(msg);
-}
+}

+ 8 - 0
bin/ch/WScriptJsrt.h

@@ -3,6 +3,7 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #pragma once
+#include <list>
 
 class WScriptJsrt
 {
@@ -118,6 +119,13 @@ private:
     static JsValueRef CALLBACK LoadTextFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
     static JsValueRef CALLBACK FlagCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
 
+    static JsValueRef CALLBACK BroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsValueRef CALLBACK ReceiveBroadcastCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsValueRef CALLBACK ReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsValueRef CALLBACK GetReportCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsValueRef CALLBACK LeavingCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsValueRef CALLBACK SleepCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+
     static JsErrorCode FetchImportedModuleHelper(JsModuleRecord referencingModule, JsValueRef specifier, __out JsModuleRecord* dependentModuleRecord);
 
     static MessageQueue *messageQueue;

+ 4 - 1
bin/ch/ch.vcxproj

@@ -57,6 +57,7 @@
     <ClInclude Include="HostConfigFlagsList.h" />
     <ClInclude Include="JITProcessManager.h" />
     <ClInclude Include="MessageQueue.h" />
+    <ClInclude Include="RuntimeThreadData.h" />
     <ClInclude Include="stdafx.h" />
     <ClInclude Include="WScriptJsrt.h" />
   </ItemGroup>
@@ -68,6 +69,7 @@
     <ClCompile Include="Helpers.cpp" />
     <ClCompile Include="HostConfigFlags.cpp" />
     <ClCompile Include="JITProcessManager.cpp" />
+    <ClCompile Include="RuntimeThreadData.cpp" />
     <ClCompile Include="WScriptJsrt.cpp" />
   </ItemGroup>
   <ItemGroup>
@@ -88,6 +90,7 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
+    <None Include="262.js" />
     <None Include="ch.def" />
   </ItemGroup>
   <ItemGroup>
@@ -103,4 +106,4 @@
   </ItemGroup>
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-</Project>
+</Project>

+ 1 - 0
bin/ch/stdafx.h

@@ -153,6 +153,7 @@ do { \
 #include "ChakraRtInterface.h"
 #include "HostConfigFlags.h"
 #include "MessageQueue.h"
+#include "RuntimeThreadData.h"
 #include "WScriptJsrt.h"
 #include "Debugger.h"
 

+ 1 - 0
lib/Jsrt/ChakraCommon.h

@@ -2062,6 +2062,7 @@ typedef unsigned short uint16_t;
             _Outptr_result_bytebuffer_(*bufferLength) ChakraBytePtr *buffer,
             _Out_ unsigned int *bufferLength);
 
+
     /// <summary>
     ///     Invokes a function.
     /// </summary>

+ 62 - 0
lib/Jsrt/ChakraCore.h

@@ -27,6 +27,15 @@
 
 typedef void* JsModuleRecord;
 
+/// <summary>
+///     A reference to an object owned by the SharedArrayBuffer.
+/// </summary>
+/// <remarks>
+///     This represents SharedContents which is heap allocated object, it can be passed through 
+///     different runtimes to share the underlying buffer.
+/// </remarks>
+typedef void *JsSharedArrayBufferContentHandle;
+
 typedef enum JsParseModuleSourceFlags
 {
     JsParseModuleSourceFlags_DataIsUTF16LE = 0x00000000,
@@ -599,5 +608,58 @@ CHAKRA_API
         _In_ JsWeakRef weakRef,
         _Out_ JsValueRef* value);
 
+/// <summary>
+///     Creates a Javascript SharedArrayBuffer object with shared content get from JsGetSharedArrayBufferContent.
+/// </summary>
+/// <remarks>
+///     Requires an active script context.
+/// </remarks>
+/// <param name="sharedContents">
+///     The storage object of a SharedArrayBuffer which can be shared between multiple thread.
+/// </param>
+/// <param name="result">The new SharedArrayBuffer object.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsCreateSharedArrayBufferWithSharedContent(
+    _In_ JsSharedArrayBufferContentHandle sharedContents,
+    _Out_ JsValueRef *result);
+
+/// <summary>
+///     Get the storage object from a SharedArrayBuffer.
+/// </summary>
+/// <remarks>
+///     Requires an active script context.
+/// </remarks>
+/// <param name="sharedArrayBuffer">The SharedArrayBuffer object.</param>
+/// <param name="sharedContents">
+///     The storage object of a SharedArrayBuffer which can be shared between multiple thread.
+///     User should call JsReleaseSharedArrayBufferContentHandle after finished using it.
+/// </param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsGetSharedArrayBufferContent(
+    _In_ JsValueRef sharedArrayBuffer,
+    _Out_ JsSharedArrayBufferContentHandle *sharedContents);
+
+/// <summary>
+///     Decrease the reference count on a SharedArrayBuffer storage object.
+/// </summary>
+/// <remarks>
+///     Requires an active script context.
+/// </remarks>
+/// <param name="sharedContents">
+///     The storage object of a SharedArrayBuffer which can be shared between multiple thread.
+/// </param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsReleaseSharedArrayBufferContentHandle(
+    _In_ JsSharedArrayBufferContentHandle sharedContents);
+
 #endif // CHAKRACOREBUILD_
 #endif // _CHAKRACORE_H_

+ 49 - 0
lib/Jsrt/Jsrt.cpp

@@ -1559,6 +1559,55 @@ CHAKRA_API JsCreateArrayBuffer(_In_ unsigned int byteLength, _Out_ JsValueRef *r
     });
 }
 
+CHAKRA_API JsCreateSharedArrayBufferWithSharedContent(_In_ JsSharedArrayBufferContentHandle sharedContents, _Out_ JsValueRef *result)
+{
+    return ContextAPIWrapper<true>([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
+
+        PARAM_NOT_NULL(result);
+
+        Js::JavascriptLibrary* library = scriptContext->GetLibrary();
+        *result = library->CreateSharedArrayBuffer((Js::SharedContents*)sharedContents);
+
+        PERFORM_JSRT_TTD_RECORD_ACTION_RESULT(scriptContext, result);
+
+        JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(*result));
+        return JsNoError;
+    });
+}
+
+CHAKRA_API JsGetSharedArrayBufferContent(_In_ JsValueRef sharedArrayBuffer, _Out_ JsSharedArrayBufferContentHandle *sharedContents)
+{
+    return ContextAPIWrapper<true>([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
+
+        PARAM_NOT_NULL(sharedContents);
+
+        if (!Js::SharedArrayBuffer::Is(sharedArrayBuffer))
+        {
+            return JsErrorInvalidArgument;
+        }
+        
+        Js::SharedContents**& content = (Js::SharedContents**&)sharedContents;
+        *content = Js::SharedArrayBuffer::FromVar(sharedArrayBuffer)->GetSharedContents();
+
+        if (*content == nullptr)
+        {
+            return JsErrorFatal;
+        }
+
+        (*content)->AddRef();
+
+        return JsNoError;
+    });
+}
+
+CHAKRA_API JsReleaseSharedArrayBufferContentHandle(_In_ JsSharedArrayBufferContentHandle sharedContents)
+{
+    return ContextAPIWrapper<true>([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
+        ((Js::SharedContents*)sharedContents)->Release();
+        return JsNoError;
+    });
+}
+
 CHAKRA_API JsCreateExternalArrayBuffer(_Pre_maybenull_ _Pre_writable_byte_size_(byteLength) void *data, _In_ unsigned int byteLength,
     _In_opt_ JsFinalizeCallback finalizeCallback, _In_opt_ void *callbackState, _Out_ JsValueRef *result)
 {

+ 11 - 4
lib/Runtime/Library/AtomicsObject.cpp

@@ -53,23 +53,27 @@ namespace Js
 
     uint32 AtomicsObject::ValidateAtomicAccess(Var typedArray, Var requestIndex, ScriptContext *scriptContext)
     {
+        Assert(TypedArrayBase::Is(typedArray));
+
         int32 accessIndex = -1;
         if (TaggedInt::Is(requestIndex))
         {
             accessIndex = TaggedInt::ToInt32(requestIndex);
         }
+        else if(Js::JavascriptOperators::IsUndefined(requestIndex))
+        {
+            accessIndex = 0;
+        }
         else
         {
             accessIndex = JavascriptConversion::ToInt32_Full(requestIndex, scriptContext);
-            double dblValue = JavascriptConversion::ToNumber(requestIndex, scriptContext);
+            double dblValue = JavascriptConversion::ToInteger(requestIndex, scriptContext);
             if (dblValue != accessIndex)
             {
                 JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidTypedArrayIndex);
             }
         }
 
-        Assert(TypedArrayBase::Is(typedArray));
-        
         if (accessIndex < 0 || accessIndex >= (int32)TypedArrayBase::FromVar(typedArray)->GetLength())
         {
             JavascriptError::ThrowRangeError(scriptContext, JSERR_InvalidTypedArrayIndex);
@@ -217,7 +221,10 @@ namespace Js
             DWORD_PTR agent = (DWORD_PTR)scriptContext;
             Assert(sharedArrayBuffer->GetSharedContents()->IsValidAgent(agent));
             awoken = waiterList->AddAndSuspendWaiter(agent, timeout);
-            waiterList->RemoveWaiter(agent);
+            if (!awoken) 
+            {
+                waiterList->RemoveWaiter(agent);
+            }
         }
 
         return awoken ? scriptContext->GetLibrary()->CreateStringFromCppLiteral(_u("ok"))

+ 4 - 1
lib/Runtime/Library/IntlEngineInterfaceExtensionObject.cpp

@@ -172,7 +172,10 @@ namespace Js
 
         void Dispose(bool isShutdown) override
         {
-            instance->Release();
+            if (!isShutdown)
+            {
+                instance->Release();
+            }
         }
         void Mark(Recycler * recycler) override
         {

+ 5 - 0
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -1843,6 +1843,11 @@ namespace Js
         library->AddFunctionToLibraryObject(atomicsObject, PropertyIds::wake, &AtomicsObject::EntryInfo::Wake, 3);
         library->AddFunctionToLibraryObject(atomicsObject, PropertyIds::xor_, &AtomicsObject::EntryInfo::Xor, 3);
 
+        if (atomicsObject->GetScriptContext()->GetConfig()->IsES6ToStringTagEnabled())
+        {
+            library->AddMember(atomicsObject, PropertyIds::_symbolToStringTag, library->CreateStringFromCppLiteral(_u("Atomics")), PropertyConfigurable);
+        }
+        
         atomicsObject->SetHasNoEnumerableProperties(true);
     }
 

+ 8 - 3
lib/Runtime/Library/SharedArrayBuffer.cpp

@@ -9,6 +9,7 @@ namespace Js
 #if DBG
     void SharedContents::AddAgent(DWORD_PTR agent)
     {
+        AutoCriticalSection autoCS(&csAgent);
         if (allowedAgents == nullptr)
         {
             allowedAgents = HeapNew(SharableAgents, &HeapAllocator::Instance);
@@ -19,6 +20,7 @@ namespace Js
 
     bool SharedContents::IsValidAgent(DWORD_PTR agent)
     {
+        AutoCriticalSection autoCS(&csAgent);
         return allowedAgents != nullptr && allowedAgents->Contains(agent);
     }
 #endif
@@ -40,10 +42,13 @@ namespace Js
         buffer = nullptr;
         bufferLength = 0;
 #if DBG
-        if (allowedAgents != nullptr)
         {
-            HeapDelete(allowedAgents);
-            allowedAgents = nullptr;
+            AutoCriticalSection autoCS(&csAgent);
+            if (allowedAgents != nullptr)
+            {
+                HeapDelete(allowedAgents);
+                allowedAgents = nullptr;
+            }
         }
 #endif
 

+ 1 - 1
lib/Runtime/Library/SharedArrayBuffer.h

@@ -10,7 +10,6 @@ namespace Js
 {
 
     class WaiterList;
-
     typedef JsUtil::List<DWORD_PTR, HeapAllocator> SharableAgents;
     typedef JsUtil::BaseDictionary<uint, WaiterList *, HeapAllocator> IndexToWaitersMap;
 
@@ -31,6 +30,7 @@ namespace Js
 #if DBG
         // This is mainly used for validation purpose as the wait/wake APIs should be used on the agents (Workers) among which this buffer is shared.
         SharableAgents *allowedAgents;
+        CriticalSection csAgent;
         void AddAgent(DWORD_PTR agent);
         bool IsValidAgent(DWORD_PTR agent);
 #endif

+ 164 - 0
test/$262/$262test.js

@@ -0,0 +1,164 @@
+"use strict";
+var $ = {  global: this,  createRealm(options) {    options = options || {};    options.globals = options.globals || {};    var realm = WScript.LoadScript(this.source, 'samethread');    realm.$.source = this.source;    realm.$.destroy = function () {      if (options.destroy) {        options.destroy();      }    };    for(var glob in options.globals) {      realm.$.global[glob] = options.globals[glob];    }    return realm.$;  },  evalScript(code) {    try {      WScript.LoadScript(code);      return { type: 'normal', value: undefined };    } catch (e) {      return { type: 'throw', value: e };    }  },  getGlobal(name) {    return this.global[name];  },  setGlobal(name, value) {    this.global[name] = value;  },  destroy() { /* noop */ },  source: "var $ = {  global: this,  createRealm(options) {    options = options || {};    options.globals = options.globals || {};    var realm = WScript.LoadScript(this.source, 'samethread');    realm.$.source = this.source;    realm.$.destroy = function () {      if (options.destroy) {        options.destroy();      }    };    for(var glob in options.globals) {      realm.$.global[glob] = options.globals[glob];    }    return realm.$;  },  evalScript(code) {    try {      WScript.LoadScript(code);      return { type: 'normal', value: undefined };    } catch (e) {      return { type: 'throw', value: e };    }  },  getGlobal(name) {    return this.global[name];  },  setGlobal(name, value) {    this.global[name] = value;  },  destroy() { /* noop */ },  source: \"\"};"};function Test262Error(message) {
+    if (message) this.message = message;
+}
+
+Test262Error.prototype.name = "Test262Error";
+
+Test262Error.prototype.toString = function () {
+    return "Test262Error: " + this.message;
+};
+
+function $ERROR(err) {
+  if(typeof err === "object" && err !== null && "name" in err) {
+    print('test262/error ' + err.name + ': ' + err.message);
+  } else {
+    print('test262/error Test262Error: ' + err);
+  }
+}
+
+function $DONE(err) {
+  if (err) {
+    $ERROR(err);
+  }
+  print('pass');
+  $.destroy();
+}
+
+function $LOG(str) {
+  print(str);
+}
+
+
+function assert(mustBeTrue, message) {
+  if (mustBeTrue === true) {
+    return;
+  }
+
+  if (message === undefined) {
+    message = 'Expected true but got ' + String(mustBeTrue);
+  }
+  $ERROR(message);
+}
+
+assert._isSameValue = function (a, b) {
+  if (a === b) {
+    // Handle +/-0 vs. -/+0
+    return a !== 0 || 1 / a === 1 / b;
+  }
+
+  // Handle NaN vs. NaN
+  return a !== a && b !== b;
+};
+
+assert.sameValue = function (actual, expected, message) {
+  if (assert._isSameValue(actual, expected)) {
+    return;
+  }
+
+  if (message === undefined) {
+    message = '';
+  } else {
+    message += ' ';
+  }
+
+  message += 'Expected SameValue(«' + String(actual) + '», «' + String(expected) + '») to be true';
+
+  $ERROR(message);
+};
+
+assert.notSameValue = function (actual, unexpected, message) {
+  if (!assert._isSameValue(actual, unexpected)) {
+    return;
+  }
+
+  if (message === undefined) {
+    message = '';
+  } else {
+    message += ' ';
+  }
+
+  message += 'Expected SameValue(«' + String(actual) + '», «' + String(unexpected) + '») to be false';
+
+  $ERROR(message);
+};
+
+assert.throws = function (expectedErrorConstructor, func, message) {
+  if (typeof func !== "function") {
+    $ERROR('assert.throws requires two arguments: the error constructor ' +
+      'and a function to run');
+    return;
+  }
+  if (message === undefined) {
+    message = '';
+  } else {
+    message += ' ';
+  }
+
+  try {
+    func();
+  } catch (thrown) {
+    if (typeof thrown !== 'object' || thrown === null) {
+      message += 'Thrown value was not an object!';
+      $ERROR(message);
+    } else if (thrown.constructor !== expectedErrorConstructor) {
+      message += 'Expected a ' + expectedErrorConstructor.name + ' but got a ' + thrown.constructor.name;
+      $ERROR(message);
+    }
+    return;
+  }
+
+  message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all';
+  $ERROR(message);
+};
+
+assert.throws.early = function(err, code) {
+  let wrappedCode = `function wrapperFn() { ${code} }`;
+  let ieval = eval;
+
+  assert.throws(err, () => { Function(wrappedCode); }, `Function: ${code}`);
+};
+
+
+// Create workers and start them all spinning.  We set atomic slots to make
+// them go into a wait, thus controlling the waiting order.  Then we wake them
+// one by one and observe the wakeup order.
+
+for ( var i=0 ; i < 3 ; i++ ) {
+$262.agent.start(
+`
+$262.agent.receiveBroadcast(function (sab) {
+  var ia = new Int32Array(sab);
+  while (Atomics.load(ia, ${i+1}) == 0);
+  $262.agent.report(${i} + Atomics.wait(ia, 0, 0));
+  $262.agent.leaving();
+})
+`);
+}
+
+var ia = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT*4));
+$262.agent.broadcast(ia.buffer);
+
+// Make them sleep in order 0 1 2 on ia[0]
+for ( var i=0 ; i < 3 ; i++ ) {
+  Atomics.store(ia, i+1, 1);
+  $262.agent.sleep(500);
+}
+
+// Wake them up one at a time and check the order is 0 1 2
+for ( var i=0 ; i < 3 ; i++ ) {
+  assert.sameValue(Atomics.wake(ia, 0, 1), 1);
+  assert.sameValue(getReport(), i + "ok");
+}
+
+function getReport() {
+    var r;
+    while ((r = $262.agent.getReport()) == null)
+        $262.agent.sleep(100);
+    return r;
+}
+
+
+
+;$DONE();
+;$.destroy();

+ 10 - 0
test/$262/rlexe.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<regress-exe>
+  <test>
+    <default>
+      <compile-flags>-$262</compile-flags>
+      <tags>exclude_xplat</tags>
+      <files>$262test.js</files>
+    </default>
+  </test>
+</regress-exe>

+ 26 - 6
test/es7/atomics_test.js

@@ -101,8 +101,12 @@ var tests = [{
                         
                         assert.throws(() => atomicFunction.op(view), RangeError, "Calling "+atomicFunction.fullname+" with 1 param only is not valid", atomicFunction.fullname+": function called with too few arguments");
                         assert.throws(() => atomicFunction.op(view, 0), RangeError, "Calling "+atomicFunction.fullname+" with 2 params only is not valid", atomicFunction.fullname+": function called with too few arguments");
-                        [undefined, -1, 1.1, "hi", NaN, {}, Infinity, -Infinity].forEach(function (index) {
-                            assert.throws(() => atomicFunction.op(view, index, 1), RangeError, "Only positive interger allowed, not " + index, "Access index is out of range");
+                        [undefined, 1.1, "hi", NaN, {}].forEach(function (index) {
+                            atomicFunction.op(view, index, 1);
+                        });
+                        
+                        [-1, Infinity, -Infinity].forEach(function (index) {
+                           assert.throws(() => atomicFunction.op(view, index, 1), RangeError, "Only positive interger allowed, not " + index, "Access index is out of range");
                         });
                         
                         assert.throws(() => atomicFunction.op(view, elements, 1), RangeError, "index is out of bound " + elements, "Access index is out of range");
@@ -131,7 +135,11 @@ var tests = [{
                     assert.throws(() => Atomics.compareExchange(view), RangeError, "Calling Atomics.compareExchange with 1 param only is not valid", "Atomics.compareExchange: function called with too few arguments");
                     assert.throws(() => Atomics.compareExchange(view, 0), RangeError, "Calling Atomics.compareExchange with 2 params only is not valid", "Atomics.compareExchange: function called with too few arguments");
                     assert.throws(() => Atomics.compareExchange(view, 0, 0), RangeError, "Calling Atomics.compareExchange with 3 params only is not valid", "Atomics.compareExchange: function called with too few arguments");
-                    [undefined, -1, 1.1, "hi", NaN, {}, Infinity, -Infinity].forEach(function (index) {
+                    [undefined, 1.1, "hi", NaN, {}].forEach(function (index) {
+                        Atomics.compareExchange(view, index, 0, 0);
+                    });
+                    
+                    [-1, Infinity, -Infinity].forEach(function (index) {
                         assert.throws(() => Atomics.compareExchange(view, index, 0, 0), RangeError, "Only positive interger allowed not, " + index, "Access index is out of range");
                     });
                     
@@ -150,7 +158,11 @@ var tests = [{
                     var view = new item.ctor(sab, offset * item.ctor.BYTES_PER_ELEMENT, length);
                     
                     assert.throws(() => Atomics.load(view), RangeError, "Calling Atomics.load with 1 param only is not valid", "Atomics.load: function called with too few arguments");
-                    [undefined, -1, 1.1, "hi", NaN, {}, Infinity, -Infinity].forEach(function (index) {
+                    [undefined, 1.1, "hi", NaN, {}].forEach(function (index) {
+                        Atomics.load(view, index);
+                    });
+                    
+                    [-1, Infinity, -Infinity].forEach(function (index) {
                         assert.throws(() => Atomics.load(view, index), RangeError, "Only positive interger allowed, not " + index, "Access index is out of range");
                     });
                     
@@ -169,7 +181,11 @@ var tests = [{
                 
                 assert.throws(() => Atomics.wait(view), RangeError, "Calling Atomics.wait with 1 param only is not valid", "Atomics.wait: function called with too few arguments");
                 assert.throws(() => Atomics.wait(view, 0), RangeError, "Calling Atomics.wait with 1 param only is not valid", "Atomics.wait: function called with too few arguments");
-                [undefined, -1, 1.1, "hi", NaN, {}, Infinity, -Infinity].forEach(function (index) {
+                [undefined, 1.1, "hi", NaN, {}].forEach(function (index) {
+                    Atomics.wait(view, index, 1);
+                });
+                
+                [-1, Infinity, -Infinity].forEach(function (index) {
                     assert.throws(() => Atomics.wait(view, index, 0), RangeError, "Only positive interger allowed, not " + index, "Access index is out of range");
                 });
                 
@@ -186,7 +202,11 @@ var tests = [{
                 var view = new Int32Array(sab, offset * Int32Array.BYTES_PER_ELEMENT, length);
                 
                 assert.throws(() => Atomics.wake(view), RangeError, "Calling Atomics.wake with 1 param only is not valid", "Atomics.wake: function called with too few arguments");
-                [undefined, -1, 1.1, "hi", NaN, {}, Infinity, -Infinity].forEach(function (index) {
+                [undefined, 1.1, "hi", NaN, {}].forEach(function (index) {
+                    Atomics.wake(view, index, 1);
+                });
+                
+                [-1, Infinity, -Infinity].forEach(function (index) {
                     assert.throws(() => Atomics.wake(view, index, 1), RangeError, "Only positive interger allowed, not " + index, "Access index is out of range");
                 });
                 

+ 5 - 0
test/rlexedirs.xml

@@ -5,6 +5,11 @@
     <files>UnitTestFramework</files>
   </default>
 </dir>
+<dir>
+  <default>
+    <files>$262</files>
+  </default>
+</dir>
 <dir>
   <default>
     <files>Array</files>