//-------------------------------------------------------------------------------------------------------
// 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 "Backend.h"

bool ValueInfo::HasIntConstantValue(const bool includeLikelyInt) const
{
    int32 constantValue;
    return TryGetIntConstantValue(&constantValue, includeLikelyInt);
}

bool ValueInfo::TryGetInt64ConstantValue(int64 *const intValueRef, const bool isUnsigned) const
{
    Assert(intValueRef);
    if (TryGetIntConstantValue(intValueRef, isUnsigned))
    {
        return true;
    }
    else
    {
        int32 int32ValueRef;
        if (TryGetIntConstantValue(&int32ValueRef, false))
        {
            if (isUnsigned)
            {
                *intValueRef = (uint)int32ValueRef;
            }
            else
            {
                *intValueRef = int32ValueRef;
            }
            return true;
        }
    }
    return false;
}

bool ValueInfo::TryGetIntConstantValue(int64 *const intValueRef, const bool isUnsigned) const
{
    Assert(intValueRef);
    if (structureKind == ValueStructureKind::Int64Constant)
    {
        *intValueRef = AsInt64Constant()->IntValue();
        return true;
    }
    return false;
}

bool ValueInfo::TryGetIntConstantValue(int32 *const intValueRef, const bool includeLikelyInt) const
{
    Assert(intValueRef);
    if (!(includeLikelyInt ? IsLikelyInt() : IsInt()))
    {
        return false;
    }

    switch (structureKind)
    {
    case ValueStructureKind::IntConstant:
        if (!includeLikelyInt || IsInt())
        {
            *intValueRef = AsIntConstant()->IntValue();
            return true;
        }
        break;

    case ValueStructureKind::IntRange:
        Assert(includeLikelyInt && !IsInt() || !AsIntRange()->IsConstant());
        break;

    case ValueStructureKind::IntBounded:
    {
        const IntConstantBounds bounds(AsIntBounded()->Bounds()->ConstantBounds());
        if (bounds.IsConstant())
        {
            *intValueRef = bounds.LowerBound();
            return true;
        }
        break;
    }
    }
    return false;
}

bool ValueInfo::TryGetIntConstantLowerBound(int32 *const intConstantBoundRef, const bool includeLikelyInt) const
{
    Assert(intConstantBoundRef);

    if (!(includeLikelyInt ? IsLikelyInt() : IsInt()))
    {
        return false;
    }

    switch (structureKind)
    {
    case ValueStructureKind::IntConstant:
        if (!includeLikelyInt || IsInt())
        {
            *intConstantBoundRef = AsIntConstant()->IntValue();
            return true;
        }
        break;

    case ValueStructureKind::IntRange:
        if (!includeLikelyInt || IsInt())
        {
            *intConstantBoundRef = AsIntRange()->LowerBound();
            return true;
        }
        break;

    case ValueStructureKind::IntBounded:
        *intConstantBoundRef = AsIntBounded()->Bounds()->ConstantLowerBound();
        return true;
    }

    *intConstantBoundRef = IsTaggedInt() ? Js::Constants::Int31MinValue : IntConstMin;
    return true;
}

bool ValueInfo::TryGetIntConstantUpperBound(int32 *const intConstantBoundRef, const bool includeLikelyInt) const
{
    Assert(intConstantBoundRef);

    if (!(includeLikelyInt ? IsLikelyInt() : IsInt()))
    {
        return false;
    }

    switch (structureKind)
    {
    case ValueStructureKind::IntConstant:
        if (!includeLikelyInt || IsInt())
        {
            *intConstantBoundRef = AsIntConstant()->IntValue();
            return true;
        }
        break;

    case ValueStructureKind::IntRange:
        if (!includeLikelyInt || IsInt())
        {
            *intConstantBoundRef = AsIntRange()->UpperBound();
            return true;
        }
        break;

    case ValueStructureKind::IntBounded:
        *intConstantBoundRef = AsIntBounded()->Bounds()->ConstantUpperBound();
        return true;
    }

    *intConstantBoundRef = IsTaggedInt() ? Js::Constants::Int31MaxValue : IntConstMax;
    return true;
}

