Переглянути джерело

Implementation of Relative indexing method proposal (#6610)

Implement TC39 stage 3 proposal Relative Indexing:
https://github.com/tc39/proposal-relative-indexing-method
- Array.prototype.at
- String.prototype.at
- TypedArray.prototype.at

Implementation passes all relevant test262 tests
MadProbe 5 роки тому
батько
коміт
c97320d3ff
31 змінених файлів з 1858 додано та 1214 видалено
  1. 9 0
      lib/Backend/Inline.cpp
  2. 3 0
      lib/Backend/InliningDecider.cpp
  3. 4 1
      lib/Backend/JnHelperMethodList.h
  4. 1 0
      lib/Runtime/Base/JnDirectFields.h
  5. 2 2
      lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h
  6. 300 300
      lib/Runtime/Library/InJavascript/JsBuiltIn.bc.32b.h
  7. 300 300
      lib/Runtime/Library/InJavascript/JsBuiltIn.bc.64b.h
  8. 303 303
      lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.32b.h
  9. 303 303
      lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.64b.h
  10. 89 1
      lib/Runtime/Library/JavascriptArray.cpp
  11. 6 1
      lib/Runtime/Library/JavascriptArray.h
  12. 4 0
      lib/Runtime/Library/JavascriptBuiltInFunctionList.h
  13. 9 2
      lib/Runtime/Library/JavascriptLibrary.cpp
  14. 65 0
      lib/Runtime/Library/JavascriptString.cpp
  15. 3 0
      lib/Runtime/Library/JavascriptString.h
  16. 16 0
      lib/Runtime/Library/TypedArray.cpp
  17. 3 0
      lib/Runtime/Library/TypedArray.h
  18. 3 0
      lib/Runtime/LibraryFunction.h
  19. 131 0
      test/Array/array_at.js
  20. 6 0
      test/Array/rlexe.xml
  21. 17 0
      test/DebuggerCommon/ES6_proto_invalidation.js.dbg.baseline
  22. 1 0
      test/DebuggerCommon/TempStrExpr.js.dbg.baseline
  23. 3 0
      test/DebuggerCommon/TypedArray.js.dbg.baseline
  24. 6 0
      test/DebuggerCommon/array_prototest.js.dbg.baseline
  25. 1 0
      test/DebuggerCommon/indexprop.js.dbg.baseline
  26. 1 0
      test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline
  27. 6 0
      test/Strings/rlexe.xml
  28. 134 0
      test/Strings/string_at.js
  29. 7 1
      test/es6/unscopablesWithScopeTest.js
  30. 7 0
      test/typedarray/rlexe.xml
  31. 115 0
      test/typedarray/typedarray_at.js

+ 9 - 0
lib/Backend/Inline.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "Backend.h"
@@ -3652,6 +3653,10 @@ Inline::SetupInlineInstrForCallDirect(Js::BuiltinFunction builtInId, IR::Instr*
 {
     switch(builtInId)
     {
+    case Js::BuiltinFunction::JavascriptArray_At:
+        callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_At, callInstr->m_func));
+        break;
+
     case Js::BuiltinFunction::JavascriptArray_Concat:
         callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Concat, callInstr->m_func));
         break;
@@ -3692,6 +3697,10 @@ Inline::SetupInlineInstrForCallDirect(Js::BuiltinFunction builtInId, IR::Instr*
         callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperArray_Unshift, callInstr->m_func));
         break;
 
+    case Js::BuiltinFunction::JavascriptString_At:
+        callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_At, callInstr->m_func));
+        break;
+
     case Js::BuiltinFunction::JavascriptString_Concat:
         callInstr->SetSrc1(IR::HelperCallOpnd::New(IR::JnHelperMethod::HelperString_Concat, callInstr->m_func));
         break;

+ 3 - 0
lib/Backend/InliningDecider.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "Backend.h"
@@ -505,12 +506,14 @@ bool InliningDecider::GetBuiltInInfoCommon(
         *inlineCandidateOpCode = Js::OpCode::InlineArrayPop;
         break;
 
+    case Js::JavascriptBuiltInFunction::JavascriptArray_At:
     case Js::JavascriptBuiltInFunction::JavascriptArray_Concat:
     case Js::JavascriptBuiltInFunction::JavascriptArray_Reverse:
     case Js::JavascriptBuiltInFunction::JavascriptArray_Shift:
     case Js::JavascriptBuiltInFunction::JavascriptArray_Slice:
     case Js::JavascriptBuiltInFunction::JavascriptArray_Splice:
 
+    case Js::JavascriptBuiltInFunction::JavascriptString_At:
     case Js::JavascriptBuiltInFunction::JavascriptString_Link:
         goto CallDirectCommon;
 

+ 4 - 1
lib/Backend/JnHelperMethodList.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #ifndef HELPERCALL
@@ -475,6 +476,7 @@ HELPERCALL(SaveAllRegistersNoSse2AndBranchBailOut, nullptr, AttrCanNotBeReentran
 #endif
 
 //Helpers for inlining built-ins
+HELPERCALLCHK(Array_At, Js::JavascriptArray::EntryAt, 0)
 HELPERCALLCHK(Array_Concat, Js::JavascriptArray::EntryConcat, 0)
 HELPERCALLCHK(Array_IndexOf, Js::JavascriptArray::EntryIndexOf, 0)
 HELPERCALLCHK(Array_Includes, Js::JavascriptArray::EntryIncludes, 0)
@@ -494,6 +496,7 @@ HELPERCALLCHK(Array_Splice, Js::JavascriptArray::EntrySplice, 0)
 HELPERCALLCHK(Array_Unshift, Js::JavascriptArray::EntryUnshift, 0)
 HELPERCALLCHK(Array_IsArray, Js::JavascriptArray::EntryIsArray, 0)
 
+HELPERCALL(String_At, Js::JavascriptString::EntryAt, 0)
 HELPERCALL(String_Concat, Js::JavascriptString::EntryConcat, 0)
 HELPERCALL(String_CharCodeAt, Js::JavascriptString::EntryCharCodeAt, 0)
 HELPERCALL(String_CharAt, Js::JavascriptString::EntryCharAt, 0)

+ 1 - 0
lib/Runtime/Base/JnDirectFields.h

@@ -95,6 +95,7 @@ ENTRY(anchor)
 ENTRY(apply)
 ENTRY(Array)
 ENTRY(assign)
+ENTRY(at)
 ENTRY(atEnd)
 ENTRY(big)
 ENTRY(bind)

+ 2 - 2
lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h

@@ -6,6 +6,6 @@
 // NOTE: If there is a merge conflict the correct fix is to make a new GUID.
 // This file was generated with tools/regenByteCode.py
 
-// {0b91cf53-8d39-4c75-9814-00c7df2d7813}
+// {4f97d737-2b2b-49a9-8a25-aa476fafb4c0}
 const GUID byteCodeCacheReleaseFileVersion =
-{ 0x0b91cf53, 0x8d39, 0x4c75, {0x98, 0x14, 0x00, 0xc7, 0xdf, 0x2d, 0x78, 0x13 } };
+{ 0x4f97d737, 0x2b2b, 0x49a9, {0x8a, 0x25, 0xaa, 0x47, 0x6f, 0xaf, 0xb4, 0xc0 } };

Різницю між файлами не показано, бо вона завелика
+ 300 - 300
lib/Runtime/Library/InJavascript/JsBuiltIn.bc.32b.h


Різницю між файлами не показано, бо вона завелика
+ 300 - 300
lib/Runtime/Library/InJavascript/JsBuiltIn.bc.64b.h


Різницю між файлами не показано, бо вона завелика
+ 303 - 303
lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.32b.h


Різницю між файлами не показано, бо вона завелика
+ 303 - 303
lib/Runtime/Library/InJavascript/JsBuiltIn.nojit.bc.64b.h


+ 89 - 1
lib/Runtime/Library/JavascriptArray.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
@@ -8770,6 +8771,93 @@ Case0:
             return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Value));
     }
 
