From 02a02c3a59f4f281d5817ca86358e4d293ec13c4 Mon Sep 17 00:00:00 2001 From: Lei Shi Date: Tue, 25 Apr 2017 10:45:23 -0700 Subject: [PATCH] Merge from unreleased/rs2 to release/1.4 This includes all internal fixes in windows RS2 --- .gitattributes | 5 - lib/Backend/Chakra.Backend.vcxproj | 2 + lib/Backend/GlobOptFields.cpp | 1 + lib/Backend/IR.h | 4 + lib/Backend/Inline.cpp | 1 - lib/Backend/InterpreterThunkEmitter.cpp | 118 +- lib/Backend/InterpreterThunkEmitter.h | 13 +- lib/Backend/JnHelperMethod.h | 4 + lib/Backend/JnHelperMethodList.h | 5 + lib/Backend/Lower.cpp | 154 +- lib/Backend/Lower.h | 35 +- lib/Backend/NativeCodeGenerator.cpp | 2 +- lib/Backend/Opnd.cpp | 1 + lib/Backend/PageAllocatorPool.cpp | 25 +- lib/Backend/PageAllocatorPool.h | 8 +- lib/Backend/amd64/EncoderMD.cpp | 7 + lib/Backend/amd64/EncoderMD.h | 1 - lib/Backend/amd64/LowererMDArch.cpp | 83 +- lib/Backend/i386/EncoderMD.cpp | 3 +- lib/Backend/i386/EncoderMD.h | 1 - lib/Common/ChakraCoreVersion.h | 2 +- .../Common/Chakra.Common.Common.vcxproj | 2 + lib/Common/Common/vtinfo.h | 14 + lib/Common/CommonDefines.h | 7 + lib/Common/CommonMin.h | 1 + lib/Common/ConfigFlagsList.h | 7 +- lib/Common/Core/Chakra.Common.Core.vcxproj | 3 + lib/Common/Core/GlobalSecurityPolicy.cpp | 45 + lib/Common/Core/GlobalSecurityPolicy.h | 17 + .../Chakra.Common.DataStructures.vcxproj | 2 + lib/Common/DefaultCommonExternalApi.cpp | 2 +- .../Chakra.Common.Exceptions.vcxproj | 2 + lib/Common/Exceptions/ReportError.h | 6 +- lib/Common/Exceptions/Throw.cpp | 8 + lib/Common/Exceptions/Throw.h | 3 + .../Memory/Chakra.Common.Memory.vcxproj | 2 + lib/Common/Memory/CustomHeap.cpp | 5 +- lib/Common/Memory/LargeHeapBlock.cpp | 2 +- lib/Common/Memory/LargeHeapBucket.cpp | 1 - lib/Common/Memory/PageAllocator.h | 6 +- lib/Common/Memory/Recycler.cpp | 3 +- lib/Common/Memory/Recycler.h | 7 +- lib/JITClient/JITManager.cpp | 7 + lib/Jsrt/Chakra.Jsrt.vcxproj | 2 + lib/Parser/Chakra.Parser.vcxproj | 2 + lib/Parser/Parse.cpp | 62 +- lib/Parser/RegexCompileTime.cpp | 134 +- lib/Parser/RegexParser.cpp | 4 +- lib/Parser/Scan.cpp | 18 +- lib/Parser/ptree.h | 6 +- lib/Runtime/Base/Chakra.Runtime.Base.vcxproj | 4 +- lib/Runtime/Base/CrossSite.cpp | 20 +- lib/Runtime/Base/CrossSite.h | 1 + lib/Runtime/Base/FunctionBody.cpp | 2 +- lib/Runtime/Base/FunctionBody.h | 1 - lib/Runtime/Base/ScriptContext.cpp | 49 +- lib/Runtime/Base/ScriptContext.h | 3 + lib/Runtime/Base/ThreadContext.cpp | 3 + lib/Runtime/Base/ThreadContext.h | 40 + lib/Runtime/Base/ThreadContextInfo.cpp | 9 + .../ByteCodeCacheReleaseFileVersion.h | 4 +- lib/Runtime/ByteCode/ByteCodeEmitter.cpp | 8 +- lib/Runtime/ByteCode/ByteCodeGenerator.cpp | 18 +- .../ByteCode/Chakra.Runtime.ByteCode.vcxproj | 2 + .../Debug/Chakra.Runtime.Debug.vcxproj | 2 + lib/Runtime/Debug/TTInflateMap.cpp | 18 +- lib/Runtime/Debug/TTInflateMap.h | 2 +- lib/Runtime/Debug/TTSerialize.h | 7 +- lib/Runtime/Language/Arguments.h | 35 +- lib/Runtime/Language/AsmJsLink.cpp | 2 +- .../Language/Chakra.Runtime.Language.vcxproj | 2 + .../Language/InterpreterStackFrame.cpp | 4 +- lib/Runtime/Language/JavascriptConversion.cpp | 4 +- .../Language/JavascriptExceptionOperators.cpp | 8 +- lib/Runtime/Language/JavascriptOperators.cpp | 34 +- lib/Runtime/Library/ArrayBuffer.cpp | 4 +- lib/Runtime/Library/ArrayBuffer.h | 15 +- .../Library/Chakra.Runtime.Library.vcxproj | 3 +- lib/Runtime/Library/CompoundString.h | 6 + lib/Runtime/Library/ConcatString.h | 7 + lib/Runtime/Library/GlobalObject.cpp | 6 +- lib/Runtime/Library/GlobalObject.h | 2 +- lib/Runtime/Library/JSONParser.cpp | 1 + lib/Runtime/Library/JavascriptArray.cpp | 2596 ++++++++--------- lib/Runtime/Library/JavascriptArray.h | 57 +- lib/Runtime/Library/JavascriptBoolean.h | 6 + lib/Runtime/Library/JavascriptDate.cpp | 2 +- lib/Runtime/Library/JavascriptFunction.cpp | 122 +- .../Library/JavascriptGeneratorFunction.cpp | 2 +- .../Library/JavascriptGeneratorFunction.h | 12 + lib/Runtime/Library/JavascriptLibrary.cpp | 133 +- lib/Runtime/Library/JavascriptLibrary.h | 45 +- lib/Runtime/Library/JavascriptLibraryBase.h | 8 + lib/Runtime/Library/JavascriptMap.cpp | 4 +- lib/Runtime/Library/JavascriptNumber.h | 9 +- lib/Runtime/Library/JavascriptObject.cpp | 2 +- lib/Runtime/Library/JavascriptPromise.cpp | 43 +- lib/Runtime/Library/JavascriptProxy.cpp | 2 +- .../Library/JavascriptRegularExpression.cpp | 2 +- .../Library/JavascriptRegularExpression.h | 6 + lib/Runtime/Library/JavascriptSet.cpp | 4 +- lib/Runtime/Library/JavascriptSimdFloat32x4.h | 6 + lib/Runtime/Library/JavascriptSimdInt32x4.h | 6 + lib/Runtime/Library/JavascriptSimdObject.cpp | 7 +- lib/Runtime/Library/JavascriptString.cpp | 12 +- lib/Runtime/Library/JavascriptString.h | 2 +- lib/Runtime/Library/JavascriptWeakMap.cpp | 2 +- lib/Runtime/Library/JavascriptWeakSet.cpp | 2 +- lib/Runtime/Library/PropertyString.h | 6 + lib/Runtime/Library/RegexHelper.cpp | 9 +- lib/Runtime/Library/ScriptFunction.h | 6 + lib/Runtime/Library/StackScriptFunction.h | 6 + lib/Runtime/Library/TypedArray.cpp | 163 +- lib/Runtime/Library/TypedArray.h | 10 + lib/Runtime/Math/Chakra.Runtime.Math.vcxproj | 2 + lib/Runtime/Runtime.h | 1 + .../Types/Chakra.Runtime.Types.vcxproj | 2 + lib/Runtime/Types/DynamicObject.h | 11 + lib/Runtime/Types/DynamicType.cpp | 2 +- lib/Runtime/Types/SpreadArgument.cpp | 78 +- lib/Runtime/Types/SpreadArgument.h | 5 +- test/Array/Array_TypeConfusion_bugs.js | 1194 ++++---- .../returnedvaluetests4.js.dbg.baseline | 6 + ...nterpreted_function_attach.js.dbg.baseline | 56 + test/Error/validate_line_column.js | 164 +- test/Strings/lastindexof.js | 2 +- test/es6/ES6NewTarget_bugfixes.js | 44 +- test/es6/ES6TypedArrayExtensions.js | 38 + test/es6/es6_stable.baseline | 4 +- test/es6/es6_stable.enable_disable.baseline | 4 +- test/es6/moduletest1.js | 2 +- test/es6/proxybug.js | 24 + test/es6/proxybugs.js | 41 +- test/es6/rlexe.xml | 4 +- test/es6/spread.js | 80 + test/es7/asyncawait-functionality.baseline | 7 + test/es7/asyncawait-functionality.js | 64 + test/typedarray/rlexe.xml | 7 + test/typedarray/transferdetach.js | 30 + 139 files changed, 3669 insertions(+), 2676 deletions(-) delete mode 100644 .gitattributes create mode 100644 lib/Common/Core/GlobalSecurityPolicy.cpp create mode 100644 lib/Common/Core/GlobalSecurityPolicy.h create mode 100644 test/es6/proxybug.js create mode 100644 test/typedarray/transferdetach.js diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 592aab401cd..00000000000 --- a/.gitattributes +++ /dev/null @@ -1,5 +0,0 @@ -*.baseline -crlf -*.cmd -crlf -test/**/*.js -crlf -test/es6/HTMLComments.js binary diff=cpp -*.wasm binary diff --git a/lib/Backend/Chakra.Backend.vcxproj b/lib/Backend/Chakra.Backend.vcxproj index 2d16e89a10b..867dcbc5960 100644 --- a/lib/Backend/Chakra.Backend.vcxproj +++ b/lib/Backend/Chakra.Backend.vcxproj @@ -40,6 +40,8 @@ Use BackEnd.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 8b34a299256..35d3de4b0a8 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -2215,6 +2215,7 @@ GlobOpt::FinishOptPropOp(IR::Instr *instr, IR::PropertySymOpnd *opnd, BasicBlock // changed by the addition of a property. SymID opndId = opnd->HasObjectTypeSym() ? opnd->GetObjectTypeSym()->m_id : -1; + if (!isObjTypeChecked) { if (block->globOptData.maybeWrittenTypeSyms == nullptr) diff --git a/lib/Backend/IR.h b/lib/Backend/IR.h index 76233e60f78..adcf6b34360 100644 --- a/lib/Backend/IR.h +++ b/lib/Backend/IR.h @@ -130,6 +130,9 @@ class Instr m_src2(nullptr), #if DBG_DUMP globOptInstrString(nullptr), +#endif +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + isFsBased(false), #endif dstIsTempNumber(false), dstIsTempNumberTransferred(false), @@ -467,6 +470,7 @@ class Instr Js::OpCode m_opcode; uint8 ignoreOverflowBitCount; // Number of bits after which ovf matters. Currently used for MULs. + bool isFsBased : 1; // TEMP : just for BS testing bool dstIsTempNumber : 1; bool dstIsTempNumberTransferred : 1; bool dstIsTempObject : 1; diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp index 875db415543..a155c12ddd3 100644 --- a/lib/Backend/Inline.cpp +++ b/lib/Backend/Inline.cpp @@ -2341,7 +2341,6 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * *pIsInlined = false; return callInstr; } - *pIsInlined = true; #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) diff --git a/lib/Backend/InterpreterThunkEmitter.cpp b/lib/Backend/InterpreterThunkEmitter.cpp index 9f0878b6a62..28670690d11 100644 --- a/lib/Backend/InterpreterThunkEmitter.cpp +++ b/lib/Backend/InterpreterThunkEmitter.cpp @@ -55,6 +55,51 @@ const BYTE InterpreterThunkEmitter::Epilog[] = { 0x48, 0x83, 0xC4, StackAllocSize, // add rsp,28h 0xC3 // ret }; + +#if _CONTROL_FLOW_GUARD_SHADOW_STACK +#define RFG_PROLOGUE_SIZE 9 + +const BYTE InterpreterThunkEmitter::InterpreterThunkRFG[] = { + 0x48, 0x8b, 0x04, 0x24, // mov rax,qword ptr [rsp] + 0x64, 0x48, 0x89, 0x04, 0x24, // mov qword ptr fs:[rsp],rax + 0x48, 0x89, 0x54, 0x24, 0x10, // mov qword ptr [rsp+10h],rdx + 0x48, 0x89, 0x4C, 0x24, 0x08, // mov qword ptr [rsp+8],rcx + 0x4C, 0x89, 0x44, 0x24, 0x18, // mov qword ptr [rsp+18h],r8 + 0x4C, 0x89, 0x4C, 0x24, 0x20, // mov qword ptr [rsp+20h],r9 + 0x48, 0x8B, 0x41, 0x00, // mov rax, qword ptr [rcx+FunctionInfoOffset] + 0x48, 0x8B, 0x48, 0x00, // mov rcx, qword ptr [rax+FunctionProxyOffset] + 0x48, 0x8B, 0x51, 0x00, // mov rdx, qword ptr [rcx+DynamicThunkAddressOffset] + // Range Check for Valid call target + 0x48, 0x83, 0xE2, 0xF8, // and rdx, 0xFFFFFFFFFFFFFFF8h ;Force 8 byte alignment + 0x48, 0x8b, 0xca, // mov rcx, rdx + 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, CallBlockStartAddress + 0x48, 0x2b, 0xc8, // sub rcx, rax + 0x48, 0x81, 0xf9, 0x00, 0x00, 0x00, 0x00, // cmp rcx, ThunkSize + 0x76, 0x09, // jbe $safe + 0x48, 0xc7, 0xc1, 0x00, 0x00, 0x00, 0x00, // mov rcx, errorcode + 0xcd, 0x29, // int 29h + + // $safe: + 0x48, 0x8D, 0x4C, 0x24, 0x08, // lea rcx, [rsp+8] ;Load the address to stack + 0x48, 0x83, 0xEC, StackAllocSize, // sub rsp,28h + 0x48, 0xB8, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, + 0xFF, 0xE2, // jmp rdx + 0xCC, 0xCC, 0xCC, 0xCC // int 3 ;for alignment to size of 8 we are adding this +}; + +const BYTE InterpreterThunkEmitter::EpilogRFG[] = { + 0x48, 0x83, 0xC4, StackAllocSize, // add rsp,28h + 0x64, 0x4c, 0x8b, 0x1c, 0x24, // mov r11,qword ptr fs:[rsp] + 0x4c, 0x3b, 0x1c, 0x24, // cmp r11,qword ptr [rsp] + 0x75, 0x01, // jne $fail + 0xC3, // ret + + // $fail: + 0xb9, 0x2c, 0x00, 0x00, 0x00, // mov ecx, errorcode + 0xcd, 0x29, // int 29h +}; +#endif + #else // Sys V AMD64 const BYTE InterpreterThunkEmitter::FunctionInfoOffset = 7; const BYTE InterpreterThunkEmitter::FunctionProxyOffset = 11; @@ -230,9 +275,19 @@ const BYTE InterpreterThunkEmitter::Call[] = { #endif -const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk); +const BYTE InterpreterThunkEmitter::_HeaderSize = sizeof(InterpreterThunk); const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call); -const uint InterpreterThunkEmitter::ThunksPerBlock = (BlockSize - HeaderSize) / ThunkSize; + +const BYTE InterpreterThunkEmitter::HeaderSize() +{ +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (_guard_rf_checks_enforced()) { + return sizeof(InterpreterThunkRFG); + } +#endif + + return _HeaderSize; +} InterpreterThunkEmitter::InterpreterThunkEmitter(Js::ScriptContext* context, ArenaAllocator* allocator, CustomHeap::InProcCodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) : emitBufferManager(allocator, codePageAllocators, /*scriptContext*/ nullptr, _u("Interpreter thunk buffer"), GetCurrentProcess()), @@ -272,7 +327,7 @@ BYTE* InterpreterThunkEmitter::GetNextThunk(PVOID* ppDynamicInterpreterThunk) #if _M_ARM thunk = (BYTE*)((DWORD)thunk | 0x01); #endif - *ppDynamicInterpreterThunk = thunk + HeaderSize + ((--thunkCount) * ThunkSize); + *ppDynamicInterpreterThunk = thunk + HeaderSize() + ((--thunkCount) * ThunkSize); #if _M_ARM AssertMsg(((uintptr_t)(*ppDynamicInterpreterThunk) & 0x6) == 0, "Not 8 byte aligned?"); #else @@ -347,7 +402,7 @@ void InterpreterThunkEmitter::NewThunkBlock() ThreadContext::GetContextForCurrentThread()->SetValidCallTargetForCFG(buffer); // Update object state only at the end when everything has succeeded - and no exceptions can be thrown. - auto block = this->thunkBlocks.PrependNode(allocator, buffer); + auto block = this->thunkBlocks.PrependNode(allocator, buffer, count); #if PDATA_ENABLED void* pdataTable; PDataManager::RegisterPdata((PRUNTIME_FUNCTION)pdataStart, (ULONG_PTR)buffer, (ULONG_PTR)epilogEnd, &pdataTable); @@ -382,7 +437,7 @@ void InterpreterThunkEmitter::NewOOPJITThunkBlock() } // Update object state only at the end when everything has succeeded - and no exceptions can be thrown. - auto block = this->thunkBlocks.PrependNode(allocator, buffer); + auto block = this->thunkBlocks.PrependNode(allocator, buffer, thunkOutput.thunkCount); #if PDATA_ENABLED void* pdataTable; PDataManager::RegisterPdata((PRUNTIME_FUNCTION)thunkOutput.pdataTableStart, (ULONG_PTR)thunkOutput.mappedBaseAddr, (ULONG_PTR)thunkOutput.epilogEndAddr, &pdataTable); @@ -421,8 +476,10 @@ void InterpreterThunkEmitter::FillBuffer( #endif DWORD bytesRemaining = BlockSize; DWORD bytesWritten = 0; - DWORD epilogSize = sizeof(Epilog); DWORD thunks = 0; + DWORD epilogSize = sizeof(Epilog); + const BYTE *epilog = Epilog; + const BYTE *header = InterpreterThunk; intptr_t interpreterThunk; @@ -438,6 +495,14 @@ void InterpreterThunkEmitter::FillBuffer( interpreterThunk = SHIFT_ADDR(threadContext, &Js::InterpreterStackFrame::InterpreterThunk); } +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (_guard_rf_checks_enforced()) { + header = InterpreterThunkRFG; + epilog = EpilogRFG; + epilogSize = sizeof(EpilogRFG); + } +#endif + BYTE * currentBuffer = buffer; // Ensure there is space for PDATA at the end BYTE* pdataStart = currentBuffer + (BlockSize - Math::Align(pdataSize, EMIT_BUFFER_ALIGNMENT)); @@ -448,10 +513,10 @@ void InterpreterThunkEmitter::FillBuffer( intptr_t finalEpilogStart = finalPdataStart - Math::Align(epilogSize, EMIT_BUFFER_ALIGNMENT); // Copy the thunk buffer and modify it. - js_memcpy_s(currentBuffer, bytesRemaining, InterpreterThunk, HeaderSize); - EncodeInterpreterThunk(currentBuffer, finalAddr, HeaderSize, finalEpilogStart, epilogSize, interpreterThunk); - currentBuffer += HeaderSize; - bytesRemaining -= HeaderSize; + js_memcpy_s(currentBuffer, bytesRemaining, header, HeaderSize()); + EncodeInterpreterThunk(currentBuffer, finalAddr, HeaderSize(), finalEpilogStart, epilogSize, interpreterThunk); + currentBuffer += HeaderSize(); + bytesRemaining -= HeaderSize(); // Copy call buffer DWORD callSize = sizeof(Call); @@ -487,7 +552,7 @@ void InterpreterThunkEmitter::FillBuffer( currentBuffer += bytesWritten; // Copy epilog - bytesWritten = CopyWithAlignment(currentBuffer, bytesRemaining, Epilog, epilogSize, EMIT_BUFFER_ALIGNMENT); + bytesWritten = CopyWithAlignment(currentBuffer, bytesRemaining, epilog, epilogSize, EMIT_BUFFER_ALIGNMENT); currentBuffer += bytesWritten; bytesRemaining -= bytesWritten; @@ -520,7 +585,7 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk( __in const DWORD epilogSize, __in const intptr_t interpreterThunk) { - _Analysis_assume_(thunkSize == HeaderSize); + _Analysis_assume_(thunkSize == HeaderSize()); // Encode MOVW DWORD lowerThunkBits = (uint32)interpreterThunk & 0x0000FFFF; DWORD movW = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/1, lowerThunkBits); @@ -539,7 +604,7 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk( thunkBuffer[DynamicThunkAddressOffset] = Js::FunctionBody::GetOffsetOfDynamicInterpreterThunk(); // Encode MOVW R12, CallBlockStartAddress - uintptr_t callBlockStartAddress = (uintptr_t)thunkBufferStartAddress + HeaderSize; + uintptr_t callBlockStartAddress = (uintptr_t)thunkBufferStartAddress + HeaderSize(); uint totalThunkSize = (uint)(epilogStart - callBlockStartAddress); DWORD lowerCallBlockStartAddress = callBlockStartAddress & 0x0000FFFF; @@ -594,8 +659,8 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk( { int addrOffset = ThunkAddressOffset; - _Analysis_assume_(thunkSize == HeaderSize); - AssertMsg(thunkSize == HeaderSize, "Mismatch in the size of the InterpreterHeaderThunk and the thunkSize used in this API (EncodeInterpreterThunk)"); + _Analysis_assume_(thunkSize == HeaderSize()); + AssertMsg(thunkSize == HeaderSize(), "Mismatch in the size of the InterpreterHeaderThunk and the thunkSize used in this API (EncodeInterpreterThunk)"); // Following 4 MOV Instrs are to move the 64-bit address of the InterpreterThunk address into register x1. @@ -677,13 +742,20 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk( __in const DWORD epilogSize, __in const intptr_t interpreterThunk) { - _Analysis_assume_(thunkSize == HeaderSize); + _Analysis_assume_(thunkSize == HeaderSize()); + +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (_guard_rf_checks_enforced()) { + thunkBuffer += RFG_PROLOGUE_SIZE; + } +#endif + Emit(thunkBuffer, ThunkAddressOffset, (uintptr_t)interpreterThunk); thunkBuffer[DynamicThunkAddressOffset] = Js::FunctionBody::GetOffsetOfDynamicInterpreterThunk(); thunkBuffer[FunctionInfoOffset] = Js::JavascriptFunction::GetOffsetOfFunctionInfo(); thunkBuffer[FunctionProxyOffset] = Js::FunctionInfo::GetOffsetOfFunctionProxy(); - Emit(thunkBuffer, CallBlockStartAddrOffset, (uintptr_t) thunkBufferStartAddress + HeaderSize); - uint totalThunkSize = (uint)(epilogStart - (thunkBufferStartAddress + HeaderSize)); + Emit(thunkBuffer, CallBlockStartAddrOffset, (uintptr_t) thunkBufferStartAddress + HeaderSize()); + uint totalThunkSize = (uint)(epilogStart - (thunkBufferStartAddress + HeaderSize())); Emit(thunkBuffer, ThunkSizeOffset, totalThunkSize); Emit(thunkBuffer, ErrorOffset, (BYTE) FAST_FAIL_INVALID_ARG); } @@ -859,15 +931,15 @@ BYTE* ThunkBlock::AllocateFromFreeList() BVIndex ThunkBlock::FromThunkAddress(BYTE* address) { - int index = ((uint)(address - start) - InterpreterThunkEmitter::HeaderSize) / InterpreterThunkEmitter::ThunkSize; - Assert(index < InterpreterThunkEmitter::ThunksPerBlock); + uint index = ((uint)(address - start) - InterpreterThunkEmitter::HeaderSize()) / InterpreterThunkEmitter::ThunkSize; + Assert(index < this->thunkCount); return index; } BYTE* ThunkBlock::ToThunkAddress(BVIndex index) { - Assert(index < InterpreterThunkEmitter::ThunksPerBlock); - BYTE* address = start + InterpreterThunkEmitter::HeaderSize + InterpreterThunkEmitter::ThunkSize * index; + Assert(index < this->thunkCount); + BYTE* address = start + InterpreterThunkEmitter::HeaderSize() + InterpreterThunkEmitter::ThunkSize * index; return address; } @@ -875,7 +947,7 @@ bool ThunkBlock::EnsureFreeList(ArenaAllocator* allocator) { if(!this->freeList) { - this->freeList = BVFixed::NewNoThrow(InterpreterThunkEmitter::ThunksPerBlock, allocator); + this->freeList = BVFixed::NewNoThrow(this->thunkCount, allocator); } return this->freeList != nullptr; } diff --git a/lib/Backend/InterpreterThunkEmitter.h b/lib/Backend/InterpreterThunkEmitter.h index ac35ee82c68..1f94f965619 100644 --- a/lib/Backend/InterpreterThunkEmitter.h +++ b/lib/Backend/InterpreterThunkEmitter.h @@ -13,10 +13,12 @@ class ThunkBlock #endif BYTE* start; BVFixed* freeList; + DWORD thunkCount; public: - ThunkBlock(BYTE* start) : + ThunkBlock(BYTE* start, DWORD thunkCount) : start(start), + thunkCount(thunkCount), freeList(NULL) #if PDATA_ENABLED , registeredPdataTable(NULL) @@ -87,6 +89,11 @@ class InterpreterThunkEmitter static const BYTE Epilog[]; +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + static const BYTE InterpreterThunkRFG[]; + static const BYTE EpilogRFG[]; +#endif + static const BYTE PageCount = 1; #if defined(_M_X64) static const BYTE PrologSize; @@ -123,10 +130,10 @@ class InterpreterThunkEmitter }; BYTE* AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk); + static const BYTE _HeaderSize; public: - static const BYTE HeaderSize; + static const BYTE HeaderSize(); static const BYTE ThunkSize; - static const uint ThunksPerBlock; static const uint BlockSize= AutoSystemInfo::PageSize * PageCount; static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk); diff --git a/lib/Backend/JnHelperMethod.h b/lib/Backend/JnHelperMethod.h index a458e6b7b2e..db835bdb1b5 100644 --- a/lib/Backend/JnHelperMethod.h +++ b/lib/Backend/JnHelperMethod.h @@ -17,6 +17,10 @@ extern "C" extern "C" PVOID __guard_check_icall_fptr; #endif +#if _CONTROL_FLOW_GUARD_SHADOW_STACK +extern "C" void __guard_ss_verify_failure(); +#endif + namespace IR { enum JnHelperMethod diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index dea116483b6..97d08b34382 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -612,6 +612,11 @@ HELPERCALL( CRT_chkstk, _chkstk, 0 ) HELPERCALL(CRT_chkstk, __chkstk, 0) #endif +#if _CONTROL_FLOW_GUARD_SHADOW_STACK +// Statically linked CRT routine used when RFG violations. +HELPERCALL(ReturnFlowGuardFailureRoutine, __guard_ss_verify_failure, 0) +#endif + #undef HELPERCALL_MATH #undef HELPERCALL_FULL_OR_INPLACE_MATH diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index defc01695e3..df58d6ff5ac 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -285,8 +285,12 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa else { // s2 = current function +#if defined(_M_ARM) + StackSym * paramSym = this->m_lowererMD.GetImplicitParamSlotSym(0); +#else StackSym * paramSym = StackSym::New(TyMachReg, currFunc); currFunc->SetArgOffset(paramSym, 2 * MachPtr); +#endif IR::Opnd * srcOpnd = IR::SymOpnd::New(paramSym, TyMachReg, currFunc); if (currFunc->GetJITFunctionBody()->IsCoroutine()) @@ -1694,6 +1698,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa if(instr->HasBailOutInfo()) { const IR::BailOutKind bailOutKind = instr->GetBailOutKind(); + Assert( (bailOutKind & ~IR::BailOutKindBits) != IR::BailOutConventionalTypedArrayAccessOnly && !( @@ -1761,6 +1766,7 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa if(instr->HasBailOutInfo()) { const IR::BailOutKind bailOutKind = instr->GetBailOutKind(); + Assert( (bailOutKind & ~IR::BailOutKindBits) != IR::BailOutConventionalTypedArrayAccessOnly && !( @@ -13331,7 +13337,15 @@ Lowerer::GenerateBailOut(IR::Instr * instr, IR::BranchInstr * branchInstr, IR::L } #if DBG - if (bailOutInstr->m_opcode == Js::OpCode::BailOnNoSimdTypeSpec || bailOutInstr->m_opcode == Js::OpCode::BailOnNoProfile || bailOutInstr->m_opcode == Js::OpCode::BailOnException || bailOutInstr->m_opcode == Js::OpCode::Yield) + const IR::BailOutKind bailOutKind = bailOutInstr->GetBailOutKind(); + + if (bailOutInstr->m_opcode == Js::OpCode::BailOnNoSimdTypeSpec || + bailOutInstr->m_opcode == Js::OpCode::BailOnNoProfile || + bailOutInstr->m_opcode == Js::OpCode::BailOnException || + bailOutInstr->m_opcode == Js::OpCode::Yield || + bailOutKind & (IR::BailOutConventionalTypedArrayAccessOnly | + IR::BailOutConventionalNativeArrayAccessOnly | + IR::BailOutOnArrayAccessHelperCall)) { bailOutLabel->m_noHelperAssert = true; } @@ -15122,7 +15136,8 @@ Lowerer::GenerateFastElemICommon( bool checkArrayLengthOverflow /*= true*/, bool forceGenerateFastPath /* = false */, bool returnLength, - IR::LabelInstr *bailOutLabelInstr /* = nullptr*/) + IR::LabelInstr *bailOutLabelInstr /* = nullptr*/, + bool * indirOpndOverflowed /* = nullptr*/) { *pIsTypedArrayElement = false; *pIsStringIndex = false; @@ -15175,7 +15190,8 @@ Lowerer::GenerateFastElemICommon( checkArrayLengthOverflow, false, returnLength, - bailOutLabelInstr); + bailOutLabelInstr, + indirOpndOverflowed); } IR::IndirOpnd * @@ -15192,12 +15208,18 @@ Lowerer::GenerateFastElemIIntIndexCommon( bool checkArrayLengthOverflow /*= true*/, bool forceGenerateFastPath /* = false */, bool returnLength, - IR::LabelInstr *bailOutLabelInstr /* = nullptr*/) + IR::LabelInstr *bailOutLabelInstr /* = nullptr*/, + bool * indirOpndOverflowed /* = nullptr */) { IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd(); IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd(); Assert(!baseOpnd->IsTaggedInt() || (indexOpnd && indexOpnd->IsNotInt())); + if (indirOpndOverflowed != nullptr) + { + *indirOpndOverflowed = false; + } + BYTE indirScale = this->m_lowererMD.GetDefaultIndirScale(); IRType indirType = TyVar; const ValueType baseValueType(baseOpnd->GetValueType()); @@ -15257,6 +15279,7 @@ Lowerer::GenerateFastElemIIntIndexCommon( IntConstType value = 0; IR::Opnd * indexValueOpnd = nullptr; bool invertBoundCheckComparison = false; + bool checkIndexConstOverflowed = false; if (indirOpnd->TryGetIntConstIndexValue(true, &value, &isIndexNotInt)) { @@ -15264,6 +15287,7 @@ Lowerer::GenerateFastElemIIntIndexCommon( { indexValueOpnd = IR::IntConstOpnd::New(value, TyUint32, this->m_func); invertBoundCheckComparison = true; // facilitate folding the constant index into the compare instruction + checkIndexConstOverflowed = true; } else { @@ -15292,6 +15316,13 @@ Lowerer::GenerateFastElemIIntIndexCommon( indirType = GetArrayIndirType(baseValueType); } + if (checkIndexConstOverflowed && (static_cast(value) << indirScale) > INT32_MAX && + indirOpndOverflowed != nullptr) + { + *indirOpndOverflowed = true; + return nullptr; + } + IRType elementType = TyIllegal; IR::Opnd * element = nullptr; @@ -16187,7 +16218,8 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) labelBailOut = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true); labelCantUseArray = labelBailOut; } - bool isTypedArrayElement, isStringIndex; + + bool isTypedArrayElement, isStringIndex, indirOpndOverflowed = false; indirOpnd = GenerateFastElemICommon( ldElem, @@ -16198,7 +16230,13 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) labelFallThru, &isTypedArrayElement, &isStringIndex, - &emitBailout); + &emitBailout, + nullptr, /* pLabelSegmentLengthIncreased */ + true, /* checkArrayLengthOverflow */ + false, /* forceGenerateFastPath */ + false, /* returnLength */ + nullptr, /* bailOutLabelInstr */ + &indirOpndOverflowed); IR::Opnd *dst = ldElem->GetDst(); IRType dstType = dst->AsRegOpnd()->GetType(); @@ -16206,11 +16244,60 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) // The index is negative or not int. if (indirOpnd == nullptr) { - Assert(!(ldElem->HasBailOutInfo() && ldElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall)); + // could have bailout kind BailOutOnArrayAccessHelperCall if indirOpnd overflows + Assert(!(ldElem->HasBailOutInfo() && ldElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall) || indirOpndOverflowed); + // don't check fast path without bailout because it might not be TypedArray + if (indirOpndOverflowed && ldElem->HasBailOutInfo()) + { + bool bailoutForOpndOverflow = false; + const IR::BailOutKind bailOutKind = ldElem->GetBailOutKind(); + + // return undefined for typed array if load dest is var, bailout otherwise + if ((bailOutKind & ~IR::BailOutKindBits) == IR::BailOutConventionalTypedArrayAccessOnly) + { + if (dst->IsVar()) + { + // returns undefined in case of indirOpnd overflow which is consistent with behavior of interpreter + IR::Opnd * undefinedOpnd = this->LoadLibraryValueOpnd(ldElem, LibraryValue::ValueUndefined); + InsertMove(dst, undefinedOpnd, ldElem); + + ldElem->FreeSrc1(); + ldElem->FreeDst(); + ldElem->Remove(); + + emittedFastPath = true; + } + else + { + bailoutForOpndOverflow = true; + } + } + + if (bailoutForOpndOverflow || (bailOutKind & (IR::BailOutConventionalNativeArrayAccessOnly | IR::BailOutOnArrayAccessHelperCall))) + { + IR::Opnd * constOpnd = nullptr; + if (dst->IsFloat()) + { + constOpnd = IR::FloatConstOpnd::New(Js::JavascriptNumber::NaN, TyFloat64, m_func); + } + else + { + constOpnd = IR::IntConstOpnd::New(0, TyInt32, this->m_func, true); + } + InsertMove(dst, constOpnd, ldElem); + + ldElem->FreeSrc1(); + ldElem->FreeDst(); + GenerateBailOut(ldElem, nullptr, nullptr); + emittedFastPath = true; + } + + return !emittedFastPath; + } // The global optimizer should never type specialize a LdElem for which the index is not int or an integer constant // with a negative value. This would force an unconditional bail out on the main code path. - if (dst->IsVar()) + else if (dst->IsVar()) { if (PHASE_TRACE(Js::TypedArrayTypeSpecPhase, this->m_func) && PHASE_TRACE(Js::LowererPhase, this->m_func)) { @@ -16241,10 +16328,9 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef) IR::IntConstOpnd *constOpnd = IR::IntConstOpnd::New(0, TyInt32, this->m_func, true); InsertMove(dst, constOpnd, ldElem); - ldElem->UnlinkSrc1(); - ldElem->UnlinkDst(); + ldElem->FreeSrc1(); + ldElem->FreeDst(); GenerateBailOut(ldElem, nullptr, nullptr); - emittedFastPath = true; return false; } } @@ -16649,7 +16735,7 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) } } - bool isTypedArrayElement, isStringIndex; + bool isTypedArrayElement, isStringIndex, indirOpndOverflowed = false; indirOpnd = GenerateFastElemICommon( stElem, @@ -16661,7 +16747,12 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) &isTypedArrayElement, &isStringIndex, &emitBailout, - &labelSegmentLengthIncreased); + &labelSegmentLengthIncreased, + true, /* checkArrayLengthOverflow */ + false, /* forceGenerateFastPath */ + false, /* returnLength */ + nullptr, /* bailOutLabelInstr */ + &indirOpndOverflowed); IR::Opnd *src = stElem->GetSrc1(); const IR::AutoReuseOpnd autoReuseSrc(src, m_func); @@ -16669,11 +16760,36 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) // The index is negative or not int. if (indirOpnd == nullptr) { - Assert(!(stElem->HasBailOutInfo() && stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall)); + Assert(!(stElem->HasBailOutInfo() && stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall) || indirOpndOverflowed); + + if (indirOpndOverflowed && stElem->HasBailOutInfo()) + { + bool emittedFastPath = false; + const IR::BailOutKind bailOutKind = stElem->GetBailOutKind(); + + // ignore StElemI in case of indirOpnd overflow only for typed array which is consistent with behavior of interpreter + if ((bailOutKind & ~IR::BailOutKindBits) == IR::BailOutConventionalTypedArrayAccessOnly) + { + stElem->FreeSrc1(); + stElem->FreeDst(); + stElem->Remove(); + emittedFastPath = true; + } + + if (!emittedFastPath && (bailOutKind & (IR::BailOutConventionalNativeArrayAccessOnly | IR::BailOutOnArrayAccessHelperCall))) + { + stElem->FreeSrc1(); + stElem->FreeDst(); + GenerateBailOut(stElem, nullptr, nullptr); + emittedFastPath = true; + } + + return !emittedFastPath; + } // The global optimizer should never type specialize a StElem for which we know the index is not int or is a negative // int constant. This would result in an unconditional bailout on the main code path. - if (src->IsVar()) + else if (src->IsVar()) { if (PHASE_TRACE(Js::TypedArrayTypeSpecPhase, this->m_func) && PHASE_TRACE(Js::LowererPhase, this->m_func)) { @@ -16697,8 +16813,8 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) // these (unlikely) conditions. AssertMsg(false, "Global optimizer shouldn't have specialized this instruction."); - stElem->UnlinkSrc1(); - stElem->UnlinkDst(); + stElem->FreeSrc1(); + stElem->FreeDst(); GenerateBailOut(stElem, nullptr, nullptr); return false; } @@ -17164,8 +17280,8 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef) if (emitBailout) { - stElem->UnlinkSrc1(); - stElem->UnlinkDst(); + stElem->FreeSrc1(); + stElem->FreeDst(); GenerateBailOut(stElem, nullptr, nullptr); } diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index 0ae0966c385..212d7e322ba 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -373,8 +373,39 @@ class Lowerer } private: - IR::IndirOpnd * GenerateFastElemICommon(IR::Instr * ldElem, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelCantUseArray, IR::LabelInstr *labelFallthrough, bool * pIsTypedArrayElement, bool * pIsStringIndex, bool *emitBailoutRef, IR::LabelInstr **pLabelSegmentLengthIncreased = nullptr, bool checkArrayLengthOverflow = true, bool forceGenerateFastPath = false, bool returnLength = false, IR::LabelInstr *bailOutLabelInstr = nullptr); - IR::IndirOpnd * GenerateFastElemIIntIndexCommon(IR::Instr * ldElem, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelCantUseArray, IR::LabelInstr *labelFallthrough, bool * pIsTypedArrayElement, bool *emitBailoutRef, IR::LabelInstr **pLabelSegmentLengthIncreased, bool checkArrayLengthOverflow, bool forceGenerateFastPath = false, bool returnLength = false, IR::LabelInstr *bailOutLabelInstr = nullptr); + IR::IndirOpnd * GenerateFastElemICommon( + IR::Instr * ldElem, + bool isStore, + IR::IndirOpnd * indirOpnd, + IR::LabelInstr * labelHelper, + IR::LabelInstr * labelCantUseArray, + IR::LabelInstr *labelFallthrough, + bool * pIsTypedArrayElement, + bool * pIsStringIndex, + bool *emitBailoutRef, + IR::LabelInstr **pLabelSegmentLengthIncreased = nullptr, + bool checkArrayLengthOverflow = true, + bool forceGenerateFastPath = false, + bool returnLength = false, + IR::LabelInstr *bailOutLabelInstr = nullptr, + bool * indirOpndOverflowed = nullptr); + + IR::IndirOpnd * GenerateFastElemIIntIndexCommon( + IR::Instr * ldElem, + bool isStore, + IR::IndirOpnd * indirOpnd, + IR::LabelInstr * labelHelper, + IR::LabelInstr * labelCantUseArray, + IR::LabelInstr *labelFallthrough, + bool * pIsTypedArrayElement, + bool *emitBailoutRef, + IR::LabelInstr **pLabelSegmentLengthIncreased, + bool checkArrayLengthOverflow, + bool forceGenerateFastPath = false, + bool returnLength = false, + IR::LabelInstr *bailOutLabelInstr = nullptr, + bool * indirOpndOverflowed = nullptr); + bool GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef); bool GenerateFastStElemI(IR::Instr *& StElem, bool *instrIsInHelperBlockRef); bool GenerateFastLdLen(IR::Instr *ldLen, bool *instrIsInHelperBlockRef); diff --git a/lib/Backend/NativeCodeGenerator.cpp b/lib/Backend/NativeCodeGenerator.cpp index b916e02236d..e237d362ec6 100644 --- a/lib/Backend/NativeCodeGenerator.cpp +++ b/lib/Backend/NativeCodeGenerator.cpp @@ -1161,7 +1161,7 @@ NativeCodeGenerator::CodeGen(PageAllocator * pageAllocator, CodeGenWorkItem* wor { body->SetDisableInlineSpread(true); } - + #ifdef PROFILE_BAILOUT_RECORD_MEMORY if (Js::Configuration::Global.flags.ProfileBailOutRecordMemory) { diff --git a/lib/Backend/Opnd.cpp b/lib/Backend/Opnd.cpp index d07b20b6f96..6fcb65a7ebc 100644 --- a/lib/Backend/Opnd.cpp +++ b/lib/Backend/Opnd.cpp @@ -899,6 +899,7 @@ PropertySymOpnd::ChangesObjectLayout() const JITTypeHolder cachedType = this->IsMono() ? this->GetType() : this->GetFirstEquivalentType(); JITTypeHolder finalType = this->GetFinalType(); + if (finalType != nullptr && Js::DynamicType::Is(finalType->GetTypeId())) { // This is the case where final type opt may cause pro-active type transition to take place. diff --git a/lib/Backend/PageAllocatorPool.cpp b/lib/Backend/PageAllocatorPool.cpp index f72fef84188..b67caa63dfa 100644 --- a/lib/Backend/PageAllocatorPool.cpp +++ b/lib/Backend/PageAllocatorPool.cpp @@ -14,9 +14,10 @@ PageAllocatorPool* PageAllocatorPool::Instance = nullptr; PageAllocatorPool::PageAllocatorPool() :pageAllocators(&NoThrowHeapAllocator::Instance), + idleCleanupTimer(NULL), activePageAllocatorCount(0) { - idleCleanupTimer = CreateWaitableTimerEx(NULL, L"JITServerIdle", 0/*auto reset*/, TIMER_ALL_ACCESS); + idleCleanupTimer = CreateThreadpoolTimer(IdleCleanupRoutine, NULL, NULL); } PageAllocatorPool::~PageAllocatorPool() @@ -33,6 +34,7 @@ void PageAllocatorPool::Initialize() Js::Throw::FatalInternalError(); } } + void PageAllocatorPool::Shutdown() { AutoCriticalSection autoCS(&cs); @@ -41,13 +43,15 @@ void PageAllocatorPool::Shutdown() { PageAllocatorPool* localInstance = Instance; Instance = nullptr; + if (localInstance->idleCleanupTimer) { - CloseHandle(localInstance->idleCleanupTimer); + CloseThreadpoolTimer(localInstance->idleCleanupTimer); } HeapDelete(localInstance); } } + void PageAllocatorPool::RemoveAll() { Assert(cs.IsLocked()); @@ -83,6 +87,7 @@ PageAllocator* PageAllocatorPool::GetPageAllocator() return pageAllocator; } + void PageAllocatorPool::ReturnPageAllocator(PageAllocator* pageAllocator) { AutoCriticalSection autoCS(&cs); @@ -104,10 +109,14 @@ void PageAllocatorPool::IdleCleanup() if (Instance) { LARGE_INTEGER liDueTime; - liDueTime.QuadPart = Js::Configuration::Global.flags.JITServerIdleTimeout * -10000000LL; // wait for 10 seconds to do the cleanup + liDueTime.QuadPart = Js::Configuration::Global.flags.JITServerIdleTimeout * -10000LL; // wait for JITServerIdleTimeout milliseconds to do the cleanup - // If the timer is already active when you call SetWaitableTimer, the timer is stopped, then it is reactivated. - if (!SetWaitableTimer(Instance->idleCleanupTimer, &liDueTime, 0, IdleCleanupRoutine, NULL, 0)) + if (Instance->idleCleanupTimer) + { + // Setting the timer cancels the previous timer, if any. + SetThreadpoolTimer(Instance->idleCleanupTimer, (PFILETIME)&liDueTime, 0, 0); + } + else { Instance->RemoveAll(); } @@ -115,9 +124,9 @@ void PageAllocatorPool::IdleCleanup() } VOID CALLBACK PageAllocatorPool::IdleCleanupRoutine( - _In_opt_ LPVOID lpArgToCompletionRoutine, - _In_ DWORD dwTimerLowValue, - _In_ DWORD dwTimerHighValue) + _Inout_ PTP_CALLBACK_INSTANCE, + _Inout_opt_ PVOID, + _Inout_ PTP_TIMER) { AutoCriticalSection autoCS(&cs); if (Instance) diff --git a/lib/Backend/PageAllocatorPool.h b/lib/Backend/PageAllocatorPool.h index 2db4e76c7f6..0a4147eb510 100644 --- a/lib/Backend/PageAllocatorPool.h +++ b/lib/Backend/PageAllocatorPool.h @@ -19,9 +19,9 @@ class PageAllocatorPool private: static VOID CALLBACK IdleCleanupRoutine( - _In_opt_ LPVOID lpArgToCompletionRoutine, - _In_ DWORD dwTimerLowValue, - _In_ DWORD dwTimerHighValue); + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_ PTP_TIMER Timer); PageAllocator* GetPageAllocator(); void ReturnPageAllocator(PageAllocator* pageAllocator); @@ -30,7 +30,7 @@ class PageAllocatorPool SList pageAllocators; static CriticalSection cs; static PageAllocatorPool* Instance; - HANDLE idleCleanupTimer; + PTP_TIMER idleCleanupTimer; volatile unsigned long long activePageAllocatorCount; }; diff --git a/lib/Backend/amd64/EncoderMD.cpp b/lib/Backend/amd64/EncoderMD.cpp index 60e21f2c926..565978243c3 100644 --- a/lib/Backend/amd64/EncoderMD.cpp +++ b/lib/Backend/amd64/EncoderMD.cpp @@ -670,6 +670,13 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress) instrRestart = instrStart = m_pc; +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (instr->isFsBased) + { + *instrRestart++ = 0x64; + } +#endif + // put out 16bit override if any if (instrSize == 2 && (opdope & (DNO16 | DFLT)) == 0) { diff --git a/lib/Backend/amd64/EncoderMD.h b/lib/Backend/amd64/EncoderMD.h index 4cb209e17ad..595afdd2c49 100644 --- a/lib/Backend/amd64/EncoderMD.h +++ b/lib/Backend/amd64/EncoderMD.h @@ -54,7 +54,6 @@ class EncodeRelocAndLabels else { m_origPtr = ptr; - if (type == RelocTypeBranch) { Assert(labelInstr); diff --git a/lib/Backend/amd64/LowererMDArch.cpp b/lib/Backend/amd64/LowererMDArch.cpp index c36582e446e..d9e743a7001 100644 --- a/lib/Backend/amd64/LowererMDArch.cpp +++ b/lib/Backend/amd64/LowererMDArch.cpp @@ -1485,6 +1485,14 @@ LowererMDArch::MovArgFromReg2Stack(IR::Instr * instr, RegNum reg, uint16 slotNum IR::Instr * LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) { +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + /* + * If RFG is enabled for the process, then this precedes the prologue: + * + * mov rax, [rsp] + * mov fs:[rsp], rax + */ +#endif /* * push rbp * mov rbp, rsp @@ -1699,6 +1707,32 @@ LowererMDArch::LowerEntryInstr(IR::EntryInstr * entryInstr) m_func->m_prologEncoder.RecordNonVolRegSave(); firstPrologInstr = pushInstr; +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (_guard_rf_checks_enforced()) { + // Generate MOV FS:[RSP], RAX + raxOpnd = IR::RegOpnd::New(nullptr, RegRAX, TyMachReg, this->m_func); + IR::Instr * storeRetInstr = IR::Instr::New(Js::OpCode::MOV, + IR::IndirOpnd::New(stackPointer, + 0, + TyInt64, + this->m_func), + raxOpnd, + this->m_func); + storeRetInstr->isFsBased = true; + entryInstr->InsertAfter(storeRetInstr); + + // Generate MOV RAX, [RSP] + IR::Instr * getRetInstr = IR::Instr::New(Js::OpCode::MOV, + raxOpnd, + IR::IndirOpnd::New(stackPointer, + 0, + TyInt64, + this->m_func), + this->m_func); + entryInstr->InsertAfter(getRetInstr); + } +#endif + // // Insert pragmas that tell the prolog encoder the extent of the prolog. // @@ -2090,13 +2124,60 @@ LowererMDArch::LowerExitInstr(IR::ExitInstr * exitInstr) { retReg = IR::RegOpnd::New(nullptr, this->GetRegReturn(TyMachReg), TyMachReg, this->m_func); } - IR::Instr *retInstr = IR::Instr::New(Js::OpCode::RET, this->m_func); + +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + IR::LabelInstr * failLabel = nullptr; + if (_guard_rf_checks_enforced()) { + // Generate MOV RCX, FS:[RSP] + IR::Opnd * rcxOpnd = IR::RegOpnd::New(nullptr, RegRCX, TyMachReg, this->m_func); + IR::Instr * getRetInstr = IR::Instr::New(Js::OpCode::MOV, + rcxOpnd, + IR::IndirOpnd::New(stackPointer, + 0, + TyInt64, + this->m_func), + this->m_func); + getRetInstr->isFsBased = true; + exitInstr->InsertBefore(getRetInstr); + + // Generate CMP RCX, [RSP] + IR::Instr * cmpRetInstr = IR::Instr::New(Js::OpCode::CMP, m_func); + cmpRetInstr->SetSrc1(IR::IndirOpnd::New(stackPointer, + 0, + TyInt64, + this->m_func)); + cmpRetInstr->SetSrc2(rcxOpnd); + exitInstr->InsertBefore(cmpRetInstr); + + // Generate JNE $fail + failLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func, true); + exitInstr->InsertBefore(IR::BranchInstr::New(Js::OpCode::JNE, failLabel, m_func)); + } +#endif + + // Generate RET + IR::Instr * retInstr = IR::Instr::New(Js::OpCode::RET, this->m_func); retInstr->SetSrc1(intSrc); retInstr->SetSrc2(retReg); exitInstr->InsertBefore(retInstr); retInstr->m_opcode = Js::OpCode::RET; +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + if (_guard_rf_checks_enforced() && failLabel != nullptr) { + // insert $fail + exitInstr->InsertBefore(failLabel); + + // MOV RCX, __guard_ss_verify_failure + IR::RegOpnd * target = IR::RegOpnd::New(nullptr, RegRCX, TyMachReg, m_func); + this->lowererMD->CreateAssign(target, IR::HelperCallOpnd::New(IR::HelperReturnFlowGuardFailureRoutine, m_func), exitInstr); + + // JMP RCX + IR::Instr * branch = IR::MultiBranchInstr::New(Js::OpCode::JMP, target, m_func); + exitInstr->InsertBefore(branch); + } +#endif + return exitInstr; } diff --git a/lib/Backend/i386/EncoderMD.cpp b/lib/Backend/i386/EncoderMD.cpp index 73f9705f703..0ed6ed3af7f 100644 --- a/lib/Backend/i386/EncoderMD.cpp +++ b/lib/Backend/i386/EncoderMD.cpp @@ -925,7 +925,7 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress) } else if (opr1->IsHelperCallOpnd()) { - const void* fnAddress = (void*)IR::GetMethodAddress(m_func->GetThreadContextInfo(), opr1->AsHelperCallOpnd()); + const void* fnAddress = (void *)IR::GetMethodAddress(m_func->GetThreadContextInfo(), opr1->AsHelperCallOpnd()); AppendRelocEntry(RelocTypeCallPcrel, (void*)m_pc, nullptr, fnAddress); AssertMsg(sizeof(uint32) == sizeof(void*), "Sizes of void* assumed to be 32-bits"); this->EmitConst(0, 4); @@ -938,7 +938,6 @@ EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress) break; // Special form which doesn't fit any existing patterns. - case SPECIAL: switch (instr->m_opcode) diff --git a/lib/Backend/i386/EncoderMD.h b/lib/Backend/i386/EncoderMD.h index 180c0e2d782..1c3aaa996b0 100644 --- a/lib/Backend/i386/EncoderMD.h +++ b/lib/Backend/i386/EncoderMD.h @@ -97,7 +97,6 @@ class EncodeRelocAndLabels m_ptr = m_origPtr; } - bool isLabel() const { return isAlignedLabel() || m_type == RelocTypeLabel; } bool isAlignedLabel() const { return m_type == RelocTypeAlignedLabel; } bool isLongBr() const { return m_type == RelocTypeBranch && m_shortBrLabel == NULL; } diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index 77498a4a7b6..ec69718723f 100644 --- a/lib/Common/ChakraCoreVersion.h +++ b/lib/Common/ChakraCoreVersion.h @@ -60,4 +60,4 @@ // Chakra RELEASE flag // Mostly redundant with CHAKRA_CORE_VERSION_RELEASE, // but semantically refers to Chakra rather than ChakraCore. -#define CHAKRA_VERSION_RELEASE 0 +#define CHAKRA_VERSION_RELEASE 1 diff --git a/lib/Common/Common/Chakra.Common.Common.vcxproj b/lib/Common/Common/Chakra.Common.Common.vcxproj index 1136e336e36..e105ce17191 100644 --- a/lib/Common/Common/Chakra.Common.Common.vcxproj +++ b/lib/Common/Common/Chakra.Common.Common.vcxproj @@ -34,6 +34,8 @@ Use CommonCommonPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Common/Common/vtinfo.h b/lib/Common/Common/vtinfo.h index a8f2033bfdf..cef19681c3b 100644 --- a/lib/Common/Common/vtinfo.h +++ b/lib/Common/Common/vtinfo.h @@ -99,6 +99,20 @@ class VirtualTableInfo : public VirtualTableInfoBase static bool HasVirtualTable(void * ptr) { return GetVirtualTable(ptr) == Address; } }; +template +class VirtualTableRecorder +{ +public: + static void RecordVirtualTableAddress(INT_PTR * vtableAddresses, int32 value) + { + vtableAddresses[value] = VirtualTableInfo::Address; + + // validate uniqueness of vtable + bool isValueEqual = reinterpret_cast(&vtableAddresses[value])->DummyVirtualFunctionToHinderLinkerICF() == value; + AssertOrFailFast(isValueEqual); + } +}; + #if !defined(USED_IN_STATIC_LIB) #pragma warning(disable:4238) // class rvalue used as lvalue template diff --git a/lib/Common/CommonDefines.h b/lib/Common/CommonDefines.h index b72a3a6fd33..eff28126b11 100644 --- a/lib/Common/CommonDefines.h +++ b/lib/Common/CommonDefines.h @@ -722,6 +722,13 @@ #define JS_PROFILE_DATA_INTERFACE 0 #endif +#define JS_REENTRANCY_FAILFAST 1 +#if DBG || JS_REENTRANCY_FAILFAST +#define ENABLE_JS_REENTRANCY_CHECK 1 +#else +#define ENABLE_JS_REENTRANCY_CHECK 0 +#endif + #ifndef PROFILE_DICTIONARY #define PROFILE_DICTIONARY 0 #endif diff --git a/lib/Common/CommonMin.h b/lib/Common/CommonMin.h index f4e69bfbaff..0f77b977d33 100644 --- a/lib/Common/CommonMin.h +++ b/lib/Common/CommonMin.h @@ -76,6 +76,7 @@ using namespace Memory; // === Configurations Header === #include "Core/ConfigFlagsTable.h" +#include "Core/GlobalSecurityPolicy.h" // === Page/Arena Memory Header Files === #include "Memory/SectionAllocWrapper.h" diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index eb9fecec2bc..09212283326 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -539,7 +539,7 @@ PHASE(All) #define DEFAULT_CONFIG_ES6PrototypeChain (false) #endif #define DEFAULT_CONFIG_ES6ToPrimitive (true) -#define DEFAULT_CONFIG_ES6ToLength (false) +#define DEFAULT_CONFIG_ES6ToLength (true) #define DEFAULT_CONFIG_ES6ToStringTag (true) #define DEFAULT_CONFIG_ES6Unicode (true) #define DEFAULT_CONFIG_ES6UnicodeVerbose (true) @@ -1357,7 +1357,7 @@ FLAGR (Number, Version , "Version in which to run the jscript eng #ifdef ENABLE_PROJECTION FLAGR (Number, HostType , "Host type in which to run the jscript engine. [one of 1,2]. Default is 1 = Browser.", 1) #endif -FLAGR (Boolean, WERExceptionSupport , "WER feature for extended exception support. Enabled when WinRT is enabled", false ) +FLAGR(Boolean, WERExceptionSupport , "WER feature for extended exception support. Enabled when WinRT is enabled", false) #ifdef ENABLE_PROJECTION FLAGR (Boolean, WinRTConstructorAllowed, "Whether WinRT constructors is allowed in WebView host type. Constructor is always allowed in other host type ", false) #endif @@ -1475,8 +1475,9 @@ FLAGNR(Boolean, CFG, "Force enable CFG on jshost. version in the jshost's manife FLAGNR(Number, SimulatePolyCacheWithOneTypeForInlineCacheIndex, "Use with SimulatePolyCacheWithOneTypeForFunction to simulate creating a polymorphic inline cache containing only one type due to a collision, for testing ObjTypeSpec", -1) #endif -FLAGR(Number, JITServerIdleTimeout, "Idle timeout in seconds to do the cleanup in JIT server", 10) +FLAGR(Number, JITServerIdleTimeout, "Idle timeout in milliseconds to do the cleanup in JIT server", 500) FLAGR(Number, JITServerMaxInactivePageAllocatorCount, "Max inactive page allocators to keep before schedule a cleanup", 10) + #undef FLAG_REGOVR_EXP #undef FLAG_REGOVR_ASMJS diff --git a/lib/Common/Core/Chakra.Common.Core.vcxproj b/lib/Common/Core/Chakra.Common.Core.vcxproj index 8ddfe8cba4a..37428583d05 100644 --- a/lib/Common/Core/Chakra.Common.Core.vcxproj +++ b/lib/Common/Core/Chakra.Common.Core.vcxproj @@ -26,6 +26,8 @@ Use CommonCorePch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) @@ -39,6 +41,7 @@ + diff --git a/lib/Common/Core/GlobalSecurityPolicy.cpp b/lib/Common/Core/GlobalSecurityPolicy.cpp new file mode 100644 index 00000000000..ec9355211f2 --- /dev/null +++ b/lib/Common/Core/GlobalSecurityPolicy.cpp @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------------------------------- +// 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 "CommonCorePch.h" + +#pragma section(".mrdata", read) + +CriticalSection GlobalSecurityPolicy::s_policyCS; + +__declspec(allocate(".mrdata")) +volatile bool GlobalSecurityPolicy::s_ro_disableSetProcessValidCallTargets = false; + +void +GlobalSecurityPolicy::DisableSetProcessValidCallTargets() +{ + // One-way transition from allowing SetProcessValidCallTargets to disabling + // the API. + if (!s_ro_disableSetProcessValidCallTargets) + { + AutoCriticalSection autocs(&s_policyCS); + DWORD oldProtect; + + BOOL res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READWRITE, &oldProtect); + if ((res == FALSE) || (oldProtect != PAGE_READONLY)) + { + RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + } + + s_ro_disableSetProcessValidCallTargets = true; + + res = VirtualProtect((LPVOID)&s_ro_disableSetProcessValidCallTargets, sizeof(s_ro_disableSetProcessValidCallTargets), PAGE_READONLY, &oldProtect); + if ((res == FALSE) || (oldProtect != PAGE_READWRITE)) + { + RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + } + } +} + +bool +GlobalSecurityPolicy::IsSetProcessValidCallTargetsAllowed() +{ + return !s_ro_disableSetProcessValidCallTargets; +} diff --git a/lib/Common/Core/GlobalSecurityPolicy.h b/lib/Common/Core/GlobalSecurityPolicy.h new file mode 100644 index 00000000000..5ba99d814b3 --- /dev/null +++ b/lib/Common/Core/GlobalSecurityPolicy.h @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- +#pragma once + +class GlobalSecurityPolicy +{ +public: + static void DisableSetProcessValidCallTargets(); + static bool IsSetProcessValidCallTargetsAllowed(); + +private: + static CriticalSection s_policyCS; + + static volatile bool s_ro_disableSetProcessValidCallTargets; +}; diff --git a/lib/Common/DataStructures/Chakra.Common.DataStructures.vcxproj b/lib/Common/DataStructures/Chakra.Common.DataStructures.vcxproj index 09240caecb5..1d61f2d1c37 100644 --- a/lib/Common/DataStructures/Chakra.Common.DataStructures.vcxproj +++ b/lib/Common/DataStructures/Chakra.Common.DataStructures.vcxproj @@ -26,6 +26,8 @@ Use CommonDataStructuresPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Common/DefaultCommonExternalApi.cpp b/lib/Common/DefaultCommonExternalApi.cpp index e030ca56100..cdd45d5bf51 100644 --- a/lib/Common/DefaultCommonExternalApi.cpp +++ b/lib/Common/DefaultCommonExternalApi.cpp @@ -20,7 +20,7 @@ bool JsUtil::ExternalApi::RaiseOnIntOverflow() return false; } -LPWSTR JsUtil::ExternalApi::GetFeatureKeyName() +LPCWSTR JsUtil::ExternalApi::GetFeatureKeyName() { return _u(""); } diff --git a/lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj b/lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj index 2aeac9a8b0f..adc60e1a4bd 100644 --- a/lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj +++ b/lib/Common/Exceptions/Chakra.Common.Exceptions.vcxproj @@ -26,6 +26,8 @@ Use CommonExceptionsPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Common/Exceptions/ReportError.h b/lib/Common/Exceptions/ReportError.h index 18a49939671..9b44fe93d81 100644 --- a/lib/Common/Exceptions/ReportError.h +++ b/lib/Common/Exceptions/ReportError.h @@ -23,9 +23,9 @@ enum ErrorReason Fatal_Debugger_AttachDetach_Failure = 15, Fatal_EntryExitRecordCorruption = 16, Fatal_UnexpectedExceptionHandling = 17, - Fatal_RpcFailure = 18, - // Reserved = 19, - Fatal_TTDAbort = 20 + Fatal_RpcFailure = 18, + Fatal_JsReentrancy_Error = 19, + Fatal_TTDAbort = 20 }; extern "C" void ReportFatalException( diff --git a/lib/Common/Exceptions/Throw.cpp b/lib/Common/Exceptions/Throw.cpp index 9c62c5f53ec..e9715e7388c 100644 --- a/lib/Common/Exceptions/Throw.cpp +++ b/lib/Common/Exceptions/Throw.cpp @@ -80,6 +80,14 @@ namespace Js { RaiseException((DWORD)DBG_TERMINATE_PROCESS, EXCEPTION_NONCONTINUABLE, 0, NULL); } +#if ENABLE_JS_REENTRANCY_CHECK + void Throw::FatalJsReentrancyError() + { + AssertMsg(false, "Js reentrancy error!!"); + ReportFatalException(NULL, E_UNEXPECTED, Fatal_JsReentrancy_Error, 0); + } +#endif + void Throw::InternalError() { AssertOrFailFastMsg(false, "Internal error!!"); diff --git a/lib/Common/Exceptions/Throw.h b/lib/Common/Exceptions/Throw.h index fc0bb938174..52caa4a8e73 100644 --- a/lib/Common/Exceptions/Throw.h +++ b/lib/Common/Exceptions/Throw.h @@ -22,6 +22,9 @@ namespace Js { static void __declspec(noreturn) FatalInternalError(); static void __declspec(noreturn) FatalInternalErrorEx(int scenario); static void __declspec(noreturn) FatalProjectionError(); +#if ENABLE_JS_REENTRANCY_CHECK + static void __declspec(noreturn) FatalJsReentrancyError(); +#endif static void CheckAndThrowOutOfMemory(BOOLEAN status); diff --git a/lib/Common/Memory/Chakra.Common.Memory.vcxproj b/lib/Common/Memory/Chakra.Common.Memory.vcxproj index e29feac56e7..c0f92991e04 100644 --- a/lib/Common/Memory/Chakra.Common.Memory.vcxproj +++ b/lib/Common/Memory/Chakra.Common.Memory.vcxproj @@ -31,6 +31,8 @@ Use CommonMemoryPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Common/Memory/CustomHeap.cpp b/lib/Common/Memory/CustomHeap.cpp index d2ce3508a96..156c8cb8c1d 100644 --- a/lib/Common/Memory/CustomHeap.cpp +++ b/lib/Common/Memory/CustomHeap.cpp @@ -565,13 +565,11 @@ bool Heap::AllocInPage(Page* page, size_t bytes, usho uint length = GetChunkSizeForBytes(bytes); BVIndex index = GetFreeIndexForPage(page, bytes); - if (index == BVInvalidIndex) { CustomHeap_BadPageState_fatal_error((ULONG_PTR)this); return false; } - char* address = page->address + Page::Alignment * index; #if PDATA_ENABLED @@ -867,7 +865,6 @@ bool Heap::FreeAllocation(Allocation* object) else { EnsureAllocationExecuteWriteable(object); - FreeAllocationHelper(object, index, length); // after freeing part of the page, the page should be in PAGE_EXECUTE_READWRITE protection, and turning to PAGE_EXECUTE (always with TARGETS_NO_UPDATE state) @@ -884,7 +881,7 @@ bool Heap::FreeAllocation(Allocation* object) } this->codePageAllocators->ProtectPages(page->address, 1, segment, protectFlags, PAGE_EXECUTE_READWRITE); - + return true; } } diff --git a/lib/Common/Memory/LargeHeapBlock.cpp b/lib/Common/Memory/LargeHeapBlock.cpp index 803484ec15b..7a0413de762 100644 --- a/lib/Common/Memory/LargeHeapBlock.cpp +++ b/lib/Common/Memory/LargeHeapBlock.cpp @@ -2050,4 +2050,4 @@ LargeHeapBlock::CapturePageHeapFreeStack() } #endif } -#endif +#endif \ No newline at end of file diff --git a/lib/Common/Memory/LargeHeapBucket.cpp b/lib/Common/Memory/LargeHeapBucket.cpp index 775869304fc..1176e460ad0 100644 --- a/lib/Common/Memory/LargeHeapBucket.cpp +++ b/lib/Common/Memory/LargeHeapBucket.cpp @@ -172,7 +172,6 @@ LargeHeapBucket::PageHeapAlloc(Recycler * recycler, size_t sizeCat, size_t size, heapBlock->heapInfo = this->heapInfo; heapBlock->actualPageCount = actualPageCount; heapBlock->guardPageAddress = guardPageAddress; - DWORD oldProtect; BOOL ret = ::VirtualProtect(guardPageAddress, AutoSystemInfo::PageSize * guardPageCount, PAGE_NOACCESS, &oldProtect); Assert(ret && oldProtect == PAGE_READWRITE); diff --git a/lib/Common/Memory/PageAllocator.h b/lib/Common/Memory/PageAllocator.h index 6f0c0f83049..f2bd423c982 100644 --- a/lib/Common/Memory/PageAllocator.h +++ b/lib/Common/Memory/PageAllocator.h @@ -895,10 +895,10 @@ class HeapPageAllocator : public PageAllocatorBase void DecommitPages(__in char* address, size_t pageCount = 1); // Release pages that has already been decommitted - void ReleaseDecommitted(void * address, size_t pageCount, __in void * segment); - bool IsAddressFromAllocator(__in void* address); + void ReleaseDecommitted(void * address, size_t pageCount, __in void * segment); + bool IsAddressFromAllocator(__in void* address); + bool AllocXdata() { return allocXdata; } - bool AllocXdata() { return allocXdata; } private: bool allocXdata; void ReleaseDecommittedSegment(__in SegmentBase* segment); diff --git a/lib/Common/Memory/Recycler.cpp b/lib/Common/Memory/Recycler.cpp index 922b2e735c2..87073479a47 100644 --- a/lib/Common/Memory/Recycler.cpp +++ b/lib/Common/Memory/Recycler.cpp @@ -4200,8 +4200,7 @@ Recycler::CollectOnConcurrentThread() // explicit instantiation template BOOL Recycler::FinishConcurrent(); template BOOL Recycler::FinishConcurrent(); -template BOOL Recycler::FinishConcurrent(); -template BOOL Recycler::FinishConcurrent(); +template BOOL Recycler::FinishConcurrent(); template BOOL Recycler::FinishConcurrent(); template diff --git a/lib/Common/Memory/Recycler.h b/lib/Common/Memory/Recycler.h index f56672c7ff1..644414b98e8 100644 --- a/lib/Common/Memory/Recycler.h +++ b/lib/Common/Memory/Recycler.h @@ -281,9 +281,10 @@ enum CollectionFlags FinishConcurrentOnIdle = CollectMode_Concurrent | CollectOverride_DisableIdleFinish, FinishConcurrentOnIdleAtRoot = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_SkipStack, - FinishConcurrentOnExitScript = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark, - FinishConcurrentOnEnterScript = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark, - FinishConcurrentOnAllocation = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark, + FinishConcurrentDefault = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark, + FinishConcurrentOnExitScript = FinishConcurrentDefault, + FinishConcurrentOnEnterScript = FinishConcurrentDefault, + FinishConcurrentOnAllocation = FinishConcurrentDefault, FinishDispose = CollectOverride_AllowDispose, FinishDisposeTimed = CollectOverride_AllowDispose | CollectHeuristic_TimeIfScriptActive, ForceFinishCollection = CollectOverride_ForceFinish | CollectOverride_ForceInThread, diff --git a/lib/JITClient/JITManager.cpp b/lib/JITClient/JITManager.cpp index 04bff7565dd..b364f11018a 100644 --- a/lib/JITClient/JITManager.cpp +++ b/lib/JITClient/JITManager.cpp @@ -200,6 +200,13 @@ void JITManager::EnableOOPJIT() { m_oopJitEnabled = true; + + if (CONFIG_FLAG(OOPCFGRegistration)) + { + // Since this client has enabled OOPJIT, perform the one-way policy update + // that will disable SetProcessValidCallTargets from being invoked. + GlobalSecurityPolicy::DisableSetProcessValidCallTargets(); + } } bool diff --git a/lib/Jsrt/Chakra.Jsrt.vcxproj b/lib/Jsrt/Chakra.Jsrt.vcxproj index f9fa8edd02a..53aff8ab5d8 100644 --- a/lib/Jsrt/Chakra.Jsrt.vcxproj +++ b/lib/Jsrt/Chakra.Jsrt.vcxproj @@ -37,6 +37,8 @@ Use JsrtPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Parser/Chakra.Parser.vcxproj b/lib/Parser/Chakra.Parser.vcxproj index 3377050108a..78e91591d9f 100644 --- a/lib/Parser/Chakra.Parser.vcxproj +++ b/lib/Parser/Chakra.Parser.vcxproj @@ -33,6 +33,8 @@ Use ParserPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index 082dcbd20c6..9a5d7b9edde 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -236,14 +236,7 @@ HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isG AssertPsz(pszSrc); AssertMemN(pse); - if (this->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackDefault); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackDefault); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackDefault); HRESULT hr; SmartFPUControl smartFpuControl; @@ -799,7 +792,7 @@ Symbol* Parser::AddDeclForPid(ParseNodePtr pnode, IdentPtr pid, SymbolType symbo Assert(this->m_reparsingLambdaParams); refForDecl->funcId = GetCurrentFunctionNode()->sxFnc.functionId; } - + if (blockInfo == GetCurrentBlockInfo()) { refForUse = refForDecl; @@ -2861,14 +2854,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall, bool isLambdaExpr = false; Assert(pToken == nullptr || pToken->tk == tkNone); // Must be empty initially - if (this->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackParseOneTerm); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackParseOneTerm); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackParseOneTerm); switch (m_token.tk) { @@ -5192,17 +5178,17 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho } return false; }); - } - if (pnodeFnc->sxFnc.IsBodyAndParamScopeMerged() && !fDeclaration && pnodeFnc->sxFnc.pnodeName != nullptr) - { - Symbol* funcSym = pnodeFnc->sxFnc.pnodeName->sxVar.sym; - if (funcSym->GetPid()->GetTopRef()->GetFuncScopeId() > pnodeFnc->sxFnc.functionId) + if (pnodeFnc->sxFnc.IsBodyAndParamScopeMerged() && !fDeclaration && pnodeFnc->sxFnc.pnodeName != nullptr) { - // This is a function expression with name captured in the param scope. In non-eval, non-split cases the function - // name symbol is added to the body scope to make it accessible in the body. But if there is a function or var - // declaration with the same name in the body then adding to the body will fail. So in this case we have to add - // the name symbol to the param scope by splitting it. - pnodeFnc->sxFnc.ResetBodyAndParamScopeMerged(); + Symbol* funcSym = pnodeFnc->sxFnc.pnodeName->sxVar.sym; + if (funcSym->GetPid()->GetTopRef()->GetFuncScopeId() > pnodeFnc->sxFnc.functionId) + { + // This is a function expression with name captured in the param scope. In non-eval, non-split cases the function + // name symbol is added to the body scope to make it accessible in the body. But if there is a function or var + // declaration with the same name in the body then adding to the body will fail. So in this case we have to add + // the name symbol to the param scope by splitting it. + pnodeFnc->sxFnc.ResetBodyAndParamScopeMerged(); + } } } } @@ -10576,9 +10562,9 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList) { Assert(pnodeFnc->nop == knopFncDecl); - // Non-simple params (such as default) require a good amount of logic to put vars on appriopriate scopes. ParseFncDecl handles it + // Non-simple params (such as default) require a good amount of logic to put vars on appropriate scopes. ParseFncDecl handles it // properly (both on defer and non-defer case). This is to avoid write duplicated logic here as well. Function with non-simple-param - // will remain deferred untill they are called. + // will remain deferred until they are called. if (pnodeFnc->sxFnc.pnodeBody == nullptr && !pnodeFnc->sxFnc.HasNonSimpleParameterList()) { // Go back and generate an AST for this function. @@ -10750,14 +10736,7 @@ void Parser::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody) return; } - if (this->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); RestoreScopeInfo(scopeInfo->GetParent()); // Recursively restore outer func scope info @@ -10806,14 +10785,7 @@ void Parser::FinishScopeInfo(Js::ParseableFunctionInfo *functionBody) return; } - if (this->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackByteCodeVisitor); int scopeId = scopeInfo->GetScopeId(); diff --git a/lib/Parser/RegexCompileTime.cpp b/lib/Parser/RegexCompileTime.cpp index 2ea0562d2ea..df2225cbfcc 100644 --- a/lib/Parser/RegexCompileTime.cpp +++ b/lib/Parser/RegexCompileTime.cpp @@ -577,7 +577,7 @@ namespace UnifiedRegex bool SimpleNode::BuildCharTrie(Compiler& compiler, CharTrie* trie, Node* cont, bool isAcceptFirst) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(tag == Empty); if (cont == 0) @@ -1095,7 +1095,7 @@ namespace UnifiedRegex bool MatchLiteralNode::BuildCharTrie(Compiler& compiler, CharTrie* trie, Node* cont, bool isAcceptFirst) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(!isEquivClass); CharTrie* tail = trie; @@ -1262,7 +1262,7 @@ namespace UnifiedRegex void MatchCharNode::BestSyncronizingNode(Compiler& compiler, Node*& bestNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (IsBetterSyncronizingNode(compiler, bestNode, this)) { @@ -1411,7 +1411,7 @@ namespace UnifiedRegex bool MatchCharNode::BuildCharTrie(Compiler& compiler, CharTrie* trie, Node* cont, bool isAcceptFirst) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (int i = 0; i < (isEquivClass ? CaseInsensitive::EquivClassSize : 1); i++) { @@ -1684,7 +1684,7 @@ namespace UnifiedRegex bool MatchSetNode::BuildCharTrie(Compiler& compiler, CharTrie* trie, Node* cont, bool isAcceptFirst) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(!isNegation && set.IsCompact()); Char entries[CharSet::MaxCompact]; @@ -1745,7 +1745,7 @@ namespace UnifiedRegex CharCount ConcatNode::TransferPass0(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(tail != 0); CharCount n = 0; @@ -1766,7 +1766,7 @@ namespace UnifiedRegex void ConcatNode::TransferPass1(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (ConcatNode *curr = this; curr != 0; curr = curr->tail) curr->head->TransferPass1(compiler, litbuf); @@ -1779,7 +1779,7 @@ namespace UnifiedRegex void ConcatNode::AnnotatePass0(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Node* prev = 0; for (ConcatNode* curr = this; curr != 0; curr = curr->tail) @@ -1804,7 +1804,7 @@ namespace UnifiedRegex void ConcatNode::AnnotatePass1(Compiler& compiler, bool parentNotInLoop, bool parentAtLeastOnce, bool parentNotSpeculative, bool parentNotNegated) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); features = HasConcat; isNotInLoop = parentNotInLoop; @@ -1863,7 +1863,7 @@ namespace UnifiedRegex void ConcatNode::AnnotatePass2(Compiler& compiler, CountDomain accumConsumes, bool accumPrevWillNotProgress, bool accumPrevWillNotRegress) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); prevConsumes = accumConsumes; isPrevWillNotProgress = accumPrevWillNotProgress; @@ -1881,7 +1881,7 @@ namespace UnifiedRegex void ConcatNode::AnnotatePass3(Compiler& compiler, CountDomain accumConsumes, CharSet* accumFollow, bool accumFollowIrrefutable, bool accumFollowEOL) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); followConsumes = accumConsumes; followSet = accumFollow; @@ -1929,7 +1929,7 @@ namespace UnifiedRegex void ConcatNode::AnnotatePass4(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); isDeterministic = true; for (ConcatNode* curr = this; curr != 0; curr = curr->tail) @@ -1942,7 +1942,7 @@ namespace UnifiedRegex bool ConcatNode::SupportsPrefixSkipping(Compiler& compiler) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); int prefix = 0; for (const ConcatNode* curr = this; curr != 0; curr = curr->tail) @@ -1957,14 +1957,14 @@ namespace UnifiedRegex Node* ConcatNode::HeadSyncronizingNode(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); return head->HeadSyncronizingNode(compiler); } void ConcatNode::AccumDefineGroups(Js::ScriptContext* scriptContext, int& minGroup, int& maxGroup) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); for (ConcatNode *curr = this; curr != 0; curr = curr->tail) curr->head->AccumDefineGroups(scriptContext, minGroup, maxGroup); @@ -1982,7 +1982,7 @@ namespace UnifiedRegex void ConcatNode::BestSyncronizingNode(Compiler& compiler, Node*& bestNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (ConcatNode* curr = this; curr != 0; curr = curr->tail) curr->head->BestSyncronizingNode(compiler, bestNode); @@ -1990,7 +1990,7 @@ namespace UnifiedRegex void ConcatNode::Emit(Compiler& compiler, CharCount& skipped) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); // // Compilation scheme: @@ -2013,7 +2013,7 @@ namespace UnifiedRegex bool ConcatNode::IsOctoquad(Compiler& compiler, OctoquadIdentifier* oi) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (ConcatNode* curr = this; curr != 0; curr = curr->tail) { @@ -2025,7 +2025,7 @@ namespace UnifiedRegex bool ConcatNode::IsCharTrieArm(Compiler& compiler, uint& accNumAlts) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (const ConcatNode* curr = this; curr != 0; curr = curr->tail) { @@ -2037,7 +2037,7 @@ namespace UnifiedRegex bool ConcatNode::BuildCharTrie(Compiler& compiler, CharTrie* trie, Node* cont, bool isAcceptFirst) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (cont != 0) // We don't want to manage a stack of continuations @@ -2076,7 +2076,7 @@ namespace UnifiedRegex CharCount AltNode::TransferPass0(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(tail != 0); CharCount n = 0; @@ -2097,7 +2097,7 @@ namespace UnifiedRegex void AltNode::TransferPass1(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (AltNode *curr = this; curr != 0; curr = curr->tail) curr->head->TransferPass1(compiler, litbuf); @@ -2110,7 +2110,7 @@ namespace UnifiedRegex void AltNode::AnnotatePass0(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); isWord = true; for (AltNode* curr = this; curr != 0; curr = curr->tail) @@ -2123,7 +2123,7 @@ namespace UnifiedRegex void AltNode::AnnotatePass1(Compiler& compiler, bool parentNotInLoop, bool parentAtLeastOnce, bool parentNotSpeculative, bool parentNotNegated) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); features = HasAlt; isNotInLoop = parentNotInLoop; @@ -2172,7 +2172,7 @@ namespace UnifiedRegex void AltNode::AnnotatePass2(Compiler& compiler, CountDomain accumConsumes, bool accumPrevWillNotProgress, bool accumPrevWillNotRegress) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); prevConsumes = accumConsumes; isPrevWillNotProgress = accumPrevWillNotProgress; @@ -2183,7 +2183,7 @@ namespace UnifiedRegex void AltNode::AnnotatePass3(Compiler& compiler, CountDomain accumConsumes, CharSet* accumFollow, bool accumFollowIrrefutable, bool accumFollowEOL) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); followConsumes = accumConsumes; followSet = accumFollow; @@ -2200,7 +2200,7 @@ namespace UnifiedRegex void AltNode::AnnotatePass4(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); // // Simplification rule @@ -2494,7 +2494,7 @@ namespace UnifiedRegex CharCount AltNode::MinSyncronizingLiteralLength(Compiler& compiler, int& numLiterals) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); // Here, we ignore nodes with length 1, which are Char nodes. The way the Alt node synchronization // is currently implemented, it expects all nodes to be Literal nodes. It requires quite a bit of @@ -2524,7 +2524,7 @@ namespace UnifiedRegex void AltNode::CollectSyncronizingLiterals(Compiler& compiler, ScannersMixin& scanners) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); for (const AltNode* curr = this; curr != 0; curr = curr->tail) curr->head->CollectSyncronizingLiterals(compiler, scanners); @@ -2532,7 +2532,7 @@ namespace UnifiedRegex void AltNode::BestSyncronizingNode(Compiler& compiler, Node*& bestNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (IsBetterSyncronizingNode(compiler, bestNode, this)) bestNode = this; @@ -2540,7 +2540,7 @@ namespace UnifiedRegex void AltNode::AccumDefineGroups(Js::ScriptContext* scriptContext, int& minGroup, int& maxGroup) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); for (AltNode *curr = this; curr != 0; curr = curr->tail) curr->head->AccumDefineGroups(scriptContext, minGroup, maxGroup); @@ -2548,7 +2548,7 @@ namespace UnifiedRegex void AltNode::Emit(Compiler& compiler, CharCount& skipped) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(skipped == 0); switch (scheme) @@ -2870,7 +2870,7 @@ namespace UnifiedRegex CharCount AltNode::EmitScan(Compiler& compiler, bool isHeadSyncronizingNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(!isHeadSyncronizingNode); @@ -2892,7 +2892,7 @@ namespace UnifiedRegex bool AltNode::IsOctoquad(Compiler& compiler, OctoquadIdentifier* oi) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (tail == 0 || tail->tail != 0) // Must be exactly two alts @@ -2948,7 +2948,7 @@ namespace UnifiedRegex CharCount DefineGroupNode::TransferPass0(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(groupId > 0 && groupId < compiler.program->numGroups); return body->TransferPass0(compiler, litbuf); @@ -2956,7 +2956,7 @@ namespace UnifiedRegex void DefineGroupNode::TransferPass1(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->TransferPass1(compiler, litbuf); } @@ -2968,7 +2968,7 @@ namespace UnifiedRegex void DefineGroupNode::AnnotatePass0(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->AnnotatePass0(compiler); isWord = body->isWord; @@ -2976,7 +2976,7 @@ namespace UnifiedRegex void DefineGroupNode::AnnotatePass1(Compiler& compiler, bool parentNotInLoop, bool parentAtLeastOnce, bool parentNotSpeculative, bool parentNotNegated) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); features = HasDefineGroup; body->AnnotatePass1(compiler, parentNotInLoop, parentAtLeastOnce, parentNotSpeculative, parentNotNegated); @@ -2995,7 +2995,7 @@ namespace UnifiedRegex void DefineGroupNode::AnnotatePass2(Compiler& compiler, CountDomain accumConsumes, bool accumPrevWillNotProgress, bool accumPrevWillNotRegress) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); prevConsumes = accumConsumes; isPrevWillNotProgress = accumPrevWillNotProgress; @@ -3005,7 +3005,7 @@ namespace UnifiedRegex void DefineGroupNode::AnnotatePass3(Compiler& compiler, CountDomain accumConsumes, CharSet* accumFollow, bool accumFollowIrrefutable, bool accumFollowEOL) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); followConsumes = accumConsumes; followSet = accumFollow; @@ -3018,7 +3018,7 @@ namespace UnifiedRegex void DefineGroupNode::AnnotatePass4(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->AnnotatePass4(compiler); isDeterministic = body->isDeterministic; @@ -3064,7 +3064,7 @@ namespace UnifiedRegex bool DefineGroupNode::SupportsPrefixSkipping(Compiler& compiler) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (scheme != Fixed) // We can't skip over part of the match if the BeginDefineGroup must capture it's start @@ -3074,7 +3074,7 @@ namespace UnifiedRegex Node* DefineGroupNode::HeadSyncronizingNode(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (scheme != Fixed) // Can't skip BeginDefineGroup @@ -3084,28 +3084,28 @@ namespace UnifiedRegex CharCount DefineGroupNode::MinSyncronizingLiteralLength(Compiler& compiler, int& numLiterals) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); return body->MinSyncronizingLiteralLength(compiler, numLiterals); } void DefineGroupNode::CollectSyncronizingLiterals(Compiler& compiler, ScannersMixin& scanners) const { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->CollectSyncronizingLiterals(compiler, scanners); } void DefineGroupNode::BestSyncronizingNode(Compiler& compiler, Node*& bestNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->BestSyncronizingNode(compiler, bestNode); } void DefineGroupNode::AccumDefineGroups(Js::ScriptContext* scriptContext, int& minGroup, int& maxGroup) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); if (groupId < minGroup) minGroup = groupId; @@ -3116,7 +3116,7 @@ namespace UnifiedRegex void DefineGroupNode::Emit(Compiler& compiler, CharCount& skipped) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); switch (scheme) { @@ -3369,7 +3369,7 @@ namespace UnifiedRegex CharCount LoopNode::TransferPass0(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(repeats.upper == CharCountFlag || repeats.upper > 0); Assert(repeats.upper == CharCountFlag || repeats.upper >= repeats.lower); @@ -3379,7 +3379,7 @@ namespace UnifiedRegex void LoopNode::TransferPass1(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->TransferPass1(compiler, litbuf); } @@ -3391,7 +3391,7 @@ namespace UnifiedRegex void LoopNode::AnnotatePass0(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->AnnotatePass0(compiler); isWord = !repeats.CouldMatchEmpty() && body->isWord; @@ -3399,7 +3399,7 @@ namespace UnifiedRegex void LoopNode::AnnotatePass1(Compiler& compiler, bool parentNotInLoop, bool parentAtLeastOnce, bool parentNotSpeculative, bool parentNotNegated) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); features = HasLoop; isNotInLoop = parentNotInLoop; @@ -3424,7 +3424,7 @@ namespace UnifiedRegex void LoopNode::AnnotatePass2(Compiler& compiler, CountDomain accumConsumes, bool accumPrevWillNotProgress, bool accumPrevWillNotRegress) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); prevConsumes = accumConsumes; isPrevWillNotProgress = accumPrevWillNotProgress; @@ -3442,7 +3442,7 @@ namespace UnifiedRegex void LoopNode::AnnotatePass3(Compiler& compiler, CountDomain accumConsumes, CharSet* accumFollow, bool accumFollowIrrefutable, bool accumFollowEOL) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); followConsumes = accumConsumes; followSet = accumFollow; @@ -3500,7 +3500,7 @@ namespace UnifiedRegex void LoopNode::AnnotatePass4(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->AnnotatePass4(compiler); isDeterministic = body->isDeterministic; @@ -3731,7 +3731,7 @@ namespace UnifiedRegex void LoopNode::BestSyncronizingNode(Compiler& compiler, Node*& bestNode) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); if (repeats.lower > 0) body->BestSyncronizingNode(compiler, bestNode); @@ -3740,14 +3740,14 @@ namespace UnifiedRegex void LoopNode::AccumDefineGroups(Js::ScriptContext* scriptContext, int& minGroup, int& maxGroup) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); body->AccumDefineGroups(scriptContext, minGroup, maxGroup); } void LoopNode::Emit(Compiler& compiler, CharCount& skipped) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(skipped == 0); @@ -4110,14 +4110,14 @@ namespace UnifiedRegex CharCount AssertionNode::TransferPass0(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); return body->TransferPass0(compiler, litbuf); } void AssertionNode::TransferPass1(Compiler& compiler, const Char* litbuf) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->TransferPass1(compiler, litbuf); } @@ -4129,7 +4129,7 @@ namespace UnifiedRegex void AssertionNode::AnnotatePass0(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); isWord = false; body->AnnotatePass0(compiler); @@ -4137,7 +4137,7 @@ namespace UnifiedRegex void AssertionNode::AnnotatePass1(Compiler& compiler, bool parentNotInLoop, bool parentAtLeastOnce, bool parentNotSpeculative, bool parentNotNegated) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); features = HasAssertion; body->AnnotatePass1(compiler, parentNotInLoop, parentAtLeastOnce, false, parentNotNegated && !isNegation); @@ -4164,7 +4164,7 @@ namespace UnifiedRegex void AssertionNode::AnnotatePass2(Compiler& compiler, CountDomain accumConsumes, bool accumPrevWillNotProgress, bool accumPrevWillNotRegress) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); prevConsumes = accumConsumes; isPrevWillNotProgress = accumPrevWillNotProgress; @@ -4174,7 +4174,7 @@ namespace UnifiedRegex void AssertionNode::AnnotatePass3(Compiler& compiler, CountDomain accumConsumes, CharSet* accumFollow, bool accumFollowIrrefutable, bool accumFollowEOL) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); followConsumes = accumConsumes; followSet = accumFollow; @@ -4192,7 +4192,7 @@ namespace UnifiedRegex void AssertionNode::AnnotatePass4(Compiler& compiler) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); body->AnnotatePass4(compiler); // Even if body is non-deterministic we cut the choicepoints on exit from the assertion, @@ -4256,14 +4256,14 @@ namespace UnifiedRegex void AssertionNode::AccumDefineGroups(Js::ScriptContext* scriptContext, int& minGroup, int& maxGroup) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); body->AccumDefineGroups(scriptContext, minGroup, maxGroup); } void AssertionNode::Emit(Compiler& compiler, CharCount& skipped) { - PROBE_STACK(compiler.scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(compiler.scriptContext, Js::Constants::MinStackRegex); Assert(skipped == 0); diff --git a/lib/Parser/RegexParser.cpp b/lib/Parser/RegexParser.cpp index 290f42938bd..981bb77c1ce 100644 --- a/lib/Parser/RegexParser.cpp +++ b/lib/Parser/RegexParser.cpp @@ -1129,7 +1129,7 @@ namespace UnifiedRegex template void Parser::TermPass0(int depth) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); // Either we have a location at the start, or the end, never both. As in between it should have been cleared if surrogate pair // Or must be cleared if we didn't perform the check bool clearLocationIfPresent = this->tempLocationOfSurrogatePair != nullptr; @@ -1282,7 +1282,7 @@ namespace UnifiedRegex template Node* Parser::TermPass1(MatchCharNode* deferredCharNode, bool& previousSurrogatePart) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); Node* node = 0; bool containsSurrogatePair = false; diff --git a/lib/Parser/Scan.cpp b/lib/Parser/Scan.cpp index e0827d76f39..67de79fe23e 100644 --- a/lib/Parser/Scan.cpp +++ b/lib/Parser/Scan.cpp @@ -951,14 +951,7 @@ tokens Scanner::RescanRegExpTokenizer() template tokens Scanner::ScanRegExpConstant(ArenaAllocator* alloc) { - if (m_parser && m_parser->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackRegex); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackRegex); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackRegex); // SEE ALSO: RegexHelper::PrimCompileDynamic() @@ -1023,14 +1016,7 @@ tokens Scanner::ScanRegExpConstant(ArenaAllocator* alloc) template tokens Scanner::ScanRegExpConstantNoAST(ArenaAllocator* alloc) { - if (m_parser && m_parser->IsBackgroundParser()) - { - PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackRegex); - } - else - { - PROBE_STACK(m_scriptContext, Js::Constants::MinStackRegex); - } + PROBE_STACK_NO_DISPOSE(m_scriptContext, Js::Constants::MinStackRegex); ThreadContext *threadContext = m_fSyntaxColor ? ThreadContext::GetContextForCurrentThread() : m_scriptContext->GetThreadContext(); UnifiedRegex::StandardChars* standardEncodedChars = threadContext->GetStandardChars((EncodedChar*)0); diff --git a/lib/Parser/ptree.h b/lib/Parser/ptree.h index e4efdb65804..7034d042145 100644 --- a/lib/Parser/ptree.h +++ b/lib/Parser/ptree.h @@ -248,9 +248,9 @@ struct PnFnc RestorePoint *pRestorePoint; DeferredFunctionStub *deferredStub; bool canBeDeferred; - bool fibPreventsDeferral; bool isBodyAndParamScopeMerged; // Indicates whether the param scope and the body scope of the function can be merged together or not. // We cannot merge both scopes together if there is any closure capture or eval is present in the param scope. + bool fibPreventsDeferral; static const int32 MaxStackClosureAST = 800000; @@ -326,8 +326,8 @@ struct PnFnc void SetIsDefaultModuleExport(bool set = true) { SetFlags(kFunctionIsDefaultModuleExport, set); } void SetNestedFuncEscapes(bool set = true) { nestedFuncEscapes = set; } void SetCanBeDeferred(bool set = true) { canBeDeferred = set; } - void SetFIBPreventsDeferral(bool set = true) { fibPreventsDeferral = set; } void ResetBodyAndParamScopeMerged() { isBodyAndParamScopeMerged = false; } + void SetFIBPreventsDeferral(bool set = true) { fibPreventsDeferral = set; } bool CallsEval() const { return HasFlags(kFunctionCallsEval); } bool ChildCallsEval() const { return HasFlags(kFunctionChildCallsEval); } @@ -366,8 +366,8 @@ struct PnFnc bool IsDefaultModuleExport() const { return HasFlags(kFunctionIsDefaultModuleExport); } bool NestedFuncEscapes() const { return nestedFuncEscapes; } bool CanBeDeferred() const { return canBeDeferred; } - bool FIBPreventsDeferral() const { return fibPreventsDeferral; } bool IsBodyAndParamScopeMerged() { return isBodyAndParamScopeMerged; } + bool FIBPreventsDeferral() const { return fibPreventsDeferral; } size_t LengthInBytes() { diff --git a/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj b/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj index 22bfc68b526..1f0735255de 100644 --- a/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj +++ b/lib/Runtime/Base/Chakra.Runtime.Base.vcxproj @@ -33,6 +33,8 @@ Use RuntimeBasePch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) @@ -128,4 +130,4 @@ - \ No newline at end of file + diff --git a/lib/Runtime/Base/CrossSite.cpp b/lib/Runtime/Base/CrossSite.cpp index 139db9bc212..417682412b9 100644 --- a/lib/Runtime/Base/CrossSite.cpp +++ b/lib/Runtime/Base/CrossSite.cpp @@ -70,6 +70,16 @@ namespace Js } } } + else if (object->GetTypeId() == TypeIds_Proxy) + { + RecyclableObject * target = JavascriptProxy::FromVar(object)->GetTarget(); + if (JavascriptConversion::IsCallable(target)) + { + Assert(JavascriptProxy::FunctionCallTrap == object->GetEntryPoint()); + TTD_XSITE_LOG(scriptContext, "setEntryPoint->CrossSiteProxyCallTrap ", object); + object->GetDynamicType()->SetEntryPoint(CrossSite::CrossSiteProxyCallTrap); + } + } } void CrossSite::MarshalPrototypeChain(ScriptContext* scriptContext, DynamicObject * object) @@ -398,6 +408,14 @@ namespace Js return CommonThunk(function, entryPoint, args); } + Var CrossSite::CrossSiteProxyCallTrap(RecyclableObject* function, CallInfo callInfo, ...) + { + RUNTIME_ARGUMENTS(args, callInfo); + Assert(JavascriptProxy::Is(function)); + + return CrossSite::CommonThunk(function, JavascriptProxy::FunctionCallTrap, args); + } + Var CrossSite::CommonThunk(RecyclableObject* recyclableObject, JavascriptMethod entryPoint, Arguments args) { DynamicObject* function = DynamicObject::FromVar(recyclableObject); @@ -425,7 +443,7 @@ namespace Js { i = 1; Assert(args.Info.Flags & CallFlags_New); - Assert(JavascriptFunction::Is(function) && JavascriptFunction::FromVar(function)->GetFunctionInfo()->GetAttributes() & FunctionInfo::SkipDefaultNewObject); + Assert(JavascriptProxy::Is(function) || (JavascriptFunction::Is(function) && JavascriptFunction::FromVar(function)->GetFunctionInfo()->GetAttributes() & FunctionInfo::SkipDefaultNewObject)); } uint count = args.Info.Count; if ((args.Info.Flags & CallFlags_ExtraArg) && ((args.Info.Flags & CallFlags_NewTarget) == 0)) diff --git a/lib/Runtime/Base/CrossSite.h b/lib/Runtime/Base/CrossSite.h index efa68fa0434..55dd0e2ebb2 100644 --- a/lib/Runtime/Base/CrossSite.h +++ b/lib/Runtime/Base/CrossSite.h @@ -16,6 +16,7 @@ namespace Js static BOOL NeedMarshalVar(Var instance, ScriptContext * requestContext); static Var DefaultThunk(RecyclableObject* function, CallInfo callInfo, ...); static Var ProfileThunk(RecyclableObject* function, CallInfo callInfo, ...); + static Var CrossSiteProxyCallTrap(RecyclableObject* function, CallInfo callInfo, ...); static Var MarshalVar(ScriptContext* scriptContext, Var value, bool fRequestWrapper = false); static void MarshalDynamicObjectAndPrototype(ScriptContext * scriptContext, DynamicObject * object); static void ForceCrossSiteThunkOnPrototypeChain(RecyclableObject* object); diff --git a/lib/Runtime/Base/FunctionBody.cpp b/lib/Runtime/Base/FunctionBody.cpp index 96507a0c4f9..f07093c1682 100644 --- a/lib/Runtime/Base/FunctionBody.cpp +++ b/lib/Runtime/Base/FunctionBody.cpp @@ -9795,7 +9795,7 @@ namespace Js } else { - Assert(!functionType->GetEntryPointInfo()->IsFunctionEntryPointInfo() || + Assert(!functionType->GetEntryPointInfo()->IsFunctionEntryPointInfo() || ((FunctionEntryPointInfo*)functionType->GetEntryPointInfo())->IsCleanedUp() || (DWORD_PTR)functionType->GetEntryPoint() != this->GetNativeAddress()); } diff --git a/lib/Runtime/Base/FunctionBody.h b/lib/Runtime/Base/FunctionBody.h index afce37ad0a5..cf7e2ef31d7 100644 --- a/lib/Runtime/Base/FunctionBody.h +++ b/lib/Runtime/Base/FunctionBody.h @@ -3977,7 +3977,6 @@ namespace Js { PropertyId * propertyIdsForRegSlots; uint length; - // This keeps the upper bound of register slots for the formals. While emitting locals in the body we skip // the properties that are below this limit. RegSlot formalsUpperBound; diff --git a/lib/Runtime/Base/ScriptContext.cpp b/lib/Runtime/Base/ScriptContext.cpp index 4f5bab3fba1..1eb2cd23303 100644 --- a/lib/Runtime/Base/ScriptContext.cpp +++ b/lib/Runtime/Base/ScriptContext.cpp @@ -85,6 +85,7 @@ namespace Js deferredBody(false), isScriptContextActuallyClosed(false), isFinalized(false), + isEvalRestricted(false), isInvalidatedForHostObjects(false), fastDOMenabled(false), directHostTypeId(TypeIds_GlobalObject), @@ -682,7 +683,7 @@ namespace Js // Stop profiling if present DeRegisterProfileProbe(S_OK, nullptr); #endif - + if (this->diagnosticArena != nullptr) { HeapDelete(this->diagnosticArena); @@ -3342,8 +3343,34 @@ namespace Js if (proxy == nullptr) { + // Not a user defined function, we need to wrap them with try-catch for "continue after exception" + if (!pFunction->IsScriptFunction() && IsExceptionWrapperForBuiltInsEnabled(scriptContext)) + { +#if defined(ENABLE_SCRIPT_DEBUGGING) || defined(ENABLE_SCRIPT_PROFILING) + if (scriptContext->IsScriptContextInDebugMode()) + { + // We are attaching. + // For built-ins, WinRT and DOM functions which are already in recycler, change entry points to route to debug/profile thunk. + ScriptContext::SetEntryPointToProfileThunk(pFunction); + } + else + { + // We are detaching. + // For built-ins, WinRT and DOM functions which are already in recycler, restore entry points to original. + if (!scriptContext->IsProfiling()) + { + ScriptContext::RestoreEntryPointFromProfileThunk(pFunction); + } + // If we are profiling, don't change anything. + } +#else + AssertMsg(false, "Debugging/Profiling needs to be enabled to change thunks"); +#endif + } + return; } + Assert(proxy->GetFunctionInfo() == info); if (!proxy->IsFunctionBody()) @@ -3820,6 +3847,26 @@ namespace Js Var aReturn = NULL; JavascriptMethod origEntryPoint = function->GetFunctionInfo()->GetOriginalEntryPoint(); + if (scriptContext->IsEvalRestriction()) + { + if (origEntryPoint == Js::GlobalObject::EntryEval) + { + origEntryPoint = Js::GlobalObject::EntryEvalRestrictedMode; + } + else if (origEntryPoint == Js::JavascriptFunction::NewInstance) + { + origEntryPoint = Js::JavascriptFunction::NewInstanceRestrictedMode; + } + else if (origEntryPoint == Js::JavascriptGeneratorFunction::NewInstance) + { + origEntryPoint = Js::JavascriptGeneratorFunction::NewInstanceRestrictedMode; + } + else if (origEntryPoint == Js::JavascriptFunction::NewAsyncFunctionInstance) + { + origEntryPoint = Js::JavascriptFunction::NewAsyncFunctionInstanceRestrictedMode; + } + } + __try { Assert(!function->IsScriptFunction() || function->GetFunctionProxy()); diff --git a/lib/Runtime/Base/ScriptContext.h b/lib/Runtime/Base/ScriptContext.h index 73c1b9d409c..f9631822e5f 100644 --- a/lib/Runtime/Base/ScriptContext.h +++ b/lib/Runtime/Base/ScriptContext.h @@ -840,6 +840,7 @@ namespace Js // this is to indicate the actual close is called once only. bool isScriptContextActuallyClosed; bool isFinalized; + bool isEvalRestricted; #if DBG bool isInitialized; bool isCloningGlobal; @@ -938,6 +939,8 @@ namespace Js bool IsFinalized() const { return isFinalized; } void SetIsFinalized() { isFinalized = true; } bool IsActuallyClosed() const { return isScriptContextActuallyClosed; } + void SetEvalRestriction(bool set) { this->isEvalRestricted = set; } + bool IsEvalRestriction() const { return this->isEvalRestricted; } #if ENABLE_NATIVE_CODEGEN bool IsClosedNativeCodeGenerator() const { diff --git a/lib/Runtime/Base/ThreadContext.cpp b/lib/Runtime/Base/ThreadContext.cpp index fcc0854fb04..4174a7367a1 100644 --- a/lib/Runtime/Base/ThreadContext.cpp +++ b/lib/Runtime/Base/ThreadContext.cpp @@ -203,6 +203,9 @@ ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager, #ifdef ENABLE_DIRECTCALL_TELEMETRY , directCallTelemetry(this) #endif +#if ENABLE_JS_REENTRANCY_CHECK + , noJsReentrancy(false) +#endif { pendingProjectionContextCloseList = JsUtil::List::New(GetThreadAlloc()); hostScriptContextStack = Anew(GetThreadAlloc(), JsUtil::Stack, GetThreadAlloc()); diff --git a/lib/Runtime/Base/ThreadContext.h b/lib/Runtime/Base/ThreadContext.h index 820eb6ccdc2..779d8719f53 100644 --- a/lib/Runtime/Base/ThreadContext.h +++ b/lib/Runtime/Base/ThreadContext.h @@ -617,6 +617,9 @@ class ThreadContext sealed : bool isThreadBound; bool hasThrownPendingException; bool callDispose; +#if ENABLE_JS_REENTRANCY_CHECK + bool noJsReentrancy; +#endif AllocationPolicyManager * allocationPolicyManager; @@ -1663,6 +1666,19 @@ class ThreadContext sealed : private: JsUtil::BaseDictionary entryPointToBuiltInOperationIdCache; +#if ENABLE_JS_REENTRANCY_CHECK +public: + void SetNoJsReentrancy(bool val) { noJsReentrancy = val; } + bool GetNoJsReentrancy() { return noJsReentrancy; } + void AssertJsReentrancy() + { + if (GetNoJsReentrancy()) + { + Js::Throw::FatalJsReentrancyError(); + } + } +#endif + public: bool IsEntryPointToBuiltInOperationIdCacheInitialized() { @@ -1746,3 +1762,27 @@ class AutoProfilingUserCode threadContext->SetIsProfilingUserCode(oldIsProfilingUserCode); } }; + +#if ENABLE_JS_REENTRANCY_CHECK +class JsReentLock +{ + ThreadContext *m_threadContext; + bool m_savedNoJsReentrancy; + +public: + JsReentLock(ThreadContext *threadContext) + { + m_savedNoJsReentrancy = threadContext->GetNoJsReentrancy(); + threadContext->SetNoJsReentrancy(true); + m_threadContext = threadContext; + } + + void unlock() { m_threadContext->SetNoJsReentrancy(m_savedNoJsReentrancy); } + void relock() { m_threadContext->SetNoJsReentrancy(true); } + + ~JsReentLock() + { + m_threadContext->SetNoJsReentrancy(m_savedNoJsReentrancy); + } +}; +#endif diff --git a/lib/Runtime/Base/ThreadContextInfo.cpp b/lib/Runtime/Base/ThreadContextInfo.cpp index 1a28121b3e1..edf48adefdc 100644 --- a/lib/Runtime/Base/ThreadContextInfo.cpp +++ b/lib/Runtime/Base/ThreadContextInfo.cpp @@ -418,6 +418,15 @@ ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetV { AssertMsg(IS_16BYTE_ALIGNED(callTargetAddress), "callTargetAddress is not 16-byte page aligned?"); + // If SetProcessValidCallTargets is not allowed by global policy (e.g. + // OOP JIT is in use in the client), then generate a fast fail + // exception as state has been corrupted and attempt is being made to + // illegally call SetProcessValidCallTargets. + if (!GlobalSecurityPolicy::IsSetProcessValidCallTargetsAllowed()) + { + RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + } + PVOID startAddressOfPage = (PVOID)(PAGE_START_ADDR(callTargetAddress)); size_t codeOffset = OFFSET_ADDR_WITHIN_PAGE(callTargetAddress); diff --git a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h index ab08d8b4c15..f33b1757e40 100644 --- a/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h +++ b/lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h @@ -4,6 +4,6 @@ //------------------------------------------------------------------------------------------------------- // NOTE: If there is a merge conflict the correct fix is to make a new GUID. -// {484481fd-f876-4769-80a7-f863fe0ff4a7} +// {FEE3FADC-D028-4221-907F-37E25EABBD96} const GUID byteCodeCacheReleaseFileVersion = -{ 0x484481fd, 0xf876, 0x4769, { 0x80, 0xa7, 0xf8, 0x63, 0xfe, 0x0f, 0xf4, 0xa7 } }; +{ 0xFEE3FADC, 0xD028, 0x4221, { 0x90, 0x7F, 0x37, 0xE2, 0x5E, 0xAB, 0xBD, 0x96 } }; diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index b559305b4b7..d2759505624 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -3395,11 +3395,11 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode) Symbol* funcSym = funcInfo->root->sxFnc.GetFuncSymbol(); paramScope->ForEachSymbol([&](Symbol* param) { Symbol* varSym = funcInfo->GetBodyScope()->FindLocalSymbol(param->GetName()); - if (varSym) + if ((funcSym == nullptr || funcSym != param) // Do not copy the symbol over to body as the function expression symbol + // is expected to stay inside the function expression scope + && (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister))) { - if ((funcSym == nullptr || funcSym != param) // Do not copy the symbol over to body as the function expression symbol - // is expected to stay inside the function expression scope - && (varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister))) + if (varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister)) { if (!varSym->GetNeedDeclaration()) { diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp index 98fa57da544..79110d1d1a0 100644 --- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp +++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp @@ -972,7 +972,7 @@ void ByteCodeGenerator::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody { if (functionBody && functionBody->GetScopeInfo()) { - PROBE_STACK(scriptContext, Js::Constants::MinStackByteCodeVisitor); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackByteCodeVisitor); Js::ScopeInfo* scopeInfo = functionBody->GetScopeInfo(); RestoreScopeInfo(scopeInfo->GetParent()); // Recursively restore outer func scope info @@ -982,11 +982,9 @@ void ByteCodeGenerator::RestoreScopeInfo(Js::ParseableFunctionInfo* functionBody if (paramScopeInfo != nullptr) { paramScope = paramScopeInfo->GetScope(); - Assert(paramScope); } Scope* bodyScope = scopeInfo->GetScope(); - Assert(bodyScope); bodyScope->SetHasOwnLocalInClosure(scopeInfo->GetHasOwnLocalInClosure()); @@ -1323,7 +1321,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen else { parseableFunctionInfo = Js::ParseableFunctionInfo::New(scriptContext, pnode->sxFnc.nestedCount, functionId, m_utf8SourceInfo, name, nameLength, shortNameOffset, propertyRecordList, attributes, - pnode->sxFnc.IsClassConstructor() ? + pnode->sxFnc.IsClassConstructor() ? Js::FunctionBody::FunctionBodyFlags::Flags_None : Js::FunctionBody::FunctionBodyFlags::Flags_HasNoExplicitReturnValue); } @@ -2451,7 +2449,7 @@ FuncInfo* PreVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerato WritePerfHint(PerfHints::HeapArgumentsDueToWriteToFormals, funcInfo->GetParsedFunctionBody(), 0); } #endif - + //With statements - need scope object to be present. if ((doStackArgsOpt && pnode->sxFnc.funcInfo->GetParamScope()->Count() > 1) && (pnode->sxFnc.funcInfo->HasDeferredChild() || (byteCodeGenerator->GetFlags() & fscrEval) || pnode->sxFnc.HasWithStmt() || byteCodeGenerator->IsInDebugMode() || PHASE_OFF1(Js::StackArgFormalsOptPhase) || PHASE_OFF1(Js::StackArgOptPhase))) @@ -2464,7 +2462,7 @@ FuncInfo* PreVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerato { WritePerfHint(PerfHints::HasWithBlock, funcInfo->GetParsedFunctionBody(), 0); } - + if(byteCodeGenerator->GetFlags() & fscrEval) { WritePerfHint(PerfHints::SrcIsEval, funcInfo->GetParsedFunctionBody(), 0); @@ -2687,7 +2685,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat { if (!top->IsGlobalFunction()) { - auto fnProcess = + auto fnProcess = [byteCodeGenerator, top](Symbol *const sym) { if (sym->GetHasNonLocalReference() && !sym->GetIsModuleExportStorage()) @@ -2971,7 +2969,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat parentFunc->GetParamScope()->SetIsObject(); // Record this for future use in the no-refresh debugging. parentFunctionBody->SetHasSetIsObject(true); - } + } } // Propagate HasMaybeEscapedNestedFunc @@ -3065,7 +3063,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat pnode->sxFnc.HasHeapArguments()) { bool doStackArgsOpt = top->byteCodeFunction->GetDoBackendArgumentsOptimization(); - + bool hasAnyParamInClosure = top->GetHasLocalInClosure() && top->GetParamScope()->GetHasOwnLocalInClosure(); if ((doStackArgsOpt && top->inArgsCount > 1)) @@ -3085,7 +3083,7 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat //Scope object creation instr will be a MOV NULL instruction in the Lowerer - if we still decide to do StackArgs after Globopt phase. top->byteCodeFunction->SetDoScopeObjectCreation(false); } - } + } } return top; } diff --git a/lib/Runtime/ByteCode/Chakra.Runtime.ByteCode.vcxproj b/lib/Runtime/ByteCode/Chakra.Runtime.ByteCode.vcxproj index 39e26e82bfa..738ec1f86e2 100644 --- a/lib/Runtime/ByteCode/Chakra.Runtime.ByteCode.vcxproj +++ b/lib/Runtime/ByteCode/Chakra.Runtime.ByteCode.vcxproj @@ -33,6 +33,8 @@ Use RuntimeByteCodePch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Runtime/Debug/Chakra.Runtime.Debug.vcxproj b/lib/Runtime/Debug/Chakra.Runtime.Debug.vcxproj index ddc3b2aea7b..a947aeec382 100644 --- a/lib/Runtime/Debug/Chakra.Runtime.Debug.vcxproj +++ b/lib/Runtime/Debug/Chakra.Runtime.Debug.vcxproj @@ -38,6 +38,8 @@ Use RuntimeDebugPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Runtime/Debug/TTInflateMap.cpp b/lib/Runtime/Debug/TTInflateMap.cpp index b16e1a6d7ea..5809e113b2b 100644 --- a/lib/Runtime/Debug/TTInflateMap.cpp +++ b/lib/Runtime/Debug/TTInflateMap.cpp @@ -6,6 +6,8 @@ #if ENABLE_TTD +#define PATH_BUFFER_COUNT 256 + namespace TTD { InflateMap::InflateMap() @@ -292,18 +294,20 @@ namespace TTD ; } - void TTDComparePath::WritePathToConsole(ThreadContext* threadContext, bool printNewline, char16* namebuff) const + void TTDComparePath::WritePathToConsole(ThreadContext* threadContext, bool printNewline, _Out_writes_z_(buffLength) char16* namebuff, charcount_t namebuffLength) const { if(this->m_prefix != nullptr) { - this->m_prefix->WritePathToConsole(threadContext, false, namebuff); + this->m_prefix->WritePathToConsole(threadContext, false, namebuff, namebuffLength); } if(this->m_stepKind == StepKind::PropertyData || this->m_stepKind == StepKind::PropertyGetter || this->m_stepKind == StepKind::PropertySetter) { const Js::PropertyRecord* pRecord = threadContext->GetPropertyName((Js::PropertyId)this->m_step.IndexOrPID); - js_memcpy_s(namebuff, 256 * sizeof(char16), pRecord->GetBuffer(), pRecord->GetLength() * sizeof(char16)); - namebuff[pRecord->GetLength()] = _u('\0'); + js_memcpy_s(namebuff, namebuffLength * sizeof(char16), pRecord->GetBuffer(), pRecord->GetLength() * sizeof(char16)); + + // Don't allow the null to be written past the end of the buffer. + namebuff[min(namebuffLength - 1, pRecord->GetLength())] = _u('\0'); } bool isFirst = (this->m_prefix == nullptr); @@ -366,7 +370,7 @@ namespace TTD { this->StrictCrossSite = !threadContext->TTDLog->IsDebugModeFlagSet(); - this->PathBuffer = TT_HEAP_ALLOC_ARRAY_ZERO(char16, 256); + this->PathBuffer = TT_HEAP_ALLOC_ARRAY_ZERO(char16, PATH_BUFFER_COUNT); this->SnapObjCmpVTable = TT_HEAP_ALLOC_ARRAY_ZERO(fPtr_AssertSnapEquivAddtlInfo, (int32)NSSnapObjects::SnapObjectType::Limit); @@ -395,7 +399,7 @@ namespace TTD TTDCompareMap::~TTDCompareMap() { - TT_HEAP_FREE_ARRAY(char16, this->PathBuffer, 256); + TT_HEAP_FREE_ARRAY(char16, this->PathBuffer, PATH_BUFFER_COUNT); TT_HEAP_FREE_ARRAY(TTD::fPtr_AssertSnapEquivAddtlInfo, this->SnapObjCmpVTable, (int32)NSSnapObjects::SnapObjectType::Limit); @@ -414,7 +418,7 @@ namespace TTD { wprintf(_u("Snap1 ptrid: *0x%I64x\n"), this->CurrentH1Ptr); wprintf(_u("Snap2 ptrid: *0x%I64x\n"), this->CurrentH2Ptr); - this->CurrentPath->WritePathToConsole(this->Context, true, this->PathBuffer); + this->CurrentPath->WritePathToConsole(this->Context, true, this->PathBuffer, PATH_BUFFER_COUNT); } } diff --git a/lib/Runtime/Debug/TTInflateMap.h b/lib/Runtime/Debug/TTInflateMap.h index 0f3338f6221..12557aa48d1 100644 --- a/lib/Runtime/Debug/TTInflateMap.h +++ b/lib/Runtime/Debug/TTInflateMap.h @@ -170,7 +170,7 @@ namespace TTD ~TTDComparePath(); - void WritePathToConsole(ThreadContext* threadContext, bool printNewline, char16* namebuff) const; + void WritePathToConsole(ThreadContext* threadContext, bool printNewline, _Out_writes_z_(namebuffLength) char16* namebuff, charcount_t namebuffLength) const; }; //A class that we use to manage all the dictionaries we need when comparing 2 snapshots diff --git a/lib/Runtime/Debug/TTSerialize.h b/lib/Runtime/Debug/TTSerialize.h index 42f15479f45..96e367ff8e9 100644 --- a/lib/Runtime/Debug/TTSerialize.h +++ b/lib/Runtime/Debug/TTSerialize.h @@ -447,7 +447,7 @@ namespace TTD } } - bool PeekRawChar(char16* c) + bool PeekRawChar(_Out_ char16* c) { if(this->m_peekChar != -1) { @@ -465,7 +465,7 @@ namespace TTD } } - bool ReadRawChar(char16* c) + bool ReadRawChar(_Out_ char16* c) { if(this->m_peekChar != -1) { @@ -484,6 +484,9 @@ namespace TTD if(this->m_cursor == this->m_buffCount) { + // Make sure to set a value before we return. + *c = _u('\0'); + return false; } else diff --git a/lib/Runtime/Language/Arguments.h b/lib/Runtime/Language/Arguments.h index a7884f67622..be1fd17f879 100644 --- a/lib/Runtime/Language/Arguments.h +++ b/lib/Runtime/Language/Arguments.h @@ -66,23 +66,50 @@ inline int _count_args(const T1&, const T2&, const T3&, const T4&, Js::CallInfo #ifdef _WIN32 -#define CALL_ENTRYPOINT(entryPoint, function, callInfo, ...) \ +#define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \ entryPoint(function, callInfo, ##__VA_ARGS__) #elif defined(_M_X64) || defined(_M_IX86) // Call an entryPoint (JavascriptMethod) with custom calling convention. // RDI == function, RSI == callInfo, (RDX/RCX/R8/R9==null/unused), // all parameters on stack. -#define CALL_ENTRYPOINT(entryPoint, function, callInfo, ...) \ +#define CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, ...) \ entryPoint(function, callInfo, nullptr, nullptr, nullptr, nullptr, \ function, callInfo, ##__VA_ARGS__) #else #error CALL_ENTRYPOINT not yet implemented #endif -#define CALL_FUNCTION(function, callInfo, ...) \ - CALL_ENTRYPOINT(function->GetEntryPoint(), \ +#define CALL_FUNCTION_NOASSERT(function, callInfo, ...) \ + CALL_ENTRYPOINT_NOASSERT(function->GetEntryPoint(), \ function, callInfo, ##__VA_ARGS__) +#if ENABLE_JS_REENTRANCY_CHECK +#define CALL_FUNCTION(threadContext, function, callInfo, ...) \ + (threadContext->AssertJsReentrancy(), \ + CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__)); +#define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \ + (threadContext->AssertJsReentrancy(), \ + CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__)); +#define JS_REENTRANT(reentrancyLock, ...) \ + reentrancyLock.unlock(); \ + __VA_ARGS__; \ + reentrancyLock.relock(); +#define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \ + reentrancyLock.unlock(); \ + __VA_ARGS__; +#define JS_REENTRANCY_LOCK(reentrancyLock, threadContext) \ + JsReentLock reentrancyLock(threadContext); +#else +#define CALL_FUNCTION(threadContext, function, callInfo, ...) \ + CALL_FUNCTION_NOASSERT(function, callInfo, __VA_ARGS__); +#define CALL_ENTRYPOINT(threadContext, entryPoint, function, callInfo, ...) \ + CALL_ENTRYPOINT_NOASSERT(entryPoint, function, callInfo, __VA_ARGS__); +#define JS_REENTRANT(reentrancyLock, ...) \ + __VA_ARGS__; +#define JS_REENTRANT_UNLOCK(reentrancyLock, ...) \ + __VA_ARGS__; +#define JS_REENTRANCY_LOCK(reentrancyLock, threadContext) +#endif // ENABLE_JS_REENTRANCY_CHECK /* * RUNTIME_ARGUMENTS is a simple wrapper around the variadic calling convention diff --git a/lib/Runtime/Language/AsmJsLink.cpp b/lib/Runtime/Language/AsmJsLink.cpp index d3947f8443e..7690b278eed 100644 --- a/lib/Runtime/Language/AsmJsLink.cpp +++ b/lib/Runtime/Language/AsmJsLink.cpp @@ -4,9 +4,9 @@ //------------------------------------------------------------------------------------------------------- #include "RuntimeLanguagePch.h" -#include "Library/BoundFunction.h" #ifdef ASMJS_PLAT +#include "Library/BoundFunction.h" namespace Js{ bool ASMLink::CheckArrayBuffer(ScriptContext* scriptContext, Var bufferView, const AsmJsModuleInfo * info) { diff --git a/lib/Runtime/Language/Chakra.Runtime.Language.vcxproj b/lib/Runtime/Language/Chakra.Runtime.Language.vcxproj index 8a1787d58bc..77ab468232b 100644 --- a/lib/Runtime/Language/Chakra.Runtime.Language.vcxproj +++ b/lib/Runtime/Language/Chakra.Runtime.Language.vcxproj @@ -39,6 +39,8 @@ Use RuntimeLanguagePch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 4fa8b26113d..ff56bfe048a 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -6216,7 +6216,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) _InstructionSynchronizationBarrier(); #endif uint newOffset = ::Math::PointerCastToIntegral( - CALL_ENTRYPOINT(address, function, CallInfo(CallFlags_InternalFrame, 1), this)); + CALL_ENTRYPOINT_NOASSERT(address, function, CallInfo(CallFlags_InternalFrame, 1), this)); #ifdef _M_IX86 _asm @@ -6250,7 +6250,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip) _InstructionSynchronizationBarrier(); #endif uint newOffset = ::Math::PointerCastToIntegral( - CALL_ENTRYPOINT(address, function, CallInfo(CallFlags_InternalFrame, 1), this)); + CALL_ENTRYPOINT_NOASSERT(address, function, CallInfo(CallFlags_InternalFrame, 1), this)); #ifdef _M_IX86 _asm diff --git a/lib/Runtime/Language/JavascriptConversion.cpp b/lib/Runtime/Language/JavascriptConversion.cpp index 96f77ede828..67b7470227c 100644 --- a/lib/Runtime/Language/JavascriptConversion.cpp +++ b/lib/Runtime/Language/JavascriptConversion.cpp @@ -524,7 +524,7 @@ namespace Js Assert(!ThreadContext::IsOnStack(recyclableObject)); // Let result be the result of calling the[[Call]] internal method of exoticToPrim, with input as thisArgument and(hint) as argumentsList. - return CALL_FUNCTION(exoticToPrim, CallInfo(CallFlags_Value, 2), recyclableObject, hintString); + return CALL_FUNCTION(threadContext, exoticToPrim, CallInfo(CallFlags_Value, 2), recyclableObject, hintString); }); if (!result) @@ -754,7 +754,7 @@ namespace Js if (JavascriptConversion::IsCallable(value)) { RecyclableObject* toLocaleStringFunction = RecyclableObject::FromVar(value); - Var aResult = CALL_FUNCTION(toLocaleStringFunction, CallInfo(1), aValue); + Var aResult = CALL_FUNCTION(scriptContext->GetThreadContext(), toLocaleStringFunction, CallInfo(1), aValue); if (JavascriptString::Is(aResult)) { return JavascriptString::FromVar(aResult); diff --git a/lib/Runtime/Language/JavascriptExceptionOperators.cpp b/lib/Runtime/Language/JavascriptExceptionOperators.cpp index c7d6671ddf8..6c13b599b6b 100644 --- a/lib/Runtime/Language/JavascriptExceptionOperators.cpp +++ b/lib/Runtime/Language/JavascriptExceptionOperators.cpp @@ -849,7 +849,13 @@ namespace Js // Make sure we didn't disable exceptions !scriptContext->GetThreadContext()->IsDisableImplicitException() ); - scriptContext->GetThreadContext()->ClearDisableImplicitFlags(); + + ThreadContext *threadContext = scriptContext->GetThreadContext(); + threadContext->ClearDisableImplicitFlags(); +#if ENABLE_JS_REENTRANCY_CHECK + threadContext->SetNoJsReentrancy(false); +#endif + if (fillExceptionContext && considerPassingToDebugger) { DispatchExceptionToDebugger(exceptionObject, scriptContext); diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 6e6aaffbfa4..4a7d79b52e5 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -197,28 +197,28 @@ namespace Js switch (argCount) { case 0: Assert(false); - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo); break; case 1: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance); break; case 2: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0]); break; case 3: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1]); break; case 4: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2]); break; case 5: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3]); break; case 6: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4]); break; case 7: - ret = CALL_ENTRYPOINT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4], stackPtr[5]); + ret = CALL_ENTRYPOINT_NOASSERT(entryPoint, funcPtr, callInfo, instance, stackPtr[0], stackPtr[1], stackPtr[2], stackPtr[3], stackPtr[4], stackPtr[5]); break; default: { // Don't need stack probe here- we just did so above @@ -1401,12 +1401,13 @@ namespace Js && JavascriptArray::FromVar(aRight)->GetHead()->left == 0 && JavascriptArray::FromVar(aRight)->GetHead()->length == JavascriptArray::FromVar(aRight)->GetLength() && JavascriptArray::FromVar(aRight)->HasNoMissingValues() + && !JavascriptArray::FromVar(aRight)->IsCrossSiteObject() )) || (TypedArrayBase::Is(aRight) && method == TypedArrayBase::EntryInfo::Values.GetOriginalEntryPoint())) // We can't optimize away the iterator if the array iterator prototype is user defined. && !JavascriptLibrary::ArrayIteratorPrototypeHasUserDefinedNext(scriptContext)) { - return aRight; + return RecyclerNew(scriptContext->GetRecycler(), SpreadArgument, aRight, true /*useDirectCall*/, scriptContext->GetLibrary()->GetSpreadArgumentType()); } ThreadContext *threadContext = scriptContext->GetThreadContext(); @@ -1414,7 +1415,7 @@ namespace Js Var iteratorVar = threadContext->ExecuteImplicitCall(function, ImplicitCall_Accessor, [=]() -> Var { - return CALL_FUNCTION(function, CallInfo(Js::CallFlags_Value, 1), aRight); + return CALL_FUNCTION(threadContext, function, CallInfo(Js::CallFlags_Value, 1), aRight); }); if (!JavascriptOperators::IsObject(iteratorVar)) @@ -1426,8 +1427,7 @@ namespace Js JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject); } - RecyclableObject* iterator = RecyclableObject::FromVar(iteratorVar); - return RecyclerNew(scriptContext->GetRecycler(), SpreadArgument, aRight, iterator, scriptContext->GetLibrary()->GetSpreadArgumentType()); + return RecyclerNew(scriptContext->GetRecycler(), SpreadArgument, iteratorVar, false /*useDirectCall*/, scriptContext->GetLibrary()->GetSpreadArgumentType()); } BOOL JavascriptOperators::IsPropertyUnscopable(Var instanceVar, JavascriptString *propertyString) @@ -5834,7 +5834,7 @@ namespace Js JavascriptOperators::NewScObjectCommon(object, functionInfo, requestContext) : JavascriptOperators::NewScObjectHostDispatchOrProxy(object, requestContext); - Var returnVar = CALL_FUNCTION(object, CallInfo(CallFlags_New, 1), newObject); + Var returnVar = CALL_FUNCTION(object->GetScriptContext()->GetThreadContext(), object, CallInfo(CallFlags_New, 1), newObject); if (JavascriptOperators::IsObject(returnVar)) { newObject = returnVar; @@ -7009,7 +7009,7 @@ namespace Js RecyclableObject *instFunc = RecyclableObject::FromVar(instOfHandler); Var result = threadContext->ExecuteImplicitCall(instFunc, ImplicitCall_Accessor, [=]()->Js::Var { - return CALL_FUNCTION(instFunc, CallInfo(CallFlags_Value, 2), constructor, instance); + return CALL_FUNCTION(scriptContext->GetThreadContext(), instFunc, CallInfo(CallFlags_Value, 2), constructor, instance); }); return JavascriptBoolean::ToVar(JavascriptConversion::ToBoolean(result, scriptContext) ? TRUE : FALSE, scriptContext); @@ -9367,7 +9367,7 @@ namespace Js Var thisVar = RootToThisObject(object, scriptContext); RecyclableObject* marshalledFunction = RecyclableObject::FromVar(CrossSite::MarshalVar(requestContext, function)); - Var result = CALL_ENTRYPOINT(marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 1), thisVar); + Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 1), thisVar); result = CrossSite::MarshalVar(requestContext, result); return result; @@ -9409,7 +9409,7 @@ namespace Js marshalledFunction = RecyclableObject::FromVar(CrossSite::MarshalVar(requestContext, function)); } - Var result = CALL_ENTRYPOINT(marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 2), thisVar, putValue); + Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 2), thisVar, putValue); Assert(result); return nullptr; }); @@ -10523,7 +10523,7 @@ namespace Js return nullptr; } - Var iterator = CALL_FUNCTION(function, CallInfo(Js::CallFlags_Value, 1), instance); + Var iterator = CALL_FUNCTION(scriptContext->GetThreadContext(), function, CallInfo(Js::CallFlags_Value, 1), instance); if (!JavascriptOperators::IsObject(iterator)) { diff --git a/lib/Runtime/Library/ArrayBuffer.cpp b/lib/Runtime/Library/ArrayBuffer.cpp index 67871567815..1dbf195c55f 100644 --- a/lib/Runtime/Library/ArrayBuffer.cpp +++ b/lib/Runtime/Library/ArrayBuffer.cpp @@ -443,7 +443,7 @@ namespace Js template ArrayBuffer::ArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) : - ArrayBufferBase(type), mIsAsmJsBuffer(false), isBufferCleared(false),isDetached(false) + ArrayBufferBase(type), mIsAsmJsBuffer(false), isBufferCleared(false) { buffer = nullptr; bufferLength = 0; @@ -490,7 +490,7 @@ namespace Js } ArrayBuffer::ArrayBuffer(byte* buffer, uint32 length, DynamicType * type) : - buffer(buffer), bufferLength(length), ArrayBufferBase(type), mIsAsmJsBuffer(false), isDetached(false) + buffer(buffer), bufferLength(length), ArrayBufferBase(type), mIsAsmJsBuffer(false) { if (length > MaxArrayBufferLength) { diff --git a/lib/Runtime/Library/ArrayBuffer.h b/lib/Runtime/Library/ArrayBuffer.h index 38609c27e1c..04d03e6384d 100644 --- a/lib/Runtime/Library/ArrayBuffer.h +++ b/lib/Runtime/Library/ArrayBuffer.h @@ -18,24 +18,28 @@ namespace Js virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext) = 0; + ArrayBufferBase(DynamicType *type) : DynamicObject(type), isDetached(false) { } + bool IsDetached() { return isDetached; } + #if ENABLE_TTD virtual void MarshalCrossSite_TTDInflate() = 0; #endif - ArrayBufferBase(DynamicType *type) : DynamicObject(type) { } - virtual bool IsArrayBuffer() = 0; virtual bool IsSharedArrayBuffer() = 0; virtual ArrayBuffer * GetAsArrayBuffer() = 0; virtual SharedArrayBuffer * GetAsSharedArrayBuffer() { return nullptr; } virtual void AddParent(ArrayBufferParent* parent) { } - virtual bool IsDetached() { return false; } virtual uint32 GetByteLength() const = 0; virtual BYTE* GetBuffer() const = 0; virtual bool IsValidVirtualBufferLength(uint length) { return false; } static bool Is(Var value); static ArrayBufferBase* FromVar(Var value); + static int GetIsDetachedOffset() { return offsetof(ArrayBufferBase, isDetached); } + + protected: + bool isDetached; }; class ArrayBuffer : public ArrayBufferBase @@ -111,13 +115,11 @@ namespace Js virtual BOOL GetDiagValueString(StringBuilder* stringBuilder, ScriptContext* requestContext) override; virtual ArrayBufferDetachedStateBase* DetachAndGetState(); - virtual bool IsDetached() override { return this->isDetached; } void SetIsAsmJsBuffer(){ mIsAsmJsBuffer = true; } virtual uint32 GetByteLength() const override { return bufferLength; } virtual BYTE* GetBuffer() const override { return buffer; } static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); } - static int GetIsDetachedOffset() { return offsetof(ArrayBuffer, isDetached); } static int GetBufferOffset() { return offsetof(ArrayBuffer, buffer); } virtual void AddParent(ArrayBufferParent* parent) override; @@ -162,9 +164,6 @@ namespace Js BYTE *buffer; // Points to a heap allocated RGBA buffer, can be null uint32 bufferLength; // Number of bytes allocated - // When an ArrayBuffer is detached, the TypedArray and DataView objects pointing to it must be made aware, - // for this purpose the ArrayBuffer needs to hold WeakReferences to them - bool isDetached; bool mIsAsmJsBuffer; bool isBufferCleared; diff --git a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj index ad696f8a892..fc3e52103b7 100644 --- a/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj +++ b/lib/Runtime/Library/Chakra.Runtime.Library.vcxproj @@ -40,7 +40,8 @@ Use RuntimeLibraryPch.h - /bigobj %(AdditionalOptions) + + -Zc:implicitNoexcept- /bigobj %(AdditionalOptions) diff --git a/lib/Runtime/Library/CompoundString.h b/lib/Runtime/Library/CompoundString.h index 4c78ba62a23..bbfa858944e 100644 --- a/lib/Runtime/Library/CompoundString.h +++ b/lib/Runtime/Library/CompoundString.h @@ -435,6 +435,12 @@ namespace Js //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma endregion + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableCompoundString; + } }; #pragma region CompoundString::Builder definition diff --git a/lib/Runtime/Library/ConcatString.h b/lib/Runtime/Library/ConcatString.h index d9a4f64c740..1a13658e3d9 100644 --- a/lib/Runtime/Library/ConcatString.h +++ b/lib/Runtime/Library/ConcatString.h @@ -228,6 +228,13 @@ namespace Js #if DBG bool IsFilled() const; #endif + + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableConcatStringMulti; + } }; } diff --git a/lib/Runtime/Library/GlobalObject.cpp b/lib/Runtime/Library/GlobalObject.cpp index a8140537dc6..f72dd2ad410 100644 --- a/lib/Runtime/Library/GlobalObject.cpp +++ b/lib/Runtime/Library/GlobalObject.cpp @@ -713,7 +713,7 @@ namespace Js } } - return library->GetGlobalObject()->ExecuteEvalParsedFunction(pfuncScript, environment, varThis); + return library->GetGlobalObject()->ExecuteEvalParsedFunction(pfuncScript, environment, varThis, scriptContext); } void GlobalObject::UpdateThisForEval(Var &varThis, ModuleID moduleID, ScriptContext *scriptContext, BOOL strictMode) @@ -729,7 +729,7 @@ namespace Js } - Var GlobalObject::ExecuteEvalParsedFunction(ScriptFunction *pfuncScript, FrameDisplay* environment, Var &varThis) + Var GlobalObject::ExecuteEvalParsedFunction(ScriptFunction *pfuncScript, FrameDisplay* environment, Var &varThis, ScriptContext *scriptContext) { Assert(pfuncScript != nullptr); @@ -741,7 +741,7 @@ namespace Js // Executing the eval causes the scope chain to escape. pfuncScript->InvalidateCachedScopeChain(); } - Var varResult = CALL_FUNCTION(pfuncScript, CallInfo(CallFlags_Eval, 1), varThis); + Var varResult = CALL_FUNCTION(scriptContext->GetThreadContext(), pfuncScript, CallInfo(CallFlags_Eval, 1), varThis); pfuncScript->SetEnvironment(nullptr); return varResult; } diff --git a/lib/Runtime/Library/GlobalObject.h b/lib/Runtime/Library/GlobalObject.h index fc8fe1182b0..b781fe21d27 100644 --- a/lib/Runtime/Library/GlobalObject.h +++ b/lib/Runtime/Library/GlobalObject.h @@ -33,7 +33,7 @@ namespace Js BOOL ReserveGlobalProperty(PropertyId propertyId); BOOL IsReservedGlobalProperty(PropertyId propertyId); - Var ExecuteEvalParsedFunction(ScriptFunction *pfuncScript, FrameDisplay* environment, Var &varThis); + Var ExecuteEvalParsedFunction(ScriptFunction *pfuncScript, FrameDisplay* environment, Var &varThis, ScriptContext *scriptContext); class EntryInfo { diff --git a/lib/Runtime/Library/JSONParser.cpp b/lib/Runtime/Library/JSONParser.cpp index 91d62303a0b..a579d22057c 100644 --- a/lib/Runtime/Library/JSONParser.cpp +++ b/lib/Runtime/Library/JSONParser.cpp @@ -6,6 +6,7 @@ #include "JSON.h" #include "JSONParser.h" + using namespace Js; namespace JSON diff --git a/lib/Runtime/Library/JavascriptArray.cpp b/lib/Runtime/Library/JavascriptArray.cpp index c344bf8b6f7..3263a040074 100644 --- a/lib/Runtime/Library/JavascriptArray.cpp +++ b/lib/Runtime/Library/JavascriptArray.cpp @@ -7,6 +7,9 @@ #include "Types/PathTypeHandler.h" #include "Types/SpreadArgument.h" +// TODO: Change this generic fatal error to the descriptive one. +#define AssertAndFailFast(x) if (!(x)) { Assert(x); Js::Throw::FatalInternalError(); } + namespace Js { // Make sure EmptySegment points to read-only memory. @@ -802,7 +805,7 @@ namespace Js } template<> - void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const uint32& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count) + void JavascriptArray::InternalFillFromPrototype(JavascriptArray *dstArray, const uint32& dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count) { RecyclableObject* prototype = srcArray->GetPrototype(); while (start + count != end && JavascriptOperators::GetTypeId(prototype) != TypeIds_Null) @@ -2963,6 +2966,52 @@ namespace Js { return pDestObj->SetItem(idxDest, aItem, Js::PropertyOperation_ThrowIfNotExtensible); } + + uint64 JavascriptArray::OP_GetLength(Var obj, ScriptContext *scriptContext) + { + if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) + { + // Casting to uint64 is okay as ToLength will always be >= 0. + return (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); + } + else + { + return (uint64)JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); + } + } + + template + void JavascriptArray::TryGetArrayAndLength(Var arg, + ScriptContext *scriptContext, + PCWSTR methodName, + __out JavascriptArray** array, + __out RecyclableObject** obj, + __out T * length) + { + Assert(array != nullptr); + Assert(obj != nullptr); + Assert(length != nullptr); + + if (JavascriptArray::Is(arg) && !JavascriptArray::FromVar(arg)->IsCrossSiteObject()) + { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(arg); +#endif + *array = JavascriptArray::FromVar(arg); + *obj = *array; + *length = (*array)->length; + } + else + { + if (!JavascriptConversion::ToObject(arg, scriptContext, obj)) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, methodName); + } + *length = OP_GetLength(*obj, scriptContext); + *array = nullptr; + } + } + BOOL JavascriptArray::SetArrayLikeObjects(RecyclableObject* pDestObj, BigIndex idxDest, Var aItem) { ScriptContext* scriptContext = pDestObj->GetScriptContext(); @@ -2988,6 +3037,7 @@ namespace Js template void JavascriptArray::ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, uint startIdxDest, BOOL firstPromotedItemIsSpreadable, BigIndex firstPromotedItemLength) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); JavascriptArray* pDestArray = nullptr; if (JavascriptArray::Is(pDestObj)) @@ -3005,11 +3055,11 @@ namespace Js // firstPromotedItemIsSpreadable is ONLY used to resume after a type promotion from uint32 to uint64 // we do this because calls to IsConcatSpreadable are observable (a big deal for proxies) and we don't // want to do the work a second time as soon as we record the length we clear the flag. - spreadable = firstPromotedItemIsSpreadable || JavascriptOperators::IsConcatSpreadable(aItem); + JS_REENTRANT(jsReentLock, spreadable = firstPromotedItemIsSpreadable || JavascriptOperators::IsConcatSpreadable(aItem)); if (!spreadable) { - JavascriptArray::SetConcatItem(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext); + JS_REENTRANT(jsReentLock, JavascriptArray::SetConcatItem(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext)); ++idxDest; continue; } @@ -3021,19 +3071,19 @@ namespace Js if (JavascriptNativeIntArray::Is(aItem)) { JavascriptNativeIntArray *pItemArray = JavascriptNativeIntArray::FromVar(aItem); - CopyNativeIntArrayElementsToVar(pDestArray, idxDest, pItemArray); + JS_REENTRANT(jsReentLock, CopyNativeIntArrayElementsToVar(pDestArray, idxDest, pItemArray)); idxDest = idxDest + pItemArray->length; } else if (JavascriptNativeFloatArray::Is(aItem)) { JavascriptNativeFloatArray *pItemArray = JavascriptNativeFloatArray::FromVar(aItem); - CopyNativeFloatArrayElementsToVar(pDestArray, idxDest, pItemArray); + JS_REENTRANT(jsReentLock, CopyNativeFloatArrayElementsToVar(pDestArray, idxDest, pItemArray)); idxDest = idxDest + pItemArray->length; } else { JavascriptArray* pItemArray = JavascriptArray::FromVar(aItem); - CopyArrayElements(pDestArray, idxDest, pItemArray); + JS_REENTRANT(jsReentLock, CopyArrayElements(pDestArray, idxDest, pItemArray)); idxDest = idxDest + pItemArray->length; } } @@ -3049,15 +3099,9 @@ namespace Js firstPromotedItemIsSpreadable = false; length = firstPromotedItemLength; } - else if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - // we can cast to uin64 without fear of converting negative numbers to large positive ones - // from int64 because ToLength makes negative lengths 0 - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext); - } else { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext); + JS_REENTRANT(jsReentLock, length = OP_GetLength(aItem, scriptContext)); } if (PromoteToBigIndex(length,idxDest)) @@ -3080,9 +3124,10 @@ namespace Js uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength; for (uint32 idxSubItem = 0u; idxSubItem < lengthToUin32Max; ++idxSubItem) { - if (JavascriptOperators::HasItem(itemObject, idxSubItem)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(itemObject, idxSubItem)); + if (hasItem) { - subItem = JavascriptOperators::GetItem(itemObject, idxSubItem, scriptContext); + JS_REENTRANT(jsReentLock, subItem = JavascriptOperators::GetItem(itemObject, idxSubItem, scriptContext)); if (pDestArray) { @@ -3090,7 +3135,7 @@ namespace Js } else { - ThrowErrorOnFailure(SetArrayLikeObjects(pDestObj, idxDest, subItem), scriptContext, idxDest); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(SetArrayLikeObjects(pDestObj, idxDest, subItem), scriptContext, idxDest)); } } ++idxDest; @@ -3100,16 +3145,17 @@ namespace Js { PropertyRecord const * propertyRecord; JavascriptOperators::GetPropertyIdForInt(idxSubItem.GetBigIndex(), scriptContext, &propertyRecord); - if (JavascriptOperators::HasProperty(itemObject,propertyRecord->GetPropertyId())) + JS_REENTRANT(jsReentLock, BOOL hasProp = JavascriptOperators::HasProperty(itemObject, propertyRecord->GetPropertyId())); + if (hasProp) { - subItem = JavascriptOperators::GetProperty(itemObject, propertyRecord->GetPropertyId(), scriptContext); + JS_REENTRANT(jsReentLock, subItem = JavascriptOperators::GetProperty(itemObject, propertyRecord->GetPropertyId(), scriptContext)); if (pDestArray) { pDestArray->DirectSetItemAt(idxDest, subItem); } else { - ThrowErrorOnFailure(SetArrayLikeObjects(pDestObj, idxDest, subItem), scriptContext, idxSubItem); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(SetArrayLikeObjects(pDestObj, idxDest, subItem), scriptContext, idxSubItem)); } } ++idxDest; @@ -3117,15 +3163,15 @@ namespace Js } else // concat 1 item { - JavascriptArray::SetConcatItem(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext); + JS_REENTRANT(jsReentLock, JavascriptArray::SetConcatItem(aItem, idxArg, pDestArray, pDestObj, idxDest, scriptContext)); ++idxDest; } } } if (!pDestArray) { - pDestObj->SetProperty(PropertyIds::length, ConvertToIndex(idxDest, scriptContext), Js::PropertyOperation_None, nullptr); - } + JS_REENTRANT(jsReentLock, pDestObj->SetProperty(PropertyIds::length, ConvertToIndex(idxDest, scriptContext), Js::PropertyOperation_None, nullptr)); + } else if (pDestArray->GetLength() != ConvertToIndex(idxDest, scriptContext)) { pDestArray->SetLength(ConvertToIndex(idxDest, scriptContext)); @@ -3149,39 +3195,45 @@ namespace Js JavascriptArray* JavascriptArray::ConcatIntArgs(JavascriptNativeIntArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); uint idxDest = 0u; for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++) { Var aItem = args[idxArg]; - bool concatSpreadable = !scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() || JavascriptOperators::IsConcatSpreadable(aItem); - if (!JavascriptNativeIntArray::Is(pDestArray)) - { - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); - return pDestArray; - } - if(!concatSpreadable) + if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled()) { - pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible); - idxDest = idxDest + 1; - if (!JavascriptNativeIntArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back + JS_REENTRANT(jsReentLock, BOOL isConcatSpreadable = JavascriptOperators::IsConcatSpreadable(aItem)); + if (!JavascriptNativeIntArray::Is(pDestArray)) { - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest); + ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); return pDestArray; } - continue; + if (!isConcatSpreadable) + { + JS_REENTRANT(jsReentLock, pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible)); + idxDest = idxDest + 1; + if (!JavascriptNativeIntArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back + { + JS_REENTRANT(jsReentLock, ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest)); + return pDestArray; + } + continue; + } } if (JavascriptNativeIntArray::Is(aItem)) // Fast path { JavascriptNativeIntArray* pItemArray = JavascriptNativeIntArray::FromVar(aItem); - bool converted = CopyNativeIntArrayElements(pDestArray, idxDest, pItemArray); + + JS_REENTRANT(jsReentLock, bool converted = CopyNativeIntArrayElements(pDestArray, idxDest, pItemArray)); + idxDest = idxDest + pItemArray->length; if (converted) { // Copying the last array forced a conversion, so switch over to the var version // to finish. - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest); + JS_REENTRANT(jsReentLock, ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest)); return pDestArray; } } @@ -3206,7 +3258,7 @@ namespace Js else { JavascriptArray *pVarDestArray = JavascriptNativeIntArray::ConvertToVarArray(pDestArray); - ConcatArgs(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); + JS_REENTRANT(jsReentLock, ConcatArgs(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest)); return pVarDestArray; } } @@ -3219,28 +3271,32 @@ namespace Js JavascriptArray* JavascriptArray::ConcatFloatArgs(JavascriptNativeFloatArray* pDestArray, TypeId *remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); uint idxDest = 0u; for (uint idxArg = 0; idxArg < args.Info.Count; idxArg++) { Var aItem = args[idxArg]; - bool concatSpreadable = !scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled() || JavascriptOperators::IsConcatSpreadable(aItem); - if (!JavascriptNativeFloatArray::Is(pDestArray)) - { - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); - return pDestArray; - } - if (!concatSpreadable) + if (scriptContext->GetConfig()->IsES6IsConcatSpreadableEnabled()) { - pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible); - - idxDest = idxDest + 1; - if (!JavascriptNativeFloatArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back + JS_REENTRANT(jsReentLock, BOOL isConcatSpreadable = JavascriptOperators::IsConcatSpreadable(aItem)); + if (!JavascriptNativeFloatArray::Is(pDestArray)) { - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest); + ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); return pDestArray; } - continue; + if (!isConcatSpreadable) + { + JS_REENTRANT(jsReentLock, pDestArray->SetItem(idxDest, aItem, PropertyOperation_ThrowIfNotExtensible)); + + idxDest = idxDest + 1; + if (!JavascriptNativeFloatArray::Is(pDestArray)) // SetItem could convert pDestArray to a var array if aItem is not an integer if so fall back + { + JS_REENTRANT(jsReentLock, ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest)); + return pDestArray; + } + continue; + } } bool converted; @@ -3249,26 +3305,34 @@ namespace Js if (JavascriptNativeIntArray::Is(aItem)) // Fast path { JavascriptNativeIntArray *pIntArray = JavascriptNativeIntArray::FromVar(aItem); - converted = CopyNativeIntArrayElementsToFloat(pDestArray, idxDest, pIntArray); + + JS_REENTRANT(jsReentLock, converted = CopyNativeIntArrayElementsToFloat(pDestArray, idxDest, pIntArray)); + idxDest = idxDest + pIntArray->length; } else if (JavascriptNativeFloatArray::Is(aItem)) { JavascriptNativeFloatArray* pItemArray = JavascriptNativeFloatArray::FromVar(aItem); - converted = CopyNativeFloatArrayElements(pDestArray, idxDest, pItemArray); + + JS_REENTRANT(jsReentLock, converted = CopyNativeFloatArrayElements(pDestArray, idxDest, pItemArray)); + idxDest = idxDest + pItemArray->length; } else { JavascriptArray *pVarDestArray = JavascriptNativeFloatArray::ConvertToVarArray(pDestArray); - ConcatArgs(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest); + + JS_REENTRANT(jsReentLock, ConcatArgs(pVarDestArray, remoteTypeIds, args, scriptContext, idxArg, idxDest)); + return pVarDestArray; } if (converted) { // Copying the last array forced a conversion, so switch over to the var version // to finish. - ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest); + + JS_REENTRANT(jsReentLock, ConcatArgs(pDestArray, remoteTypeIds, args, scriptContext, idxArg + 1, idxDest)); + return pDestArray; } } @@ -3305,6 +3369,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -3363,13 +3428,15 @@ namespace Js { if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) { - int64 len = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext); + JS_REENTRANT(jsReentLock, + int64 len = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext)); // clipping to MaxArrayLength will overflow when added to cDestLength which we catch below cDestLength = UInt32Math::Add(cDestLength, len < MaxArrayLength ? (uint32)len : MaxArrayLength, destLengthOverflow); } else { - uint len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext); + JS_REENTRANT(jsReentLock, + uint len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(aItem, scriptContext), scriptContext)); cDestLength = UInt32Math::Add(cDestLength, len, destLengthOverflow); } } @@ -3429,7 +3496,9 @@ namespace Js // RecyclableObject* pDestObj = nullptr; bool isArray = false; - pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext); + + JS_REENTRANT(jsReentLock, pDestObj = ArraySpeciesCreate(args[0], 0, scriptContext)); + if (pDestObj) { #if ENABLE_COPYONACCESS_ARRAY @@ -3481,6 +3550,7 @@ namespace Js isArray = JavascriptArray::Is(pDestObj); } } + } if (pDestObj == nullptr || isArray) @@ -3489,13 +3559,15 @@ namespace Js { JavascriptNativeIntArray *pIntArray = isArray ? JavascriptNativeIntArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeIntArray(cDestLength); pIntArray->EnsureHead(); - pDestArray = ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext); + + JS_REENTRANT(jsReentLock, pDestArray = ConcatIntArgs(pIntArray, remoteTypeIds, args, scriptContext)); } else if (isFloat) { JavascriptNativeFloatArray *pFArray = isArray ? JavascriptNativeFloatArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateNativeFloatArray(cDestLength); pFArray->EnsureHead(); - pDestArray = ConcatFloatArgs(pFArray, remoteTypeIds, args, scriptContext); + + JS_REENTRANT(jsReentLock, pDestArray = ConcatFloatArgs(pFArray, remoteTypeIds, args, scriptContext)); } else { @@ -3503,7 +3575,8 @@ namespace Js pDestArray = isArray ? JavascriptArray::FromVar(pDestObj) : scriptContext->GetLibrary()->CreateArray(cDestLength); // if the constructor has changed then we no longer specialize for ints and floats pDestArray->EnsureHead(); - ConcatArgsCallingHelper(pDestArray, remoteTypeIds, args, scriptContext, destLengthOverflow); + + JS_REENTRANT(jsReentLock, ConcatArgsCallingHelper(pDestArray, remoteTypeIds, args, scriptContext, destLengthOverflow)); } // @@ -3516,8 +3589,9 @@ namespace Js return pDestArray; } + Assert(pDestObj); - ConcatArgsCallingHelper(pDestObj, remoteTypeIds, args, scriptContext, destLengthOverflow); + JS_REENTRANT(jsReentLock, ConcatArgsCallingHelper(pDestObj, remoteTypeIds, args, scriptContext, destLengthOverflow)); return pDestObj; } @@ -3568,81 +3642,6 @@ namespace Js } } - uint32 JavascriptArray::GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext) - { - uint32 fromIndex; - - if (TaggedInt::Is(arg)) - { - int intValue = TaggedInt::ToInt32(arg); - - if (intValue >= 0) - { - fromIndex = intValue; - } - else - { - // (intValue + length) may exceed 2^31 or may be < 0, so promote to int64 - fromIndex = (uint32)max(0i64, (int64)(length) + intValue); - } - } - else - { - double value = JavascriptConversion::ToInteger(arg, scriptContext); - if (value > length) - { - return (uint32)-1; - } - else if (value >= 0) - { - fromIndex = (uint32)value; - } - else - { - fromIndex = (uint32)max((double)0, value + length); - } - } - - return fromIndex; - } - - uint64 JavascriptArray::GetFromIndex(Var arg, uint64 length, ScriptContext *scriptContext) - { - uint64 fromIndex; - - if (TaggedInt::Is(arg)) - { - int64 intValue = TaggedInt::ToInt64(arg); - - if (intValue >= 0) - { - fromIndex = intValue; - } - else - { - fromIndex = max((int64)0, (int64)(intValue + length)); - } - } - else - { - double value = JavascriptConversion::ToInteger(arg, scriptContext); - if (value > length) - { - return (uint64)-1; - } - else if (value >= 0) - { - fromIndex = (uint64)value; - } - else - { - fromIndex = (uint64)max((double)0, value + length); - } - } - - return fromIndex; - } - int64 JavascriptArray::GetFromLastIndex(Var arg, int64 length, ScriptContext *scriptContext) { int64 fromIndex; @@ -3694,43 +3693,23 @@ namespace Js template Var JavascriptArray::IndexOfHelper(Arguments const & args, ScriptContext *scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + RecyclableObject* obj = nullptr; JavascriptArray* pArr = nullptr; BigIndex length; Var trueValue = scriptContext->GetLibrary()->GetTrue(); Var falseValue = scriptContext->GetLibrary()->GetFalse(); - if (JavascriptArray::Is(args[0])) - { -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(args[0]); -#endif - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.indexOf")); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.indexOf"), &pArr, &obj, &length)); if (pArr) { Var search; uint32 fromIndex; uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(); - if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotParam = GetParamForIndexOf(len, args, search, fromIndex, scriptContext)); + if (!gotParam) { return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1); } @@ -3757,14 +3736,14 @@ namespace Js switch (pArr->GetTypeId()) { case Js::TypeIds_Array: - return TemplatedIndexOfHelper(pArr, search, fromIndex, len, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(pArr, search, fromIndex, len, scriptContext)); case Js::TypeIds_NativeIntArray: - return TemplatedIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, len, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, len, scriptContext)); case Js::TypeIds_NativeFloatArray: - return TemplatedIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, len, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, len, scriptContext)); default: AssertMsg(FALSE, "invalid array typeid"); - return TemplatedIndexOfHelper(pArr, search, fromIndex, len, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(pArr, search, fromIndex, len, scriptContext)); } } @@ -3776,32 +3755,35 @@ namespace Js Var search; uint32 fromIndex; uint32 len = length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(); - if (!GetParamForIndexOf(len, args, search, fromIndex, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotParam = GetParamForIndexOf(len, args, search, fromIndex, scriptContext)); + if (!gotParam) { return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1); } - return TemplatedIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, length.GetSmallIndex(), scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, length.GetSmallIndex(), scriptContext)); } } if (length.IsSmallIndex()) { Var search; uint32 fromIndex; - if (!GetParamForIndexOf(length.GetSmallIndex(), args, search, fromIndex, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotParam = GetParamForIndexOf(length.GetSmallIndex(), args, search, fromIndex, scriptContext)); + if (!gotParam) { return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1); } - return TemplatedIndexOfHelper(obj, search, fromIndex, length.GetSmallIndex(), scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(obj, search, fromIndex, length.GetSmallIndex(), scriptContext)); } else { Var search; uint64 fromIndex; - if (!GetParamForIndexOf(length.GetBigIndex(), args, search, fromIndex, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotParam = GetParamForIndexOf(length.GetBigIndex(), args, search, fromIndex, scriptContext)); + if (!gotParam) { return includesAlgorithm ? falseValue : TaggedInt::ToVarUnchecked(-1); } - return TemplatedIndexOfHelper(obj, search, fromIndex, length.GetBigIndex(), scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return TemplatedIndexOfHelper(obj, search, fromIndex, length.GetBigIndex(), scriptContext)); } } @@ -3812,12 +3794,13 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_indexOf); - Var returnValue = IndexOfHelper(args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, Var returnValue = IndexOfHelper(args, scriptContext)); //IndexOfHelper code is reused for array.prototype.includes as well. Let us assert here we didn't get a true or false instead of index Assert(returnValue != scriptContext->GetLibrary()->GetTrue() && returnValue != scriptContext->GetLibrary()->GetFalse()); @@ -3831,12 +3814,13 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_includes); - Var returnValue = IndexOfHelper(args, scriptContext); + JS_REENTRANT(jsReentLock, Var returnValue = IndexOfHelper(args, scriptContext)); Assert(returnValue == scriptContext->GetLibrary()->GetTrue() || returnValue == scriptContext->GetLibrary()->GetFalse()); return returnValue; @@ -3891,13 +3875,14 @@ namespace Js && !VirtualTableInfo::HasVirtualTable(obj)); PropertyRecord const * propertyRecord; JavascriptOperators::GetPropertyIdForInt(index, scriptContext, &propertyRecord); + if (checkHasItem && !JavascriptOperators::HasProperty(obj, propertyRecord->GetPropertyId())) { return FALSE; } + *element = JavascriptOperators::GetProperty(obj, propertyRecord->GetPropertyId(), scriptContext); return *element != scriptContext->GetLibrary()->GetUndefined(); - } template <> @@ -4240,6 +4225,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -4255,7 +4241,7 @@ namespace Js //ES5 15.4.4.5 If separator is undefined, let separator be the single-character String ",". if (TypeIds_Undefined != typeId) { - separator = JavascriptConversion::ToString(args[1], scriptContext); + JS_REENTRANT(jsReentLock, separator = JavascriptConversion::ToString(args[1], scriptContext)); } else { @@ -4267,7 +4253,7 @@ namespace Js separator = scriptContext->GetLibrary()->GetCommaDisplayString(); } - return JoinHelper(args[0], separator, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JoinHelper(args[0], separator, scriptContext)); } JavascriptString* JavascriptArray::JoinToString(Var value, ScriptContext* scriptContext) @@ -4285,6 +4271,8 @@ namespace Js JavascriptString* JavascriptArray::JoinHelper(Var thisArg, JavascriptString* separator, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + bool isArray = JavascriptArray::Is(thisArg) && (scriptContext == JavascriptArray::FromVar(thisArg)->GetScriptContext()); bool isProxy = JavascriptProxy::Is(thisArg) && (scriptContext == JavascriptProxy::FromVar(thisArg)->GetScriptContext()); Var target = NULL; @@ -4334,24 +4322,24 @@ namespace Js switch (arr->GetTypeId()) { case Js::TypeIds_Array: - res = JoinArrayHelper(arr, separator, scriptContext); + JS_REENTRANT(jsReentLock, res = JoinArrayHelper(arr, separator, scriptContext)); break; case Js::TypeIds_NativeIntArray: - res = JoinArrayHelper(JavascriptNativeIntArray::FromVar(arr), separator, scriptContext); + JS_REENTRANT(jsReentLock, res = JoinArrayHelper(JavascriptNativeIntArray::FromVar(arr), separator, scriptContext)); break; case Js::TypeIds_NativeFloatArray: - res = JoinArrayHelper(JavascriptNativeFloatArray::FromVar(arr), separator, scriptContext); + JS_REENTRANT(jsReentLock, res = JoinArrayHelper(JavascriptNativeFloatArray::FromVar(arr), separator, scriptContext)); break; } } else if (RecyclableObject::Is(thisArg)) { - res = JoinOtherHelper(RecyclableObject::FromVar(thisArg), separator, scriptContext); + JS_REENTRANT(jsReentLock, res = JoinOtherHelper(RecyclableObject::FromVar(thisArg), separator, scriptContext)); } else { - res = JoinOtherHelper(scriptContext->GetLibrary()->CreateNumberObject(thisArg), separator, scriptContext); + JS_REENTRANT(jsReentLock, res = JoinOtherHelper(scriptContext->GetLibrary()->CreateNumberObject(thisArg), separator, scriptContext)); } }, [&](bool/*hasException*/) @@ -4380,6 +4368,8 @@ namespace Js template JavascriptString* JavascriptArray::JoinArrayHelper(T * arr, JavascriptString* separator, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + Assert(VirtualTableInfo::HasVirtualTable(arr) || VirtualTableInfo>::HasVirtualTable(arr)); const uint32 arrLength = arr->length; switch(arrLength) @@ -4395,10 +4385,13 @@ namespace Js CompoundString *const cs = CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary()); Var item; - if (TemplatedGetItem(arr, 0u, &item, scriptContext)) + BOOL gotItem; + JS_REENTRANT(jsReentLock, gotItem = TemplatedGetItem(arr, 0u, &item, scriptContext)); + if (gotItem) { - cs->Append(JavascriptArray::JoinToString(item, scriptContext)); + JS_REENTRANT(jsReentLock, cs->Append(JavascriptArray::JoinToString(item, scriptContext))); } + for (uint32 i = 1; i < arrLength; i++) { if (hasSeparator) @@ -4406,9 +4399,10 @@ namespace Js cs->Append(separator); } - if (TryTemplatedGetItem(arr, i, &item, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = TryTemplatedGetItem(arr, i, &item, scriptContext)); + if (gotItem) { - cs->Append(JavascriptArray::JoinToString(item, scriptContext)); + JS_REENTRANT(jsReentLock, cs->Append(JavascriptArray::JoinToString(item, scriptContext))); } } return cs; @@ -4417,6 +4411,7 @@ namespace Js case 2: { bool hasSeparator = (separator->GetLength() != 0); + BOOL gotItem; if(hasSeparator) { goto CaseDefault; @@ -4425,14 +4420,15 @@ namespace Js JavascriptString *res = nullptr; Var item; - if (TemplatedGetItem(arr, 0u, &item, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = TemplatedGetItem(arr, 0u, &item, scriptContext)); + if (gotItem) { - res = JavascriptArray::JoinToString(item, scriptContext); + JS_REENTRANT(jsReentLock, res = JavascriptArray::JoinToString(item, scriptContext)); } - - if (TryTemplatedGetItem(arr, 1u, &item, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = TryTemplatedGetItem(arr, 1u, &item, scriptContext)); + if (gotItem) { - JavascriptString *const itemString = JavascriptArray::JoinToString(item, scriptContext); + JS_REENTRANT(jsReentLock, JavascriptString *const itemString = JavascriptArray::JoinToString(item, scriptContext)); return res ? ConcatString::New(res, itemString) : itemString; } @@ -4447,9 +4443,11 @@ namespace Js case 1: { Var item; - if (TemplatedGetItem(arr, 0u, &item, scriptContext)) + BOOL gotItem; + JS_REENTRANT(jsReentLock, gotItem = TemplatedGetItem(arr, 0u, &item, scriptContext)); + if (gotItem) { - return JavascriptArray::JoinToString(item, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::JoinToString(item, scriptContext)); } // fall through } @@ -4462,11 +4460,13 @@ namespace Js JavascriptString* JavascriptArray::JoinOtherHelper(RecyclableObject* object, JavascriptString* separator, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + // In ES6-mode, we always load the length property from the object instead of using the internal slot. // Even for arrays, this is now observable via proxies. // If source object is not an array, we fall back to this behavior anyway. - Var lenValue = JavascriptOperators::OP_GetLength(object, scriptContext); - int64 cSrcLength = JavascriptConversion::ToLength(lenValue, scriptContext); + JS_REENTRANT(jsReentLock, + int64 cSrcLength = (int64)OP_GetLength(object, scriptContext)); switch (cSrcLength) { @@ -4474,6 +4474,7 @@ namespace Js { CaseDefault: bool hasSeparator = (separator->GetLength() != 0); + BOOL gotItem; const charcount_t estimatedAppendCount = min( Join_MaxEstimatedAppendCount, @@ -4481,9 +4482,10 @@ namespace Js CompoundString *const cs = CompoundString::NewWithPointerCapacity(estimatedAppendCount, scriptContext->GetLibrary()); Var value; - if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = JavascriptOperators::GetItem(object, 0u, &value, scriptContext)); + if (gotItem) { - cs->Append(JavascriptArray::JoinToString(value, scriptContext)); + JS_REENTRANT(jsReentLock, cs->Append(JavascriptArray::JoinToString(value, scriptContext))); } for (uint32 i = 1; i < cSrcLength; i++) { @@ -4491,9 +4493,10 @@ namespace Js { cs->Append(separator); } - if (JavascriptOperators::GetItem(object, i, &value, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = JavascriptOperators::GetItem(object, i, &value, scriptContext)); + if (gotItem) { - cs->Append(JavascriptArray::JoinToString(value, scriptContext)); + JS_REENTRANT(jsReentLock, cs->Append(JavascriptArray::JoinToString(value, scriptContext))); } } return cs; @@ -4502,6 +4505,7 @@ namespace Js case 2: { bool hasSeparator = (separator->GetLength() != 0); + BOOL gotItem; if(hasSeparator) { goto CaseDefault; @@ -4509,13 +4513,15 @@ namespace Js JavascriptString *res = nullptr; Var value; - if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = JavascriptOperators::GetItem(object, 0u, &value, scriptContext)); + if (gotItem) { - res = JavascriptArray::JoinToString(value, scriptContext); + JS_REENTRANT(jsReentLock, res = JavascriptArray::JoinToString(value, scriptContext)); } - if (JavascriptOperators::GetItem(object, 1u, &value, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = JavascriptOperators::GetItem(object, 1u, &value, scriptContext)); + if (gotItem) { - JavascriptString *const valueString = JavascriptArray::JoinToString(value, scriptContext); + JS_REENTRANT(jsReentLock, JavascriptString *const valueString = JavascriptArray::JoinToString(value, scriptContext)); return res ? ConcatString::New(res, valueString) : valueString; } if(res) @@ -4528,9 +4534,10 @@ namespace Js case 1: { Var value; - if (JavascriptOperators::GetItem(object, 0u, &value, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetItem(object, 0u, &value, scriptContext)); + if (gotItem) { - return JavascriptArray::JoinToString(value, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::JoinToString(value, scriptContext)); } // fall through } @@ -4547,6 +4554,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_lastIndexOf); @@ -4556,28 +4564,13 @@ namespace Js JavascriptArray * pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0])) - { -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(args[0]); -#endif - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.lastIndexOf")); - } - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - length = JavascriptConversion::ToLength(lenValue, scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.lastIndexOf"), &pArr, &obj, &length)); Var search; int64 fromIndex; - if (!GetParamForLastIndexOf(length, args, search, fromIndex, scriptContext)) + JS_REENTRANT(jsReentLock, + BOOL gotParam = GetParamForLastIndexOf(length, args, search, fromIndex, scriptContext)); + if (!gotParam) { return TaggedInt::ToVarUnchecked(-1); } @@ -4587,24 +4580,24 @@ namespace Js switch (pArr->GetTypeId()) { case Js::TypeIds_Array: - return LastIndexOfHelper(pArr, search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(pArr, search, fromIndex, scriptContext)); case Js::TypeIds_NativeIntArray: - return LastIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(JavascriptNativeIntArray::FromVar(pArr), search, fromIndex, scriptContext)); case Js::TypeIds_NativeFloatArray: - return LastIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(JavascriptNativeFloatArray::FromVar(pArr), search, fromIndex, scriptContext)); default: AssertMsg(FALSE, "invalid array typeid"); - return LastIndexOfHelper(pArr, search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(pArr, search, fromIndex, scriptContext)); } } // source object is not a JavascriptArray but source could be a TypedArray if (TypedArrayBase::Is(obj)) { - return LastIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(TypedArrayBase::FromVar(obj), search, fromIndex, scriptContext)); } - return LastIndexOfHelper(obj, search, fromIndex, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return LastIndexOfHelper(obj, search, fromIndex, scriptContext)); } // Array.prototype.lastIndexOf as described in ES6.0 (draft 22) Section 22.1.3.14 @@ -4788,6 +4781,7 @@ namespace Js Var JavascriptArray::EntryPopJavascriptArray(ScriptContext * scriptContext, Var object) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); JavascriptArray * arr = JavascriptArray::FromVar(object); uint32 length = arr->length; @@ -4799,8 +4793,9 @@ namespace Js uint32 index = length - 1; Var element; + JS_REENTRANT(jsReentLock, BOOL gotItem = arr->DirectGetItemAtFull(index, &element)); - if (!arr->DirectGetItemAtFull(index, &element)) + if (!gotItem) { element = scriptContext->GetLibrary()->GetUndefined(); } @@ -4818,27 +4813,20 @@ namespace Js Var JavascriptArray::EntryPopNonJavascriptArray(ScriptContext * scriptContext, Var object) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); RecyclableObject* dynamicObject = nullptr; if (FALSE == JavascriptConversion::ToObject(object, scriptContext, &dynamicObject)) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.pop")); } - BigIndex length; - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64)JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - + JS_REENTRANT(jsReentLock, BigIndex length = OP_GetLength(dynamicObject, scriptContext)); ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.pop")); if (length == 0u) { // Set length = 0 - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible))); return scriptContext->GetLibrary()->GetUndefined(); } BigIndex index = length; @@ -4846,25 +4834,26 @@ namespace Js Var element; if (index.IsSmallIndex()) { - if (!JavascriptOperators::GetItem(dynamicObject, index.GetSmallIndex(), &element, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetItem(dynamicObject, index.GetSmallIndex(), &element, scriptContext)); + if (!gotItem) { element = scriptContext->GetLibrary()->GetUndefined(); } - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetSmallIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)); - - // Set the new length - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetSmallIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)), + // Set the new length + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))); } else { - if (!JavascriptOperators::GetItem(dynamicObject, index.GetBigIndex(), &element, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetItem(dynamicObject, index.GetBigIndex(), &element, scriptContext)); + if (!gotItem) { element = scriptContext->GetLibrary()->GetUndefined(); } - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetBigIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)); - - // Set the new length - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, index.GetBigIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)), + // Set the new length + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(index.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))); } return element; } @@ -4875,6 +4864,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -4885,11 +4875,12 @@ namespace Js if (JavascriptArray::Is(args[0])) { - return EntryPopJavascriptArray(scriptContext, args.Values[0]); + JS_REENTRANT_UNLOCK(jsReentLock, return EntryPopJavascriptArray(scriptContext, args.Values[0])); } else { - return EntryPopNonJavascriptArray(scriptContext, args.Values[0]); + + JS_REENTRANT_UNLOCK(jsReentLock, return EntryPopNonJavascriptArray(scriptContext, args.Values[0])); } } @@ -4978,36 +4969,61 @@ namespace Js */ Var JavascriptArray::EntryPushNonJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount) { - RecyclableObject* obj = nullptr; - if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push")); - } + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); - Var length = JavascriptOperators::OP_GetLength(obj, scriptContext); - if(JavascriptOperators::GetTypeId(length) == TypeIds_Undefined && scriptContext->GetThreadContext()->IsDisableImplicitCall() && - scriptContext->GetThreadContext()->GetImplicitCallFlags() != Js::ImplicitCall_None) - { - return length; - } + RecyclableObject* obj = nullptr; + if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &obj)) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.push")); + } - ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push")); - BigIndex n; - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - n = (uint64) JavascriptConversion::ToLength(length, scriptContext); - } - else + JS_REENTRANT_UNLOCK(jsReentLock, Var length = JavascriptOperators::OP_GetLength(obj, scriptContext)); + if(JavascriptOperators::GetTypeId(length) == TypeIds_Undefined && scriptContext->GetThreadContext()->IsDisableImplicitCall() && + scriptContext->GetThreadContext()->GetImplicitCallFlags() != Js::ImplicitCall_None) + { + return length; + } + + ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push")); + BigIndex n; + if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) + { + n = (uint64) JavascriptConversion::ToLength(length, scriptContext); + } + else + { + n = JavascriptConversion::ToUInt32(length, scriptContext); + } + // First handle "small" indices. + uint index; + for (index=1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n) + { + JS_REENTRANT(jsReentLock, + BOOL setItem = JavascriptOperators::SetItem(obj, obj, n.GetSmallIndex(), args[index], scriptContext, PropertyOperation_ThrowIfNotExtensible)); + if (h.IsThrowTypeError(setItem)) { - n = JavascriptConversion::ToUInt32(length, scriptContext); + if (scriptContext->GetThreadContext()->RecordImplicitException()) + { + h.ThrowTypeErrorOnFailure(); + } + else + { + return nullptr; + } } - // First handle "small" indices. - uint index; - for (index=1; index < argCount && n < JavascriptArray::MaxArrayLength; ++index, ++n) + } + + // Use BigIndex if we need to push indices >= MaxArrayLength + if (index < argCount) + { + BigIndex big = n; + + for (; index < argCount; ++index, ++big) { - if (h.IsThrowTypeError(JavascriptOperators::SetItem(obj, obj, n.GetSmallIndex(), args[index], scriptContext, PropertyOperation_ThrowIfNotExtensible))) + JS_REENTRANT(jsReentLock, BOOL setItem = big.SetItem(obj, args[index], PropertyOperation_ThrowIfNotExtensible)); + if (h.IsThrowTypeError(setItem)) { - if (scriptContext->GetThreadContext()->RecordImplicitException()) + if(scriptContext->GetThreadContext()->RecordImplicitException()) { h.ThrowTypeErrorOnFailure(); } @@ -5016,62 +5032,46 @@ namespace Js return nullptr; } } + } - // Use BigIndex if we need to push indices >= MaxArrayLength - if (index < argCount) + // Set the new length; for objects it is all right for this to be >= MaxArrayLength + JS_REENTRANT(jsReentLock, + BOOL setLength = JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, big.ToNumber(scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + if (h.IsThrowTypeError(setLength)) { - BigIndex big = n; - - for (; index < argCount; ++index, ++big) + if(scriptContext->GetThreadContext()->RecordImplicitException()) { - if (h.IsThrowTypeError(big.SetItem(obj, args[index], PropertyOperation_ThrowIfNotExtensible))) - { - if(scriptContext->GetThreadContext()->RecordImplicitException()) - { - h.ThrowTypeErrorOnFailure(); - } - else - { - return nullptr; - } - } - + h.ThrowTypeErrorOnFailure(); } - - // Set the new length; for objects it is all right for this to be >= MaxArrayLength - if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, big.ToNumber(scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))) + else { - if(scriptContext->GetThreadContext()->RecordImplicitException()) - { - h.ThrowTypeErrorOnFailure(); - } - else - { - return nullptr; - } + return nullptr; } - - return big.ToNumber(scriptContext); } - else + + return big.ToNumber(scriptContext); + } + else + { + // Set the new length + Var lengthAsNUmberVar = JavascriptNumber::ToVar(n.IsSmallIndex() ? n.GetSmallIndex() : n.GetBigIndex(), scriptContext); + JS_REENTRANT(jsReentLock, + BOOL setLength = JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, lengthAsNUmberVar, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + if (h.IsThrowTypeError(setLength)) { - // Set the new length - Var lengthAsNUmberVar = JavascriptNumber::ToVar(n.IsSmallIndex() ? n.GetSmallIndex() : n.GetBigIndex(), scriptContext); - if (h.IsThrowTypeError(JavascriptOperators::SetProperty(obj, obj, PropertyIds::length, lengthAsNUmberVar, scriptContext, PropertyOperation_ThrowIfNotExtensible))) + if(scriptContext->GetThreadContext()->RecordImplicitException()) { - if(scriptContext->GetThreadContext()->RecordImplicitException()) - { - h.ThrowTypeErrorOnFailure(); - } - else - { - return nullptr; - } + h.ThrowTypeErrorOnFailure(); + } + else + { + return nullptr; } - - return lengthAsNUmberVar; } + + return lengthAsNUmberVar; + } } /* @@ -5111,6 +5111,7 @@ namespace Js Var JavascriptArray::EntryPushJavascriptArrayNoFastPath(ScriptContext * scriptContext, Var * args, uint argCount) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); JavascriptArray * arr = JavascriptArray::FromAnyArray(args[0]); uint n = arr->length; ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.push")); @@ -5131,7 +5132,7 @@ namespace Js Assert(n == JavascriptArray::MaxArrayLength); for (BigIndex big = n; index < argCount; ++index, ++big) { - h.ThrowTypeErrorOnFailure(big.SetItem(arr, args[index])); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(big.SetItem(arr, args[index]))); } #ifdef VALIDATE_ARRAY @@ -5157,7 +5158,6 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); - Assert(!(callInfo.Flags & CallFlags_New)); if (args.Info.Count == 0) @@ -5182,7 +5182,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); - + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -5195,44 +5195,23 @@ namespace Js JavascriptArray* pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0])) - { - pArr = JavascriptArray::FromVar(args[0]); -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(pArr); -#endif - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reverse")); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.reverse"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetSmallIndex(), scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetSmallIndex(), 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 - return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetBigIndex(), scriptContext); + + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReverseHelper(pArr, nullptr, obj, length.GetBigIndex(), scriptContext)); } // Array.prototype.reverse as described in ES6.0 (draft 22) Section 22.1.3.20 template Var JavascriptArray::ReverseHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + T middle = length / 2; Var lowerValue = nullptr, upperValue = nullptr; T lowerExists, upperExists; @@ -5271,13 +5250,14 @@ namespace Js // so we cannot fill it from the prototypes. if (length % 2 == 0) { - pArr->FillFromPrototypes(0, (uint32)length); + JS_REENTRANT(jsReentLock, pArr->FillFromPrototypes(0, (uint32)length)); } else { middle = length / 2; - pArr->FillFromPrototypes(0, (uint32)middle); - pArr->FillFromPrototypes(1 + (uint32)middle, (uint32)length); + JS_REENTRANT(jsReentLock, + pArr->FillFromPrototypes(0, (uint32)middle), + pArr->FillFromPrototypes(1 + (uint32)middle, (uint32)length)); } } @@ -5397,8 +5377,9 @@ namespace Js lowerExists = typedArrayBase->HasItem(lower); upperExists = typedArrayBase->HasItem(upper); - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue)); - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue)), + h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue))); } } else @@ -5417,21 +5398,22 @@ namespace Js { if (upperExists) { - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue)); - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue)), + h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue))); } else { // This will always fail for a TypedArray if lower < length h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(lower, PropertyOperation_ThrowOnDeleteIfNotConfig)); - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(upper, lowerValue))); } } else { if (upperExists) { - h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(typedArrayBase->DirectSetItem(lower, upperValue))); // This will always fail for a TypedArray if upper < length h.ThrowTypeErrorOnFailure(typedArrayBase->DeleteItem(upper, PropertyOperation_ThrowOnDeleteIfNotConfig)); } @@ -5445,31 +5427,32 @@ namespace Js { T upper = length - lower - 1; - lowerExists = JavascriptOperators::HasItem(obj, lower) && - JavascriptOperators::GetItem(obj, lower, &lowerValue, scriptContext); - - upperExists = JavascriptOperators::HasItem(obj, upper) && - JavascriptOperators::GetItem(obj, upper, &upperValue, scriptContext); + JS_REENTRANT(jsReentLock, + lowerExists = JavascriptOperators::HasItem(obj, lower) && JavascriptOperators::GetItem(obj, lower, &lowerValue, scriptContext), + upperExists = JavascriptOperators::HasItem(obj, upper) && JavascriptOperators::GetItem(obj, upper, &upperValue, scriptContext)); if (lowerExists) { if (upperExists) { - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible))); } else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, lower, PropertyOperation_ThrowOnDeleteIfNotConfig)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, lower, PropertyOperation_ThrowOnDeleteIfNotConfig)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, upper, lowerValue, scriptContext, PropertyOperation_ThrowIfNotExtensible))); } } else { if (upperExists) { - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, upper, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(obj, obj, lower, upperValue, scriptContext, PropertyOperation_ThrowIfNotExtensible)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(obj, upper, PropertyOperation_ThrowOnDeleteIfNotConfig))); } } } @@ -5547,6 +5530,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -5570,7 +5554,7 @@ namespace Js if(pArr->IsFillFromPrototypes()) { - pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object + JS_REENTRANT(jsReentLock, pArr->FillFromPrototypes(0, pArr->length)); // We need find all missing value from [[proto]] object } if(pArr->HasNoMissingValues() && pArr->head && pArr->head->next) @@ -5666,23 +5650,19 @@ namespace Js ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.shift")); - BigIndex length = 0u; - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, BigIndex length = OP_GetLength(dynamicObject, scriptContext)); if (length == 0u) { // If length is 0, return 'undefined' - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, TaggedInt::ToVarUnchecked(0), scriptContext, PropertyOperation_ThrowIfNotExtensible))); return scriptContext->GetLibrary()->GetUndefined(); } - if (!JavascriptOperators::GetItem(dynamicObject, 0u, &res, scriptContext)) + + JS_REENTRANT(jsReentLock, + BOOL gotItem = JavascriptOperators::GetItem(dynamicObject, 0u, &res, scriptContext)); + if (!gotItem) { res = scriptContext->GetLibrary()->GetUndefined(); } @@ -5690,39 +5670,47 @@ namespace Js uint32 lengthToUin32Max = length.IsSmallIndex() ? length.GetSmallIndex() : MaxArrayLength; for (uint32 i = 0u; i < lengthToUin32Max; i++) { - if (JavascriptOperators::HasItem(dynamicObject, i + 1)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(dynamicObject, i + 1)); + if (hasItem) { - Var element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible, /*skipPrototypeCheck*/ true)); + Var element = nullptr; + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible, /*skipPrototypeCheck*/ true))); } else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowOnDeleteIfNotConfig))); } } for (uint64 i = MaxArrayLength; length > i; i++) { - if (JavascriptOperators::HasItem(dynamicObject, i + 1)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(dynamicObject, i + 1)); + if (hasItem) { - Var element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + Var element = nullptr; + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(dynamicObject, i + 1, scriptContext), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible))); } else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, i, PropertyOperation_ThrowOnDeleteIfNotConfig))); } } if (length.IsSmallIndex()) { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetSmallIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetSmallIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetSmallIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))); } else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetBigIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(dynamicObject, length.GetBigIndex(), PropertyOperation_ThrowOnDeleteIfNotConfig)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, JavascriptNumber::ToVar(length.GetBigIndex(), scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))); } } return res; @@ -5761,6 +5749,8 @@ namespace Js template void JavascriptArray::SliceHelper(JavascriptArray* pArr, JavascriptArray* pnewArr, uint32 start, uint32 newLen) { + JS_REENTRANCY_LOCK(jsReentLock, pArr->GetScriptContext()->GetThreadContext()); + SparseArraySegment* headSeg = (SparseArraySegment*)pArr->head; SparseArraySegment* pnewHeadSeg = (SparseArraySegment*)pnewArr->head; @@ -5780,9 +5770,10 @@ namespace Js { Var element; pnewArr->SetHasNoMissingValues(false); - if (pArr->DirectGetItemAtFull(i + start, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(i + start, &element)); + if (gotItem) { - pnewArr->SetItem(i, element, PropertyOperation_None); + JS_REENTRANT(jsReentLock, pnewArr->SetItem(i, element, PropertyOperation_None)); } } } @@ -5850,6 +5841,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -5864,42 +5856,23 @@ namespace Js JavascriptArray* pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.slice")); - } - } - - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(lenValue, scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(lenValue, scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.slice"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext)); } + Assert(pArr == nullptr || length.IsUint32Max()); - return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::SliceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } // Array.prototype.slice as described in ES6.0 (draft 22) Section 22.1.3.22 template Var JavascriptArray::SliceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { - JavascriptLibrary* library = scriptContext->GetLibrary(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + JavascriptArray* newArr = nullptr; RecyclableObject* newObj = nullptr; bool isIntArray = false; @@ -5915,28 +5888,11 @@ namespace Js #endif if (args.Info.Count > 1) { - startT = GetFromIndex(args[1], length, scriptContext); + JS_REENTRANT(jsReentLock, startT = GetFromIndex(args[1], length, scriptContext)); - if (startT > length) + if (args.Info.Count > 2 && JavascriptOperators::GetTypeId(args[2]) != TypeIds_Undefined) { - startT = length; - } - - if (args.Info.Count > 2) - { - if (JavascriptOperators::GetTypeId(args[2]) == TypeIds_Undefined) - { - endT = length; - } - else - { - endT = GetFromIndex(args[2], length, scriptContext); - - if (endT > length) - { - endT = length; - } - } + JS_REENTRANT(jsReentLock, endT = GetFromIndex(args[2], length, scriptContext)); } newLenT = endT > startT ? endT - startT : 0; @@ -5957,52 +5913,27 @@ namespace Js // and use it to construct the return object. if (isTypedArrayEntryPoint) { - Var constructor = JavascriptOperators::SpeciesConstructor(typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext); - isBuiltinArrayCtor = (constructor == library->GetArrayConstructor()); - - // If we have an array source object, we need to make sure to do the right thing if it's a native array. - // The helpers below which do the element copying require the source and destination arrays to have the same native type. - if (pArr && isBuiltinArrayCtor) - { - if (newLenT > JavascriptArray::MaxArrayLength) - { - JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect); - } + JS_REENTRANT(jsReentLock, + Var constructor = JavascriptOperators::SpeciesConstructor(typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext)); + isBuiltinArrayCtor = false; - // If the constructor function is the built-in Array constructor, we can be smart and create the right type of native array. - pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray); - newArr = CreateNewArrayHelper(static_cast(newLenT), isIntArray, isFloatArray, pArr, scriptContext); - newObj = newArr; - } - else if (JavascriptOperators::IsConstructor(constructor)) - { - if (pArr) - { - // If the constructor function is any other function, it can return anything so we have to call it. - // Roll the source array into a non-native array if it was one. - pArr = EnsureNonNativeArray(pArr); - } + AssertAndFailFast(pArr == nullptr); + Assert(JavascriptOperators::IsConstructor(constructor)); - Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newLenT, scriptContext) }; - Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); - newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)newLenT, scriptContext)); - } - else - { - // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.slice was called - JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidTypedArray_Constructor, _u("[TypedArray].prototype.slice")); - } + Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newLenT, scriptContext) }; + Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); + JS_REENTRANT(jsReentLock, newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)newLenT, scriptContext))); } else if (pArr != nullptr) { - newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray, &isBuiltinArrayCtor); + JS_REENTRANT(jsReentLock, newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray, &isBuiltinArrayCtor)); } // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array. else { - newObj = ArraySpeciesCreate(obj, newLenT, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor); + JS_REENTRANT(jsReentLock, newObj = ArraySpeciesCreate(obj, newLenT, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor)); } // If we didn't create a new object above we will create a new array here. @@ -6060,30 +5991,30 @@ namespace Js { if (isIntArray) { - SliceHelper(pArr, newArr, start, newLen); + JS_REENTRANT(jsReentLock, SliceHelper(pArr, newArr, start, newLen)); } else if (isFloatArray) { - SliceHelper(pArr, newArr, start, newLen); + JS_REENTRANT(jsReentLock, SliceHelper(pArr, newArr, start, newLen)); } else { - SliceHelper(pArr, newArr, start, newLen); + JS_REENTRANT(jsReentLock, SliceHelper(pArr, newArr, start, newLen)); } } else { if (isIntArray) { - CopyNativeIntArrayElements(JavascriptNativeIntArray::FromVar(newArr), 0, JavascriptNativeIntArray::FromVar(pArr), start, start + newLen); + JS_REENTRANT(jsReentLock, CopyNativeIntArrayElements(JavascriptNativeIntArray::FromVar(newArr), 0, JavascriptNativeIntArray::FromVar(pArr), start, start + newLen)); } else if (isFloatArray) { - CopyNativeFloatArrayElements(JavascriptNativeFloatArray::FromVar(newArr), 0, JavascriptNativeFloatArray::FromVar(pArr), start, start + newLen); + JS_REENTRANT(jsReentLock, CopyNativeFloatArrayElements(JavascriptNativeFloatArray::FromVar(newArr), 0, JavascriptNativeFloatArray::FromVar(pArr), start, start + newLen)); } else { - CopyArrayElements(newArr, 0u, pArr, start, start + newLen); + JS_REENTRANT(jsReentLock, CopyArrayElements(newArr, 0u, pArr, start, start + newLen)); } } } @@ -6093,7 +6024,8 @@ namespace Js Var element; for (uint32 i = 0; i < newLen; i++) { - if (!pArr->DirectGetItemAtFull(i + start, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(i + start, &element)); + if (!gotItem) { continue; } @@ -6109,17 +6041,20 @@ namespace Js for (uint32 i = 0; i < newLen; i++) { - if (!pArr->DirectGetItemAtFull(i + start, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(i + start, &element)); + if (!gotItem) { continue; } - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i)); } } } else if (typedArrayBase) { + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + // Source is a TypedArray, we must have created the return object via a call to constructor, but newObj may not be a TypedArray (or an array either) TypedArrayBase* newTypedArray = nullptr; @@ -6127,6 +6062,10 @@ namespace Js { newTypedArray = TypedArrayBase::FromVar(newObj); } + else + { + AssertAndFailFast(newArr != nullptr); + } Var element; @@ -6143,15 +6082,11 @@ namespace Js // The object we got back from the constructor might not be a TypedArray. In fact, it could be any object. if (newTypedArray) { - newTypedArray->DirectSetItem(i, element); - } - else if (newArr) - { - newArr->DirectSetItemAt(i, element); + JS_REENTRANT(jsReentLock, newTypedArray->DirectSetItem(i, element)); } else { - JavascriptOperators::OP_SetElementI_UInt32(newObj, i, element, scriptContext, PropertyOperation_ThrowIfNotExtensible); + newArr->SetItem(i, element, PropertyOperation_None); } } } @@ -6159,16 +6094,17 @@ namespace Js { for (uint32 i = 0; i < newLen; i++) { - if (JavascriptOperators::HasItem(obj, i + start)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, i + start)); + if (hasItem) { - Var element = JavascriptOperators::GetItem(obj, i + start, scriptContext); + JS_REENTRANT(jsReentLock, Var element = JavascriptOperators::GetItem(obj, i + start, scriptContext)); if (newArr != nullptr) { newArr->SetItem(i, element, PropertyOperation_None); } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i)); } } } @@ -6176,7 +6112,8 @@ namespace Js if (!isTypedArrayEntryPoint) { - JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, + JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); } #ifdef VALIDATE_ARRAY @@ -6207,6 +6144,7 @@ namespace Js if (compFn != nullptr) { ScriptContext* scriptContext = compFn->GetScriptContext(); + // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes CallFlags flags = CallFlags_Value; Var undefined = scriptContext->GetLibrary()->GetUndefined(); @@ -6215,11 +6153,11 @@ namespace Js { Var leftVar = CrossSite::MarshalVar(scriptContext, *(Var*)aRef); Var rightVar = CrossSite::MarshalVar(scriptContext, *(Var*)bRef); - retVal = CALL_FUNCTION(compFn, CallInfo(flags, 3), undefined, leftVar, rightVar); + retVal = CALL_FUNCTION(scriptContext->GetThreadContext(), compFn, CallInfo(flags, 3), undefined, leftVar, rightVar); } else { - retVal = CALL_FUNCTION(compFn, CallInfo(flags, 3), undefined, *(Var*)aRef, *(Var*)bRef); + retVal = CALL_FUNCTION(scriptContext->GetThreadContext(), compFn, CallInfo(flags, 3), undefined, *(Var*)aRef, *(Var*)bRef); } if (TaggedInt::Is(retVal)) @@ -6289,6 +6227,8 @@ namespace Js void JavascriptArray::Sort(RecyclableObject* compFn) { + JS_REENTRANCY_LOCK(jsReentLock, this->GetScriptContext()->GetThreadContext()); + if (length <= 1) { return; @@ -6330,11 +6270,11 @@ namespace Js #ifdef VALIDATE_ARRAY ValidateSegment(startSeg); #endif - hybridSort(startSeg->elements, startSeg->length, &cvInfo); + JS_REENTRANT(jsReentLock, hybridSort(startSeg->elements, startSeg->length, &cvInfo)); } else { - countUndefined = sort(startSeg->elements, &startSeg->length, scriptContext); + JS_REENTRANT(jsReentLock, countUndefined = sort(startSeg->elements, &startSeg->length, scriptContext)); } head = startSeg; } @@ -6362,11 +6302,11 @@ namespace Js if (compFn != nullptr) { - hybridSort(allElements->elements, allElements->length, &cvInfo); + JS_REENTRANT(jsReentLock, hybridSort(allElements->elements, allElements->length, &cvInfo)); } else { - sort(allElements->elements, &allElements->length, scriptContext); + JS_REENTRANT(jsReentLock, sort(allElements->elements, &allElements->length, scriptContext)); } head = allElements; @@ -6495,6 +6435,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.sort")); Assert(!(callInfo.Flags & CallFlags_New)); @@ -6537,7 +6478,7 @@ namespace Js if(arr->IsFillFromPrototypes()) { - arr->FillFromPrototypes(0, arr->length); // We need find all missing value from [[proto]] object + JS_REENTRANT(jsReentLock, arr->FillFromPrototypes(0, arr->length)); // We need find all missing value from [[proto]] object } // Maintain nativity of the array only for the following cases (To favor inplace conversions - keeps the conversion cost less): @@ -6549,26 +6490,26 @@ namespace Js if(compFn && JavascriptNativeFloatArray::Is(arr)) { arr = JavascriptNativeFloatArray::ConvertToVarArray((JavascriptNativeFloatArray*)arr); - arr->Sort(compFn); + JS_REENTRANT(jsReentLock, arr->Sort(compFn)); arr = arr->ConvertToNativeArrayInPlace(arr); } else { EnsureNonNativeArray(arr); - arr->Sort(compFn); + JS_REENTRANT(jsReentLock, arr->Sort(compFn)); } #else if(compFn && JavascriptNativeIntArray::Is(arr)) { //EnsureNonNativeArray(arr); arr = JavascriptNativeIntArray::ConvertToVarArray((JavascriptNativeIntArray*)arr); - arr->Sort(compFn); + JS_REENTRANT(jsReentLock, arr->Sort(compFn)); arr = arr->ConvertToNativeArrayInPlace(arr); } else { EnsureNonNativeArray(arr); - arr->Sort(compFn); + JS_REENTRANT(jsReentLock, arr->Sort(compFn)); } #endif @@ -6580,7 +6521,8 @@ namespace Js { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.sort")); } - uint32 len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext); + JS_REENTRANT(jsReentLock, + uint32 len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext)); JavascriptArray* sortArray = scriptContext->GetLibrary()->CreateArray(len); sortArray->EnsureHead(); ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.sort")); @@ -6592,7 +6534,8 @@ namespace Js for (uint32 i = 0; i < len; i++) { Var item; - if (JavascriptOperators::GetItem(pObj, i, &item, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetItem(pObj, i, &item, scriptContext)); + if (gotItem) { indexList->Add(i); sortArray->DirectSetItemAt(i, item); @@ -6604,20 +6547,20 @@ namespace Js { sortArray->FillFromPrototypes(0, sortArray->length); // We need find all missing value from [[proto]] object } - sortArray->Sort(compFn); + JS_REENTRANT(jsReentLock, sortArray->Sort(compFn)); uint32 removeIndex = sortArray->head->length; for (uint32 i = 0; i < removeIndex; i++) { AssertMsg(!SparseArraySegment::IsMissingItem(&((SparseArraySegment*)sortArray->head)->elements[i]), "No gaps expected in sorted array"); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, i, ((SparseArraySegment*)sortArray->head)->elements[i], scriptContext)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, i, ((SparseArraySegment*)sortArray->head)->elements[i], scriptContext))); } for (int i = 0; i < indexList->Count(); i++) { uint32 value = indexList->Item(i); if (value >= removeIndex) { - h.ThrowTypeErrorOnFailure((JavascriptOperators::DeleteItem(pObj, value))); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure((JavascriptOperators::DeleteItem(pObj, value)))); } } } @@ -6634,312 +6577,59 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); - Recycler *recycler = scriptContext->GetRecycler(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); AssertMsg(args.Info.Count >= 1, "Should have at least one argument"); - bool isArr = false; JavascriptArray* pArr = 0; RecyclableObject* pObj = 0; - RecyclableObject* newObj = nullptr; - uint32 start = 0; - uint32 deleteLen = 0; - uint32 len = 0; - - if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext()) - { - isArr = true; - pArr = JavascriptArray::FromVar(args[0]); - pObj = pArr; - len = pArr->length; + uint64 start = 0u; + uint64 deleteLen = 0u; + uint64 length = 0u; -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(args[0]); -#endif - } - else - { - if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &pObj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.splice")); - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - int64 len64 = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext); - len = len64 > UINT_MAX ? UINT_MAX : (uint)len64; - } - else - { - len = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(pObj, scriptContext), scriptContext); - } - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.splice"), &pArr, &pObj, &length)); switch (args.Info.Count) { case 1: - start = len; - deleteLen = 0; + start = length; + deleteLen = 0u; break; case 2: - start = min(GetFromIndex(args[1], len, scriptContext), len); - deleteLen = len - start; + JS_REENTRANT(jsReentLock, start = GetFromIndex(args[1], length, scriptContext)); + deleteLen = length - start; break; default: - start = GetFromIndex(args[1], len, scriptContext); - - if (start > len) - { - start = len; - } - - // When start >= len, we know we won't be deleting any items and don't really need to evaluate the second argument. - // However, ECMA 262 15.4.4.12 requires that it be evaluated, anyway. If the argument is an object with a valueOf - // with a side effect, this evaluation is observable. Hence, we must evaluate. - if (TaggedInt::Is(args[2])) - { - int intDeleteLen = TaggedInt::ToInt32(args[2]); - if (intDeleteLen < 0) - { - deleteLen = 0; - } - else - { - deleteLen = intDeleteLen; - } - } - else - { - double dblDeleteLen = JavascriptConversion::ToInteger(args[2], scriptContext); - - if (dblDeleteLen > len) - { - deleteLen = (uint32)-1; - } - else if (dblDeleteLen <= 0) - { - deleteLen = 0; - } - else - { - deleteLen = (uint32)dblDeleteLen; - } - } - deleteLen = min(len - start, deleteLen); + JS_REENTRANT(jsReentLock, start = GetFromIndex(args[1], length, scriptContext), + deleteLen = GetFromIndex(args[2], (length - start), scriptContext, false)); break; } Var* insertArgs = args.Info.Count > 3 ? &args.Values[3] : nullptr; uint32 insertLen = args.Info.Count > 3 ? args.Info.Count - 3 : 0; - ::Math::RecordOverflowPolicy newLenOverflow; - uint32 newLen = UInt32Math::Add(len - deleteLen, insertLen, newLenOverflow); // new length of the array after splice - - if (isArr) + if (pArr != nullptr) { - // If we have missing values then convert to not native array for now - // In future, we could support this scenario. - if (deleteLen == insertLen) - { - pArr->FillFromPrototypes(start, start + deleteLen); - } - else if (len) - { - pArr->FillFromPrototypes(start, len); - } - - // - // If newLen overflowed, pre-process to prevent pushing sparse array segments or elements out of - // max array length, which would result in tons of index overflow and difficult to fix. - // - if (newLenOverflow.HasOverflowed()) - { - pArr = EnsureNonNativeArray(pArr); - BigIndex dstIndex = MaxArrayLength; - - uint32 maxInsertLen = MaxArrayLength - start; - if (insertLen > maxInsertLen) - { - // Copy overflowing insertArgs to properties - for (uint32 i = maxInsertLen; i < insertLen; i++) - { - pArr->DirectSetItemAt(dstIndex, insertArgs[i]); - ++dstIndex; - } - - insertLen = maxInsertLen; // update - - // Truncate elements on the right to properties - if (start + deleteLen < len) - { - pArr->TruncateToProperties(dstIndex, start + deleteLen); - } - } - else - { - // Truncate would-overflow elements to properties - pArr->TruncateToProperties(dstIndex, MaxArrayLength - insertLen + deleteLen); - } - - len = pArr->length; // update - newLen = len - deleteLen + insertLen; - Assert(newLen == MaxArrayLength); - } - - if (insertArgs) - { - pArr = EnsureNonNativeArray(pArr); - } - - bool isIntArray = false; - bool isFloatArray = false; - bool isBuiltinArrayCtor = true; - JavascriptArray *newArr = nullptr; - - // Just dump the segment map on splice (before any possible allocation and throw) - pArr->ClearSegmentMap(); - - // If the source object is an Array exotic object (Array.isArray) we should try to load the constructor property - // and use it to construct the return object. - newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor); - if (newObj != nullptr) - { - pArr = EnsureNonNativeArray(pArr); - // If the new object we created is an array, remember that as it will save us time setting properties in the object below - if (JavascriptArray::Is(newObj)) - { -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(newObj); -#endif - newArr = JavascriptArray::FromVar(newObj); - } - } - else - // This is the ES5 case, pArr['constructor'] doesn't exist, or pArr['constructor'] is the builtin Array constructor - { - pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray); - newArr = CreateNewArrayHelper(deleteLen, isIntArray, isFloatArray, pArr, scriptContext); -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(newArr); -#endif - } - - // If return object is a JavascriptArray, we can use all the array splice helpers - if (newArr && isBuiltinArrayCtor && len == pArr->length) - { - - // Array has a single segment (need not start at 0) and splice start lies in the range - // of that segment we optimize splice - Fast path. - if (pArr->IsSingleSegmentArray() && pArr->head->HasIndex(start)) - { - if (isIntArray) - { - ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); - } - else if (isFloatArray) - { - ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); - } - else - { - ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); - } - - // Since the start index is within the bounds of the original array's head segment, it will not acquire any new - // missing values. If the original array had missing values in the head segment, some of them may have been - // copied into the array that will be returned; otherwise, the array that is returned will also not have any - // missing values. - newArr->SetHasNoMissingValues(pArr->HasNoMissingValues()); - } - else - { - if (isIntArray) - { - ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); - } - else if (isFloatArray) - { - ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); - } - else - { - ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); - } - - // This function currently does not track missing values in the head segment if there are multiple segments - pArr->SetHasNoMissingValues(false); - newArr->SetHasNoMissingValues(false); - } - - if (isIntArray) - { - pArr->EnsureHeadStartsFromZero(recycler); - newArr->EnsureHeadStartsFromZero(recycler); - } - else if (isFloatArray) - { - pArr->EnsureHeadStartsFromZero(recycler); - newArr->EnsureHeadStartsFromZero(recycler); - } - else - { - pArr->EnsureHeadStartsFromZero(recycler); - newArr->EnsureHeadStartsFromZero(recycler); - } - - pArr->InvalidateLastUsedSegment(); - - // it is possible for valueOf accessors for the start or deleteLen - // arguments to modify the size of the array. Since the resulting size of the array - // is based on the cached value of length, this might lead to us having to trim - // excess array segments at the end of the splice operation, which SetLength() will do. - // However, this is also slower than performing the simple length assignment, so we only - // do it if we can detect the array length changing. - if(pArr->length != len) - { - pArr->SetLength(newLen); - } - else - { - pArr->length = newLen; - } - - if (newArr->length != deleteLen) - { - newArr->SetLength(deleteLen); - } - else - { - newArr->length = deleteLen; - } - - newArr->InvalidateLastUsedSegment(); - -#ifdef VALIDATE_ARRAY - newArr->ValidateArray(); - pArr->ValidateArray(); -#endif - if (newLenOverflow.HasOverflowed()) - { - // ES5 15.4.4.12 16: If new len overflowed, SetLength throws - JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect); - } - - return newArr; - } + // Since we get the length from an array and that cannot be more than uint32. + _Analysis_assume_(length <= UINT_MAX); + JS_REENTRANT_UNLOCK(jsReentLock, + return TryArraySplice(pArr, (uint32)start, (uint32)length, (uint32)deleteLen, insertArgs, insertLen, scriptContext)); } - if (newLenOverflow.HasOverflowed()) + uint64 newLen = (length - deleteLen) + insertLen; + if (newLen > UINT_MAX || length > UINT_MAX || (length + insertLen) > UINT_MAX) { - return ObjectSpliceHelper(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj); + JS_REENTRANT_UNLOCK(jsReentLock, + return ObjectSpliceHelper(pObj, length, start, deleteLen, insertArgs, insertLen, scriptContext, nullptr)); } - else // Use uint32 version if no overflow + else { - return ObjectSpliceHelper(pObj, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj); + JS_REENTRANT_UNLOCK(jsReentLock, + return ObjectSpliceHelper(pObj, (uint32)length, (uint32)start, (uint32)deleteLen, insertArgs, insertLen, scriptContext, nullptr)); } } @@ -7300,18 +6990,239 @@ namespace Js } } - template - RecyclableObject* JavascriptArray::ObjectSpliceHelper(RecyclableObject* pObj, uint32 len, uint32 start, - uint32 deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj) + Var JavascriptArray::TryArraySplice(JavascriptArray* pArr, uint32 start, uint32 len, uint32 deleteLen, + Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext) + { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + + Assert(pArr != nullptr); + + RecyclableObject* newObj = nullptr; + Recycler *recycler = scriptContext->GetRecycler(); + + ::Math::RecordOverflowPolicy newLenOverflow; + uint32 newLen = UInt32Math::Add(len - deleteLen, insertLen, newLenOverflow); // new length of the array after splice + + // If we have missing values then convert to not native array for now + // In future, we could support this scenario. + if (deleteLen == insertLen) + { + JS_REENTRANT(jsReentLock, pArr->FillFromPrototypes(start, start + deleteLen)); + } + else if (len) + { + JS_REENTRANT(jsReentLock, pArr->FillFromPrototypes(start, len)); + } + + // + // If newLen overflowed, pre-process to prevent pushing sparse array segments or elements out of + // max array length, which would result in tons of index overflow and difficult to fix. + // + if (newLenOverflow.HasOverflowed()) + { + pArr = EnsureNonNativeArray(pArr); + BigIndex dstIndex = MaxArrayLength; + + uint32 maxInsertLen = MaxArrayLength - start; + if (insertLen > maxInsertLen) + { + // Copy overflowing insertArgs to properties + for (uint32 i = maxInsertLen; i < insertLen; i++) + { + pArr->DirectSetItemAt(dstIndex, insertArgs[i]); + ++dstIndex; + } + + insertLen = maxInsertLen; // update + + // Truncate elements on the right to properties + if (start + deleteLen < len) + { + pArr->TruncateToProperties(dstIndex, start + deleteLen); + } + } + else + { + // Truncate would-overflow elements to properties + pArr->TruncateToProperties(dstIndex, MaxArrayLength - insertLen + deleteLen); + } + + len = pArr->length; // update + newLen = len - deleteLen + insertLen; + Assert(newLen == MaxArrayLength); + } + + if (insertArgs) + { + pArr = EnsureNonNativeArray(pArr); + } + + bool isIntArray = false; + bool isFloatArray = false; + bool isBuiltinArrayCtor = true; + JavascriptArray *newArr = nullptr; + + // Just dump the segment map on splice (before any possible allocation and throw) + pArr->ClearSegmentMap(); + + // If the source object is an Array exotic object (Array.isArray) we should try to load the constructor property + // and use it to construct the return object. + JS_REENTRANT(jsReentLock, newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor)); + if (newObj != nullptr) + { + pArr = EnsureNonNativeArray(pArr); + // If the new object we created is an array, remember that as it will save us time setting properties in the object below + if (JavascriptArray::Is(newObj)) + { +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(newObj); +#endif + newArr = JavascriptArray::FromVar(newObj); + } + } + else + // This is the ES5 case, pArr['constructor'] doesn't exist, or pArr['constructor'] is the builtin Array constructor + { + pArr->GetArrayTypeAndConvert(&isIntArray, &isFloatArray); + newArr = CreateNewArrayHelper(deleteLen, isIntArray, isFloatArray, pArr, scriptContext); +#if ENABLE_COPYONACCESS_ARRAY + JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(newArr); +#endif + } + + // If return object is a JavascriptArray, we can use all the array splice helpers + if (newArr && isBuiltinArrayCtor && len == pArr->length) + { + + // Array has a single segment (need not start at 0) and splice start lies in the range + // of that segment we optimize splice - Fast path. + if (pArr->IsSingleSegmentArray() && pArr->head->HasIndex(start)) + { + if (isIntArray) + { + ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); + } + else if (isFloatArray) + { + ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); + } + else + { + ArraySegmentSpliceHelper(newArr, (SparseArraySegment*)pArr->head, (SparseArraySegment**)&pArr->head, start, deleteLen, insertArgs, insertLen, recycler); + } + + // Since the start index is within the bounds of the original array's head segment, it will not acquire any new + // missing values. If the original array had missing values in the head segment, some of them may have been + // copied into the array that will be returned; otherwise, the array that is returned will also not have any + // missing values. + newArr->SetHasNoMissingValues(pArr->HasNoMissingValues()); + } + else + { + if (isIntArray) + { + ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); + } + else if (isFloatArray) + { + ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); + } + else + { + ArraySpliceHelper(newArr, pArr, start, deleteLen, insertArgs, insertLen, scriptContext); + } + + // This function currently does not track missing values in the head segment if there are multiple segments + pArr->SetHasNoMissingValues(false); + newArr->SetHasNoMissingValues(false); + } + + if (isIntArray) + { + pArr->EnsureHeadStartsFromZero(recycler); + newArr->EnsureHeadStartsFromZero(recycler); + } + else if (isFloatArray) + { + pArr->EnsureHeadStartsFromZero(recycler); + newArr->EnsureHeadStartsFromZero(recycler); + } + else + { + pArr->EnsureHeadStartsFromZero(recycler); + newArr->EnsureHeadStartsFromZero(recycler); + } + + pArr->InvalidateLastUsedSegment(); + + // it is possible for valueOf accessors for the start or deleteLen + // arguments to modify the size of the array. Since the resulting size of the array + // is based on the cached value of length, this might lead to us having to trim + // excess array segments at the end of the splice operation, which SetLength() will do. + // However, this is also slower than performing the simple length assignment, so we only + // do it if we can detect the array length changing. + if (pArr->length != len) + { + pArr->SetLength(newLen); + } + else + { + pArr->length = newLen; + } + + if (newArr->length != deleteLen) + { + newArr->SetLength(deleteLen); + } + else + { + newArr->length = deleteLen; + } + + newArr->InvalidateLastUsedSegment(); + +#ifdef VALIDATE_ARRAY + newArr->ValidateArray(); + pArr->ValidateArray(); +#endif + if (newLenOverflow.HasOverflowed()) + { + // ES5 15.4.4.12 16: If new len overflowed, SetLength throws + JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect); + } + + return newArr; + } + + if (newLenOverflow.HasOverflowed()) + { + JS_REENTRANT_UNLOCK(jsReentLock, return ObjectSpliceHelper(pArr, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj)); + } + else // Use uint32 version if no overflow + { + JS_REENTRANT_UNLOCK(jsReentLock, return ObjectSpliceHelper(pArr, len, start, deleteLen, insertArgs, insertLen, scriptContext, newObj)); + } + + } + + template + RecyclableObject* JavascriptArray::ObjectSpliceHelper(RecyclableObject* pObj, T len, T start, + T deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); JavascriptArray *pnewArr = nullptr; if (pNewObj == nullptr) { - pNewObj = ArraySpeciesCreate(pObj, deleteLen, scriptContext); - if (pNewObj == nullptr || !JavascriptArray::Is(pNewObj)) + JS_REENTRANT(jsReentLock, pNewObj = ArraySpeciesCreate(pObj, deleteLen, scriptContext)); + if (pNewObj == nullptr) { - pnewArr = scriptContext->GetLibrary()->CreateArray(deleteLen); + if (deleteLen > UINT_MAX) + { + JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthConstructIncorrect); + } + + pnewArr = scriptContext->GetLibrary()->CreateArray(static_cast(deleteLen)); pnewArr->EnsureHead(); pNewObj = pnewArr; @@ -7327,77 +7238,85 @@ namespace Js } // copy elements to delete to new array - if (deleteLen > 0) + if (pnewArr != nullptr) { for (uint32 i = 0; i < deleteLen; i++) { - if (JavascriptOperators::HasItem(pObj, start+i)) - { - Var element = JavascriptOperators::GetItem(pObj, start + i, scriptContext); - if (pnewArr) - { - pnewArr->SetItem(i, element, PropertyOperation_None); - } - else - { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(pNewObj, i, element), scriptContext, i); - } - } + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(pObj, start + i)); + if (hasItem) + { + JS_REENTRANT(jsReentLock, Var element = JavascriptOperators::GetItem(pObj, start + i, scriptContext)); + pnewArr->SetItem(i, element, PropertyOperation_None); + } } } - - ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.splice")); - - // If the return object is not an array, we'll need to set the 'length' property - if (pnewArr == nullptr) + else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, JavascriptNumber::ToVar(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + BigIndex k = 0u; + for (T i = 0u; i < deleteLen; i++) + { + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(pObj, start + i)); + if (hasItem) + { + Var element = nullptr; + JS_REENTRANT(jsReentLock, element = JavascriptOperators::GetItem(pObj, start + i, scriptContext), + ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(pNewObj, k, element), scriptContext, k)); + } + ++k; + } } + ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.splice")); + // Now we need reserve room if it is necessary if (insertLen > deleteLen) // Might overflow max array length { // Unshift [start + deleteLen, len) to start + insertLen - Unshift(pObj, start + insertLen, start + deleteLen, len, scriptContext); + JS_REENTRANT(jsReentLock, Unshift(pObj, start + insertLen, start + deleteLen, len, scriptContext)); } else if (insertLen < deleteLen) // Won't overflow max array length { - uint32 j = 0; - for (uint32 i = start + deleteLen; i < len; i++) + T j = 0; + for (T i = start + deleteLen; i < len; i++) { - if (JavascriptOperators::HasItem(pObj, i)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(pObj, i)); + if (hasItem) { - Var element = JavascriptOperators::GetItem(pObj, i, scriptContext); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, start + insertLen + j, element, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + Var element = nullptr; + JS_REENTRANT(jsReentLock, element = JavascriptOperators::GetItem(pObj, i, scriptContext), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetItem(pObj, pObj, start + insertLen + j, element, scriptContext, PropertyOperation_ThrowIfNotExtensible))); } else { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, start + insertLen + j, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, start + insertLen + j, PropertyOperation_ThrowOnDeleteIfNotConfig))); } j++; } // Clean up the rest - for (uint32 i = len; i > len - deleteLen + insertLen; i--) + for (T i = len; i > len - deleteLen + insertLen; i--) { - h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, i - 1, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(JavascriptOperators::DeleteItem(pObj, i - 1, PropertyOperation_ThrowOnDeleteIfNotConfig))); } } if (insertLen > 0) { - indexT dstIndex = start; // insert index might overflow max array length - for (uint i = 0; i < insertLen; i++) + T dstIndex = start; // insert index might overflow max array length + for (uint32 i = 0; i < insertLen; i++) { - h.ThrowTypeErrorOnFailure(IndexTrace::SetItem(pObj, dstIndex, insertArgs[i], PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(IndexTrace::SetItem(pObj, dstIndex, insertArgs[i], PropertyOperation_ThrowIfNotExtensible))); ++dstIndex; } } // Set up new length - indexT newLen = indexT(len - deleteLen) + insertLen; - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pObj, pObj, PropertyIds::length, IndexTrace::ToNumber(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, IndexTrace::ToNumber(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); + T newLen = T(len - deleteLen) + insertLen; + JS_REENTRANT(jsReentLock, + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pObj, pObj, PropertyIds::length, IndexTrace::ToNumber(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)), + h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, IndexTrace::ToNumber(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible))); #ifdef VALIDATE_ARRAY if (pnewArr) { @@ -7413,6 +7332,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -7424,7 +7344,7 @@ namespace Js if (JavascriptArray::IsDirectAccessArray(args[0])) { JavascriptArray* arr = JavascriptArray::FromVar(args[0]); - return ToLocaleString(arr, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return ToLocaleString(arr, scriptContext)); } else { @@ -7438,7 +7358,7 @@ namespace Js { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.toLocaleString")); } - return ToLocaleString(obj, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return ToLocaleString(obj, scriptContext)); } } @@ -7446,8 +7366,10 @@ namespace Js // Unshift object elements [start, end) to toIndex, asserting toIndex > start. // template - void JavascriptArray::Unshift(RecyclableObject* obj, const T& toIndex, uint32 start, P end, ScriptContext* scriptContext) + void JavascriptArray::Unshift(RecyclableObject* obj, const T& toIndex, P start, P end, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + typedef IndexTrace index_trace; ThrowTypeErrorOnFailureHelper h(scriptContext, _u("Array.prototype.unshift")); @@ -7455,40 +7377,18 @@ namespace Js { T newEnd = (end - start - 1);// newEnd - 1 T dst = toIndex + newEnd; - uint32 i = 0; - if (end > UINT32_MAX) + for (P i = end; i > start; --i) { - uint64 i64 = end; - for (; i64 > UINT32_MAX; i64--) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, i - 1)); + if (hasItem) { - if (JavascriptOperators::HasItem(obj, i64 - 1)) - { - Var element = JavascriptOperators::GetItem(obj, i64 - 1, scriptContext); - h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible)); - } - else - { - h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowOnDeleteIfNotConfig)); - } - - --dst; - } - i = UINT32_MAX; - } - else - { - i = (uint32) end; - } - for (; i > start; i--) - { - if (JavascriptOperators::HasItem(obj, i-1)) - { - Var element = JavascriptOperators::GetItem(obj, i - 1, scriptContext); - h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible)); + Var element = nullptr; + JS_REENTRANT(jsReentLock, element = JavascriptOperators::GetItem(obj, i - 1, scriptContext), + h.ThrowTypeErrorOnFailure(index_trace::SetItem(obj, dst, element, PropertyOperation_ThrowIfNotExtensible))); } else { - h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowOnDeleteIfNotConfig)); + JS_REENTRANT(jsReentLock, h.ThrowTypeErrorOnFailure(index_trace::DeleteItem(obj, dst, PropertyOperation_ThrowOnDeleteIfNotConfig))); } --dst; @@ -7550,6 +7450,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -7572,7 +7473,7 @@ namespace Js { if (pArr->IsFillFromPrototypes()) { - pArr->FillFromPrototypes(0, pArr->length); // We need find all missing value from [[proto]] object + JS_REENTRANT(jsReentLock, pArr->FillFromPrototypes(0, pArr->length)); // We need find all missing value from [[proto]] object } // Pre-process: truncate overflowing elements to properties @@ -7669,15 +7570,7 @@ namespace Js JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.unshift")); } - BigIndex length; - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, BigIndex length = OP_GetLength(dynamicObject, scriptContext)); uint32 unshiftElements = args.Info.Count - 1; if (unshiftElements > 0) { @@ -7691,21 +7584,22 @@ namespace Js // MaxArrayLength + (length - MaxSpaceUint32 - 1) = length + unshiftElements -1 if (length.IsSmallIndex()) { - Unshift(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetSmallIndex(), scriptContext); + JS_REENTRANT(jsReentLock, Unshift(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetSmallIndex(), scriptContext)); } else { - Unshift(dynamicObject, MaxArrayLength, end.GetSmallIndex(), length.GetBigIndex(), scriptContext); + JS_REENTRANT(jsReentLock, Unshift(dynamicObject, MaxArrayLength, (uint64)end.GetSmallIndex(), length.GetBigIndex(), scriptContext)); } } // Unshift [0, end) to unshiftElements // unshiftElements + (MaxSpaceUint32 - 0 - 1) = MaxArrayLength -1 therefore this unshift covers up to MaxArrayLength - 1 - Unshift(dynamicObject, unshiftElements, 0, end.GetSmallIndex(), scriptContext); + JS_REENTRANT(jsReentLock, Unshift(dynamicObject, unshiftElements, (uint32)0, end.GetSmallIndex(), scriptContext)); for (uint32 i = 0; i < unshiftElements; i++) { - JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, args[i + 1], scriptContext, PropertyOperation_ThrowIfNotExtensible, true); + JS_REENTRANT(jsReentLock, + JavascriptOperators::SetItem(dynamicObject, dynamicObject, i, args[i + 1], scriptContext, PropertyOperation_ThrowIfNotExtensible, true)); } } @@ -7714,7 +7608,9 @@ namespace Js //ES6 - update 'length' even if unshiftElements == 0; BigIndex newLen = length + unshiftElements; res = JavascriptNumber::ToVar(newLen.IsSmallIndex() ? newLen.GetSmallIndex() : newLen.GetBigIndex(), scriptContext); - h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, res, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + JS_REENTRANT(jsReentLock, + BOOL setLength = JavascriptOperators::SetProperty(dynamicObject, dynamicObject, PropertyIds::length, res, scriptContext, PropertyOperation_ThrowIfNotExtensible)); + h.ThrowTypeErrorOnFailure(setLength); } return res; @@ -7726,6 +7622,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -7743,22 +7640,23 @@ namespace Js } // In ES5 we could be calling a user defined join, even on array. We must [[Get]] join at runtime. - Var join = JavascriptOperators::GetProperty(obj, PropertyIds::join, scriptContext); + JS_REENTRANT(jsReentLock, Var join = JavascriptOperators::GetProperty(obj, PropertyIds::join, scriptContext)); if (JavascriptConversion::IsCallable(join)) { RecyclableObject* func = RecyclableObject::FromVar(join); // We need to record implicit call here, because marked the Array.toString as no side effect, // but if we call user code here which may have side effect ThreadContext * threadContext = scriptContext->GetThreadContext(); - Var result = threadContext->ExecuteImplicitCall(func, ImplicitCall_ToPrimitive, [=]() -> Js::Var + JS_REENTRANT(jsReentLock, + Var result = threadContext->ExecuteImplicitCall(func, ImplicitCall_ToPrimitive, [=]() -> Js::Var { // Stack object should have a pre-op bail on implicit call. We shouldn't see them here. Assert(!ThreadContext::IsOnStack(obj)); // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes CallFlags flags = CallFlags_Value; - return CALL_FUNCTION(func, CallInfo(flags, 1), obj); - }); + return CALL_FUNCTION(threadContext, func, CallInfo(flags, 1), obj); + })); if(!result) { @@ -7772,7 +7670,8 @@ namespace Js else { // call built-in Object.prototype.toString - return CALL_ENTRYPOINT(JavascriptObject::EntryToString, function, CallInfo(1), obj); + JS_REENTRANT_UNLOCK(jsReentLock, + return CALL_ENTRYPOINT(scriptContext->GetThreadContext(), JavascriptObject::EntryToString, function, CallInfo(1), obj)); } } @@ -7854,6 +7753,8 @@ namespace Js template JavascriptString* JavascriptArray::ToLocaleString(T* arr, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + uint32 length = 0; if (TypedArrayBase::Is(arr)) { @@ -7863,7 +7764,7 @@ namespace Js else { //For anything else, use the "length" property if present. - length = ItemTrace::GetLength(arr, scriptContext); + JS_REENTRANT(jsReentLock, length = ItemTrace::GetLength(arr, scriptContext)); } if (length == 0 || scriptContext->CheckObject(arr)) @@ -7880,9 +7781,10 @@ namespace Js pushedObject = true; Var element; - if (ItemTrace::GetItem(arr, 0, &element, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = ItemTrace::GetItem(arr, 0, &element, scriptContext)); + if (gotItem) { - res = JavascriptArray::ToLocaleStringHelper(element, scriptContext); + JS_REENTRANT(jsReentLock, res = JavascriptArray::ToLocaleStringHelper(element, scriptContext)); } if (length > 1) @@ -7892,9 +7794,10 @@ namespace Js for (uint32 i = 1; i < length; i++) { res = JavascriptString::Concat(res, separator); - if (ItemTrace::GetItem(arr, i, &element, scriptContext)) + JS_REENTRANT(jsReentLock, gotItem = ItemTrace::GetItem(arr, i, &element, scriptContext)); + if (gotItem) { - res = JavascriptString::Concat(res, JavascriptArray::ToLocaleStringHelper(element, scriptContext)); + JS_REENTRANT(jsReentLock, res = JavascriptString::Concat(res, JavascriptArray::ToLocaleStringHelper(element, scriptContext))); } } } @@ -7922,6 +7825,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -7953,6 +7857,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -7965,32 +7870,15 @@ namespace Js JavascriptArray * pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.find")); - } - // In ES6-mode, we always load the length property from the object instead of using the internal slot. - // Even for arrays, this is now observable via proxies. - // If source object is not an array, we fall back to this behavior anyway. - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - length = JavascriptConversion::ToLength(lenValue, scriptContext); - } - - - return JavascriptArray::FindHelper(pArr, nullptr, obj, length, args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.find"), &pArr, &obj, &length)); + return JavascriptArray::FindHelper(pArr, nullptr, obj, length, args, scriptContext); } template Var JavascriptArray::FindHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint @@ -8033,14 +7921,15 @@ namespace Js for (uint32 k = 0; k < length; k++) { element = undefined; - pArr->DirectGetItemAtFull(k, &element); + JS_REENTRANT(jsReentLock, pArr->DirectGetItemAtFull(k, &element)); Var index = JavascriptNumber::ToVar(k, scriptContext); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - index, - pArr); + JS_REENTRANT(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + index, + pArr)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8052,14 +7941,17 @@ namespace Js { for (uint32 k = 0; k < length; k++) { + // Spec does not ask to call HasItem, so we need to go to visit the whole length + element = typedArrayBase->DirectGetItem(k); Var index = JavascriptNumber::ToVar(k, scriptContext); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - index, - typedArrayBase); + JS_REENTRANT(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + index, + typedArrayBase)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8071,13 +7963,14 @@ namespace Js { for (uint32 k = 0; k < length; k++) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); + JS_REENTRANT(jsReentLock, element = JavascriptOperators::GetItem(obj, k, scriptContext)); Var index = JavascriptNumber::ToVar(k, scriptContext); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - index, - obj); + JS_REENTRANT(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + index, + obj)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8100,6 +7993,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8112,26 +8006,9 @@ namespace Js JavascriptArray * pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.findIndex")); - } - // In ES6-mode, we always load the length property from the object instead of using the internal slot. - // Even for arrays, this is now observable via proxies. - // If source object is not an array, we fall back to this behavior anyway. - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - length = JavascriptConversion::ToLength(lenValue, scriptContext); - } - - return JavascriptArray::FindHelper(pArr, nullptr, obj, length, args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, + TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.findIndex"), &pArr, &obj, &length)); + return JavascriptArray::FindHelper(pArr, nullptr, obj, length, args, scriptContext); } ///---------------------------------------------------------------------------- @@ -8145,6 +8022,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8162,7 +8040,8 @@ namespace Js #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(thisObj); #endif - return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::KeyAndValue); + JS_REENTRANT_UNLOCK(jsReentLock, + return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::KeyAndValue)); } ///---------------------------------------------------------------------------- @@ -8175,6 +8054,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8192,7 +8072,8 @@ namespace Js #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(thisObj); #endif - return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Key); + JS_REENTRANT_UNLOCK(jsReentLock, + return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Key)); } ///---------------------------------------------------------------------------- @@ -8205,6 +8086,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8222,7 +8104,8 @@ namespace Js #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(thisObj); #endif - return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Value); + JS_REENTRANT_UNLOCK(jsReentLock, + return scriptContext->GetLibrary()->CreateArrayIterator(thisObj, JavascriptArrayIteratorKind::Value)); } Var JavascriptArray::EntryEvery(RecyclableObject* function, CallInfo callInfo, ...) @@ -8231,6 +8114,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.every")); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8246,40 +8130,22 @@ namespace Js JavascriptArray* pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.every")); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.every"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::EveryHelper(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 - return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::EveryHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } // Array.prototype.every as described by ES6.0 (draft 22) Section 22.1.3.5 template Var JavascriptArray::EveryHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { // typedArrayBase is only non-null if and only if we came here via the TypedArray entrypoint @@ -8321,15 +8187,17 @@ namespace Js { for (uint32 k = 0; k < length; k++) { - if (!pArr->DirectGetItemAtFull(k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(k, &element)); + if (!gotItem) { continue; } - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - pArr); + JS_REENTRANT(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + pArr)); if (!JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8339,21 +8207,20 @@ namespace Js } else if (typedArrayBase) { - Assert(length <= UINT_MAX); + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); - for (uint32 k = 0; k < length; k++) + for (uint32 k = 0; k < end; k++) { - if (!typedArrayBase->HasItem(k)) - { - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. element = typedArrayBase->DirectGetItem(k); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - typedArrayBase); + JS_REENTRANT(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + typedArrayBase)); if (!JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8366,14 +8233,15 @@ namespace Js for (T k = 0; k < length; k++) { // According to es6 spec, we need to call Has first before calling Get - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); - - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(obj, k, scriptContext), + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); if (!JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8392,6 +8260,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.some")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_some); @@ -8407,41 +8277,22 @@ namespace Js JavascriptArray* pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.some")); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.some"), &pArr, &obj, &length)); - if (length.IsSmallIndex()) + if (length.IsSmallIndex()) { - return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::SomeHelper(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 - return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::SomeHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } // Array.prototype.some as described in ES6.0 (draft 22) Section 22.1.3.23 template Var JavascriptArray::SomeHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { // We are in the TypedArray version of this API if and only if typedArrayBase != nullptr @@ -8482,15 +8333,17 @@ namespace Js { for (uint32 k = 0; k < length; k++) { - if (!pArr->DirectGetItemAtFull(k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(k, &element)); + if (!gotItem) { continue; } - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - pArr); + JS_REENTRANT_UNLOCK(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + pArr)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8500,24 +8353,20 @@ namespace Js } else if (typedArrayBase) { - Assert(length <= UINT_MAX); + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); - for (uint32 k = 0; k < length; k++) + for (uint32 k = 0; k < end; k++) { - // If k < typedArrayBase->length, we know that HasItem will return true. - // But we still have to call it in case there's a proxy trap or in the case that we are calling - // Array.prototype.some with a TypedArray that has a different length instance property. - if (!typedArrayBase->HasItem(k)) - { - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. element = typedArrayBase->DirectGetItem(k); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - typedArrayBase); + JS_REENTRANT_UNLOCK(jsReentLock, + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + typedArrayBase)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8529,13 +8378,15 @@ namespace Js { for (T k = 0; k < length; k++) { - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); - testResult = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT_UNLOCK(jsReentLock, + element = JavascriptOperators::GetItem(obj, k, scriptContext), + testResult = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); if (JavascriptConversion::ToBoolean(testResult, scriptContext)) { @@ -8554,6 +8405,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.forEach")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_forEach) @@ -8571,35 +8424,7 @@ namespace Js RecyclableObject* callBackFn = nullptr; Var thisArg = nullptr; -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(args[0]); -#endif - if (JavascriptArray::Is(args[0]) && scriptContext == JavascriptArray::FromVar(args[0])->GetScriptContext()) - { - pArr = JavascriptArray::FromVar(args[0]); - dynamicObject = pArr; - } - else - { - if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.forEach")); - } - - if (JavascriptArray::Is(dynamicObject) && scriptContext == JavascriptArray::FromVar(dynamicObject)->GetScriptContext()) - { - pArr = JavascriptArray::FromVar(dynamicObject); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.forEach"), &pArr, &dynamicObject, &length)); if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { @@ -8619,17 +8444,19 @@ namespace Js // The correct flag value is CallFlags_Value but we pass CallFlags_None in compat modes CallFlags flags = CallFlags_Value; - auto fn32 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint32 k, Var element) + auto fn32 = [dynamicObject, callBackFn, flags, thisArg, + scriptContext](uint32 k, Var element) { - CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, + CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, element, JavascriptNumber::ToVar(k, scriptContext), dynamicObject); }; - auto fn64 = [dynamicObject, callBackFn, flags, thisArg, scriptContext](uint64 k, Var element) + auto fn64 = [dynamicObject, callBackFn, flags, thisArg, + scriptContext](uint64 k, Var element) { - CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, + CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 4), thisArg, element, JavascriptNumber::ToVar(k, scriptContext), dynamicObject); @@ -8638,17 +8465,17 @@ namespace Js if (pArr) { Assert(pArr == dynamicObject); - pArr->ForEachItemInRange(0, length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(), scriptContext, fn32); + JS_REENTRANT(jsReentLock, pArr->ForEachItemInRange(0, length.IsUint32Max() ? MaxArrayLength : length.GetSmallIndex(), scriptContext, fn32)); } else { if (length.IsSmallIndex()) { - TemplatedForEachItemInRange(dynamicObject, 0u, length.GetSmallIndex(), scriptContext, fn32); + JS_REENTRANT(jsReentLock, TemplatedForEachItemInRange(dynamicObject, 0u, length.GetSmallIndex(), scriptContext, fn32)); } else { - TemplatedForEachItemInRange(dynamicObject, 0ui64, length.GetBigIndex(), scriptContext, fn64); + JS_REENTRANT(jsReentLock, TemplatedForEachItemInRange(dynamicObject, 0ui64, length.GetBigIndex(), scriptContext, fn64)); } } return scriptContext->GetLibrary()->GetUndefined(); @@ -8660,6 +8487,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8667,36 +8495,15 @@ namespace Js JavascriptArray* pArr = nullptr; int64 length; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { -#if ENABLE_COPYONACCESS_ARRAY - JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(args[0]); -#endif - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.copyWithin")); - } - - // In ES6-mode, we always load the length property from the object instead of using the internal slot. - // Even for arrays, this is now observable via proxies. - // If source object is not an array, we fall back to this behavior anyway. - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - length = JavascriptConversion::ToLength(lenValue, scriptContext); - } - - return JavascriptArray::CopyWithinHelper(pArr, nullptr, obj, length, args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.copyWithin"), &pArr, &obj, &length)); + return JavascriptArray::CopyWithinHelper(pArr, nullptr, obj, length, args, scriptContext); } // Array.prototype.copyWithin as defined in ES6.0 (draft 22) Section 22.1.3.3 Var JavascriptArray::CopyWithinHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + Assert(args.Info.Count > 0); JavascriptLibrary* library = scriptContext->GetLibrary(); @@ -8712,15 +8519,15 @@ namespace Js if (args.Info.Count > 1) { - toVal = JavascriptArray::GetIndexFromVar(args[1], length, scriptContext); + JS_REENTRANT(jsReentLock, toVal = JavascriptArray::GetIndexFromVar(args[1], length, scriptContext)); if (args.Info.Count > 2) { - fromVal = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext); + JS_REENTRANT(jsReentLock, fromVal = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext)); if (args.Info.Count > 3 && args[3] != library->GetUndefined()) { - finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext); + JS_REENTRANT(jsReentLock, finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext)); } } } @@ -8761,11 +8568,13 @@ namespace Js { Var index = JavascriptNumber::ToVar(fromVal, scriptContext); - if (JavascriptOperators::OP_HasItem(obj, index, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::OP_HasItem(obj, index, scriptContext)); + if (hasItem) { - Var val = JavascriptOperators::OP_GetElementI(obj, index, scriptContext); - - JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), val, scriptContext, PropertyOperation_ThrowIfNotExtensible); + Var val = nullptr; + JS_REENTRANT(jsReentLock, + val = JavascriptOperators::OP_GetElementI(obj, index, scriptContext), + JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(toVal, scriptContext), val, scriptContext, PropertyOperation_ThrowIfNotExtensible)); } else { @@ -8788,25 +8597,26 @@ namespace Js while (count > 0) { - if (obj->HasItem(fromIndex)) + JS_REENTRANT(jsReentLock, BOOL hasItem = obj->HasItem(fromIndex)); + if (hasItem) { if (typedArrayBase) { Var val = typedArrayBase->DirectGetItem(fromIndex); - typedArrayBase->DirectSetItem(toIndex, val); + JS_REENTRANT(jsReentLock, typedArrayBase->DirectSetItem(toIndex, val)); } else if (pArr) { - Var val = pArr->DirectGetItem(fromIndex); - + JS_REENTRANT(jsReentLock, Var val = pArr->DirectGetItem(fromIndex)); pArr->SetItem(toIndex, val, Js::PropertyOperation_ThrowIfNotExtensible); } else { - Var val = JavascriptOperators::OP_GetElementI_UInt32(obj, fromIndex, scriptContext); - - JavascriptOperators::OP_SetElementI_UInt32(obj, toIndex, val, scriptContext, PropertyOperation_ThrowIfNotExtensible); + Var val = nullptr; + JS_REENTRANT(jsReentLock, + val = JavascriptOperators::OP_GetElementI_UInt32(obj, fromIndex, scriptContext), + JavascriptOperators::OP_SetElementI_UInt32(obj, toIndex, val, scriptContext, PropertyOperation_ThrowIfNotExtensible)); } } else @@ -8829,6 +8639,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -8836,33 +8647,16 @@ namespace Js JavascriptArray* pArr = nullptr; int64 length; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.fill")); - } - - // In ES6-mode, we always load the length property from the object instead of using the internal slot. - // Even for arrays, this is now observable via proxies. - // If source object is not an array, we fall back to this behavior anyway. - Var lenValue = JavascriptOperators::OP_GetLength(obj, scriptContext); - length = JavascriptConversion::ToLength(lenValue, scriptContext); - } - - return JavascriptArray::FillHelper(pArr, nullptr, obj, length, args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, + TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.fill"), &pArr, &obj, &length)); + return JavascriptArray::FillHelper(pArr, nullptr, obj, length, args, scriptContext); } // Array.prototype.fill as defined in ES6.0 (draft 22) Section 22.1.3.6 Var JavascriptArray::FillHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + Assert(args.Info.Count > 0); JavascriptLibrary* library = scriptContext->GetLibrary(); @@ -8889,11 +8683,11 @@ namespace Js if (args.Info.Count > 2) { - k = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, k = JavascriptArray::GetIndexFromVar(args[2], length, scriptContext)); if (args.Info.Count > 3 && !JavascriptOperators::IsUndefinedObject(args[3])) { - finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, finalVal = JavascriptArray::GetIndexFromVar(args[3], length, scriptContext)); } } @@ -8906,7 +8700,7 @@ namespace Js { if (typedArrayBase) { - typedArrayBase->DirectSetItem(u32k, fillValue); + JS_REENTRANT(jsReentLock, typedArrayBase->DirectSetItem(u32k, fillValue)); } else if (pArr) { @@ -8914,7 +8708,8 @@ namespace Js } else { - JavascriptOperators::OP_SetElementI_UInt32(obj, u32k, fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, + JavascriptOperators::OP_SetElementI_UInt32(obj, u32k, fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible)); } u32k++; @@ -8931,7 +8726,8 @@ namespace Js } else { - JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, + JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible)); } } } @@ -8948,7 +8744,8 @@ namespace Js } else { - JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, + JavascriptOperators::OP_SetElementI(obj, JavascriptNumber::ToVar(i, scriptContext), fillValue, scriptContext, Js::PropertyOperation_ThrowIfNotExtensible)); } } } @@ -8963,6 +8760,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.map")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_map); @@ -8978,33 +8776,22 @@ namespace Js JavascriptArray* pArr = nullptr; RecyclableObject* obj = nullptr; - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.map")); - } - } - - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.map"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::MapHelper(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 - return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::MapHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } template Var JavascriptArray::MapHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + RecyclableObject* newObj = nullptr; JavascriptArray* newArr = nullptr; bool isTypedArrayEntryPoint = typedArrayBase != nullptr; @@ -9044,26 +8831,22 @@ namespace Js // and use it to construct the return object. if (isTypedArrayEntryPoint) { - Var constructor = JavascriptOperators::SpeciesConstructor( - typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext); - isBuiltinArrayCtor = (constructor == scriptContext->GetLibrary()->GetArrayConstructor()); + JS_REENTRANT(jsReentLock, + Var constructor = JavascriptOperators::SpeciesConstructor( + typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext)); + + isBuiltinArrayCtor = false; - if (JavascriptOperators::IsConstructor(constructor)) - { - Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) }; - Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); - newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)length, scriptContext)); - } - else if (isTypedArrayEntryPoint) - { - // We only need to throw a TypeError when the constructor property is not an actual constructor if %TypedArray%.prototype.map was called - JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("[TypedArray].prototype.map")); - } + Assert(JavascriptOperators::IsConstructor(constructor)); + + Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) }; + Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); + JS_REENTRANT(jsReentLock, newObj = RecyclableObject::FromVar(TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), (uint32)length, scriptContext))); } // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array. else if (pArr == nullptr || scriptContext->GetConfig()->IsES6SpeciesEnabled()) { - newObj = ArraySpeciesCreate(obj, length, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor); + JS_REENTRANT(jsReentLock, newObj = ArraySpeciesCreate(obj, length, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor)); } if (newObj == nullptr) @@ -9103,15 +8886,17 @@ namespace Js for (uint32 k = 0; k < length; k++) { - if (!pArr->DirectGetItemAtFull(k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(k, &element)); + if (!gotItem) { continue; } - mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - pArr); + JS_REENTRANT(jsReentLock, + mappedValue = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, callBackFnInfo, thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + pArr)); // If newArr is a valid pointer, then we constructed an array to return. Otherwise we need to do generic object operations if (newArr && isBuiltinArrayCtor) @@ -9120,12 +8905,14 @@ namespace Js } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue), scriptContext, k); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue), scriptContext, k)); } } } else if (typedArrayBase != nullptr) { + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + // Source is a TypedArray, we may have tried to call a constructor, but newObj may not be a TypedArray (or an array either) TypedArrayBase* newTypedArray = nullptr; @@ -9133,39 +8920,32 @@ namespace Js { newTypedArray = TypedArrayBase::FromVar(newObj); } + else + { + AssertAndFailFast(newArr != nullptr); + } - for (uint32 k = 0; k < length; k++) + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); + + for (uint32 k = 0; k < end; k++) { - // We can't rely on the length value being equal to typedArrayBase->GetLength() because user code may lie and - // attach any length property to a TypedArray instance and pass it as this parameter when .calling - // Array.prototype.map. - if (!typedArrayBase->HasItem(k)) - { - // We know that if HasItem returns false, all the future calls to HasItem will return false as well since - // we visit the items in order. We could return early here except that we have to continue calling HasItem - // on all the subsequent items according to the spec. - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. element = typedArrayBase->DirectGetItem(k); - mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + mappedValue = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, callBackFnInfo, thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); - // If newObj is a TypedArray, set the mappedValue directly, otherwise see if it's an array and finally fall back to - // the normal Set path. + // If newObj is a TypedArray, set the mappedValue directly, otherwise it should be an array, set that item to that array if (newTypedArray) { - newTypedArray->DirectSetItem(k, mappedValue); - } - else if (newArr) - { - newArr->DirectSetItemAt(k, mappedValue); + JS_REENTRANT(jsReentLock, newTypedArray->DirectSetItem(k, mappedValue)); } else { - JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue); + newArr->SetItem(k, mappedValue, PropertyOperation_None); } } } @@ -9173,13 +8953,15 @@ namespace Js { for (uint32 k = 0; k < length; k++) { - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); - mappedValue = CALL_FUNCTION(callBackFn, callBackFnInfo, thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(obj, k, scriptContext), + mappedValue = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, callBackFnInfo, thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); if (newArr && isBuiltinArrayCtor) { @@ -9187,7 +8969,7 @@ namespace Js } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue), scriptContext, k); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, mappedValue), scriptContext, k)); } } } @@ -9209,6 +8991,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.filter")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_filter); @@ -9221,40 +9005,23 @@ namespace Js BigIndex length; JavascriptArray* pArr = nullptr; - RecyclableObject* dynamicObject = nullptr; - - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - dynamicObject = pArr; - } - else - { - if (FALSE == JavascriptConversion::ToObject(args[0], scriptContext, &dynamicObject)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.filter")); - } - } + RecyclableObject* obj = nullptr; - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(dynamicObject, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.filter"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::FilterHelper(pArr, dynamicObject, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::FilterHelper(pArr, obj, length.GetSmallIndex(), args, scriptContext)); } - return JavascriptArray::FilterHelper(pArr, dynamicObject, length.GetBigIndex(), args, scriptContext); + + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::FilterHelper(pArr, obj, length.GetBigIndex(), args, scriptContext)); } template Var JavascriptArray::FilterHelper(JavascriptArray* pArr, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Array.prototype.filter")); @@ -9274,7 +9041,7 @@ namespace Js // If the source object is an Array exotic object we should try to load the constructor property and use it to construct the return object. bool isBuiltinArrayCtor = true; - RecyclableObject* newObj = ArraySpeciesCreate(obj, 0, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor); + JS_REENTRANT(jsReentLock, RecyclableObject* newObj = ArraySpeciesCreate(obj, 0, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor)); JavascriptArray* newArr = nullptr; if (newObj == nullptr) @@ -9305,16 +9072,19 @@ namespace Js for (uint32 k = 0; k < length; k++) { - if (!pArr->DirectGetItemAtFull(k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull(k, &element)); + if (!gotItem) { continue; } - selected = CALL_ENTRYPOINT(callBackFn->GetEntryPoint(), callBackFn, CallInfo(CallFlags_Value, 4), - thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - pArr); + JS_REENTRANT(jsReentLock, + selected = CALL_ENTRYPOINT(scriptContext->GetThreadContext(), + callBackFn->GetEntryPoint(), callBackFn, CallInfo(CallFlags_Value, 4), + thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + pArr)); if (JavascriptConversion::ToBoolean(selected, scriptContext)) { @@ -9325,7 +9095,7 @@ namespace Js } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i)); } ++i; } @@ -9337,14 +9107,17 @@ namespace Js for (T k = 0; k < length; k++) { - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); - selected = CALL_ENTRYPOINT(callBackFn->GetEntryPoint(), callBackFn, CallInfo(CallFlags_Value, 4), - thisArg, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(obj, k, scriptContext), + selected = CALL_ENTRYPOINT(scriptContext->GetThreadContext(), + callBackFn->GetEntryPoint(), callBackFn, CallInfo(CallFlags_Value, 4), + thisArg, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); if (JavascriptConversion::ToBoolean(selected, scriptContext)) { @@ -9354,7 +9127,7 @@ namespace Js } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(newObj, i, element), scriptContext, i)); } ++i; } @@ -9378,6 +9151,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduce")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_reduce); @@ -9392,41 +9167,22 @@ namespace Js BigIndex length; JavascriptArray * pArr = nullptr; RecyclableObject* obj = nullptr; + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.reduce"), &pArr, &obj, &length)); - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - - length = pArr->length; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduce")); - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } - } if (length.IsSmallIndex()) { - return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext)); } - return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReduceHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } // Array.prototype.reduce as described in ES6.0 (draft 22) Section 22.1.3.18 template Var JavascriptArray::ReduceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { if (typedArrayBase != nullptr) @@ -9467,7 +9223,8 @@ namespace Js { for (; k < length && bPresent == false; k++) { - if (!pArr->DirectGetItemAtFull((uint32)k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull((uint32)k, &element)); + if (!gotItem) { continue; } @@ -9478,14 +9235,12 @@ namespace Js } else if (typedArrayBase) { - Assert(length <= UINT_MAX); + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); - for (; k < length && bPresent == false; k++) + for (; k < end && bPresent == false; k++) { - if (!typedArrayBase->HasItem((uint32)k)) - { - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. element = typedArrayBase->DirectGetItem((uint32)k); @@ -9497,9 +9252,10 @@ namespace Js { for (; k < length && bPresent == false; k++) { - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - accumulator = JavascriptOperators::GetItem(obj, k, scriptContext); + JS_REENTRANT(jsReentLock, accumulator = JavascriptOperators::GetItem(obj, k, scriptContext)); bPresent = true; } } @@ -9521,50 +9277,53 @@ namespace Js { for (; k < length; k++) { - if (!pArr->DirectGetItemAtFull((uint32)k, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull((uint32)k, &element)); + if (!gotItem) { continue; } - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(k, scriptContext), - pArr); + JS_REENTRANT(jsReentLock, + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(k, scriptContext), + pArr)); } } else if (typedArrayBase) { - Assert(length <= UINT_MAX); - for (; k < length; k++) + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); + + for (; k < end; k++) { - if (!typedArrayBase->HasItem((uint32)k)) - { - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. element = typedArrayBase->DirectGetItem((uint32)k); - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(k, scriptContext), - typedArrayBase); + JS_REENTRANT(jsReentLock, + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(k, scriptContext), + typedArrayBase)); } } else { for (; k < length; k++) { - if (JavascriptOperators::HasItem(obj, k)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, k)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, k, scriptContext); - - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(k, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(obj, k, scriptContext), + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(k, scriptContext), + obj)); } } } @@ -9578,6 +9337,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.prototype.reduceRight")); CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Array_Prototype_reduceRight); @@ -9592,40 +9353,22 @@ namespace Js BigIndex length; JavascriptArray * pArr = nullptr; RecyclableObject* obj = nullptr; - - if (JavascriptArray::Is(args[0]) && !JavascriptArray::FromVar(args[0])->IsCrossSiteObject()) - { - pArr = JavascriptArray::FromVar(args[0]); - obj = pArr; - } - else - { - if (!JavascriptConversion::ToObject(args[0], scriptContext, &obj)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.prototype.reduceRight")); - } - } - - if (scriptContext->GetConfig()->IsES6ToLengthEnabled()) - { - length = (uint64) JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } - else - { - length = JavascriptConversion::ToUInt32(JavascriptOperators::OP_GetLength(obj, scriptContext), scriptContext); - } + JS_REENTRANT(jsReentLock, TryGetArrayAndLength(args[0], scriptContext, _u("Array.prototype.reduceRight"), &pArr, &obj, &length)); if (length.IsSmallIndex()) { - return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetSmallIndex(), args, scriptContext)); } - return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext); + + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::ReduceRightHelper(pArr, nullptr, obj, length.GetBigIndex(), args, scriptContext)); } // Array.prototype.reduceRight as described in ES6.0 (draft 22) Section 22.1.3.19 template Var JavascriptArray::ReduceRightHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1])) { if (typedArrayBase != nullptr) @@ -9667,7 +9410,8 @@ namespace Js for (; k < length && bPresent == false; k++) { index = length - k - 1; - if (!pArr->DirectGetItemAtFull((uint32)index, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull((uint32)index, &element)); + if (!gotItem) { continue; } @@ -9677,14 +9421,14 @@ namespace Js } else if (typedArrayBase) { - Assert(length <= UINT_MAX); - for (; k < length && bPresent == false; k++) + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); + + for (; k < end && bPresent == false; k++) { + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. + index = length - k - 1; - if (!typedArrayBase->HasItem((uint32)index)) - { - continue; - } element = typedArrayBase->DirectGetItem((uint32)index); bPresent = true; accumulator = element; @@ -9695,9 +9439,10 @@ namespace Js for (; k < length && bPresent == false; k++) { index = length - k - 1; - if (JavascriptOperators::HasItem(obj, index)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, index)); + if (hasItem) { - accumulator = JavascriptOperators::GetItem(obj, index, scriptContext); + JS_REENTRANT(jsReentLock, accumulator = JavascriptOperators::GetItem(obj, index, scriptContext)); bPresent = true; } } @@ -9718,36 +9463,38 @@ namespace Js for (; k < length; k++) { index = length - k - 1; - if (!pArr->DirectGetItemAtFull((uint32)index, &element)) + JS_REENTRANT(jsReentLock, BOOL gotItem = pArr->DirectGetItemAtFull((uint32)index, &element)); + if (!gotItem) { continue; } - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(index, scriptContext), - pArr); + JS_REENTRANT(jsReentLock, + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(index, scriptContext), + pArr)); } } else if (typedArrayBase) { - Assert(length <= UINT_MAX); - for (; k < length; k++) + AssertAndFailFast(TypedArrayBase::Is(typedArrayBase)); + uint32 end = (uint32)min(length, (T)typedArrayBase->GetLength()); + + for (; k < end; k++) { - index = length - k - 1; - if (!typedArrayBase->HasItem((uint32) index)) - { - continue; - } + // No need to do HasItem, as it cannot be observable unless 'typedArrayBase' is proxy. And we have established that it is indeed typedarray. + index = length - k - 1; element = typedArrayBase->DirectGetItem((uint32)index); - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(index, scriptContext), - typedArrayBase); + JS_REENTRANT(jsReentLock, + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(index, scriptContext), + typedArrayBase)); } } else @@ -9755,14 +9502,16 @@ namespace Js for (; k < length; k++) { index = length - k - 1; - if (JavascriptOperators::HasItem(obj, index)) + JS_REENTRANT(jsReentLock, BOOL hasItem = JavascriptOperators::HasItem(obj, index)); + if (hasItem) { - element = JavascriptOperators::GetItem(obj, index, scriptContext); - accumulator = CALL_FUNCTION(callBackFn, CallInfo(flags, 5), undefinedValue, - accumulator, - element, - JavascriptNumber::ToVar(index, scriptContext), - obj); + JS_REENTRANT(jsReentLock, + element = JavascriptOperators::GetItem(obj, index, scriptContext), + accumulator = CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(flags, 5), undefinedValue, + accumulator, + element, + JavascriptNumber::ToVar(index, scriptContext), + obj)); } } } @@ -9776,6 +9525,8 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Array.from")); Assert(!(callInfo.Flags & CallFlags_New)); @@ -9833,7 +9584,7 @@ namespace Js RecyclableObject* newObj = nullptr; JavascriptArray* newArr = nullptr; - RecyclableObject* iterator = JavascriptOperators::GetIterator(items, scriptContext, true /* optional */); + JS_REENTRANT(jsReentLock, RecyclableObject* iterator = JavascriptOperators::GetIterator(items, scriptContext, true /* optional */)); if (iterator != nullptr) { @@ -9841,7 +9592,7 @@ namespace Js { Js::Var constructorArgs[] = { constructor }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); - newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext)); + JS_REENTRANT(jsReentLock, newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext))); if (JavascriptArray::Is(newObj)) { @@ -9860,7 +9611,7 @@ namespace Js uint32 k = 0; - JavascriptOperators::DoIteratorStepAndValue(iterator, scriptContext, [&](Var nextValue) { + JS_REENTRANT(jsReentLock, JavascriptOperators::DoIteratorStepAndValue(iterator, scriptContext, [&](Var nextValue) { if (mapping) { Assert(mapFn != nullptr); @@ -9881,20 +9632,19 @@ namespace Js } k++; - }); + })); - JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(k, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(k, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); } else { - Var lenValue = JavascriptOperators::OP_GetLength(items, scriptContext); - int64 len = JavascriptConversion::ToLength(lenValue, scriptContext); + JS_REENTRANT(jsReentLock, int64 len = (int64)OP_GetLength(items, scriptContext)); if (constructor) { Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); - newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext)); + JS_REENTRANT(jsReentLock, newObj = RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext))); if (JavascriptArray::Is(newObj)) { @@ -9926,11 +9676,11 @@ namespace Js if (itemsArr) { - kValue = itemsArr->DirectGetItem(k); + JS_REENTRANT(jsReentLock, kValue = itemsArr->DirectGetItem(k)); } else { - kValue = JavascriptOperators::OP_GetElementI_UInt32(items, k, scriptContext); + JS_REENTRANT(jsReentLock, kValue = JavascriptOperators::OP_GetElementI_UInt32(items, k, scriptContext)); } if (mapping) @@ -9940,7 +9690,7 @@ namespace Js Js::Var mapFnArgs[] = { mapFnThisArg, kValue, JavascriptNumber::ToVar(k, scriptContext) }; Js::CallInfo mapFnCallInfo(Js::CallFlags_Value, _countof(mapFnArgs)); - kValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs)); + JS_REENTRANT(jsReentLock, kValue = mapFn->CallFunction(Js::Arguments(mapFnCallInfo, mapFnArgs))); } if (newArr) @@ -9949,11 +9699,11 @@ namespace Js } else { - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue), scriptContext, k); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue), scriptContext, k)); } } - JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, JavascriptOperators::SetProperty(newObj, newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible)); } return newObj; @@ -9965,6 +9715,7 @@ namespace Js ARGUMENTS(args, callInfo); ScriptContext* scriptContext = function->GetScriptContext(); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); Assert(!(callInfo.Flags & CallFlags_New)); @@ -9973,7 +9724,7 @@ namespace Js JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NullOrUndefined, _u("Array.of")); } - return JavascriptArray::OfHelper(false, args, scriptContext); + JS_REENTRANT_UNLOCK(jsReentLock, return JavascriptArray::OfHelper(false, args, scriptContext)); } Var JavascriptArray::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...) @@ -9988,6 +9739,8 @@ namespace Js // Array.of and %TypedArray%.of as described in ES6.0 (draft 22) Section 22.1.2.2 and 22.2.2.2 Var JavascriptArray::OfHelper(bool isTypedArrayEntryPoint, Arguments& args, ScriptContext* scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + Assert(args.Info.Count > 0); // args.Info.Count cannot equal zero or we would have thrown above so no chance of underflowing @@ -10004,9 +9757,14 @@ namespace Js Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) }; Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs)); - newObj = isTypedArrayEntryPoint ? - TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext) : - JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext); + if (isTypedArrayEntryPoint) + { + JS_REENTRANT(jsReentLock, newObj = TypedArrayBase::TypedArrayCreate(constructor, &Js::Arguments(constructorCallInfo, constructorArgs), len, scriptContext)); + } + else + { + JS_REENTRANT(jsReentLock, newObj = JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext)); + } // If the new object we created is an array, remember that as it will save us time setting properties in the object below if (JavascriptArray::Is(newObj)) @@ -10052,7 +9810,7 @@ namespace Js { Var kValue = args[k + 1]; - newTypedArray->DirectSetItem(k, kValue); + JS_REENTRANT(jsReentLock, newTypedArray->DirectSetItem(k, kValue)); } } else @@ -10060,14 +9818,14 @@ namespace Js for (uint32 k = 0; k < len; k++) { Var kValue = args[k + 1]; - ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue), scriptContext, k); + JS_REENTRANT(jsReentLock, ThrowErrorOnFailure(JavascriptArray::SetArrayLikeObjects(RecyclableObject::FromVar(newObj), k, kValue), scriptContext, k)); } } if (!isTypedArrayEntryPoint) { // Set length if we are in the Array version of the function - JavascriptOperators::OP_SetProperty(newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, nullptr, PropertyOperation_ThrowIfNotExtensible); + JS_REENTRANT(jsReentLock, JavascriptOperators::OP_SetProperty(newObj, Js::PropertyIds::length, JavascriptNumber::ToVar(len, scriptContext), scriptContext, nullptr, PropertyOperation_ThrowIfNotExtensible)); } return newObj; @@ -10178,6 +9936,7 @@ namespace Js template void JavascriptArray::ForEachOwnMissingArrayIndexOfObject(JavascriptArray *baseArray, JavascriptArray *destArray, RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, T destIndex, Fn fn) { + JS_REENTRANCY_LOCK(jsReentLock, baseArray->GetScriptContext()->GetThreadContext()); Assert(DynamicObject::IsAnyArray(obj) || JavascriptOperators::IsObject(obj)); Var oldValue; @@ -10208,7 +9967,7 @@ namespace Js T n = destIndex + (index - startIndex); if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue)) { - fn(index, e.GetItem()); + JS_REENTRANT(jsReentLock, fn(index, e.GetItem())); } } } @@ -10234,9 +9993,10 @@ namespace Js if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue)) { Var value = nullptr; - if (JavascriptOperators::GetOwnItem(obj, index, &value, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetOwnItem(obj, index, &value, scriptContext)); + if (gotItem) { - fn(index, value); + JS_REENTRANT(jsReentLock, fn(index, value)); } } } @@ -10997,6 +10757,7 @@ namespace Js // iterate on the array itself ScriptContext *scriptContext = dstArray->GetScriptContext(); + ArrayElementEnumerator e(srcArray, start, end); while(e.MoveNext()) { @@ -11038,23 +10799,27 @@ namespace Js Var JavascriptArray::SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray *spreadIndices, ScriptContext *scriptContext) { + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); + // At this stage we have an array literal with some arguments to be spread. // First we need to calculate the real size of the final literal. #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray(arrayToSpread); #endif JavascriptArray *array = FromVar(arrayToSpread); - uint32 actualLength = array->GetLength(); + uint32 arrayLength = array->GetLength(); + uint32 actualLength = arrayLength; for (unsigned i = 0; i < spreadIndices->count; ++i) { - actualLength = UInt32Math::Add(actualLength - 1, GetSpreadArgLen(array->DirectGetItem(spreadIndices->elements[i]), scriptContext)); + JS_REENTRANT(jsReentLock, + actualLength = UInt32Math::Add(actualLength - 1, GetSpreadArgLen(array->DirectGetItem(spreadIndices->elements[i]), scriptContext))); } JavascriptArray *result = FromVar(OP_NewScArrayWithMissingValues(actualLength, scriptContext)); // Now we copy each element and expand the spread parameters inline. - for (unsigned i = 0, spreadArrIndex = 0, resultIndex = 0; i < array->GetLength() && resultIndex < actualLength; ++i) + for (unsigned i = 0, spreadArrIndex = 0, resultIndex = 0; i < arrayLength && resultIndex < actualLength; ++i) { uint32 spreadIndex = spreadIndices->elements[spreadArrIndex]; // The index of the next element to be spread. @@ -11063,14 +10828,16 @@ namespace Js if (JavascriptArray::Is(instance)) { JavascriptArray *arr = JavascriptArray::FromVar(instance); - return arr->IsCrossSiteObject() || arr->IsFillFromPrototypes(); + JS_REENTRANT_UNLOCK(jsReentLock, return arr->IsCrossSiteObject() || arr->IsFillFromPrototypes()); } return false; }; // Designed to have interchangeable arguments with CopyAnyArrayElementsToVar. - auto slowCopy = [&scriptContext, &needArraySlowCopy](JavascriptArray *dstArray, unsigned dstIndex, Var srcArray, uint32 start, uint32 end) { + auto slowCopy = [&scriptContext, &needArraySlowCopy + ](JavascriptArray *dstArray, unsigned dstIndex, Var srcArray, uint32 start, uint32 end) { Assert(needArraySlowCopy(srcArray) || ArgumentsObject::Is(srcArray) || TypedArrayBase::Is(srcArray) || JavascriptString::Is(srcArray)); + JS_REENTRANCY_LOCK(jsReentLock, scriptContext->GetThreadContext()); RecyclableObject *propertyObject; if (!JavascriptOperators::GetPropertyObject(srcArray, scriptContext, &propertyObject)) @@ -11081,7 +10848,8 @@ namespace Js for (uint32 j = start; j < end; j++) { Var element; - if (!JavascriptOperators::GetItem(srcArray, propertyObject, j, &element, scriptContext)) + JS_REENTRANT(jsReentLock, BOOL gotItem = JavascriptOperators::GetItem(srcArray, propertyObject, j, &element, scriptContext)); + if (!gotItem) { // Copy across missing values as undefined as per 12.2.5.2 SpreadElement : ... AssignmentExpression 5f. element = scriptContext->GetLibrary()->GetUndefined(); @@ -11100,7 +10868,7 @@ namespace Js } else { - CopyAnyArrayElementsToVar(result, resultIndex, array, i, spreadIndex); + JS_REENTRANT(jsReentLock, CopyAnyArrayElementsToVar(result, resultIndex, array, i, spreadIndex)); } resultIndex += spreadIndex - i; i = spreadIndex - 1; @@ -11112,17 +10880,17 @@ namespace Js Assert(spreadArrIndex == spreadIndices->count - 1); if (needArraySlowCopy(array)) { - slowCopy(result, resultIndex, array, i, array->GetLength()); + slowCopy(result, resultIndex, array, i, arrayLength); } else { - CopyAnyArrayElementsToVar(result, resultIndex, array, i, array->GetLength()); + JS_REENTRANT(jsReentLock, CopyAnyArrayElementsToVar(result, resultIndex, array, i, arrayLength)); } break; } else { - Var instance = array->DirectGetItem(i); + JS_REENTRANT(jsReentLock, Var instance = array->DirectGetItem(i)); if (SpreadArgument::Is(instance)) { @@ -11137,36 +10905,8 @@ namespace Js } else { - AssertMsg(JavascriptArray::Is(instance) || TypedArrayBase::Is(instance), "Only SpreadArgument, TypedArray, and JavascriptArray should be listed as spread arguments"); - - // We first try to interpret the spread parameter as a JavascriptArray. - JavascriptArray *arr = nullptr; - if (JavascriptArray::Is(instance)) - { - arr = JavascriptArray::FromVar(instance); - } - - if (arr != nullptr) - { - if (arr->GetLength() > 0) - { - if (needArraySlowCopy(arr)) - { - slowCopy(result, resultIndex, arr, 0, arr->GetLength()); - } - else - { - CopyAnyArrayElementsToVar(result, resultIndex, arr, 0, arr->GetLength()); - } - resultIndex += arr->GetLength(); - } - } - else - { - uint32 len = GetSpreadArgLen(instance, scriptContext); - slowCopy(result, resultIndex, instance, 0, len); - resultIndex += len; - } + Assert(JavascriptOperators::IsUndefinedObject(instance)); + result->DirectSetItemAt(resultIndex++, instance); } if (spreadArrIndex < spreadIndices->count - 1) @@ -11175,6 +10915,7 @@ namespace Js } } } + AssertMsg(arrayLength == array->GetLength(), "Array's length should not have changed"); return result; } @@ -11654,7 +11395,6 @@ namespace Js { *pIsBuiltinArrayCtor = false; } - return nullptr; } if (constructor == scriptContext->GetLibrary()->GetNull()) diff --git a/lib/Runtime/Library/JavascriptArray.h b/lib/Runtime/Library/JavascriptArray.h index f5c62530290..f0c46ef8b82 100644 --- a/lib/Runtime/Library/JavascriptArray.h +++ b/lib/Runtime/Library/JavascriptArray.h @@ -494,7 +494,24 @@ namespace Js static Var ReduceRightHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext); static Var OfHelper(bool isTypedArrayEntryPoint, Arguments& args, ScriptContext* scriptContext); - static uint32 GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext); + template + static T GetFromIndex(Var arg, T length, ScriptContext *scriptContext, bool addWithLength = true) + { + T fromIndex = 0; + + double value = TaggedInt::Is(arg) ? (double)TaggedInt::ToInt64(arg) : JavascriptConversion::ToInteger(arg, scriptContext); + + if (value < 0) + { + fromIndex = addWithLength ? (T)max(0i64, (int64)(value + length)) : 0; + } + else + { + fromIndex = (T)min(value, (double)length); + } + return fromIndex; + } + protected: template bool IsMissingHeadSegmentItemImpl(const uint32 index) const; SegmentBTreeRoot * GetSegmentMap() const; @@ -524,7 +541,6 @@ namespace Js template static void GrowArrayHeadHelperForUnshift(JavascriptArray* pArr, uint32 unshiftElements, ScriptContext * scriptContext); - static uint64 GetFromIndex(Var arg, uint64 length, ScriptContext *scriptContext); static int64 GetFromLastIndex(Var arg, int64 length, ScriptContext *scriptContext); static JavascriptString* JoinToString(Var value, ScriptContext* scriptContext); static JavascriptString* JoinHelper(Var thisArg, JavascriptString* separatorStr, ScriptContext* scriptContext); @@ -545,11 +561,14 @@ namespace Js static void ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment *seg, SparseArraySegment **prev, uint32 start, uint32 deleteLen, Var* insertArgs, uint32 insertLen, Recycler *recycler); template - static RecyclableObject* ObjectSpliceHelper(RecyclableObject* pObj, uint32 len, uint32 start, uint32 deleteLen, + static RecyclableObject* ObjectSpliceHelper(RecyclableObject* pObj, T len, T start, T deleteLen, Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj = nullptr); static JavascriptString* ToLocaleStringHelper(Var value, ScriptContext* scriptContext); static Js::JavascriptArray* CreateNewArrayHelper(uint32 len, bool isIntArray, bool isFloatArray, Js::JavascriptArray *baseArray, ScriptContext* scriptContext); + static Var TryArraySplice(JavascriptArray* pArr, uint32 start, uint32 len, uint32 deleteLen, + Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext); + void FillFromPrototypes(uint32 startIndex, uint32 endIndex); bool IsFillFromPrototypes(); void GetArrayTypeAndConvert(bool* isIntArray, bool* isFloatArray); @@ -798,9 +817,13 @@ namespace Js static void ThrowErrorOnFailure(BOOL succeeded, ScriptContext* scriptContext, uint32 index); static void ThrowErrorOnFailure(BOOL succeeded, ScriptContext* scriptContext, BigIndex index); + template + static void TryGetArrayAndLength(Var arg, ScriptContext *scriptContext, PCWSTR methodName, __out JavascriptArray** array, __out RecyclableObject** obj, __out T * length); + static uint64 OP_GetLength(Var obj, ScriptContext *scriptContext); + public: template - static void Unshift(RecyclableObject* obj, const T& toIndex, uint32 start, P end, ScriptContext* scriptContext); + static void Unshift(RecyclableObject* obj, const T& toIndex, P start, P end, ScriptContext* scriptContext); template class ItemTrace @@ -842,6 +865,12 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VtableHelper(); + } }; // Ideally we would propagate the throw flag setting of true from the array operations down to the [[Delete]]/[[Put]]/... methods. But that is a big change @@ -1014,6 +1043,12 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VtableHelper(); + } }; #if ENABLE_COPYONACCESS_ARRAY @@ -1064,6 +1099,13 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VtableHelper(); + } + }; #endif @@ -1169,6 +1211,13 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VtableHelper(); + } + }; template <> diff --git a/lib/Runtime/Library/JavascriptBoolean.h b/lib/Runtime/Library/JavascriptBoolean.h index 503b4a610ee..e94ea10a075 100644 --- a/lib/Runtime/Library/JavascriptBoolean.h +++ b/lib/Runtime/Library/JavascriptBoolean.h @@ -48,6 +48,12 @@ namespace Js virtual BOOL ToPrimitive(JavascriptHint hint, Var* value, ScriptContext* requestContext) override {AssertMsg(false, "Boolean ToPrimitive should not be called"); *value = this; return true;} virtual RecyclableObject * CloneToScriptContext(ScriptContext* requestContext) override; + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableJavascriptBoolean; + } + private: static BOOL Equals(JavascriptBoolean* left, Var right, BOOL* value, ScriptContext * requestContext); static Var TryInvokeRemotelyOrThrow(JavascriptMethod entryPoint, ScriptContext * scriptContext, Arguments & args, int32 errorCode, PCWSTR varName); diff --git a/lib/Runtime/Library/JavascriptDate.cpp b/lib/Runtime/Library/JavascriptDate.cpp index f6debeb0824..d82b145aa93 100644 --- a/lib/Runtime/Library/JavascriptDate.cpp +++ b/lib/Runtime/Library/JavascriptDate.cpp @@ -1314,7 +1314,7 @@ namespace Js JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_NeedFunction, scriptContext->GetPropertyName(PropertyIds::toISOString)->GetBuffer()); } RecyclableObject* toISOFunc = RecyclableObject::FromVar(toISO); - return CALL_FUNCTION(toISOFunc, CallInfo(1), thisObj); + return CALL_FUNCTION(scriptContext->GetThreadContext(), toISOFunc, CallInfo(1), thisObj); } Var JavascriptDate::EntryToLocaleDateString(RecyclableObject* function, CallInfo callInfo, ...) diff --git a/lib/Runtime/Library/JavascriptFunction.cpp b/lib/Runtime/Library/JavascriptFunction.cpp index acf23f20cbf..324b0f9f14d 100644 --- a/lib/Runtime/Library/JavascriptFunction.cpp +++ b/lib/Runtime/Library/JavascriptFunction.cpp @@ -978,9 +978,9 @@ namespace Js destArgs.Values[0] = args[0]; // Iterate over the arguments, spreading inline. We skip 'this'. - Var undefined = scriptContext->GetLibrary()->GetUndefined(); - for (unsigned i = 1, argsIndex = 1, spreadArgIndex = 0; i < callInfo.Count; ++i) + uint32 argsIndex = 1; + for (unsigned i = 1, spreadArgIndex = 0; i < callInfo.Count; ++i) { uint32 spreadIndex = spreadIndices->elements[spreadArgIndex]; // Next index to be spread. if (i < spreadIndex) @@ -1012,71 +1012,20 @@ namespace Js { SpreadArgument* spreadedArgs = SpreadArgument::FromVar(instance); uint size = spreadedArgs->GetArgumentSpreadCount(); - const Var * spreadBuffer = spreadedArgs->GetArgumentSpread(); - js_memcpy_s(destArgs.Values + argsIndex, - size * sizeof(Var), - spreadBuffer, - size * sizeof(Var)); - argsIndex += size; + if (size > 0) + { + const Var * spreadBuffer = spreadedArgs->GetArgumentSpread(); + js_memcpy_s(destArgs.Values + argsIndex, + size * sizeof(Var), + spreadBuffer, + size * sizeof(Var)); + argsIndex += size; + } } else { - AssertMsg(JavascriptArray::Is(instance) || TypedArrayBase::Is(instance), "Only SpreadArgument, TypedArray, and JavascriptArray should be listed as spread arguments"); - - // We first try to interpret the spread parameter as a JavascriptArray. - JavascriptArray *arr = nullptr; - if (JavascriptArray::Is(instance)) - { - arr = JavascriptArray::FromVar(instance); - } - - if (arr != nullptr && !arr->IsCrossSiteObject()) - { - uint32 length = arr->GetLength(); - // CONSIDER: Optimize by creating a JavascriptArray routine which allows - // memcpy-like semantics in optimal situations (no gaps, etc.) - if (argsIndex + length > destArgs.Info.Count) - { - AssertMsg(false, "The array length has changed since we allocated the destArgs buffer?"); - Throw::FatalInternalError(); - } - - for (uint32 j = 0; j < length; j++) - { - Var element; - if (!arr->DirectGetItemAtFull(j, &element)) - { - element = undefined; - } - destArgs.Values[argsIndex++] = element; - } - } - else - { - // Emulate %ArrayPrototype%.values() iterator; basically iterate from 0 to length - RecyclableObject *propertyObject; - if (!JavascriptOperators::GetPropertyObject(instance, scriptContext, &propertyObject)) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_InvalidSpreadArgument); - } - - uint32 len = JavascriptArray::GetSpreadArgLen(instance, scriptContext); - if (argsIndex + len > destArgs.Info.Count) - { - AssertMsg(false, "The array length has changed since we allocated the destArgs buffer?"); - Throw::FatalInternalError(); - } - - for (uint j = 0; j < len; j++) - { - Var element; - if (!JavascriptOperators::GetItem(instance, propertyObject, j, &element, scriptContext)) - { - element = undefined; - } - destArgs.Values[argsIndex++] = element; - } - } + Assert(JavascriptOperators::IsUndefinedObject(instance)); + destArgs.Values[argsIndex++] = instance; } if (spreadArgIndex < spreadIndices->count - 1) @@ -1085,6 +1034,12 @@ namespace Js } } } + if (argsIndex > destArgs.Info.Count) + { + AssertMsg(false, "The array length has changed since we allocated the destArgs buffer?"); + Throw::FatalInternalError(); + } + } Var JavascriptFunction::CallSpreadFunction(RecyclableObject* function, Arguments args, const Js::AuxArray *spreadIndices) @@ -1403,11 +1358,13 @@ void __cdecl _alloca_probe_16() unsigned count = args.Info.Count; if (count == 0) { - varResult = CALL_ENTRYPOINT(entryPoint, (JavascriptFunction*)function, args.Info); + varResult = CALL_ENTRYPOINT(function->GetScriptContext()->GetThreadContext(), + entryPoint, (JavascriptFunction*)function, args.Info); } else if (count == 1) { - varResult = CALL_ENTRYPOINT(entryPoint, (JavascriptFunction*)function, args.Info, args.Values[0]); + varResult = CALL_ENTRYPOINT(function->GetScriptContext()->GetThreadContext(), + entryPoint, (JavascriptFunction*)function, args.Info, args.Values[0]); } else { @@ -1654,13 +1611,14 @@ void __cdecl _alloca_probe_16() Assert(functionInfo); + ScriptFunctionWithInlineCache * funcObjectWithInlineCache = ScriptFunctionWithInlineCache::Is(*functionRef) ? ScriptFunctionWithInlineCache::FromVar(*functionRef) : nullptr; if (functionInfo->IsDeferredParseFunction()) { - if (ScriptFunctionWithInlineCache::Is(*functionRef)) + if (funcObjectWithInlineCache) { // If inline caches were populated from a function body that has been redeferred, the caches have been cleaned up, // so clear the pointers. REVIEW: Is this a perf loss in some cases? - ScriptFunctionWithInlineCache::FromVar(*functionRef)->ClearBorrowedInlineCacheOnFunctionObject(); + funcObjectWithInlineCache->ClearBorrowedInlineCacheOnFunctionObject(); } funcBody = functionInfo->Parse(functionRef); @@ -1688,13 +1646,16 @@ void __cdecl _alloca_probe_16() JavascriptMethod thunkEntryPoint = (*functionRef)->UpdateUndeferredBody(funcBody); - if (ScriptFunctionWithInlineCache::Is(*functionRef)) + if (funcObjectWithInlineCache && !funcObjectWithInlineCache->GetHasOwnInlineCaches()) { - ScriptFunctionWithInlineCache * funcObjectWithInlineCache = ScriptFunctionWithInlineCache::FromVar(*functionRef); - if (!funcObjectWithInlineCache->GetHasOwnInlineCaches()) - { - funcObjectWithInlineCache->SetInlineCachesFromFunctionBody(); - } + // If the function object needs to use the inline caches from the function body, point them to the + // function body's caches. This is required in two redeferral cases: + // + // 1. We might have cleared the caches on the function object (ClearBorrowedInlineCacheOnFunctionObject) + // above if the function body was redeferred. + // 2. Another function object could have been called before and undeferred the function body, thereby creating + // new inline caches. This function object would still be pointing to the old ones and needs updating. + funcObjectWithInlineCache->SetInlineCachesFromFunctionBody(); } return thunkEntryPoint; @@ -1885,6 +1846,15 @@ void __cdecl _alloca_probe_16() bool isSIB = false; // Read first byte - check for prefix BYTE* beginPc = pc; + +#if _CONTROL_FLOW_GUARD_SHADOW_STACK + // skip first byte if it's 0x64 (fs-based mem access) + if (*pc == 0x64) + { + pc++; + } +#endif + if (((*pc) == 0x0F2) || ((*pc) == 0x0F3)) { //MOVSD or MOVSS @@ -2654,7 +2624,7 @@ void __cdecl _alloca_probe_16() if (scriptContext->GetThreadContext()->RecordImplicitException()) { JavascriptFunction* accessor = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction(); - *value = CALL_FUNCTION(accessor, CallInfo(1), originalInstance); + *value = CALL_FUNCTION(scriptContext->GetThreadContext(), accessor, CallInfo(1), originalInstance); } return true; } @@ -2732,7 +2702,7 @@ void __cdecl _alloca_probe_16() if (scriptContext->GetThreadContext()->RecordImplicitException()) { JavascriptFunction* accessor = requestContext->GetLibrary()->GetThrowTypeErrorRestrictedPropertyAccessorFunction(); - *value = CALL_FUNCTION(accessor, CallInfo(1), originalInstance); + *value = CALL_FUNCTION(scriptContext->GetThreadContext(), accessor, CallInfo(1), originalInstance); } return true; } diff --git a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp index 8b67108b927..a177b3133ec 100644 --- a/lib/Runtime/Library/JavascriptGeneratorFunction.cpp +++ b/lib/Runtime/Library/JavascriptGeneratorFunction.cpp @@ -158,7 +158,7 @@ namespace Js try { - CALL_FUNCTION(executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject); + CALL_FUNCTION(scriptContext->GetThreadContext(), executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject); } catch (const JavascriptException& err) { diff --git a/lib/Runtime/Library/JavascriptGeneratorFunction.h b/lib/Runtime/Library/JavascriptGeneratorFunction.h index 7555d6ec56b..762ed3555c3 100644 --- a/lib/Runtime/Library/JavascriptGeneratorFunction.h +++ b/lib/Runtime/Library/JavascriptGeneratorFunction.h @@ -79,6 +79,12 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableJavascriptGeneratorFunction; + } }; class JavascriptAsyncFunction : public JavascriptGeneratorFunction @@ -105,6 +111,12 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableJavascriptAsyncFunction; + } }; class GeneratorVirtualScriptFunction : public ScriptFunction diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp index 71442ac474d..8b3087ce710 100644 --- a/lib/Runtime/Library/JavascriptLibrary.cpp +++ b/lib/Runtime/Library/JavascriptLibrary.cpp @@ -1126,8 +1126,11 @@ namespace Js moduleTypeDisplayString = CreateStringFromCppLiteral(_u("Module")); variantDateTypeDisplayString = CreateStringFromCppLiteral(_u("date")); promiseResolveFunction = nullptr; + promiseThenFunction = nullptr; generatorNextFunction = nullptr; generatorThrowFunction = nullptr; + jsonStringifyFunction = nullptr; + objectFreezeFunction = nullptr; #ifdef ENABLE_SIMDJS if (GetScriptContext()->GetConfig()->IsSimdjsEnabled()) @@ -2262,7 +2265,7 @@ namespace Js } return promiseResolveFunction; } - + void JavascriptLibrary::InitializePromisePrototype(DynamicObject* promisePrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { typeHandler->Convert(promisePrototype, mode, 4); @@ -2278,12 +2281,22 @@ namespace Js } scriptContext->SetBuiltInLibraryFunction(JavascriptPromise::EntryInfo::Catch.GetOriginalEntryPoint(), library->AddFunctionToLibraryObject(promisePrototype, PropertyIds::catch_, &JavascriptPromise::EntryInfo::Catch, 1)); - scriptContext->SetBuiltInLibraryFunction(JavascriptPromise::EntryInfo::Then.GetOriginalEntryPoint(), - library->AddFunctionToLibraryObject(promisePrototype, PropertyIds::then, &JavascriptPromise::EntryInfo::Then, 2)); + library->AddMember(promisePrototype, PropertyIds::then, library->EnsurePromiseThenFunction(), PropertyBuiltInMethodDefaults); + scriptContext->SetBuiltInLibraryFunction(JavascriptPromise::EntryInfo::Then.GetOriginalEntryPoint(), library->EnsurePromiseThenFunction()); promisePrototype->SetHasNoEnumerableProperties(true); } + JavascriptFunction* JavascriptLibrary::EnsurePromiseThenFunction() + { + if (promiseThenFunction == nullptr) + { + promiseThenFunction = DefaultCreateFunction(&JavascriptPromise::EntryInfo::Then, 2, nullptr, nullptr, PropertyIds::then); + } + + return promiseThenFunction; + } + void JavascriptLibrary::InitializeGeneratorFunctionConstructor(DynamicObject* generatorFunctionConstructor, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { typeHandler->Convert(generatorFunctionConstructor, mode, 3); @@ -3390,57 +3403,60 @@ namespace Js defaultPropertyDescriptor.SetConfigurable(false); #if !defined(_M_X64_OR_ARM64) - vtableAddresses[VTableValue::VtableJavascriptNumber] = VirtualTableInfo::Address; + + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptNumber); #else vtableAddresses[VTableValue::VtableJavascriptNumber] = 0; #endif - vtableAddresses[VTableValue::VtableDynamicObject] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableDynamicObject); vtableAddresses[VTableValue::VtableInvalid] = Js::ScriptContextOptimizationOverrideInfo::InvalidVtable; - vtableAddresses[VTableValue::VtablePropertyString] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableJavascriptBoolean] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableJavascriptArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt8Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint8Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint8ClampedArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt16Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint16Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt32Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint32Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableFloat32Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableFloat64Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt64Array] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint64Array] = VirtualTableInfo::Address; - - vtableAddresses[VTableValue::VtableInt8VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint8VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint8ClampedVirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt16VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint16VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableInt32VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableUint32VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableFloat32VirtualArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableFloat64VirtualArray] = VirtualTableInfo::Address; - - vtableAddresses[VTableValue::VtableBoolArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableCharArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableNativeIntArray] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtablePropertyString); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptBoolean); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt8Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint8Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint8ClampedArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt16Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint16Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt32Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint32Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableFloat32Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableFloat64Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt64Array); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint64Array); + + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt8VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint8VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint8ClampedVirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt16VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint16VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableInt32VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableUint32VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableFloat32VirtualArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableFloat64VirtualArray); + + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableBoolArray); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableCharArray); + + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableNativeIntArray); #if ENABLE_COPYONACCESS_ARRAY - vtableAddresses[VTableValue::VtableCopyOnAccessNativeIntArray] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableCopyOnAccessNativeIntArray); #endif - vtableAddresses[VTableValue::VtableNativeFloatArray] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableNativeFloatArray); + // don't validate vtable for VtableJavascriptNativeIntArray because its vtable is used for VtableNativeIntArray vtableAddresses[VTableValue::VtableJavascriptNativeIntArray] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableJavascriptRegExp] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableStackScriptFunction] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableScriptFunction] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableJavascriptGeneratorFunction] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableJavascriptAsyncFunction] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableConcatStringMulti] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableCompoundString] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptRegExp); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableStackScriptFunction); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableScriptFunction); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptGeneratorFunction); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableJavascriptAsyncFunction); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableConcatStringMulti); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableCompoundString); // SIMD_JS #ifdef ENABLE_SIMDJS - vtableAddresses[VTableValue::VtableSimd128F4] = VirtualTableInfo::Address; - vtableAddresses[VTableValue::VtableSimd128I4] = VirtualTableInfo::Address; + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableSimd128F4); + VirtualTableRecorder::RecordVirtualTableAddress(vtableAddresses, VTableValue::VtableSimd128I4); #endif for (TypeId typeId = static_cast(0); typeId < TypeIds_Limit; typeId = static_cast(typeId + 1)) @@ -4105,8 +4121,8 @@ namespace Js library->AddFunctionToLibraryObject(objectConstructor, PropertyIds::create, &JavascriptObject::EntryInfo::Create, 2); scriptContext->SetBuiltInLibraryFunction(JavascriptObject::EntryInfo::Seal.GetOriginalEntryPoint(), library->AddFunctionToLibraryObject(objectConstructor, PropertyIds::seal, &JavascriptObject::EntryInfo::Seal, 1)); - scriptContext->SetBuiltInLibraryFunction(JavascriptObject::EntryInfo::Freeze.GetOriginalEntryPoint(), - library->AddFunctionToLibraryObject(objectConstructor, PropertyIds::freeze, &JavascriptObject::EntryInfo::Freeze, 1)); + library->AddMember(objectConstructor, PropertyIds::freeze, library->EnsureObjectFreezeFunction(), PropertyBuiltInMethodDefaults); + scriptContext->SetBuiltInLibraryFunction(JavascriptObject::EntryInfo::Freeze.GetOriginalEntryPoint(), library->EnsureObjectFreezeFunction()); scriptContext->SetBuiltInLibraryFunction(JavascriptObject::EntryInfo::PreventExtensions.GetOriginalEntryPoint(), library->AddFunctionToLibraryObject(objectConstructor, PropertyIds::preventExtensions, &JavascriptObject::EntryInfo::PreventExtensions, 1)); scriptContext->SetBuiltInLibraryFunction(JavascriptObject::EntryInfo::IsSealed.GetOriginalEntryPoint(), @@ -4144,6 +4160,15 @@ namespace Js objectConstructor->SetHasNoEnumerableProperties(true); } + JavascriptFunction* JavascriptLibrary::EnsureObjectFreezeFunction() + { + if (objectFreezeFunction == nullptr) + { + objectFreezeFunction = DefaultCreateFunction(&JavascriptObject::EntryInfo::Freeze, 1, nullptr, nullptr, PropertyIds::freeze); + } + return objectFreezeFunction; + } + void JavascriptLibrary::InitializeObjectPrototype(DynamicObject* objectPrototype, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { JavascriptLibrary* library = objectPrototype->GetLibrary(); @@ -4906,8 +4931,8 @@ namespace Js { typeHandler->Convert(JSONObject, mode, 3); JavascriptLibrary* library = JSONObject->GetLibrary(); - JSONObject->GetScriptContext()->SetBuiltInLibraryFunction(JSON::EntryInfo::Stringify.GetOriginalEntryPoint(), - library->AddFunctionToLibraryObject(JSONObject, PropertyIds::stringify, &JSON::EntryInfo::Stringify, 3)); + library->AddMember(JSONObject, PropertyIds::stringify, library->EnsureJSONStringifyFunction()); + JSONObject->GetScriptContext()->SetBuiltInLibraryFunction(JSON::EntryInfo::Stringify.GetOriginalEntryPoint(), library->EnsureJSONStringifyFunction()); library->AddFunctionToLibraryObject(JSONObject, PropertyIds::parse, &JSON::EntryInfo::Parse, 2); if (JSONObject->GetScriptContext()->GetConfig()->IsES6ToStringTagEnabled()) @@ -4918,6 +4943,15 @@ namespace Js JSONObject->SetHasNoEnumerableProperties(true); } + JavascriptFunction* JavascriptLibrary::EnsureJSONStringifyFunction() + { + if (jsonStringifyFunction == nullptr) + { + jsonStringifyFunction = DefaultCreateFunction(&JSON::EntryInfo::Stringify, 3, nullptr, nullptr, PropertyIds::stringify); + } + return jsonStringifyFunction; + } + #if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_PROJECTION) void JavascriptLibrary::InitializeEngineInterfaceObject(DynamicObject* engineInterface, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode) { @@ -6205,6 +6239,9 @@ namespace Js default: baseErrorType = errorType; break; + case kjstEvalError: + baseErrorType = evalErrorType; + break; case kjstRangeError: baseErrorType = rangeErrorType; break; diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h index c997ac15a0a..467b40fdfbe 100644 --- a/lib/Runtime/Library/JavascriptLibrary.h +++ b/lib/Runtime/Library/JavascriptLibrary.h @@ -381,7 +381,6 @@ namespace Js JavascriptFunction* arrayPrototypeToLocaleStringFunction; JavascriptFunction* identityFunction; JavascriptFunction* throwerFunction; - JavascriptFunction* promiseResolveFunction; JavascriptFunction* generatorNextFunction; JavascriptFunction* generatorThrowFunction; @@ -569,29 +568,12 @@ namespace Js ScriptContext* GetScriptContext() const { return scriptContext; } Recycler * GetRecycler() const { return recycler; } - Var GetPI() { return pi; } - Var GetNaN() { return nan; } - Var GetNegativeInfinite() { return negativeInfinite; } - Var GetPositiveInfinite() { return positiveInfinite; } - Var GetMaxValue() { return maxValue; } - Var GetMinValue() { return minValue; } - Var GetNegativeZero() { return negativeZero; } - RecyclableObject* GetUndefined() { return undefinedValue; } - RecyclableObject* GetNull() { return nullValue; } - JavascriptBoolean* GetTrue() { return booleanTrue; } - JavascriptBoolean* GetFalse() { return booleanFalse; } Var GetTrueOrFalse(BOOL value) { return value ? booleanTrue : booleanFalse; } - JavascriptSymbol* GetSymbolHasInstance() { return symbolHasInstance; } - JavascriptSymbol* GetSymbolIsConcatSpreadable() { return symbolIsConcatSpreadable; } - JavascriptSymbol* GetSymbolIterator() { return symbolIterator; } JavascriptSymbol* GetSymbolMatch() { return symbolMatch; } JavascriptSymbol* GetSymbolReplace() { return symbolReplace; } JavascriptSymbol* GetSymbolSearch() { return symbolSearch; } JavascriptSymbol* GetSymbolSplit() { return symbolSplit; } JavascriptSymbol* GetSymbolSpecies() { return symbolSpecies; } - JavascriptSymbol* GetSymbolToPrimitive() { return symbolToPrimitive; } - JavascriptSymbol* GetSymbolToStringTag() { return symbolToStringTag; } - JavascriptSymbol* GetSymbolUnscopables() { return symbolUnscopables; } JavascriptString* GetNullString() { return nullString; } JavascriptString* GetEmptyString() const; JavascriptString* GetWhackString() { return whackString; } @@ -639,32 +621,9 @@ namespace Js JavascriptString* GetSymbolTypeDisplayString() const { return symbolTypeDisplayString; } JavascriptString* GetDebuggerDeadZoneBlockVariableString() { Assert(debuggerDeadZoneBlockVariableString); return debuggerDeadZoneBlockVariableString; } JavascriptRegExp* CreateEmptyRegExp(); - JavascriptFunction* GetObjectConstructor() const {return objectConstructor; } - JavascriptFunction* GetBooleanConstructor() const {return booleanConstructor; } - JavascriptFunction* GetDateConstructor() const {return dateConstructor; } - JavascriptFunction* GetFunctionConstructor() const {return functionConstructor; } - JavascriptFunction* GetNumberConstructor() const {return numberConstructor; } - JavascriptRegExpConstructor* GetRegExpConstructor() const {return regexConstructor; } - JavascriptFunction* GetStringConstructor() const {return stringConstructor; } - JavascriptFunction* GetArrayBufferConstructor() const {return arrayBufferConstructor; } - JavascriptFunction* GetErrorConstructor() const { return errorConstructor; } - JavascriptFunction* GetInt8ArrayConstructor() const {return Int8ArrayConstructor; } - JavascriptFunction* GetUint8ArrayConstructor() const {return Uint8ArrayConstructor; } - JavascriptFunction* GetInt16ArrayConstructor() const {return Int16ArrayConstructor; } - JavascriptFunction* GetUint16ArrayConstructor() const {return Uint16ArrayConstructor; } - JavascriptFunction* GetInt32ArrayConstructor() const {return Int32ArrayConstructor; } - JavascriptFunction* GetUint32ArrayConstructor() const {return Uint32ArrayConstructor; } - JavascriptFunction* GetFloat32ArrayConstructor() const {return Float32ArrayConstructor; } - JavascriptFunction* GetFloat64ArrayConstructor() const {return Float64ArrayConstructor; } - JavascriptFunction* GetWeakMapConstructor() const {return weakMapConstructor; } - JavascriptFunction* GetMapConstructor() const {return mapConstructor; } - JavascriptFunction* GetSetConstructor() const {return setConstructor; } - JavascriptFunction* GetSymbolConstructor() const {return symbolConstructor; } JavascriptFunction* GetEvalFunctionObject() { return evalFunctionObject; } JavascriptFunction* GetArrayPrototypeValuesFunction() { return EnsureArrayPrototypeValuesFunction(); } JavascriptFunction* GetArrayIteratorPrototypeBuiltinNextFunction() { return arrayIteratorPrototypeBuiltinNextFunction; } - DynamicObject* GetMathObject() const {return mathObject; } - DynamicObject* GetJSONObject() const {return JSONObject; } DynamicObject* GetReflectObject() const { return reflectObject; } const PropertyDescriptor* GetDefaultPropertyDescriptor() const { return &defaultPropertyDescriptor; } DynamicObject* GetMissingPropertyHolder() const { return missingPropertyHolder; } @@ -723,7 +682,6 @@ namespace Js #endif #ifdef ENABLE_INTL_OBJECT - DynamicObject* GetINTLObject() const { return IntlObject; } void ResetIntlObject(); void EnsureIntlObjectReady(); template @@ -1085,8 +1043,11 @@ namespace Js RecyclableObject* CreateThrowErrorObject(JavascriptError* error); JavascriptFunction* EnsurePromiseResolveFunction(); + JavascriptFunction* EnsurePromiseThenFunction(); JavascriptFunction* EnsureGeneratorNextFunction(); JavascriptFunction* EnsureGeneratorThrowFunction(); + JavascriptFunction* EnsureJSONStringifyFunction(); + JavascriptFunction* EnsureObjectFreezeFunction(); void SetCrossSiteForSharedFunctionType(JavascriptFunction * function); diff --git a/lib/Runtime/Library/JavascriptLibraryBase.h b/lib/Runtime/Library/JavascriptLibraryBase.h index d93c33d4ff3..fb5ec30a6d1 100644 --- a/lib/Runtime/Library/JavascriptLibraryBase.h +++ b/lib/Runtime/Library/JavascriptLibraryBase.h @@ -77,6 +77,10 @@ namespace Js JavascriptFunction* GetSyntaxErrorConstructor() const { return syntaxErrorConstructor; } JavascriptFunction* GetTypeErrorConstructor() const { return typeErrorConstructor; } JavascriptFunction* GetURIErrorConstructor() const { return uriErrorConstructor; } + JavascriptFunction* GetPromiseResolve() const { return promiseResolveFunction; } + JavascriptFunction* GetPromiseThen() const { return promiseThenFunction; } + JavascriptFunction* GetJSONStringify() const { return jsonStringifyFunction; } + JavascriptFunction* GetObjectFreeze() const { return objectFreezeFunction; } DynamicObject* GetMathObject() { return mathObject; } DynamicObject* GetJSONObject() { return JSONObject; } @@ -179,6 +183,10 @@ namespace Js JavascriptFunction* __proto__getterFunction; JavascriptFunction* __proto__setterFunction; JavascriptFunction* arrayIteratorPrototypeBuiltinNextFunction; + JavascriptFunction* promiseResolveFunction; + JavascriptFunction* promiseThenFunction; + JavascriptFunction* jsonStringifyFunction; + JavascriptFunction* objectFreezeFunction; DynamicObject* mathObject; // SIMD_JS DynamicObject* simdObject; diff --git a/lib/Runtime/Library/JavascriptMap.cpp b/lib/Runtime/Library/JavascriptMap.cpp index 988b9ec6e97..6613ea787ff 100644 --- a/lib/Runtime/Library/JavascriptMap.cpp +++ b/lib/Runtime/Library/JavascriptMap.cpp @@ -110,7 +110,7 @@ namespace Js } // CONSIDER: if adder is the default built-in, fast path it and skip the JS call? - CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 3), mapObject, key, value); + CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 3), mapObject, key, value); }); } @@ -189,7 +189,7 @@ namespace Js Var key = iterator.Current().Key(); Var value = iterator.Current().Value(); - CALL_FUNCTION(callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, key, map); + CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, key, map); } return scriptContext->GetLibrary()->GetUndefined(); diff --git a/lib/Runtime/Library/JavascriptNumber.h b/lib/Runtime/Library/JavascriptNumber.h index 1df7d2f962e..fd9a9d3d328 100644 --- a/lib/Runtime/Library/JavascriptNumber.h +++ b/lib/Runtime/Library/JavascriptNumber.h @@ -146,8 +146,13 @@ namespace Js static JavascriptNumber* NewUninitialized(Recycler * recycler); static Var BoxStackInstance(Var instance, ScriptContext* scriptContext); static Var BoxStackNumber(Var instance, ScriptContext* scriptContext); - // This is needed to ensure JavascriptNumber has a VTABLE and JavascriptNumber::Is (which checks for the VTABLE value) works correctly - virtual BOOL ToPrimitive(JavascriptHint, Var* value, ScriptContext *) override { AssertMsg(false, "Number ToPrimitive should not be called"); *value = this; return true;} + + // This is needed to ensure JavascriptNumber has a VTABLE and JavascriptNumber::Is (which checks for the VTABLE value) works correctly. + // This also prevents the vtable from being folded with other classes unexpectedly. + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableJavascriptNumber; + } #endif diff --git a/lib/Runtime/Library/JavascriptObject.cpp b/lib/Runtime/Library/JavascriptObject.cpp index bb2586ed473..176b79e70dd 100644 --- a/lib/Runtime/Library/JavascriptObject.cpp +++ b/lib/Runtime/Library/JavascriptObject.cpp @@ -341,7 +341,7 @@ namespace Js } RecyclableObject* toStringFunc = RecyclableObject::FromVar(toStringVar); - return CALL_FUNCTION(toStringFunc, CallInfo(CallFlags_Value, 1), thisValue); + return CALL_FUNCTION(scriptContext->GetThreadContext(), toStringFunc, CallInfo(CallFlags_Value, 1), thisValue); } Var JavascriptObject::EntryToString(RecyclableObject* function, CallInfo callInfo, ...) diff --git a/lib/Runtime/Library/JavascriptPromise.cpp b/lib/Runtime/Library/JavascriptPromise.cpp index d696dc9275e..1ce33bb3bdf 100644 --- a/lib/Runtime/Library/JavascriptPromise.cpp +++ b/lib/Runtime/Library/JavascriptPromise.cpp @@ -73,7 +73,8 @@ namespace Js // 9. Let completion be Call(executor, undefined, << resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] >>). try { - CALL_FUNCTION(executor, CallInfo(CallFlags_Value, 3), + CALL_FUNCTION(scriptContext->GetThreadContext(), + executor, CallInfo(CallFlags_Value, 3), library->GetUndefined(), resolve, reject); @@ -218,7 +219,8 @@ namespace Js RecyclableObject* resolveFunc = RecyclableObject::FromVar(resolveVar); - Var nextPromise = CALL_FUNCTION(resolveFunc, Js::CallInfo(CallFlags_Value, 2), + Var nextPromise = CALL_FUNCTION(scriptContext->GetThreadContext(), + resolveFunc, Js::CallInfo(CallFlags_Value, 2), constructorObject, next); @@ -242,7 +244,8 @@ namespace Js RecyclableObject* thenFunc = RecyclableObject::FromVar(thenVar); - CALL_FUNCTION(thenFunc, Js::CallInfo(CallFlags_Value, 3), + CALL_FUNCTION(scriptContext->GetThreadContext(), + thenFunc, Js::CallInfo(CallFlags_Value, 3), nextPromiseObject, resolveElement, promiseCapability->GetReject()); @@ -316,7 +319,8 @@ namespace Js RecyclableObject* func = RecyclableObject::FromVar(funcVar); - return CALL_FUNCTION(func, Js::CallInfo(CallFlags_Value, 3), + return CALL_FUNCTION(scriptContext->GetThreadContext(), + func, Js::CallInfo(CallFlags_Value, 3), promise, undefinedVar, onRejected); @@ -379,7 +383,8 @@ namespace Js RecyclableObject* resolveFunc = RecyclableObject::FromVar(resolveVar); - Var nextPromise = CALL_FUNCTION(resolveFunc, Js::CallInfo(CallFlags_Value, 2), + Var nextPromise = CALL_FUNCTION(scriptContext->GetThreadContext(), + resolveFunc, Js::CallInfo(CallFlags_Value, 2), constructorObject, next); @@ -399,7 +404,8 @@ namespace Js RecyclableObject* thenFunc = RecyclableObject::FromVar(thenVar); - CALL_FUNCTION(thenFunc, Js::CallInfo(CallFlags_Value, 3), + CALL_FUNCTION(scriptContext->GetThreadContext(), + thenFunc, Js::CallInfo(CallFlags_Value, 3), nextPromiseObject, promiseCapability->GetResolve(), promiseCapability->GetReject()); @@ -724,7 +730,8 @@ namespace Js Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext); try { - handlerResult = CALL_FUNCTION(handler, Js::CallInfo(Js::CallFlags::CallFlags_Value, 2), + handlerResult = CALL_FUNCTION(scriptContext->GetThreadContext(), + handler, Js::CallInfo(Js::CallFlags::CallFlags_Value, 2), undefinedVar, argument); } @@ -755,7 +762,8 @@ namespace Js RecyclableObject* handlerFunc = RecyclableObject::FromVar(handler); - return CALL_FUNCTION(handlerFunc, CallInfo(CallFlags_Value, 2), + return CALL_FUNCTION(scriptContext->GetThreadContext(), + handlerFunc, CallInfo(CallFlags_Value, 2), undefinedVar, value); } @@ -861,7 +869,8 @@ namespace Js Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext); try { - return CALL_FUNCTION(thenFunction, Js::CallInfo(Js::CallFlags::CallFlags_Value, 3), + return CALL_FUNCTION(scriptContext->GetThreadContext(), + thenFunction, Js::CallInfo(Js::CallFlags::CallFlags_Value, 3), thenable, resolve, reject); @@ -1014,7 +1023,7 @@ namespace Js Var argument = asyncSpawnStepArgumentExecutorFunction->GetArgument(); JavascriptFunction* next = function->GetScriptContext()->GetLibrary()->EnsureGeneratorNextFunction(); - return CALL_FUNCTION(next, CallInfo(CallFlags_Value, 2), asyncSpawnStepArgumentExecutorFunction->GetGenerator(), argument); + return CALL_FUNCTION(function->GetScriptContext()->GetThreadContext(), next, CallInfo(CallFlags_Value, 2), asyncSpawnStepArgumentExecutorFunction->GetGenerator(), argument); } Var JavascriptPromise::EntryJavascriptPromiseAsyncSpawnStepThrowExecutorFunction(RecyclableObject* function, CallInfo callInfo, ...) @@ -1023,7 +1032,7 @@ namespace Js JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction* asyncSpawnStepArgumentExecutorFunction = JavascriptPromiseAsyncSpawnStepArgumentExecutorFunction::FromVar(function); JavascriptFunction* throw_ = function->GetScriptContext()->GetLibrary()->EnsureGeneratorThrowFunction(); - return CALL_FUNCTION(throw_, CallInfo(CallFlags_Value, 2), asyncSpawnStepArgumentExecutorFunction->GetGenerator(), asyncSpawnStepArgumentExecutorFunction->GetArgument()); + return CALL_FUNCTION(function->GetScriptContext()->GetThreadContext(), throw_, CallInfo(CallFlags_Value, 2), asyncSpawnStepArgumentExecutorFunction->GetGenerator(), asyncSpawnStepArgumentExecutorFunction->GetArgument()); } Var JavascriptPromise::EntryJavascriptPromiseAsyncSpawnCallStepExecutorFunction(RecyclableObject* function, CallInfo callInfo, ...) @@ -1075,7 +1084,8 @@ namespace Js try { - next = RecyclableObject::FromVar(CALL_FUNCTION(nextFunction, CallInfo(CallFlags_Value, 1), undefinedVar)); + Var nextVar = CALL_FUNCTION(scriptContext->GetThreadContext(), nextFunction, CallInfo(CallFlags_Value, 1), undefinedVar); + next = RecyclableObject::FromVar(nextVar); } catch (const JavascriptException& err) { @@ -1095,7 +1105,7 @@ namespace Js { // finished with success, resolve the promise value = JavascriptOperators::GetProperty(next, PropertyIds::value, scriptContext); - CALL_FUNCTION(resolve, CallInfo(CallFlags_Value, 2), undefinedVar, value); + CALL_FUNCTION(scriptContext->GetThreadContext(), resolve, CallInfo(CallFlags_Value, 2), undefinedVar, value); return; } @@ -1105,13 +1115,14 @@ namespace Js JavascriptFunction* promiseResolve = library->EnsurePromiseResolveFunction(); value = JavascriptOperators::GetProperty(next, PropertyIds::value, scriptContext); - JavascriptPromise* promise = FromVar(CALL_FUNCTION(promiseResolve, CallInfo(CallFlags_Value, 2), library->GetPromiseConstructor(), value)); + Var promiseVar = CALL_FUNCTION(scriptContext->GetThreadContext(), promiseResolve, CallInfo(CallFlags_Value, 2), library->GetPromiseConstructor(), value); + JavascriptPromise* promise = FromVar(promiseVar); JavascriptFunction* promiseThen = JavascriptFunction::FromVar(JavascriptOperators::GetProperty(promise, PropertyIds::then, scriptContext)); - CALL_FUNCTION(promiseThen, CallInfo(CallFlags_Value, 2), promise, successFunction); + CALL_FUNCTION(scriptContext->GetThreadContext(), promiseThen, CallInfo(CallFlags_Value, 2), promise, successFunction); JavascriptFunction* promiseCatch = JavascriptFunction::FromVar(JavascriptOperators::GetProperty(promise, PropertyIds::catch_, scriptContext)); - CALL_FUNCTION(promiseCatch, CallInfo(CallFlags_Value, 2), promise, failFunction); + CALL_FUNCTION(scriptContext->GetThreadContext(), promiseCatch, CallInfo(CallFlags_Value, 2), promise, failFunction); } #if ENABLE_TTD diff --git a/lib/Runtime/Library/JavascriptProxy.cpp b/lib/Runtime/Library/JavascriptProxy.cpp index 4c29a32410c..fae210f91b0 100644 --- a/lib/Runtime/Library/JavascriptProxy.cpp +++ b/lib/Runtime/Library/JavascriptProxy.cpp @@ -1982,7 +1982,7 @@ namespace Js { functionResult = JavascriptFunction::CallFunction(this, this->GetEntryPoint(), args); } - return CrossSite::MarshalVar(scriptContext, functionResult); + return functionResult; } Var JavascriptProxy::FunctionCallTrap(RecyclableObject* function, CallInfo callInfo, ...) diff --git a/lib/Runtime/Library/JavascriptRegularExpression.cpp b/lib/Runtime/Library/JavascriptRegularExpression.cpp index 854a5e8ed33..ba1ba57cfc8 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.cpp +++ b/lib/Runtime/Library/JavascriptRegularExpression.cpp @@ -861,7 +861,7 @@ namespace Js if (JavascriptConversion::IsCallable(exec)) { RecyclableObject* execFn = RecyclableObject::FromVar(exec); - Var result = CALL_FUNCTION(execFn, CallInfo(CallFlags_Value, 2), thisObj, string); + Var result = CALL_FUNCTION(scriptContext->GetThreadContext(), execFn, CallInfo(CallFlags_Value, 2), thisObj, string); if (!JavascriptOperators::IsObjectOrNull(result)) { diff --git a/lib/Runtime/Library/JavascriptRegularExpression.h b/lib/Runtime/Library/JavascriptRegularExpression.h index 56bc7ce56d8..58c22312b16 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.h +++ b/lib/Runtime/Library/JavascriptRegularExpression.h @@ -210,6 +210,12 @@ namespace Js void SetLastIndexInfo_TTD(CharCount lastIndex, Js::Var lastVar); #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableJavascriptRegExp; + } }; } // namespace Js diff --git a/lib/Runtime/Library/JavascriptSet.cpp b/lib/Runtime/Library/JavascriptSet.cpp index 60e37c97dbd..b26e2b1cca9 100644 --- a/lib/Runtime/Library/JavascriptSet.cpp +++ b/lib/Runtime/Library/JavascriptSet.cpp @@ -89,7 +89,7 @@ namespace Js if (iter != nullptr) { JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) { - CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 2), setObject, nextItem); + CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 2), setObject, nextItem); }); } @@ -195,7 +195,7 @@ namespace Js { Var value = iterator.Current(); - CALL_FUNCTION(callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, value, args[0]); + CALL_FUNCTION(scriptContext->GetThreadContext(), callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, value, args[0]); } return scriptContext->GetLibrary()->GetUndefined(); diff --git a/lib/Runtime/Library/JavascriptSimdFloat32x4.h b/lib/Runtime/Library/JavascriptSimdFloat32x4.h index 2d133950595..415a93a199f 100644 --- a/lib/Runtime/Library/JavascriptSimdFloat32x4.h +++ b/lib/Runtime/Library/JavascriptSimdFloat32x4.h @@ -48,5 +48,11 @@ namespace Js private: virtual bool GetPropertyBuiltIns(PropertyId propertyId, Var* value, ScriptContext* requestContext) override; + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableSimd128F4; + } }; } diff --git a/lib/Runtime/Library/JavascriptSimdInt32x4.h b/lib/Runtime/Library/JavascriptSimdInt32x4.h index 690d8e1daf1..df00acd9347 100644 --- a/lib/Runtime/Library/JavascriptSimdInt32x4.h +++ b/lib/Runtime/Library/JavascriptSimdInt32x4.h @@ -52,5 +52,11 @@ namespace Js private: virtual bool GetPropertyBuiltIns(PropertyId propertyId, Var* value, ScriptContext* requestContext) override; + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableSimd128I4; + } }; } diff --git a/lib/Runtime/Library/JavascriptSimdObject.cpp b/lib/Runtime/Library/JavascriptSimdObject.cpp index 0f4dd44e918..0c20fa10556 100644 --- a/lib/Runtime/Library/JavascriptSimdObject.cpp +++ b/lib/Runtime/Library/JavascriptSimdObject.cpp @@ -147,7 +147,7 @@ namespace Js } template - Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, const T(&laneValues)[N], + Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, const T (&laneValues)[N], CallInfo* callInfo, ScriptContext* scriptContext) const { Assert(args); @@ -208,7 +208,7 @@ namespace Js { for (; idx < numLanes - 1; ++idx) { - laneVar = JavascriptNumber::ToVar(static_cast(laneValues[idx]), scriptContext); + laneVar = JavascriptNumber::ToVar(static_cast(laneValues[idx]), scriptContext); newArgs[0] = laneVar; JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, newCallInfo, scriptContext); result = JavascriptString::Concat(result, laneValue); @@ -223,7 +223,7 @@ namespace Js Assert((typeDescriptor == TypeIds_SIMDUint8x16 || typeDescriptor == TypeIds_SIMDUint16x8 || typeDescriptor == TypeIds_SIMDUint32x4)); for (; idx < numLanes - 1; ++idx) { - laneVar = JavascriptNumber::ToVar(static_cast(laneValues[idx]), scriptContext); + laneVar = JavascriptNumber::ToVar(static_cast(laneValues[idx]), scriptContext); newArgs[0] = laneVar; JavascriptString *laneValue = JavascriptNumber::ToLocaleStringIntl(newArgs, newCallInfo, scriptContext); result = JavascriptString::Concat(result, laneValue); @@ -236,6 +236,7 @@ namespace Js END_TEMP_ALLOCATOR(tempAllocator, scriptContext); return JavascriptString::Concat(result, JavascriptString::NewWithSz(_u(")"), scriptContext)); } + template Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, const float (&laneValues)[4], CallInfo* callInfo, ScriptContext* scriptContext) const; template Var JavascriptSIMDObject::ToLocaleString(const Var* args, uint numArgs, const char16 *typeString, diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index 53914b1b6ac..5019fe91c54 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -1759,18 +1759,18 @@ namespace Js } RecyclableObject* fnObj = RecyclableObject::FromVar(fn); - return CallRegExFunction(fnObj, regExp, args); + return CallRegExFunction(fnObj, regExp, args, scriptContext); } template<> - Var JavascriptString::CallRegExFunction<1>(RecyclableObject* fnObj, Var regExp, Arguments& args) + Var JavascriptString::CallRegExFunction<1>(RecyclableObject* fnObj, Var regExp, Arguments& args, ScriptContext *scriptContext) { // args[0]: String - return CALL_FUNCTION(fnObj, CallInfo(CallFlags_Value, 2), regExp, args[0]); + return CALL_FUNCTION(scriptContext->GetThreadContext(), fnObj, CallInfo(CallFlags_Value, 2), regExp, args[0]); } template<> - Var JavascriptString::CallRegExFunction<2>(RecyclableObject* fnObj, Var regExp, Arguments& args) + Var JavascriptString::CallRegExFunction<2>(RecyclableObject* fnObj, Var regExp, Arguments& args, ScriptContext * scriptContext) { // args[0]: String // args[1]: RegExp (ignored since we need to create one when the argument is "undefined") @@ -1778,10 +1778,10 @@ namespace Js if (args.Info.Count < 3) { - return CallRegExFunction<1>(fnObj, regExp, args); + return CallRegExFunction<1>(fnObj, regExp, args, scriptContext); } - return CALL_FUNCTION(fnObj, CallInfo(CallFlags_Value, 3), regExp, args[0], args[2]); + return CALL_FUNCTION(scriptContext->GetThreadContext(), fnObj, CallInfo(CallFlags_Value, 3), regExp, args[0], args[2]); } Var JavascriptString::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...) diff --git a/lib/Runtime/Library/JavascriptString.h b/lib/Runtime/Library/JavascriptString.h index 40141dffcf4..a0d80f120ec 100644 --- a/lib/Runtime/Library/JavascriptString.h +++ b/lib/Runtime/Library/JavascriptString.h @@ -366,7 +366,7 @@ namespace Js template // The count is excluding 'this' static Var CallRegExSymbolFunction(Var fn, Var regExp, Arguments& args, PCWSTR const varName, ScriptContext* scriptContext); template // The count is excluding 'this' - static Var CallRegExFunction(RecyclableObject* fnObj, Var regExp, Arguments& args); + static Var CallRegExFunction(RecyclableObject* fnObj, Var regExp, Arguments& args, ScriptContext *scriptContext); }; template<> diff --git a/lib/Runtime/Library/JavascriptWeakMap.cpp b/lib/Runtime/Library/JavascriptWeakMap.cpp index 1f92dac6b86..8111c3ce0d5 100644 --- a/lib/Runtime/Library/JavascriptWeakMap.cpp +++ b/lib/Runtime/Library/JavascriptWeakMap.cpp @@ -132,7 +132,7 @@ namespace Js value = undefined; } - CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 3), weakMapObject, key, value); + CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 3), weakMapObject, key, value); }); } diff --git a/lib/Runtime/Library/JavascriptWeakSet.cpp b/lib/Runtime/Library/JavascriptWeakSet.cpp index a80980edb35..60c85b2d85e 100644 --- a/lib/Runtime/Library/JavascriptWeakSet.cpp +++ b/lib/Runtime/Library/JavascriptWeakSet.cpp @@ -68,7 +68,7 @@ namespace Js if (iter != nullptr) { JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) { - CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 2), weakSetObject, nextItem); + CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 2), weakSetObject, nextItem); }); } diff --git a/lib/Runtime/Library/PropertyString.h b/lib/Runtime/Library/PropertyString.h index 6116f3e3576..a2c9539df47 100644 --- a/lib/Runtime/Library/PropertyString.h +++ b/lib/Runtime/Library/PropertyString.h @@ -62,6 +62,12 @@ namespace Js //Get the associated property id for this string if there is on (e.g. it is a propertystring otherwise return Js::PropertyIds::_none) virtual Js::PropertyId TryGetAssociatedPropertyId() const override { return this->m_propertyRecord->GetPropertyId(); } #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtablePropertyString; + } }; class ArenaAllocPropertyString sealed : public PropertyString diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index 6630b7ee92b..50c6414dde5 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -149,7 +149,7 @@ namespace Js UnifiedRegex::RegexPattern* RegexHelper::PrimCompileDynamic(ScriptContext *scriptContext, const char16* psz, CharCount csz, const char16* pszOpts, CharCount cszOpts, bool isLiteralSource) { - PROBE_STACK(scriptContext, Js::Constants::MinStackRegex); + PROBE_STACK_NO_DISPOSE(scriptContext, Js::Constants::MinStackRegex); // SEE ALSO: Scanner::ScanRegExpConstant() #ifdef PROFILE_EXEC @@ -896,7 +896,7 @@ namespace Js // Number of captures can be at most 99, so we won't overflow. ushort argCount = (ushort) numberOfCaptures + 4; - PROBE_STACK(scriptContext, argCount * sizeof(Var)); + PROBE_STACK_NO_DISPOSE(scriptContext, argCount * sizeof(Var)); Var* args = (Var*) _alloca(argCount * sizeof(Var)); args[0] = scriptContext->GetLibrary()->GetUndefined(); @@ -1223,7 +1223,7 @@ namespace Js // Replace function must be called with arguments (, group0, ..., groupn, offset, input) // The garbage collector must know about this array since it is being passed back into script land Var* replaceArgs; - PROBE_STACK(scriptContext, (numGroups + 3) * sizeof(Var)); + PROBE_STACK_NO_DISPOSE(scriptContext, (numGroups + 3) * sizeof(Var)); replaceArgs = (Var*)_alloca((numGroups + 3) * sizeof(Var)); replaceArgs[0] = scriptContext->GetLibrary()->GetUndefined(); replaceArgs[numGroups + 2] = input; @@ -1398,7 +1398,8 @@ namespace Js if (indexMatched != CharCountFlag) { Var pThis = scriptContext->GetLibrary()->GetUndefined(); - JavascriptString* replace = JavascriptConversion::ToString(CALL_FUNCTION(replacefn, CallInfo(4), pThis, match, JavascriptNumber::ToVar((int)indexMatched, scriptContext), input), scriptContext); + Var replaceVar = CALL_FUNCTION(scriptContext->GetThreadContext(), replacefn, CallInfo(4), pThis, match, JavascriptNumber::ToVar((int)indexMatched, scriptContext), input); + JavascriptString* replace = JavascriptConversion::ToString(replaceVar, scriptContext); const char16* inputStr = input->GetString(); const char16* prefixStr = inputStr; CharCount prefixLength = indexMatched; diff --git a/lib/Runtime/Library/ScriptFunction.h b/lib/Runtime/Library/ScriptFunction.h index d06ab9b59da..c402a119abd 100644 --- a/lib/Runtime/Library/ScriptFunction.h +++ b/lib/Runtime/Library/ScriptFunction.h @@ -109,6 +109,12 @@ namespace Js virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override; virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override; #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableScriptFunction; + } }; class AsmJsScriptFunction : public ScriptFunction diff --git a/lib/Runtime/Library/StackScriptFunction.h b/lib/Runtime/Library/StackScriptFunction.h index dbd95f28c13..d781506e0a9 100644 --- a/lib/Runtime/Library/StackScriptFunction.h +++ b/lib/Runtime/Library/StackScriptFunction.h @@ -90,5 +90,11 @@ namespace Js Assert(false); } #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableStackScriptFunction; + } }; }; diff --git a/lib/Runtime/Library/TypedArray.cpp b/lib/Runtime/Library/TypedArray.cpp index 1f36aff4559..6e7ec0efbd9 100644 --- a/lib/Runtime/Library/TypedArray.cpp +++ b/lib/Runtime/Library/TypedArray.cpp @@ -439,7 +439,7 @@ namespace Js (iteratorFn != scriptContext->GetLibrary()->GetArrayPrototypeValuesFunction() || !JavascriptArray::Is(firstArgument) || JavascriptLibrary::ArrayIteratorPrototypeHasUserDefinedNext(scriptContext))) { - Var iterator = CALL_FUNCTION(iteratorFn, CallInfo(Js::CallFlags_Value, 1), RecyclableObject::FromVar(firstArgument)); + Var iterator = CALL_FUNCTION(scriptContext->GetThreadContext(), iteratorFn, CallInfo(Js::CallFlags_Value, 1), RecyclableObject::FromVar(firstArgument)); if (!JavascriptOperators::IsObject(iterator)) { @@ -1415,7 +1415,8 @@ namespace Js uint32 TypedArrayBase::GetFromIndex(Var arg, uint32 length, ScriptContext *scriptContext) { uint32 index = JavascriptArray::GetFromIndex(arg, length, scriptContext); - return min(index, length); + Assert(index <= length); + return index; } Var TypedArrayBase::CommonSubarray(Arguments& args) @@ -1809,7 +1810,8 @@ namespace Js // We know that the TypedArray.HasItem will be true because k < length and we can skip the check in the TypedArray version of filter. element = typedArrayBase->DirectGetItem(k); - selected = CALL_FUNCTION(callBackFn, CallInfo(flags, 4), thisArg, + selected = CALL_FUNCTION(scriptContext->GetThreadContext(), + callBackFn, CallInfo(flags, 4), thisArg, element, JavascriptNumber::ToVar(k, scriptContext), typedArrayBase); @@ -1922,7 +1924,8 @@ namespace Js Var element = typedArrayBase->DirectGetItem(k); - CALL_FUNCTION(callBackFn, CallInfo(CallFlags_Value, 4), + CALL_FUNCTION(scriptContext->GetThreadContext(), + callBackFn, CallInfo(CallFlags_Value, 4), thisArg, element, JavascriptNumber::ToVar(k, scriptContext), @@ -2204,7 +2207,8 @@ namespace Js ScriptContext* scriptContext = compFn->GetScriptContext(); Var undefined = scriptContext->GetLibrary()->GetUndefined(); double dblResult; - Var retVal = CALL_FUNCTION(compFn, CallInfo(CallFlags_Value, 3), + Var retVal = CALL_FUNCTION(scriptContext->GetThreadContext(), + compFn, CallInfo(CallFlags_Value, 3), undefined, JavascriptNumber::ToVarWithCheck((double)x, scriptContext), JavascriptNumber::ToVarWithCheck((double)y, scriptContext)); @@ -2227,12 +2231,12 @@ namespace Js else { dblResult = JavascriptConversion::ToNumber_Full(retVal, scriptContext); - } - // ToNumber may execute user-code which can cause the array to become detached - if (TypedArrayBase::IsDetachedTypedArray(contextArray[0])) - { - JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("[TypedArray].prototype.sort")); + // ToNumber may execute user-code which can cause the array to become detached + if (TypedArrayBase::IsDetachedTypedArray(contextArray[0])) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("[TypedArray].prototype.sort")); + } } if (dblResult < 0) @@ -2939,6 +2943,12 @@ namespace Js GENERATE_FOREACH_TYPEDARRAY(TypedArraySubOp, GenerateNotSupportedStub2, Sub) GENERATE_FOREACH_TYPEDARRAY(TypedArrayXorOp, GenerateNotSupportedStub2, Xor) + template<> + VTableValue Int8Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt8Array; + } + template<> inline Var Int8VirtualArray::DirectGetItem(__in uint32 index) { @@ -2946,13 +2956,13 @@ namespace Js } template<> - inline BOOL Uint8Array::DirectSetItem(__in uint32 index, __in Js::Var value) + VTableValue Int8VirtualArray::DummyVirtualFunctionToHinderLinkerICF() { - return BaseTypedDirectSetItem(index, value, JavascriptConversion::ToUInt8); + return VTableValue::VtableInt8VirtualArray; } template<> - inline BOOL Uint8VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) + inline BOOL Uint8Array::DirectSetItem(__in uint32 index, __in Js::Var value) { return BaseTypedDirectSetItem(index, value, JavascriptConversion::ToUInt8); } @@ -2963,6 +2973,24 @@ namespace Js return BaseTypedDirectSetItemNoSet(index, value, JavascriptConversion::ToUInt8); } + template<> + inline Var Uint8Array::DirectGetItem(__in uint32 index) + { + return BaseTypedDirectGetItem(index); + } + + template<> + VTableValue Uint8Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint8Array; + } + + template<> + inline BOOL Uint8VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) + { + return BaseTypedDirectSetItem(index, value, JavascriptConversion::ToUInt8); + } + template<> inline BOOL Uint8VirtualArray::DirectSetItemNoSet(__in uint32 index, __in Js::Var value) { @@ -2970,15 +2998,15 @@ namespace Js } template<> - inline Var Uint8Array::DirectGetItem(__in uint32 index) + inline Var Uint8VirtualArray::DirectGetItem(__in uint32 index) { return BaseTypedDirectGetItem(index); } template<> - inline Var Uint8VirtualArray::DirectGetItem(__in uint32 index) + VTableValue Uint8VirtualArray::DummyVirtualFunctionToHinderLinkerICF() { - return BaseTypedDirectGetItem(index); + return VTableValue::VtableUint8VirtualArray; } template<> @@ -2999,6 +3027,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint8ClampedArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint8ClampedArray; + } + template<> inline BOOL Uint8ClampedVirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3017,6 +3051,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint8ClampedVirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint8ClampedVirtualArray; + } + template<> inline BOOL Int16Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3035,6 +3075,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Int16Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt16Array; + } + template<> inline BOOL Int16VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3053,6 +3099,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Int16VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt16VirtualArray; + } + template<> inline BOOL Uint16Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3071,6 +3123,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint16Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint16Array; + } + template<> inline BOOL Uint16VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3089,6 +3147,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint16VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint16VirtualArray; + } + template<> inline BOOL Int32Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3107,6 +3171,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Int32Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt32Array; + } + template<> inline BOOL Int32VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3125,6 +3195,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Int32VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt32VirtualArray; + } + template<> inline BOOL Uint32Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3143,6 +3219,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint32Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint32Array; + } + template<> inline BOOL Uint32VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3161,6 +3243,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint32VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint32VirtualArray; + } + template<> inline BOOL Float32Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3179,6 +3267,12 @@ namespace Js return TypedDirectGetItemWithCheck(index); } + template<> + VTableValue Float32Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableFloat32Array; + } + template<> inline BOOL Float32VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3197,6 +3291,12 @@ namespace Js return TypedDirectGetItemWithCheck(index); } + template<> + VTableValue Float32VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableFloat32VirtualArray; + } + template<> inline BOOL Float64Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3215,6 +3315,12 @@ namespace Js return TypedDirectGetItemWithCheck(index); } + template<> + VTableValue Float64Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableFloat64Array; + } + template<> inline BOOL Float64VirtualArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3233,6 +3339,12 @@ namespace Js return TypedDirectGetItemWithCheck(index); } + template<> + VTableValue Float64VirtualArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableFloat64VirtualArray; + } + template<> inline BOOL Int64Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3251,6 +3363,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Int64Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableInt64Array; + } + template<> inline BOOL Uint64Array::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3269,6 +3387,12 @@ namespace Js return BaseTypedDirectGetItem(index); } + template<> + VTableValue Uint64Array::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableUint64Array; + } + template<> inline BOOL BoolArray::DirectSetItem(__in uint32 index, __in Js::Var value) { @@ -3301,6 +3425,13 @@ namespace Js return typedBuffer[index] ? GetLibrary()->GetTrue() : GetLibrary()->GetFalse(); } + template<> + VTableValue BoolArray::DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableBoolArray; + } + + Var CharArray::Create(ArrayBufferBase* arrayBuffer, uint32 byteOffSet, uint32 mappedLength, JavascriptLibrary* javascriptLibrary) { CharArray* arr; diff --git a/lib/Runtime/Library/TypedArray.h b/lib/Runtime/Library/TypedArray.h index e0a0bb18fbf..ac767cd48cf 100644 --- a/lib/Runtime/Library/TypedArray.h +++ b/lib/Runtime/Library/TypedArray.h @@ -217,6 +217,7 @@ namespace Js { Assert(this->GetScriptContext() != scriptContext); AssertMsg(VirtualTableInfo::HasVirtualTable(this), "Derived class need to define marshal to script context"); + VirtualTableInfo>>::SetVirtualTable(this); ArrayBufferBase* arrayBuffer = this->GetArrayBuffer(); if (arrayBuffer && !arrayBuffer->IsCrossSiteObject()) @@ -493,6 +494,9 @@ namespace Js { return &TypedArrayCompareElementsHelper; } + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF(); }; // in windows build environment, char16 is not an intrinsic type, and we cannot do the type @@ -551,6 +555,12 @@ namespace Js { return &TypedArrayCompareElementsHelper; } + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + return VTableValue::VtableCharArray; + } }; #if defined(__clang__) diff --git a/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj b/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj index 3d31199de97..eebd104c09f 100644 --- a/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj +++ b/lib/Runtime/Math/Chakra.Runtime.Math.vcxproj @@ -33,6 +33,8 @@ Use RuntimeMathPch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Runtime/Runtime.h b/lib/Runtime/Runtime.h index 754be47f291..a8be453901b 100644 --- a/lib/Runtime/Runtime.h +++ b/lib/Runtime/Runtime.h @@ -566,6 +566,7 @@ enum tagDEBUG_EVENT_INFO_TYPE #include "Language/InlineCachePointerArray.inl" #include "Language/JavascriptOperators.inl" #include "Language/TaggedInt.inl" +#include "Library/JavascriptGeneratorFunction.h" #ifndef USED_IN_STATIC_LIB diff --git a/lib/Runtime/Types/Chakra.Runtime.Types.vcxproj b/lib/Runtime/Types/Chakra.Runtime.Types.vcxproj index 994749aa4d6..986f7d34655 100644 --- a/lib/Runtime/Types/Chakra.Runtime.Types.vcxproj +++ b/lib/Runtime/Types/Chakra.Runtime.Types.vcxproj @@ -33,6 +33,8 @@ Use RuntimeTypePch.h + + -Zc:implicitNoexcept- %(AdditionalOptions) diff --git a/lib/Runtime/Types/DynamicObject.h b/lib/Runtime/Types/DynamicObject.h index da2c847fe40..40f19e8c35c 100644 --- a/lib/Runtime/Types/DynamicObject.h +++ b/lib/Runtime/Types/DynamicObject.h @@ -311,6 +311,7 @@ namespace Js void SetArrayCallSiteIndex(ProfileId profileId); static DynamicObject * BoxStackInstance(DynamicObject * instance); + private: ArrayObject* EnsureObjectArray(); ArrayObject* GetObjectArrayOrFlagsAsArray() const { return objectArray; } @@ -343,5 +344,15 @@ namespace Js #endif #endif + + public: + virtual VTableValue DummyVirtualFunctionToHinderLinkerICF() + { + // This virtual function hinders linker to do ICF vtable of this class with other classes. + // ICF vtable causes unexpected behavior in type check code. Objects uses vtable as identify should + // override this function and return a unique value. + return VTableValue::VtableDynamicObject; + } + }; } // namespace Js diff --git a/lib/Runtime/Types/DynamicType.cpp b/lib/Runtime/Types/DynamicType.cpp index 9a53d339fed..afb56bef86f 100644 --- a/lib/Runtime/Types/DynamicType.cpp +++ b/lib/Runtime/Types/DynamicType.cpp @@ -312,7 +312,7 @@ namespace Js { // Stack object should have a pre-op bail on implicit call. We shouldn't see them here. Assert(!ThreadContext::IsOnStack(this) || threadContext->HasNoSideEffect(toStringFunction)); - return CALL_FUNCTION(toStringFunction, CallInfo(CallFlags_Value, 1), this); + return CALL_FUNCTION(threadContext, toStringFunction, CallInfo(CallFlags_Value, 1), this); }); if (!aResult) diff --git a/lib/Runtime/Types/SpreadArgument.cpp b/lib/Runtime/Types/SpreadArgument.cpp index 554b5836e50..539cfe2dccb 100644 --- a/lib/Runtime/Types/SpreadArgument.cpp +++ b/lib/Runtime/Types/SpreadArgument.cpp @@ -16,21 +16,83 @@ namespace Js return static_cast(aValue); } - SpreadArgument::SpreadArgument(Var iterable, RecyclableObject* iterator, DynamicType * type) : DynamicObject(type), iterable(iterable), - iterator(iterator), iteratorIndices(nullptr) + SpreadArgument::SpreadArgument(Var iterator, bool useDirectCall, DynamicType * type) + : DynamicObject(type), iteratorIndices(nullptr) { - Var nextItem; + Assert(iterator != nullptr); ScriptContext * scriptContext = this->GetScriptContext(); + Assert(iteratorIndices == nullptr); - while (JavascriptOperators::IteratorStepAndValue(iterator, scriptContext, &nextItem)) + if (useDirectCall) { - if (iteratorIndices == nullptr) + if (JavascriptArray::Is(iterator)) { - iteratorIndices = RecyclerNew(scriptContext->GetRecycler(), VarList, scriptContext->GetRecycler()); + JavascriptArray *array = JavascriptArray::FromVar(iterator); + if (!array->HasNoMissingValues()) + { + AssertAndFailFast(); + } + + uint32 length = array->GetLength(); + if (length > 0) + { + iteratorIndices = RecyclerNew(scriptContext->GetRecycler(), VarList, scriptContext->GetRecycler()); + for (uint32 j = 0; j < length; j++) + { + Var element = nullptr; + if (array->DirectGetItemAtFull(j, &element)) + { + iteratorIndices->Add(element); + } + } + // Array length shouldn't have changed as we determined that there is no missing values. + Assert(length == array->GetLength()); + } } + else if (TypedArrayBase::Is(iterator)) + { + TypedArrayBase *typedArray = TypedArrayBase::FromVar(iterator); + + if (typedArray->IsDetachedBuffer()) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray); + } + + uint32 length = typedArray->GetLength(); + if (length > 0) + { + iteratorIndices = RecyclerNew(scriptContext->GetRecycler(), VarList, scriptContext->GetRecycler()); + for (uint32 j = 0; j < length; j++) + { + Var element = typedArray->DirectGetItemNoDetachCheck(j); + iteratorIndices->Add(element); + } + } - iteratorIndices->Add(nextItem); + // The ArrayBuffer shouldn't have detached in above loop. + Assert(!typedArray->IsDetachedBuffer()); + } + else + { + Assert(false); + } } - } + else if (RecyclableObject::Is(iterator)) + { + Var nextItem; + while (JavascriptOperators::IteratorStepAndValue(RecyclableObject::FromVar(iterator), scriptContext, &nextItem)) + { + if (iteratorIndices == nullptr) + { + iteratorIndices = RecyclerNew(scriptContext->GetRecycler(), VarList, scriptContext->GetRecycler()); + } + iteratorIndices->Add(nextItem); + } + } + else + { + Assert(false); + } + } } // namespace Js diff --git a/lib/Runtime/Types/SpreadArgument.h b/lib/Runtime/Types/SpreadArgument.h index 6035ebafa74..6e39f91abb1 100644 --- a/lib/Runtime/Types/SpreadArgument.h +++ b/lib/Runtime/Types/SpreadArgument.h @@ -9,8 +9,6 @@ namespace Js class SpreadArgument : public DynamicObject { private: - Var iterable; - RecyclableObject* iterator; typedef JsUtil::List VarList; VarList* iteratorIndices; @@ -22,8 +20,7 @@ namespace Js public: static bool Is(Var aValue); static SpreadArgument* FromVar(Var value); - SpreadArgument(Var iterable, RecyclableObject* iterator, DynamicType * type); - Var GetArgument() const { return iterable; } + SpreadArgument(Var iterator, bool useDirectCall, DynamicType * type); const Var* GetArgumentSpread() const { return iteratorIndices ? iteratorIndices->GetBuffer() : nullptr; } uint GetArgumentSpreadCount() const { return iteratorIndices ? iteratorIndices->Count() : 0; } diff --git a/test/Array/Array_TypeConfusion_bugs.js b/test/Array/Array_TypeConfusion_bugs.js index 43430fbc33a..beb6940daa4 100644 --- a/test/Array/Array_TypeConfusion_bugs.js +++ b/test/Array/Array_TypeConfusion_bugs.js @@ -1,598 +1,598 @@ -//------------------------------------------------------------------------------------------------------- -// Copyright (C) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -//------------------------------------------------------------------------------------------------------- - -//Note: see function ArraySpliceHelper of JavascriptArray.cpp - -if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch - this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); -} - -var restorePropertyFromDescriptor = function (obj, prop, desc) { - if (typeof desc == 'undefined') { - delete obj[prop]; - } else { - Object.defineProperty(obj, prop, desc); - } -} - -var tests = [ - { - name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements", - body: function () - { - function test() { - var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe]; - - class MyArray extends Uint32Array { } - Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } }); - - var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; - var test = [float_val, float_val, float_val, float_val]; - test.length = 0x1000; - test.__proto__ = new MyArray(0); - - var res = Array.prototype.slice.apply(test, []); // OOB write - assert.areEqual(0x1000, res.length, "res.length == 0x1000"); - assert.areEqual(float_val, res[0], "res[0] == float_val"); - assert.areEqual(float_val, res[1], "res[1] == float_val"); - assert.areEqual(float_val, res[2], "res[2] == float_val"); - assert.areEqual(float_val, res[3], "res[3] == float_val"); - assert.areEqual(undefined, res[4], "res[4] == float_val"); - assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined"); - } - test(); - test(); - test(); - } - }, - { - name: "OS7342689:OOB writes using type confusion in InternalFillFromPrototypes", - body: function () - { - function test() { - var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, - 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, - 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, - 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe]; - - class MyArray extends Uint32Array { } - Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } }); - - var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; - var test = [{}]; - delete test[0]; - test.length = 0x1000; - var src = [float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, - float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, - float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, - float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, - float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val]; - test.__proto__ = src; - test.__proto__.__proto__ = new MyArray(0); - - //this will write 0xfffc0daddeadbabe to [arr1] + 0x1D8 - var res = Array.prototype.slice.apply(test, []) - assert.areEqual(0x1000, res.length, "res.length == 0x1000"); - assert.areEqual(float_val, res[0], "res[0] == float_val"); - assert.areEqual(float_val, res[1], "res[1] == float_val"); - assert.areEqual(float_val, res[2], "res[2] == float_val"); - assert.areEqual(float_val, res[src.length-1], "res[src.length-1] == float_val"); - assert.areEqual(undefined, res[src.length], "res[src] == undefined"); - assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined"); - } - test(); - test(); - test(); - } - }, - { - name: "OS7307908:type confusion in Array.prototype.slice", - body: function () - { - function test() { - var arr = [1, 2] - var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); - - //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper - Object.defineProperty( - arr.constructor, - Symbol.species, - { - value : function() - { - //change 'arr' from TypeIds_NativeIntArray to TypeIds_Array - arr[0] = WScript; - - //return a TypeIds_NativeIntArray so we can read back out the 64 bit pointer as two 32bit ints. - return []; - } - } - ); - - //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. - var brr = arr.slice(); - - assert.areEqual(2, brr.length, "brr.length == 2"); - assert.areEqual(WScript, brr[0], "brr[0] == WScript"); - assert.areEqual(2, brr[1], "brr[0] == WScript"); - - restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); - } - test(); - test(); - test(); - } - }, - { - name: "OS7342791:type confusion in Array.from", - body: function () - { - function test() { - var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe]; - - var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; - var test = [float_val, float_val, float_val, float_val]; - delete test[0]; - delete test[1]; - delete test[2]; - - var res = Array.from.apply(function(){return arr1}, [test]); - assert.areEqual(4, res.length, "res.length == 4"); - assert.areEqual(undefined, res[0], "res[0] == undefined"); - assert.areEqual(undefined, res[1], "res[1] == undefined"); - assert.areEqual(undefined, res[2], "res[2] == undefined"); - assert.areEqual(float_val, res[3], "res[3] == float_val"); - - assert.areEqual(['1','2','3'], Array.from.apply(()=>new Array(), ["123"]), "Array.from on iterable"); - assert.areEqual([1,2,3], Array.from.apply(()=>new Array(), [{"0":1, "1":2, "2":3, "length":3}]), "Array.from on non-iterable"); - } - test(); - test(); - test(); - } - }, - { - name: "OS7342844:type confusion in Array.of", - body: function () - { - function test() { - var brr = Array.of.call(()=>[ 1, 2, 3, 4 ], - WScript, // supply 2 copies of target so the brr array will have a length of 2 and we can read the 64bit pointer. - WScript - ); - - assert.areEqual(2, brr.length, "brr.length == 2"); - assert.areEqual(WScript, brr[0], "res[0] == WScript"); - assert.areEqual(WScript, brr[1], "res[1] == WScript"); - assert.areEqual(undefined, brr[2], "res[2] == undefined"); - assert.areEqual(undefined, brr[3], "res[3] == undefined"); - } - test(); - test(); - test(); - } - }, - { - name: "OS7342907:type confusion in Array.prototype.map", - body: function () - { - function test() { - var arr = [ 1, 2 ]; - var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); - - Object.defineProperty( - arr.constructor, - Symbol.species, - { - value : function() - { - return []; - } - } - ); - - //The value returned from our callback is directly set into the array whose type we create via the species. - var brr = arr.map( function( v ) - { - if( v == 1 ) - return WScript; - } - ); - - assert.areEqual(2, brr.length, "brr.length == 2"); - assert.areEqual(WScript, brr[0], "brr[0] == WScript"); - assert.areEqual(undefined, brr[1], "brr[1] == undefined"); - - restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); - } - test(); - test(); - test(); - } - }, - { - name: "type confusion in Array.prototype.map with Proxy", - body: function () - { - function test() { - var d = [1,2,3]; - class dummy { - constructor() { - return d; - } - } - - var handler = { - get: function(target, name) { - if(name == "length") { - return 0x100; - } - - return {[Symbol.species] : dummy}; - }, - - has: function(target, name) { - return true; - } - }; - - var p = new Proxy([], handler); - var a = new Array(1,2,3); - - function test(){ - return 0x777777777777; - } - - var o = a.map.call(p, test); - assert.areEqual(Array(0x100).fill(0x777777777777), o); - } - test(); - test(); - test(); - } - }, - { - name: "OS7342965:type confusion in Array.prototype.splice", - body: function () - { - function test() { - //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values). - var arr = [ WScript, WScript ]; - var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); - - //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice - Object.defineProperty( - arr.constructor, - Symbol.species, - { - value : function() - { - //return a TypeIds_NativeIntArray so we can read back out a 64 bit pointer as two 32bit ints. - return [ 1, 2, 3, 4 ]; - } - } - ); - - //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. The helper - //method ArraySegmentSpliceHelper will directly copy over the TypeIds_Array segment data - //into the TypeIds_NativeIntArray segment. - var brr = arr.splice( 0, 2 ); - - assert.areEqual(2, brr.length, "brr.length == 2"); - assert.areEqual(WScript, brr[0], "brr[0] == WScript"); - assert.areEqual(WScript, brr[1], "brr[1] == WScript"); - assert.areEqual(undefined, brr[2], "brr[2] == undefined"); - assert.areEqual(undefined, brr[3], "brr[3] == undefined"); - - restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); - } - test(); - test(); - test(); - } - }, - { - name: "type confusion in Array.prototype.join", - body: function () - { - function test() { - var a = [0, 1, 2, 3]; - var b = []; - delete a[0]; - Object.setPrototypeOf(a, b) - Object.defineProperty(b, "0", - { - get: function() { - a[2] = "abc"; - return -1; - } - }); - - assert.areEqual("-1,1,abc,3", a.join()); - } - test(); - test(); - test(); - } - }, - { - name: "type confusion in Array.prototype.indexOf", - body: function () - { - function test() { - var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; - var a = [0, 1, 2, 3]; - var b = []; - delete a[1]; - Object.setPrototypeOf(a, b); - Object.defineProperty(b, "1", - { - get: function() { - a[2] = float_val; //"abc"; - return -1; - } - }); - - assert.areEqual(3, a.indexOf(3)); - } - test(); - test(); - test(); - } - }, - { - name: "type confusion in Array.prototype.lastIndexOf", - body: function () - { - function test() { - var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; - var a = [3, 2, 1, 0]; - var b = []; - delete a[3]; - Object.setPrototypeOf(a, b); - Object.defineProperty(b, "3", - { - get: function() { - a[1] = float_val; //"abc"; - return -1; - } - }); - - assert.areEqual(0, a.lastIndexOf(3)); - } - test(); - test(); - test(); - } - }, - { - name: "type confusion in Function.prototype.apply", - body: function () - { - function test() { - var t = [1,2,3]; - - function f(){ - var h = []; - var a = [...arguments] - - for(item in a){ - var n = new Number(a[item]); - - if( n < 0) { - n = n + 0x100000000; - } - - h.push(n.toString(16)); - } - - return h; - } - - var q = f; - - t.length = 20; - var o = {}; - Object.defineProperty(o, '3', { - get: function() { - var ta = []; - ta.fill.call(t, "natalie"); - return 5; - } - }); - - t.__proto__ = o; - - var j = []; - assert.areEqual("1,2,3,5,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN", f.apply(null, t).toString()); - } - test(); - test(); - test(); - } - }, - { - name: "[MSRC34910] type confusion in Array.prototype.filter", - body: function () - { - function mappingFn(elem, index, arr) { - arr[1] = 'hello'; - return true; - } - - var arr = [1, 2, 3]; - - var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); - Object.defineProperty(arr.constructor, Symbol.species, { get : function () { return function() { return [22, 33]; } } } ); - var b = Array.prototype.filter.call(arr, mappingFn); - assert.areEqual('hello', b[1]); - - restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); - } - }, - { - name: "[MSRC35046] heap overflow in Array.prototype.splice", - body: function () - { - var a = []; - var o = {}; - - Object.defineProperty(o, 'constructor', { - get: function() { - a.length = 0xfffffffe; - [].fill.call(a, 0, 0xfffff000, 0xfffffffe); - return Array; - }}); - - a.__proto__ = o; - var q = new Array(50).fill(1.1); - - var b = [].splice.call(a, 0, 0, ...q); - assert.areEqual(50, a.length); - assert.areEqual(q, a); - } - }, - { - name: "[MSRC35086] type confusion in FillFromPrototypes", - body: function () - { - var a = new Array(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x12121212); - - var handler = { - getPrototypeOf: function(target, name) { - return a; - } - }; - - var p = new Proxy([], handler); - var b = [{}, [], "abc"]; - - b.__proto__ = p; - b.length = 4; - var c = [[],"abc",1145324612]; - - a.shift.call(b); - assert.areEqual(3, b.length); - assert.areEqual([], b[0]); - assert.areEqual("abc", b[1]); - assert.areEqual(1145324612, b[2]); - } - }, - { - name: "[MSRC35272] type confusion in JSON.parse", - body: function () - { - var a = 1; - var once = false; - function f(){ - if(!once){ - a = new Array(2) - this[2] = a; - } - once = true; - return 0x41414141; - } - - var r = JSON.parse("[1111, 22222, 333333]", f); - assert.areEqual(0x41414141, r); - } - }, - { - name: "[MSRC35383] type confusion in Array.prototype.concat", - body: function () - { - var n = []; - for (var i = 0; i < 0x10; i++) - n.push([0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111,0x11111111, 0, 0x11111111,0x11111111 ,1 ,2 ,3 ,4]); - - class fake extends Object { - static get [Symbol.species]() { return function() { return n[3]; }; }; - } - - var f = function(a){ return a; } - - var x = ["dabao", 0, 0, 0x41414141]; - var y = new Proxy(x, { - get: function(t, p, r) { - return (p == "constructor") ? fake : x[p]; - } - }); - - assert.areEqual(x, Array.prototype.concat.apply(y)); - } - }, - { - name: "[MSRC35389] type confusion in Array.prototype.splice", - body: function () - { - var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344] - class fake extends Object { - static get [Symbol.species]() { return function() { - return arr; - }; }; - } - - var x = [0, 2, 0, 0x41414141]; - var y = new Proxy(x, { - get: function(t, p, r) { - return (p == "constructor") ? fake : x[p]; - } - }); - - Array.prototype.splice.apply(y); - assert.areEqual(x, y); - } - }, - { - name: "[MSRC35389 variation] type confusion in Array.prototype.slice", - body: function () - { - var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344]; - - class fake extends Object { - static get [Symbol.species]() { return function() { - return arr; - }; }; - } - - var x = [0, 2, 0, 0x41414141]; - var y = new Proxy(x, { - get: function(t, p, r) { - if (p == "constructor") - return fake - if (p == 'length'); - return 1; - return x[p]; - }, - has: function() { - return false; - } - }); - - assert.areEqual([0x41424344], Array.prototype.slice.call(y)); - } - }, - { - name: "[MSRC34994,35226] heap overflow in Array.prototype.reverse", - body: function () - { - var count = 0; - arr = new Array(100); - var desc = Object.getOwnPropertyDescriptor(Array.prototype, 1); - Object.defineProperty(Array.prototype, 1, { get: function () { - count++; - if (count == 1) { - arr.push(null); - } - }}); - - arr.reverse(); - restorePropertyFromDescriptor(Array.prototype, 1, desc); - assert.areEqual(101, arr.length); - } - }, +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +//Note: see function ArraySpliceHelper of JavascriptArray.cpp + +if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch + this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); +} + +var restorePropertyFromDescriptor = function (obj, prop, desc) { + if (typeof desc == 'undefined') { + delete obj[prop]; + } else { + Object.defineProperty(obj, prop, desc); + } +} + +var tests = [ + { + name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements", + body: function () + { + function test() { + var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe]; + + class MyArray extends Uint32Array { } + Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } }); + + var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; + var test = [float_val, float_val, float_val, float_val]; + test.length = 0x1000; + test.__proto__ = new MyArray(0); + + var res = Array.prototype.slice.apply(test, []); // OOB write + assert.areEqual(0x1000, res.length, "res.length == 0x1000"); + assert.areEqual(float_val, res[0], "res[0] == float_val"); + assert.areEqual(float_val, res[1], "res[1] == float_val"); + assert.areEqual(float_val, res[2], "res[2] == float_val"); + assert.areEqual(float_val, res[3], "res[3] == float_val"); + assert.areEqual(undefined, res[4], "res[4] == float_val"); + assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined"); + } + test(); + test(); + test(); + } + }, + { + name: "OS7342689:OOB writes using type confusion in InternalFillFromPrototypes", + body: function () + { + function test() { + var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, + 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, + 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, + 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe]; + + class MyArray extends Uint32Array { } + Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } }); + + var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; + var test = [{}]; + delete test[0]; + test.length = 0x1000; + var src = [float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, + float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, + float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, + float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, + float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val]; + test.__proto__ = src; + test.__proto__.__proto__ = new MyArray(0); + + //this will write 0xfffc0daddeadbabe to [arr1] + 0x1D8 + var res = Array.prototype.slice.apply(test, []) + assert.areEqual(0x1000, res.length, "res.length == 0x1000"); + assert.areEqual(float_val, res[0], "res[0] == float_val"); + assert.areEqual(float_val, res[1], "res[1] == float_val"); + assert.areEqual(float_val, res[2], "res[2] == float_val"); + assert.areEqual(float_val, res[src.length-1], "res[src.length-1] == float_val"); + assert.areEqual(undefined, res[src.length], "res[src] == undefined"); + assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined"); + } + test(); + test(); + test(); + } + }, + { + name: "OS7307908:type confusion in Array.prototype.slice", + body: function () + { + function test() { + var arr = [1, 2] + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); + + //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper + Object.defineProperty( + arr.constructor, + Symbol.species, + { + value : function() + { + //change 'arr' from TypeIds_NativeIntArray to TypeIds_Array + arr[0] = WScript; + + //return a TypeIds_NativeIntArray so we can read back out the 64 bit pointer as two 32bit ints. + return []; + } + } + ); + + //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. + var brr = arr.slice(); + + assert.areEqual(2, brr.length, "brr.length == 2"); + assert.areEqual(WScript, brr[0], "brr[0] == WScript"); + assert.areEqual(2, brr[1], "brr[0] == WScript"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); + } + test(); + test(); + test(); + } + }, + { + name: "OS7342791:type confusion in Array.from", + body: function () + { + function test() { + var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe]; + + var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; + var test = [float_val, float_val, float_val, float_val]; + delete test[0]; + delete test[1]; + delete test[2]; + + var res = Array.from.apply(function(){return arr1}, [test]); + assert.areEqual(4, res.length, "res.length == 4"); + assert.areEqual(undefined, res[0], "res[0] == undefined"); + assert.areEqual(undefined, res[1], "res[1] == undefined"); + assert.areEqual(undefined, res[2], "res[2] == undefined"); + assert.areEqual(float_val, res[3], "res[3] == float_val"); + + assert.areEqual(['1','2','3'], Array.from.apply(()=>new Array(), ["123"]), "Array.from on iterable"); + assert.areEqual([1,2,3], Array.from.apply(()=>new Array(), [{"0":1, "1":2, "2":3, "length":3}]), "Array.from on non-iterable"); + } + test(); + test(); + test(); + } + }, + { + name: "OS7342844:type confusion in Array.of", + body: function () + { + function test() { + var brr = Array.of.call(()=>[ 1, 2, 3, 4 ], + WScript, // supply 2 copies of target so the brr array will have a length of 2 and we can read the 64bit pointer. + WScript + ); + + assert.areEqual(2, brr.length, "brr.length == 2"); + assert.areEqual(WScript, brr[0], "res[0] == WScript"); + assert.areEqual(WScript, brr[1], "res[1] == WScript"); + assert.areEqual(undefined, brr[2], "res[2] == undefined"); + assert.areEqual(undefined, brr[3], "res[3] == undefined"); + } + test(); + test(); + test(); + } + }, + { + name: "OS7342907:type confusion in Array.prototype.map", + body: function () + { + function test() { + var arr = [ 1, 2 ]; + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); + + Object.defineProperty( + arr.constructor, + Symbol.species, + { + value : function() + { + return []; + } + } + ); + + //The value returned from our callback is directly set into the array whose type we create via the species. + var brr = arr.map( function( v ) + { + if( v == 1 ) + return WScript; + } + ); + + assert.areEqual(2, brr.length, "brr.length == 2"); + assert.areEqual(WScript, brr[0], "brr[0] == WScript"); + assert.areEqual(undefined, brr[1], "brr[1] == undefined"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); + } + test(); + test(); + test(); + } + }, + { + name: "type confusion in Array.prototype.map with Proxy", + body: function () + { + function test() { + var d = [1,2,3]; + class dummy { + constructor() { + return d; + } + } + + var handler = { + get: function(target, name) { + if(name == "length") { + return 0x100; + } + + return {[Symbol.species] : dummy}; + }, + + has: function(target, name) { + return true; + } + }; + + var p = new Proxy([], handler); + var a = new Array(1,2,3); + + function test(){ + return 0x777777777777; + } + + var o = a.map.call(p, test); + assert.areEqual(Array(0x100).fill(0x777777777777), o); + } + test(); + test(); + test(); + } + }, + { + name: "OS7342965:type confusion in Array.prototype.splice", + body: function () + { + function test() { + //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values). + var arr = [ WScript, WScript ]; + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); + + //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice + Object.defineProperty( + arr.constructor, + Symbol.species, + { + value : function() + { + //return a TypeIds_NativeIntArray so we can read back out a 64 bit pointer as two 32bit ints. + return [ 1, 2, 3, 4 ]; + } + } + ); + + //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. The helper + //method ArraySegmentSpliceHelper will directly copy over the TypeIds_Array segment data + //into the TypeIds_NativeIntArray segment. + var brr = arr.splice( 0, 2 ); + + assert.areEqual(2, brr.length, "brr.length == 2"); + assert.areEqual(WScript, brr[0], "brr[0] == WScript"); + assert.areEqual(WScript, brr[1], "brr[1] == WScript"); + assert.areEqual(undefined, brr[2], "brr[2] == undefined"); + assert.areEqual(undefined, brr[3], "brr[3] == undefined"); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); + } + test(); + test(); + test(); + } + }, + { + name: "type confusion in Array.prototype.join", + body: function () + { + function test() { + var a = [0, 1, 2, 3]; + var b = []; + delete a[0]; + Object.setPrototypeOf(a, b) + Object.defineProperty(b, "0", + { + get: function() { + a[2] = "abc"; + return -1; + } + }); + + assert.areEqual("-1,1,abc,3", a.join()); + } + test(); + test(); + test(); + } + }, + { + name: "type confusion in Array.prototype.indexOf", + body: function () + { + function test() { + var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; + var a = [0, 1, 2, 3]; + var b = []; + delete a[1]; + Object.setPrototypeOf(a, b); + Object.defineProperty(b, "1", + { + get: function() { + a[2] = float_val; //"abc"; + return -1; + } + }); + + assert.areEqual(3, a.indexOf(3)); + } + test(); + test(); + test(); + } + }, + { + name: "type confusion in Array.prototype.lastIndexOf", + body: function () + { + function test() { + var float_val = 0xdaddeadbabe * 4.9406564584124654E-324; + var a = [3, 2, 1, 0]; + var b = []; + delete a[3]; + Object.setPrototypeOf(a, b); + Object.defineProperty(b, "3", + { + get: function() { + a[1] = float_val; //"abc"; + return -1; + } + }); + + assert.areEqual(0, a.lastIndexOf(3)); + } + test(); + test(); + test(); + } + }, + { + name: "type confusion in Function.prototype.apply", + body: function () + { + function test() { + var t = [1,2,3]; + + function f(){ + var h = []; + var a = [...arguments] + + for(item in a){ + var n = new Number(a[item]); + + if( n < 0) { + n = n + 0x100000000; + } + + h.push(n.toString(16)); + } + + return h; + } + + var q = f; + + t.length = 20; + var o = {}; + Object.defineProperty(o, '3', { + get: function() { + var ta = []; + ta.fill.call(t, "natalie"); + return 5; + } + }); + + t.__proto__ = o; + + var j = []; + assert.areEqual("1,2,3,5,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN", f.apply(null, t).toString()); + } + test(); + test(); + test(); + } + }, + { + name: "[MSRC34910] type confusion in Array.prototype.filter", + body: function () + { + function mappingFn(elem, index, arr) { + arr[1] = 'hello'; + return true; + } + + var arr = [1, 2, 3]; + + var desc = Object.getOwnPropertyDescriptor(arr.constructor, Symbol.species); + Object.defineProperty(arr.constructor, Symbol.species, { get : function () { return function() { return [22, 33]; } } } ); + var b = Array.prototype.filter.call(arr, mappingFn); + assert.areEqual('hello', b[1]); + + restorePropertyFromDescriptor(arr.constructor, Symbol.species, desc); + } + }, + { + name: "[MSRC35046] heap overflow in Array.prototype.splice", + body: function () + { + var a = []; + var o = {}; + + Object.defineProperty(o, 'constructor', { + get: function() { + a.length = 0xfffffffe; + [].fill.call(a, 0, 0xfffff000, 0xfffffffe); + return Array; + }}); + + a.__proto__ = o; + var q = new Array(50).fill(1.1); + + var b = [].splice.call(a, 0, 0, ...q); + assert.areEqual(50, a.length); + assert.areEqual(q, a); + } + }, + { + name: "[MSRC35086] type confusion in FillFromPrototypes", + body: function () + { + var a = new Array(0x11111111, 0x22222222, 0x33333333, 0x44444444, 0x12121212); + + var handler = { + getPrototypeOf: function(target, name) { + return a; + } + }; + + var p = new Proxy([], handler); + var b = [{}, [], "abc"]; + + b.__proto__ = p; + b.length = 4; + var c = [[],"abc",1145324612]; + + a.shift.call(b); + assert.areEqual(3, b.length); + assert.areEqual([], b[0]); + assert.areEqual("abc", b[1]); + assert.areEqual(1145324612, b[2]); + } + }, + { + name: "[MSRC35272] type confusion in JSON.parse", + body: function () + { + var a = 1; + var once = false; + function f(){ + if(!once){ + a = new Array(2) + this[2] = a; + } + once = true; + return 0x41414141; + } + + var r = JSON.parse("[1111, 22222, 333333]", f); + assert.areEqual(0x41414141, r); + } + }, + { + name: "[MSRC35383] type confusion in Array.prototype.concat", + body: function () + { + var n = []; + for (var i = 0; i < 0x10; i++) + n.push([0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111, 0x11111111, 0, 0x11111111,0x11111111,0x11111111, 0, 0x11111111,0x11111111 ,1 ,2 ,3 ,4]); + + class fake extends Object { + static get [Symbol.species]() { return function() { return n[3]; }; }; + } + + var f = function(a){ return a; } + + var x = ["dabao", 0, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + return (p == "constructor") ? fake : x[p]; + } + }); + + assert.areEqual(x, Array.prototype.concat.apply(y)); + } + }, + { + name: "[MSRC35389] type confusion in Array.prototype.splice", + body: function () + { + var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344] + class fake extends Object { + static get [Symbol.species]() { return function() { + return arr; + }; }; + } + + var x = [0, 2, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + return (p == "constructor") ? fake : x[p]; + } + }); + + Array.prototype.splice.apply(y); + assert.areEqual(x, y); + } + }, + { + name: "[MSRC35389 variation] type confusion in Array.prototype.slice", + body: function () + { + var arr = [0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344,0x41424344]; + + class fake extends Object { + static get [Symbol.species]() { return function() { + return arr; + }; }; + } + + var x = [0, 2, 0, 0x41414141]; + var y = new Proxy(x, { + get: function(t, p, r) { + if (p == "constructor") + return fake + if (p == 'length'); + return 1; + return x[p]; + }, + has: function() { + return false; + } + }); + + assert.areEqual([0x41424344], Array.prototype.slice.call(y)); + } + }, + { + name: "[MSRC34994,35226] heap overflow in Array.prototype.reverse", + body: function () + { + var count = 0; + arr = new Array(100); + var desc = Object.getOwnPropertyDescriptor(Array.prototype, 1); + Object.defineProperty(Array.prototype, 1, { get: function () { + count++; + if (count == 1) { + arr.push(null); + } + }}); + + arr.reverse(); + restorePropertyFromDescriptor(Array.prototype, 1, desc); + assert.areEqual(101, arr.length); + } + }, { name: "Heap overread when splice mutates the array when executing slice", body: function () @@ -620,5 +620,5 @@ var tests = [ assert.areEqual(100 * 1024 + 2, b.length, "Validating that slice will return the full array even though splice is deleting the whole array"); } }, -]; -testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); +]; +testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); diff --git a/test/DebuggerCommon/returnedvaluetests4.js.dbg.baseline b/test/DebuggerCommon/returnedvaluetests4.js.dbg.baseline index 65dacc2ab27..f3e73faeae7 100644 --- a/test/DebuggerCommon/returnedvaluetests4.js.dbg.baseline +++ b/test/DebuggerCommon/returnedvaluetests4.js.dbg.baseline @@ -9,6 +9,9 @@ { "this": "Object {...}", "arguments": "Object {...}", + "functionCallsReturn": { + "[Date returned]": "string " + }, "locals": { "a": "string " } @@ -37,6 +40,9 @@ { "this": "Object {...}", "arguments": "Object {...}", + "functionCallsReturn": { + "[Array returned]": "Array [a,b]" + }, "locals": { "arr": "Array [a,b]", "str": "undefined undefined", diff --git a/test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline b/test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline index e1362535e5d..dc8383c2f14 100644 --- a/test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline +++ b/test/DebuggerCommon/step_in_from_interpreted_function_attach.js.dbg.baseline @@ -99,6 +99,62 @@ } }, "[Return value]": "undefined undefined", + "functionCallsReturn": { + "[Date returned]": { + "#__proto__": { + "#__proto__": "Object {...}", + "constructor": "function ", + "getDate": "function ", + "getDay": "function ", + "getFullYear": "function ", + "getHours": "function ", + "getMilliseconds": "function ", + "getMinutes": "function ", + "getMonth": "function ", + "getSeconds": "function ", + "getTime": "function ", + "getTimezoneOffset": "function ", + "getUTCDate": "function ", + "getUTCDay": "function ", + "getUTCFullYear": "function ", + "getUTCHours": "function ", + "getUTCMilliseconds": "function ", + "getUTCMinutes": "function ", + "getUTCMonth": "function ", + "getUTCSeconds": "function ", + "getVarDate": "function ", + "getYear": "function ", + "setDate": "function ", + "setFullYear": "function ", + "setHours": "function ", + "setMilliseconds": "function ", + "setMinutes": "function ", + "setMonth": "function ", + "setSeconds": "function ", + "setTime": "function ", + "setUTCDate": "function ", + "setUTCFullYear": "function ", + "setUTCHours": "function ", + "setUTCMilliseconds": "function ", + "setUTCMinutes": "function ", + "setUTCMonth": "function ", + "setUTCSeconds": "function ", + "setYear": "function ", + "toDateString": "function ", + "toISOString": "function ", + "toJSON": "function ", + "toLocaleDateString": "function ", + "toLocaleString": "function ", + "toLocaleTimeString": "function ", + "toString": "function ", + "toTimeString": "function ", + "toUTCString": "function ", + "toGMTString": "function ", + "valueOf": "function ", + "Symbol.toPrimitive": "function " + } + } + }, "locals": { "a": "string bar", "b": { diff --git a/test/Error/validate_line_column.js b/test/Error/validate_line_column.js index 49a81b3b6ce..38db39a6d37 100644 --- a/test/Error/validate_line_column.js +++ b/test/Error/validate_line_column.js @@ -1,82 +1,82 @@ -//------------------------------------------------------------------------------------------------------- -// Copyright (C) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -//------------------------------------------------------------------------------------------------------- - -// Validating that error thrown has right line and column number - -function foo(validate) { - try { - validate(); - } catch (e) { - print(e.stack); - } -} - -foo(function() { - ([z1]); // Error thrown here. -}); - -foo(function() { - ({a:z1}); // Error thrown here. -}); - -foo(function() { - var a; - a;class b extends ([]){}; // Error thrown here. -}); - -foo(function() { - (typeof a.b); // Error thrown here. -}); - -foo(function() { - var k = 1; - !a.b; // Error thrown here. -}); - -foo(function() { - var k = 1; - ~a.b; // Error thrown here. -}); - -foo(function() { - var k = 1; - (a.b && a.b); // Error thrown here. -}); - -foo(function() { - var k = 1; - (a.b || a.b); // Error thrown here. -}); - -foo(function() { - var k = 1; - (a.b * a.b); // Error thrown here. -}); - -foo(function() { - var k = 1; - `${a.b}`; -}); - -foo(function() { - var k = 1; - while(unresolved[0]) { // Error thrown here. - break; - } -}); - -foo(function() { - var k = 1; - while(typeof unresolved[0]) { // Error thrown here. - break; - } -}); - -foo(function() { - var k = 1; - while(unresolved instanceof blah) { // Error thrown here. - break; - } -}); +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +// Validating that error thrown has right line and column number + +function foo(validate) { + try { + validate(); + } catch (e) { + print(e.stack); + } +} + +foo(function() { + ([z1]); // Error thrown here. +}); + +foo(function() { + ({a:z1}); // Error thrown here. +}); + +foo(function() { + var a; + a;class b extends ([]){}; // Error thrown here. +}); + +foo(function() { + (typeof a.b); // Error thrown here. +}); + +foo(function() { + var k = 1; + !a.b; // Error thrown here. +}); + +foo(function() { + var k = 1; + ~a.b; // Error thrown here. +}); + +foo(function() { + var k = 1; + (a.b && a.b); // Error thrown here. +}); + +foo(function() { + var k = 1; + (a.b || a.b); // Error thrown here. +}); + +foo(function() { + var k = 1; + (a.b * a.b); // Error thrown here. +}); + +foo(function() { + var k = 1; + `${a.b}`; // Error thrown here. +}); + +foo(function() { + var k = 1; + while(unresolved[0]) { // Error thrown here. + break; + } +}); + +foo(function() { + var k = 1; + while(typeof unresolved[0]) { // Error thrown here. + break; + } +}); + +foo(function() { + var k = 1; + while(unresolved instanceof blah) { // Error thrown here. + break; + } +}); diff --git a/test/Strings/lastindexof.js b/test/Strings/lastindexof.js index 3de14933f7e..c1bcb329251 100644 --- a/test/Strings/lastindexof.js +++ b/test/Strings/lastindexof.js @@ -78,7 +78,7 @@ var tests = [ { name: "Substring matching", body: function () { - + for (var str of [new String(sevenBitStr), new String(eightBitStr), new String(unicodeStr)]) { for (var i = 0; i < str.length; ++i) { var match = str.substring(i); diff --git a/test/es6/ES6NewTarget_bugfixes.js b/test/es6/ES6NewTarget_bugfixes.js index c5aecfc89a8..cfe2dbcce61 100644 --- a/test/es6/ES6NewTarget_bugfixes.js +++ b/test/es6/ES6NewTarget_bugfixes.js @@ -6,25 +6,31 @@ WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); var tests = [ - { - name: "OS4497597: ScopeInfo::FromScope() should increment scope symbol count to accomodate 'new.target'", - body: function () { - (function (){ - function f() {} - eval(""); - () =>new.target; - })(); - // Repro: - // ASSERTION : (jscript\core\lib\Runtime\ByteCode\ScopeInfo.h, line 68) - // Failure: (i >= 0 && i < symbolCount) - } - }, - { - name: "OS5427497: Parser mistakes 'new.target' as in global function under -forceundodefer", - body: function () { - new.target; // bug repro: SyntaxError: Invalid use of the 'new.target' keyword - } - }, + { + name: "OS4497597: ScopeInfo::FromScope() should increment scope symbol count to accomodate 'new.target'", + body: function () { + (function (){ + function f() {} + eval(""); + () =>new.target; + })(); + // Repro: + // ASSERTION : (jscript\core\lib\Runtime\ByteCode\ScopeInfo.h, line 68) + // Failure: (i >= 0 && i < symbolCount) + } + }, + { + name: "OS5427497: Parser mistakes 'new.target' as in global function under -forceundodefer", + body: function () { + new.target; // bug repro: SyntaxError: Invalid use of the 'new.target' keyword + } + }, + { + name: "OS8806229: eval in default parameter of arrow function", + body: function() { + assert.doesNotThrow(()=>(function() { (a = eval(undefined)) => {}; })); + } + }, { name: "[MSRC35208] parameter type confusion in eval", body: function () diff --git a/test/es6/ES6TypedArrayExtensions.js b/test/es6/ES6TypedArrayExtensions.js index 61ddb0d7e05..72f55d5a351 100644 --- a/test/es6/ES6TypedArrayExtensions.js +++ b/test/es6/ES6TypedArrayExtensions.js @@ -1065,6 +1065,44 @@ var tests = [ assert.isFalse(ArrayBuffer.isView(res), "Array.prototype.map returns a normal array object even if the this parameter is a TypedArray"); } }, + { + name: "Array.prototype.map called with a typedarray having different length", + body: function() { + var counter = 0; + var fn = function(elem) { + counter++; + return elem; + }; + + // Validating how many times the map function is called. + [[-1, 0], [2, 2], [100, 8], [2**31, 8]].forEach(function ([len, expectedCounter]) { + var v = new Int8Array(8); + counter = 0; + Object.defineProperty(v, 'length', {value : len }); + Array.prototype.map.call(v, fn); + assert.areEqual(counter, expectedCounter); + }); + } + }, + { + name: "Array.prototype.find called with a typedarray having different length", + body: function() { + var counter = 0; + var fn = function(elem) { + counter++; + return elem; + }; + + // Validating how many times the find function is called. + [[-1, 0], [2, 2], [100, 100]].forEach(function ([len, expectedCounter]) { + var v = new Int8Array(8); + counter = 0; + Object.defineProperty(v, 'length', {value : len }); + Array.prototype.find.call(v, fn); + assert.areEqual(counter, expectedCounter); + }); + } + }, { name: "%TypedArray%.prototype.forEach behavior", body: function() { diff --git a/test/es6/es6_stable.baseline b/test/es6/es6_stable.baseline index 9f4ea4912e1..9e8eccdcaa0 100644 --- a/test/es6/es6_stable.baseline +++ b/test/es6/es6_stable.baseline @@ -55,8 +55,8 @@ FLAG ES6 = 1 - setting child flag ES6PrototypeChain = 0 FLAG ES6PrototypeChain = 0 FLAG ES6 = 1 - setting child flag ES6ToPrimitive = 1 FLAG ES6ToPrimitive = 1 -FLAG ES6 = 1 - setting child flag ES6ToLength = 0 -FLAG ES6ToLength = 0 +FLAG ES6 = 1 - setting child flag ES6ToLength = 1 +FLAG ES6ToLength = 1 FLAG ES6 = 1 - setting child flag ES6ToStringTag = 1 FLAG ES6ToStringTag = 1 FLAG ES6 = 1 - setting child flag ES6Unicode = 1 diff --git a/test/es6/es6_stable.enable_disable.baseline b/test/es6/es6_stable.enable_disable.baseline index 74e50315ad3..044f97b6c0e 100644 --- a/test/es6/es6_stable.enable_disable.baseline +++ b/test/es6/es6_stable.enable_disable.baseline @@ -55,8 +55,8 @@ FLAG ES6 = 1 - setting child flag ES6PrototypeChain = 0 FLAG ES6PrototypeChain = 0 FLAG ES6 = 1 - setting child flag ES6ToPrimitive = 1 FLAG ES6ToPrimitive = 1 -FLAG ES6 = 1 - setting child flag ES6ToLength = 0 -FLAG ES6ToLength = 0 +FLAG ES6 = 1 - setting child flag ES6ToLength = 1 +FLAG ES6ToLength = 1 FLAG ES6 = 1 - setting child flag ES6ToStringTag = 1 FLAG ES6ToStringTag = 1 FLAG ES6 = 1 - setting child flag ES6Unicode = 1 diff --git a/test/es6/moduletest1.js b/test/es6/moduletest1.js index 7c2e3f1387c..bc57431a212 100644 --- a/test/es6/moduletest1.js +++ b/test/es6/moduletest1.js @@ -2,4 +2,4 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- -WScript.LoadScriptFile("passmodule.js", "module"); +WScript.LoadScriptFile("passmodule.js", "module"); diff --git a/test/es6/proxybug.js b/test/es6/proxybug.js new file mode 100644 index 00000000000..fb079261296 --- /dev/null +++ b/test/es6/proxybug.js @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- +var func3 = function () +{ + var sc4 = WScript.LoadScript('function test(){ obj2.prop4 = {needMarshal:true}; }', 'samethread'); + var obj1=new Proxy({}, {set:function(target, property, value) { Reflect.set(value);}}) + sc4.obj2 = obj1; + sc4.test(); + obj1.prop4 = {needMarshal:false}; + obj1.prop5 = {needMarshal:false}; +}; +func3(); + + + +var bug = new Proxy(new Array(1), {has: () => true}); +var a = bug.concat(); +if (a[0] !== undefined || a.length !== 1) { + print("failed"); +} else { + print("passed"); +} diff --git a/test/es6/proxybugs.js b/test/es6/proxybugs.js index e8e21577d75..900d686750c 100644 --- a/test/es6/proxybugs.js +++ b/test/es6/proxybugs.js @@ -102,6 +102,23 @@ var tests = [ assert.isTrue(getOwnPropertyDescriptorCalled); } }, + { + name: "Cross-site on proxy exercising function trap - no function handler provided", + body() { + var targetCalled = false; + var func4 = function () { targetCalled = true; }; + var v0 = new Proxy(func4, {}); + + var anotherScript = `function foo() { + var a = undefined; + v0(a) > 1; + }`; + var sc0 = WScript.LoadScript(anotherScript, 'samethread'); + sc0.v0 = v0; + sc0.foo(); + assert.isTrue(targetCalled); + } + }, { name: "Type confusion in JavascriptProxy::SetPropertyTrap when using a Symbol", body: function () { @@ -111,7 +128,29 @@ var tests = [ var obj1 = Object.create(new Proxy({}, {})); obj1[Symbol.species] = 0; } - } + }, + { + name: "Cross-site on proxy exercising function trap - with 'apply' function trap", + body() { + var trapCalled = false; + var func4 = function () {}; + var handler = { + apply : function(a, b, c) { + trapCalled = true; + } + }; + var v0 = new Proxy(func4, handler); + + var anotherScript = `function foo() { + var a = undefined; + v0(a) > 1; + }`; + var sc0 = WScript.LoadScript(anotherScript, 'samethread'); + sc0.v0 = v0; + sc0.foo(); + assert.isTrue(trapCalled); + } + }, ]; testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); diff --git a/test/es6/rlexe.xml b/test/es6/rlexe.xml index 35be57110c9..09e5d68a775 100644 --- a/test/es6/rlexe.xml +++ b/test/es6/rlexe.xml @@ -500,7 +500,7 @@ es6_stable.js - -verbose -es6 -ES6DefaultArgs -oopjit- -WERExceptionSupport + -verbose -es6 -ES6DefaultArgs -WERExceptionSupport -oopjit- es6_stable.baseline exclude_dynapogo @@ -509,7 +509,7 @@ es6_stable.js - -verbose -es6 -es6- -ES6DefaultArgs -oopjit- -WERExceptionSupport + -verbose -es6 -es6- -ES6DefaultArgs -WERExceptionSupport -oopjit- es6_stable.enable_disable.baseline exclude_dynapogo diff --git a/test/es6/spread.js b/test/es6/spread.js index 40de318c179..0018f46e7a9 100644 --- a/test/es6/spread.js +++ b/test/es6/spread.js @@ -412,6 +412,86 @@ var tests = [ assert.throws(function () { eval("foo(typeof ...[1,2,3]);"); }, SyntaxError, "Spread with keyword unary operator throws a syntax error", "Unexpected ... operator"); assert.throws(function () { eval("foo(!!...[1,2,3]);"); }, SyntaxError, "Spread with chained unary operators throws a syntax error", "Unexpected ... operator"); } + }, + { + name: "call scenario - second spread is changing the first spread's length", + body: function () { + function foo() { + var args = [...arguments]; + assert.areEqual([101, 102, 201], args, "2 values from the first spread and 1 value from the second spread is expected"); + } + + var first = [101, 102]; + + var obj = {}; + Object.defineProperty(obj, '2', {get : function() { + assert.fail('this should not have called') + return 103; + }}); + + var second = []; + second.length = 1; + var getterCalled = false; + Object.defineProperty(second, '0', {get : function() { + // Changing the state of the first spread + first.__proto__ = obj; + first.length = 3; + getterCalled = true; + return 201; + }}); + + foo(...first, ...second); + assert.isTrue(getterCalled, "getter of the second spread is executed"); + } + }, + { + name: "array scenario - second spread is changing the first spread's length", + body: function () { + + var first = [101, 102]; + + var obj = {}; + Object.defineProperty(obj, '2', {get : function() { + assert.fail('this should not have called') + return 103; + }}); + + var second = []; + second.length = 1; + var getterCalled = false; + Object.defineProperty(second, '0', {get : function() { + // Changing the state of the first spread + first.__proto__ = obj; + first.length = 3; + getterCalled = true; + return 201; + }}); + + var result = [...first, ...second]; + assert.areEqual([101, 102, 201], result, "2 values from the first spread and 1 value from the second spread is expected"); + assert.isTrue(getterCalled, "getter of the second spread is executed"); + } + }, + { + name: "typedarray scenario - second spread is changing value of first spread which is a typedarray", + body: function () { + + var first = new Uint32Array([101, 102]); + + var second = []; + second.length = 1; + var getterCalled = false; + Object.defineProperty(second, '0', {get : function() { + // Changing the state of the first spread + first[0] = 11; // This should not affect the resultant spread. + getterCalled = true; + return 201; + }}); + + var result = [...first, ...second]; + assert.areEqual([101, 102, 201], result, "2 values from the first spread and 1 value from the second spread is expected"); + assert.isTrue(getterCalled, "getter of the second spread is executed"); + } } ]; diff --git a/test/es7/asyncawait-functionality.baseline b/test/es7/asyncawait-functionality.baseline index 40025eda42c..77b77f8f850 100644 --- a/test/es7/asyncawait-functionality.baseline +++ b/test/es7/asyncawait-functionality.baseline @@ -34,6 +34,9 @@ Executing test #28 - Async function with try-catch and try-finally in the body Executing test #29 - Async function and with Executing test #30 - Async and arguments.callee Executing test #31 - Async and arguments.caller +Executing test #32 - Async and split scope +Test #32 - Success initial value of the formal is the same as the default param value +Test #32 - Success initial value of the body symbol is the same as the default param value Completion Results: Test #1 - Success lambda expression with no argument called with result = 'true' @@ -78,6 +81,8 @@ Test #27 - Success Caught the expected exception inside catch in async body Test #28 - Success Caught the expected exception inside the inner catch in async body Test #28 - Success finally block is executed in async body Test #30 - Success async function and arguments.callee +Test #33 - Success updated value of the formal is the same as the value returned from the second async function +Test #33 - Success updated value of the body symbol is the same as the value returned from the second async function Test #6 - Success await in an async function #1 called with result = '-4' Test #6 - Success await in an async function #2 called with result = '2' Test #6 - Success await in an async function catch a rejected Promise in 'err'. Error = 'Error: My Error' @@ -91,6 +96,8 @@ Test #23 - Success functions completes the second await call Test #24 - Success caught the expected exception Test #25 - Success caught the expected exception Test #31 - Success async function returned through caller property is the same as the original async function +Test #33 - Success value returned through await is assigned to the formal +Test #33 - Success value returned through await is not assigned to the formal Test #8 - Success async function with default arguments's value has been rejected as expected by 'err' #2 called with err = 'expected error' Test #9 - Success resolved promise in an async function #1 called with result = 'resolved' Test #9 - Success promise in an async function has been rejected as expected by 'err' #3 called with err = 'rejected' diff --git a/test/es7/asyncawait-functionality.js b/test/es7/asyncawait-functionality.js index 9199a89933b..bd339a87715 100644 --- a/test/es7/asyncawait-functionality.js +++ b/test/es7/asyncawait-functionality.js @@ -973,6 +973,70 @@ var tests = [ } ) } + }, + { + name: "Async and split scope", + body: function () { + async function asyncMethod1(b) { + return b() + 100; + } + async function asynMethod2(a = 10, b = () => a) { + if (a === 10) { + echo(`Test #${index} - Success initial value of the formal is the same as the default param value`); + } else { + echo(`Test #${index} - Failed initial value of the formal is not the same as the default param value, expected 10, result = ${a}`); + } + a = await asyncMethod1(b); + if (a === 110) { + echo(`Test #${index} - Success updated value of the formal is the same as the value returned from the second async function`); + } else { + echo(`Test #${index} - Failed updated value of the formal is not the same as the value returned from the second async function, expected 110, result = ${a}`); + } + return b; + } + asynMethod2().then( + result => { + if (result() === 110) { + echo(`Test #${index} - Success value returned through await is assigned to the formal`); + } else { + echo(`Test #${index} - Failed value returned through the await is different from the expected 110, result = ${result()}`); + } + }, + error => { + echo(`Test #${index} - Failed error while trying to return through the await in a split scope function, expected 100, error = ${error}`); + } + ); + + async function asyncMethod3(b) { + return b() + 100; + } + async function asynMethod4(a = 10, b = () => a) { + if (a === 10) { + echo(`Test #${index} - Success initial value of the body symbol is the same as the default param value`); + } else { + echo(`Test #${index} - Failed initial value of the body symbol is not the same as the default param value, expected 10, result = ${a}`); + } + var a = await asyncMethod3(b); + if (a === 110) { + echo(`Test #${index} - Success updated value of the body symbol is the same as the value returned from the second async function`); + } else { + echo(`Test #${index} - Failed updated value of the body symbol is not the same as the value returned from the second async function, expected 110, result = ${a}`); + } + return b; + } + asynMethod4().then( + result => { + if (result() === 10) { + echo(`Test #${index} - Success value returned through await is not assigned to the formal`); + } else { + echo(`Test #${index} - Failed value of the formal is different from the expected 10, result = ${result()}`); + } + }, + error => { + echo(`Test #${index} - Failed error while trying to return through the await in a split scope function with duplicate symbol in the body, expected 100, error = ${error}`); + } + ); + } } ]; diff --git a/test/typedarray/rlexe.xml b/test/typedarray/rlexe.xml index 376f79ab320..35110711816 100644 --- a/test/typedarray/rlexe.xml +++ b/test/typedarray/rlexe.xml @@ -290,6 +290,13 @@ Below test fails with difference in space. Investigate the cause and re-enable t typedarray + + + transferdetach.js + -ArrayBufferTransfer + typedarray + + memset.js diff --git a/test/typedarray/transferdetach.js b/test/typedarray/transferdetach.js new file mode 100644 index 00000000000..2a929066dbd --- /dev/null +++ b/test/typedarray/transferdetach.js @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- +if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch + this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); +} + +var a = new ArrayBuffer(0x10); + +function foo() { + detach(a); + return 6; +}; + +function detach(ab) { + var c = ArrayBuffer.transfer(a, 0x10); +}; + +var obj = { + valueOf: foo +}; + +function test() { + ArrayBuffer.transfer(a, obj); +} + +assert.throws(test, TypeError, "", "ArrayBuffer.transfer: The ArrayBuffer is detached."); + +print("pass");