bool ValueInfo::TryGetIntConstantBounds(IntConstantBounds *const intConstantBoundsRef, const bool includeLikelyInt) const
{
    Assert(intConstantBoundsRef);

    if (!(includeLikelyInt ? IsLikelyInt() : IsInt()))
    {
        return false;
    }

    switch (structureKind)
    {
    case ValueStructureKind::IntConstant:
        if (!includeLikelyInt || IsInt())
        {
            const int32 intValue = AsIntConstant()->IntValue();
            *intConstantBoundsRef = IntConstantBounds(intValue, intValue);
            return true;
        }
        break;

    case ValueStructureKind::IntRange:
        if (!includeLikelyInt || IsInt())
        {
            *intConstantBoundsRef = *AsIntRange();
            return true;
        }
        break;

    case ValueStructureKind::IntBounded:
        *intConstantBoundsRef = AsIntBounded()->Bounds()->ConstantBounds();
        return true;
    }

    *intConstantBoundsRef =
        IsTaggedInt()
        ? IntConstantBounds(Js::Constants::Int31MinValue, Js::Constants::Int31MaxValue)
        : IntConstantBounds(INT32_MIN, INT32_MAX);
    return true;
}

bool ValueInfo::WasNegativeZeroPreventedByBailout() const
{
    if (!IsInt())
    {
        return false;
    }

    switch (structureKind)
    {
    case ValueStructureKind::IntRange:
        return AsIntRange()->WasNegativeZeroPreventedByBailout();

    case ValueStructureKind::IntBounded:
        return AsIntBounded()->WasNegativeZeroPreventedByBailout();
    }
    return false;
}

bool ValueInfo::IsEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    const bool result =
        IsEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2) ||
        IsEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1);
    Assert(!result || !IsNotEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2));
    Assert(!result || !IsNotEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1));
    return result;
}

bool ValueInfo::IsNotEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    const bool result =
        IsNotEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2) ||
        IsNotEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1);
    Assert(!result || !IsEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2));
    Assert(!result || !IsEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1));
    return result;
}

bool ValueInfo::IsEqualTo_NoConverse(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return
        IsGreaterThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2) &&
        IsLessThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2);
}

bool ValueInfo::IsNotEqualTo_NoConverse(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return
        IsGreaterThan(src1Value, min1, max1, src2Value, min2, max2) ||
        IsLessThan(src1Value, min1, max1, src2Value, min2, max2);
}

bool ValueInfo::IsGreaterThanOrEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return IsGreaterThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2, 0);
}

bool ValueInfo::IsGreaterThan(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return IsGreaterThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2, 1);
}

bool ValueInfo::IsLessThanOrEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return IsLessThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2, 0);
}

bool ValueInfo::IsLessThan(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2)
{
    return IsLessThanOrEqualTo(src1Value, min1, max1, src2Value, min2, max2, -1);
}

bool ValueInfo::IsGreaterThanOrEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2,
    const int src2Offset)
{
    return
        IsGreaterThanOrEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2, src2Offset) ||
        src2Offset == IntConstMin ||
        IsLessThanOrEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1, -src2Offset);
}

bool ValueInfo::IsLessThanOrEqualTo(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2,
    const int src2Offset)
{
    return
        IsLessThanOrEqualTo_NoConverse(src1Value, min1, max1, src2Value, min2, max2, src2Offset) ||
        (
            src2Offset != IntConstMin &&
            IsGreaterThanOrEqualTo_NoConverse(src2Value, min2, max2, src1Value, min1, max1, -src2Offset)
            );
}

