diff --git a/Build/NuGet/.pack-version b/Build/NuGet/.pack-version index 29eb2917fe6..40faed9aa40 100644 --- a/Build/NuGet/.pack-version +++ b/Build/NuGet/.pack-version @@ -1 +1 @@ -1.11.7 +1.11.8 diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index f09edda7745..76af5504773 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -3258,10 +3258,14 @@ GlobOpt::OptSrc(IR::Opnd *opnd, IR::Instr * *pInstr, Value **indirIndexValRef, I } originalPropertySym = sym->AsPropertySym(); - // Dont give a vale to 'arguments' property sym to prevent field copy prop of 'arguments' + // Don't give a value to 'arguments' property sym to prevent field copy prop of 'arguments' if (originalPropertySym->AsPropertySym()->m_propertyId == Js::PropertyIds::arguments && originalPropertySym->AsPropertySym()->m_fieldKind == PropertyKindData) { + if (opnd->AsSymOpnd()->IsPropertySymOpnd()) + { + this->FinishOptPropOp(instr, opnd->AsPropertySymOpnd()); + } return nullptr; } @@ -4813,7 +4817,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) } else { - return NewGenericValue(src1ValueInfo->Type().ToDefiniteAnyNumber(), dst); + return NewGenericValue(src1ValueInfo->Type().ToDefiniteAnyNumber().SetCanBeTaggedValue(true), dst); } break; @@ -4874,7 +4878,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) { valueType = ValueType::Number; } - return CreateDstUntransferredValue(valueType, instr, src1Val, src2Val); + return CreateDstUntransferredValue(valueType.SetCanBeTaggedValue(true), instr, src1Val, src2Val); } case Js::OpCode::Add_A: @@ -4908,12 +4912,12 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) { // If one of them is a float, the result probably is a float instead of just int // but should always be a number. - valueType = ValueType::Float; + valueType = ValueType::Float.SetCanBeTaggedValue(true); } else { // Could be int, could be number - valueType = ValueType::Number; + valueType = ValueType::Number.SetCanBeTaggedValue(true); } } else if (src1ValueInfo->IsLikelyFloat() || src2ValueInfo->IsLikelyFloat()) @@ -4937,7 +4941,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) && (src2Val && src2ValueInfo->IsNotString() && src2ValueInfo->IsPrimitive())) { // If src1 and src2 are not strings and primitive, add should yield a number. - valueType = ValueType::Number; + valueType = ValueType::Number.SetCanBeTaggedValue(true); } else if((src1Val && src1ValueInfo->IsLikelyString()) || (src2Val && src2ValueInfo->IsLikelyString())) { @@ -4958,7 +4962,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) ValueType divValueType = GetDivValueType(instr, src1Val, src2Val, false); if (divValueType.IsLikelyInt() || divValueType.IsFloat()) { - return CreateDstUntransferredValue(divValueType, instr, src1Val, src2Val); + return CreateDstUntransferredValue(divValueType.SetCanBeTaggedValue(true), instr, src1Val, src2Val); } } // fall-through @@ -4990,11 +4994,11 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val) // This should ideally be NewNumberAndLikelyFloatValue since we know the result is a number but not sure if it will // be a float value. However, that Number/LikelyFloat value type doesn't exist currently and all the necessary // checks are done for float values (tagged int checks, etc.) so it's sufficient to just create a float value here. - valueType = ValueType::Float; + valueType = ValueType::Float.SetCanBeTaggedValue(true); } else { - valueType = ValueType::Number; + valueType = ValueType::Number.SetCanBeTaggedValue(true); } return CreateDstUntransferredValue(valueType, instr, src1Val, src2Val); diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index 6a8007d3e46..dbddb245921 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -237,10 +237,17 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms SetAnyPropertyMayBeWrittenTo(); } - else if (inGlobOpt && indexOpnd && !indexOpnd->GetValueType().IsInt() && !currentBlock->globOptData.IsInt32TypeSpecialized(indexOpnd->m_sym)) + else if (inGlobOpt) { - // Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout - this->KillAllObjectTypes(bv); + Value * indexValue = indexOpnd ? this->currentBlock->globOptData.FindValue(indexOpnd->GetSym()) : nullptr; + ValueInfo * indexValueInfo = indexValue ? indexValue->GetValueInfo() : nullptr; + int indexLowerBound = 0; + + if (indirOpnd->GetOffset() < 0 || (indexOpnd && (!indexValueInfo || !indexValueInfo->TryGetIntConstantLowerBound(&indexLowerBound, false) || indexLowerBound < 0))) + { + // Write/delete to a non-integer numeric index can't alias a name on the RHS of a dot, but it change object layout + this->KillAllObjectTypes(bv); + } } } @@ -487,7 +494,9 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo case Js::OpCode::NewScObjectNoCtor: if (inGlobOpt) { - KillObjectHeaderInlinedTypeSyms(this->currentBlock, false); + // Opcodes that make an object into a prototype may break object-header-inlining and final type opt. + // Kill all known object layouts. + KillAllObjectTypes(bv); } break; diff --git a/lib/Backend/JITType.cpp b/lib/Backend/JITType.cpp index a207dc5ffcf..a235bd02d31 100644 --- a/lib/Backend/JITType.cpp +++ b/lib/Backend/JITType.cpp @@ -35,7 +35,7 @@ JITType::BuildFromJsType(__in Js::Type * jsType, __out JITType * jitType) Js::DynamicTypeHandler * handler = dynamicType->GetTypeHandler(); data->handler.isObjectHeaderInlinedTypeHandler = handler->IsObjectHeaderInlinedTypeHandler(); - data->handler.isLocked = handler->GetIsLocked(); + data->handler.flags = handler->GetFlags(); data->handler.inlineSlotCapacity = handler->GetInlineSlotCapacity(); data->handler.offsetOfInlineSlots = handler->GetOffsetOfInlineSlots(); data->handler.slotCapacity = handler->GetSlotCapacity(); diff --git a/lib/Backend/JITTypeHandler.cpp b/lib/Backend/JITTypeHandler.cpp index aca726bb060..dbecfb9dafa 100644 --- a/lib/Backend/JITTypeHandler.cpp +++ b/lib/Backend/JITTypeHandler.cpp @@ -19,7 +19,13 @@ JITTypeHandler::IsObjectHeaderInlinedTypeHandler() const bool JITTypeHandler::IsLocked() const { - return m_data.isLocked != FALSE; + return Js::DynamicTypeHandler::GetIsLocked(m_data.flags); +} + +bool +JITTypeHandler::IsPrototype() const +{ + return Js::DynamicTypeHandler::GetIsPrototype(m_data.flags); } uint16 diff --git a/lib/Backend/JITTypeHandler.h b/lib/Backend/JITTypeHandler.h index c6e909fd7a2..9d2cc35bb93 100644 --- a/lib/Backend/JITTypeHandler.h +++ b/lib/Backend/JITTypeHandler.h @@ -12,6 +12,7 @@ class JITTypeHandler bool IsObjectHeaderInlinedTypeHandler() const; bool IsLocked() const; + bool IsPrototype() const; uint16 GetInlineSlotCapacity() const; uint16 GetOffsetOfInlineSlots() const; diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index cb3efbe0efe..a5d19c4021e 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -6223,7 +6223,7 @@ Lowerer::GenerateLdFldWithCachedType(IR::Instr * instrLdFld, bool* continueAsHel // Load the value from the slot, getting the slot ID from the cache. uint16 index = propertySymOpnd->GetSlotIndex(); - Assert(index != -1); + AssertOrFailFast(index != (uint16)-1); if (opndSlotArray->IsRegOpnd()) { @@ -7204,7 +7204,7 @@ Lowerer::GenerateDirectFieldStore(IR::Instr* instrStFld, IR::PropertySymOpnd* pr // Store the value to the slot, getting the slot index from the cache. uint16 index = propertySymOpnd->GetSlotIndex(); - Assert(index != -1); + AssertOrFailFast(index != (uint16)-1); #if defined(RECYCLER_WRITE_BARRIER_JIT) && (defined(_M_IX86) || defined(_M_AMD64)) if (opndSlotArray->IsRegOpnd()) @@ -7353,6 +7353,19 @@ Lowerer::GenerateStFldWithCachedType(IR::Instr *instrStFld, bool* continueAsHelp { Assert(labelTypeCheckFailed == nullptr && labelBothTypeChecksFailed == nullptr); AssertMsg(!instrStFld->HasBailOutInfo(), "Why does a direct field store have bailout?"); + + if (propertySymOpnd->HasInitialType() && propertySymOpnd->HasFinalType()) + { + bool isPrototypeTypeHandler = propertySymOpnd->GetInitialType()->GetTypeHandler()->IsPrototype(); + if (isPrototypeTypeHandler) + { + LoadScriptContext(instrStFld); + m_lowererMD.LoadHelperArgument(instrStFld, IR::IntConstOpnd::New(propertySymOpnd->GetPropertyId(), TyInt32, m_func, true)); + IR::Instr * invalidateCallInstr = IR::Instr::New(Js::OpCode::Call, m_func); + instrStFld->InsertBefore(invalidateCallInstr); + m_lowererMD.ChangeToHelperCall(invalidateCallInstr, IR::HelperInvalidateProtoCaches); + } + } instrStFld->Remove(); return true; } @@ -8177,6 +8190,16 @@ Lowerer::GenerateFieldStoreWithTypeChange(IR::Instr * instrStFld, IR::PropertySy // Now do the store. GenerateDirectFieldStore(instrStFld, propertySymOpnd); + + bool isPrototypeTypeHandler = initialType->GetTypeHandler()->IsPrototype(); + if (isPrototypeTypeHandler) + { + LoadScriptContext(instrStFld); + m_lowererMD.LoadHelperArgument(instrStFld, IR::IntConstOpnd::New(propertySymOpnd->GetPropertyId(), TyInt32, m_func, true)); + IR::Instr * invalidateCallInstr = IR::Instr::New(Js::OpCode::Call, m_func); + instrStFld->InsertBefore(invalidateCallInstr); + m_lowererMD.ChangeToHelperCall(invalidateCallInstr, IR::HelperInvalidateProtoCaches); + } } bool diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h index 80ab6edd71d..0ade5e496a3 100644 --- a/lib/Common/ChakraCoreVersion.h +++ b/lib/Common/ChakraCoreVersion.h @@ -17,7 +17,7 @@ // ChakraCore version number definitions (used in ChakraCore binary metadata) #define CHAKRA_CORE_MAJOR_VERSION 1 #define CHAKRA_CORE_MINOR_VERSION 11 -#define CHAKRA_CORE_PATCH_VERSION 7 +#define CHAKRA_CORE_PATCH_VERSION 8 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0. // ------------- diff --git a/lib/JITIDL/JITTypes.h b/lib/JITIDL/JITTypes.h index 600a38bae7e..8bc85a78fa5 100644 --- a/lib/JITIDL/JITTypes.h +++ b/lib/JITIDL/JITTypes.h @@ -90,7 +90,7 @@ typedef IDL_DEF([ref]) PSCRIPTCONTEXT_HANDLE * PPSCRIPTCONTEXT_HANDLE; typedef struct TypeHandlerIDL { IDL_Field(boolean) isObjectHeaderInlinedTypeHandler; - IDL_Field(boolean) isLocked; + IDL_Field(unsigned char) flags; IDL_Field(unsigned short) inlineSlotCapacity; IDL_Field(unsigned short) offsetOfInlineSlots; diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp index 8d6d2ca0c7d..90f94b6e184 100644 --- a/lib/Runtime/Language/JavascriptOperators.cpp +++ b/lib/Runtime/Language/JavascriptOperators.cpp @@ -9582,6 +9582,11 @@ using namespace Js; Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 2), thisVar, putValue); Assert(result); + + // Set implicit call flags so we bail out if we're trying to propagate the stored value forward. We can't count on the getter/setter + // to produce the stored value on a LdFld. + threadContext->AddImplicitCallFlags(ImplicitCall_Accessor); + return nullptr; }); } diff --git a/lib/Runtime/Library/JavascriptRegExpConstructor.cpp b/lib/Runtime/Library/JavascriptRegExpConstructor.cpp index e1d347bea80..2be484895a6 100644 --- a/lib/Runtime/Library/JavascriptRegExpConstructor.cpp +++ b/lib/Runtime/Library/JavascriptRegExpConstructor.cpp @@ -360,6 +360,10 @@ namespace Js EnsureValues(); // The last match info relies on the last input. Use it before it is changed. this->lastInput = tempInput; } + + // Set implicit call flags since we are not necessarily making the original stored value available on re-load + // and are killing the store that backs two exposed properties. + this->GetScriptContext()->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor); *result = true; return true; case PropertyIds::lastMatch: diff --git a/lib/Runtime/Types/DictionaryTypeHandler.cpp b/lib/Runtime/Types/DictionaryTypeHandler.cpp index 5da831b0733..e0025540d3f 100644 --- a/lib/Runtime/Types/DictionaryTypeHandler.cpp +++ b/lib/Runtime/Types/DictionaryTypeHandler.cpp @@ -139,7 +139,7 @@ namespace Js PropertyString* propertyString = scriptContext->GetPropertyString(*propertyId); *propertyStringName = propertyString; T dataSlot = descriptor.template GetDataPropertyIndex(); - if (dataSlot != NoSlots && (attribs & PropertyWritable)) + if (dataSlot != NoSlots && (attribs & PropertyWritable) && type == typeToEnumerate) { PropertyValueInfo::SetCacheInfo(info, propertyString, propertyString->GetLdElemInlineCache(), false); SetPropertyValueInfo(info, instance, dataSlot, &descriptor); diff --git a/lib/Runtime/Types/SimpleTypeHandler.cpp b/lib/Runtime/Types/SimpleTypeHandler.cpp index 32e6d3f0b29..dd08d1220a5 100644 --- a/lib/Runtime/Types/SimpleTypeHandler.cpp +++ b/lib/Runtime/Types/SimpleTypeHandler.cpp @@ -328,7 +328,7 @@ namespace Js *propertyStringName = propStr; PropertyValueInfo::SetCacheInfo(info, propStr, propStr->GetLdElemInlineCache(), false); - if ((attribs & PropertyWritable) == PropertyWritable) + if ((attribs & PropertyWritable) == PropertyWritable && type == typeToEnumerate) { PropertyValueInfo::Set(info, instance, index, attribs); } diff --git a/lib/Runtime/Types/TypeHandler.h b/lib/Runtime/Types/TypeHandler.h index 0feb677f075..6034bf3e91a 100644 --- a/lib/Runtime/Types/TypeHandler.h +++ b/lib/Runtime/Types/TypeHandler.h @@ -348,13 +348,17 @@ namespace Js (v) Seal (vi) Freeze */ - bool GetIsLocked() const { return (this->flags & IsLockedFlag) != 0; } + bool GetIsLocked() const { return GetIsLocked(this->flags); } + static bool GetIsLocked(byte flags) { return (flags & IsLockedFlag) != 0; } bool GetIsShared() const { return (this->flags & IsSharedFlag) != 0; } bool GetMayBecomeShared() const { return (this->flags & MayBecomeSharedFlag) != 0; } bool GetIsOrMayBecomeShared() const { return (this->flags & (MayBecomeSharedFlag | IsSharedFlag)) != 0; } bool GetHasKnownSlot0() const { return (this->flags & HasKnownSlot0Flag) != 0; } - bool GetIsPrototype() const { return (this->flags & IsPrototypeFlag) != 0; } + + bool GetIsPrototype() const { return GetIsPrototype(this->flags); } + static bool GetIsPrototype(byte flags) { return (flags & IsPrototypeFlag) != 0; } + bool GetIsInlineSlotCapacityLocked() const { return (this->propertyTypes & PropertyTypesInlineSlotCapacityLocked) != 0; } void LockTypeHandler() { Assert(IsLockable()); SetFlags(IsLockedFlag); }