+    // Relative indexing proposal 
+    // Array.prototype.at(index): https://tc39.es/proposal-relative-indexing-method/#sec-array.prototype.at
+    // Spec: https://tc39.es/proposal-relative-indexing-method
+    // Github: https://github.com/tc39/proposal-relative-indexing-method
+    Var JavascriptArray::EntryAt(RecyclableObject* function, CallInfo callInfo, ...)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Array_At);
+        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
+        
+        ARGUMENTS(args, callInfo);
+        ScriptContext* scriptContext = function->GetScriptContext();
+        JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext());
+        AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.at"));
+
+        Assert(!(callInfo.Flags & CallFlags_New));
+
+        CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_at);
+
+        if (args.Info.Count == 0)
+        {
+            JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.at"));
+        }
+
+        BigIndex length;
+        JavascriptArray* pArr = nullptr;
+        RecyclableObject* obj = nullptr;
+
+        JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.at"), &pArr, &obj, &length));
+
+        if (length.IsSmallIndex())
+        {
+            JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::AtHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext));
+        }
+        Assert(pArr == nullptr || length.IsUint32Max()); // if pArr is not null lets make sure length is safe to cast, which will only happen if length is a uint32max
+        JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::AtHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext));
+        JIT_HELPER_END(Array_At);
+    }
+
+    template <typename T>
+    Var JavascriptArray::AtHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext)
+    {
+        JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext());
+        SETOBJECT_FOR_MUTATION(jsReentLock, pArr);
+
+        // 3. Let relativeIndex be ? ToInteger(index).
+        int64_t relativeIndex = 0;
+
+        if (args.Info.Count > 1)
+        {
+            JS_REENTRANT(jsReentLock, relativeIndex = NumberUtilities::TryToInt64(JavascriptConversion::ToInteger(args[1], scriptContext)));
+        }
+
+        // 4. If relativeIndex >= 0, then
+        //     a. Let k be relativeIndex.
+        // 5. Else,
+        //     a. Let k be len + relativeIndex.
+        int64_t k = relativeIndex;
+        
+        if (relativeIndex < 0)
+        {
+            k += (int64_t)length;
+        }
+        
+        // 6. If k < 0 or k >= len, then return undefined.
+        if (k < 0 || k >= (int64_t)length)
+        {
+            return scriptContext->GetLibrary()->GetUndefined();
+        }
+        
+        
+        if (typedArrayBase)
+        {
+            // %typedarray%.prototype.at(index): https://tc39.es/proposal-relative-indexing-method/#sec-array.prototype.at
+            // 8. Return ? Get(O, ! ToString(k)).
+            return typedArrayBase->DirectGetItem((uint32_t)k);
+        }
+        else
+        {
+            Var element;
+            // 7. Return ? Get(O, ! ToString(k)).
+            JS_REENTRANT(jsReentLock, element = JavascriptOperators::GetItem(obj, (T)k, scriptContext));
+            return element;
+        }
+    }
+
+    template Var JavascriptArray::AtHelper<uint32_t>(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, uint32_t length, Arguments& args, ScriptContext* scriptContext);
+
     Var JavascriptArray::EntryEvery(RecyclableObject* function, CallInfo callInfo, ...)
     {
         PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

+ 6 - 1
lib/Runtime/Library/JavascriptArray.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft. All rights reserved.
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
@@ -233,6 +234,7 @@ namespace Js
         {
         public:
             static FunctionInfo NewInstance;
+            static FunctionInfo At;
             static FunctionInfo Concat;
             static FunctionInfo Every;
             static FunctionInfo Filter;
@@ -271,6 +273,7 @@ namespace Js
         static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
         static Var NewInstance(RecyclableObject* function, Arguments args);
         static Var ProfiledNewInstance(RecyclableObject* function, CallInfo callInfo, ...);
+        static Var EntryAt(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryConcat(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryEvery(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryFilter(RecyclableObject* function, CallInfo callInfo, ...);
@@ -503,6 +506,8 @@ namespace Js
         static Var SliceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
         static Var SliceObjectHelper(RecyclableObject* obj, uint32 sliceStart, uint32 start, JavascriptArray* newArr, RecyclableObject* newObj, uint32 newLen, ScriptContext* scriptContext);
         template <typename T = uint32>
+        static Var AtHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
+        template <typename T = uint32>
         static Var EveryHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
         template <typename T = uint32>
         static Var EveryObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);

+ 4 - 0
lib/Runtime/Library/JavascriptBuiltInFunctionList.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 
@@ -41,6 +42,7 @@ BUILTIN(GlobalObject, ChWriteTraceEvent, EntryChWriteTraceEvent, FunctionInfo::E
 #endif /* IR_VIEWER */
 
 BUILTIN(JavascriptArray, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject)
+BUILTIN(JavascriptArray, At, EntryAt, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptArray, Concat, EntryConcat, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptArray, Every, EntryEvery, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptArray, Filter, EntryFilter, FunctionInfo::ErrorOnNew)
@@ -219,6 +221,7 @@ BUILTIN(JavascriptRegExp, GetterSticky, EntryGetterSticky, FunctionInfo::ErrorOn
 BUILTIN(JavascriptRegExp, GetterUnicode, EntryGetterUnicode, FunctionInfo::ErrorOnNew | FunctionInfo::HasNoSideEffect)
 BUILTIN(JavascriptRegExp, GetterDotAll, EntryGetterDotAll, FunctionInfo::ErrorOnNew | FunctionInfo::HasNoSideEffect)
 BUILTIN(JavascriptString, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject)
+BUILTIN(JavascriptString, At, EntryAt, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptString, CharAt, EntryCharAt, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptString, CharCodeAt, EntryCharCodeAt, FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptString, CodePointAt, EntryCodePointAt, FunctionInfo::ErrorOnNew)
@@ -348,6 +351,7 @@ BUILTIN(TypedArrayBase, Set, EntrySet, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, Subarray, EntrySubarray, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, Of, EntryOf, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, From, EntryFrom, FunctionInfo::ErrorOnNew)
+BUILTIN(TypedArrayBase, At, EntryAt, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, CopyWithin, EntryCopyWithin, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, Entries, EntryEntries, FunctionInfo::ErrorOnNew)
 BUILTIN(TypedArrayBase, Every, EntryEvery, FunctionInfo::ErrorOnNew)

+ 9 - 2
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -2021,7 +2021,7 @@ namespace Js
 
     bool JavascriptLibrary::InitializeArrayPrototype(DynamicObject* arrayPrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
     {
-        typeHandler->Convert(arrayPrototype, mode, 26);
+        typeHandler->Convert(arrayPrototype, mode, 27);
         // Note: Any new function addition/deletion/modification should also be updated in JavascriptLibrary::ProfilerRegisterArray
         // so that the update is in sync with profiler
 
@@ -2032,6 +2032,7 @@ namespace Js
 
         Field(JavascriptFunction*)* builtinFuncs = library->GetBuiltinFunctions();
 
+        builtinFuncs[BuiltinFunction::JavascriptArray_At]                 = library->AddFunctionToLibraryObject(arrayPrototype, PropertyIds::at,              &JavascriptArray::EntryInfo::At,                1);
         builtinFuncs[BuiltinFunction::JavascriptArray_Push]               = library->AddFunctionToLibraryObject(arrayPrototype, PropertyIds::push,            &JavascriptArray::EntryInfo::Push,              1);
         builtinFuncs[BuiltinFunction::JavascriptArray_Concat]             = library->AddFunctionToLibraryObject(arrayPrototype, PropertyIds::concat,          &JavascriptArray::EntryInfo::Concat,            1);
         builtinFuncs[BuiltinFunction::JavascriptArray_Join]               = library->AddFunctionToLibraryObject(arrayPrototype, PropertyIds::join,            &JavascriptArray::EntryInfo::Join,              1);
@@ -2104,6 +2105,7 @@ namespace Js
 
         DynamicType* dynamicType = DynamicType::New(scriptContext, TypeIds_Object, library->nullValue, nullptr, NullTypeHandler<false>::GetDefaultInstance(), false);
         DynamicObject* unscopablesList = DynamicObject::New(library->GetRecycler(), dynamicType);
+        unscopablesList->SetProperty(PropertyIds::at,         JavascriptBoolean::ToVar(true, scriptContext), PropertyOperation_None, nullptr);
         unscopablesList->SetProperty(PropertyIds::copyWithin, JavascriptBoolean::ToVar(true, scriptContext), PropertyOperation_None, nullptr);
         unscopablesList->SetProperty(PropertyIds::entries,    JavascriptBoolean::ToVar(true, scriptContext), PropertyOperation_None, nullptr);
         unscopablesList->SetProperty(PropertyIds::fill,       JavascriptBoolean::ToVar(true, scriptContext), PropertyOperation_None, nullptr);
@@ -2321,6 +2323,7 @@ namespace Js
         library->AddMember(typedarrayPrototype, PropertyIds::constructor, library->typedArrayConstructor);
         library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::set, &TypedArrayBase::EntryInfo::Set, 2);
         library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::subarray, &TypedArrayBase::EntryInfo::Subarray, 2);
+        library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::at, &TypedArrayBase::EntryInfo::At, 1);
         library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::copyWithin, &TypedArrayBase::EntryInfo::CopyWithin, 2);
         library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::every, &TypedArrayBase::EntryInfo::Every, 1);
         library->AddFunctionToLibraryObject(typedarrayPrototype, PropertyIds::fill, &TypedArrayBase::EntryInfo::Fill, 1);
@@ -3721,6 +3724,9 @@ namespace Js
         case PropertyIds::codePointAt:
             return BuiltinFunction::JavascriptString_CodePointAt;
 
+        case PropertyIds::at:
+            return BuiltinFunction::JavascriptArray_At;
+
         case PropertyIds::push:
             return BuiltinFunction::JavascriptArray_Push;
 
@@ -4380,7 +4386,7 @@ namespace Js
 
     bool JavascriptLibrary::InitializeStringPrototype(DynamicObject* stringPrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode)
     {
-        typeHandler->Convert(stringPrototype, mode, 38);
+        typeHandler->Convert(stringPrototype, mode, 39);
         // Note: Any new function addition/deletion/modification should also be updated in JavascriptLibrary::ProfilerRegisterString
         // so that the update is in sync with profiler
         ScriptContext* scriptContext = stringPrototype->GetScriptContext();
@@ -4401,6 +4407,7 @@ namespace Js
             /* builtinFuncs[BuiltinFunction::String_Normalize] =*/library->AddFunctionToLibraryObject(stringPrototype, PropertyIds::normalize,          &JavascriptString::EntryInfo::Normalize,            0);
         }
 
+        builtinFuncs[BuiltinFunction::JavascriptString_At]                = library->AddFunctionToLibraryObject(stringPrototype, PropertyIds::at,                 &JavascriptString::EntryInfo::At,               1);
         builtinFuncs[BuiltinFunction::JavascriptString_CharAt]            = library->AddFunctionToLibraryObject(stringPrototype, PropertyIds::charAt,             &JavascriptString::EntryInfo::CharAt,               1);
         builtinFuncs[BuiltinFunction::JavascriptString_CharCodeAt]        = library->AddFunctionToLibraryObject(stringPrototype, PropertyIds::charCodeAt,         &JavascriptString::EntryInfo::CharCodeAt,           1);
         builtinFuncs[BuiltinFunction::JavascriptString_Concat]            = library->AddFunctionToLibraryObject(stringPrototype, PropertyIds::concat,             &JavascriptString::EntryInfo::Concat,               1);

+ 65 - 0
lib/Runtime/Library/JavascriptString.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 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"
@@ -696,6 +697,70 @@ case_2:
         return builder.ToString();
     }
 
+
+    // Relative indexing proposal 
+    // String.prototype.at(index): https://tc39.es/proposal-relative-indexing-method/#sec-string.prototype.at
+    // Spec: https://tc39.es/proposal-relative-indexing-method
+    // Github: https://github.com/tc39/proposal-relative-indexing-method
+    Var JavascriptString::EntryAt(RecyclableObject* function, CallInfo callInfo, ...) {
+        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
+
+        ARGUMENTS(args, callInfo);
+        ScriptContext* scriptContext = function->GetScriptContext();
+        JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext());
+
+        Assert(!(callInfo.Flags & CallFlags_New));
+
+        JavascriptString * pThis = nullptr;
+
+        // 1. Let O be ? RequireObjectCoercible(this value).
+        // 2. Let S be ? ToString(O).
+        JS_REENTRANT(jsReentLock, GetThisStringArgument(args, scriptContext, _u("String.prototype.at"), &pThis));
+
+        // 3. Let len be the length of S.
+        charcount_t len = pThis->GetLength();
+
+        // 4. Let relativeIndex be ? ToInteger(index).
+        int64_t relativeIndex = 0;
+
+        if (args.Info.Count > 1)
+        {
+            JS_REENTRANT(jsReentLock, relativeIndex = NumberUtilities::TryToInt64(JavascriptConversion::ToInteger(args[1], scriptContext)));
+        }
+
+        // 5. If relativeIndex >= 0, then
+        //     a. Let k be relativeIndex.
+        // 6. Else,
+        //     a. Let k be len + relativeIndex.
+        int64_t k = relativeIndex;
+        
+        if (relativeIndex < 0)
+        {
+            k += len;
+        }
+        
+        // 7. If k < 0 or k >= len, then return undefined.
+        if (k < 0 || k >= (int64_t)len)
+        {
+            return scriptContext->GetLibrary()->GetUndefined();
+        }
+        
+        Var value;
+        // 8. Return the String value consisting of only the code unit at position k in S.
+        if (pThis->GetItemAt((charcount_t)k, &value))
+        {
+#ifdef ENABLE_SPECTRE_RUNTIME_MITIGATIONS
+            value = BreakSpeculation(value);
+#endif
+            return value;
+        }
+        else
+        {
+            // Yes, i except this to be
+            return scriptContext->GetLibrary()->GetUndefined();
+        }
+    }
+
     Var JavascriptString::EntryCharAt(RecyclableObject* function, CallInfo callInfo, ...)
     {
         PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

+ 3 - 0
lib/Runtime/Library/JavascriptString.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #pragma once
@@ -216,6 +217,7 @@ namespace Js
         {
         public:
             static FunctionInfo NewInstance;
+            static FunctionInfo At;
             static FunctionInfo CharAt;
             static FunctionInfo CharCodeAt;
             static FunctionInfo CodePointAt;
@@ -262,6 +264,7 @@ namespace Js
         };
 
         static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
+        static Var EntryAt(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryCharAt(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryCharCodeAt(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryCodePointAt(RecyclableObject* function, CallInfo callInfo, ...);

+ 16 - 0
lib/Runtime/Library/TypedArray.cpp

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 // Implementation for typed arrays based on ArrayBuffer.
@@ -1578,6 +1579,21 @@ namespace Js
         return JavascriptArray::OfHelper(true, args, scriptContext);
     }
 
+    Var TypedArrayBase::EntryAt(RecyclableObject* function, CallInfo callInfo, ...)
+    {
+        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
+
+        ARGUMENTS(args, callInfo);
+        ScriptContext* scriptContext = function->GetScriptContext();
+
+        Assert(!(callInfo.Flags & CallFlags_New));
+        CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(TypedArray_Prototype_at);
+
+        TypedArrayBase* typedArrayBase = ValidateTypedArray(args, scriptContext, _u("[TypedArray].prototype.at"));
+
+        return JavascriptArray::AtHelper<uint32>(nullptr, typedArrayBase, typedArrayBase, typedArrayBase->GetLength(), args, scriptContext);
+    }
+
     Var TypedArrayBase::EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...)
     {
         PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

+ 3 - 0
lib/Runtime/Library/TypedArray.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 //  Implements typed array.
@@ -32,6 +33,7 @@ namespace Js
 
             static FunctionInfo From;
             static FunctionInfo Of;
+            static FunctionInfo At;
             static FunctionInfo CopyWithin;
             static FunctionInfo Entries;
             static FunctionInfo Every;
@@ -70,6 +72,7 @@ namespace Js
         static Var EntrySubarray(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryFrom(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryOf(RecyclableObject* function, CallInfo callInfo, ...);
+        static Var EntryAt(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryEntries(RecyclableObject* function, CallInfo callInfo, ...);
         static Var EntryEvery(RecyclableObject* function, CallInfo callInfo, ...);

+ 3 - 0
lib/Runtime/LibraryFunction.h

@@ -1,5 +1,6 @@
 //-------------------------------------------------------------------------------------------------------
 // Copyright (C) Microsoft. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 /*
@@ -19,6 +20,7 @@ LIBRARY_FUNCTION(Math,          Asin,               1,    BIF_TypeSpecUnaryToFlo
 LIBRARY_FUNCTION(Math,          Atan,               1,    BIF_TypeSpecUnaryToFloat                          , Math::EntryInfo::Atan)
 LIBRARY_FUNCTION(Math,          Atan2,              2,    BIF_TypeSpecAllToFloat                            , Math::EntryInfo::Atan2)
 LIBRARY_FUNCTION(Math,          Ceil,               1,    BIF_TypeSpecDstToInt | BIF_TypeSpecSrc1ToFloat    , Math::EntryInfo::Ceil)
+LIBRARY_FUNCTION(JavascriptString,        At,                 2,    BIF_UseSrc0                                       , JavascriptString::EntryInfo::At)
 LIBRARY_FUNCTION(JavascriptString,        CodePointAt,        2,    BIF_TypeSpecSrc2ToInt | BIF_UseSrc0               , JavascriptString::EntryInfo::CodePointAt)
 LIBRARY_FUNCTION(JavascriptString,        CharAt,             2,    BIF_UseSrc0                                       , JavascriptString::EntryInfo::CharAt  )
 LIBRARY_FUNCTION(JavascriptString,        CharCodeAt,         2,    BIF_UseSrc0                                       , JavascriptString::EntryInfo::CharCodeAt )
@@ -60,6 +62,7 @@ LIBRARY_FUNCTION(Math,          Round,              1,    BIF_TypeSpecDstToInt |
 LIBRARY_FUNCTION(Math,          Sin,                1,    BIF_TypeSpecUnaryToFloat                          , Math::EntryInfo::Sin)
 LIBRARY_FUNCTION(Math,          Sqrt,               1,    BIF_TypeSpecUnaryToFloat                          , Math::EntryInfo::Sqrt)
 LIBRARY_FUNCTION(Math,          Tan,                1,    BIF_TypeSpecUnaryToFloat                          , Math::EntryInfo::Tan)
+LIBRARY_FUNCTION(JavascriptArray,         At,                 2,    BIF_UseSrc0                                       , JavascriptArray::EntryInfo::At)
 LIBRARY_FUNCTION(JavascriptArray,         Concat,             15,   BIF_UseSrc0 | BIF_VariableArgsNumber              , JavascriptArray::EntryInfo::Concat)
 LIBRARY_FUNCTION(JavascriptArray,         IndexOf,            2,    BIF_UseSrc0                                       , JavascriptArray::EntryInfo::IndexOf)
 LIBRARY_FUNCTION(JavascriptArray,         Includes,           2,    BIF_UseSrc0                                       , JavascriptArray::EntryInfo::Includes)

+ 131 - 0
test/Array/array_at.js

@@ -0,0 +1,131 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+const ArrayPrototype = Array.prototype;
+const at = ArrayPrototype.at;
+
+const call = Function.call.bind(Function.call); // (fn: Function, thisArg: any, ...args: any[]) => any
+
+
+const tests = [
+    {
+        name: "Existence of Array.prototype.at",
+        body: function () {
+            assert.isTrue(typeof at === "function", "Array.prototype.at should be a function");
+        }
+    },
+    {
+        name: "Array.prototype.at's descriptor",
+        body: function () {
+            const desc = Object.getOwnPropertyDescriptor(ArrayPrototype, "at");
+            assert.areEqual(desc.configurable, true, "Array.prototype.at should be configurable");
+            assert.areEqual(desc.writable, true, "Array.prototype.at should be writable");
+            assert.areEqual(desc.enumerable, false, "Array.prototype.at should not be enumerable");
+        }
+    },
+    {
+        name: "Properties of Array.prototype.at",
+        body: function () {
+            const nameDesc = Object.getOwnPropertyDescriptor(at, "name");
+            assert.areEqual(at.name, "at", 'Array.prototype.at.name should be "at"');
+            assert.areEqual(nameDesc.configurable, true, "Array.prototype.at.name should be configurable");
+            assert.areEqual(nameDesc.writable, false, "Array.prototype.at.name should not be writable");
+            assert.areEqual(nameDesc.enumerable, false, "Array.prototype.at.name should not be enumerable");
+            const lengthDesc = Object.getOwnPropertyDescriptor(at, "length");
+            assert.areEqual(at.length, 1, 'Array.prototype.at.length should be 1');
+            assert.areEqual(lengthDesc.configurable, true, "Array.prototype.at.length should be configurable");
+            assert.areEqual(lengthDesc.writable, false, "Array.prototype.at.length should not be writable");
+            assert.areEqual(lengthDesc.enumerable, false, "Array.prototype.at.length should not be enumerable");
+        }
+    },
+    {
+        name: "Array.prototype.at called on nullish object",
+        body: function () {
+            assert.throws(() => { at.call(null) }, TypeError);
+            assert.throws(() => { at.call(undefined) }, TypeError);
+        }
+    },
+    {
+        name: "Indexation of array with non-numerical argument",
+        body: function () {
+            const array = [0, 1];
+
+            assert.areEqual(array.at(false), 0, 'array.at(false) should be 0');
+            assert.areEqual(array.at(null), 0, 'array.at(null) should be 0');
+            assert.areEqual(array.at(undefined), 0, 'array.at(undefined) should be 0');
+            assert.areEqual(array.at(""), 0, 'array.at("") should be 0');
+            assert.areEqual(array.at(function () { }), 0, 'array.at(function() {}) should be 0');
+            assert.areEqual(array.at([]), 0, 'array.at([]) should be 0');
+            assert.areEqual(array.at(true), 1, 'array.at(true) should be 1');
+            assert.areEqual(array.at("1"), 1, 'array.at("1") should be 1');
+        }
+    },
+    {
+        name: "Indexation of array with negative numerical argument",
+        body: function () {
+            const array = [7, 1, 4, , 2, 12];
+
+            assert.areEqual(array.at(0), 7, 'array.at(0) should be 7');
+            assert.areEqual(array.at(-1), 12, 'array.at(-1) should be 12');
+            assert.areEqual(array.at(-2), 2, 'array.at(-2) should be 2');
+            assert.areEqual(array.at(-3), undefined, 'array.at(-3) should be undefined');
+            assert.areEqual(array.at(-4), 4, 'array.at(-4) should be 4');
+        }
+    },
+    {
+        name: "Indexation of array with positive numerical argument",
+        body: function () {
+            const array = [7, 1, 4, , 2, 12];
+
+            assert.areEqual(array.at(0), 7, 'array.at(0) should be 7');
+            assert.areEqual(array.at(1), 1, 'array.at(-1) should be 1');
+            assert.areEqual(array.at(2), 4, 'array.at(-2) should be 4');
+            assert.areEqual(array.at(3), undefined, 'array.at(-3) should be undefined');
+            assert.areEqual(array.at(4), 2, 'array.at(-4) should be 2');
+        }
+    },
+    {
+        name: "Out of bounds",
+        body: function () {
+            const array = [];
+
+            assert.areEqual(array.at(0), undefined, `array.at(0) should be undefined`);
+            assert.areEqual(array.at(-1), undefined, `array.at(-1) should be undefined`);
+            assert.areEqual(array.at(2), undefined, `array.at(2) should be undefined`);
+        }
+    },
+    {
+        name: "Argument ToInteger()",
+        body: function () {
+            let count = 0;
+            const index = {
+                valueOf() {
+                    count++;
+                    return 1;
+                }
+            };
+
+            const array = [0, 1, 2, 3];
+
+            assert.areEqual(array.at(index), 1, 'result of array.at(index) should be 1');
+            assert.areEqual(count, 1, 'The value of count should be 1');
+        }
+    },
+    {
+        name: "Array.prototype.at called on Array-Like object",
+        body: function () {
+            const arraylike = { 0: 1, 1: 2, 2: 3, length: 2 };
+
+            assert.areEqual(call(at, arraylike, 0), 1, `call(at, arraylike, 0) should be 1`);
+            assert.areEqual(call(at, arraylike, -1), 2, `call(at, arraylike, -1) should be 2`); // -1 + arraylike.length === 1
+            assert.areEqual(call(at, arraylike, 2), undefined, `call(at, arraylike, 2) should be undefined`);
+        }
+    }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 6 - 0
test/Array/rlexe.xml

@@ -6,6 +6,12 @@
       <baseline>array_fastinit.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>array_at.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>array_flat.js</files>

+ 17 - 0
test/DebuggerCommon/ES6_proto_invalidation.js.dbg.baseline

@@ -26,6 +26,11 @@
             "fromCodePoint": "function <large string>",
             "raw": "function <large string>"
           },
+          "at": {
+            "#__proto__": "function <large string>",
+            "length": "number 1",
+            "name": "string at"
+          },
           "indexOf": {
             "#__proto__": "function <large string>",
             "length": "number 1",
@@ -293,6 +298,11 @@
               "from": "function <large string>",
               "of": "function <large string>"
             },
+            "at": {
+              "#__proto__": "function <large string>",
+              "length": "number 1",
+              "name": "string at"
+            },
             "push": {
               "#__proto__": "function <large string>",
               "length": "number 1",
@@ -439,6 +449,7 @@
               "name": "string forEach"
             },
             "Symbol.unscopables": {
+              "at": "boolean true",
               "copyWithin": "boolean true",
               "entries": "boolean true",
               "fill": "boolean true",
@@ -522,6 +533,11 @@
               "from": "function <large string>",
               "of": "function <large string>"
             },
+            "at": {
+              "#__proto__": "function <large string>",
+              "length": "number 1",
+              "name": "string at"
+            },
             "push": {
               "#__proto__": "function <large string>",
               "length": "number 1",
@@ -668,6 +684,7 @@
               "name": "string forEach"
             },
             "Symbol.unscopables": {
+              "at": "boolean true",
               "copyWithin": "boolean true",
               "entries": "boolean true",
               "fill": "boolean true",

+ 1 - 0
test/DebuggerCommon/TempStrExpr.js.dbg.baseline

@@ -130,6 +130,7 @@
         "prototype": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "indexOf": "function <large string>",
           "lastIndexOf": "function <large string>",
           "replace": "function <large string>",

+ 3 - 0
test/DebuggerCommon/TypedArray.js.dbg.baseline

@@ -23,6 +23,7 @@
             "constructor": "function function() {\n    [native code]\n}",
             "set": "function function set() { [native code] }",
             "subarray": "function function subarray() { [native code] }",
+            "at": "function function at() { [native code] }",
             "copyWithin": "function function copyWithin() { [native code] }",
             "every": "function function every() { [native code] }",
             "fill": "function function fill() { [native code] }",
@@ -80,6 +81,7 @@
             "constructor": "function function() {\n    [native code]\n}",
             "set": "function function set() { [native code] }",
             "subarray": "function function subarray() { [native code] }",
+            "at": "function function at() { [native code] }",
             "copyWithin": "function function copyWithin() { [native code] }",
             "every": "function function every() { [native code] }",
             "fill": "function function fill() { [native code] }",
@@ -131,6 +133,7 @@
             "constructor": "function function() {\n    [native code]\n}",
             "set": "function function set() { [native code] }",
             "subarray": "function function subarray() { [native code] }",
+            "at": "function function at() { [native code] }",
             "copyWithin": "function function copyWithin() { [native code] }",
             "every": "function function every() { [native code] }",
             "fill": "function function fill() { [native code] }",

+ 6 - 0
test/DebuggerCommon/array_prototest.js.dbg.baseline

@@ -62,6 +62,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",
@@ -105,6 +106,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",
@@ -149,6 +151,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",
@@ -290,6 +293,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",
@@ -333,6 +337,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",
@@ -378,6 +383,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",

+ 1 - 0
test/DebuggerCommon/indexprop.js.dbg.baseline

@@ -39,6 +39,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",

+ 1 - 0
test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline

@@ -141,6 +141,7 @@
         "#__proto__": {
           "#__proto__": "Object {...}",
           "constructor": "function <large string>",
+          "at": "function <large string>",
           "push": "function <large string>",
           "concat": "function <large string>",
           "join": "function <large string>",

+ 6 - 0
test/Strings/rlexe.xml

@@ -1,5 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <regress-exe>
+  <test>
+    <default>
+      <files>string_at.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>charAt.js</files>

+ 134 - 0
test/Strings/string_at.js

@@ -0,0 +1,134 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+const StringPrototype = String.prototype;
+const at = StringPrototype.at;
+
+const call = Function.call.bind(Function.call); // (fn: Function, thisArg: any, ...args: any[]) => any
+
+
+const tests = [
+    {
+        name: "Existnce of String.prototype.at",
+        body: function () {
+            assert.isTrue(typeof at === "function", "String.prototype.at should be a function");
+        }
+    },
+    {
+        name: "String.prototype.at's descriptor",
+        body: function () {
+            const desc = Object.getOwnPropertyDescriptor(StringPrototype, "at");
+            assert.areEqual(desc.configurable, true, "String.prototype.at should be configurable");
+            assert.areEqual(desc.writable, true, "String.prototype.at should be writable");
+            assert.areEqual(desc.enumerable, false, "String.prototype.at should not be enumerable");
+        }
+    },
+    {
+        name: "Properties of String.prototype.at",
+        body: function () {
+            const nameDesc = Object.getOwnPropertyDescriptor(at, "name");
+            assert.areEqual(at.name, "at", 'String.prototype.at.name should be "at"');
+            assert.areEqual(nameDesc.configurable, true, "String.prototype.at.name should be configurable");
+            assert.areEqual(nameDesc.writable, false, "String.prototype.at.name should not be writable");
+            assert.areEqual(nameDesc.enumerable, false, "String.prototype.at.name should not be enumerable");
+            const lengthDesc = Object.getOwnPropertyDescriptor(at, "length");
+            assert.areEqual(at.length, 1, 'String.prototype.at.length should be 1');
+            assert.areEqual(lengthDesc.configurable, true, "String.prototype.at.length should be configurable");
+            assert.areEqual(lengthDesc.writable, false, "String.prototype.at.length should not be writable");
+            assert.areEqual(lengthDesc.enumerable, false, "String.prototype.at.length should not be enumerable");
+        }
+    },
+    {
+        name: "String.prototype.at called on invalid object",
+        body: function () {
+            // on object without toString() should throw 
+            assert.throws(() => { call(at, Object.create(null)) }, TypeError);
+            assert.throws(() => { call(at, null) }, TypeError);
+            assert.throws(() => { call(at, undefined) }, TypeError);
+        }
+    },
+    {
+        name: "Indexation of string with non-numerical argument",
+        body: function () {
+            const string = "01";
+
+            assert.areEqual(string.at(false), "0", '"01".at(false) should be "0"');
+            assert.areEqual(string.at(null), "0", '"01".at(null) should be "0"');
+            assert.areEqual(string.at(undefined), "0", '"01".at(undefined) should be "0"');
+            assert.areEqual(string.at(""), "0", '"01".at("") should be "0"');
+            assert.areEqual(string.at(function () { }), "0", '"01".at(function() {}) should be "0"');
+            assert.areEqual(string.at([]), "0", '"01".at([]) should be "0"');
+            assert.areEqual(string.at(true), "1", '"01".at(true) should be "1"');
+            assert.areEqual(string.at("1"), "1", '"01".at("1") should be "1"');
+        }
+    },
+    {
+        name: "Indexation of string with negative numerical argument",
+        body: function () {
+            const string = "0\u26052";
+
+            assert.areEqual(string.at(0), "0", '"0\u26052".at(0) should be "0"');
+            assert.areEqual(string.at(-1), "2", '"0\u26052".at(-1) should be "2"');
+            assert.areEqual(string.at(-2), "\u2605", '"0\u26052".at(-2) should be "\u2605"');
+        }
+    },
+    {
+        name: "Indexation of string with positive numerical argument",
+        body: function () {
+            const string = "0\u26052";
+
+            assert.areEqual(string.at(0), "0", '"0\u26052".at(0) should be "0"');
+            assert.areEqual(string.at(1), "\u2605", '"0\u26052".at(-1) should be "\u2605"');
+            assert.areEqual(string.at(2), "2", '"0\u26052".at(-2) should be "2"');
+        }
+    },
+    {
+        name: "Out of bounds",
+        body: function () {
+            const string = "";
+
+            assert.areEqual(string.at(0), undefined, `"".at(0) should be undefined`);
+            assert.areEqual(string.at(-1), undefined, `"".at(-1) should be undefined`);
+            assert.areEqual(string.at(2), undefined, `"".at(2) should be undefined`);
+        }
+    },
+    {
+        name: "Argument ToInteger()",
+        body: function () {
+            let count = 0;
+            const index = {
+                valueOf() {
+                    count++;
+                    return 1;
+                }
+            };
+
+            const string = "123";
+
+            assert.areEqual(string.at(index), "2", 'result of string.at(index) should be "2"');
+            assert.areEqual(count, 1, 'The value of count should be 1');
+        }
+    },
+    {
+        name: "String.prototype.at called on to-stringable object",
+        body: function () {
+            const object = {
+                toString() {
+                    return "Test";
+                }
+            }
+            
+            assert.areEqual(call(at, object, 0), "T", 'call(at, object, 0) should be "T"');
+            assert.areEqual(call(at, object, -1), "t", 'call(at, object, -1) should be "t"');
+            assert.areEqual(call(at, object, 1), "e", 'call(at, object, 1) should be "e"');
+            assert.areEqual(call(at, object, 2), "s", 'call(at, object, 2) should be "T"');
+        }
+    }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 7 - 1
test/es6/unscopablesWithScopeTest.js

@@ -21,7 +21,7 @@ var tests = [
             
             const unscopables = Array.prototype[Symbol.unscopables];
 
-            const list = ["copyWithin", "entries", "fill", "find", "findIndex", "flat", "flatMap", "includes", "keys", "values"];
+            const list = ["at", "copyWithin", "entries", "fill", "find", "findIndex", "flat", "flatMap", "includes", "keys", "values"];
             const length = list.length;
 
             for (let index = 0; index < length; index++)
@@ -37,6 +37,7 @@ var tests = [
         body: function ()
         {
             var globalScope = -1;
+            var at          = globalScope;
             var find        = globalScope;
             var findIndex   = globalScope;
             var fill        = globalScope;
@@ -49,6 +50,7 @@ var tests = [
             var flatMap     = globalScope;
             with([])
             {
+                assert.areEqual(globalScope, at,         "at property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, find,       "find property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, findIndex,  "findIndex property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, fill,       "fill property is not brought into scope by the with statement");
@@ -67,6 +69,7 @@ var tests = [
         body: function ()
         {
             var globalScope = -1;
+            var at          = globalScope;
             var find        = globalScope;
             var findIndex   = globalScope;
             var fill        = globalScope;
@@ -82,6 +85,7 @@ var tests = [
             a[Symbol.unscopables]["slice"] = true;
             with(a)
             {
+                assert.areEqual(globalScope, at,         "at property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, find,       "find property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, findIndex,  "findIndex property is not brought into scope by the with statement");
                 assert.areEqual(globalScope, fill,       "fill property is not brought into scope by the with statement");
@@ -255,6 +259,7 @@ var tests = [
         body: function ()
         {
             var globalScope = -1;
+            var at         = globalScope;
             var find       = globalScope;
             var findIndex  = globalScope;
             var fill       = globalScope;
@@ -275,6 +280,7 @@ var tests = [
                         {
                             function bat()
                             {
+                                assert.areEqual(globalScope, at,         "at property is not brought into scope by the with statement");
                                 assert.areEqual(globalScope, find,       "find property is not brought into scope by the with statement");
                                 assert.areEqual(globalScope, findIndex,  "findIndex property is not brought into scope by the with statement");
                                 assert.areEqual(globalScope, fill,       "fill property is not brought into scope by the with statement");

+ 7 - 0
test/typedarray/rlexe.xml

@@ -1,5 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <regress-exe>
+  <test>
+    <default>
+      <files>typedarray_at.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+      <tags>typedarray</tags>
+    </default>
+  </test>
   <test>
     <default>
       <files>likely.js</files>

+ 115 - 0
test/typedarray/typedarray_at.js

@@ -0,0 +1,115 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
+// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+const TypedArrayPrototype = Object.getPrototypeOf(Int8Array).prototype;
+const at = TypedArrayPrototype.at;
+
+
+const tests = [
+    {
+        name: "Existence of TypedArray.prototype.at",
+        body: function () {
+            assert.isTrue(typeof at === "function", "TypedArray.prototype.at should be a function");
+        }
+    },
+    {
+        name: "TypedArray.prototype.at's descriptor",
+        body: function () {
+            const desc = Object.getOwnPropertyDescriptor(TypedArrayPrototype, "at");
+            assert.areEqual(desc.configurable, true, "TypedArray.prototype.at should be configurable");
+            assert.areEqual(desc.writable, true, "TypedArray.prototype.at should be writable");
+            assert.areEqual(desc.enumerable, false, "TypedArray.prototype.at should not be enumerable");
+        }
+    },
+    {
+        name: "Properties of TypedArray.prototype.at",
+        body: function () {
+            const nameDesc = Object.getOwnPropertyDescriptor(at, "name");
+            assert.areEqual(at.name, "at", 'TypedArray.prototype.at.name should be "at"');
+            assert.areEqual(nameDesc.configurable, true, "TypedArray.prototype.at.name should be configurable");
+            assert.areEqual(nameDesc.writable, false, "TypedArray.prototype.at.name should not be writable");
+            assert.areEqual(nameDesc.enumerable, false, "TypedArray.prototype.at.name should not be enumerable");
+            const lengthDesc = Object.getOwnPropertyDescriptor(at, "length");
+            assert.areEqual(at.length, 1, 'TypedArray.prototype.at.length should be 1');
+            assert.areEqual(lengthDesc.configurable, true, "TypedArray.prototype.at.length should be configurable");
+            assert.areEqual(lengthDesc.writable, false, "TypedArray.prototype.at.length should not be writable");
+            assert.areEqual(lengthDesc.enumerable, false, "TypedArray.prototype.at.length should not be enumerable");
+        }
+    },
+    {
+        name: "TypedArray.prototype.at called on invalid object",
+        body: function () {
+            assert.throws(() => { at.call(null) }, TypeError);
+            assert.throws(() => { at.call(undefined) }, TypeError);
+        }
+    },
+    {
+        name: "Indexation of typed array with non-numerical argument",
+        body: function () {
+            const typedarray = new Int8Array([0, 1]);
+
+            assert.areEqual(typedarray.at(false), 0, 'typedarray.at(false) should be 0');
+            assert.areEqual(typedarray.at(null), 0, 'typedarray.at(null) should be 0');
+            assert.areEqual(typedarray.at(undefined), 0, 'typedarray.at(undefined) should be 0');
+            assert.areEqual(typedarray.at(""), 0, 'typedarray.at("") should be 0');
+            assert.areEqual(typedarray.at(function () { }), 0, 'typedarray.at(function() {}) should be 0');
+            assert.areEqual(typedarray.at([]), 0, 'typedarray.at([]) should be 0');
+            assert.areEqual(typedarray.at(true), 1, 'typedarray.at(true) should be 1');
+            assert.areEqual(typedarray.at("1"), 1, 'typedarray.at("1") should be 1');
+        }
+    },
+    {
+        name: "Indexation of typed array with negative numerical argument",
+        body: function () {
+            const typedarray = new Int16Array([1, 2, 3]);
+
+            assert.areEqual(typedarray.at(0), 1, 'typedarray.at(0) should be 1');
+            assert.areEqual(typedarray.at(-1), 3, 'typedarray.at(-1) should be 3');
+            assert.areEqual(typedarray.at(-2), 2, 'typedarray.at(-2) should be 2');
+        }
+    },
+    {
+        name: "Indexation of typed array with positive numerical argument",
+        body: function () {
+            const typedarray = new Int32Array([1, 2, 3]);
+
+            assert.areEqual(typedarray.at(0), 1, 'typedarray.at(0) should be 1');
+            assert.areEqual(typedarray.at(1), 2, 'typedarray.at(1) should be 2');
+            assert.areEqual(typedarray.at(2), 3, 'typedarray.at(2) should be 3');
+        }
+    },
+    {
+        name: "Out of bounds",
+        body: function () {
+            const typedarray = new Uint32Array;
+
+            assert.areEqual(typedarray.at(0), undefined, `typedarray.at(0) should be undefined`);
+            assert.areEqual(typedarray.at(-1), undefined, `typedarray.at(-1) should be undefined`);
+            assert.areEqual(typedarray.at(2), undefined, `typedarray.at(2) should be undefined`);
+        }
+    },
+    {
+        name: "Argument ToInteger()",
+        body: function () {
+            let count = 0;
+            const index = {
+                valueOf() {
+                    count++;
+                    return 1;
+                }
+            };
+
+            const typedarray = new Uint32Array([1, 2, 3]);
+
+            assert.areEqual(typedarray.at(index), 2, 'result of typedarray.at(index) should be 2');
+            assert.areEqual(count, 1, 'The value of count should be 1');
+        }
+    }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

Деякі файли не було показано, через те що забагато файлів було змінено