bool ValueInfo::IsGreaterThanOrEqualTo_NoConverse(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2,
    const int src2Offset)
{
    Assert(src1Value || min1 == max1);
    Assert(!src1Value || src1Value->GetValueInfo()->IsLikelyInt());
    Assert(src2Value || min2 == max2);
    Assert(!src2Value || src2Value->GetValueInfo()->IsLikelyInt());

    if (src1Value)
    {
        if (src2Value && src1Value->GetValueNumber() == src2Value->GetValueNumber())
        {
            return src2Offset <= 0;
        }

        ValueInfo const * const src1ValueInfo = src1Value->GetValueInfo();
        if (src1ValueInfo->structureKind == ValueStructureKind::IntBounded)
        {
            const IntBounds *const bounds = src1ValueInfo->AsIntBounded()->Bounds();
            return
                src2Value
                ? bounds->IsGreaterThanOrEqualTo(src2Value, src2Offset)
                : bounds->IsGreaterThanOrEqualTo(min2, src2Offset);
        }
    }
    return IntBounds::IsGreaterThanOrEqualTo(min1, max2, src2Offset);
}

bool ValueInfo::IsLessThanOrEqualTo_NoConverse(
    const Value *const src1Value,
    const int32 min1,
    const int32 max1,
    const Value *const src2Value,
    const int32 min2,
    const int32 max2,
    const int src2Offset)
{
    Assert(src1Value || min1 == max1);
    Assert(!src1Value || src1Value->GetValueInfo()->IsLikelyInt());
    Assert(src2Value || min2 == max2);
    Assert(!src2Value || src2Value->GetValueInfo()->IsLikelyInt());

    if (src1Value)
    {
        if (src2Value && src1Value->GetValueNumber() == src2Value->GetValueNumber())
        {
            return src2Offset >= 0;
        }

        ValueInfo const * const src1ValueInfo = src1Value->GetValueInfo();
        if (src1ValueInfo->structureKind == ValueStructureKind::IntBounded)
        {
            const IntBounds *const bounds = src1ValueInfo->AsIntBounded()->Bounds();
            return
                src2Value
                ? bounds->IsLessThanOrEqualTo(src2Value, src2Offset)
                : bounds->IsLessThanOrEqualTo(min2, src2Offset);
        }
    }
    return IntBounds::IsLessThanOrEqualTo(max1, min2, src2Offset);
}

bool
ValueInfo::IsGeneric() const
{
    return structureKind == ValueStructureKind::Generic;
}

bool
ValueInfo::IsIntConstant() const
{
    return IsInt() && structureKind == ValueStructureKind::IntConstant;
}

bool
ValueInfo::IsInt64Constant() const
{
    return IsInt() && structureKind == ValueStructureKind::Int64Constant;
}

const IntConstantValueInfo *
ValueInfo::AsIntConstant() const
{
    Assert(IsIntConstant());
    return static_cast<const IntConstantValueInfo *>(this);
}
const Int64ConstantValueInfo *
ValueInfo::AsInt64Constant() const
{
    Assert(IsInt64Constant());
    return static_cast<const Int64ConstantValueInfo *>(this);
}

bool
ValueInfo::IsIntRange() const
{
    return IsInt() && structureKind == ValueStructureKind::IntRange;
}

const IntRangeValueInfo *
ValueInfo::AsIntRange() const
{
    Assert(IsIntRange());
    return static_cast<const IntRangeValueInfo *>(this);
}

bool
ValueInfo::IsIntBounded() const
{
    const bool isIntBounded = IsLikelyInt() && structureKind == ValueStructureKind::IntBounded;

    // Bounds for definitely int values should have relative bounds, otherwise those values should use one of the other value
    // infos
    Assert(!isIntBounded || static_cast<const IntBoundedValueInfo *>(this)->Bounds()->RequiresIntBoundedValueInfo(Type()));

    return isIntBounded;
}

const IntBoundedValueInfo *
ValueInfo::AsIntBounded() const
{
    Assert(IsIntBounded());
    return static_cast<const IntBoundedValueInfo *>(this);
}

bool
ValueInfo::IsFloatConstant() const
{
    return IsFloat() && structureKind == ValueStructureKind::FloatConstant;
}

FloatConstantValueInfo *
ValueInfo::AsFloatConstant()
{
    Assert(IsFloatConstant());
    return static_cast<FloatConstantValueInfo *>(this);
}

