// Copyright 2013 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "src/compiler/types.h" #include "src/heap/factory-inl.h" #include "src/heap/heap.h" #include "src/isolate.h" #include "src/objects.h" #include "test/cctest/cctest.h" #include "test/cctest/types-fuzz.h" namespace v8 { namespace internal { namespace compiler { namespace { // Testing auxiliaries (breaking the Type abstraction). static bool IsInteger(double x) { return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities. } typedef uint32_t bitset; struct Tests { typedef Types::TypeVector::iterator TypeIterator; typedef Types::ValueVector::iterator ValueIterator; Isolate* isolate; HandleScope scope; CanonicalHandleScope canonical; Zone zone; Types T; Tests() : isolate(CcTest::InitIsolateOnce()), scope(isolate), canonical(isolate), zone(isolate->allocator(), ZONE_NAME), T(&zone, isolate, isolate->random_number_generator()) {} bool IsBitset(Type type) { return type.IsBitset(); } bool IsUnion(Type type) { return type.IsUnionForTesting(); } BitsetType::bitset AsBitset(Type type) { return type.AsBitsetForTesting(); } const UnionType* AsUnion(Type type) { return type.AsUnionForTesting(); } bool Equal(Type type1, Type type2) { return type1.Equals(type2) && this->IsBitset(type1) == this->IsBitset(type2) && this->IsUnion(type1) == this->IsUnion(type2) && type1.NumConstants() == type2.NumConstants() && (!this->IsBitset(type1) || this->AsBitset(type1) == this->AsBitset(type2)) && (!this->IsUnion(type1) || this->AsUnion(type1)->LengthForTesting() == this->AsUnion(type2)->LengthForTesting()); } void CheckEqual(Type type1, Type type2) { CHECK(Equal(type1, type2)); } void CheckSub(Type type1, Type type2) { CHECK(type1.Is(type2)); CHECK(!type2.Is(type1)); if (this->IsBitset(type1) && this->IsBitset(type2)) { CHECK(this->AsBitset(type1) != this->AsBitset(type2)); } } void CheckSubOrEqual(Type type1, Type type2) { CHECK(type1.Is(type2)); if (this->IsBitset(type1) && this->IsBitset(type2)) { CHECK((this->AsBitset(type1) | this->AsBitset(type2)) == this->AsBitset(type2)); } } void CheckUnordered(Type type1, Type type2) { CHECK(!type1.Is(type2)); CHECK(!type2.Is(type1)); if (this->IsBitset(type1) && this->IsBitset(type2)) { CHECK(this->AsBitset(type1) != this->AsBitset(type2)); } } void CheckOverlap(Type type1, Type type2) { CHECK(type1.Maybe(type2)); CHECK(type2.Maybe(type1)); } void CheckDisjoint(Type type1, Type type2) { CHECK(!type1.Is(type2)); CHECK(!type2.Is(type1)); CHECK(!type1.Maybe(type2)); CHECK(!type2.Maybe(type1)); } void IsSomeType() { for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type t = *it; CHECK_EQ(1, this->IsBitset(t) + t.IsHeapConstant() + t.IsRange() + t.IsOtherNumberConstant() + this->IsUnion(t)); } } void Bitset() { // None and Any are bitsets. CHECK(this->IsBitset(T.None)); CHECK(this->IsBitset(T.Any)); CHECK(bitset(0) == this->AsBitset(T.None)); CHECK(bitset(0xFFFFFFFEu) == this->AsBitset(T.Any)); // Union(T1, T2) is bitset for bitsets T1,T2 for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); CHECK(!(this->IsBitset(type1) && this->IsBitset(type2)) || this->IsBitset(union12)); } } // Intersect(T1, T2) is bitset for bitsets T1,T2 for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type intersect12 = T.Intersect(type1, type2); CHECK(!(this->IsBitset(type1) && this->IsBitset(type2)) || this->IsBitset(intersect12)); } } // Union(T1, T2) is bitset if T2 is bitset and T1.Is(T2) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); CHECK(!(this->IsBitset(type2) && type1.Is(type2)) || this->IsBitset(union12)); } } // Union(T1, T2) is bitwise disjunction for bitsets T1,T2 for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); if (this->IsBitset(type1) && this->IsBitset(type2)) { CHECK( (this->AsBitset(type1) | this->AsBitset(type2)) == this->AsBitset(union12)); } } } // Intersect(T1, T2) is bitwise conjunction for bitsets T1,T2 (modulo None) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; if (this->IsBitset(type1) && this->IsBitset(type2)) { Type intersect12 = T.Intersect(type1, type2); bitset bits = this->AsBitset(type1) & this->AsBitset(type2); CHECK(bits == this->AsBitset(intersect12)); } } } } void Constant() { // Constructor for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) { Handle value = *vt; Type type = T.NewConstant(value); CHECK(type.IsHeapConstant() || type.IsOtherNumberConstant() || type.IsRange()); } // Value attribute for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) { Handle value = *vt; Type type = T.NewConstant(value); if (type.IsHeapConstant()) { CHECK(value.address() == type.AsHeapConstant()->Value().address()); } else if (type.IsOtherNumberConstant()) { CHECK(value->IsHeapNumber()); CHECK(value->Number() == type.AsOtherNumberConstant()->Value()); } else { CHECK(type.IsRange()); double v = value->Number(); CHECK(v == type.AsRange()->Min() && v == type.AsRange()->Max()); } } // Functionality & Injectivity: Constant(V1) = Constant(V2) iff V1 = V2 for (ValueIterator vt1 = T.values.begin(); vt1 != T.values.end(); ++vt1) { for (ValueIterator vt2 = T.values.begin(); vt2 != T.values.end(); ++vt2) { Handle value1 = *vt1; Handle value2 = *vt2; Type type1 = T.NewConstant(value1); Type type2 = T.NewConstant(value2); if (type1.IsOtherNumberConstant() && type2.IsOtherNumberConstant()) { CHECK(Equal(type1, type2) == (type1.AsOtherNumberConstant()->Value() == type2.AsOtherNumberConstant()->Value())); } else if (type1.IsRange() && type2.IsRange()) { CHECK(Equal(type1, type2) == ((type1.AsRange()->Min() == type2.AsRange()->Min()) && (type1.AsRange()->Max() == type2.AsRange()->Max()))); } else { CHECK(Equal(type1, type2) == (*value1 == *value2)); } } } // Typing of numbers Factory* fac = isolate->factory(); CHECK(T.NewConstant(fac->NewNumber(0)).Is(T.UnsignedSmall)); CHECK(T.NewConstant(fac->NewNumber(1)).Is(T.UnsignedSmall)); CHECK(T.NewConstant(fac->NewNumber(0x3FFFFFFF)).Is(T.UnsignedSmall)); CHECK(T.NewConstant(fac->NewNumber(-1)).Is(T.Negative31)); CHECK(T.NewConstant(fac->NewNumber(-0x3FFFFFFF)).Is(T.Negative31)); CHECK(T.NewConstant(fac->NewNumber(-0x40000000)).Is(T.Negative31)); CHECK(T.NewConstant(fac->NewNumber(0x40000000)).Is(T.Unsigned31)); CHECK(!T.NewConstant(fac->NewNumber(0x40000000)).Is(T.Unsigned30)); CHECK(T.NewConstant(fac->NewNumber(0x7FFFFFFF)).Is(T.Unsigned31)); CHECK(!T.NewConstant(fac->NewNumber(0x7FFFFFFF)).Is(T.Unsigned30)); CHECK(T.NewConstant(fac->NewNumber(-0x40000001)).Is(T.Negative32)); CHECK(!T.NewConstant(fac->NewNumber(-0x40000001)).Is(T.Negative31)); CHECK(T.NewConstant(fac->NewNumber(-0x7FFFFFFF)).Is(T.Negative32)); CHECK(!T.NewConstant(fac->NewNumber(-0x7FFFFFFF - 1)).Is(T.Negative31)); if (SmiValuesAre31Bits()) { CHECK(!T.NewConstant(fac->NewNumber(0x40000000)).Is(T.UnsignedSmall)); CHECK(!T.NewConstant(fac->NewNumber(0x7FFFFFFF)).Is(T.UnsignedSmall)); CHECK(!T.NewConstant(fac->NewNumber(-0x40000001)).Is(T.SignedSmall)); CHECK(!T.NewConstant(fac->NewNumber(-0x7FFFFFFF - 1)).Is(T.SignedSmall)); } else { CHECK(SmiValuesAre32Bits()); CHECK(T.NewConstant(fac->NewNumber(0x40000000)).Is(T.UnsignedSmall)); CHECK(T.NewConstant(fac->NewNumber(0x7FFFFFFF)).Is(T.UnsignedSmall)); CHECK(T.NewConstant(fac->NewNumber(-0x40000001)).Is(T.SignedSmall)); CHECK(T.NewConstant(fac->NewNumber(-0x7FFFFFFF - 1)).Is(T.SignedSmall)); } CHECK(T.NewConstant(fac->NewNumber(0x80000000u)).Is(T.Unsigned32)); CHECK(!T.NewConstant(fac->NewNumber(0x80000000u)).Is(T.Unsigned31)); CHECK(T.NewConstant(fac->NewNumber(0xFFFFFFFFu)).Is(T.Unsigned32)); CHECK(!T.NewConstant(fac->NewNumber(0xFFFFFFFFu)).Is(T.Unsigned31)); CHECK(T.NewConstant(fac->NewNumber(0xFFFFFFFFu + 1.0)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(0xFFFFFFFFu + 1.0)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(-0x7FFFFFFF - 2.0)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(-0x7FFFFFFF - 2.0)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(0.1)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(0.1)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(-10.1)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(-10.1)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(10e60)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(10e60)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(-1.0 * 0.0)).Is(T.MinusZero)); CHECK( T.NewConstant(fac->NewNumber(std::numeric_limits::quiet_NaN())) .Is(T.NaN)); CHECK(T.NewConstant(fac->NewNumber(V8_INFINITY)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(V8_INFINITY)).Is(T.Integral32)); CHECK(T.NewConstant(fac->NewNumber(-V8_INFINITY)).Is(T.PlainNumber)); CHECK(!T.NewConstant(fac->NewNumber(-V8_INFINITY)).Is(T.Integral32)); // Typing of Strings Handle s1 = fac->NewStringFromAsciiChecked("a"); CHECK(T.NewConstant(s1).Is(T.InternalizedString)); const uc16 two_byte[1] = {0x2603}; Handle s2 = fac->NewTwoByteInternalizedString(Vector(two_byte, 1), 1); CHECK(T.NewConstant(s2).Is(T.InternalizedString)); } void Range() { // Constructor for (ValueIterator i = T.integers.begin(); i != T.integers.end(); ++i) { for (ValueIterator j = T.integers.begin(); j != T.integers.end(); ++j) { double min = (*i)->Number(); double max = (*j)->Number(); if (min > max) std::swap(min, max); Type type = T.Range(min, max); CHECK(type.IsRange()); } } // Range attributes for (ValueIterator i = T.integers.begin(); i != T.integers.end(); ++i) { for (ValueIterator j = T.integers.begin(); j != T.integers.end(); ++j) { double min = (*i)->Number(); double max = (*j)->Number(); if (min > max) std::swap(min, max); Type type = T.Range(min, max); CHECK(min == type.AsRange()->Min()); CHECK(max == type.AsRange()->Max()); } } // Functionality & Injectivity: // Range(min1, max1) = Range(min2, max2) <=> min1 = min2 /\ max1 = max2 for (ValueIterator i1 = T.integers.begin(); i1 != T.integers.end(); ++i1) { for (ValueIterator j1 = i1; j1 != T.integers.end(); ++j1) { for (ValueIterator i2 = T.integers.begin(); i2 != T.integers.end(); ++i2) { for (ValueIterator j2 = i2; j2 != T.integers.end(); ++j2) { double min1 = (*i1)->Number(); double max1 = (*j1)->Number(); double min2 = (*i2)->Number(); double max2 = (*j2)->Number(); if (min1 > max1) std::swap(min1, max1); if (min2 > max2) std::swap(min2, max2); Type type1 = T.Range(min1, max1); Type type2 = T.Range(min2, max2); CHECK(Equal(type1, type2) == (min1 == min2 && max1 == max2)); } } } } } void MinMax() { // If b is regular numeric bitset, then Range(b.Min(), b.Max()).Is(b). // TODO(neis): Need to ignore representation for this to be true. /* for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (this->IsBitset(type) && type.Is(T.Number) && !type.Is(T.None) && !type.Is(T.NaN)) { Type range = T.Range( isolate->factory()->NewNumber(type.Min()), isolate->factory()->NewNumber(type.Max())); CHECK(range.Is(type)); } } */ // If b is regular numeric bitset, then b.Min() and b.Max() are integers. for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (this->IsBitset(type) && type.Is(T.Number) && !type.Is(T.NaN)) { CHECK(IsInteger(type.Min()) && IsInteger(type.Max())); } } // If b1 and b2 are regular numeric bitsets with b1.Is(b2), then // b1.Min() >= b2.Min() and b1.Max() <= b2.Max(). for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; if (this->IsBitset(type1) && type1.Is(type2) && type2.Is(T.Number) && !type1.Is(T.NaN) && !type2.Is(T.NaN)) { CHECK(type1.Min() >= type2.Min()); CHECK(type1.Max() <= type2.Max()); } } } // Lub(Range(x,y)).Min() <= x and y <= Lub(Range(x,y)).Max() for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (type.IsRange()) { Type lub = type.BitsetLubForTesting(); CHECK(lub.Min() <= type.Min() && type.Max() <= lub.Max()); } } // Rangification: If T.Is(Range(-inf,+inf)) and T is inhabited, then // T.Is(Range(T.Min(), T.Max())). for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(!type.Is(T.Integer) || type.IsNone() || type.Is(T.Range(type.Min(), type.Max()))); } } void BitsetGlb() { // Lower: (T->BitsetGlb()).Is(T) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; Type glb = type.BitsetGlbForTesting(); CHECK(glb.Is(type)); } // Greatest: If T1.IsBitset() and T1.Is(T2), then T1.Is(T2->BitsetGlb()) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type glb2 = type2.BitsetGlbForTesting(); CHECK(!this->IsBitset(type1) || !type1.Is(type2) || type1.Is(glb2)); } } // Monotonicity: T1.Is(T2) implies (T1->BitsetGlb()).Is(T2->BitsetGlb()) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type glb1 = type1.BitsetGlbForTesting(); Type glb2 = type2.BitsetGlbForTesting(); CHECK(!type1.Is(type2) || glb1.Is(glb2)); } } } void BitsetLub() { // Upper: T.Is(T->BitsetLub()) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; Type lub = type.BitsetLubForTesting(); CHECK(type.Is(lub)); } // Least: If T2.IsBitset() and T1.Is(T2), then (T1->BitsetLub()).Is(T2) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type lub1 = type1.BitsetLubForTesting(); CHECK(!this->IsBitset(type2) || !type1.Is(type2) || lub1.Is(type2)); } } // Monotonicity: T1.Is(T2) implies (T1->BitsetLub()).Is(T2->BitsetLub()) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type lub1 = type1.BitsetLubForTesting(); Type lub2 = type2.BitsetLubForTesting(); CHECK(!type1.Is(type2) || lub1.Is(lub2)); } } } void Is1() { // Least Element (Bottom): None.Is(T) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(T.None.Is(type)); } // Greatest Element (Top): T.Is(Any) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(type.Is(T.Any)); } // Bottom Uniqueness: T.Is(None) implies T = None for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (type.Is(T.None)) CheckEqual(type, T.None); } // Top Uniqueness: Any.Is(T) implies T = Any for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (T.Any.Is(type)) CheckEqual(type, T.Any); } // Reflexivity: T.Is(T) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(type.Is(type)); } // Transitivity: T1.Is(T2) and T2.Is(T3) implies T1.Is(T3) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) { Type type1 = *it1; Type type2 = *it2; Type type3 = *it3; CHECK(!(type1.Is(type2) && type2.Is(type3)) || type1.Is(type3)); } } } // Antisymmetry: T1.Is(T2) and T2.Is(T1) iff T1 = T2 for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; CHECK((type1.Is(type2) && type2.Is(type1)) == Equal(type1, type2)); } } // (In-)Compatibilities. for (TypeIterator i = T.types.begin(); i != T.types.end(); ++i) { for (TypeIterator j = T.types.begin(); j != T.types.end(); ++j) { Type type1 = *i; Type type2 = *j; CHECK( !type1.Is(type2) || this->IsBitset(type2) || this->IsUnion(type2) || this->IsUnion(type1) || (type1.IsHeapConstant() && type2.IsHeapConstant()) || (this->IsBitset(type1) && type2.IsRange()) || (type1.IsRange() && type2.IsRange()) || (type1.IsOtherNumberConstant() && type2.IsOtherNumberConstant()) || type1.IsNone()); } } } void Is2() { // Range(X1, Y1).Is(Range(X2, Y2)) iff X1 >= X2 /\ Y1 <= Y2 for (ValueIterator i1 = T.integers.begin(); i1 != T.integers.end(); ++i1) { for (ValueIterator j1 = i1; j1 != T.integers.end(); ++j1) { for (ValueIterator i2 = T.integers.begin(); i2 != T.integers.end(); ++i2) { for (ValueIterator j2 = i2; j2 != T.integers.end(); ++j2) { double min1 = (*i1)->Number(); double max1 = (*j1)->Number(); double min2 = (*i2)->Number(); double max2 = (*j2)->Number(); if (min1 > max1) std::swap(min1, max1); if (min2 > max2) std::swap(min2, max2); Type type1 = T.Range(min1, max1); Type type2 = T.Range(min2, max2); CHECK(type1.Is(type2) == (min1 >= min2 && max1 <= max2)); } } } } // Constant(V1).Is(Constant(V2)) iff V1 = V2 for (ValueIterator vt1 = T.values.begin(); vt1 != T.values.end(); ++vt1) { for (ValueIterator vt2 = T.values.begin(); vt2 != T.values.end(); ++vt2) { Handle value1 = *vt1; Handle value2 = *vt2; Type const_type1 = T.NewConstant(value1); Type const_type2 = T.NewConstant(value2); if (const_type1.IsOtherNumberConstant() && const_type2.IsOtherNumberConstant()) { CHECK(const_type1.Is(const_type2) == (const_type1.AsOtherNumberConstant()->Value() == const_type2.AsOtherNumberConstant()->Value())); } else if (const_type1.IsRange() && const_type2.IsRange()) { CHECK( Equal(const_type1, const_type2) == ((const_type1.AsRange()->Min() == const_type2.AsRange()->Min()) && (const_type1.AsRange()->Max() == const_type2.AsRange()->Max()))); } else { CHECK(const_type1.Is(const_type2) == (*value1 == *value2)); } } } // Range-specific subtyping // Lub(Range(x,y)).Is(T.Union(T.Integral32, T.OtherNumber)) for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; if (type.IsRange()) { Type lub = type.BitsetLubForTesting(); CHECK(lub.Is(T.PlainNumber)); } } // Subtyping between concrete basic types CheckUnordered(T.Boolean, T.Null); CheckUnordered(T.Undefined, T.Null); CheckUnordered(T.Boolean, T.Undefined); CheckSub(T.SignedSmall, T.Number); CheckSub(T.Signed32, T.Number); CheckSubOrEqual(T.SignedSmall, T.Signed32); CheckUnordered(T.SignedSmall, T.MinusZero); CheckUnordered(T.Signed32, T.Unsigned32); CheckSub(T.UniqueName, T.Name); CheckSub(T.String, T.Name); CheckSub(T.InternalizedString, T.String); CheckSub(T.InternalizedString, T.UniqueName); CheckSub(T.InternalizedString, T.Name); CheckSub(T.Symbol, T.UniqueName); CheckSub(T.Symbol, T.Name); CheckUnordered(T.String, T.UniqueName); CheckUnordered(T.String, T.Symbol); CheckUnordered(T.InternalizedString, T.Symbol); CheckSub(T.Object, T.Receiver); CheckSub(T.Proxy, T.Receiver); CheckSub(T.Array, T.Object); CheckSub(T.OtherObject, T.Object); CheckSub(T.OtherUndetectable, T.Object); CheckUnordered(T.Object, T.Proxy); CheckUnordered(T.Array, T.Undetectable); CheckUnordered(T.OtherObject, T.Undetectable); // Subtyping between concrete structural types CheckSub(T.SmiConstant, T.SignedSmall); CheckSub(T.SmiConstant, T.Signed32); CheckSub(T.SmiConstant, T.Number); CheckSub(T.ObjectConstant1, T.Object); CheckSub(T.ObjectConstant2, T.Object); CheckSub(T.ArrayConstant, T.Object); CheckSub(T.ArrayConstant, T.Array); CheckSub(T.ArrayConstant, T.Receiver); CheckSub(T.UninitializedConstant, T.Internal); CheckUnordered(T.ObjectConstant1, T.ObjectConstant2); CheckUnordered(T.ObjectConstant1, T.ArrayConstant); CheckUnordered(T.UninitializedConstant, T.Null); CheckUnordered(T.UninitializedConstant, T.Undefined); } void Maybe() { // T.Maybe(Any) iff T inhabited for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(type.Maybe(T.Any) == !type.IsNone()); } // T.Maybe(None) never for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(!type.Maybe(T.None)); } // Reflexivity upto Inhabitation: T.Maybe(T) iff T inhabited for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; CHECK(type.Maybe(type) == !type.IsNone()); } // Symmetry: T1.Maybe(T2) iff T2.Maybe(T1) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; CHECK(type1.Maybe(type2) == type2.Maybe(type1)); } } // T1.Maybe(T2) implies T1, T2 inhabited for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; CHECK(!type1.Maybe(type2) || (!type1.IsNone() && !type2.IsNone())); } } // T1.Maybe(T2) implies Intersect(T1, T2) inhabited for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type intersect12 = T.Intersect(type1, type2); CHECK(!type1.Maybe(type2) || !intersect12.IsNone()); } } // T1.Is(T2) and T1 inhabited implies T1.Maybe(T2) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; CHECK(!(type1.Is(type2) && !type1.IsNone()) || type1.Maybe(type2)); } } // Constant(V1).Maybe(Constant(V2)) iff V1 = V2 for (ValueIterator vt1 = T.values.begin(); vt1 != T.values.end(); ++vt1) { for (ValueIterator vt2 = T.values.begin(); vt2 != T.values.end(); ++vt2) { Handle value1 = *vt1; Handle value2 = *vt2; Type const_type1 = T.NewConstant(value1); Type const_type2 = T.NewConstant(value2); if (const_type1.IsOtherNumberConstant() && const_type2.IsOtherNumberConstant()) { CHECK(const_type1.Maybe(const_type2) == (const_type1.AsOtherNumberConstant()->Value() == const_type2.AsOtherNumberConstant()->Value())); } else if (const_type1.IsRange() && const_type2.IsRange()) { CHECK( Equal(const_type1, const_type2) == ((const_type1.AsRange()->Min() == const_type2.AsRange()->Min()) && (const_type1.AsRange()->Max() == const_type2.AsRange()->Max()))); } else { CHECK(const_type1.Maybe(const_type2) == (*value1 == *value2)); } } } // Basic types CheckDisjoint(T.Boolean, T.Null); CheckDisjoint(T.Undefined, T.Null); CheckDisjoint(T.Boolean, T.Undefined); CheckOverlap(T.SignedSmall, T.Number); CheckOverlap(T.NaN, T.Number); CheckDisjoint(T.Signed32, T.NaN); CheckOverlap(T.UniqueName, T.Name); CheckOverlap(T.String, T.Name); CheckOverlap(T.InternalizedString, T.String); CheckOverlap(T.InternalizedString, T.UniqueName); CheckOverlap(T.InternalizedString, T.Name); CheckOverlap(T.Symbol, T.UniqueName); CheckOverlap(T.Symbol, T.Name); CheckOverlap(T.String, T.UniqueName); CheckDisjoint(T.String, T.Symbol); CheckDisjoint(T.InternalizedString, T.Symbol); CheckOverlap(T.Object, T.Receiver); CheckOverlap(T.OtherObject, T.Object); CheckOverlap(T.Proxy, T.Receiver); CheckDisjoint(T.Object, T.Proxy); // Structural types CheckOverlap(T.SmiConstant, T.SignedSmall); CheckOverlap(T.SmiConstant, T.Signed32); CheckOverlap(T.SmiConstant, T.Number); CheckOverlap(T.ObjectConstant1, T.Object); CheckOverlap(T.ObjectConstant2, T.Object); CheckOverlap(T.ArrayConstant, T.Object); CheckOverlap(T.ArrayConstant, T.Receiver); CheckOverlap(T.ObjectConstant1, T.ObjectConstant1); CheckDisjoint(T.ObjectConstant1, T.ObjectConstant2); CheckDisjoint(T.ObjectConstant1, T.ArrayConstant); } void Union1() { // Identity: Union(T, None) = T for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; Type union_type = T.Union(type, T.None); CheckEqual(union_type, type); } // Domination: Union(T, Any) = Any for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; Type union_type = T.Union(type, T.Any); CheckEqual(union_type, T.Any); } // Idempotence: Union(T, T) = T for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) { Type type = *it; Type union_type = T.Union(type, type); CheckEqual(union_type, type); } // Commutativity: Union(T1, T2) = Union(T2, T1) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); Type union21 = T.Union(type2, type1); CheckEqual(union12, union21); } } // Associativity: Union(T1, Union(T2, T3)) = Union(Union(T1, T2), T3) // This does NOT hold! For example: // (Unsigned32 \/ Range(0,5)) \/ Range(-5,0) = Unsigned32 \/ Range(-5,0) // Unsigned32 \/ (Range(0,5) \/ Range(-5,0)) = Unsigned32 \/ Range(-5,5) /* for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) { Type type1 = *it1; Type type2 = *it2; Type type3 = *it3; Type union12 = T.Union(type1, type2); Type union23 = T.Union(type2, type3); Type union1_23 = T.Union(type1, union23); Type union12_3 = T.Union(union12, type3); CheckEqual(union1_23, union12_3); } } } */ // Meet: T1.Is(Union(T1, T2)) and T2.Is(Union(T1, T2)) for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); CHECK(type1.Is(union12)); CHECK(type2.Is(union12)); } } // Upper Boundedness: T1.Is(T2) implies Union(T1, T2) = T2 for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) { for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) { Type type1 = *it1; Type type2 = *it2; Type union12 = T.Union(type1, type2); if (type1.Is(type2)) CheckEqual(union12, type2); } } // Monotonicity: T1.Is(T2) implies Union(T1, T3).Is(Union(T2, T3)) // This does NOT hold. For example: // Range(-5,-1) <= Signed32 // Range(-5,-1) \/ Range(1,5) = Range(-5,5) Min()); CHECK(type1.Max() == range->Max()); } } } }; } // namespace TEST(IsSomeType) { Tests().IsSomeType(); } TEST(BitsetType) { Tests().Bitset(); } TEST(ConstantType) { Tests().Constant(); } TEST(RangeType) { Tests().Range(); } TEST(MinMax) { Tests().MinMax(); } TEST(BitsetGlb) { Tests().BitsetGlb(); } TEST(BitsetLub) { Tests().BitsetLub(); } TEST(Is1) { Tests().Is1(); } TEST(Is2) { Tests().Is2(); } TEST(Maybe) { Tests().Maybe(); } TEST(Union1) { Tests().Union1(); } TEST(Union2) { Tests().Union2(); } TEST(Union3) { Tests().Union3(); } TEST(Union4) { Tests().Union4(); } TEST(Intersect) { Tests().Intersect(); } TEST(Distributivity) { Tests().Distributivity(); } TEST(GetRange) { Tests().GetRange(); } } // namespace compiler } // namespace internal } // namespace v8