blob: fb264cbe1e628a05247236b3944719eab1ca2dcd [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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 "RuntimeLanguagePch.h"
#define BASE_VALUE_TYPE(t, b) const ValueType ValueType::##t(b);
#include "ValueTypes.h"
#undef BASE_VALUE_TYPE
const ValueType ValueType::AnyNumber(
Bits::Int | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::Float | Bits::Number);
void ValueType::Initialize()
{
InitializeTypeIdToBitsMap();
#if DBG
RunUnitTests();
#endif
}
inline ValueType::Bits ValueType::BitPattern(const TSize onCount)
{
CompileAssert(sizeof(TSize) <= sizeof(size_t));
Assert(onCount && onCount <= sizeof(TSize) * 8);
#pragma prefast(suppress:6235, "Non-Zero Constant TSize and size_t in Condition. This is By Design to allow for TSize to be increased in size to uint32 without breaking anything")
return
static_cast<Bits>(
sizeof(TSize) < sizeof(size_t) || onCount < sizeof(TSize) * 8
? (static_cast<size_t>(1) << onCount) - 1
: static_cast<size_t>(-1));
}
inline ValueType::Bits ValueType::BitPattern(const TSize onCount, const TSize offCount)
{
Assert(onCount && onCount <= sizeof(TSize) * 8);
Assert(offCount && offCount <= sizeof(TSize) * 8);
return BitPattern(onCount + offCount) - BitPattern(offCount);
}
ValueType ValueType::GetTaggedInt()
{
return Verify(Int);
}
ValueType ValueType::GetInt(const bool isLikelyTagged)
{
Bits intBits = Bits::Int | Bits::IntCanBeUntagged | Bits::CanBeTaggedValue;
if(!isLikelyTagged)
intBits |= Bits::IntIsLikelyUntagged;
return Verify(intBits);
}
ValueType ValueType::GetNumberAndLikelyInt(const bool isLikelyTagged)
{
return Verify(GetInt(isLikelyTagged).bits | Bits::Number);
}
ValueType ValueType::GetObject(const ObjectType objectType)
{
ValueType valueType(UninitializedObject);
valueType.SetObjectType(objectType);
if(objectType == ObjectType::Array || objectType == ObjectType::ObjectWithArray)
{
// Default to the most conservative array-specific information. This just a safeguard to guarantee that the returned
// value type has a valid set of array-specific information. Callers should not rely on these defaults, and should
// instead always set each piece of information explicitly.
valueType = valueType.SetHasNoMissingValues(false).SetArrayTypeId(Js::TypeIds_Array);
}
return Verify(valueType);
}
inline ValueType ValueType::GetArray(const ObjectType objectType)
{
// Should typically use GetObject instead. This function should only be used for performance, when the array info is
// guaranteed to be updated correctly by the caller.
Assert(objectType == ObjectType::Array || objectType == ObjectType::ObjectWithArray);
ValueType valueType(UninitializedObject);
valueType.SetObjectType(objectType);
return Verify(valueType);
}
ValueType::ValueType() : bits(Uninitialized.bits)
{
CompileAssert(sizeof(ValueType) == sizeof(TSize));
CompileAssert(sizeof(ObjectType) == sizeof(TSize));
}
ValueType::ValueType(const Bits bits) : bits(bits)
{
}
ValueType ValueType::Verify(const Bits bits)
{
return Verify(ValueType(bits));
}
ValueType ValueType::Verify(const ValueType valueType)
{
Assert(valueType.bits);
Assert(!valueType.OneOn(Bits::Object) || valueType.GetObjectType() < ObjectType::Count);
Assert(
valueType.OneOn(Bits::Object) ||
valueType.OneOn(Bits::Int) ||
valueType.AnyOnExcept(Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged));
Assert(
valueType.OneOn(Bits::Object) ||
!valueType.AllEqual(Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged, Bits::IntIsLikelyUntagged));
return valueType;
}
bool ValueType::OneOn(const Bits b) const
{
return AnyOn(b);
}
bool ValueType::AnyOn(const Bits b) const
{
Assert(b);
return !!(bits & b);
}
bool ValueType::AllEqual(const Bits b, const Bits e) const
{
Assert(b);
return (bits & b) == e;
}
bool ValueType::AllOn(const Bits b) const
{
return AllEqual(b, b);
}
bool ValueType::OneOnOneOff(const Bits on, const Bits off) const
{
return AllOnAllOff(on, off);
}
bool ValueType::AllOnAllOff(const Bits on, const Bits off) const
{
return AllEqual(on | off, on);
}
bool ValueType::OneOnOthersOff(const Bits b) const
{
return AllOnOthersOff(b);
}
bool ValueType::OneOnOthersOff(const Bits b, const Bits ignore) const
{
return AllOnOthersOff(b, ignore);
}
bool ValueType::AnyOnOthersOff(const Bits b) const
{
Assert(b);
return !(bits & ~b);
}
bool ValueType::AnyOnOthersOff(const Bits b, const Bits ignore) const
{
Assert(b);
Assert(ignore);
Assert(!(b & ignore)); // not necessary for this function to work correctly, but generally not expected
return AnyOn(b) && AnyOnOthersOff(b | ignore);
}
bool ValueType::AllOnOthersOff(const Bits b) const
{
Assert(b);
return bits == b;
}
bool ValueType::AllOnOthersOff(const Bits b, const Bits ignore) const
{
Assert(b);
Assert(ignore);
Assert(!(b & ignore));
return AllEqual(~ignore, b);
}
bool ValueType::AnyOnExcept(const Bits b) const
{
Assert(!!b && !!~b);
return !AnyOn(b);
}
bool ValueType::IsUninitialized() const
{
return AllOnOthersOff(Bits::Likely, Bits::CanBeTaggedValue);
}
bool ValueType::IsDefinite() const
{
return !OneOn(Bits::Likely);
}
bool ValueType::IsTaggedInt() const
{
return AllOnOthersOff(Bits::Int | Bits::CanBeTaggedValue);
}
bool ValueType::IsIntAndLikelyTagged() const
{
return AllOnOthersOff(Bits::Int | Bits::CanBeTaggedValue, Bits::IntCanBeUntagged);
}
bool ValueType::IsLikelyTaggedInt() const
{
return AllOnOthersOff(Bits::Int | Bits::CanBeTaggedValue, Bits::Likely | Bits::IntCanBeUntagged | Bits::Number);
}
bool ValueType::HasBeenUntaggedInt() const
{
return OneOnOneOff(Bits::IntIsLikelyUntagged, Bits::Object);
}
bool ValueType::IsIntAndLikelyUntagged() const
{
return AllOnOthersOff(Bits::Int | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyUntaggedInt() const
{
return AllOnOthersOff(Bits::Int | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged,
Bits::Likely | Bits::Number | Bits::CanBeTaggedValue);
}
bool ValueType::IsNotTaggedValue() const
{
return IsNotNumber() || !OneOn(Bits::CanBeTaggedValue);
}
bool ValueType::CanBeTaggedValue() const
{
return !IsNotTaggedValue();
}
ValueType ValueType::SetCanBeTaggedValue(const bool b) const
{
if (b)
{
Assert(!IsNotNumber());
return Verify(bits | Bits::CanBeTaggedValue);
}
return Verify(bits & ~Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenInt() const
{
return OneOnOneOff(Bits::Int, Bits::Object);
}
bool ValueType::IsInt() const
{
return OneOnOthersOff(Bits::Int, Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyInt() const
{
return OneOnOthersOff(
Bits::Int,
Bits::Likely | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::Number | Bits::CanBeTaggedValue);
}
bool ValueType::IsNotInt() const
{
return
AnyOnExcept(Bits::Likely | Bits::Object | Bits::Int | Bits::CanBeTaggedValue | Bits::Float | Bits::Number) ||
OneOnOneOff(Bits::Object, Bits::Likely);
}
bool ValueType::IsNotNumber() const
{
// These are the same for now.
return IsNotInt();
}
bool ValueType::HasBeenFloat() const
{
return OneOnOneOff(Bits::Float, Bits::Object);
}
bool ValueType::IsFloat() const
{
// TODO: Require that the int bits are off. We can then use (!IsFloat() && IsNumber()) to determine that a tagged int check
// needs to be done but not a JavascriptNumber/TaggedFloat check.
return
OneOnOthersOff(
Bits::Float,
(
Bits::Int |
Bits::IntCanBeUntagged |
Bits::IntIsLikelyUntagged |
Bits::CanBeTaggedValue |
Bits::Number
));
}
bool ValueType::IsNotFloat() const
{
return
AnyOnExcept(Bits::Likely | Bits::Object | Bits::CanBeTaggedValue | Bits::Float | Bits::Number) ||
OneOnOneOff(Bits::Object, Bits::Likely);
}
bool ValueType::IsLikelyFloat() const
{
return
OneOnOthersOff(
Bits::Float,
(
Bits::Likely |
Bits::Undefined |
Bits::Int |
Bits::IntCanBeUntagged |
Bits::IntIsLikelyUntagged |
Bits::CanBeTaggedValue |
Bits::Number
));
}
bool ValueType::HasBeenNumber() const
{
return !OneOn(Bits::Object) && AnyOn(Bits::Int | Bits::Float | Bits::Number);
}
bool ValueType::IsNumber() const
{
return AnyOnOthersOff(Bits::Int | Bits::Float | Bits::Number,
Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyNumber() const
{
return
AnyOnOthersOff(
Bits::Int | Bits::Float | Bits::Number,
Bits::Likely | Bits::Undefined | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenUnknownNumber() const
{
return OneOnOneOff(Bits::Number, Bits::Object);
}
bool ValueType::IsUnknownNumber() const
{
// Equivalent to IsNumber() && !IsLikelyInt() && !IsLikelyFloat()
return OneOnOthersOff(Bits::Number, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyUnknownNumber() const
{
// If true, equivalent to IsLikelyNumber() && !IsLikelyInt() && !IsLikelyFloat()
return OneOnOthersOff(Bits::Number, Bits::Likely | Bits::Undefined | Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenUndefined() const
{
return OneOn(Bits::Undefined);
}
bool ValueType::IsUndefined() const
{
return OneOnOthersOff(Bits::Undefined, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyUndefined() const
{
return OneOnOthersOff(Bits::Undefined, Bits::Likely | Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenNull() const
{
return OneOn(Bits::Null);
}
bool ValueType::IsNull() const
{
return OneOnOthersOff(Bits::Null, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyNull() const
{
return OneOnOthersOff(Bits::Null, Bits::Likely | Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenBoolean() const
{
return OneOnOneOff(Bits::Boolean, Bits::Object);
}
bool ValueType::IsBoolean() const
{
return OneOnOthersOff(Bits::Boolean, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelyBoolean() const
{
return OneOnOthersOff(Bits::Boolean, Bits::Likely | Bits::CanBeTaggedValue);
}
bool ValueType::HasBeenString() const
{
return OneOnOneOff(Bits::String, Bits::Object);
}
bool ValueType::IsString() const
{
return OneOnOthersOff(Bits::String, Bits::CanBeTaggedValue);
}
bool ValueType::HasHadStringTag() const
{
return !!(bits & Bits::String);
}
bool ValueType::IsLikelyString() const
{
return OneOnOthersOff(Bits::String, Bits::Likely | Bits::CanBeTaggedValue);
}
bool ValueType::IsNotString() const
{
return AnyOnExcept(Bits::Likely | Bits::Object | Bits::String | Bits::CanBeTaggedValue)
|| OneOnOneOff(Bits::Object, Bits::Likely);
}
bool ValueType::HasBeenSymbol() const
{
return OneOnOneOff(Bits::Symbol, Bits::Object);
}
bool ValueType::IsSymbol() const
{
return OneOnOthersOff(Bits::Symbol, Bits::CanBeTaggedValue);
}
bool ValueType::IsLikelySymbol() const
{
return OneOnOthersOff(Bits::Symbol, Bits::Likely | Bits::CanBeTaggedValue);
}
bool ValueType::IsNotSymbol() const
{
return AnyOnExcept(Bits::Likely | Bits::Object | Bits::Symbol | Bits::CanBeTaggedValue)
|| OneOnOneOff(Bits::Object, Bits::Likely);
}
bool ValueType::HasBeenPrimitive() const
{
return
OneOn(Bits::Object)
?
AnyOn(Bits::Undefined | Bits::Null)
:
AnyOn(
Bits::Undefined |
Bits::Null |
Bits::Int |
Bits::Float |
Bits::Number |
Bits::Boolean |
Bits::String |
Bits::Symbol |
Bits::PrimitiveOrObject |
Bits::Simd);
}
bool ValueType::IsPrimitive() const
{
bool result =
AnyOnOthersOff(
Bits::Undefined | Bits::Null | Bits::Int | Bits::Float | Bits::Number | Bits::Boolean | Bits::String | Bits::Symbol | Bits::Simd,
Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue);
return result;
}
bool ValueType::IsLikelyPrimitive() const
{
bool result =
AnyOnOthersOff(
Bits::Undefined | Bits::Null | Bits::Int | Bits::Float | Bits::Number | Bits::Boolean | Bits::String | Bits::Symbol | Bits::Simd,
Bits::Likely | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue);
return result;
}
#if DBG
bool ValueType::IsSimilar(ValueType v) const
{
// Remove bits we don't care for comparison
ValueType left = Verify(bits & ~(Bits::NoMissingValues | Bits::CanBeTaggedValue | Bits::Likely | Bits::Null | Bits::Undefined));
ValueType right = Verify(v.bits & ~(Bits::NoMissingValues | Bits::CanBeTaggedValue | Bits::Likely | Bits::Null | Bits::Undefined));
return left == right;
}
#endif
bool ValueType::HasBeenObject() const
{
return AnyOn(Bits::Object | Bits::PrimitiveOrObject);
}
bool ValueType::IsObject() const
{
return AllOnAllOff(Bits::Object, Bits::Likely | Bits::Undefined | Bits::Null);
}
bool ValueType::IsLikelyObject() const
{
// For syms that are typically used as objects, they often also have Undefined or Null values, and they are used as objects
// only after checking for Undefined or Null. So, for the purpose of determining whether a value type is likely object, the
// Undefined and Null bits are ignored.
return OneOn(Bits::Object);
}
bool ValueType::IsNotObject() const
{
return AnyOnExcept(Bits::Likely | Bits::Object | Bits::PrimitiveOrObject);
}
bool ValueType::CanMergeToObject() const
{
Assert(!IsLikelyObject());
return AnyOnExcept(BitPattern(VALUE_TYPE_NONOBJECT_BIT_COUNT, VALUE_TYPE_COMMON_BIT_COUNT));
}
bool ValueType::CanMergeToSpecificObjectType() const
{
return IsLikelyObject() ? GetObjectType() == ObjectType::UninitializedObject : CanMergeToObject();
}
bool ValueType::IsRegExp() const
{
return IsObject() && GetObjectType() == ObjectType::RegExp;
}
bool ValueType::IsLikelyRegExp() const
{
return IsLikelyObject() && GetObjectType() == ObjectType::RegExp;
}
bool ValueType::IsArray() const
{
return IsObject() && GetObjectType() == ObjectType::Array;
}
bool ValueType::IsLikelyArray() const
{
return IsLikelyObject() && GetObjectType() == ObjectType::Array;
}
bool ValueType::IsNotArray() const
{
return IsNotObject() || (IsObject() && GetObjectType() > ObjectType::Object && GetObjectType() != ObjectType::Array);
}
bool ValueType::IsArrayOrObjectWithArray() const
{
return IsObject() && (GetObjectType() == ObjectType::ObjectWithArray || GetObjectType() == ObjectType::Array);
}
bool ValueType::IsLikelyArrayOrObjectWithArray() const
{
return IsLikelyObject() && (GetObjectType() == ObjectType::ObjectWithArray || GetObjectType() == ObjectType::Array);
}
bool ValueType::IsNotArrayOrObjectWithArray() const
{
return
IsNotObject() ||
(IsObject() && GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array);
}
bool ValueType::IsNativeArray() const
{
return IsArrayOrObjectWithArray() && !HasVarElements();
}
bool ValueType::IsLikelyNativeArray() const
{
return IsLikelyArrayOrObjectWithArray() && !HasVarElements();
}
bool ValueType::IsNotNativeArray() const
{
return
IsNotObject() ||
(
IsObject() &&
((GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array) || HasVarElements())
);
}
bool ValueType::IsNativeIntArray() const
{
return IsArrayOrObjectWithArray() && HasIntElements();
}
bool ValueType::IsLikelyNativeIntArray() const
{
return IsLikelyArrayOrObjectWithArray() && HasIntElements();
}
bool ValueType::IsNativeFloatArray() const
{
return IsArrayOrObjectWithArray() && HasFloatElements();
}
bool ValueType::IsLikelyNativeFloatArray() const
{
return IsLikelyArrayOrObjectWithArray() && HasFloatElements();
}
bool ValueType::IsTypedIntArray() const
{
return IsObject() && GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Uint32Array;
}
bool ValueType::IsLikelyTypedIntArray() const
{
return IsLikelyObject() && GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Uint32Array;
}
bool ValueType::IsTypedArray() const
{
return IsObject() && GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::CharArray;
}
bool ValueType::IsLikelyTypedArray() const
{
return IsLikelyObject() && GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::CharArray;
}
bool ValueType::IsTypedIntOrFloatArray() const
{
return IsObject() && ((GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Float64Array));
}
bool ValueType::IsOptimizedTypedArray() const
{
return IsObject() && ((GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Float64MixedArray));
}
bool ValueType::IsOptimizedVirtualTypedArray() const
{
return IsObject() && (GetObjectType() >= ObjectType::Int8VirtualArray && GetObjectType() <= ObjectType::Float64VirtualArray);
}
bool ValueType::IsLikelyOptimizedTypedArray() const
{
return IsLikelyObject() && ((GetObjectType() >= ObjectType::Int8Array && GetObjectType() <= ObjectType::Float64MixedArray));
}
bool ValueType::IsLikelyOptimizedVirtualTypedArray() const
{
return IsLikelyObject() && (GetObjectType() >= ObjectType::Int8VirtualArray && GetObjectType() <= ObjectType::Float64VirtualArray);
}
bool ValueType::IsAnyArrayWithNativeFloatValues() const
{
if(!IsObject())
return false;
switch(GetObjectType())
{
case ObjectType::ObjectWithArray:
case ObjectType::Array:
return HasFloatElements();
case ObjectType::Float32Array:
case ObjectType::Float32VirtualArray:
case ObjectType::Float32MixedArray:
case ObjectType::Float64Array:
case ObjectType::Float64VirtualArray:
case ObjectType::Float64MixedArray:
return true;
}
return false;
}
bool ValueType::IsLikelyAnyArrayWithNativeFloatValues() const
{
if(!IsLikelyObject())
return false;
switch(GetObjectType())
{
case ObjectType::ObjectWithArray:
case ObjectType::Array:
return HasFloatElements();
case ObjectType::Float32Array:
case ObjectType::Float32VirtualArray:
case ObjectType::Float32MixedArray:
case ObjectType::Float64Array:
case ObjectType::Float64VirtualArray:
case ObjectType::Float64MixedArray:
return true;
}
return false;
}
bool ValueType::IsAnyArray() const
{
return IsObject() && GetObjectType() >= ObjectType::ObjectWithArray && GetObjectType() <= ObjectType::CharArray;
}
bool ValueType::IsLikelyAnyArray() const
{
return IsLikelyObject() && GetObjectType() >= ObjectType::ObjectWithArray && GetObjectType() <= ObjectType::CharArray;
}
bool ValueType::IsAnyOptimizedArray() const
{
return IsObject() && ((GetObjectType() >= ObjectType::ObjectWithArray && GetObjectType() <= ObjectType::Float64MixedArray));
}
bool ValueType::IsLikelyAnyOptimizedArray() const
{
return IsLikelyObject() && ((GetObjectType() >= ObjectType::ObjectWithArray && GetObjectType() <= ObjectType::Float64MixedArray));
}
bool ValueType::IsLikelyAnyUnOptimizedArray() const
{
return IsLikelyObject() && GetObjectType() >= ObjectType::Int64Array && GetObjectType() <= ObjectType::CharArray;
}
bool ValueType::IsSimd128() const
{
return OneOnOthersOff(Bits::Simd);
}
ObjectType ValueType::GetObjectType() const
{
Assert(OneOn(Bits::Object));
return _objectType;
}
void ValueType::SetObjectType(const ObjectType objectType)
{
Assert(OneOn(Bits::Object));
Assert(objectType < ObjectType::Count);
_objectType = objectType;
}
ValueType ValueType::SetIsNotAnyOf(const ValueType other) const
{
Assert(other.IsDefinite());
Assert(!other.HasBeenObject());
return bits & ~other.bits;
}
bool ValueType::HasNoMissingValues() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return OneOn(Bits::NoMissingValues);
}
ValueType ValueType::SetHasNoMissingValues(const bool noMissingValues) const
{
Assert(IsLikelyArrayOrObjectWithArray());
if(noMissingValues)
return Verify(bits | Bits::NoMissingValues);
return Verify(bits & ~Bits::NoMissingValues);
}
bool ValueType::HasNonInts() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return OneOn(Bits::NonInts);
}
bool ValueType::HasNonFloats() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return OneOn(Bits::NonFloats);
}
bool ValueType::HasIntElements() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return !OneOn(Bits::NonInts);
}
bool ValueType::HasFloatElements() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return OneOnOneOff(Bits::NonInts, Bits::NonFloats);
}
bool ValueType::HasVarElements() const
{
Assert(IsLikelyArrayOrObjectWithArray());
return AllOn(Bits::NonInts | Bits::NonFloats);
}
ValueType ValueType::SetArrayTypeId(const Js::TypeId typeId) const
{
using namespace Js;
Assert(IsLikelyArrayOrObjectWithArray());
Assert(JavascriptArray::Is(typeId));
Assert(typeId == TypeIds_Array || IsLikelyObject() && GetObjectType() == ObjectType::Array); // objects with native arrays are currently not supported
Bits newBits = bits & ~(Bits::NonInts | Bits::NonFloats);
switch(typeId)
{
case TypeIds_Array:
newBits |= Bits::NonFloats;
// fall through
case TypeIds_NativeFloatArray:
newBits |= Bits::NonInts;
break;
}
return Verify(newBits);
}
bool ValueType::IsSubsetOf(
const ValueType other,
const bool isAggressiveIntTypeSpecEnabled,
const bool isFloatSpecEnabled,
const bool isArrayMissingValueCheckHoistEnabled,
const bool isNativeArrayEnabled) const
{
if(IsUninitialized())
return other.IsUninitialized();
if(other.IsUninitialized())
return true;
if(IsLikelyNumber() && other.IsLikelyNumber())
{
// Special case for numbers since there are multiple combinations of bits and a bit-subset produces incorrect results in
// some cases
// When type specialization is enabled, a value type that is likely int or float is considered to be more specific than
// a value type that is definitely number but unknown as to whether it was int or float, because the former can
// participate in type specialization while the latter cannot. When type specialization is disabled, the definite value
// type is considered to be a subset of the indefinite value type because neither will participate in type
// specialization.
if(other.IsUnknownNumber() &&
((isAggressiveIntTypeSpecEnabled && IsLikelyInt()) || (isFloatSpecEnabled && IsLikelyFloat())))
{
return true;
}
// The following number types are listed in order of most specific type to least specific type. Types are considered to
// be subsets of the less specific types below it, with the exception that int types are not considered to be subsets of
// float types.
// TaggedInt
// IntAndLikelyTagged
// Int
// Float
// Number
// This logic doesn't play well with Bits::Undefined, so remove it before proceeding.
ValueType _this = this->bits & ~Bits::Undefined;
ValueType _other = other.bits & ~Bits::Undefined;
return
(!_this.OneOn(Bits::Likely) || _other.OneOn(Bits::Likely)) &&
(
((
_this.IsTaggedInt() ||
(_this.IsLikelyTaggedInt() && !_other.IsTaggedInt()) ||
(_this.IsLikelyInt() && !_other.IsLikelyTaggedInt())
) && !_other.IsLikelyFloat()) ||
(_this.IsLikelyFloat() && !_other.IsLikelyInt()) ||
_other.IsLikelyUnknownNumber()
);
}
const Bits commonBits = bits & (BitPattern(VALUE_TYPE_COMMON_BIT_COUNT) - Bits::Object);
if(!!commonBits && !other.AllOn(commonBits))
return false;
if(OneOn(Bits::Object))
{
if(!other.OneOn(Bits::Object))
return other.OneOn(Bits::PrimitiveOrObject) || (!other.IsDefinite() && other.CanMergeToObject());
}
else
{
if(!other.OneOn(Bits::Object))
return other.AllOn(bits);
return CanMergeToObject();
}
if(other.GetObjectType() == ObjectType::UninitializedObject && GetObjectType() != ObjectType::UninitializedObject)
return true; // object types other than UninitializedObject are a subset of UninitializedObject regardless of the Likely bit
if(GetObjectType() != other.GetObjectType())
return false;
if((!OneOn(Bits::Likely) && other.OneOn(Bits::Likely)) ||
(other.GetObjectType() != ObjectType::ObjectWithArray && other.GetObjectType() != ObjectType::Array))
{
return true;
}
// The following javascript array types are listed in the order of most specific type to least specific type. Types are
// considered to be subsets of the less specific types below it.
// Int32 !HasNonInts() (!HasNonFloats() is implied)
// Float64 HasNonInts() && !HasNonFloats()
// Var HasNonFloats() (HasNonInts() is implied)
return
(HasNoMissingValues() || !other.HasNoMissingValues() || !isArrayMissingValueCheckHoistEnabled) &&
(
((!HasNonInts() || other.HasNonInts()) && (!HasNonFloats() || other.HasNonFloats())) ||
!isNativeArrayEnabled
);
}
ValueType ValueType::ToDefinite() const
{
Assert(!IsUninitialized());
return Verify(bits & ~Bits::Likely);
}
ValueType ValueType::ToLikelyUntaggedInt() const
{
Assert(IsLikelyInt());
Assert(!IsInt());
return Verify(bits | (Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged));
}
ValueType ValueType::ToDefiniteNumber_PreferFloat() const
{
return IsNumber() ? *this : ToDefiniteAnyFloat();
}
ValueType ValueType::ToDefiniteAnyFloat() const
{
// Not asserting on expected value type because float specialization allows specializing values of arbitrary types, even
// values that are definitely not float
return
Verify(
OneOn(Bits::Object)
? (Bits::Float | Bits::CanBeTaggedValue)
: bits & (Bits::Int | Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged | Bits::CanBeTaggedValue | Bits::Number) | Bits::Float);
}
ValueType ValueType::ToDefiniteNumber() const
{
Assert(IsLikelyNumber());
return IsNumber() ? *this : ToDefiniteAnyNumber();
}
ValueType ValueType::ToDefiniteAnyNumber() const
{
// Not asserting on expected value type because Conv_Num allows converting values of arbitrary types to number
if(OneOn(Bits::Object | Bits::PrimitiveOrObject))
return Verify(Bits::Number | Bits::CanBeTaggedValue);
Bits numberBits =
bits &
(
Bits::Int |
Bits::IntCanBeUntagged |
Bits::IntIsLikelyUntagged |
Bits::CanBeTaggedValue |
Bits::Float |
Bits::Number
);
if(!(numberBits & (Bits::Float | Bits::Number)))
numberBits |= Bits::Number | Bits::CanBeTaggedValue;
return Verify(numberBits);
}
ValueType ValueType::ToDefinitePrimitiveSubset() const
{
// This function does not do a safe conversion of an arbitrary type to a definitely-primitive type. It only obtains the
// primitive subset of bits from the type.
// When Undefined (or Null) merge with Object, the resulting value type is still likely Object (IsLikelyObject() returns
// true). ToDefinite() on the merged type would return a type that is definitely Undefined or Object. Usually, that type is
// not interesting and a test for one or more of the primitive types may have been done. ToDefinitePrimitive() removes the
// object-specific bits.
Assert(HasBeenPrimitive());
Assert(HasBeenObject());
// If we have an object format of a type (object bit = 1) that represents a primitive (e.g. SIMD128),
// we want to keep the object bit and type along with other merged primitives (Undefined and/or Null).
if (IsLikelyObject() && IsLikelyPrimitive())
return Verify(bits & (Bits::Undefined | Bits::Null) | ToDefiniteObject().bits);
return
Verify(
bits &
(
OneOn(Bits::Object)
?
BitPattern(VALUE_TYPE_COMMON_BIT_COUNT) - (Bits::Likely | Bits::Object)
:
BitPattern(VALUE_TYPE_COMMON_BIT_COUNT + VALUE_TYPE_NONOBJECT_BIT_COUNT) -
(Bits::Likely | Bits::Object | Bits::PrimitiveOrObject)
));
}
ValueType ValueType::ToDefiniteObject() const
{
// When Undefined (or Null) merge with Object, the resulting value type is still likely Object (IsLikelyObject() returns
// true). ToDefinite() on the merged type would return a type that is definitely Undefined or Object. Usually, that type is
// not interesting and a test for the Object type may have been done to ensure the Object type. ToDefiniteObject() removes
// the Undefined and Null bits as well.
Assert(IsLikelyObject());
return Verify(bits & ~(BitPattern(VALUE_TYPE_COMMON_BIT_COUNT) - Bits::Object));
}
ValueType ValueType::ToLikely() const
{
return Verify(bits | Bits::Likely | Bits::CanBeTaggedValue);
}
ValueType ValueType::ToArray() const
{
Assert(GetObjectType() == ObjectType::ObjectWithArray);
ValueType valueType(*this);
valueType.SetObjectType(ObjectType::Array);
return Verify(valueType);
}
ValueType ValueType::ToPrimitiveOrObject() const
{
// When an object type is merged with a non-object type, the PrimitiveOrObject bit is set in the merged type by converting
// the object type to a PrimitiveOrObject type (preserving only the common bits other than Object) and merging it with the
// non-object type. The PrimitiveOrObject type will not have the Object bit set, so that it can still be queried for whether
// it was any of the other types.
Assert(OneOn(Bits::Object));
return Verify(bits & (BitPattern(VALUE_TYPE_COMMON_BIT_COUNT) - Bits::Object) | Bits::PrimitiveOrObject);
}
ValueType ValueType::MergeWithObject(const ValueType other) const
{
ValueType merged(bits | other.bits);
Assert(merged.OneOn(Bits::Object));
if(ValueType(bits & other.bits).OneOn(Bits::Object)) // both have the Object bit set
{
if (GetObjectType() == other.GetObjectType())
return Verify(merged);
const ObjectType typedArrayMergedObjectType =
TypedArrayMergeMap[static_cast<uint16>(GetObjectType())][static_cast<uint16>(other.GetObjectType())];
if (typedArrayMergedObjectType != ObjectType::UninitializedObject)
{
merged.SetObjectType(typedArrayMergedObjectType);
return Verify(merged);
}
if(GetObjectType() != ObjectType::UninitializedObject && other.GetObjectType() != ObjectType::UninitializedObject)
{
// Any two different specific object types (excludes UninitializedObject and Object, which don't indicate any
// specific type of object) merge to Object since the resulting type is not guaranteed to indicate any specific type
if (IsArrayOrObjectWithArray() || other.IsArrayOrObjectWithArray())
{
return Verify(GetObject(ObjectType::Object).ToLikely());
}
merged.SetObjectType(ObjectType::Object);
return Verify(merged);
}
// Since UninitializedObject is a generic object type, when merged with a specific object type, the resulting object
// type is not guaranteed to be any specific object type. However, since UninitializedObject means that we don't have
// info about the object type, we can assume that the merged type is likely an object of the specific type.
//
// If both were definitely object, we lose the information that the merged type is also definitely object. For now
// though, it's better to have "likely object of a specific type" than "definitely object of an unknown type". It may
// eventually become necessary to distinguish these and have a "definitely object, likely of a specific type".
return
Verify(
GetObjectType() > ObjectType::Object || other.GetObjectType() > ObjectType::Object
? merged.ToLikely()
: merged);
}
if(OneOn(Bits::Object))
{
if(other.CanMergeToObject())
return Verify(merged);
return Verify(ToPrimitiveOrObject().bits | other.bits); // see ToPrimitiveOrObject
}
Assert(other.OneOn(Bits::Object));
if(CanMergeToObject())
return Verify(merged);
return Verify(bits | other.ToPrimitiveOrObject().bits); // see ToPrimitiveOrObject
}
ValueType ValueType::Merge(const Js::Var var) const
{
using namespace Js;
Assert(var);
if(TaggedInt::Is(var))
return Merge(GetTaggedInt());
if(JavascriptNumber::Is_NoTaggedIntCheck(var))
{
return
Merge(
(IsUninitialized() || IsLikelyInt()) && JavascriptNumber::IsInt32_NoChecks(var)
? GetInt(false)
: ValueType::Float);
}
return Merge(FromObject(RecyclableObject::UnsafeFromVar(var)));
}
ValueType::Bits ValueType::TypeIdToBits[Js::TypeIds_Limit];
ValueType::Bits ValueType::VirtualTypeIdToBits[Js::TypeIds_Limit];
INT_PTR ValueType::TypeIdToVtable[Js::TypeIds_Limit];
ObjectType ValueType::VirtualTypedArrayPair[(uint16)ObjectType::Count];
ObjectType ValueType::MixedTypedArrayPair[(uint16)ObjectType::Count];
ObjectType ValueType::TypedArrayMergeMap[(uint16)ObjectType::Count][(uint16)ObjectType::Count];
ObjectType ValueType::MixedTypedToVirtualTypedArray[(uint16)ObjectType::Count];
void ValueType::InitializeTypeIdToBitsMap()
{
using namespace Js;
// Initialize all static types to Uninitialized first, so that a zero will indicate that it's a dynamic type
for (TypeId typeId = static_cast<TypeId>(0); typeId <= TypeIds_LastStaticType; typeId = static_cast<TypeId>(typeId + 1))
{
TypeIdToBits[typeId] = ValueType::Uninitialized.bits;
VirtualTypeIdToBits[typeId] = ValueType::Uninitialized.bits;
TypeIdToVtable[typeId] = (INT_PTR)nullptr;
}
for (ObjectType objType = static_cast<ObjectType>(0); objType <ObjectType::Count; objType = static_cast<ObjectType>((uint16)(objType) + 1))
{
VirtualTypedArrayPair[(uint16)objType] = ObjectType::UninitializedObject;
MixedTypedArrayPair[(uint16)objType] = ObjectType::UninitializedObject;
MixedTypedToVirtualTypedArray[(uint16)objType] = ObjectType::UninitializedObject;
}
for (ObjectType objType = static_cast<ObjectType>(0); objType < ObjectType::Count; objType = static_cast<ObjectType>((uint16)(objType)+1))
{
for (ObjectType objTypeInner = static_cast<ObjectType>(0); objTypeInner < ObjectType::Count; objTypeInner = static_cast<ObjectType>((uint16)(objTypeInner)+1))
TypedArrayMergeMap[(uint16)objType][(uint16)objTypeInner] = ObjectType::UninitializedObject;
}
TypeIdToBits[TypeIds_Undefined ] = ValueType::Undefined.bits;
TypeIdToBits[TypeIds_Null ] = ValueType::Null.bits;
TypeIdToBits[TypeIds_Boolean ] = ValueType::Boolean.bits;
TypeIdToBits[TypeIds_String ] = ValueType::String.bits;
TypeIdToBits[TypeIds_Symbol ] = ValueType::Symbol.bits;
TypeIdToBits[TypeIds_RegEx ] = GetObject(ObjectType::RegExp).bits;
TypeIdToBits[TypeIds_Int8Array ] = GetObject(ObjectType::Int8Array).bits;
TypeIdToBits[TypeIds_Uint8Array ] = GetObject(ObjectType::Uint8Array).bits;
TypeIdToBits[TypeIds_Uint8ClampedArray ] = GetObject(ObjectType::Uint8ClampedArray).bits;
TypeIdToBits[TypeIds_Int16Array ] = GetObject(ObjectType::Int16Array).bits;
TypeIdToBits[TypeIds_Uint16Array ] = GetObject(ObjectType::Uint16Array).bits;
TypeIdToBits[TypeIds_Int32Array ] = GetObject(ObjectType::Int32Array).bits;
TypeIdToBits[TypeIds_Uint32Array ] = GetObject(ObjectType::Uint32Array).bits;
TypeIdToBits[TypeIds_Float32Array ] = GetObject(ObjectType::Float32Array).bits;
TypeIdToBits[TypeIds_Float64Array ] = GetObject(ObjectType::Float64Array).bits;
TypeIdToBits[TypeIds_Int64Array ] = GetObject(ObjectType::Int64Array).bits;
TypeIdToBits[TypeIds_Uint64Array ] = GetObject(ObjectType::Uint64Array).bits;
TypeIdToBits[TypeIds_CharArray ] = GetObject(ObjectType::CharArray).bits;
TypeIdToBits[TypeIds_BoolArray ] = GetObject(ObjectType::BoolArray).bits;
TypeIdToBits[TypeIds_SIMDFloat32x4 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDInt32x4 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDInt16x8 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDInt8x16 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDUint16x8 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDUint8x16 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_SIMDFloat64x2 ] = ValueType::Simd.bits;
TypeIdToBits[TypeIds_HostDispatch ] = ValueType::UninitializedObject.bits;
VirtualTypeIdToBits[TypeIds_Int8Array] = GetObject(ObjectType::Int8VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Uint8Array] = GetObject(ObjectType::Uint8VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Uint8ClampedArray] = GetObject(ObjectType::Uint8ClampedArray).bits;
VirtualTypeIdToBits[TypeIds_Int16Array] = GetObject(ObjectType::Int16VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Uint16Array] = GetObject(ObjectType::Uint16VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Int32Array] = GetObject(ObjectType::Int32VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Uint32Array] = GetObject(ObjectType::Uint32VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Float32Array] = GetObject(ObjectType::Float32VirtualArray).bits;
VirtualTypeIdToBits[TypeIds_Float64Array] = GetObject(ObjectType::Float64VirtualArray).bits;
TypeIdToVtable[TypeIds_Int8Array] = VirtualTableInfo<Int8VirtualArray>::Address;
TypeIdToVtable[TypeIds_Uint8Array] = VirtualTableInfo<Uint8VirtualArray>::Address;
TypeIdToVtable[TypeIds_Uint8ClampedArray] = VirtualTableInfo<Uint8ClampedVirtualArray>::Address;
TypeIdToVtable[TypeIds_Int16Array] = VirtualTableInfo<Int16VirtualArray>::Address;
TypeIdToVtable[TypeIds_Uint16Array] = VirtualTableInfo<Uint16VirtualArray>::Address;
TypeIdToVtable[TypeIds_Int32Array] = VirtualTableInfo<Int32VirtualArray>::Address;
TypeIdToVtable[TypeIds_Uint32Array] = VirtualTableInfo<Uint32VirtualArray>::Address;
TypeIdToVtable[TypeIds_Float32Array] = VirtualTableInfo<Float32VirtualArray>::Address;
TypeIdToVtable[TypeIds_Float64Array] = VirtualTableInfo<Float64VirtualArray>::Address;
VirtualTypedArrayPair[(int)ObjectType::Int8VirtualArray] = ObjectType::Int8Array;
VirtualTypedArrayPair[(int)ObjectType::Int8Array] = ObjectType::Int8VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Uint8VirtualArray] = ObjectType::Uint8Array;
VirtualTypedArrayPair[(int)ObjectType::Uint8Array] = ObjectType::Uint8VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Uint8ClampedVirtualArray] = ObjectType::Uint8ClampedArray;
VirtualTypedArrayPair[(int)ObjectType::Uint8ClampedArray] = ObjectType::Uint8ClampedVirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Int16VirtualArray] = ObjectType::Int16Array;
VirtualTypedArrayPair[(int)ObjectType::Int16Array] = ObjectType::Int16VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Uint16VirtualArray] = ObjectType::Uint16Array;
VirtualTypedArrayPair[(int)ObjectType::Uint16Array] = ObjectType::Uint16VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Int32VirtualArray] = ObjectType::Int32Array;
VirtualTypedArrayPair[(int)ObjectType::Int32Array] = ObjectType::Int32VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Uint32VirtualArray] = ObjectType::Uint32Array;
VirtualTypedArrayPair[(int)ObjectType::Uint32Array] = ObjectType::Uint32VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Float32VirtualArray] = ObjectType::Float32Array;
VirtualTypedArrayPair[(int)ObjectType::Float32Array] = ObjectType::Float32VirtualArray;
VirtualTypedArrayPair[(int)ObjectType::Float64VirtualArray] = ObjectType::Float64Array;
VirtualTypedArrayPair[(int)ObjectType::Float64Array] = ObjectType::Float64VirtualArray;
MixedTypedArrayPair[(int)ObjectType::Int8VirtualArray] = ObjectType::Int8MixedArray;
MixedTypedArrayPair[(int)ObjectType::Int8Array] = ObjectType::Int8MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint8VirtualArray] = ObjectType::Uint8MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint8Array] = ObjectType::Uint8MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint8ClampedVirtualArray] = ObjectType::Uint8ClampedMixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint8ClampedArray] = ObjectType::Uint8ClampedMixedArray;
MixedTypedArrayPair[(int)ObjectType::Int16VirtualArray] = ObjectType::Int16MixedArray;
MixedTypedArrayPair[(int)ObjectType::Int16Array] = ObjectType::Int16MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint16VirtualArray] = ObjectType::Uint16MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint16Array] = ObjectType::Uint16MixedArray;
MixedTypedArrayPair[(int)ObjectType::Int32VirtualArray] = ObjectType::Int32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Int32Array] = ObjectType::Int32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint32VirtualArray] = ObjectType::Uint32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Uint32Array] = ObjectType::Uint32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Float32VirtualArray] = ObjectType::Float32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Float32Array] = ObjectType::Float32MixedArray;
MixedTypedArrayPair[(int)ObjectType::Float64VirtualArray] = ObjectType::Float64MixedArray;
MixedTypedArrayPair[(int)ObjectType::Float64Array] = ObjectType::Float64MixedArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Int8MixedArray] = ObjectType::Int8VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Uint8MixedArray] = ObjectType::Uint8VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Uint8ClampedMixedArray] = ObjectType::Uint8ClampedVirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Int16MixedArray] = ObjectType::Int16VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Uint16MixedArray] = ObjectType::Uint16VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Int32MixedArray] = ObjectType::Int32VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Uint32MixedArray] = ObjectType::Uint32VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Float32MixedArray] = ObjectType::Float32VirtualArray;
MixedTypedToVirtualTypedArray[(int)ObjectType::Float64MixedArray] = ObjectType::Float64VirtualArray;
TypedArrayMergeMap[(int)ObjectType::Int8Array][(int)ObjectType::Int8VirtualArray] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int8VirtualArray][(int)ObjectType::Int8Array] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int8MixedArray][(int)ObjectType::Int8Array] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int8MixedArray][(int)ObjectType::Int8VirtualArray] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int8Array][(int)ObjectType::Int8MixedArray] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int8VirtualArray][(int)ObjectType::Int8MixedArray] = ObjectType::Int8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8Array][(int)ObjectType::Uint8VirtualArray] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8VirtualArray][(int)ObjectType::Uint8Array] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8MixedArray][(int)ObjectType::Uint8VirtualArray] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8MixedArray][(int)ObjectType::Uint8Array] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8Array][(int)ObjectType::Uint8MixedArray] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8VirtualArray][(int)ObjectType::Uint8MixedArray] = ObjectType::Uint8MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedArray][(int)ObjectType::Uint8ClampedVirtualArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedVirtualArray][(int)ObjectType::Uint8ClampedArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedMixedArray][(int)ObjectType::Uint8ClampedVirtualArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedMixedArray][(int)ObjectType::Uint8ClampedArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedArray][(int)ObjectType::Uint8ClampedMixedArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint8ClampedVirtualArray][(int)ObjectType::Uint8ClampedMixedArray] = ObjectType::Uint8ClampedMixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16Array][(int)ObjectType::Int16VirtualArray] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16VirtualArray][(int)ObjectType::Int16Array] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16MixedArray][(int)ObjectType::Int16VirtualArray] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16MixedArray][(int)ObjectType::Int16Array] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16Array][(int)ObjectType::Int16MixedArray] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int16VirtualArray][(int)ObjectType::Int16MixedArray] = ObjectType::Int16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16Array][(int)ObjectType::Uint16VirtualArray] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16VirtualArray][(int)ObjectType::Uint16Array] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16MixedArray][(int)ObjectType::Uint16VirtualArray] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16MixedArray][(int)ObjectType::Uint16Array] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16Array][(int)ObjectType::Uint16MixedArray] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint16VirtualArray][(int)ObjectType::Uint16MixedArray] = ObjectType::Uint16MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32Array][(int)ObjectType::Int32VirtualArray] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32VirtualArray][(int)ObjectType::Int32Array] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32MixedArray][(int)ObjectType::Int32VirtualArray] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32MixedArray][(int)ObjectType::Int32Array] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32Array][(int)ObjectType::Int32MixedArray] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Int32VirtualArray][(int)ObjectType::Int32MixedArray] = ObjectType::Int32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32Array][(int)ObjectType::Uint32VirtualArray] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32VirtualArray][(int)ObjectType::Uint32Array] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32MixedArray][(int)ObjectType::Uint32VirtualArray] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32MixedArray][(int)ObjectType::Uint32Array] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32Array][(int)ObjectType::Uint32MixedArray] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Uint32VirtualArray][(int)ObjectType::Uint32MixedArray] = ObjectType::Uint32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32Array][(int)ObjectType::Float32VirtualArray] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32VirtualArray][(int)ObjectType::Float32Array] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32MixedArray][(int)ObjectType::Float32VirtualArray] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32MixedArray][(int)ObjectType::Float32Array] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32Array][(int)ObjectType::Float32MixedArray] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float32VirtualArray][(int)ObjectType::Float32MixedArray] = ObjectType::Float32MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64Array][(int)ObjectType::Float64VirtualArray] = ObjectType::Float64MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64VirtualArray][(int)ObjectType::Float64Array] = ObjectType::Float64MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64MixedArray][(int)ObjectType::Float64VirtualArray] = ObjectType::Float64MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64MixedArray][(int)ObjectType::Float64Array] = ObjectType::Float64MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64Array][(int)ObjectType::Float64MixedArray] = ObjectType::Float64MixedArray;
TypedArrayMergeMap[(int)ObjectType::Float64VirtualArray][(int)ObjectType::Float64MixedArray] = ObjectType::Float64MixedArray;
}
INT_PTR ValueType::GetVirtualTypedArrayVtable(const Js::TypeId typeId)
{
if (typeId < _countof(TypeIdToVtable))
{
return TypeIdToVtable[typeId];
}
return NULL;
}
ValueType ValueType::FromTypeId(const Js::TypeId typeId, bool useVirtual)
{
if(typeId < _countof(TypeIdToBits))
{
if (useVirtual)
{
const Bits bits = VirtualTypeIdToBits[typeId];
if (!!bits)
return bits;
}
else
{
const Bits bits = TypeIdToBits[typeId];
if (!!bits)
return bits;
}
}
return Uninitialized;
}
ValueType ValueType::FromObject(Js::RecyclableObject *const recyclableObject)
{
using namespace Js;
Assert(recyclableObject);
const TypeId typeId = recyclableObject->GetTypeId();
if (typeId < _countof(TypeIdToBits))
{
const Bits bits = TypeIdToBits[typeId];
if (!!bits)
{
const ValueType valueType = Verify(bits);
if (!valueType.IsLikelyOptimizedTypedArray())
return valueType;
bool isVirtual = (VirtualTableInfoBase::GetVirtualTable(recyclableObject) == ValueType::GetVirtualTypedArrayVtable(typeId));
if (!isVirtual)
return valueType;
return GetObject(VirtualTypedArrayPair[static_cast<uint16>(valueType.GetObjectType())]);
}
}
Assert(DynamicType::Is(typeId)); // all static type IDs have nonzero values in TypeIdToBits
if(!JavascriptArray::Is(typeId))
{
// TODO: Once the issue with loop bodies and uninitialized interpreter local slots is fixed, use FromVar
DynamicObject *const object = static_cast<DynamicObject *>(recyclableObject);
if(!VirtualTableInfo<DynamicObject>::HasVirtualTable(object) || !object->HasObjectArray())
return GetObject(ObjectType::Object);
return FromObjectWithArray(object);
}
return FromArray(ObjectType::Array, static_cast<JavascriptArray *>(recyclableObject), typeId);
}
ValueType ValueType::FromObjectWithArray(Js::DynamicObject *const object)
{
using namespace Js;
Assert(object);
Assert(VirtualTableInfo<DynamicObject>::HasVirtualTable(object));
Assert(object->GetTypeId() == TypeIds_Object); // this check should be a superset of the DynamicObject vtable check above
Assert(object->HasObjectArray());
ArrayObject *const objectArray = object->GetObjectArray();
Assert(objectArray);
if(!VirtualTableInfo<JavascriptArray>::HasVirtualTable(objectArray))
return GetObject(ObjectType::Object);
return FromObjectArray(JavascriptArray::FromVar(objectArray));
}
ValueType ValueType::FromObjectArray(Js::JavascriptArray *const objectArray)
{
using namespace Js;
Assert(objectArray);
return FromArray(ObjectType::ObjectWithArray, objectArray, TypeIds_Array); // objects with native arrays are currently not supported
}
ValueType ValueType::FromArray(
const ObjectType objectType,
Js::JavascriptArray *const array,
const Js::TypeId arrayTypeId)
{
Assert(array);
Assert(array->GetTypeId() == arrayTypeId);
// TODO: Once the issue with loop bodies and uninitialized interpreter local slots is fixed, use FromVar and the checked version of HasNoMissingValues
return
Verify(
GetArray(objectType)
.SetHasNoMissingValues(array->HasNoMissingValues_Unchecked())
.SetArrayTypeId(arrayTypeId));
}
bool ValueType::operator ==(const ValueType other) const
{
return bits == other.bits;
}
bool ValueType::operator !=(const ValueType other) const
{
return !(*this == other);
}
uint ValueType::GetHashCode() const
{
return static_cast<uint>(bits);
}
const char *const ValueType::BitNames[] =
{
#define VALUE_TYPE_BIT(t, b) "" STRINGIZE(t) "",
#include "ValueTypes.h"
#undef VALUE_TYPE_BIT
};
const char *const ObjectTypeNames[] =
{
#define OBJECT_TYPE(ot) "" STRINGIZE(ot) "",
#include "ValueTypes.h"
#undef OBJECT_TYPE
};
size_t ValueType::GetLowestBitIndex(const Bits b)
{
Assert(b);
DWORD i;
::GetFirstBitSet(&i, static_cast<UnitWord32>(b));
return i;
}
void ValueType::ToVerboseString(char (&str)[VALUE_TYPE_MAX_STRING_SIZE]) const
{
if(IsUninitialized())
{
strcpy_s(str, "Uninitialized");
return;
}
Bits b = bits;
if(OneOn(Bits::Object))
{
// Exclude the object type for enumerating bits, and exclude bits specific to a different object type
b = static_cast<Bits>(_objectBits);
if(IsLikelyArrayOrObjectWithArray())
b &= ~(Bits::NonInts | Bits::NonFloats); // these are handled separately for better readability
else
b &= ~BitPattern(VALUE_TYPE_ARRAY_BIT_COUNT, VALUE_TYPE_COMMON_BIT_COUNT);
}
else if(!CONFIG_FLAG(Verbose))
b &= ~(Bits::IntCanBeUntagged | Bits::IntIsLikelyUntagged); // these will be simplified
size_t length = 0;
bool addUnderscore = false;
size_t nameIndexOffset = 0;
do
{
const char *name;
switch(b & -b) // bit to be printed
{
case Bits::Object:
if(IsLikelyNativeArray())
{
Assert(GetObjectType() == ObjectType::Array || GetObjectType() == ObjectType::ObjectWithArray);
Assert(HasIntElements() || HasFloatElements());
name =
GetObjectType() == ObjectType::Array
? HasIntElements() ? "NativeIntArray" : "NativeFloatArray"
: HasIntElements() ? "ObjectWithNativeIntArray" : "ObjectWithNativeFloatArray";
break;
}
name = ObjectTypeNames[static_cast<TSize>(GetObjectType())]; // print the object type instead
break;
case Bits::Int:
if(!CONFIG_FLAG(Verbose) && !OneOn(Bits::Object))
{
if(AnyOnExcept(Bits::Likely | Bits::IntCanBeUntagged | Bits::CanBeTaggedValue))
{
name = "TaggedInt";
break;
}
if(OneOn(Bits::IntIsLikelyUntagged))
{
name = "IntAndLikelyUntagged";
break;
}
}
// fall through
default:
size_t nameIndex = nameIndexOffset + GetLowestBitIndex(b);
Assert(nameIndex < sizeof(BitNames) / sizeof(BitNames[0]));
__analysis_assume(nameIndex < sizeof(BitNames) / sizeof(BitNames[0])); // function is not used in shipping builds, satisfy oacr
name = BitNames[nameIndex];
break;
}
size_t nameLength = strlen(name);
if(addUnderscore)
++nameLength;
if(length + nameLength >= VALUE_TYPE_MAX_STRING_SIZE)
break;
if(addUnderscore)
{
str[length++] = '_';
--nameLength;
}
else
addUnderscore = !(b & Bits::Likely);
js_memcpy_s(&str[length], VALUE_TYPE_MAX_STRING_SIZE - 1 - length, name, nameLength);
length += nameLength;
if((b & -b) == BitPattern(1, VALUE_TYPE_OBJECT_BIT_INDEX)) // if the bit that was just printed is the last common bit
nameIndexOffset += VALUE_TYPE_NONOBJECT_BIT_COUNT; // skip bit names for bits that only apply when the Object bit is set
b &= b - 1; // unset the least significant set bit
} while(!!b);
Assert(length < VALUE_TYPE_MAX_STRING_SIZE);
str[length] = '\0';
}
void ValueType::ToString(wchar (&str)[VALUE_TYPE_MAX_STRING_SIZE]) const
{
char charStr[VALUE_TYPE_MAX_STRING_SIZE];
ToString(charStr);
for(int i = 0; i < VALUE_TYPE_MAX_STRING_SIZE; ++i)
{
str[i] = charStr[i];
if(!charStr[i])
break;
}
}
void ValueType::ToString(char (&str)[VALUE_TYPE_MAX_STRING_SIZE]) const
{
if(IsUninitialized() || CONFIG_FLAG(Verbose))
{
ToVerboseString(str);
return;
}
bool canBeTaggedValue = CanBeTaggedValue();
const ValueType definiteType = ToDefinite();
ValueType generalizedType;
if(definiteType.IsInt())
generalizedType = definiteType;
else if(definiteType.IsFloat())
generalizedType = Float;
else if(definiteType.IsNumber())
{
generalizedType = Number;
if(definiteType.IsLikelyInt())
generalizedType = generalizedType.Merge(GetInt(definiteType.IsLikelyTaggedInt()));
}
else if(definiteType.IsUndefined())
generalizedType = Undefined;
else if(definiteType.IsNull())
generalizedType = Null;
else if(definiteType.IsBoolean())
generalizedType = Boolean;
else if (definiteType.IsString())
generalizedType = String;
else if (definiteType.IsSymbol())
generalizedType = Symbol;
else if(definiteType.IsPrimitive() && !IsLikelyObject())
{
strcpy_s(str, IsDefinite() ? "Primitive" : "LikelyPrimitive");
return;
}
else if(definiteType.IsLikelyObject())
{
generalizedType = definiteType.ToDefiniteObject();
if(!definiteType.IsObject())
generalizedType = generalizedType.ToLikely();
}
else
{
strcpy_s(str, IsDefinite() ? "Mixed" : "LikelyMixed");
return;
}
if(!IsDefinite())
generalizedType = generalizedType.ToLikely();
generalizedType.SetCanBeTaggedValue(canBeTaggedValue).ToVerboseString(str);
}
#if ENABLE_DEBUG_CONFIG_OPTIONS
void ValueType::ToStringDebug(__out_ecount(strSize) char *const str, const size_t strSize) const
{
Assert(str);
if(strSize == 0)
return;
char generalizedStr[VALUE_TYPE_MAX_STRING_SIZE];
ToString(generalizedStr);
char verboseStr[VALUE_TYPE_MAX_STRING_SIZE];
ToVerboseString(verboseStr);
const size_t generalizedStrLength = strlen(generalizedStr);
if(strcmp(generalizedStr, verboseStr) == 0)
{
if(generalizedStrLength >= strSize)
{
str[0] = '\0';
return;
}
strcpy_s(str, strSize, generalizedStr);
return;
}
const size_t verboseStrLength = strlen(verboseStr);
if(generalizedStrLength + verboseStrLength + 3 >= strSize)
{
str[0] = '\0';
return;
}
sprintf_s(str, strSize, "%s (%s)", generalizedStr, verboseStr);
}
#endif
bool ValueType::FromString(const wchar *const str, ValueType *valueType)
{
Assert(str);
Assert(valueType);
char charStr[VALUE_TYPE_MAX_STRING_SIZE];
int i = 0;
for(; i < VALUE_TYPE_MAX_STRING_SIZE - 1 && str[i]; ++i)
{
Assert(static_cast<wchar>(static_cast<char>(str[i])) == str[i]);
charStr[i] = static_cast<char>(str[i]);
}
charStr[i] = '\0';
return FromString(charStr, valueType);
}
bool ValueType::FromString(const char *const str, ValueType *valueType)
{
Assert(str);
Assert(valueType);
bool found = false;
MapInitialValueTypesUntil([&](const ValueType initialValueType, const size_t) -> bool
{
char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE];
initialValueType.ToString(valueTypeStr);
if(strcmp(str, valueTypeStr))
return false;
*valueType = initialValueType;
found = true;
return true;
});
return found;
}
ValueType::TSize ValueType::GetRawData() const
{
return static_cast<TSize>(bits);
}
ValueType ValueType::FromRawData(const TSize rawData)
{
return Verify(static_cast<Bits>(rawData));
}
// Virtual and Mixed Typed Array Methods
bool ValueType::IsVirtualTypedArrayPair(const ObjectType other) const
{
return (VirtualTypedArrayPair[(int)GetObjectType()] == other);
}
bool ValueType::IsLikelyMixedTypedArrayType() const
{
return (IsLikelyObject() && GetObjectType() >= ObjectType::Int8MixedArray && GetObjectType() <= ObjectType::Float64MixedArray);
}
bool ValueType::IsMixedTypedArrayPair(const ValueType other) const
{
return ( IsLikelyObject() && other.IsLikelyObject() &&
( (MixedTypedArrayPair[(int)GetObjectType()] == other.GetObjectType()) ||
(MixedTypedArrayPair[(int)other.GetObjectType()] == GetObjectType()) ||
(IsLikelyMixedTypedArrayType() && other.IsLikelyMixedTypedArrayType())
)
);
}
ValueType ValueType::ChangeToMixedTypedArrayType() const
{
ObjectType objType = MixedTypedArrayPair[(int)GetObjectType()];
Assert(objType);
ValueType valueType(bits);
valueType.SetObjectType(objType);
return Verify(valueType);
}
ObjectType ValueType::GetMixedTypedArrayObjectType() const
{
return MixedTypedArrayPair[(int)GetObjectType()];
}
ObjectType ValueType::GetMixedToVirtualTypedArrayObjectType() const
{
return MixedTypedToVirtualTypedArray[(int)GetObjectType()];
}
#if DBG
void ValueType::RunUnitTests()
{
Assert(Uninitialized.bits == (Bits::Likely | Bits::CanBeTaggedValue));
Assert(!ObjectType::UninitializedObject); // this is assumed in Merge
const ValueType TaggedInt(GetTaggedInt());
const ValueType IntAndLikelyTagged(GetInt(true));
const ValueType IntAndLikelyUntagged(GetInt(false));
Assert(TaggedInt.IsTaggedInt());
Assert(TaggedInt.IsIntAndLikelyTagged());
Assert(TaggedInt.IsLikelyTaggedInt());
Assert(!TaggedInt.IsLikelyUntaggedInt());
Assert(TaggedInt.IsInt());
Assert(TaggedInt.IsLikelyInt());
Assert(!TaggedInt.IsLikelyFloat());
Assert(TaggedInt.IsNumber());
Assert(TaggedInt.IsLikelyNumber());
Assert(TaggedInt.IsPrimitive());
Assert(TaggedInt.IsLikelyPrimitive());
Assert(!IntAndLikelyTagged.IsTaggedInt());
Assert(IntAndLikelyTagged.IsIntAndLikelyTagged());
Assert(IntAndLikelyTagged.IsLikelyTaggedInt());
Assert(!IntAndLikelyTagged.IsLikelyUntaggedInt());
Assert(IntAndLikelyTagged.IsInt());
Assert(IntAndLikelyTagged.IsLikelyInt());
Assert(!IntAndLikelyTagged.IsLikelyFloat());
Assert(IntAndLikelyTagged.IsNumber());
Assert(IntAndLikelyTagged.IsLikelyNumber());
Assert(IntAndLikelyTagged.IsPrimitive());
Assert(IntAndLikelyTagged.IsLikelyPrimitive());
Assert(GetNumberAndLikelyInt(true).IsLikelyTaggedInt());
Assert(!GetNumberAndLikelyInt(true).IsLikelyUntaggedInt());
Assert(!GetNumberAndLikelyInt(true).IsInt());
Assert(GetNumberAndLikelyInt(true).IsLikelyInt());
Assert(!GetNumberAndLikelyInt(true).IsLikelyFloat());
Assert(GetNumberAndLikelyInt(true).IsNumber());
Assert(GetNumberAndLikelyInt(true).IsLikelyNumber());
Assert(GetNumberAndLikelyInt(true).IsPrimitive());
Assert(GetNumberAndLikelyInt(true).IsLikelyPrimitive());
Assert(TaggedInt.ToLikely().IsLikelyTaggedInt());
Assert(!TaggedInt.ToLikely().IsLikelyUntaggedInt());
Assert(TaggedInt.ToLikely().IsLikelyInt());
Assert(!TaggedInt.ToLikely().IsLikelyFloat());
Assert(TaggedInt.ToLikely().IsLikelyNumber());
Assert(!TaggedInt.ToLikely().IsPrimitive());
Assert(TaggedInt.ToLikely().IsLikelyPrimitive());
Assert(IntAndLikelyTagged.ToLikely().IsLikelyTaggedInt());
Assert(!IntAndLikelyTagged.ToLikely().IsLikelyUntaggedInt());
Assert(IntAndLikelyTagged.ToLikely().IsLikelyInt());
Assert(!IntAndLikelyTagged.ToLikely().IsLikelyFloat());
Assert(IntAndLikelyTagged.ToLikely().IsLikelyNumber());
Assert(!IntAndLikelyTagged.ToLikely().IsPrimitive());
Assert(IntAndLikelyTagged.ToLikely().IsLikelyPrimitive());
Assert(!IntAndLikelyUntagged.IsLikelyTaggedInt());
Assert(IntAndLikelyUntagged.IsIntAndLikelyUntagged());
Assert(IntAndLikelyUntagged.IsLikelyUntaggedInt());
Assert(IntAndLikelyUntagged.IsInt());
Assert(IntAndLikelyUntagged.IsLikelyInt());
Assert(!IntAndLikelyUntagged.IsLikelyFloat());
Assert(IntAndLikelyUntagged.IsNumber());
Assert(IntAndLikelyUntagged.IsLikelyNumber());
Assert(IntAndLikelyUntagged.IsPrimitive());
Assert(IntAndLikelyUntagged.IsLikelyPrimitive());
Assert(!GetNumberAndLikelyInt(false).IsLikelyTaggedInt());
Assert(!GetNumberAndLikelyInt(false).IsIntAndLikelyUntagged());
Assert(GetNumberAndLikelyInt(false).IsLikelyUntaggedInt());
Assert(!GetNumberAndLikelyInt(false).IsInt());
Assert(GetNumberAndLikelyInt(false).IsLikelyInt());
Assert(!GetNumberAndLikelyInt(false).IsLikelyFloat());
Assert(GetNumberAndLikelyInt(false).IsNumber());
Assert(GetNumberAndLikelyInt(false).IsLikelyNumber());
Assert(GetNumberAndLikelyInt(false).IsPrimitive());
Assert(GetNumberAndLikelyInt(false).IsLikelyPrimitive());
Assert(!IntAndLikelyUntagged.ToLikely().IsLikelyTaggedInt());
Assert(IntAndLikelyUntagged.ToLikely().IsLikelyUntaggedInt());
Assert(IntAndLikelyUntagged.ToLikely().IsLikelyInt());
Assert(!IntAndLikelyUntagged.ToLikely().IsLikelyFloat());
Assert(IntAndLikelyUntagged.ToLikely().IsLikelyNumber());
Assert(!IntAndLikelyUntagged.ToLikely().IsPrimitive());
Assert(IntAndLikelyUntagged.ToLikely().IsLikelyPrimitive());
Assert(!Float.IsLikelyInt());
Assert(Float.IsFloat());
Assert(Float.IsLikelyFloat());
Assert(Float.IsNumber());
Assert(Float.IsLikelyNumber());
Assert(Float.IsPrimitive());
Assert(Float.IsLikelyPrimitive());
Assert(!Float.ToLikely().IsLikelyInt());
Assert(Float.ToLikely().IsLikelyFloat());
Assert(Float.ToLikely().IsLikelyNumber());
Assert(!Float.ToLikely().IsPrimitive());
Assert(!Float.ToLikely().IsPrimitive());
Assert(Float.ToLikely().IsLikelyPrimitive());
Assert(!Number.IsLikelyInt());
Assert(!Number.IsLikelyFloat());
Assert(Number.IsNumber());
Assert(Number.IsUnknownNumber());
Assert(Number.IsLikelyNumber());
Assert(Number.IsPrimitive());
Assert(Number.IsLikelyPrimitive());
Assert(!Number.ToLikely().IsLikelyInt());
Assert(!Number.ToLikely().IsLikelyFloat());
Assert(Number.ToLikely().IsLikelyNumber());
Assert(Number.ToLikely().IsLikelyUnknownNumber());
Assert(!Number.ToLikely().IsPrimitive());
Assert(Number.ToLikely().IsLikelyPrimitive());
Assert(!UninitializedObject.IsLikelyPrimitive());
Assert(UninitializedObject.IsObject());
Assert(UninitializedObject.IsLikelyObject());
Assert(!UninitializedObject.ToLikely().IsLikelyPrimitive());
Assert(!UninitializedObject.ToLikely().IsObject());
Assert(UninitializedObject.ToLikely().IsLikelyObject());
Assert(Undefined.IsNotInt());
Assert(!Undefined.ToLikely().IsNotInt());
Assert(Null.IsNotInt());
Assert(!Null.ToLikely().IsNotInt());
Assert(Boolean.IsNotInt());
Assert(!Boolean.ToLikely().IsNotInt());
Assert(String.IsNotInt());
Assert(!String.ToLikely().IsNotInt());
Assert(UninitializedObject.IsNotInt());
Assert(!UninitializedObject.ToLikely().IsNotInt());
{
const ValueType m(IntAndLikelyUntagged.Merge(Null));
Assert(m.IsPrimitive());
Assert(m.IsLikelyPrimitive());
Assert(IntAndLikelyUntagged.IsSubsetOf(m, true, true, true, true));
Assert(!m.IsSubsetOf(IntAndLikelyUntagged, true, true, true, true));
}
{
const ValueType m(IntAndLikelyUntagged.Merge(UninitializedObject));
Assert(m.HasBeenInt());
Assert(!m.IsLikelyPrimitive());
Assert(m.HasBeenObject());
Assert(!m.IsLikelyObject());
}
{
const ValueType m(Uninitialized.Merge(IntAndLikelyTagged));
Assert(!m.IsPrimitive());
Assert(!m.IsDefinite());
Assert(m.IsLikelyTaggedInt());
}
{
const ValueType m(UninitializedObject.Merge(Null));
Assert(UninitializedObject.IsSubsetOf(m, true, true, true, true));
Assert(!m.IsSubsetOf(UninitializedObject, true, true, true, true));
Assert(Null.IsSubsetOf(m, true, true, true, true));
Assert(!m.IsSubsetOf(Null, true, true, true, true));
Assert(!TaggedInt.IsSubsetOf(m, true, true, true, true));
Assert(!m.IsSubsetOf(TaggedInt, true, true, true, true));
const ValueType po = m.Merge(TaggedInt);
Assert(m.IsSubsetOf(po, true, true, true, true));
Assert(!po.IsSubsetOf(m, true, true, true, true));
}
MapInitialValueTypesUntil([](const ValueType valueType0, const size_t i) -> bool
{
MapInitialValueTypesUntil([=](const ValueType t1, const size_t j) -> bool
{
if(j < i)
return false;
const ValueType t0(valueType0);
const ValueType m(t0.Merge(t1));
Assert(m.bits == t1.Merge(t0).bits);
Assert(m.IsUninitialized() == (t0.IsUninitialized() && t1.IsUninitialized()));
const bool isSubsetWithTypeSpecEnabled = t0.IsSubsetOf(t1, true, true, true, true);
if(t0.IsUninitialized())
{
Assert(isSubsetWithTypeSpecEnabled == t1.IsUninitialized());
return false;
}
else if(t1.IsUninitialized())
{
Assert(isSubsetWithTypeSpecEnabled);
return false;
}
Assert(m.IsIntAndLikelyTagged() == (t0.IsIntAndLikelyTagged() && t1.IsIntAndLikelyTagged()));
Assert(
m.IsLikelyTaggedInt() ==
(
t0.IsLikelyNumber() && t1.IsLikelyNumber() && // both are likely number
!t0.IsLikelyFloat() && !t1.IsLikelyFloat() && // neither is likely float
!t0.IsLikelyUntaggedInt() && !t1.IsLikelyUntaggedInt() && // neither is likely untagged int
(t0.IsLikelyTaggedInt() || t1.IsLikelyTaggedInt()) // one is likely tagged int
));
Assert(m.IsInt() == (t0.IsInt() && t1.IsInt()));
Assert(
m.IsLikelyInt() ==
(
t0.IsLikelyNumber() && t1.IsLikelyNumber() && // both are likely number
!t0.IsLikelyFloat() && !t1.IsLikelyFloat() && // neither is likely float
(t0.IsLikelyInt() || t1.IsLikelyInt()) // one is likely int
));
if(!(
t0.IsObject() && t1.IsObject() && // both are objects
(
(
(
t0.GetObjectType() == ObjectType::UninitializedObject ||
t1.GetObjectType() == ObjectType::UninitializedObject
) && // one has an uninitialized object type
(t0.GetObjectType() > ObjectType::Object || t1.GetObjectType() > ObjectType::Object) // one has a specific object type
) ||
(t0.IsArrayOrObjectWithArray() || t1.IsArrayOrObjectWithArray()) // or one was an array or an object with array
)
)) // then the resulting object type is not guaranteed
{
Assert(m.IsNotInt() == (t0.IsNotInt() && t1.IsNotInt()));
}
Assert(m.IsFloat() == (t0.IsNumber() && t1.IsNumber() && (t0.IsFloat() || t1.IsFloat())));
Assert(
m.IsLikelyFloat() ==
(
(t0.IsLikelyFloat() || t1.IsLikelyFloat()) && // one is likely float
(t0.IsLikelyUndefined() || t0.IsLikelyNumber()) &&
(t1.IsLikelyUndefined() || t1.IsLikelyNumber()) // both are likely undefined or number
));
Assert(m.IsNumber() == (t0.IsNumber() && t1.IsNumber()));
Assert(
m.IsLikelyNumber() ==
(
(t0.IsLikelyNumber() || t1.IsLikelyNumber()) && // one is likely number
(t0.IsLikelyUndefined() || t0.IsLikelyNumber()) &&
(t1.IsLikelyUndefined() || t1.IsLikelyNumber()) // both are likely undefined or number
));
Assert(m.IsUnknownNumber() == (m.IsNumber() && !m.IsLikelyInt() && !m.IsLikelyFloat()));
Assert(!m.IsLikelyUnknownNumber() || m.IsLikelyNumber() && !m.IsLikelyInt() && !m.IsLikelyFloat());
Assert(m.IsUndefined() == (t0.IsUndefined() && t1.IsUndefined()));
Assert(m.IsLikelyUndefined() == (t0.IsLikelyUndefined() && t1.IsLikelyUndefined()));
Assert(m.IsNull() == (t0.IsNull() && t1.IsNull()));
Assert(m.IsLikelyNull() == (t0.IsLikelyNull() && t1.IsLikelyNull()));
Assert(m.IsBoolean() == (t0.IsBoolean() && t1.IsBoolean()));
Assert(m.IsLikelyBoolean() == (t0.IsLikelyBoolean() && t1.IsLikelyBoolean()));
Assert(m.IsString() == (t0.IsString() && t1.IsString()));
Assert(m.IsLikelyString() == (t0.IsLikelyString() && t1.IsLikelyString()));
if(!(
t0.IsObject() && t1.IsObject() && // both are objects
(
(
(
t0.GetObjectType() == ObjectType::UninitializedObject ||
t1.GetObjectType() == ObjectType::UninitializedObject
) && // one has an uninitialized object type
(t0.GetObjectType() > ObjectType::Object || t1.GetObjectType() > ObjectType::Object) // one has a specific object type
) ||
(t0.IsArrayOrObjectWithArray() || t1.IsArrayOrObjectWithArray()) // or one was an array or an object with array
)
)) // then the resulting object type is not guaranteed
{
Assert(m.IsObject() == (t0.IsObject() && t1.IsObject()));
}
Assert(
m.IsLikelyObject() ==
(
(t0.IsLikelyObject() || t1.IsLikelyObject()) && // one is likely object
(t0.IsLikelyUndefined() || t0.IsLikelyNull() || t0.IsLikelyObject()) &&
(t1.IsLikelyUndefined() || t1.IsLikelyNull() || t1.IsLikelyObject()) // both are likely undefined, null, or object
));
if(t1.IsUnknownNumber())
{
Assert(isSubsetWithTypeSpecEnabled == (t0.IsNumber() || t0.IsLikelyInt() || t0.IsLikelyFloat()));
Assert(t0.IsSubsetOf(t1, false, true, true, true) == (t0.IsNumber() || t0.IsLikelyFloat()));
Assert(t0.IsSubsetOf(t1, true, false, true, true) == (t0.IsNumber() || t0.IsLikelyInt()));
}
else if(t0.IsLikelyInt() && t1.IsLikelyInt())
{
Assert(
isSubsetWithTypeSpecEnabled ==
(
(t0.IsDefinite() || !t1.IsDefinite()) &&
(
t0.IsTaggedInt() ||
t0.IsLikelyTaggedInt() && !t1.IsTaggedInt() ||
!t1.IsLikelyTaggedInt()
)
));
}
else if(t0.IsLikelyFloat() && t1.IsLikelyFloat())
{
Assert(isSubsetWithTypeSpecEnabled == (t0.IsDefinite() || !t1.IsDefinite()));
}
else if(t0.IsLikelyNumber() && t1.IsLikelyNumber())
{
Assert(
isSubsetWithTypeSpecEnabled ==
(
(t0.IsDefinite() || !t1.IsDefinite()) &&
(
t0.IsLikelyInt() && !t1.IsLikelyFloat() ||
t0.IsLikelyFloat() && !t1.IsLikelyInt() ||
t1.IsLikelyUnknownNumber()
)
));
}
else if(t0.IsLikelyObject() && (t1.IsLikelyUndefined() || t1.IsLikelyNull()))
{
Assert(isSubsetWithTypeSpecEnabled);
}
else if(t0.IsLikelyObject() && t1.IsLikelyObject())
{
if(t1.GetObjectType() == ObjectType::UninitializedObject &&
t0.GetObjectType() != ObjectType::UninitializedObject)
{
Assert(isSubsetWithTypeSpecEnabled);
}
else if((!t0.IsDefinite() && t1.IsDefinite()) || t0.GetObjectType() != t1.GetObjectType())
{
Assert(!isSubsetWithTypeSpecEnabled);
}
else if(
(t0.IsDefinite() && !t1.IsDefinite()) ||
(t0.GetObjectType() != ObjectType::ObjectWithArray && t0.GetObjectType() != ObjectType::Array))
{
Assert(isSubsetWithTypeSpecEnabled);
}
else
{
Assert(
isSubsetWithTypeSpecEnabled ==
(
(t0.HasNoMissingValues() || !t1.HasNoMissingValues()) &&
(
(!t0.HasNonInts() || t1.HasNonInts()) && (!t0.HasNonFloats() || t1.HasNonFloats())
)
));
Assert(
t0.IsSubsetOf(t1, true, true, false, true) ==
(
(!t0.HasNonInts() || t1.HasNonInts()) && (!t0.HasNonFloats() || t1.HasNonFloats())
));
Assert(t0.IsSubsetOf(t1, true, true, false, false));
}
}
else
{
Assert(
isSubsetWithTypeSpecEnabled ==
(
(t0.IsDefinite() || !t1.IsDefinite()) &&
!t0.IsLikelyObject() && !t1.IsLikelyObject() &&
t1.AllOn(t0.bits)
));
}
return false;
});
return false;
});
}
#endif
void ValueType::InstantiateForceInlinedMembers()
{
// Force-inlined functions defined in a translation unit need a reference from an extern non-force-inlined function in the
// same translation unit to force an instantiation of the force-inlined function. Otherwise, if the force-inlined function
// is not referenced in the same translation unit, it will not be generated and the linker is not able to find the
// definition to inline the function in other translation units.
AnalysisAssert(false);
const Js::Var var = nullptr;
ValueType *const t = nullptr;
t->Merge(*t);
t->Merge(var);
}
bool ValueTypeComparer::Equals(const ValueType t0, const ValueType t1)
{
return t0 == t1;
}
uint ValueTypeComparer::GetHashCode(const ValueType t)
{
return t.GetHashCode();
}