const FloatConstantValueInfo *
ValueInfo::AsFloatConstant() const
{
    Assert(IsFloatConstant());
    return static_cast<const FloatConstantValueInfo *>(this);
}

bool
ValueInfo::IsVarConstant() const
{
    return structureKind == ValueStructureKind::VarConstant;
}

VarConstantValueInfo *
ValueInfo::AsVarConstant()
{
    Assert(IsVarConstant());
    return static_cast<VarConstantValueInfo *>(this);
}

bool
ValueInfo::IsJsType() const
{
    Assert(!(structureKind == ValueStructureKind::JsType && !IsUninitialized()));
    return structureKind == ValueStructureKind::JsType;
}

JsTypeValueInfo *
ValueInfo::AsJsType()
{
    Assert(IsJsType());
    return static_cast<JsTypeValueInfo *>(this);
}

const JsTypeValueInfo *
ValueInfo::AsJsType() const
{
    Assert(IsJsType());
    return static_cast<const JsTypeValueInfo *>(this);
}

bool
ValueInfo::IsArrayValueInfo() const
{
    return IsAnyOptimizedArray() && structureKind == ValueStructureKind::Array;
}

const ArrayValueInfo *
ValueInfo::AsArrayValueInfo() const
{
    Assert(IsArrayValueInfo());
    return static_cast<const ArrayValueInfo *>(this);
}

ArrayValueInfo *
ValueInfo::AsArrayValueInfo()
{
    Assert(IsArrayValueInfo());
    return static_cast<ArrayValueInfo *>(this);
}

ValueInfo *
ValueInfo::SpecializeToInt32(JitArenaAllocator *const allocator, const bool isForLoopBackEdgeCompensation)
{
    // Int specialization in some uncommon loop cases involving dependencies, needs to allow specializing values of arbitrary
    // types, even values that are definitely not int, to compensate for aggressive assumptions made by a loop prepass. In all
    // other cases, only values that are likely int may be int-specialized.
    Assert(IsUninitialized() || IsLikelyInt() || isForLoopBackEdgeCompensation);

    if (IsInt())
    {
        return this;
    }

    if (!IsIntBounded())
    {
        ValueInfo *const newValueInfo = CopyWithGenericStructureKind(allocator);
        newValueInfo->Type() = ValueType::GetInt(true);
        return newValueInfo;
    }

    const IntBoundedValueInfo *const boundedValueInfo = AsIntBounded();
    const IntBounds *const bounds = boundedValueInfo->Bounds();
    const IntConstantBounds constantBounds = bounds->ConstantBounds();
    if (bounds->RequiresIntBoundedValueInfo())
    {
        IntBoundedValueInfo *const newValueInfo = boundedValueInfo->Copy(allocator);
        newValueInfo->Type() = constantBounds.GetValueType();
        return newValueInfo;
    }

    ValueInfo *const newValueInfo =
        constantBounds.IsConstant()
        ? static_cast<ValueInfo *>(IntConstantValueInfo::New(allocator, constantBounds.LowerBound()))
        : IntRangeValueInfo::New(allocator, constantBounds.LowerBound(), constantBounds.UpperBound(), false);
    newValueInfo->SetSymStore(GetSymStore());
    return newValueInfo;
}

ValueInfo *
ValueInfo::SpecializeToFloat64(JitArenaAllocator *const allocator)
{
    if (IsNumber())
    {
        return this;
    }

    ValueInfo *const newValueInfo = CopyWithGenericStructureKind(allocator);

    // If the value type was likely int, after float-specializing, it's preferable to use Int_Number rather than Float, as the
    // former is also likely int and allows int specialization later.
    newValueInfo->Type() = IsLikelyInt() ? Type().ToDefiniteAnyNumber() : Type().ToDefiniteAnyFloat();

    return newValueInfo;
}

bool
ValueInfo::GetIsShared() const
{
    return IsJsType() ? AsJsType()->GetIsShared() : false;
}

void
ValueInfo::SetIsShared()
{
    if (IsJsType()) AsJsType()->SetIsShared();
}

