diff --git a/Build/Common.Build.Default.props b/Build/Common.Build.Default.props
index 815121e3ab9..07d1dd35f69 100644
--- a/Build/Common.Build.Default.props
+++ b/Build/Common.Build.Default.props
@@ -17,6 +17,7 @@
v120
v140
v141
+ v142
diff --git a/Build/NuGet/.pack-version b/Build/NuGet/.pack-version
index 361ffc5b4d8..f33bbfa17f1 100644
--- a/Build/NuGet/.pack-version
+++ b/Build/NuGet/.pack-version
@@ -1 +1 @@
-1.11.9
+1.11.10
diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp
index be4e42d4be4..59210a247b5 100644
--- a/lib/Backend/BackwardPass.cpp
+++ b/lib/Backend/BackwardPass.cpp
@@ -1645,6 +1645,8 @@ BackwardPass::ProcessLoop(BasicBlock * lastBlock)
{
Assert(loop->symsAssignedToInLoop == nullptr);
loop->symsAssignedToInLoop = JitAnew(this->globOpt->alloc, BVSparse, this->globOpt->alloc);
+ Assert(loop->preservesNumberValue == nullptr);
+ loop->preservesNumberValue = JitAnew(this->globOpt->alloc, BVSparse, this->globOpt->alloc);
}
FOREACH_BLOCK_BACKWARD_IN_RANGE_DEAD_OR_ALIVE(block, lastBlock, nullptr)
@@ -4316,7 +4318,10 @@ BackwardPass::ProcessNoImplicitCallDef(IR::Instr *const instr)
const bool transferArrayLengthSymUse = !!currentBlock->noImplicitCallArrayLengthSymUses->TestAndClear(dstSym->m_id);
IR::Opnd *const src = instr->GetSrc1();
- if(!src || instr->GetSrc2())
+
+ // Stop attempting to transfer noImplicitCallUses symbol if the instr is not a transfer instr (based on the opcode's
+ // flags) or does not have the attributes to be a transfer instr (based on the existance of src and src2).
+ if(!src || (instr->GetSrc2() && !OpCodeAttr::NonIntTransfer(instr->m_opcode)))
{
return;
}
@@ -5004,16 +5009,24 @@ BackwardPass::UpdateArrayBailOutKind(IR::Instr *const instr)
return;
}
+ instr->GetDst()->AsIndirOpnd()->AllowConversion(true);
IR::BailOutKind includeBailOutKinds = IR::BailOutInvalid;
if (!baseValueType.IsNotNativeArray() &&
- (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar()) &&
!currentBlock->noImplicitCallNativeArrayUses->IsEmpty() &&
!(instr->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall))
{
// There is an upwards-exposed use of a native array. Since the array referenced by this instruction can be aliased,
// this instruction needs to bail out if it converts the native array even if this array specifically is not
// upwards-exposed.
- includeBailOutKinds |= IR::BailOutConvertedNativeArray;
+ if (!baseValueType.IsLikelyNativeArray() || instr->GetSrc1()->IsVar())
+ {
+ includeBailOutKinds |= IR::BailOutConvertedNativeArray;
+ }
+ else
+ {
+ // We are assuming that array conversion is impossible here, so make sure we execute code that fails if conversion does happen.
+ instr->GetDst()->AsIndirOpnd()->AllowConversion(false);
+ }
}
if(baseOpnd->IsArrayRegOpnd() && baseOpnd->AsArrayRegOpnd()->EliminatedUpperBoundCheck())
@@ -7410,6 +7423,52 @@ BackwardPass::TrackFloatSymEquivalence(IR::Instr *const instr)
}
}
+bool
+BackwardPass::SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd)
+{
+ Assert(sym->IsStackSym());
+ if (!opnd->IsRegOpnd())
+ {
+ return false;
+ }
+ StackSym *opndSym = opnd->AsRegOpnd()->m_sym;
+
+ if (sym == opndSym)
+ {
+ return true;
+ }
+
+ if (!opndSym->IsSingleDef())
+ {
+ return false;
+ }
+
+ if (opndSym->GetInstrDef()->m_opcode == Js::OpCode::LdC_A_I4)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+BackwardPass::InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym)
+{
+ if (instr->m_opcode == Js::OpCode::Ld_A)
+ {
+ if (instr->GetSrc1()->IsRegOpnd())
+ {
+ IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd();
+ if (src1->m_sym->IsSingleDef())
+ {
+ instr = src1->m_sym->GetInstrDef();
+ }
+ }
+ }
+ return (OpCodeAttr::ProducesNumber(instr->m_opcode) ||
+ (instr->m_opcode == Js::OpCode::Add_A && this->SymIsIntconstOrSelf(defSym, instr->GetSrc1()) && this->SymIsIntconstOrSelf(defSym, instr->GetSrc2())));
+}
+
bool
BackwardPass::ProcessDef(IR::Opnd * opnd)
{
@@ -7424,7 +7483,19 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
this->InvalidateCloneStrCandidate(opnd);
if ((tag == Js::BackwardPhase) && IsPrePass())
{
- this->currentPrePassLoop->symsAssignedToInLoop->Set(sym->m_id);
+ bool firstDef = !this->currentPrePassLoop->symsAssignedToInLoop->TestAndSet(sym->m_id);
+
+ if (firstDef)
+ {
+ if (this->InstrPreservesNumberValues(this->currentInstr, sym))
+ {
+ this->currentPrePassLoop->preservesNumberValue->Set(sym->m_id);
+ }
+ }
+ else if (!this->InstrPreservesNumberValues(this->currentInstr, sym))
+ {
+ this->currentPrePassLoop->preservesNumberValue->Clear(sym->m_id);
+ }
}
}
}
diff --git a/lib/Backend/BackwardPass.h b/lib/Backend/BackwardPass.h
index 4522594d5d3..18a6fe5a94e 100644
--- a/lib/Backend/BackwardPass.h
+++ b/lib/Backend/BackwardPass.h
@@ -36,6 +36,9 @@ class BackwardPass
bool ProcessDef(IR::Opnd * opnd);
void ProcessTransfers(IR::Instr * instr);
void ProcessFieldKills(IR::Instr * instr);
+ bool SymIsIntconstOrSelf(Sym *sym, IR::Opnd *opnd);
+ bool InstrPreservesNumberValues(IR::Instr *instr, Sym *defSym);
+
template void ClearBucketsOnFieldKill(IR::Instr *instr, HashTable *table);
StackSym* ProcessByteCodeUsesDst(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
const BVSparse* ProcessByteCodeUsesSrcs(IR::ByteCodeUsesInstr * byteCodeUsesInstr);
diff --git a/lib/Backend/FlowGraph.h b/lib/Backend/FlowGraph.h
index b08fc1d4c50..0d516222075 100644
--- a/lib/Backend/FlowGraph.h
+++ b/lib/Backend/FlowGraph.h
@@ -588,6 +588,7 @@ class Loop
// cleanup in PreOptPeep in the pre-pass of a loop. For aggressively transferring
// values in prepass, we need to know if a source sym was ever assigned to in a loop.
BVSparse *symsAssignedToInLoop;
+ BVSparse *preservesNumberValue;
BailOutInfo * bailOutInfo;
IR::BailOutInstr * toPrimitiveSideEffectCheck;
@@ -733,6 +734,7 @@ class Loop
symsAssignedToInLoop(nullptr),
needImplicitCallBailoutChecksForJsArrayCheckHoist(false),
inductionVariables(nullptr),
+ preservesNumberValue(nullptr),
dominatingLoopCountableBlock(nullptr),
loopCount(nullptr),
loopCountBasedBoundBaseSyms(nullptr),
diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp
index 261d6b9a5ca..652410f7347 100644
--- a/lib/Backend/GlobOpt.cpp
+++ b/lib/Backend/GlobOpt.cpp
@@ -1244,7 +1244,7 @@ void GlobOpt::InsertValueCompensation(
{
IR::Instr *const newInstr =
IR::Instr::New(
- Js::OpCode::Ld_I4,
+ Js::OpCode::Ld_A,
IR::RegOpnd::New(mergedHeadSegmentLengthSym, mergedHeadSegmentLengthSym->GetType(), func),
IR::RegOpnd::New(predecessorHeadSegmentLengthSym, predecessorHeadSegmentLengthSym->GetType(), func),
func);
@@ -2694,6 +2694,48 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
return instrNext;
}
+bool
+GlobOpt::IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const
+{
+ if (opnd == nullptr)
+ {
+ return false;
+ }
+
+ if (opnd->m_sym->m_isNotNumber)
+ {
+ return true;
+ }
+
+ if (!inGlobOpt)
+ {
+ return false;
+ }
+
+ if (opnd->GetValueType().IsNumber() || currentBlock->globOptData.IsTypeSpecialized(opnd->m_sym))
+ {
+ if (!this->IsLoopPrePass())
+ {
+ return false;
+ }
+
+ Value * opndValue = this->currentBlock->globOptData.FindValue(opnd->m_sym);
+ ValueInfo * opndValueInfo = opndValue ? opndValue->GetValueInfo() : nullptr;
+ if (!opndValueInfo)
+ {
+ return true;
+ }
+ if (this->prePassLoop->preservesNumberValue->Test(opnd->m_sym->m_id))
+ {
+ return false;
+ }
+
+ return !this->IsSafeToTransferInPrepass(opnd->m_sym, opndValueInfo);
+ }
+
+ return true;
+}
+
bool
GlobOpt::OptTagChecks(IR::Instr *instr)
{
@@ -12827,6 +12869,26 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr)
it.RemoveCurrent();
}
}
+ else if(kills.KillsObjectArraysWithNoMissingValues())
+ {
+ // Some operations may kill objects with arrays-with-no-missing-values in unlikely circumstances. Convert their value types to likely
+ // versions so that the checks have to be redone.
+ for(auto it = valuesToKillOnCalls->GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext())
+ {
+ Value *const value = it.CurrentValue();
+ ValueInfo *const valueInfo = value->GetValueInfo();
+ Assert(
+ valueInfo->IsArrayOrObjectWithArray() ||
+ valueInfo->IsOptimizedVirtualTypedArray() ||
+ valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym());
+ if(!valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsArray() || !valueInfo->HasNoMissingValues())
+ {
+ continue;
+ }
+ ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false);
+ it.RemoveCurrent();
+ }
+ }
if(kills.KillsNativeArrays())
{
@@ -13358,6 +13420,11 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
{
kills.SetKillsArrayLengths();
}
+
+ if(doArrayMissingValueCheckHoist && !(useValueTypes && arrayValueType.IsArray()))
+ {
+ kills.SetKillsObjectArraysWithNoMissingValues();
+ }
break;
}
diff --git a/lib/Backend/GlobOpt.h b/lib/Backend/GlobOpt.h
index b6afcf657fe..e71c1900730 100644
--- a/lib/Backend/GlobOpt.h
+++ b/lib/Backend/GlobOpt.h
@@ -317,6 +317,7 @@ class JsArrayKills
{
bool killsAllArrays : 1;
bool killsArraysWithNoMissingValues : 1;
+ bool killsObjectArraysWithNoMissingValues : 1;
bool killsNativeArrays : 1;
bool killsArrayHeadSegments : 1;
bool killsArrayHeadSegmentLengths : 1;
@@ -342,6 +343,9 @@ class JsArrayKills
bool KillsArraysWithNoMissingValues() const { return killsArraysWithNoMissingValues; }
void SetKillsArraysWithNoMissingValues() { killsArraysWithNoMissingValues = true; }
+ bool KillsObjectArraysWithNoMissingValues() const { return killsObjectArraysWithNoMissingValues; }
+ void SetKillsObjectArraysWithNoMissingValues() { killsObjectArraysWithNoMissingValues = true; }
+
bool KillsNativeArrays() const { return killsNativeArrays; }
void SetKillsNativeArrays() { killsNativeArrays = true; }
@@ -769,6 +773,8 @@ class GlobOpt
const bool lossy = false, const bool forceInvariantHoisting = false, IR::BailOutKind bailoutKind = IR::BailOutInvalid);
void HoistInvariantValueInfo(ValueInfo *const invariantValueInfoToHoist, Value *const valueToUpdate, BasicBlock *const targetBlock);
void OptHoistUpdateValueType(Loop* loop, IR::Instr* instr, IR::Opnd** srcOpndPtr, Value *const srcVal);
+ bool IsNonNumericRegOpnd(IR::RegOpnd *opnd, bool inGlobOpt) const;
+
public:
static bool IsTypeSpecPhaseOff(Func const * func);
static bool DoAggressiveIntTypeSpec(Func const * func);
@@ -891,7 +897,7 @@ class GlobOpt
void KillLiveFields(StackSym * stackSym, BVSparse * bv);
void KillLiveFields(PropertySym * propertySym, BVSparse * bv);
void KillLiveFields(BVSparse *const fieldsToKill, BVSparse *const bv) const;
- void KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * bv, bool inGlobOpt, Func *func);
+ void KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse * bv, bool inGlobOpt, Func *func);
void KillAllFields(BVSparse * bv);
void SetAnyPropertyMayBeWrittenTo();
void AddToPropertiesWrittenTo(Js::PropertyId propertyId);
diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp
index 5a0e3fa8337..92a7e9ec108 100644
--- a/lib/Backend/GlobOptFields.cpp
+++ b/lib/Backend/GlobOptFields.cpp
@@ -208,7 +208,7 @@ void GlobOpt::KillLiveFields(BVSparse *const fieldsToKill, BV
}
void
-GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse * bv, bool inGlobOpt, Func *func)
+GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, IR::Opnd * valueOpnd, BVSparse * bv, bool inGlobOpt, Func *func)
{
IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
@@ -225,14 +225,7 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse *
// - We check the type specialization status for the sym as well. For the purpose of doing kills, we can assume that
// if type specialization happened, that fields don't need to be killed. Note that they may be killed in the next
// pass based on the value.
- if (func->GetThisOrParentInlinerHasArguments() ||
- (
- indexOpnd &&
- (
- indexOpnd->m_sym->m_isNotNumber ||
- (inGlobOpt && !indexOpnd->GetValueType().IsNumber() && !currentBlock->globOptData.IsTypeSpecialized(indexOpnd->m_sym))
- )
- ))
+ if (func->GetThisOrParentInlinerHasArguments() || this->IsNonNumericRegOpnd(indexOpnd, inGlobOpt))
{
this->KillAllFields(bv); // This also kills all property type values, as the same bit-vector tracks those stack syms
SetAnyPropertyMayBeWrittenTo();
@@ -248,6 +241,23 @@ GlobOpt::KillLiveElems(IR::IndirOpnd * indirOpnd, BVSparse *
// 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);
}
+ else if ((!valueOpnd || valueOpnd->IsVar()) && this->objectTypeSyms != nullptr)
+ {
+ // If we wind up converting a native array, block final-type opt at this point, because we could evolve
+ // to a type with the wrong type ID. Do this by noting that we may have evolved any type and so must
+ // check it before evolving it further.
+ IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
+ Value * baseValue = baseOpnd ? this->currentBlock->globOptData.FindValue(baseOpnd->m_sym) : nullptr;
+ ValueInfo * baseValueInfo = baseValue ? baseValue->GetValueInfo() : nullptr;
+ if (!baseValueInfo || !baseValueInfo->IsNotNativeArray())
+ {
+ if (this->currentBlock->globOptData.maybeWrittenTypeSyms == nullptr)
+ {
+ this->currentBlock->globOptData.maybeWrittenTypeSyms = JitAnew(this->alloc, BVSparse, this->alloc);
+ }
+ this->currentBlock->globOptData.maybeWrittenTypeSyms->Or(this->objectTypeSyms);
+ }
+ }
}
}
@@ -340,7 +350,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo
case Js::OpCode::StElemI_A_Strict:
Assert(dstOpnd != nullptr);
KillLiveFields(this->lengthEquivBv, bv);
- KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
+ KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
@@ -350,7 +360,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo
case Js::OpCode::InitComputedProperty:
case Js::OpCode::InitGetElemI:
case Js::OpCode::InitSetElemI:
- KillLiveElems(dstOpnd->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
+ KillLiveElems(dstOpnd->AsIndirOpnd(), instr->GetSrc1(), bv, inGlobOpt, instr->m_func);
if (inGlobOpt)
{
KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
@@ -360,7 +370,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo
case Js::OpCode::DeleteElemI_A:
case Js::OpCode::DeleteElemIStrict_A:
Assert(dstOpnd != nullptr);
- KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), bv, inGlobOpt, instr->m_func);
+ KillLiveElems(instr->GetSrc1()->AsIndirOpnd(), nullptr, bv, inGlobOpt, instr->m_func);
break;
case Js::OpCode::DeleteFld:
diff --git a/lib/Backend/IR.cpp b/lib/Backend/IR.cpp
index 6580465cb73..1e3a4c1074b 100644
--- a/lib/Backend/IR.cpp
+++ b/lib/Backend/IR.cpp
@@ -3307,7 +3307,14 @@ bool Instr::TransfersSrcValue()
// Consider: Add opcode attribute to indicate whether the opcode would use the value or not
- return this->GetDst() != nullptr && this->GetSrc2() == nullptr && !OpCodeAttr::DoNotTransfer(this->m_opcode) && !this->CallsAccessor();
+ return
+ this->GetDst() != nullptr &&
+
+ // The lack of a Src2 does not always indicate that the instr is not a transfer instr (ex: StSlotChkUndecl).
+ (this->GetSrc2() == nullptr || OpCodeAttr::NonIntTransfer(this->m_opcode)) &&
+
+ !OpCodeAttr::DoNotTransfer(this->m_opcode) &&
+ !this->CallsAccessor();
}
diff --git a/lib/Backend/InductionVariable.cpp b/lib/Backend/InductionVariable.cpp
index bbb71e7a3ee..275370ef4f7 100644
--- a/lib/Backend/InductionVariable.cpp
+++ b/lib/Backend/InductionVariable.cpp
@@ -81,6 +81,21 @@ bool InductionVariable::Add(const int n)
if(n == 0)
return true;
+ int lowerBound = changeBounds.LowerBound();
+ int upperBound = changeBounds.UpperBound();
+
+ if (n < 0 && (lowerBound < upperBound || (lowerBound == upperBound && lowerBound > 0)))
+ {
+ isChangeDeterminate = false;
+ return false;
+ }
+
+ if (n > 0 && (lowerBound > upperBound || (lowerBound == upperBound && lowerBound < 0)))
+ {
+ isChangeDeterminate = false;
+ return false;
+ }
+
int newLowerBound;
if(changeBounds.LowerBound() == IntConstMin)
{
@@ -148,6 +163,25 @@ void InductionVariable::Merge(const InductionVariable &other)
// The value number may be different, the caller will give the merged info the appropriate value number
isChangeDeterminate &= other.isChangeDeterminate;
+ if(!isChangeDeterminate)
+ return;
+
+ int lowerBound = this->ChangeBounds().LowerBound();
+ int upperBound = this->ChangeBounds().UpperBound();
+
+ int otherLowerBound = other.ChangeBounds().LowerBound();
+ int otherUpperBound = other.ChangeBounds().UpperBound();
+
+ if ((lowerBound < upperBound || (lowerBound == upperBound && lowerBound > 0)) && !(otherLowerBound < otherUpperBound || (otherLowerBound == otherUpperBound && otherLowerBound > 0)))
+ {
+ isChangeDeterminate = false;
+ }
+
+ if ((lowerBound > upperBound || (lowerBound == upperBound && lowerBound < 0)) && !(otherLowerBound > otherUpperBound || (otherLowerBound == otherUpperBound && otherLowerBound < 0)))
+ {
+ isChangeDeterminate = false;
+ }
+
if(!isChangeDeterminate)
return;
diff --git a/lib/Backend/Inline.cpp b/lib/Backend/Inline.cpp
index 5703333c78c..a09ba6bbb4a 100644
--- a/lib/Backend/Inline.cpp
+++ b/lib/Backend/Inline.cpp
@@ -69,7 +69,7 @@ Inline::Optimize(Func *func, __in_ecount_opt(callerArgOutCount) IR::Instr *calle
if (instr->AsLabelInstr()->m_isForInExit)
{
- Assert(this->currentForInDepth != 0);
+ AssertOrFailFast(this->currentForInDepth != 0);
this->currentForInDepth--;
}
}
@@ -2158,14 +2158,12 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo *
IR::Instr *inlineBuiltInEndInstr = nullptr;
if (inlineCallOpCode == Js::OpCode::InlineFunctionApply)
{
- inlineBuiltInEndInstr = InlineApply(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth, inlineCallArgCount - (usesThisArgument ? 1 : 0));
- return inlineBuiltInEndInstr->m_next;
+ return InlineApply(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth, inlineCallArgCount - (usesThisArgument ? 1 : 0));
}
if (inlineCallOpCode == Js::OpCode::InlineFunctionCall)
{
- inlineBuiltInEndInstr = InlineCall(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth);
- return inlineBuiltInEndInstr->m_next;
+ return InlineCall(callInstr, inlineeData, inlinerData, symCallerThis, pIsInlined, profileId, recursiveInlineDepth);
}
@@ -2415,9 +2413,10 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo *
// We may still decide not to inline.
*pIsInlined = false;
+ IR::Instr* instrNext = callInstr->m_next;
if (argsCount == 0)
{
- return callInstr;
+ return instrNext;
}
Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(applyData->GetLocalFunctionId());
@@ -2459,7 +2458,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo *
if (PHASE_OFF1(Js::InlineApplyWithoutArrayArgPhase))
{
*pIsInlined = false;
- return callInstr;
+ return instrNext;
}
*pIsInlined = true;
@@ -2490,7 +2489,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo *
{
INLINE_TESTTRACE(_u("INLINING: Skip Inline: Supporting inlining func.apply(this, array) or func.apply(this, arguments) with formals in the parent function only when func is a built-in inlinable as apply target \tCaller: %s (%s)\n"),
inlinerData->GetBody()->GetDisplayName(), inlinerData->GetDebugNumberSet(debugStringBuffer));
- return callInstr;
+ return instrNext;
}
}
@@ -2506,6 +2505,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo *
IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, const FunctionJITTimeInfo * funcInfo)
{
+ IR::Instr* instrNext = callInstr->m_next;
IR::Instr* ldHeapArguments = argsObjectArgInstr->GetSrc1()->GetStackSym()->GetInstrDef();
argsObjectArgInstr->ReplaceSrc1(ldHeapArguments->GetDst());
@@ -2595,7 +2595,7 @@ IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::In
argout = IR::Instr::New(Js::OpCode::ArgOut_A_Dynamic, linkOpnd2, explicitThisArgOut->GetSrc1(), linkOpnd1, callInstr->m_func); // push explicit this as this pointer
callInstr->InsertBefore(argout);
- return callInstr;
+ return instrNext;
}
/*
@@ -2603,6 +2603,7 @@ This method will only do CallDirect style inlining of built-in targets. No scrip
*/
IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo)
{
+ IR::Instr* instrNext = callInstr->m_next;
IR::Instr * implicitThisArgOut = nullptr;
IR::Instr * explicitThisArgOut = nullptr;
IR::Instr * arrayArgOut = nullptr;
@@ -2620,7 +2621,7 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con
IR::Instr* applyTargetLdInstr = nullptr;
if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr))
{
- return callInstr;
+ return instrNext;
}
// Fixed function/function object checks for target built-in
callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst());
@@ -2685,11 +2686,12 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con
callInstr->ReplaceSrc1(helperCallOpnd);
callInstr->ReplaceSrc2(argOut->GetDst());
- return callInstr;
+ return instrNext;
}
IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * applyTargetInfo)
{
+ IR::Instr* instrNext = callInstr->m_next;
IR::Instr * implicitThisArgOut = nullptr;
IR::Instr * explicitThisArgOut = nullptr;
IR::Instr * dummyInstr = nullptr;
@@ -2728,12 +2730,12 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const
if (!callTargetStackSym->IsSingleDef())
{
- return callInstr;
+ return instrNext;
}
if (!applyTargetInfo)
{
- return callInstr;
+ return instrNext;
}
bool safeThis = false;
@@ -2745,7 +2747,7 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const
callInstr->InsertBefore(bytecodeUses);
}
- return callInstr;
+ return instrNext;
}
void Inline::GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount)
@@ -3015,7 +3017,7 @@ Inline::InlineCallApplyTarget_Shared(IR::Instr *callInstr, bool originalCallTarg
// instrNext
IR::Instr* instrNext = callInstr->m_next;
- return InlineFunctionCommon(callInstr, originalCallTargetOpndIsJITOpt, originalCallTargetStackSym, inlineeData, inlinee, instrNext, returnValueOpnd, callInstr, nullptr, recursiveInlineDepth, safeThis, isApplyTarget)->m_prev;
+ return InlineFunctionCommon(callInstr, originalCallTargetOpndIsJITOpt, originalCallTargetStackSym, inlineeData, inlinee, instrNext, returnValueOpnd, callInstr, nullptr, recursiveInlineDepth, safeThis, isApplyTarget);
}
IR::Opnd *
@@ -3029,6 +3031,7 @@ Inline::ConvertToInlineBuiltInArgOut(IR::Instr * argInstr)
IR::Instr*
Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, const FunctionJITTimeInfo * inlinerData, const StackSym *symCallerThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth)
{
+ IR::Instr* instrNext = callInstr->m_next;
Func *func = callInstr->m_func;
Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(funcInfo->GetLocalFunctionId());
@@ -3036,7 +3039,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co
if (PHASE_OFF(Js::InlineCallPhase, this->topFunc) || PHASE_OFF(Js::InlineCallPhase, func)
|| !this->topFunc->GetJITFunctionBody()->GetInParamsCount())
{
- return callInstr;
+ return instrNext;
}
// Convert all the current ARG_OUT to ArgOut_A_InlineBuiltIn
@@ -3045,7 +3048,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co
if (!GetDefInstr(linkOpnd)->GetSrc2()->IsSymOpnd())
{
// There is no benefit of inlining.call() with no arguments.
- return callInstr;
+ return instrNext;
}
*pIsInlined = true;
@@ -3125,7 +3128,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co
}
clonedArgout->SetSrc2(startCall->GetDst());
Assert(GetDefInstr(orgLinkOpnd) == argImplicitInstr);
- return callInstr;
+ return instrNext;
}
bool
diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h
index 1d834ac6273..4a86f9f55ce 100644
--- a/lib/Backend/JnHelperMethodList.h
+++ b/lib/Backend/JnHelperMethodList.h
@@ -205,6 +205,12 @@ HELPERCALLCHK(Op_SetNativeIntElementI_Int32, Js::JavascriptOperators::OP_SetNati
HELPERCALLCHK(Op_SetNativeFloatElementI_Int32, Js::JavascriptOperators::OP_SetNativeFloatElementI_Int32, AttrCanThrow)
HELPERCALLCHK(Op_SetNativeIntElementI_UInt32, Js::JavascriptOperators::OP_SetNativeIntElementI_UInt32, AttrCanThrow)
HELPERCALLCHK(Op_SetNativeFloatElementI_UInt32, Js::JavascriptOperators::OP_SetNativeFloatElementI_UInt32, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeIntElementI_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_NoConvert, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeFloatElementI_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_NoConvert, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeIntElementI_Int32_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_Int32_NoConvert, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeFloatElementI_Int32_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_Int32_NoConvert, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeIntElementI_UInt32_NoConvert, Js::JavascriptOperators::OP_SetNativeIntElementI_UInt32_NoConvert, AttrCanThrow)
+HELPERCALLCHK(Op_SetNativeFloatElementI_UInt32_NoConvert, Js::JavascriptOperators::OP_SetNativeFloatElementI_UInt32_NoConvert, AttrCanThrow)
HELPERCALLCHK(ScrArr_SetNativeIntElementC, Js::JavascriptArray::OP_SetNativeIntElementC, AttrCanNotBeReentrant)
HELPERCALLCHK(ScrArr_SetNativeFloatElementC, Js::JavascriptArray::OP_SetNativeFloatElementC, AttrCanNotBeReentrant)
HELPERCALLCHK(Op_DeleteElementI, Js::JavascriptOperators::OP_DeleteElementI, AttrCanThrow)
diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp
index 52b5be80238..dcbfba67c79 100644
--- a/lib/Backend/Lower.cpp
+++ b/lib/Backend/Lower.cpp
@@ -9001,6 +9001,8 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool
AssertMsg(dst->IsIndirOpnd(), "Expected indirOpnd on StElementI");
+ bool allowConvert = dst->AsIndirOpnd()->ConversionAllowed();
+
#if !FLOATVAR
if (dst->AsIndirOpnd()->GetBaseOpnd()->GetValueType().IsLikelyOptimizedTypedArray() && src1->IsRegOpnd())
{
@@ -9085,15 +9087,17 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool
{
helperMethod =
srcType == TyVar ? IR::HelperOp_SetElementI_Int32 :
- srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI_Int32 :
- IR::HelperOp_SetNativeFloatElementI_Int32;
+ srcType == TyInt32 ?
+ (allowConvert ? IR::HelperOp_SetNativeIntElementI_Int32 : IR::HelperOp_SetNativeIntElementI_Int32_NoConvert) :
+ (allowConvert ? IR::HelperOp_SetNativeFloatElementI_Int32 : IR::HelperOp_SetNativeFloatElementI_Int32_NoConvert) ;
}
else if (indexOpnd->GetType() == TyUint32)
{
helperMethod =
srcType == TyVar ? IR::HelperOp_SetElementI_UInt32 :
- srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI_UInt32 :
- IR::HelperOp_SetNativeFloatElementI_UInt32;
+ srcType == TyInt32 ?
+ (allowConvert ? IR::HelperOp_SetNativeIntElementI_UInt32 : IR::HelperOp_SetNativeIntElementI_UInt32_NoConvert) :
+ (allowConvert ? IR::HelperOp_SetNativeFloatElementI_UInt32 : IR::HelperOp_SetNativeFloatElementI_UInt32_NoConvert) ;
}
else
{
@@ -9111,8 +9115,9 @@ Lowerer::LowerStElemI(IR::Instr * instr, Js::PropertyOperationFlags flags, bool
if (srcType != TyVar)
{
- helperMethod =
- srcType == TyInt32 ? IR::HelperOp_SetNativeIntElementI : IR::HelperOp_SetNativeFloatElementI;
+ helperMethod = srcType == TyInt32 ?
+ (allowConvert ? IR::HelperOp_SetNativeIntElementI : IR::HelperOp_SetNativeIntElementI_NoConvert) :
+ (allowConvert ? IR::HelperOp_SetNativeFloatElementI : IR::HelperOp_SetNativeFloatElementI_NoConvert);
}
}
diff --git a/lib/Backend/Opnd.cpp b/lib/Backend/Opnd.cpp
index fa8a2814123..d9243e86299 100644
--- a/lib/Backend/Opnd.cpp
+++ b/lib/Backend/Opnd.cpp
@@ -2538,6 +2538,7 @@ IndirOpnd::New(RegOpnd *baseOpnd, int32 offset, IRType type, Func *func, bool do
indirOpnd->m_type = type;
indirOpnd->SetIsJITOptimizedReg(false);
+ indirOpnd->m_conversionAllowed = false;
indirOpnd->m_kind = OpndKindIndir;
@@ -2596,6 +2597,7 @@ IndirOpnd::CopyInternal(Func *func)
newOpnd->canStoreTemp = this->canStoreTemp;
newOpnd->SetOffset(m_offset, m_dontEncode);
newOpnd->SetIsJITOptimizedReg(this->GetIsJITOptimizedReg());
+ newOpnd->m_conversionAllowed = this->m_conversionAllowed;
#if DBG_DUMP
newOpnd->m_addrKind = m_addrKind;
diff --git a/lib/Backend/Opnd.h b/lib/Backend/Opnd.h
index 2f63cd1d28c..500f5403364 100644
--- a/lib/Backend/Opnd.h
+++ b/lib/Backend/Opnd.h
@@ -1662,6 +1662,8 @@ class IndirOpnd: public Opnd
byte GetScale() const;
void SetScale(byte scale);
bool TryGetIntConstIndexValue(bool trySym, IntConstType *pValue, bool *pIsNotInt);
+ void AllowConversion(bool value) { m_conversionAllowed = value; }
+ bool ConversionAllowed() const { return m_conversionAllowed; }
#if DBG_DUMP || defined(ENABLE_IR_VIEWER)
const char16 * GetDescription();
IR::AddrOpndKind GetAddrKind() const;
@@ -1678,6 +1680,7 @@ class IndirOpnd: public Opnd
RegOpnd * m_indexOpnd;
int32 m_offset;
byte m_scale;
+ bool m_conversionAllowed;
Func * m_func; // We need the allocator to copy the base and index...
#if DBG_DUMP || defined(ENABLE_IR_VIEWER)
diff --git a/lib/Common/ChakraCoreVersion.h b/lib/Common/ChakraCoreVersion.h
index b7144c0f96d..a13c4fac2fa 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 9
+#define CHAKRA_CORE_PATCH_VERSION 10
#define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0.
// -------------
diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp
index 69159bcba42..b49d0feec31 100644
--- a/lib/Parser/Parse.cpp
+++ b/lib/Parser/Parse.cpp
@@ -5444,20 +5444,6 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us
}
return false;
});
-
- if (pnodeFnc->IsBodyAndParamScopeMerged() && !fDeclaration && pnodeFnc->pnodeName != nullptr)
- {
- Assert(pnodeFnc->pnodeName->nop == knopVarDecl);
- Symbol* funcSym = pnodeFnc->pnodeName->sym;
- if (funcSym->GetPid()->GetTopRef()->GetFuncScopeId() > pnodeFnc->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->ResetBodyAndParamScopeMerged();
- }
- }
}
}
diff --git a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp
index 1d6c3f1d17e..8230046ea17 100644
--- a/lib/Runtime/ByteCode/ByteCodeGenerator.cpp
+++ b/lib/Runtime/ByteCode/ByteCodeGenerator.cpp
@@ -3091,6 +3091,17 @@ void ByteCodeGenerator::ProcessCapturedSym(Symbol *sym)
Assert(sym->NeedsSlotAlloc(this, funcHome) || sym->GetIsGlobal() || sym->GetIsModuleImport() || sym->GetIsModuleExportStorage());
+ if (sym->GetScope()->GetScopeType() == ScopeType_FuncExpr)
+ {
+ if ((funcHome->GetParamScope() && Scope::HasSymbolName(funcHome->GetParamScope(), sym->GetName())) ||
+ (funcHome->IsBodyAndParamScopeMerged() && funcHome->GetBodyScope() && Scope::HasSymbolName(funcHome->GetBodyScope(), sym->GetName())))
+ {
+ // Make sure the function expression scope gets instantiated, since we can't merge the name symbol into another scope.
+ // Make it an object, since that's the only case the code gen can currently handle.
+ sym->GetScope()->SetIsObject();
+ }
+ }
+
// If this is not a local property, or not all its references can be tracked, or
// it's not scoped to the function, or we're in debug mode, disable the delayed capture optimization.
if (funcHome->IsGlobalFunction() ||
diff --git a/lib/Runtime/ByteCode/OpCodes.h b/lib/Runtime/ByteCode/OpCodes.h
index d75019d0d04..9cfa26b03bb 100755
--- a/lib/Runtime/ByteCode/OpCodes.h
+++ b/lib/Runtime/ByteCode/OpCodes.h
@@ -464,21 +464,21 @@ MACRO_WMS( StEnvSlot, ElementSlotI2, None)
MACRO_WMS( StInnerSlot, ElementSlotI2, None)
MACRO_WMS( StLocalSlot, ElementSlotI1, None)
MACRO_EXTEND_WMS( StParamSlot, ElementSlotI1, None)
-MACRO_BACKEND_ONLY( StSlotChkUndecl, ElementSlot, OpSideEffect)
-MACRO_EXTEND_WMS( StEnvSlotChkUndecl, ElementSlotI2, OpSideEffect)
-MACRO_EXTEND_WMS( StInnerSlotChkUndecl, ElementSlotI2, OpSideEffect)
-MACRO_EXTEND_WMS( StLocalSlotChkUndecl, ElementSlotI1, OpSideEffect)
-MACRO_EXTEND_WMS( StParamSlotChkUndecl, ElementSlotI1, OpSideEffect)
+MACRO_BACKEND_ONLY( StSlotChkUndecl, ElementSlot, OpSideEffect|OpNonIntTransfer) // Src1 is transferred to Dst, Src2 holds the same value as Dst to communicate Dst's liveness.
+MACRO_EXTEND_WMS( StEnvSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StInnerSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StLocalSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StParamSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer)
MACRO_EXTEND_WMS( StObjSlot, ElementSlot, OpSideEffect)
MACRO_EXTEND_WMS( StInnerObjSlot, ElementSlotI2, OpSideEffect)
MACRO_EXTEND_WMS( StLocalObjSlot, ElementSlotI1, OpSideEffect)
MACRO_EXTEND_WMS( StParamObjSlot, ElementSlotI1, OpSideEffect)
-MACRO_EXTEND_WMS( StLocalObjSlotChkUndecl, ElementSlotI1, OpSideEffect)
-MACRO_EXTEND_WMS( StParamObjSlotChkUndecl, ElementSlotI1, OpSideEffect)
+MACRO_EXTEND_WMS( StLocalObjSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StParamObjSlotChkUndecl, ElementSlotI1, OpSideEffect|OpNonIntTransfer)
MACRO_EXTEND_WMS( StEnvObjSlot, ElementSlotI2, OpSideEffect)
-MACRO_EXTEND_WMS( StObjSlotChkUndecl, ElementSlot, OpSideEffect)
-MACRO_EXTEND_WMS( StInnerObjSlotChkUndecl, ElementSlotI2, OpSideEffect)
-MACRO_EXTEND_WMS( StEnvObjSlotChkUndecl, ElementSlotI2, OpSideEffect)
+MACRO_EXTEND_WMS( StObjSlotChkUndecl, ElementSlot, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StInnerObjSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer)
+MACRO_EXTEND_WMS( StEnvObjSlotChkUndecl, ElementSlotI2, OpSideEffect|OpNonIntTransfer)
MACRO_EXTEND_WMS( StModuleSlot, ElementSlotI2, OpSideEffect)
MACRO_BACKEND_ONLY( LdAsmJsFunc, ElementSlot, OpTempNumberSources|OpCanCSE)
MACRO_BACKEND_ONLY( LdWasmFunc, ElementSlot, OpSideEffect)
diff --git a/lib/Runtime/ByteCode/Scope.h b/lib/Runtime/ByteCode/Scope.h
index 2618f27b16b..5f9dc1ccb57 100644
--- a/lib/Runtime/ByteCode/Scope.h
+++ b/lib/Runtime/ByteCode/Scope.h
@@ -110,6 +110,18 @@ class Scope
}
}
+ static bool HasSymbolName(Scope * scope, const JsUtil::CharacterBuffer& name)
+ {
+ for (Symbol *sym = scope->m_symList; sym; sym = sym->GetNext())
+ {
+ if (sym->GetName() == name)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
void AddSymbol(Symbol *sym)
{
if (enclosingScope == nullptr)
@@ -117,12 +129,9 @@ class Scope
sym->SetIsGlobal(true);
}
sym->SetScope(this);
- for (Symbol *symInList = m_symList; symInList; symInList = symInList->GetNext())
+ if (HasSymbolName(this, sym->GetName()))
{
- if (symInList->GetName() == sym->GetName())
- {
- return;
- }
+ return;
}
sym->SetNext(m_symList);
m_symList = sym;
@@ -136,6 +145,7 @@ class Scope
sym->SetIsGlobal(true);
}
sym->SetScope(this);
+ Assert(!HasSymbolName(this, sym->GetName()));
sym->SetNext(m_symList);
m_symList = sym;
m_count++;
diff --git a/lib/Runtime/Language/JavascriptOperators.cpp b/lib/Runtime/Language/JavascriptOperators.cpp
index 90f94b6e184..4d67ce2a0b7 100644
--- a/lib/Runtime/Language/JavascriptOperators.cpp
+++ b/lib/Runtime/Language/JavascriptOperators.cpp
@@ -4644,6 +4644,120 @@ using namespace Js;
return JavascriptOperators::SetProperty(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags);
}
+ BOOL JavascriptOperators::OP_SetNativeIntElementI_NoConvert(
+ Var instance,
+ Var aElementIndex,
+ int32 iValue,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_NoConvert, Op_SetNativeIntElementI);
+ BOOL converted = OP_SetNativeIntElementI(instance, aElementIndex, iValue, scriptContext, flags);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeIntElementI_NoConvert);
+ }
+
+ BOOL JavascriptOperators::OP_SetNativeIntElementI_UInt32_NoConvert(
+ Var instance,
+ uint32 aElementIndex,
+ int32 iValue,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_UInt32_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_UInt32_NoConvert, Op_SetNativeIntElementI_UInt32);
+ BOOL converted = OP_SetNativeIntElementI_UInt32(instance, aElementIndex, iValue, scriptContext, flags);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeIntElementI_UInt32_NoConvert);
+ }
+
+ BOOL JavascriptOperators::OP_SetNativeIntElementI_Int32_NoConvert(
+ Var instance,
+ int32 aElementIndex,
+ int32 iValue,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeIntElementI_Int32_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeIntElementI_Int32_NoConvert, Op_SetNativeIntElementI_Int32);
+ BOOL converted = OP_SetNativeIntElementI_Int32(instance, aElementIndex, iValue, scriptContext, flags);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeIntElementI_Int32_NoConvert);
+ }
+
+ BOOL JavascriptOperators::OP_SetNativeFloatElementI_NoConvert(
+ Var instance,
+ Var aElementIndex,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags,
+ double dValue)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI);
+ BOOL converted = OP_SetNativeFloatElementI(instance, aElementIndex, scriptContext, flags, dValue);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeFloatElementI_NoConvert);
+ }
+
+ BOOL JavascriptOperators::OP_SetNativeFloatElementI_UInt32_NoConvert(
+ Var instance,
+ uint32 aElementIndex,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags,
+ double dValue)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_UInt32_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI_UInt32);
+ BOOL converted = OP_SetNativeFloatElementI_UInt32(instance, aElementIndex, scriptContext, flags, dValue);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeFloatElementI_UInt32_NoConvert);
+ }
+
+ BOOL JavascriptOperators::OP_SetNativeFloatElementI_Int32_NoConvert(
+ Var instance,
+ int32 aElementIndex,
+ ScriptContext* scriptContext,
+ PropertyOperationFlags flags,
+ double dValue)
+ {
+ JIT_HELPER_REENTRANT_HEADER(Op_SetNativeFloatElementI_Int32_NoConvert);
+ JIT_HELPER_SAME_ATTRIBUTES(Op_SetNativeFloatElementI_NoConvert, Op_SetNativeFloatElementI_Int32);
+ BOOL converted = OP_SetNativeFloatElementI_Int32(instance, aElementIndex, scriptContext, flags, dValue);
+ if (converted)
+ {
+ AssertMsg(false, "Unexpected native array conversion");
+ Js::Throw::FatalInternalError();
+ }
+ return FALSE;
+ JIT_HELPER_END(Op_SetNativeFloatElementI_Int32_NoConvert);
+ }
+
BOOL JavascriptOperators::OP_SetNativeIntElementI(
Var instance,
Var aElementIndex,
@@ -9541,6 +9655,10 @@ using namespace Js;
Var result = CALL_ENTRYPOINT(threadContext, marshalledFunction->GetEntryPoint(), function, CallInfo(flags, 1), thisVar);
result = CrossSite::MarshalVar(requestContext, result);
+ // Set implicit call flags so we bail out if we're trying to propagate the value forward, e.g., from a compare. Subsequent calls
+ // to the getter may produce different results.
+ threadContext->AddImplicitCallFlags(ImplicitCall_Accessor);
+
return result;
});
}
diff --git a/lib/Runtime/Language/JavascriptOperators.h b/lib/Runtime/Language/JavascriptOperators.h
index fe88a90575b..583488bd610 100644
--- a/lib/Runtime/Language/JavascriptOperators.h
+++ b/lib/Runtime/Language/JavascriptOperators.h
@@ -392,6 +392,12 @@ namespace Js
static BOOL OP_SetElementI_UInt32(Var instance, uint32 aElementIndex, Var aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
static BOOL OP_SetElementI_Int32(Var instance, int32 aElementIndex, Var aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
static BOOL SetElementIHelper(Var receiver, RecyclableObject* object, Var index, Var value, ScriptContext* scriptContext, PropertyOperationFlags flags);
+ static BOOL OP_SetNativeIntElementI_NoConvert(Var instance, Var aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
+ static BOOL OP_SetNativeIntElementI_UInt32_NoConvert(Var instance, uint32 aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
+ static BOOL OP_SetNativeIntElementI_Int32_NoConvert(Var instance, int aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
+ static BOOL OP_SetNativeFloatElementI_NoConvert(Var instance, Var aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value);
+ static BOOL OP_SetNativeFloatElementI_UInt32_NoConvert(Var instance, uint32 aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value);
+ static BOOL OP_SetNativeFloatElementI_Int32_NoConvert(Var instance, int aElementIndex, ScriptContext* scriptContext, PropertyOperationFlags flags, double value);
static BOOL OP_SetNativeIntElementI(Var instance, Var aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
static BOOL OP_SetNativeIntElementI_UInt32(Var instance, uint32 aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
static BOOL OP_SetNativeIntElementI_Int32(Var instance, int aElementIndex, int32 aValue, ScriptContext* scriptContext, PropertyOperationFlags flags = PropertyOperation_None);
diff --git a/lib/Runtime/Library/JavascriptProxy.cpp b/lib/Runtime/Library/JavascriptProxy.cpp
index 6e026109735..ed817269a82 100644
--- a/lib/Runtime/Library/JavascriptProxy.cpp
+++ b/lib/Runtime/Library/JavascriptProxy.cpp
@@ -392,6 +392,8 @@ namespace Js
}
propertyDescriptor->SetValue(getGetResult);
+ threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
+
return TRUE;
}
@@ -1907,6 +1909,9 @@ namespace Js
}
}
}
+
+ threadContext->AddImplicitCallFlags(Js::ImplicitCall_External);
+
return TRUE;
}