ValueInfo *
ValueInfo::Copy(JitArenaAllocator * allocator)
{
    if (IsIntConstant())
    {
        return AsIntConstant()->Copy(allocator);
    }
    if (IsIntRange())
    {
        return AsIntRange()->Copy(allocator);
    }
    if (IsIntBounded())
    {
        return AsIntBounded()->Copy(allocator);
    }
    if (IsFloatConstant())
    {
        return AsFloatConstant()->Copy(allocator);
    }
    if (IsJsType())
    {
        return AsJsType()->Copy(allocator);
    }
    if (IsArrayValueInfo())
    {
        return AsArrayValueInfo()->Copy(allocator);
    }
    return CopyWithGenericStructureKind(allocator);
}

bool
ValueInfo::GetIntValMinMax(int *pMin, int *pMax, bool doAggressiveIntTypeSpec)
{
    IntConstantBounds intConstantBounds;
    if (TryGetIntConstantBounds(&intConstantBounds, doAggressiveIntTypeSpec))
    {
        *pMin = intConstantBounds.LowerBound();
        *pMax = intConstantBounds.UpperBound();
        return true;
    }

    Assert(!IsInt());
    Assert(!doAggressiveIntTypeSpec || !IsLikelyInt());
    return false;
}

ValueInfo *
ValueInfo::MergeLikelyIntValueInfo(JitArenaAllocator* alloc, Value *toDataVal, Value *fromDataVal, ValueType const newValueType)
{
    Assert(newValueType.IsLikelyInt());

    ValueInfo *const toDataValueInfo = toDataVal->GetValueInfo();
    ValueInfo *const fromDataValueInfo = fromDataVal->GetValueInfo();
    Assert(toDataValueInfo != fromDataValueInfo);

    bool wasNegativeZeroPreventedByBailout;
    if(newValueType.IsInt())
    {
        int32 toDataIntConstantValue, fromDataIntConstantValue;
        if (toDataValueInfo->TryGetIntConstantValue(&toDataIntConstantValue) &&
            fromDataValueInfo->TryGetIntConstantValue(&fromDataIntConstantValue) &&
            toDataIntConstantValue == fromDataIntConstantValue)
        {
            // A new value number must be created to register the fact that the value has changed. Otherwise, if the value
            // changed inside a loop, the sym may look invariant on the loop back-edge (and hence not turned into a number
            // value), and its constant value from the first iteration may be incorrectly propagated after the loop.
            return IntConstantValueInfo::New(alloc, toDataIntConstantValue);
        }

        wasNegativeZeroPreventedByBailout =
            toDataValueInfo->WasNegativeZeroPreventedByBailout() ||
            fromDataValueInfo->WasNegativeZeroPreventedByBailout();
    }
    else
    {
        wasNegativeZeroPreventedByBailout = false;
    }

    const IntBounds *const toDataValBounds =
        toDataValueInfo->IsIntBounded() ? toDataValueInfo->AsIntBounded()->Bounds() : nullptr;
    const IntBounds *const fromDataValBounds =
        fromDataValueInfo->IsIntBounded() ? fromDataValueInfo->AsIntBounded()->Bounds() : nullptr;
    if(toDataValBounds || fromDataValBounds)
    {
        const IntBounds *mergedBounds;
        if(toDataValBounds && fromDataValBounds)
        {
            mergedBounds = IntBounds::Merge(toDataVal, toDataValBounds, fromDataVal, fromDataValBounds);
        }
        else
        {
            IntConstantBounds constantBounds;
            if(toDataValBounds)
            {
                mergedBounds =
                    fromDataValueInfo->TryGetIntConstantBounds(&constantBounds, true)
                        ? IntBounds::Merge(toDataVal, toDataValBounds, fromDataVal, constantBounds)
                        : nullptr;
            }
            else
            {
                Assert(fromDataValBounds);
                mergedBounds =
                    toDataValueInfo->TryGetIntConstantBounds(&constantBounds, true)
                        ? IntBounds::Merge(fromDataVal, fromDataValBounds, toDataVal, constantBounds)
                        : nullptr;
            }
        }

        if(mergedBounds)
        {
            if(mergedBounds->RequiresIntBoundedValueInfo(newValueType))
            {
                return IntBoundedValueInfo::New(newValueType, mergedBounds, wasNegativeZeroPreventedByBailout, alloc);
            }
            mergedBounds->Delete();
        }
    }

    if(newValueType.IsInt())
    {
        int32 min1 = INT32_MIN;
        int32 max1 = INT32_MAX;
        int32 min2 = INT32_MIN;
        int32 max2 = INT32_MAX;
        toDataValueInfo->GetIntValMinMax(&min1, &max1, false);
        fromDataValueInfo->GetIntValMinMax(&min2, &max2, false);
        return ValueInfo::NewIntRangeValueInfo(alloc, min(min1, min2), max(max1, max2), wasNegativeZeroPreventedByBailout);
    }

    return ValueInfo::New(alloc, newValueType);
}

ValueInfo * ValueInfo::NewIntRangeValueInfo(JitArenaAllocator * alloc, int32 min, int32 max, bool wasNegativeZeroPreventedByBailout)
{
    if (min == max)
    {
        // Since int constant values are const-propped, negative zero tracking does not track them, and so it's okay to ignore
        // 'wasNegativeZeroPreventedByBailout'
        return IntConstantValueInfo::New(alloc, max);
    }

    return IntRangeValueInfo::New(alloc, min, max, wasNegativeZeroPreventedByBailout);
}

#if DBG_DUMP
void ValueInfo::Dump()
{
    if (!IsJsType()) // The value type is uninitialized for a type value
    {
        char typeStr[VALUE_TYPE_MAX_STRING_SIZE];
        Type().ToString(typeStr);
        Output::Print(_u("%S"), typeStr);
    }

    IntConstantBounds intConstantBounds;
    if (TryGetIntConstantBounds(&intConstantBounds))
    {
        if (intConstantBounds.IsConstant())
        {
            Output::Print(_u(" constant:%d"), intConstantBounds.LowerBound());
            return;
        }
        Output::Print(_u(" range:%d - %d"), intConstantBounds.LowerBound(), intConstantBounds.UpperBound());
    }
    else if (IsFloatConstant())
    {
        Output::Print(_u(" constant:%g"), AsFloatConstant()->FloatValue());
    }
    else if (IsJsType())
    {
        const JITTypeHolder type(AsJsType()->GetJsType());
        type != nullptr ? Output::Print(_u("type: 0x%p, "), type->GetAddr()) : Output::Print(_u("type: null, "));
        Output::Print(_u("type Set: "));
        Js::EquivalentTypeSet* typeSet = AsJsType()->GetJsTypeSet();
        if (typeSet != nullptr)
        {
            uint16 typeCount = typeSet->GetCount();
            for (uint16 ti = 0; ti < typeCount - 1; ti++)
            {
                Output::Print(_u("0x%p, "), typeSet->GetType(ti));
            }
            Output::Print(_u("0x%p"), typeSet->GetType(typeCount - 1));
        }
        else
        {
            Output::Print(_u("null"));
        }
    }
    else if (IsArrayValueInfo())
    {
        const ArrayValueInfo *const arrayValueInfo = AsArrayValueInfo();
        if (arrayValueInfo->HeadSegmentSym())
        {
            Output::Print(_u(" seg: "));
            arrayValueInfo->HeadSegmentSym()->Dump();
        }
        if (arrayValueInfo->HeadSegmentLengthSym())
        {
            Output::Print(_u(" segLen: "));
            arrayValueInfo->HeadSegmentLengthSym()->Dump();
        }
        if (arrayValueInfo->LengthSym())
        {
            Output::Print(_u(" len: "));
            arrayValueInfo->LengthSym()->Dump();
        }
    }

    if (this->GetSymStore())
    {
        Output::Print(_u("\t\tsym:"));
        this->GetSymStore()->Dump();
    }
}
#endif
