Commit 3c9ee8f3 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[ast] Remove AstType type system.

R=marja@chromium.org
BUG=v8:6408

Change-Id: Ied0c4d1aba18ec84d5feb02c3522b77759be216e
Reviewed-on: https://chromium-review.googlesource.com/548636Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46226}
parent 71eb30bc
......@@ -1160,9 +1160,6 @@ v8_source_set("v8_base") {
"src/ast/ast-numbering.cc",
"src/ast/ast-numbering.h",
"src/ast/ast-traversal-visitor.h",
"src/ast/ast-type-bounds.h",
"src/ast/ast-types.cc",
"src/ast/ast-types.h",
"src/ast/ast-value-factory.cc",
"src/ast/ast-value-factory.h",
"src/ast/ast.cc",
......
// Copyright 2016 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.
// A container to associate type bounds with AST Expression nodes.
#ifndef V8_AST_AST_TYPE_BOUNDS_H_
#define V8_AST_AST_TYPE_BOUNDS_H_
#include "src/ast/ast-types.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class Expression;
class AstTypeBounds {
public:
explicit AstTypeBounds(Zone* zone) : bounds_map_(zone) {}
~AstTypeBounds() {}
AstBounds get(Expression* expression) const {
ZoneMap<Expression*, AstBounds>::const_iterator i =
bounds_map_.find(expression);
return (i != bounds_map_.end()) ? i->second : AstBounds::Unbounded();
}
void set(Expression* expression, AstBounds bounds) {
bounds_map_[expression] = bounds;
}
private:
ZoneMap<Expression*, AstBounds> bounds_map_;
};
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_TYPE_BOUNDS_H_
// Copyright 2014 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 <iomanip>
#include "src/ast/ast-types.h"
#include "src/handles-inl.h"
#include "src/objects-inl.h"
#include "src/ostreams.h"
namespace v8 {
namespace internal {
// NOTE: If code is marked as being a "shortcut", this means that removing
// the code won't affect the semantics of the surrounding function definition.
// static
bool AstType::IsInteger(i::Object* x) {
return x->IsNumber() && AstType::IsInteger(x->Number());
}
// -----------------------------------------------------------------------------
// Range-related helper functions.
bool AstRangeType::Limits::IsEmpty() { return this->min > this->max; }
AstRangeType::Limits AstRangeType::Limits::Intersect(Limits lhs, Limits rhs) {
DisallowHeapAllocation no_allocation;
Limits result(lhs);
if (lhs.min < rhs.min) result.min = rhs.min;
if (lhs.max > rhs.max) result.max = rhs.max;
return result;
}
AstRangeType::Limits AstRangeType::Limits::Union(Limits lhs, Limits rhs) {
DisallowHeapAllocation no_allocation;
if (lhs.IsEmpty()) return rhs;
if (rhs.IsEmpty()) return lhs;
Limits result(lhs);
if (lhs.min > rhs.min) result.min = rhs.min;
if (lhs.max < rhs.max) result.max = rhs.max;
return result;
}
bool AstType::Overlap(AstRangeType* lhs, AstRangeType* rhs) {
DisallowHeapAllocation no_allocation;
return !AstRangeType::Limits::Intersect(AstRangeType::Limits(lhs),
AstRangeType::Limits(rhs))
.IsEmpty();
}
bool AstType::Contains(AstRangeType* lhs, AstRangeType* rhs) {
DisallowHeapAllocation no_allocation;
return lhs->Min() <= rhs->Min() && rhs->Max() <= lhs->Max();
}
bool AstType::Contains(AstRangeType* lhs, AstConstantType* rhs) {
DisallowHeapAllocation no_allocation;
return IsInteger(*rhs->Value()) && lhs->Min() <= rhs->Value()->Number() &&
rhs->Value()->Number() <= lhs->Max();
}
bool AstType::Contains(AstRangeType* range, i::Object* val) {
DisallowHeapAllocation no_allocation;
return IsInteger(val) && range->Min() <= val->Number() &&
val->Number() <= range->Max();
}
// -----------------------------------------------------------------------------
// Min and Max computation.
double AstType::Min() {
DCHECK(this->SemanticIs(Number()));
if (this->IsBitset()) return AstBitsetType::Min(this->AsBitset());
if (this->IsUnion()) {
double min = +V8_INFINITY;
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
min = std::min(min, this->AsUnion()->Get(i)->Min());
}
return min;
}
if (this->IsRange()) return this->AsRange()->Min();
if (this->IsConstant()) return this->AsConstant()->Value()->Number();
UNREACHABLE();
}
double AstType::Max() {
DCHECK(this->SemanticIs(Number()));
if (this->IsBitset()) return AstBitsetType::Max(this->AsBitset());
if (this->IsUnion()) {
double max = -V8_INFINITY;
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
max = std::max(max, this->AsUnion()->Get(i)->Max());
}
return max;
}
if (this->IsRange()) return this->AsRange()->Max();
if (this->IsConstant()) return this->AsConstant()->Value()->Number();
UNREACHABLE();
}
// -----------------------------------------------------------------------------
// Glb and lub computation.
// The largest bitset subsumed by this type.
AstType::bitset AstBitsetType::Glb(AstType* type) {
DisallowHeapAllocation no_allocation;
// Fast case.
if (IsBitset(type)) {
return type->AsBitset();
} else if (type->IsUnion()) {
SLOW_DCHECK(type->AsUnion()->Wellformed());
return type->AsUnion()->Get(0)->BitsetGlb() |
AST_SEMANTIC(type->AsUnion()->Get(1)->BitsetGlb()); // Shortcut.
} else if (type->IsRange()) {
bitset glb = AST_SEMANTIC(
AstBitsetType::Glb(type->AsRange()->Min(), type->AsRange()->Max()));
return glb | AST_REPRESENTATION(type->BitsetLub());
} else {
return type->Representation();
}
}
// The smallest bitset subsuming this type, possibly not a proper one.
AstType::bitset AstBitsetType::Lub(AstType* type) {
DisallowHeapAllocation no_allocation;
if (IsBitset(type)) return type->AsBitset();
if (type->IsUnion()) {
// Take the representation from the first element, which is always
// a bitset.
int bitset = type->AsUnion()->Get(0)->BitsetLub();
for (int i = 0, n = type->AsUnion()->Length(); i < n; ++i) {
// Other elements only contribute their semantic part.
bitset |= AST_SEMANTIC(type->AsUnion()->Get(i)->BitsetLub());
}
return bitset;
}
if (type->IsClass()) return type->AsClass()->Lub();
if (type->IsConstant()) return type->AsConstant()->Lub();
if (type->IsRange()) return type->AsRange()->Lub();
if (type->IsContext()) return kOtherInternal & kTaggedPointer;
if (type->IsArray()) return kOtherObject;
if (type->IsFunction()) return kFunction;
if (type->IsTuple()) return kOtherInternal;
UNREACHABLE();
}
AstType::bitset AstBitsetType::Lub(i::Map* map) {
DisallowHeapAllocation no_allocation;
switch (map->instance_type()) {
case STRING_TYPE:
case ONE_BYTE_STRING_TYPE:
case CONS_STRING_TYPE:
case CONS_ONE_BYTE_STRING_TYPE:
case THIN_STRING_TYPE:
case THIN_ONE_BYTE_STRING_TYPE:
case SLICED_STRING_TYPE:
case SLICED_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_TYPE:
case EXTERNAL_ONE_BYTE_STRING_TYPE:
case EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
case SHORT_EXTERNAL_STRING_TYPE:
case SHORT_EXTERNAL_ONE_BYTE_STRING_TYPE:
case SHORT_EXTERNAL_STRING_WITH_ONE_BYTE_DATA_TYPE:
return kOtherString;
case INTERNALIZED_STRING_TYPE:
case ONE_BYTE_INTERNALIZED_STRING_TYPE:
case EXTERNAL_INTERNALIZED_STRING_TYPE:
case EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE:
case EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
case SHORT_EXTERNAL_INTERNALIZED_STRING_TYPE:
case SHORT_EXTERNAL_ONE_BYTE_INTERNALIZED_STRING_TYPE:
case SHORT_EXTERNAL_INTERNALIZED_STRING_WITH_ONE_BYTE_DATA_TYPE:
return kInternalizedString;
case SYMBOL_TYPE:
return kSymbol;
case ODDBALL_TYPE: {
Heap* heap = map->GetHeap();
if (map == heap->undefined_map()) return kUndefined;
if (map == heap->null_map()) return kNull;
if (map == heap->boolean_map()) return kBoolean;
if (map == heap->the_hole_map()) return kHole;
DCHECK(map == heap->uninitialized_map() ||
map == heap->termination_exception_map() ||
map == heap->arguments_marker_map() ||
map == heap->optimized_out_map() ||
map == heap->stale_register_map());
return kOtherInternal & kTaggedPointer;
}
case HEAP_NUMBER_TYPE:
return kNumber & kTaggedPointer;
case JS_OBJECT_TYPE:
case JS_ARGUMENTS_TYPE:
case JS_ERROR_TYPE:
case JS_GLOBAL_OBJECT_TYPE:
case JS_GLOBAL_PROXY_TYPE:
case JS_API_OBJECT_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE:
if (map->is_undetectable()) return kOtherUndetectable;
return kOtherObject;
case JS_VALUE_TYPE:
case JS_MESSAGE_OBJECT_TYPE:
case JS_DATE_TYPE:
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_GENERATOR_OBJECT_TYPE:
case JS_ASYNC_GENERATOR_OBJECT_TYPE:
case JS_MODULE_NAMESPACE_TYPE:
case JS_ARRAY_BUFFER_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type.
case JS_TYPED_ARRAY_TYPE:
case JS_DATA_VIEW_TYPE:
case JS_SET_TYPE:
case JS_MAP_TYPE:
case JS_SET_ITERATOR_TYPE:
case JS_MAP_ITERATOR_TYPE:
case JS_STRING_ITERATOR_TYPE:
case JS_ASYNC_FROM_SYNC_ITERATOR_TYPE:
case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE:
case JS_FAST_ARRAY_KEY_ITERATOR_TYPE:
case JS_GENERIC_ARRAY_KEY_ITERATOR_TYPE:
case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_GENERIC_ARRAY_KEY_VALUE_ITERATOR_TYPE:
case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE:
case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
case JS_GENERIC_ARRAY_VALUE_ITERATOR_TYPE:
case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE:
case JS_PROMISE_CAPABILITY_TYPE:
case JS_PROMISE_TYPE:
case JS_BOUND_FUNCTION_TYPE:
DCHECK(!map->is_undetectable());
return kOtherObject;
case JS_FUNCTION_TYPE:
DCHECK(!map->is_undetectable());
return kFunction;
case JS_PROXY_TYPE:
DCHECK(!map->is_undetectable());
return kProxy;
case MAP_TYPE:
case ALLOCATION_SITE_TYPE:
case ACCESSOR_INFO_TYPE:
case SHARED_FUNCTION_INFO_TYPE:
case ACCESSOR_PAIR_TYPE:
case FIXED_ARRAY_TYPE:
case FIXED_DOUBLE_ARRAY_TYPE:
case BYTE_ARRAY_TYPE:
case BYTECODE_ARRAY_TYPE:
case TRANSITION_ARRAY_TYPE:
case FOREIGN_TYPE:
case SCRIPT_TYPE:
case CODE_TYPE:
case PROPERTY_CELL_TYPE:
case MODULE_TYPE:
case MODULE_INFO_ENTRY_TYPE:
case ASYNC_GENERATOR_REQUEST_TYPE:
return kOtherInternal & kTaggedPointer;
// Remaining instance types are unsupported for now. If any of them do
// require bit set types, they should get kOtherInternal & kTaggedPointer.
case MUTABLE_HEAP_NUMBER_TYPE:
case FREE_SPACE_TYPE:
#define FIXED_TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
case FIXED_##TYPE##_ARRAY_TYPE:
TYPED_ARRAYS(FIXED_TYPED_ARRAY_CASE)
#undef FIXED_TYPED_ARRAY_CASE
case FILLER_TYPE:
case ACCESS_CHECK_INFO_TYPE:
case INTERCEPTOR_INFO_TYPE:
case PROMISE_RESOLVE_THENABLE_JOB_INFO_TYPE:
case PROMISE_REACTION_JOB_INFO_TYPE:
case FUNCTION_TEMPLATE_INFO_TYPE:
case OBJECT_TEMPLATE_INFO_TYPE:
case ALLOCATION_MEMENTO_TYPE:
case ALIASED_ARGUMENTS_ENTRY_TYPE:
case DEBUG_INFO_TYPE:
case STACK_FRAME_INFO_TYPE:
case CELL_TYPE:
case WEAK_CELL_TYPE:
case PROTOTYPE_INFO_TYPE:
case SMALL_ORDERED_HASH_MAP_TYPE:
case SMALL_ORDERED_HASH_SET_TYPE:
case TUPLE2_TYPE:
case TUPLE3_TYPE:
case CONTEXT_EXTENSION_TYPE:
case PADDING_TYPE_1:
case PADDING_TYPE_2:
case PADDING_TYPE_3:
case PADDING_TYPE_4:
UNREACHABLE();
}
UNREACHABLE();
}
AstType::bitset AstBitsetType::Lub(i::Object* value) {
DisallowHeapAllocation no_allocation;
if (value->IsNumber()) {
return Lub(value->Number()) &
(value->IsSmi() ? kTaggedSigned : kTaggedPointer);
}
return Lub(i::HeapObject::cast(value)->map());
}
AstType::bitset AstBitsetType::Lub(double value) {
DisallowHeapAllocation no_allocation;
if (i::IsMinusZero(value)) return kMinusZero;
if (std::isnan(value)) return kNaN;
if (IsUint32Double(value) || IsInt32Double(value)) return Lub(value, value);
return kOtherNumber;
}
// Minimum values of plain numeric bitsets.
const AstBitsetType::Boundary AstBitsetType::BoundariesArray[] = {
{kOtherNumber, kPlainNumber, -V8_INFINITY},
{kOtherSigned32, kNegative32, kMinInt},
{kNegative31, kNegative31, -0x40000000},
{kUnsigned30, kUnsigned30, 0},
{kOtherUnsigned31, kUnsigned31, 0x40000000},
{kOtherUnsigned32, kUnsigned32, 0x80000000},
{kOtherNumber, kPlainNumber, static_cast<double>(kMaxUInt32) + 1}};
const AstBitsetType::Boundary* AstBitsetType::Boundaries() {
return BoundariesArray;
}
size_t AstBitsetType::BoundariesSize() {
// Windows doesn't like arraysize here.
// return arraysize(BoundariesArray);
return 7;
}
AstType::bitset AstBitsetType::ExpandInternals(AstType::bitset bits) {
DisallowHeapAllocation no_allocation;
if (!(bits & AST_SEMANTIC(kPlainNumber))) return bits; // Shortcut.
const Boundary* boundaries = Boundaries();
for (size_t i = 0; i < BoundariesSize(); ++i) {
DCHECK(AstBitsetType::Is(boundaries[i].internal, boundaries[i].external));
if (bits & AST_SEMANTIC(boundaries[i].internal))
bits |= AST_SEMANTIC(boundaries[i].external);
}
return bits;
}
AstType::bitset AstBitsetType::Lub(double min, double max) {
DisallowHeapAllocation no_allocation;
int lub = kNone;
const Boundary* mins = Boundaries();
for (size_t i = 1; i < BoundariesSize(); ++i) {
if (min < mins[i].min) {
lub |= mins[i - 1].internal;
if (max < mins[i].min) return lub;
}
}
return lub | mins[BoundariesSize() - 1].internal;
}
AstType::bitset AstBitsetType::NumberBits(bitset bits) {
return AST_SEMANTIC(bits & kPlainNumber);
}
AstType::bitset AstBitsetType::Glb(double min, double max) {
DisallowHeapAllocation no_allocation;
int glb = kNone;
const Boundary* mins = Boundaries();
// If the range does not touch 0, the bound is empty.
if (max < -1 || min > 0) return glb;
for (size_t i = 1; i + 1 < BoundariesSize(); ++i) {
if (min <= mins[i].min) {
if (max + 1 < mins[i + 1].min) break;
glb |= mins[i].external;
}
}
// OtherNumber also contains float numbers, so it can never be
// in the greatest lower bound.
return glb & ~(AST_SEMANTIC(kOtherNumber));
}
double AstBitsetType::Min(bitset bits) {
DisallowHeapAllocation no_allocation;
DCHECK(Is(AST_SEMANTIC(bits), kNumber));
const Boundary* mins = Boundaries();
bool mz = AST_SEMANTIC(bits & kMinusZero);
for (size_t i = 0; i < BoundariesSize(); ++i) {
if (Is(AST_SEMANTIC(mins[i].internal), bits)) {
return mz ? std::min(0.0, mins[i].min) : mins[i].min;
}
}
if (mz) return 0;
return std::numeric_limits<double>::quiet_NaN();
}
double AstBitsetType::Max(bitset bits) {
DisallowHeapAllocation no_allocation;
DCHECK(Is(AST_SEMANTIC(bits), kNumber));
const Boundary* mins = Boundaries();
bool mz = AST_SEMANTIC(bits & kMinusZero);
if (AstBitsetType::Is(AST_SEMANTIC(mins[BoundariesSize() - 1].internal),
bits)) {
return +V8_INFINITY;
}
for (size_t i = BoundariesSize() - 1; i-- > 0;) {
if (Is(AST_SEMANTIC(mins[i].internal), bits)) {
return mz ? std::max(0.0, mins[i + 1].min - 1) : mins[i + 1].min - 1;
}
}
if (mz) return 0;
return std::numeric_limits<double>::quiet_NaN();
}
// -----------------------------------------------------------------------------
// Predicates.
bool AstType::SimplyEquals(AstType* that) {
DisallowHeapAllocation no_allocation;
if (this->IsClass()) {
return that->IsClass() &&
*this->AsClass()->Map() == *that->AsClass()->Map();
}
if (this->IsConstant()) {
return that->IsConstant() &&
*this->AsConstant()->Value() == *that->AsConstant()->Value();
}
if (this->IsContext()) {
return that->IsContext() &&
this->AsContext()->Outer()->Equals(that->AsContext()->Outer());
}
if (this->IsArray()) {
return that->IsArray() &&
this->AsArray()->Element()->Equals(that->AsArray()->Element());
}
if (this->IsFunction()) {
if (!that->IsFunction()) return false;
AstFunctionType* this_fun = this->AsFunction();
AstFunctionType* that_fun = that->AsFunction();
if (this_fun->Arity() != that_fun->Arity() ||
!this_fun->Result()->Equals(that_fun->Result()) ||
!this_fun->Receiver()->Equals(that_fun->Receiver())) {
return false;
}
for (int i = 0, n = this_fun->Arity(); i < n; ++i) {
if (!this_fun->Parameter(i)->Equals(that_fun->Parameter(i))) return false;
}
return true;
}
if (this->IsTuple()) {
if (!that->IsTuple()) return false;
AstTupleType* this_tuple = this->AsTuple();
AstTupleType* that_tuple = that->AsTuple();
if (this_tuple->Arity() != that_tuple->Arity()) {
return false;
}
for (int i = 0, n = this_tuple->Arity(); i < n; ++i) {
if (!this_tuple->Element(i)->Equals(that_tuple->Element(i))) return false;
}
return true;
}
UNREACHABLE();
}
AstType::bitset AstType::Representation() {
return AST_REPRESENTATION(this->BitsetLub());
}
// Check if [this] <= [that].
bool AstType::SlowIs(AstType* that) {
DisallowHeapAllocation no_allocation;
// Fast bitset cases
if (that->IsBitset()) {
return AstBitsetType::Is(this->BitsetLub(), that->AsBitset());
}
if (this->IsBitset()) {
return AstBitsetType::Is(this->AsBitset(), that->BitsetGlb());
}
// Check the representations.
if (!AstBitsetType::Is(Representation(), that->Representation())) {
return false;
}
// Check the semantic part.
return SemanticIs(that);
}
// Check if AST_SEMANTIC([this]) <= AST_SEMANTIC([that]). The result of the
// method
// should be independent of the representation axis of the types.
bool AstType::SemanticIs(AstType* that) {
DisallowHeapAllocation no_allocation;
if (this == that) return true;
if (that->IsBitset()) {
return AstBitsetType::Is(AST_SEMANTIC(this->BitsetLub()), that->AsBitset());
}
if (this->IsBitset()) {
return AstBitsetType::Is(AST_SEMANTIC(this->AsBitset()), that->BitsetGlb());
}
// (T1 \/ ... \/ Tn) <= T if (T1 <= T) /\ ... /\ (Tn <= T)
if (this->IsUnion()) {
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (!this->AsUnion()->Get(i)->SemanticIs(that)) return false;
}
return true;
}
// T <= (T1 \/ ... \/ Tn) if (T <= T1) \/ ... \/ (T <= Tn)
if (that->IsUnion()) {
for (int i = 0, n = that->AsUnion()->Length(); i < n; ++i) {
if (this->SemanticIs(that->AsUnion()->Get(i))) return true;
if (i > 1 && this->IsRange()) return false; // Shortcut.
}
return false;
}
if (that->IsRange()) {
return (this->IsRange() && Contains(that->AsRange(), this->AsRange())) ||
(this->IsConstant() &&
Contains(that->AsRange(), this->AsConstant()));
}
if (this->IsRange()) return false;
return this->SimplyEquals(that);
}
// Most precise _current_ type of a value (usually its class).
AstType* AstType::NowOf(i::Object* value, Zone* zone) {
if (value->IsSmi() ||
i::HeapObject::cast(value)->map()->instance_type() == HEAP_NUMBER_TYPE) {
return Of(value, zone);
}
return Class(i::handle(i::HeapObject::cast(value)->map()), zone);
}
bool AstType::NowContains(i::Object* value) {
DisallowHeapAllocation no_allocation;
if (this->IsAny()) return true;
if (value->IsHeapObject()) {
i::Map* map = i::HeapObject::cast(value)->map();
for (Iterator<i::Map> it = this->Classes(); !it.Done(); it.Advance()) {
if (*it.Current() == map) return true;
}
}
return this->Contains(value);
}
bool AstType::NowIs(AstType* that) {
DisallowHeapAllocation no_allocation;
// TODO(rossberg): this is incorrect for
// Union(Constant(V), T)->NowIs(Class(M))
// but fuzzing does not cover that!
if (this->IsConstant()) {
i::Object* object = *this->AsConstant()->Value();
if (object->IsHeapObject()) {
i::Map* map = i::HeapObject::cast(object)->map();
for (Iterator<i::Map> it = that->Classes(); !it.Done(); it.Advance()) {
if (*it.Current() == map) return true;
}
}
}
return this->Is(that);
}
// Check if [this] contains only (currently) stable classes.
bool AstType::NowStable() {
DisallowHeapAllocation no_allocation;
return !this->IsClass() || this->AsClass()->Map()->is_stable();
}
// Check if [this] and [that] overlap.
bool AstType::Maybe(AstType* that) {
DisallowHeapAllocation no_allocation;
// Take care of the representation part (and also approximate
// the semantic part).
if (!AstBitsetType::IsInhabited(this->BitsetLub() & that->BitsetLub()))
return false;
return SemanticMaybe(that);
}
bool AstType::SemanticMaybe(AstType* that) {
DisallowHeapAllocation no_allocation;
// (T1 \/ ... \/ Tn) overlaps T if (T1 overlaps T) \/ ... \/ (Tn overlaps T)
if (this->IsUnion()) {
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (this->AsUnion()->Get(i)->SemanticMaybe(that)) return true;
}
return false;
}
// T overlaps (T1 \/ ... \/ Tn) if (T overlaps T1) \/ ... \/ (T overlaps Tn)
if (that->IsUnion()) {
for (int i = 0, n = that->AsUnion()->Length(); i < n; ++i) {
if (this->SemanticMaybe(that->AsUnion()->Get(i))) return true;
}
return false;
}
if (!AstBitsetType::SemanticIsInhabited(this->BitsetLub() &
that->BitsetLub()))
return false;
if (this->IsBitset() && that->IsBitset()) return true;
if (this->IsClass() != that->IsClass()) return true;
if (this->IsRange()) {
if (that->IsConstant()) {
return Contains(this->AsRange(), that->AsConstant());
}
if (that->IsRange()) {
return Overlap(this->AsRange(), that->AsRange());
}
if (that->IsBitset()) {
bitset number_bits = AstBitsetType::NumberBits(that->AsBitset());
if (number_bits == AstBitsetType::kNone) {
return false;
}
double min = std::max(AstBitsetType::Min(number_bits), this->Min());
double max = std::min(AstBitsetType::Max(number_bits), this->Max());
return min <= max;
}
}
if (that->IsRange()) {
return that->SemanticMaybe(this); // This case is handled above.
}
if (this->IsBitset() || that->IsBitset()) return true;
return this->SimplyEquals(that);
}
// Return the range in [this], or [NULL].
AstType* AstType::GetRange() {
DisallowHeapAllocation no_allocation;
if (this->IsRange()) return this;
if (this->IsUnion() && this->AsUnion()->Get(1)->IsRange()) {
return this->AsUnion()->Get(1);
}
return NULL;
}
bool AstType::Contains(i::Object* value) {
DisallowHeapAllocation no_allocation;
for (Iterator<i::Object> it = this->Constants(); !it.Done(); it.Advance()) {
if (*it.Current() == value) return true;
}
if (IsInteger(value)) {
AstType* range = this->GetRange();
if (range != NULL && Contains(range->AsRange(), value)) return true;
}
return AstBitsetType::New(AstBitsetType::Lub(value))->Is(this);
}
bool AstUnionType::Wellformed() {
DisallowHeapAllocation no_allocation;
// This checks the invariants of the union representation:
// 1. There are at least two elements.
// 2. The first element is a bitset, no other element is a bitset.
// 3. At most one element is a range, and it must be the second one.
// 4. No element is itself a union.
// 5. No element (except the bitset) is a subtype of any other.
// 6. If there is a range, then the bitset type does not contain
// plain number bits.
DCHECK(this->Length() >= 2); // (1)
DCHECK(this->Get(0)->IsBitset()); // (2a)
for (int i = 0; i < this->Length(); ++i) {
if (i != 0) DCHECK(!this->Get(i)->IsBitset()); // (2b)
if (i != 1) DCHECK(!this->Get(i)->IsRange()); // (3)
DCHECK(!this->Get(i)->IsUnion()); // (4)
for (int j = 0; j < this->Length(); ++j) {
if (i != j && i != 0)
DCHECK(!this->Get(i)->SemanticIs(this->Get(j))); // (5)
}
}
DCHECK(!this->Get(1)->IsRange() ||
(AstBitsetType::NumberBits(this->Get(0)->AsBitset()) ==
AstBitsetType::kNone)); // (6)
return true;
}
// -----------------------------------------------------------------------------
// Union and intersection
static bool AddIsSafe(int x, int y) {
return x >= 0 ? y <= std::numeric_limits<int>::max() - x
: y >= std::numeric_limits<int>::min() - x;
}
AstType* AstType::Intersect(AstType* type1, AstType* type2, Zone* zone) {
// Fast case: bit sets.
if (type1->IsBitset() && type2->IsBitset()) {
return AstBitsetType::New(type1->AsBitset() & type2->AsBitset());
}
// Fast case: top or bottom types.
if (type1->IsNone() || type2->IsAny()) return type1; // Shortcut.
if (type2->IsNone() || type1->IsAny()) return type2; // Shortcut.
// Semi-fast case.
if (type1->Is(type2)) return type1;
if (type2->Is(type1)) return type2;
// Slow case: create union.
// Figure out the representation of the result first.
// The rest of the method should not change this representation and
// it should not make any decisions based on representations (i.e.,
// it should only use the semantic part of types).
const bitset representation =
type1->Representation() & type2->Representation();
// Semantic subtyping check - this is needed for consistency with the
// semi-fast case above - we should behave the same way regardless of
// representations. Intersection with a universal bitset should only update
// the representations.
if (type1->SemanticIs(type2)) {
type2 = Any();
} else if (type2->SemanticIs(type1)) {
type1 = Any();
}
bitset bits =
AST_SEMANTIC(type1->BitsetGlb() & type2->BitsetGlb()) | representation;
int size1 = type1->IsUnion() ? type1->AsUnion()->Length() : 1;
int size2 = type2->IsUnion() ? type2->AsUnion()->Length() : 1;
if (!AddIsSafe(size1, size2)) return Any();
int size = size1 + size2;
if (!AddIsSafe(size, 2)) return Any();
size += 2;
AstType* result_type = AstUnionType::New(size, zone);
AstUnionType* result = result_type->AsUnion();
size = 0;
// Deal with bitsets.
result->Set(size++, AstBitsetType::New(bits));
AstRangeType::Limits lims = AstRangeType::Limits::Empty();
size = IntersectAux(type1, type2, result, size, &lims, zone);
// If the range is not empty, then insert it into the union and
// remove the number bits from the bitset.
if (!lims.IsEmpty()) {
size = UpdateRange(AstRangeType::New(lims, representation, zone), result,
size, zone);
// Remove the number bits.
bitset number_bits = AstBitsetType::NumberBits(bits);
bits &= ~number_bits;
result->Set(0, AstBitsetType::New(bits));
}
return NormalizeUnion(result_type, size, zone);
}
int AstType::UpdateRange(AstType* range, AstUnionType* result, int size,
Zone* zone) {
if (size == 1) {
result->Set(size++, range);
} else {
// Make space for the range.
result->Set(size++, result->Get(1));
result->Set(1, range);
}
// Remove any components that just got subsumed.
for (int i = 2; i < size;) {
if (result->Get(i)->SemanticIs(range)) {
result->Set(i, result->Get(--size));
} else {
++i;
}
}
return size;
}
AstRangeType::Limits AstType::ToLimits(bitset bits, Zone* zone) {
bitset number_bits = AstBitsetType::NumberBits(bits);
if (number_bits == AstBitsetType::kNone) {
return AstRangeType::Limits::Empty();
}
return AstRangeType::Limits(AstBitsetType::Min(number_bits),
AstBitsetType::Max(number_bits));
}
AstRangeType::Limits AstType::IntersectRangeAndBitset(AstType* range,
AstType* bitset,
Zone* zone) {
AstRangeType::Limits range_lims(range->AsRange());
AstRangeType::Limits bitset_lims = ToLimits(bitset->AsBitset(), zone);
return AstRangeType::Limits::Intersect(range_lims, bitset_lims);
}
int AstType::IntersectAux(AstType* lhs, AstType* rhs, AstUnionType* result,
int size, AstRangeType::Limits* lims, Zone* zone) {
if (lhs->IsUnion()) {
for (int i = 0, n = lhs->AsUnion()->Length(); i < n; ++i) {
size =
IntersectAux(lhs->AsUnion()->Get(i), rhs, result, size, lims, zone);
}
return size;
}
if (rhs->IsUnion()) {
for (int i = 0, n = rhs->AsUnion()->Length(); i < n; ++i) {
size =
IntersectAux(lhs, rhs->AsUnion()->Get(i), result, size, lims, zone);
}
return size;
}
if (!AstBitsetType::SemanticIsInhabited(lhs->BitsetLub() &
rhs->BitsetLub())) {
return size;
}
if (lhs->IsRange()) {
if (rhs->IsBitset()) {
AstRangeType::Limits lim = IntersectRangeAndBitset(lhs, rhs, zone);
if (!lim.IsEmpty()) {
*lims = AstRangeType::Limits::Union(lim, *lims);
}
return size;
}
if (rhs->IsClass()) {
*lims = AstRangeType::Limits::Union(AstRangeType::Limits(lhs->AsRange()),
*lims);
}
if (rhs->IsConstant() && Contains(lhs->AsRange(), rhs->AsConstant())) {
return AddToUnion(rhs, result, size, zone);
}
if (rhs->IsRange()) {
AstRangeType::Limits lim =
AstRangeType::Limits::Intersect(AstRangeType::Limits(lhs->AsRange()),
AstRangeType::Limits(rhs->AsRange()));
if (!lim.IsEmpty()) {
*lims = AstRangeType::Limits::Union(lim, *lims);
}
}
return size;
}
if (rhs->IsRange()) {
// This case is handled symmetrically above.
return IntersectAux(rhs, lhs, result, size, lims, zone);
}
if (lhs->IsBitset() || rhs->IsBitset()) {
return AddToUnion(lhs->IsBitset() ? rhs : lhs, result, size, zone);
}
if (lhs->IsClass() != rhs->IsClass()) {
return AddToUnion(lhs->IsClass() ? rhs : lhs, result, size, zone);
}
if (lhs->SimplyEquals(rhs)) {
return AddToUnion(lhs, result, size, zone);
}
return size;
}
// Make sure that we produce a well-formed range and bitset:
// If the range is non-empty, the number bits in the bitset should be
// clear. Moreover, if we have a canonical range (such as Signed32),
// we want to produce a bitset rather than a range.
AstType* AstType::NormalizeRangeAndBitset(AstType* range, bitset* bits,
Zone* zone) {
// Fast path: If the bitset does not mention numbers, we can just keep the
// range.
bitset number_bits = AstBitsetType::NumberBits(*bits);
if (number_bits == 0) {
return range;
}
// If the range is semantically contained within the bitset, return None and
// leave the bitset untouched.
bitset range_lub = AST_SEMANTIC(range->BitsetLub());
if (AstBitsetType::Is(range_lub, *bits)) {
return None();
}
// Slow path: reconcile the bitset range and the range.
double bitset_min = AstBitsetType::Min(number_bits);
double bitset_max = AstBitsetType::Max(number_bits);
double range_min = range->Min();
double range_max = range->Max();
// Remove the number bits from the bitset, they would just confuse us now.
// NOTE: bits contains OtherNumber iff bits contains PlainNumber, in which
// case we already returned after the subtype check above.
*bits &= ~number_bits;
if (range_min <= bitset_min && range_max >= bitset_max) {
// Bitset is contained within the range, just return the range.
return range;
}
if (bitset_min < range_min) {
range_min = bitset_min;
}
if (bitset_max > range_max) {
range_max = bitset_max;
}
return AstRangeType::New(range_min, range_max, AstBitsetType::kNone, zone);
}
AstType* AstType::Union(AstType* type1, AstType* type2, Zone* zone) {
// Fast case: bit sets.
if (type1->IsBitset() && type2->IsBitset()) {
return AstBitsetType::New(type1->AsBitset() | type2->AsBitset());
}
// Fast case: top or bottom types.
if (type1->IsAny() || type2->IsNone()) return type1;
if (type2->IsAny() || type1->IsNone()) return type2;
// Semi-fast case.
if (type1->Is(type2)) return type2;
if (type2->Is(type1)) return type1;
// Figure out the representation of the result.
// The rest of the method should not change this representation and
// it should not make any decisions based on representations (i.e.,
// it should only use the semantic part of types).
const bitset representation =
type1->Representation() | type2->Representation();
// Slow case: create union.
int size1 = type1->IsUnion() ? type1->AsUnion()->Length() : 1;
int size2 = type2->IsUnion() ? type2->AsUnion()->Length() : 1;
if (!AddIsSafe(size1, size2)) return Any();
int size = size1 + size2;
if (!AddIsSafe(size, 2)) return Any();
size += 2;
AstType* result_type = AstUnionType::New(size, zone);
AstUnionType* result = result_type->AsUnion();
size = 0;
// Compute the new bitset.
bitset new_bitset = AST_SEMANTIC(type1->BitsetGlb() | type2->BitsetGlb());
// Deal with ranges.
AstType* range = None();
AstType* range1 = type1->GetRange();
AstType* range2 = type2->GetRange();
if (range1 != NULL && range2 != NULL) {
AstRangeType::Limits lims =
AstRangeType::Limits::Union(AstRangeType::Limits(range1->AsRange()),
AstRangeType::Limits(range2->AsRange()));
AstType* union_range = AstRangeType::New(lims, representation, zone);
range = NormalizeRangeAndBitset(union_range, &new_bitset, zone);
} else if (range1 != NULL) {
range = NormalizeRangeAndBitset(range1, &new_bitset, zone);
} else if (range2 != NULL) {
range = NormalizeRangeAndBitset(range2, &new_bitset, zone);
}
new_bitset = AST_SEMANTIC(new_bitset) | representation;
AstType* bits = AstBitsetType::New(new_bitset);
result->Set(size++, bits);
if (!range->IsNone()) result->Set(size++, range);
size = AddToUnion(type1, result, size, zone);
size = AddToUnion(type2, result, size, zone);
return NormalizeUnion(result_type, size, zone);
}
// Add [type] to [result] unless [type] is bitset, range, or already subsumed.
// Return new size of [result].
int AstType::AddToUnion(AstType* type, AstUnionType* result, int size,
Zone* zone) {
if (type->IsBitset() || type->IsRange()) return size;
if (type->IsUnion()) {
for (int i = 0, n = type->AsUnion()->Length(); i < n; ++i) {
size = AddToUnion(type->AsUnion()->Get(i), result, size, zone);
}
return size;
}
for (int i = 0; i < size; ++i) {
if (type->SemanticIs(result->Get(i))) return size;
}
result->Set(size++, type);
return size;
}
AstType* AstType::NormalizeUnion(AstType* union_type, int size, Zone* zone) {
AstUnionType* unioned = union_type->AsUnion();
DCHECK(size >= 1);
DCHECK(unioned->Get(0)->IsBitset());
// If the union has just one element, return it.
if (size == 1) {
return unioned->Get(0);
}
bitset bits = unioned->Get(0)->AsBitset();
// If the union only consists of a range, we can get rid of the union.
if (size == 2 && AST_SEMANTIC(bits) == AstBitsetType::kNone) {
bitset representation = AST_REPRESENTATION(bits);
if (representation == unioned->Get(1)->Representation()) {
return unioned->Get(1);
}
if (unioned->Get(1)->IsRange()) {
return AstRangeType::New(unioned->Get(1)->AsRange()->Min(),
unioned->Get(1)->AsRange()->Max(),
unioned->Get(0)->AsBitset(), zone);
}
}
unioned->Shrink(size);
SLOW_DCHECK(unioned->Wellformed());
return union_type;
}
// -----------------------------------------------------------------------------
// Component extraction
// static
AstType* AstType::Representation(AstType* t, Zone* zone) {
return AstBitsetType::New(t->Representation());
}
// static
AstType* AstType::Semantic(AstType* t, Zone* zone) {
return Intersect(t, AstBitsetType::New(AstBitsetType::kSemantic), zone);
}
// -----------------------------------------------------------------------------
// Iteration.
int AstType::NumClasses() {
DisallowHeapAllocation no_allocation;
if (this->IsClass()) {
return 1;
} else if (this->IsUnion()) {
int result = 0;
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (this->AsUnion()->Get(i)->IsClass()) ++result;
}
return result;
} else {
return 0;
}
}
int AstType::NumConstants() {
DisallowHeapAllocation no_allocation;
if (this->IsConstant()) {
return 1;
} else if (this->IsUnion()) {
int result = 0;
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
if (this->AsUnion()->Get(i)->IsConstant()) ++result;
}
return result;
} else {
return 0;
}
}
template <class T>
AstType* AstType::Iterator<T>::get_type() {
DCHECK(!Done());
return type_->IsUnion() ? type_->AsUnion()->Get(index_) : type_;
}
// C++ cannot specialise nested templates, so we have to go through this
// contortion with an auxiliary template to simulate it.
template <class T>
struct TypeImplIteratorAux {
static bool matches(AstType* type);
static i::Handle<T> current(AstType* type);
};
template <>
struct TypeImplIteratorAux<i::Map> {
static bool matches(AstType* type) { return type->IsClass(); }
static i::Handle<i::Map> current(AstType* type) {
return type->AsClass()->Map();
}
};
template <>
struct TypeImplIteratorAux<i::Object> {
static bool matches(AstType* type) { return type->IsConstant(); }
static i::Handle<i::Object> current(AstType* type) {
return type->AsConstant()->Value();
}
};
template <class T>
bool AstType::Iterator<T>::matches(AstType* type) {
return TypeImplIteratorAux<T>::matches(type);
}
template <class T>
i::Handle<T> AstType::Iterator<T>::Current() {
return TypeImplIteratorAux<T>::current(get_type());
}
template <class T>
void AstType::Iterator<T>::Advance() {
DisallowHeapAllocation no_allocation;
++index_;
if (type_->IsUnion()) {
for (int n = type_->AsUnion()->Length(); index_ < n; ++index_) {
if (matches(type_->AsUnion()->Get(index_))) return;
}
} else if (index_ == 0 && matches(type_)) {
return;
}
index_ = -1;
}
// -----------------------------------------------------------------------------
// Printing.
const char* AstBitsetType::Name(bitset bits) {
switch (bits) {
case AST_REPRESENTATION(kAny):
return "Any";
#define RETURN_NAMED_REPRESENTATION_TYPE(type, value) \
case AST_REPRESENTATION(k##type): \
return #type;
AST_REPRESENTATION_BITSET_TYPE_LIST(RETURN_NAMED_REPRESENTATION_TYPE)
#undef RETURN_NAMED_REPRESENTATION_TYPE
#define RETURN_NAMED_SEMANTIC_TYPE(type, value) \
case AST_SEMANTIC(k##type): \
return #type;
AST_SEMANTIC_BITSET_TYPE_LIST(RETURN_NAMED_SEMANTIC_TYPE)
AST_INTERNAL_BITSET_TYPE_LIST(RETURN_NAMED_SEMANTIC_TYPE)
#undef RETURN_NAMED_SEMANTIC_TYPE
default:
return NULL;
}
}
void AstBitsetType::Print(std::ostream& os, // NOLINT
bitset bits) {
DisallowHeapAllocation no_allocation;
const char* name = Name(bits);
if (name != NULL) {
os << name;
return;
}
// clang-format off
static const bitset named_bitsets[] = {
#define BITSET_CONSTANT(type, value) AST_REPRESENTATION(k##type),
AST_REPRESENTATION_BITSET_TYPE_LIST(BITSET_CONSTANT)
#undef BITSET_CONSTANT
#define BITSET_CONSTANT(type, value) AST_SEMANTIC(k##type),
AST_INTERNAL_BITSET_TYPE_LIST(BITSET_CONSTANT)
AST_SEMANTIC_BITSET_TYPE_LIST(BITSET_CONSTANT)
#undef BITSET_CONSTANT
};
// clang-format on
bool is_first = true;
os << "(";
for (int i(arraysize(named_bitsets) - 1); bits != 0 && i >= 0; --i) {
bitset subset = named_bitsets[i];
if ((bits & subset) == subset) {
if (!is_first) os << " | ";
is_first = false;
os << Name(subset);
bits -= subset;
}
}
DCHECK(bits == 0);
os << ")";
}
void AstType::PrintTo(std::ostream& os, PrintDimension dim) {
DisallowHeapAllocation no_allocation;
if (dim != REPRESENTATION_DIM) {
if (this->IsBitset()) {
AstBitsetType::Print(os, AST_SEMANTIC(this->AsBitset()));
} else if (this->IsClass()) {
os << "Class(" << static_cast<void*>(*this->AsClass()->Map()) << " < ";
AstBitsetType::New(AstBitsetType::Lub(this))->PrintTo(os, dim);
os << ")";
} else if (this->IsConstant()) {
os << "Constant(" << Brief(*this->AsConstant()->Value()) << ")";
} else if (this->IsRange()) {
std::ostream::fmtflags saved_flags = os.setf(std::ios::fixed);
std::streamsize saved_precision = os.precision(0);
os << "Range(" << this->AsRange()->Min() << ", " << this->AsRange()->Max()
<< ")";
os.flags(saved_flags);
os.precision(saved_precision);
} else if (this->IsContext()) {
os << "Context(";
this->AsContext()->Outer()->PrintTo(os, dim);
os << ")";
} else if (this->IsUnion()) {
os << "(";
for (int i = 0, n = this->AsUnion()->Length(); i < n; ++i) {
AstType* type_i = this->AsUnion()->Get(i);
if (i > 0) os << " | ";
type_i->PrintTo(os, dim);
}
os << ")";
} else if (this->IsArray()) {
os << "Array(";
AsArray()->Element()->PrintTo(os, dim);
os << ")";
} else if (this->IsFunction()) {
if (!this->AsFunction()->Receiver()->IsAny()) {
this->AsFunction()->Receiver()->PrintTo(os, dim);
os << ".";
}
os << "(";
for (int i = 0; i < this->AsFunction()->Arity(); ++i) {
if (i > 0) os << ", ";
this->AsFunction()->Parameter(i)->PrintTo(os, dim);
}
os << ")->";
this->AsFunction()->Result()->PrintTo(os, dim);
} else if (this->IsTuple()) {
os << "<";
for (int i = 0, n = this->AsTuple()->Arity(); i < n; ++i) {
AstType* type_i = this->AsTuple()->Element(i);
if (i > 0) os << ", ";
type_i->PrintTo(os, dim);
}
os << ">";
} else {
UNREACHABLE();
}
}
if (dim == BOTH_DIMS) os << "/";
if (dim != SEMANTIC_DIM) {
AstBitsetType::Print(os, AST_REPRESENTATION(this->BitsetLub()));
}
}
#ifdef DEBUG
void AstType::Print() {
OFStream os(stdout);
PrintTo(os);
os << std::endl;
}
void AstBitsetType::Print(bitset bits) {
OFStream os(stdout);
Print(os, bits);
os << std::endl;
}
#endif
AstBitsetType::bitset AstBitsetType::SignedSmall() {
return i::SmiValuesAre31Bits() ? kSigned31 : kSigned32;
}
AstBitsetType::bitset AstBitsetType::UnsignedSmall() {
return i::SmiValuesAre31Bits() ? kUnsigned30 : kUnsigned31;
}
// -----------------------------------------------------------------------------
// Instantiations.
template class AstType::Iterator<i::Map>;
template class AstType::Iterator<i::Object>;
} // namespace internal
} // namespace v8
// Copyright 2014 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.
#ifndef V8_AST_AST_TYPES_H_
#define V8_AST_AST_TYPES_H_
#include "src/conversions.h"
#include "src/handles.h"
#include "src/objects.h"
#include "src/ostreams.h"
namespace v8 {
namespace internal {
// SUMMARY
//
// A simple type system for compiler-internal use. It is based entirely on
// union types, and all subtyping hence amounts to set inclusion. Besides the
// obvious primitive types and some predefined unions, the type language also
// can express class types (a.k.a. specific maps) and singleton types (i.e.,
// concrete constants).
//
// Types consist of two dimensions: semantic (value range) and representation.
// Both are related through subtyping.
//
//
// SEMANTIC DIMENSION
//
// The following equations and inequations hold for the semantic axis:
//
// None <= T
// T <= Any
//
// Number = Signed32 \/ Unsigned32 \/ Double
// Smi <= Signed32
// Name = String \/ Symbol
// UniqueName = InternalizedString \/ Symbol
// InternalizedString < String
//
// Receiver = Object \/ Proxy
// Array < Object
// Function < Object
// RegExp < Object
// OtherUndetectable < Object
// DetectableReceiver = Receiver - OtherUndetectable
//
// Class(map) < T iff instance_type(map) < T
// Constant(x) < T iff instance_type(map(x)) < T
// Array(T) < Array
// Function(R, S, T0, T1, ...) < Function
// Context(T) < Internal
//
// Both structural Array and Function types are invariant in all parameters;
// relaxing this would make Union and Intersect operations more involved.
// There is no subtyping relation between Array, Function, or Context types
// and respective Constant types, since these types cannot be reconstructed
// for arbitrary heap values.
// Note also that Constant(x) < Class(map(x)) does _not_ hold, since x's map can
// change! (Its instance type cannot, however.)
// TODO(rossberg): the latter is not currently true for proxies, because of fix,
// but will hold once we implement direct proxies.
// However, we also define a 'temporal' variant of the subtyping relation that
// considers the _current_ state only, i.e., Constant(x) <_now Class(map(x)).
//
//
// REPRESENTATIONAL DIMENSION
//
// For the representation axis, the following holds:
//
// None <= R
// R <= Any
//
// UntaggedInt = UntaggedInt1 \/ UntaggedInt8 \/
// UntaggedInt16 \/ UntaggedInt32
// UntaggedFloat = UntaggedFloat32 \/ UntaggedFloat64
// UntaggedNumber = UntaggedInt \/ UntaggedFloat
// Untagged = UntaggedNumber \/ UntaggedPtr
// Tagged = TaggedInt \/ TaggedPtr
//
// Subtyping relates the two dimensions, for example:
//
// Number <= Tagged \/ UntaggedNumber
// Object <= TaggedPtr \/ UntaggedPtr
//
// That holds because the semantic type constructors defined by the API create
// types that allow for all possible representations, and dually, the ones for
// representation types initially include all semantic ranges. Representations
// can then e.g. be narrowed for a given semantic type using intersection:
//
// SignedSmall /\ TaggedInt (a 'smi')
// Number /\ TaggedPtr (a heap number)
//
//
// RANGE TYPES
//
// A range type represents a continuous integer interval by its minimum and
// maximum value. Either value may be an infinity, in which case that infinity
// itself is also included in the range. A range never contains NaN or -0.
//
// If a value v happens to be an integer n, then Constant(v) is considered a
// subtype of Range(n, n) (and therefore also a subtype of any larger range).
// In order to avoid large unions, however, it is usually a good idea to use
// Range rather than Constant.
//
//
// PREDICATES
//
// There are two main functions for testing types:
//
// T1->Is(T2) -- tests whether T1 is included in T2 (i.e., T1 <= T2)
// T1->Maybe(T2) -- tests whether T1 and T2 overlap (i.e., T1 /\ T2 =/= 0)
//
// Typically, the former is to be used to select representations (e.g., via
// T->Is(SignedSmall())), and the latter to check whether a specific case needs
// handling (e.g., via T->Maybe(Number())).
//
// There is no functionality to discover whether a type is a leaf in the
// lattice. That is intentional. It should always be possible to refine the
// lattice (e.g., splitting up number types further) without invalidating any
// existing assumptions or tests.
// Consequently, do not normally use Equals for type tests, always use Is!
//
// The NowIs operator implements state-sensitive subtying, as described above.
// Any compilation decision based on such temporary properties requires runtime
// guarding!
//
//
// PROPERTIES
//
// Various formal properties hold for constructors, operators, and predicates
// over types. For example, constructors are injective and subtyping is a
// complete partial order.
//
// See test/cctest/test-types.cc for a comprehensive executable specification,
// especially with respect to the properties of the more exotic 'temporal'
// constructors and predicates (those prefixed 'Now').
//
//
// IMPLEMENTATION
//
// Internally, all 'primitive' types, and their unions, are represented as
// bitsets. Bit 0 is reserved for tagging. Class is a heap pointer to the
// respective map. Only structured types require allocation.
// Note that the bitset representation is closed under both Union and Intersect.
// -----------------------------------------------------------------------------
// Values for bitset types
// clang-format off
#define AST_MASK_BITSET_TYPE_LIST(V) \
V(Representation, 0xffc00000u) \
V(Semantic, 0x003ffffeu)
#define AST_REPRESENTATION(k) ((k) & AstBitsetType::kRepresentation)
#define AST_SEMANTIC(k) ((k) & AstBitsetType::kSemantic)
// Bits 21-22 are available.
#define AST_REPRESENTATION_BITSET_TYPE_LIST(V) \
V(None, 0) \
V(UntaggedBit, 1u << 23 | kSemantic) \
V(UntaggedIntegral8, 1u << 24 | kSemantic) \
V(UntaggedIntegral16, 1u << 25 | kSemantic) \
V(UntaggedIntegral32, 1u << 26 | kSemantic) \
V(UntaggedFloat32, 1u << 27 | kSemantic) \
V(UntaggedFloat64, 1u << 28 | kSemantic) \
V(UntaggedPointer, 1u << 29 | kSemantic) \
V(TaggedSigned, 1u << 30 | kSemantic) \
V(TaggedPointer, 1u << 31 | kSemantic) \
\
V(UntaggedIntegral, kUntaggedBit | kUntaggedIntegral8 | \
kUntaggedIntegral16 | kUntaggedIntegral32) \
V(UntaggedFloat, kUntaggedFloat32 | kUntaggedFloat64) \
V(UntaggedNumber, kUntaggedIntegral | kUntaggedFloat) \
V(Untagged, kUntaggedNumber | kUntaggedPointer) \
V(Tagged, kTaggedSigned | kTaggedPointer)
#define AST_INTERNAL_BITSET_TYPE_LIST(V) \
V(OtherUnsigned31, 1u << 1 | AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(OtherUnsigned32, 1u << 2 | AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(OtherSigned32, 1u << 3 | AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(OtherNumber, 1u << 4 | AST_REPRESENTATION(kTagged | kUntaggedNumber))
#define AST_SEMANTIC_BITSET_TYPE_LIST(V) \
V(Negative31, 1u << 5 | \
AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(Null, 1u << 6 | AST_REPRESENTATION(kTaggedPointer)) \
V(Undefined, 1u << 7 | AST_REPRESENTATION(kTaggedPointer)) \
V(Boolean, 1u << 8 | AST_REPRESENTATION(kTaggedPointer)) \
V(Unsigned30, 1u << 9 | \
AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(MinusZero, 1u << 10 | \
AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(NaN, 1u << 11 | \
AST_REPRESENTATION(kTagged | kUntaggedNumber)) \
V(Symbol, 1u << 12 | AST_REPRESENTATION(kTaggedPointer)) \
V(InternalizedString, 1u << 13 | AST_REPRESENTATION(kTaggedPointer)) \
V(OtherString, 1u << 14 | AST_REPRESENTATION(kTaggedPointer)) \
V(OtherObject, 1u << 15 | AST_REPRESENTATION(kTaggedPointer)) \
V(OtherUndetectable, 1u << 16 | AST_REPRESENTATION(kTaggedPointer)) \
V(Proxy, 1u << 17 | AST_REPRESENTATION(kTaggedPointer)) \
V(Function, 1u << 18 | AST_REPRESENTATION(kTaggedPointer)) \
V(Hole, 1u << 19 | AST_REPRESENTATION(kTaggedPointer)) \
V(OtherInternal, 1u << 20 | \
AST_REPRESENTATION(kTagged | kUntagged)) \
\
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | \
kOtherSigned32) \
V(Signed32OrMinusZero, kSigned32 | kMinusZero) \
V(Signed32OrMinusZeroOrNaN, kSigned32 | kMinusZero | kNaN) \
V(Negative32, kNegative31 | kOtherSigned32) \
V(Unsigned31, kUnsigned30 | kOtherUnsigned31) \
V(Unsigned32, kUnsigned30 | kOtherUnsigned31 | \
kOtherUnsigned32) \
V(Unsigned32OrMinusZero, kUnsigned32 | kMinusZero) \
V(Unsigned32OrMinusZeroOrNaN, kUnsigned32 | kMinusZero | kNaN) \
V(Integral32, kSigned32 | kUnsigned32) \
V(PlainNumber, kIntegral32 | kOtherNumber) \
V(OrderedNumber, kPlainNumber | kMinusZero) \
V(MinusZeroOrNaN, kMinusZero | kNaN) \
V(Number, kOrderedNumber | kNaN) \
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(BooleanOrNumber, kBoolean | kNumber) \
V(BooleanOrNullOrNumber, kBooleanOrNumber | kNull) \
V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
V(NullOrNumber, kNull | kNumber) \
V(NullOrUndefined, kNull | kUndefined) \
V(Undetectable, kNullOrUndefined | kOtherUndetectable) \
V(NumberOrOddball, kNumber | kNullOrUndefined | kBoolean | kHole) \
V(NumberOrString, kNumber | kString) \
V(NumberOrUndefined, kNumber | kUndefined) \
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
V(Primitive, kSymbol | kPlainPrimitive) \
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \
V(Object, kFunction | kOtherObject | kOtherUndetectable) \
V(Receiver, kObject | kProxy) \
V(StringOrReceiver, kString | kReceiver) \
V(Unique, kBoolean | kUniqueName | kNull | kUndefined | \
kReceiver) \
V(Internal, kHole | kOtherInternal) \
V(NonInternal, kPrimitive | kReceiver) \
V(NonNumber, kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
// clang-format on
/*
* The following diagrams show how integers (in the mathematical sense) are
* divided among the different atomic numerical types.
*
* ON OS32 N31 U30 OU31 OU32 ON
* ______[_______[_______[_______[_______[_______[_______
* -2^31 -2^30 0 2^30 2^31 2^32
*
* E.g., OtherUnsigned32 (OU32) covers all integers from 2^31 to 2^32-1.
*
* Some of the atomic numerical bitsets are internal only (see
* INTERNAL_BITSET_TYPE_LIST). To a types user, they should only occur in
* union with certain other bitsets. For instance, OtherNumber should only
* occur as part of PlainNumber.
*/
#define AST_PROPER_BITSET_TYPE_LIST(V) \
AST_REPRESENTATION_BITSET_TYPE_LIST(V) \
AST_SEMANTIC_BITSET_TYPE_LIST(V)
#define AST_BITSET_TYPE_LIST(V) \
AST_MASK_BITSET_TYPE_LIST(V) \
AST_REPRESENTATION_BITSET_TYPE_LIST(V) \
AST_INTERNAL_BITSET_TYPE_LIST(V) \
AST_SEMANTIC_BITSET_TYPE_LIST(V)
class AstType;
// -----------------------------------------------------------------------------
// Bitset types (internal).
class AstBitsetType {
public:
typedef uint32_t bitset; // Internal
enum : uint32_t {
#define DECLARE_TYPE(type, value) k##type = (value),
AST_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
kUnusedEOL = 0
};
static bitset SignedSmall();
static bitset UnsignedSmall();
bitset Bitset() {
return static_cast<bitset>(reinterpret_cast<uintptr_t>(this) ^ 1u);
}
static bool IsInhabited(bitset bits) {
return AST_SEMANTIC(bits) != kNone && AST_REPRESENTATION(bits) != kNone;
}
static bool SemanticIsInhabited(bitset bits) {
return AST_SEMANTIC(bits) != kNone;
}
static bool Is(bitset bits1, bitset bits2) {
return (bits1 | bits2) == bits2;
}
static double Min(bitset);
static double Max(bitset);
static bitset Glb(AstType* type); // greatest lower bound that's a bitset
static bitset Glb(double min, double max);
static bitset Lub(AstType* type); // least upper bound that's a bitset
static bitset Lub(i::Map* map);
static bitset Lub(i::Object* value);
static bitset Lub(double value);
static bitset Lub(double min, double max);
static bitset ExpandInternals(bitset bits);
static const char* Name(bitset);
static void Print(std::ostream& os, bitset); // NOLINT
#ifdef DEBUG
static void Print(bitset);
#endif
static bitset NumberBits(bitset bits);
static bool IsBitset(AstType* type) {
return reinterpret_cast<uintptr_t>(type) & 1;
}
static AstType* NewForTesting(bitset bits) { return New(bits); }
private:
friend class AstType;
static AstType* New(bitset bits) {
return reinterpret_cast<AstType*>(static_cast<uintptr_t>(bits | 1u));
}
struct Boundary {
bitset internal;
bitset external;
double min;
};
static const Boundary BoundariesArray[];
static inline const Boundary* Boundaries();
static inline size_t BoundariesSize();
};
// -----------------------------------------------------------------------------
// Superclass for non-bitset types (internal).
class AstTypeBase {
protected:
friend class AstType;
enum Kind {
kClass,
kConstant,
kContext,
kArray,
kFunction,
kTuple,
kUnion,
kRange
};
Kind kind() const { return kind_; }
explicit AstTypeBase(Kind kind) : kind_(kind) {}
static bool IsKind(AstType* type, Kind kind) {
if (AstBitsetType::IsBitset(type)) return false;
AstTypeBase* base = reinterpret_cast<AstTypeBase*>(type);
return base->kind() == kind;
}
// The hacky conversion to/from AstType*.
static AstType* AsType(AstTypeBase* type) {
return reinterpret_cast<AstType*>(type);
}
static AstTypeBase* FromType(AstType* type) {
return reinterpret_cast<AstTypeBase*>(type);
}
private:
Kind kind_;
};
// -----------------------------------------------------------------------------
// Class types.
class AstClassType : public AstTypeBase {
public:
i::Handle<i::Map> Map() { return map_; }
private:
friend class AstType;
friend class AstBitsetType;
static AstType* New(i::Handle<i::Map> map, Zone* zone) {
return AsType(new (zone->New(sizeof(AstClassType)))
AstClassType(AstBitsetType::Lub(*map), map));
}
static AstClassType* cast(AstType* type) {
DCHECK(IsKind(type, kClass));
return static_cast<AstClassType*>(FromType(type));
}
AstClassType(AstBitsetType::bitset bitset, i::Handle<i::Map> map)
: AstTypeBase(kClass), bitset_(bitset), map_(map) {}
AstBitsetType::bitset Lub() { return bitset_; }
AstBitsetType::bitset bitset_;
Handle<i::Map> map_;
};
// -----------------------------------------------------------------------------
// Constant types.
class AstConstantType : public AstTypeBase {
public:
i::Handle<i::Object> Value() { return object_; }
private:
friend class AstType;
friend class AstBitsetType;
static AstType* New(i::Handle<i::Object> value, Zone* zone) {
AstBitsetType::bitset bitset = AstBitsetType::Lub(*value);
return AsType(new (zone->New(sizeof(AstConstantType)))
AstConstantType(bitset, value));
}
static AstConstantType* cast(AstType* type) {
DCHECK(IsKind(type, kConstant));
return static_cast<AstConstantType*>(FromType(type));
}
AstConstantType(AstBitsetType::bitset bitset, i::Handle<i::Object> object)
: AstTypeBase(kConstant), bitset_(bitset), object_(object) {}
AstBitsetType::bitset Lub() { return bitset_; }
AstBitsetType::bitset bitset_;
Handle<i::Object> object_;
};
// TODO(neis): Also cache value if numerical.
// TODO(neis): Allow restricting the representation.
// -----------------------------------------------------------------------------
// Range types.
class AstRangeType : public AstTypeBase {
public:
struct Limits {
double min;
double max;
Limits(double min, double max) : min(min), max(max) {}
explicit Limits(AstRangeType* range)
: min(range->Min()), max(range->Max()) {}
bool IsEmpty();
static Limits Empty() { return Limits(1, 0); }
static Limits Intersect(Limits lhs, Limits rhs);
static Limits Union(Limits lhs, Limits rhs);
};
double Min() { return limits_.min; }
double Max() { return limits_.max; }
private:
friend class AstType;
friend class AstBitsetType;
friend class AstUnionType;
static AstType* New(double min, double max,
AstBitsetType::bitset representation, Zone* zone) {
return New(Limits(min, max), representation, zone);
}
static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
}
static AstType* New(Limits lim, AstBitsetType::bitset representation,
Zone* zone) {
DCHECK(IsInteger(lim.min) && IsInteger(lim.max));
DCHECK(lim.min <= lim.max);
DCHECK(AST_REPRESENTATION(representation) == representation);
AstBitsetType::bitset bits =
AST_SEMANTIC(AstBitsetType::Lub(lim.min, lim.max)) | representation;
return AsType(new (zone->New(sizeof(AstRangeType)))
AstRangeType(bits, lim));
}
static AstRangeType* cast(AstType* type) {
DCHECK(IsKind(type, kRange));
return static_cast<AstRangeType*>(FromType(type));
}
AstRangeType(AstBitsetType::bitset bitset, Limits limits)
: AstTypeBase(kRange), bitset_(bitset), limits_(limits) {}
AstBitsetType::bitset Lub() { return bitset_; }
AstBitsetType::bitset bitset_;
Limits limits_;
};
// -----------------------------------------------------------------------------
// Context types.
class AstContextType : public AstTypeBase {
public:
AstType* Outer() { return outer_; }
private:
friend class AstType;
static AstType* New(AstType* outer, Zone* zone) {
return AsType(new (zone->New(sizeof(AstContextType)))
AstContextType(outer)); // NOLINT
}
static AstContextType* cast(AstType* type) {
DCHECK(IsKind(type, kContext));
return static_cast<AstContextType*>(FromType(type));
}
explicit AstContextType(AstType* outer)
: AstTypeBase(kContext), outer_(outer) {}
AstType* outer_;
};
// -----------------------------------------------------------------------------
// Array types.
class AstArrayType : public AstTypeBase {
public:
AstType* Element() { return element_; }
private:
friend class AstType;
explicit AstArrayType(AstType* element)
: AstTypeBase(kArray), element_(element) {}
static AstType* New(AstType* element, Zone* zone) {
return AsType(new (zone->New(sizeof(AstArrayType))) AstArrayType(element));
}
static AstArrayType* cast(AstType* type) {
DCHECK(IsKind(type, kArray));
return static_cast<AstArrayType*>(FromType(type));
}
AstType* element_;
};
// -----------------------------------------------------------------------------
// Superclass for types with variable number of type fields.
class AstStructuralType : public AstTypeBase {
public:
int LengthForTesting() { return Length(); }
protected:
friend class AstType;
int Length() { return length_; }
AstType* Get(int i) {
DCHECK(0 <= i && i < this->Length());
return elements_[i];
}
void Set(int i, AstType* type) {
DCHECK(0 <= i && i < this->Length());
elements_[i] = type;
}
void Shrink(int length) {
DCHECK(2 <= length && length <= this->Length());
length_ = length;
}
AstStructuralType(Kind kind, int length, i::Zone* zone)
: AstTypeBase(kind), length_(length) {
elements_ =
reinterpret_cast<AstType**>(zone->New(sizeof(AstType*) * length));
}
private:
int length_;
AstType** elements_;
};
// -----------------------------------------------------------------------------
// Function types.
class AstFunctionType : public AstStructuralType {
public:
int Arity() { return this->Length() - 2; }
AstType* Result() { return this->Get(0); }
AstType* Receiver() { return this->Get(1); }
AstType* Parameter(int i) { return this->Get(2 + i); }
void InitParameter(int i, AstType* type) { this->Set(2 + i, type); }
private:
friend class AstType;
AstFunctionType(AstType* result, AstType* receiver, int arity, Zone* zone)
: AstStructuralType(kFunction, 2 + arity, zone) {
Set(0, result);
Set(1, receiver);
}
static AstType* New(AstType* result, AstType* receiver, int arity,
Zone* zone) {
return AsType(new (zone->New(sizeof(AstFunctionType)))
AstFunctionType(result, receiver, arity, zone));
}
static AstFunctionType* cast(AstType* type) {
DCHECK(IsKind(type, kFunction));
return static_cast<AstFunctionType*>(FromType(type));
}
};
// -----------------------------------------------------------------------------
// Tuple types.
class AstTupleType : public AstStructuralType {
public:
int Arity() { return this->Length(); }
AstType* Element(int i) { return this->Get(i); }
void InitElement(int i, AstType* type) { this->Set(i, type); }
private:
friend class AstType;
AstTupleType(int length, Zone* zone)
: AstStructuralType(kTuple, length, zone) {}
static AstType* New(int length, Zone* zone) {
return AsType(new (zone->New(sizeof(AstTupleType)))
AstTupleType(length, zone));
}
static AstTupleType* cast(AstType* type) {
DCHECK(IsKind(type, kTuple));
return static_cast<AstTupleType*>(FromType(type));
}
};
// -----------------------------------------------------------------------------
// Union types (internal).
// A union is a structured type with the following invariants:
// - its length is at least 2
// - at most one field is a bitset, and it must go into index 0
// - no field is a union
// - no field is a subtype of any other field
class AstUnionType : public AstStructuralType {
private:
friend AstType;
friend AstBitsetType;
AstUnionType(int length, Zone* zone)
: AstStructuralType(kUnion, length, zone) {}
static AstType* New(int length, Zone* zone) {
return AsType(new (zone->New(sizeof(AstUnionType)))
AstUnionType(length, zone));
}
static AstUnionType* cast(AstType* type) {
DCHECK(IsKind(type, kUnion));
return static_cast<AstUnionType*>(FromType(type));
}
bool Wellformed();
};
class AstType {
public:
typedef AstBitsetType::bitset bitset; // Internal
// Constructors.
#define DEFINE_TYPE_CONSTRUCTOR(type, value) \
static AstType* type() { return AstBitsetType::New(AstBitsetType::k##type); }
AST_PROPER_BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
#undef DEFINE_TYPE_CONSTRUCTOR
static AstType* SignedSmall() {
return AstBitsetType::New(AstBitsetType::SignedSmall());
}
static AstType* UnsignedSmall() {
return AstBitsetType::New(AstBitsetType::UnsignedSmall());
}
static AstType* Class(i::Handle<i::Map> map, Zone* zone) {
return AstClassType::New(map, zone);
}
static AstType* Constant(i::Handle<i::Object> value, Zone* zone) {
return AstConstantType::New(value, zone);
}
static AstType* Range(double min, double max, Zone* zone) {
return AstRangeType::New(min, max,
AST_REPRESENTATION(AstBitsetType::kTagged |
AstBitsetType::kUntaggedNumber),
zone);
}
static AstType* Context(AstType* outer, Zone* zone) {
return AstContextType::New(outer, zone);
}
static AstType* Array(AstType* element, Zone* zone) {
return AstArrayType::New(element, zone);
}
static AstType* Function(AstType* result, AstType* receiver, int arity,
Zone* zone) {
return AstFunctionType::New(result, receiver, arity, zone);
}
static AstType* Function(AstType* result, Zone* zone) {
return Function(result, Any(), 0, zone);
}
static AstType* Function(AstType* result, AstType* param0, Zone* zone) {
AstType* function = Function(result, Any(), 1, zone);
function->AsFunction()->InitParameter(0, param0);
return function;
}
static AstType* Function(AstType* result, AstType* param0, AstType* param1,
Zone* zone) {
AstType* function = Function(result, Any(), 2, zone);
function->AsFunction()->InitParameter(0, param0);
function->AsFunction()->InitParameter(1, param1);
return function;
}
static AstType* Function(AstType* result, AstType* param0, AstType* param1,
AstType* param2, Zone* zone) {
AstType* function = Function(result, Any(), 3, zone);
function->AsFunction()->InitParameter(0, param0);
function->AsFunction()->InitParameter(1, param1);
function->AsFunction()->InitParameter(2, param2);
return function;
}
static AstType* Function(AstType* result, int arity, AstType** params,
Zone* zone) {
AstType* function = Function(result, Any(), arity, zone);
for (int i = 0; i < arity; ++i) {
function->AsFunction()->InitParameter(i, params[i]);
}
return function;
}
static AstType* Tuple(AstType* first, AstType* second, AstType* third,
Zone* zone) {
AstType* tuple = AstTupleType::New(3, zone);
tuple->AsTuple()->InitElement(0, first);
tuple->AsTuple()->InitElement(1, second);
tuple->AsTuple()->InitElement(2, third);
return tuple;
}
static AstType* Union(AstType* type1, AstType* type2, Zone* zone);
static AstType* Intersect(AstType* type1, AstType* type2, Zone* zone);
static AstType* Of(double value, Zone* zone) {
return AstBitsetType::New(
AstBitsetType::ExpandInternals(AstBitsetType::Lub(value)));
}
static AstType* Of(i::Object* value, Zone* zone) {
return AstBitsetType::New(
AstBitsetType::ExpandInternals(AstBitsetType::Lub(value)));
}
static AstType* Of(i::Handle<i::Object> value, Zone* zone) {
return Of(*value, zone);
}
static AstType* For(i::Map* map) {
return AstBitsetType::New(
AstBitsetType::ExpandInternals(AstBitsetType::Lub(map)));
}
static AstType* For(i::Handle<i::Map> map) { return For(*map); }
// Extraction of components.
static AstType* Representation(AstType* t, Zone* zone);
static AstType* Semantic(AstType* t, Zone* zone);
// Predicates.
bool IsInhabited() { return AstBitsetType::IsInhabited(this->BitsetLub()); }
bool Is(AstType* that) { return this == that || this->SlowIs(that); }
bool Maybe(AstType* that);
bool Equals(AstType* that) { return this->Is(that) && that->Is(this); }
// Equivalent to Constant(val)->Is(this), but avoiding allocation.
bool Contains(i::Object* val);
bool Contains(i::Handle<i::Object> val) { return this->Contains(*val); }
// State-dependent versions of the above that consider subtyping between
// a constant and its map class.
static AstType* NowOf(i::Object* value, Zone* zone);
static AstType* NowOf(i::Handle<i::Object> value, Zone* zone) {
return NowOf(*value, zone);
}
bool NowIs(AstType* that);
bool NowContains(i::Object* val);
bool NowContains(i::Handle<i::Object> val) { return this->NowContains(*val); }
bool NowStable();
// Inspection.
bool IsRange() { return IsKind(AstTypeBase::kRange); }
bool IsClass() { return IsKind(AstTypeBase::kClass); }
bool IsConstant() { return IsKind(AstTypeBase::kConstant); }
bool IsContext() { return IsKind(AstTypeBase::kContext); }
bool IsArray() { return IsKind(AstTypeBase::kArray); }
bool IsFunction() { return IsKind(AstTypeBase::kFunction); }
bool IsTuple() { return IsKind(AstTypeBase::kTuple); }
AstClassType* AsClass() { return AstClassType::cast(this); }
AstConstantType* AsConstant() { return AstConstantType::cast(this); }
AstRangeType* AsRange() { return AstRangeType::cast(this); }
AstContextType* AsContext() { return AstContextType::cast(this); }
AstArrayType* AsArray() { return AstArrayType::cast(this); }
AstFunctionType* AsFunction() { return AstFunctionType::cast(this); }
AstTupleType* AsTuple() { return AstTupleType::cast(this); }
// Minimum and maximum of a numeric type.
// These functions do not distinguish between -0 and +0. If the type equals
// kNaN, they return NaN; otherwise kNaN is ignored. Only call these
// functions on subtypes of Number.
double Min();
double Max();
// Extracts a range from the type: if the type is a range or a union
// containing a range, that range is returned; otherwise, NULL is returned.
AstType* GetRange();
static bool IsInteger(i::Object* x);
static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
}
int NumClasses();
int NumConstants();
template <class T>
class Iterator {
public:
bool Done() const { return index_ < 0; }
i::Handle<T> Current();
void Advance();
private:
friend class AstType;
Iterator() : index_(-1) {}
explicit Iterator(AstType* type) : type_(type), index_(-1) { Advance(); }
inline bool matches(AstType* type);
inline AstType* get_type();
AstType* type_;
int index_;
};
Iterator<i::Map> Classes() {
if (this->IsBitset()) return Iterator<i::Map>();
return Iterator<i::Map>(this);
}
Iterator<i::Object> Constants() {
if (this->IsBitset()) return Iterator<i::Object>();
return Iterator<i::Object>(this);
}
// Printing.
enum PrintDimension { BOTH_DIMS, SEMANTIC_DIM, REPRESENTATION_DIM };
void PrintTo(std::ostream& os, PrintDimension dim = BOTH_DIMS); // NOLINT
#ifdef DEBUG
void Print();
#endif
// Helpers for testing.
bool IsBitsetForTesting() { return IsBitset(); }
bool IsUnionForTesting() { return IsUnion(); }
bitset AsBitsetForTesting() { return AsBitset(); }
AstUnionType* AsUnionForTesting() { return AsUnion(); }
private:
// Friends.
template <class>
friend class Iterator;
friend AstBitsetType;
friend AstUnionType;
// Internal inspection.
bool IsKind(AstTypeBase::Kind kind) {
return AstTypeBase::IsKind(this, kind);
}
bool IsNone() { return this == None(); }
bool IsAny() { return this == Any(); }
bool IsBitset() { return AstBitsetType::IsBitset(this); }
bool IsUnion() { return IsKind(AstTypeBase::kUnion); }
bitset AsBitset() {
DCHECK(this->IsBitset());
return reinterpret_cast<AstBitsetType*>(this)->Bitset();
}
AstUnionType* AsUnion() { return AstUnionType::cast(this); }
bitset Representation();
// Auxiliary functions.
bool SemanticMaybe(AstType* that);
bitset BitsetGlb() { return AstBitsetType::Glb(this); }
bitset BitsetLub() { return AstBitsetType::Lub(this); }
bool SlowIs(AstType* that);
bool SemanticIs(AstType* that);
static bool Overlap(AstRangeType* lhs, AstRangeType* rhs);
static bool Contains(AstRangeType* lhs, AstRangeType* rhs);
static bool Contains(AstRangeType* range, AstConstantType* constant);
static bool Contains(AstRangeType* range, i::Object* val);
static int UpdateRange(AstType* type, AstUnionType* result, int size,
Zone* zone);
static AstRangeType::Limits IntersectRangeAndBitset(AstType* range,
AstType* bits,
Zone* zone);
static AstRangeType::Limits ToLimits(bitset bits, Zone* zone);
bool SimplyEquals(AstType* that);
static int AddToUnion(AstType* type, AstUnionType* result, int size,
Zone* zone);
static int IntersectAux(AstType* type, AstType* other, AstUnionType* result,
int size, AstRangeType::Limits* limits, Zone* zone);
static AstType* NormalizeUnion(AstType* unioned, int size, Zone* zone);
static AstType* NormalizeRangeAndBitset(AstType* range, bitset* bits,
Zone* zone);
};
// -----------------------------------------------------------------------------
// Type bounds. A simple struct to represent a pair of lower/upper types.
struct AstBounds {
AstType* lower;
AstType* upper;
AstBounds()
: // Make sure accessing uninitialized bounds crashes big-time.
lower(nullptr),
upper(nullptr) {}
explicit AstBounds(AstType* t) : lower(t), upper(t) {}
AstBounds(AstType* l, AstType* u) : lower(l), upper(u) {
DCHECK(lower->Is(upper));
}
// Unrestricted bounds.
static AstBounds Unbounded() {
return AstBounds(AstType::None(), AstType::Any());
}
// Meet: both b1 and b2 are known to hold.
static AstBounds Both(AstBounds b1, AstBounds b2, Zone* zone) {
AstType* lower = AstType::Union(b1.lower, b2.lower, zone);
AstType* upper = AstType::Intersect(b1.upper, b2.upper, zone);
// Lower bounds are considered approximate, correct as necessary.
if (!lower->Is(upper)) lower = upper;
return AstBounds(lower, upper);
}
// Join: either b1 or b2 is known to hold.
static AstBounds Either(AstBounds b1, AstBounds b2, Zone* zone) {
AstType* lower = AstType::Intersect(b1.lower, b2.lower, zone);
AstType* upper = AstType::Union(b1.upper, b2.upper, zone);
return AstBounds(lower, upper);
}
static AstBounds NarrowLower(AstBounds b, AstType* t, Zone* zone) {
AstType* lower = AstType::Union(b.lower, t, zone);
// Lower bounds are considered approximate, correct as necessary.
if (!lower->Is(b.upper)) lower = b.upper;
return AstBounds(lower, b.upper);
}
static AstBounds NarrowUpper(AstBounds b, AstType* t, Zone* zone) {
AstType* lower = b.lower;
AstType* upper = AstType::Intersect(b.upper, t, zone);
// Lower bounds are considered approximate, correct as necessary.
if (!lower->Is(upper)) lower = upper;
return AstBounds(lower, upper);
}
bool Narrows(AstBounds that) {
return that.lower->Is(this->lower) && this->upper->Is(that.upper);
}
};
} // namespace internal
} // namespace v8
#endif // V8_AST_AST_TYPES_H_
......@@ -5,7 +5,6 @@
#ifndef V8_AST_AST_H_
#define V8_AST_AST_H_
#include "src/ast/ast-types.h"
#include "src/ast/ast-value-factory.h"
#include "src/ast/modules.h"
#include "src/ast/variables.h"
......
......@@ -4,7 +4,6 @@
#include "src/field-type.h"
#include "src/ast/ast-types.h"
#include "src/handles-inl.h"
#include "src/objects-inl.h"
#include "src/ostreams.h"
......@@ -72,13 +71,6 @@ bool FieldType::NowIs(FieldType* other) {
bool FieldType::NowIs(Handle<FieldType> other) { return NowIs(*other); }
AstType* FieldType::Convert(Zone* zone) {
if (IsAny()) return AstType::NonInternal();
if (IsNone()) return AstType::None();
DCHECK(IsClass());
return AstType::Class(AsClass(), zone);
}
void FieldType::PrintTo(std::ostream& os) {
if (IsAny()) {
os << "Any";
......
......@@ -5,7 +5,6 @@
#ifndef V8_FIELD_TYPE_H_
#define V8_FIELD_TYPE_H_
#include "src/ast/ast-types.h"
#include "src/objects.h"
#include "src/objects/map.h"
#include "src/ostreams.h"
......@@ -42,7 +41,6 @@ class FieldType : public Object {
bool NowStable();
bool NowIs(FieldType* other);
bool NowIs(Handle<FieldType> other);
AstType* Convert(Zone* zone);
void PrintTo(std::ostream& os);
};
......
......@@ -4,7 +4,6 @@
#include "src/ic/ic-state.h"
#include "src/ast/ast-types.h"
#include "src/feedback-vector.h"
#include "src/ic/ic.h"
#include "src/objects-inl.h"
......@@ -45,32 +44,6 @@ const char* CompareICState::GetStateName(State state) {
UNREACHABLE();
}
AstType* CompareICState::StateToType(Zone* zone, State state, Handle<Map> map) {
switch (state) {
case UNINITIALIZED:
return AstType::None();
case BOOLEAN:
return AstType::Boolean();
case SMI:
return AstType::SignedSmall();
case NUMBER:
return AstType::Number();
case STRING:
return AstType::String();
case INTERNALIZED_STRING:
return AstType::InternalizedString();
case UNIQUE_NAME:
return AstType::UniqueName();
case RECEIVER:
return AstType::Receiver();
case KNOWN_RECEIVER:
return map.is_null() ? AstType::Receiver() : AstType::Class(map, zone);
case GENERIC:
return AstType::Any();
}
UNREACHABLE();
}
CompareICState::State CompareICState::NewInputState(State old_state,
Handle<Object> value) {
......
......@@ -11,8 +11,6 @@
namespace v8 {
namespace internal {
class AstType;
const int kMaxKeyedPolymorphism = 4;
......@@ -45,9 +43,6 @@ class CompareICState {
GENERIC
};
static AstType* StateToType(Zone* zone, State state,
Handle<Map> map = Handle<Map>());
static State NewInputState(State old_state, Handle<Object> value);
static const char* GetStateName(CompareICState::State state);
......
......@@ -5,7 +5,7 @@
#ifndef V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_
#define V8_PARSING_PARAMETER_EXPRESSION_REWRITER_H_
#include "src/ast/ast-types.h"
#include <stdint.h>
namespace v8 {
namespace internal {
......
......@@ -593,9 +593,6 @@
'ast/ast-numbering.cc',
'ast/ast-numbering.h',
'ast/ast-traversal-visitor.h',
'ast/ast-type-bounds.h',
'ast/ast-types.cc',
'ast/ast-types.h',
'ast/ast-value-factory.cc',
'ast/ast-value-factory.h',
'ast/ast.cc',
......
......@@ -14,7 +14,6 @@ v8_executable("cctest") {
"../../test/common/wasm/flag-utils.h",
"../../test/common/wasm/test-signatures.h",
"../../test/common/wasm/wasm-macro-gen.h",
"ast-types-fuzz.h",
"cctest.cc",
"cctest.h",
"compiler/c-signature.h",
......@@ -113,7 +112,6 @@ v8_executable("cctest") {
"test-api.cc",
"test-api.h",
"test-array-list.cc",
"test-ast-types.cc",
"test-ast.cc",
"test-atomicops.cc",
"test-bignum-dtoa.cc",
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_TEST_CCTEST_TYPES_H_
#define V8_TEST_CCTEST_TYPES_H_
#include "src/base/utils/random-number-generator.h"
#include "src/factory.h"
#include "src/isolate.h"
#include "src/v8.h"
namespace v8 {
namespace internal {
class AstTypes {
public:
AstTypes(Zone* zone, Isolate* isolate, v8::base::RandomNumberGenerator* rng)
: zone_(zone), isolate_(isolate), rng_(rng) {
#define DECLARE_TYPE(name, value) \
name = AstType::name(); \
types.push_back(name);
AST_PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
SignedSmall = AstType::SignedSmall();
UnsignedSmall = AstType::UnsignedSmall();
object_map =
isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
array_map = isolate->factory()->NewMap(JS_ARRAY_TYPE, JSArray::kSize);
number_map =
isolate->factory()->NewMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
uninitialized_map = isolate->factory()->uninitialized_map();
ObjectClass = AstType::Class(object_map, zone);
ArrayClass = AstType::Class(array_map, zone);
NumberClass = AstType::Class(number_map, zone);
UninitializedClass = AstType::Class(uninitialized_map, zone);
maps.push_back(object_map);
maps.push_back(array_map);
maps.push_back(uninitialized_map);
for (MapVector::iterator it = maps.begin(); it != maps.end(); ++it) {
types.push_back(AstType::Class(*it, zone));
}
smi = handle(Smi::FromInt(666), isolate);
signed32 = isolate->factory()->NewHeapNumber(0x40000000);
object1 = isolate->factory()->NewJSObjectFromMap(object_map);
object2 = isolate->factory()->NewJSObjectFromMap(object_map);
array = isolate->factory()->NewJSArray(20);
uninitialized = isolate->factory()->uninitialized_value();
SmiConstant = AstType::Constant(smi, zone);
Signed32Constant = AstType::Constant(signed32, zone);
ObjectConstant1 = AstType::Constant(object1, zone);
ObjectConstant2 = AstType::Constant(object2, zone);
ArrayConstant = AstType::Constant(array, zone);
UninitializedConstant = AstType::Constant(uninitialized, zone);
values.push_back(smi);
values.push_back(signed32);
values.push_back(object1);
values.push_back(object2);
values.push_back(array);
values.push_back(uninitialized);
for (ValueVector::iterator it = values.begin(); it != values.end(); ++it) {
types.push_back(AstType::Constant(*it, zone));
}
integers.push_back(isolate->factory()->NewNumber(-V8_INFINITY));
integers.push_back(isolate->factory()->NewNumber(+V8_INFINITY));
integers.push_back(isolate->factory()->NewNumber(-rng_->NextInt(10)));
integers.push_back(isolate->factory()->NewNumber(+rng_->NextInt(10)));
for (int i = 0; i < 10; ++i) {
double x = rng_->NextInt();
integers.push_back(isolate->factory()->NewNumber(x));
x *= rng_->NextInt();
if (!IsMinusZero(x)) integers.push_back(isolate->factory()->NewNumber(x));
}
Integer = AstType::Range(-V8_INFINITY, +V8_INFINITY, zone);
NumberArray = AstType::Array(Number, zone);
StringArray = AstType::Array(String, zone);
AnyArray = AstType::Array(Any, zone);
SignedFunction1 = AstType::Function(SignedSmall, SignedSmall, zone);
NumberFunction1 = AstType::Function(Number, Number, zone);
NumberFunction2 = AstType::Function(Number, Number, Number, zone);
MethodFunction = AstType::Function(String, Object, 0, zone);
for (int i = 0; i < 30; ++i) {
types.push_back(Fuzz());
}
USE(isolate_); // Currently unused.
}
Handle<i::Map> object_map;
Handle<i::Map> array_map;
Handle<i::Map> number_map;
Handle<i::Map> uninitialized_map;
Handle<i::Smi> smi;
Handle<i::HeapNumber> signed32;
Handle<i::JSObject> object1;
Handle<i::JSObject> object2;
Handle<i::JSArray> array;
Handle<i::Oddball> uninitialized;
#define DECLARE_TYPE(name, value) AstType* name;
AST_PROPER_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
#define DECLARE_TYPE(name, value) AstType* Mask##name##ForTesting;
AST_MASK_BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
AstType* SignedSmall;
AstType* UnsignedSmall;
AstType* ObjectClass;
AstType* ArrayClass;
AstType* NumberClass;
AstType* UninitializedClass;
AstType* SmiConstant;
AstType* Signed32Constant;
AstType* ObjectConstant1;
AstType* ObjectConstant2;
AstType* ArrayConstant;
AstType* UninitializedConstant;
AstType* Integer;
AstType* NumberArray;
AstType* StringArray;
AstType* AnyArray;
AstType* SignedFunction1;
AstType* NumberFunction1;
AstType* NumberFunction2;
AstType* MethodFunction;
typedef std::vector<AstType*> TypeVector;
typedef std::vector<Handle<i::Map> > MapVector;
typedef std::vector<Handle<i::Object> > ValueVector;
TypeVector types;
MapVector maps;
ValueVector values;
ValueVector integers; // "Integer" values used for range limits.
AstType* Of(Handle<i::Object> value) { return AstType::Of(value, zone_); }
AstType* NowOf(Handle<i::Object> value) {
return AstType::NowOf(value, zone_);
}
AstType* Class(Handle<i::Map> map) { return AstType::Class(map, zone_); }
AstType* Constant(Handle<i::Object> value) {
return AstType::Constant(value, zone_);
}
AstType* Range(double min, double max) {
return AstType::Range(min, max, zone_);
}
AstType* Context(AstType* outer) { return AstType::Context(outer, zone_); }
AstType* Array1(AstType* element) { return AstType::Array(element, zone_); }
AstType* Function0(AstType* result, AstType* receiver) {
return AstType::Function(result, receiver, 0, zone_);
}
AstType* Function1(AstType* result, AstType* receiver, AstType* arg) {
AstType* type = AstType::Function(result, receiver, 1, zone_);
type->AsFunction()->InitParameter(0, arg);
return type;
}
AstType* Function2(AstType* result, AstType* arg1, AstType* arg2) {
return AstType::Function(result, arg1, arg2, zone_);
}
AstType* Union(AstType* t1, AstType* t2) {
return AstType::Union(t1, t2, zone_);
}
AstType* Intersect(AstType* t1, AstType* t2) {
return AstType::Intersect(t1, t2, zone_);
}
AstType* Representation(AstType* t) {
return AstType::Representation(t, zone_);
}
AstType* Semantic(AstType* t) { return AstType::Semantic(t, zone_); }
AstType* Random() {
return types[rng_->NextInt(static_cast<int>(types.size()))];
}
AstType* Fuzz(int depth = 4) {
switch (rng_->NextInt(depth == 0 ? 3 : 20)) {
case 0: { // bitset
#define COUNT_BITSET_TYPES(type, value) +1
int n = 0 AST_PROPER_BITSET_TYPE_LIST(COUNT_BITSET_TYPES);
#undef COUNT_BITSET_TYPES
// Pick a bunch of named bitsets and return their intersection.
AstType* result = AstType::Any();
for (int i = 0, m = 1 + rng_->NextInt(3); i < m; ++i) {
int j = rng_->NextInt(n);
#define PICK_BITSET_TYPE(type, value) \
if (j-- == 0) { \
AstType* tmp = AstType::Intersect(result, AstType::type(), zone_); \
if (tmp->Is(AstType::None()) && i != 0) { \
break; \
} else { \
result = tmp; \
continue; \
} \
}
AST_PROPER_BITSET_TYPE_LIST(PICK_BITSET_TYPE)
#undef PICK_BITSET_TYPE
}
return result;
}
case 1: { // class
int i = rng_->NextInt(static_cast<int>(maps.size()));
return AstType::Class(maps[i], zone_);
}
case 2: { // constant
int i = rng_->NextInt(static_cast<int>(values.size()));
return AstType::Constant(values[i], zone_);
}
case 3: { // range
int i = rng_->NextInt(static_cast<int>(integers.size()));
int j = rng_->NextInt(static_cast<int>(integers.size()));
double min = integers[i]->Number();
double max = integers[j]->Number();
if (min > max) std::swap(min, max);
return AstType::Range(min, max, zone_);
}
case 4: { // context
int depth = rng_->NextInt(3);
AstType* type = AstType::Internal();
for (int i = 0; i < depth; ++i) type = AstType::Context(type, zone_);
return type;
}
case 5: { // array
AstType* element = Fuzz(depth / 2);
return AstType::Array(element, zone_);
}
case 6:
case 7: { // function
AstType* result = Fuzz(depth / 2);
AstType* receiver = Fuzz(depth / 2);
int arity = rng_->NextInt(3);
AstType* type = AstType::Function(result, receiver, arity, zone_);
for (int i = 0; i < type->AsFunction()->Arity(); ++i) {
AstType* parameter = Fuzz(depth / 2);
type->AsFunction()->InitParameter(i, parameter);
}
return type;
}
default: { // union
int n = rng_->NextInt(10);
AstType* type = None;
for (int i = 0; i < n; ++i) {
AstType* operand = Fuzz(depth - 1);
type = AstType::Union(type, operand, zone_);
}
return type;
}
}
UNREACHABLE();
}
Zone* zone() { return zone_; }
private:
Zone* zone_;
Isolate* isolate_;
v8::base::RandomNumberGenerator* rng_;
};
} // namespace internal
} // namespace v8
#endif
......@@ -32,7 +32,6 @@
'v8_code': 1,
'generated_file': '<(SHARED_INTERMEDIATE_DIR)/resources.cc',
'cctest_sources': [ ### gcmole(all) ###
'ast-types-fuzz.h',
'compiler/c-signature.h',
'compiler/call-tester.h',
'compiler/codegen-tester.cc',
......@@ -193,7 +192,6 @@
'test-traced-value.cc',
'test-transitions.cc',
'test-typedarrays.cc',
'test-ast-types.cc',
'test-types.cc',
'test-unbound-queue.cc',
'test-unboxed-doubles.cc',
......
......@@ -64,9 +64,7 @@
'test-parsing/ParserSync': [PASS, NO_VARIANTS],
# This tests only the type system, no point in running several variants.
'test-hydrogen-types/*': [PASS, NO_VARIANTS],
'test-types/*': [PASS, NO_VARIANTS],
'test-ast-types/*': [PASS, NO_VARIANTS],
# This tests API threading, no point in running several variants.
'test-api/Threading*': [PASS, SLOW, NO_VARIANTS],
......
// 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 <vector>
#include "src/factory.h"
#include "src/heap/heap.h"
#include "src/isolate.h"
// FIXME(mstarzinger, marja): This is weird, but required because of the missing
// (disallowed) include: src/factory.h -> src/objects-inl.h
#include "src/ast/ast-types.h"
#include "src/objects-inl.h"
// FIXME(mstarzinger, marja): This is weird, but required because of the missing
// (disallowed) include: src/feedback-vector.h ->
// src/feedback-vector-inl.h
#include "src/feedback-vector-inl.h"
#include "test/cctest/ast-types-fuzz.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
namespace {
// Testing auxiliaries (breaking the Type abstraction).
static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
}
static bool IsInteger(i::Object* x) {
return x->IsNumber() && IsInteger(x->Number());
}
typedef uint32_t bitset;
struct Tests {
typedef AstTypes::TypeVector::iterator TypeIterator;
typedef AstTypes::MapVector::iterator MapIterator;
typedef AstTypes::ValueVector::iterator ValueIterator;
Isolate* isolate;
HandleScope scope;
Zone zone;
AstTypes T;
Tests()
: isolate(CcTest::InitIsolateOnce()),
scope(isolate),
zone(isolate->allocator(), ZONE_NAME),
T(&zone, isolate, isolate->random_number_generator()) {}
bool IsBitset(AstType* type) { return type->IsBitsetForTesting(); }
bool IsUnion(AstType* type) { return type->IsUnionForTesting(); }
AstBitsetType::bitset AsBitset(AstType* type) {
return type->AsBitsetForTesting();
}
AstUnionType* AsUnion(AstType* type) { return type->AsUnionForTesting(); }
bool Equal(AstType* type1, AstType* type2) {
return type1->Equals(type2) &&
this->IsBitset(type1) == this->IsBitset(type2) &&
this->IsUnion(type1) == this->IsUnion(type2) &&
type1->NumClasses() == type2->NumClasses() &&
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(AstType* type1, AstType* type2) {
CHECK(Equal(type1, type2));
}
void CheckSub(AstType* type1, AstType* 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(AstType* type1, AstType* type2) {
CHECK(type1->Is(type2));
if (this->IsBitset(type1) && this->IsBitset(type2)) {
CHECK((this->AsBitset(type1) | this->AsBitset(type2)) ==
this->AsBitset(type2));
}
}
void CheckUnordered(AstType* type1, AstType* 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(AstType* type1, AstType* type2) {
CHECK(type1->Maybe(type2));
CHECK(type2->Maybe(type1));
}
void CheckDisjoint(AstType* type1, AstType* 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) {
AstType* t = *it;
CHECK(1 ==
this->IsBitset(t) + t->IsClass() + t->IsConstant() + t->IsRange() +
this->IsUnion(t) + t->IsArray() + t->IsFunction() +
t->IsContext());
}
}
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
if (this->IsBitset(type1) && this->IsBitset(type2)) {
AstType* intersect12 = T.Intersect(type1, type2);
bitset bits = this->AsBitset(type1) & this->AsBitset(type2);
CHECK(bits == this->AsBitset(intersect12));
}
}
}
}
void PointwiseRepresentation() {
// Check we can decompose type into semantics and representation and
// then compose it back to get an equivalent type.
int counter = 0;
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
counter++;
printf("Counter: %i\n", counter);
fflush(stdout);
AstType* type1 = *it1;
AstType* representation = T.Representation(type1);
AstType* semantic = T.Semantic(type1);
AstType* composed = T.Union(representation, semantic);
CHECK(type1->Equals(composed));
}
// Pointwiseness of Union.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* representation1 = T.Representation(type1);
AstType* semantic1 = T.Semantic(type1);
AstType* representation2 = T.Representation(type2);
AstType* semantic2 = T.Semantic(type2);
AstType* direct_union = T.Union(type1, type2);
AstType* representation_union =
T.Union(representation1, representation2);
AstType* semantic_union = T.Union(semantic1, semantic2);
AstType* composed_union = T.Union(representation_union, semantic_union);
CHECK(direct_union->Equals(composed_union));
}
}
// Pointwiseness of Intersect.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* representation1 = T.Representation(type1);
AstType* semantic1 = T.Semantic(type1);
AstType* representation2 = T.Representation(type2);
AstType* semantic2 = T.Semantic(type2);
AstType* direct_intersection = T.Intersect(type1, type2);
AstType* representation_intersection =
T.Intersect(representation1, representation2);
AstType* semantic_intersection = T.Intersect(semantic1, semantic2);
AstType* composed_intersection =
T.Union(representation_intersection, semantic_intersection);
CHECK(direct_intersection->Equals(composed_intersection));
}
}
// Pointwiseness of Is.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* representation1 = T.Representation(type1);
AstType* semantic1 = T.Semantic(type1);
AstType* representation2 = T.Representation(type2);
AstType* semantic2 = T.Semantic(type2);
bool representation_is = representation1->Is(representation2);
bool semantic_is = semantic1->Is(semantic2);
bool direct_is = type1->Is(type2);
CHECK(direct_is == (semantic_is && representation_is));
}
}
}
void Class() {
// Constructor
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
Handle<i::Map> map = *mt;
AstType* type = T.Class(map);
CHECK(type->IsClass());
}
// Map attribute
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
Handle<i::Map> map = *mt;
AstType* type = T.Class(map);
CHECK(*map == *type->AsClass()->Map());
}
// Functionality & Injectivity: Class(M1) = Class(M2) iff M1 = M2
for (MapIterator mt1 = T.maps.begin(); mt1 != T.maps.end(); ++mt1) {
for (MapIterator mt2 = T.maps.begin(); mt2 != T.maps.end(); ++mt2) {
Handle<i::Map> map1 = *mt1;
Handle<i::Map> map2 = *mt2;
AstType* type1 = T.Class(map1);
AstType* type2 = T.Class(map2);
CHECK(Equal(type1, type2) == (*map1 == *map2));
}
}
}
void Constant() {
// Constructor
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Object> value = *vt;
AstType* type = T.Constant(value);
CHECK(type->IsConstant());
}
// Value attribute
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Object> value = *vt;
AstType* type = T.Constant(value);
CHECK(*value == *type->AsConstant()->Value());
}
// 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<i::Object> value1 = *vt1;
Handle<i::Object> value2 = *vt2;
AstType* type1 = T.Constant(value1);
AstType* type2 = T.Constant(value2);
CHECK(Equal(type1, type2) == (*value1 == *value2));
}
}
// Typing of numbers
Factory* fac = isolate->factory();
CHECK(T.Constant(fac->NewNumber(0))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(1))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(0x3fffffff))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(-1))->Is(T.Negative31));
CHECK(T.Constant(fac->NewNumber(-0x3fffffff))->Is(T.Negative31));
CHECK(T.Constant(fac->NewNumber(-0x40000000))->Is(T.Negative31));
CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.Unsigned31));
CHECK(!T.Constant(fac->NewNumber(0x40000000))->Is(T.Unsigned30));
CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.Unsigned31));
CHECK(!T.Constant(fac->NewNumber(0x7fffffff))->Is(T.Unsigned30));
CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.Negative32));
CHECK(!T.Constant(fac->NewNumber(-0x40000001))->Is(T.Negative31));
CHECK(T.Constant(fac->NewNumber(-0x7fffffff))->Is(T.Negative32));
CHECK(!T.Constant(fac->NewNumber(-0x7fffffff - 1))->Is(T.Negative31));
if (SmiValuesAre31Bits()) {
CHECK(!T.Constant(fac->NewNumber(0x40000000))->Is(T.UnsignedSmall));
CHECK(!T.Constant(fac->NewNumber(0x7fffffff))->Is(T.UnsignedSmall));
CHECK(!T.Constant(fac->NewNumber(-0x40000001))->Is(T.SignedSmall));
CHECK(!T.Constant(fac->NewNumber(-0x7fffffff - 1))->Is(T.SignedSmall));
} else {
CHECK(SmiValuesAre32Bits());
CHECK(T.Constant(fac->NewNumber(0x40000000))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(0x7fffffff))->Is(T.UnsignedSmall));
CHECK(T.Constant(fac->NewNumber(-0x40000001))->Is(T.SignedSmall));
CHECK(T.Constant(fac->NewNumber(-0x7fffffff - 1))->Is(T.SignedSmall));
}
CHECK(T.Constant(fac->NewNumber(0x80000000u))->Is(T.Unsigned32));
CHECK(!T.Constant(fac->NewNumber(0x80000000u))->Is(T.Unsigned31));
CHECK(T.Constant(fac->NewNumber(0xffffffffu))->Is(T.Unsigned32));
CHECK(!T.Constant(fac->NewNumber(0xffffffffu))->Is(T.Unsigned31));
CHECK(T.Constant(fac->NewNumber(0xffffffffu + 1.0))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(0xffffffffu + 1.0))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(-0x7fffffff - 2.0))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(-0x7fffffff - 2.0))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(0.1))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(0.1))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(-10.1))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(-10.1))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(10e60))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(10e60))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(-1.0 * 0.0))->Is(T.MinusZero));
CHECK(T.Constant(fac->NewNumber(std::numeric_limits<double>::quiet_NaN()))
->Is(T.NaN));
CHECK(T.Constant(fac->NewNumber(V8_INFINITY))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(V8_INFINITY))->Is(T.Integral32));
CHECK(T.Constant(fac->NewNumber(-V8_INFINITY))->Is(T.PlainNumber));
CHECK(!T.Constant(fac->NewNumber(-V8_INFINITY))->Is(T.Integral32));
}
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);
AstType* 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);
AstType* 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);
AstType* type1 = T.Range(min1, max1);
AstType* type2 = T.Range(min2, max2);
CHECK(Equal(type1, type2) == (min1 == min2 && max1 == max2));
}
}
}
}
}
void Context() {
// Constructor
for (int i = 0; i < 20; ++i) {
AstType* type = T.Random();
AstType* context = T.Context(type);
CHECK(context->IsContext());
}
// Attributes
for (int i = 0; i < 20; ++i) {
AstType* type = T.Random();
AstType* context = T.Context(type);
CheckEqual(type, context->AsContext()->Outer());
}
// Functionality & Injectivity: Context(T1) = Context(T2) iff T1 = T2
for (int i = 0; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
AstType* type1 = T.Random();
AstType* type2 = T.Random();
AstType* context1 = T.Context(type1);
AstType* context2 = T.Context(type2);
CHECK(Equal(context1, context2) == Equal(type1, type2));
}
}
}
void Array() {
// Constructor
for (int i = 0; i < 20; ++i) {
AstType* type = T.Random();
AstType* array = T.Array1(type);
CHECK(array->IsArray());
}
// Attributes
for (int i = 0; i < 20; ++i) {
AstType* type = T.Random();
AstType* array = T.Array1(type);
CheckEqual(type, array->AsArray()->Element());
}
// Functionality & Injectivity: Array(T1) = Array(T2) iff T1 = T2
for (int i = 0; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
AstType* type1 = T.Random();
AstType* type2 = T.Random();
AstType* array1 = T.Array1(type1);
AstType* array2 = T.Array1(type2);
CHECK(Equal(array1, array2) == Equal(type1, type2));
}
}
}
void Function() {
// Constructors
for (int i = 0; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
for (int k = 0; k < 20; ++k) {
AstType* type1 = T.Random();
AstType* type2 = T.Random();
AstType* type3 = T.Random();
AstType* function0 = T.Function0(type1, type2);
AstType* function1 = T.Function1(type1, type2, type3);
AstType* function2 = T.Function2(type1, type2, type3);
CHECK(function0->IsFunction());
CHECK(function1->IsFunction());
CHECK(function2->IsFunction());
}
}
}
// Attributes
for (int i = 0; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
for (int k = 0; k < 20; ++k) {
AstType* type1 = T.Random();
AstType* type2 = T.Random();
AstType* type3 = T.Random();
AstType* function0 = T.Function0(type1, type2);
AstType* function1 = T.Function1(type1, type2, type3);
AstType* function2 = T.Function2(type1, type2, type3);
CHECK_EQ(0, function0->AsFunction()->Arity());
CHECK_EQ(1, function1->AsFunction()->Arity());
CHECK_EQ(2, function2->AsFunction()->Arity());
CheckEqual(type1, function0->AsFunction()->Result());
CheckEqual(type1, function1->AsFunction()->Result());
CheckEqual(type1, function2->AsFunction()->Result());
CheckEqual(type2, function0->AsFunction()->Receiver());
CheckEqual(type2, function1->AsFunction()->Receiver());
CheckEqual(T.Any, function2->AsFunction()->Receiver());
CheckEqual(type3, function1->AsFunction()->Parameter(0));
CheckEqual(type2, function2->AsFunction()->Parameter(0));
CheckEqual(type3, function2->AsFunction()->Parameter(1));
}
}
}
// Functionality & Injectivity: Function(Ts1) = Function(Ts2) iff Ts1 = Ts2
for (int i = 0; i < 20; ++i) {
for (int j = 0; j < 20; ++j) {
for (int k = 0; k < 20; ++k) {
AstType* type1 = T.Random();
AstType* type2 = T.Random();
AstType* type3 = T.Random();
AstType* function01 = T.Function0(type1, type2);
AstType* function02 = T.Function0(type1, type3);
AstType* function03 = T.Function0(type3, type2);
AstType* function11 = T.Function1(type1, type2, type2);
AstType* function12 = T.Function1(type1, type2, type3);
AstType* function21 = T.Function2(type1, type2, type2);
AstType* function22 = T.Function2(type1, type2, type3);
AstType* function23 = T.Function2(type1, type3, type2);
CHECK(Equal(function01, function02) == Equal(type2, type3));
CHECK(Equal(function01, function03) == Equal(type1, type3));
CHECK(Equal(function11, function12) == Equal(type2, type3));
CHECK(Equal(function21, function22) == Equal(type2, type3));
CHECK(Equal(function21, function23) == Equal(type2, type3));
}
}
}
}
void Of() {
// Constant(V)->Is(Of(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* of_type = T.Of(value);
CHECK(const_type->Is(of_type));
}
// If Of(V)->Is(T), then Constant(V)->Is(T)
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
Handle<i::Object> value = *vt;
AstType* type = *it;
AstType* const_type = T.Constant(value);
AstType* of_type = T.Of(value);
CHECK(!of_type->Is(type) || const_type->Is(type));
}
}
// If Constant(V)->Is(T), then Of(V)->Is(T) or T->Maybe(Constant(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
Handle<i::Object> value = *vt;
AstType* type = *it;
AstType* const_type = T.Constant(value);
AstType* of_type = T.Of(value);
CHECK(!const_type->Is(type) || of_type->Is(type) ||
type->Maybe(const_type));
}
}
}
void NowOf() {
// Constant(V)->NowIs(NowOf(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* nowof_type = T.NowOf(value);
CHECK(const_type->NowIs(nowof_type));
}
// NowOf(V)->Is(Of(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Object> value = *vt;
AstType* nowof_type = T.NowOf(value);
AstType* of_type = T.Of(value);
CHECK(nowof_type->Is(of_type));
}
// If NowOf(V)->NowIs(T), then Constant(V)->NowIs(T)
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
Handle<i::Object> value = *vt;
AstType* type = *it;
AstType* const_type = T.Constant(value);
AstType* nowof_type = T.NowOf(value);
CHECK(!nowof_type->NowIs(type) || const_type->NowIs(type));
}
}
// If Constant(V)->NowIs(T),
// then NowOf(V)->NowIs(T) or T->Maybe(Constant(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
Handle<i::Object> value = *vt;
AstType* type = *it;
AstType* const_type = T.Constant(value);
AstType* nowof_type = T.NowOf(value);
CHECK(!const_type->NowIs(type) || nowof_type->NowIs(type) ||
type->Maybe(const_type));
}
}
// If Constant(V)->Is(T),
// then NowOf(V)->Is(T) or T->Maybe(Constant(V))
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
Handle<i::Object> value = *vt;
AstType* type = *it;
AstType* const_type = T.Constant(value);
AstType* nowof_type = T.NowOf(value);
CHECK(!const_type->Is(type) || nowof_type->Is(type) ||
type->Maybe(const_type));
}
}
}
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) {
AstType* type = *it;
if (this->IsBitset(type) && type->Is(T.Number) &&
!type->Is(T.None) && !type->Is(T.NaN)) {
AstType* 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) {
AstType* 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) {
AstType* type1 = *it1;
AstType* 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) {
AstType* type = *it;
if (type->IsRange()) {
AstType* lub = AstBitsetType::NewForTesting(AstBitsetType::Lub(type));
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) {
AstType* type = *it;
CHECK(!type->Is(T.Integer) || !type->IsInhabited() ||
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) {
AstType* type = *it;
AstType* glb = AstBitsetType::NewForTesting(AstBitsetType::Glb(type));
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* glb2 = AstBitsetType::NewForTesting(AstBitsetType::Glb(type2));
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* glb1 = AstBitsetType::NewForTesting(AstBitsetType::Glb(type1));
AstType* glb2 = AstBitsetType::NewForTesting(AstBitsetType::Glb(type2));
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) {
AstType* type = *it;
AstType* lub = AstBitsetType::NewForTesting(AstBitsetType::Lub(type));
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* lub1 = AstBitsetType::NewForTesting(AstBitsetType::Lub(type1));
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* lub1 = AstBitsetType::NewForTesting(AstBitsetType::Lub(type1));
AstType* lub2 = AstBitsetType::NewForTesting(AstBitsetType::Lub(type2));
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) {
AstType* type = *it;
CHECK(T.None->Is(type));
}
// Greatest Element (Top): T->Is(Any)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* 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) {
AstType* 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) {
AstType* 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) {
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* 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) {
AstType* type1 = *i;
AstType* type2 = *j;
CHECK(!type1->Is(type2) || this->IsBitset(type2) ||
this->IsUnion(type2) || this->IsUnion(type1) ||
(type1->IsClass() && type2->IsClass()) ||
(type1->IsConstant() && type2->IsConstant()) ||
(type1->IsConstant() && type2->IsRange()) ||
(this->IsBitset(type1) && type2->IsRange()) ||
(type1->IsRange() && type2->IsRange()) ||
(type1->IsContext() && type2->IsContext()) ||
(type1->IsArray() && type2->IsArray()) ||
(type1->IsFunction() && type2->IsFunction()) ||
!type1->IsInhabited());
}
}
}
void Is2() {
// Class(M1)->Is(Class(M2)) iff M1 = M2
for (MapIterator mt1 = T.maps.begin(); mt1 != T.maps.end(); ++mt1) {
for (MapIterator mt2 = T.maps.begin(); mt2 != T.maps.end(); ++mt2) {
Handle<i::Map> map1 = *mt1;
Handle<i::Map> map2 = *mt2;
AstType* class_type1 = T.Class(map1);
AstType* class_type2 = T.Class(map2);
CHECK(class_type1->Is(class_type2) == (*map1 == *map2));
}
}
// 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);
AstType* type1 = T.Range(min1, max1);
AstType* 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<i::Object> value1 = *vt1;
Handle<i::Object> value2 = *vt2;
AstType* const_type1 = T.Constant(value1);
AstType* const_type2 = T.Constant(value2);
CHECK(const_type1->Is(const_type2) == (*value1 == *value2));
}
}
// Context(T1)->Is(Context(T2)) 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) {
AstType* outer1 = *it1;
AstType* outer2 = *it2;
AstType* type1 = T.Context(outer1);
AstType* type2 = T.Context(outer2);
CHECK(type1->Is(type2) == outer1->Equals(outer2));
}
}
// Array(T1)->Is(Array(T2)) 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) {
AstType* element1 = *it1;
AstType* element2 = *it2;
AstType* type1 = T.Array1(element1);
AstType* type2 = T.Array1(element2);
CHECK(type1->Is(type2) == element1->Equals(element2));
}
}
// Function0(S1, T1)->Is(Function0(S2, T2)) iff S1 = S2 and T1 = T2
for (TypeIterator i = T.types.begin(); i != T.types.end(); ++i) {
for (TypeIterator j = T.types.begin(); j != T.types.end(); ++j) {
AstType* result1 = *i;
AstType* receiver1 = *j;
AstType* type1 = T.Function0(result1, receiver1);
AstType* result2 = T.Random();
AstType* receiver2 = T.Random();
AstType* type2 = T.Function0(result2, receiver2);
CHECK(type1->Is(type2) ==
(result1->Equals(result2) && receiver1->Equals(receiver2)));
}
}
// Range-specific subtyping
// If IsInteger(v) then Constant(v)->Is(Range(v, v)).
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
if (type->IsConstant() && IsInteger(*type->AsConstant()->Value())) {
CHECK(type->Is(T.Range(type->AsConstant()->Value()->Number(),
type->AsConstant()->Value()->Number())));
}
}
// If Constant(x)->Is(Range(min,max)) then IsInteger(v) and min <= x <= max.
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
if (type1->IsConstant() && type2->IsRange() && type1->Is(type2)) {
double x = type1->AsConstant()->Value()->Number();
double min = type2->AsRange()->Min();
double max = type2->AsRange()->Max();
CHECK(IsInteger(x) && min <= x && x <= max);
}
}
}
// Lub(Range(x,y))->Is(T.Union(T.Integral32, T.OtherNumber))
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
if (type->IsRange()) {
AstType* lub = AstBitsetType::NewForTesting(AstBitsetType::Lub(type));
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.OtherObject, T.Object);
CheckSub(T.OtherUndetectable, T.Object);
CheckSub(T.OtherObject, T.Object);
CheckUnordered(T.Object, T.Proxy);
CheckUnordered(T.OtherObject, T.Undetectable);
// Subtyping between concrete structural types
CheckSub(T.ObjectClass, T.Object);
CheckSub(T.ArrayClass, T.OtherObject);
CheckSub(T.UninitializedClass, T.Internal);
CheckUnordered(T.ObjectClass, T.ArrayClass);
CheckUnordered(T.UninitializedClass, T.Null);
CheckUnordered(T.UninitializedClass, T.Undefined);
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.OtherObject);
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);
CheckUnordered(T.ObjectConstant1, T.ObjectClass);
CheckUnordered(T.ObjectConstant2, T.ObjectClass);
CheckUnordered(T.ObjectConstant1, T.ArrayClass);
CheckUnordered(T.ObjectConstant2, T.ArrayClass);
CheckUnordered(T.ArrayConstant, T.ObjectClass);
CheckSub(T.NumberArray, T.OtherObject);
CheckSub(T.NumberArray, T.Receiver);
CheckSub(T.NumberArray, T.Object);
CheckUnordered(T.StringArray, T.AnyArray);
CheckSub(T.MethodFunction, T.Object);
CheckSub(T.NumberFunction1, T.Object);
CheckUnordered(T.SignedFunction1, T.NumberFunction1);
CheckUnordered(T.NumberFunction1, T.NumberFunction2);
}
void NowIs() {
// Least Element (Bottom): None->NowIs(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
CHECK(T.None->NowIs(type));
}
// Greatest Element (Top): T->NowIs(Any)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
CHECK(type->NowIs(T.Any));
}
// Bottom Uniqueness: T->NowIs(None) implies T = None
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
if (type->NowIs(T.None)) CheckEqual(type, T.None);
}
// Top Uniqueness: Any->NowIs(T) implies T = Any
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
if (T.Any->NowIs(type)) CheckEqual(type, T.Any);
}
// Reflexivity: T->NowIs(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
CHECK(type->NowIs(type));
}
// Transitivity: T1->NowIs(T2) and T2->NowIs(T3) implies T1->NowIs(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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
CHECK(!(type1->NowIs(type2) && type2->NowIs(type3)) ||
type1->NowIs(type3));
}
}
}
// Antisymmetry: T1->NowIs(T2) and T2->NowIs(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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
CHECK((type1->NowIs(type2) && type2->NowIs(type1)) ==
Equal(type1, type2));
}
}
// T1->Is(T2) implies T1->NowIs(T2)
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
CHECK(!type1->Is(type2) || type1->NowIs(type2));
}
}
// Constant(V1)->NowIs(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<i::Object> value1 = *vt1;
Handle<i::Object> value2 = *vt2;
AstType* const_type1 = T.Constant(value1);
AstType* const_type2 = T.Constant(value2);
CHECK(const_type1->NowIs(const_type2) == (*value1 == *value2));
}
}
// Class(M1)->NowIs(Class(M2)) iff M1 = M2
for (MapIterator mt1 = T.maps.begin(); mt1 != T.maps.end(); ++mt1) {
for (MapIterator mt2 = T.maps.begin(); mt2 != T.maps.end(); ++mt2) {
Handle<i::Map> map1 = *mt1;
Handle<i::Map> map2 = *mt2;
AstType* class_type1 = T.Class(map1);
AstType* class_type2 = T.Class(map2);
CHECK(class_type1->NowIs(class_type2) == (*map1 == *map2));
}
}
// Constant(V)->NowIs(Class(M)) iff V has map M
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Map> map = *mt;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* class_type = T.Class(map);
CHECK((value->IsHeapObject() &&
i::HeapObject::cast(*value)->map() == *map) ==
const_type->NowIs(class_type));
}
}
// Class(M)->NowIs(Constant(V)) never
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Map> map = *mt;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* class_type = T.Class(map);
CHECK(!class_type->NowIs(const_type));
}
}
}
void Contains() {
// T->Contains(V) iff Constant(V)->Is(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
AstType* type = *it;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
CHECK(type->Contains(value) == const_type->Is(type));
}
}
}
void NowContains() {
// T->NowContains(V) iff Constant(V)->NowIs(T)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
AstType* type = *it;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
CHECK(type->NowContains(value) == const_type->NowIs(type));
}
}
// T->Contains(V) implies T->NowContains(V)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
AstType* type = *it;
Handle<i::Object> value = *vt;
CHECK(!type->Contains(value) || type->NowContains(value));
}
}
// NowOf(V)->Is(T) implies T->NowContains(V)
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
AstType* type = *it;
Handle<i::Object> value = *vt;
AstType* nowof_type = T.Of(value);
CHECK(!nowof_type->NowIs(type) || type->NowContains(value));
}
}
}
void Maybe() {
// T->Maybe(Any) iff T inhabited
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
CHECK(type->Maybe(T.Any) == type->IsInhabited());
}
// T->Maybe(None) never
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* 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) {
AstType* type = *it;
CHECK(type->Maybe(type) == type->IsInhabited());
}
// 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) {
AstType* type1 = *it1;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
CHECK(!type1->Maybe(type2) ||
(type1->IsInhabited() && type2->IsInhabited()));
}
}
// 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* intersect12 = T.Intersect(type1, type2);
CHECK(!type1->Maybe(type2) || intersect12->IsInhabited());
}
}
// 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
CHECK(!(type1->Is(type2) && type1->IsInhabited()) ||
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<i::Object> value1 = *vt1;
Handle<i::Object> value2 = *vt2;
AstType* const_type1 = T.Constant(value1);
AstType* const_type2 = T.Constant(value2);
CHECK(const_type1->Maybe(const_type2) == (*value1 == *value2));
}
}
// Class(M1)->Maybe(Class(M2)) iff M1 = M2
for (MapIterator mt1 = T.maps.begin(); mt1 != T.maps.end(); ++mt1) {
for (MapIterator mt2 = T.maps.begin(); mt2 != T.maps.end(); ++mt2) {
Handle<i::Map> map1 = *mt1;
Handle<i::Map> map2 = *mt2;
AstType* class_type1 = T.Class(map1);
AstType* class_type2 = T.Class(map2);
CHECK(class_type1->Maybe(class_type2) == (*map1 == *map2));
}
}
// Constant(V)->Maybe(Class(M)) never
// This does NOT hold!
/*
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Map> map = *mt;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* class_type = T.Class(map);
CHECK(!const_type->Maybe(class_type));
}
}
*/
// Class(M)->Maybe(Constant(V)) never
// This does NOT hold!
/*
for (MapIterator mt = T.maps.begin(); mt != T.maps.end(); ++mt) {
for (ValueIterator vt = T.values.begin(); vt != T.values.end(); ++vt) {
Handle<i::Map> map = *mt;
Handle<i::Object> value = *vt;
AstType* const_type = T.Constant(value);
AstType* class_type = T.Class(map);
CHECK(!class_type->Maybe(const_type));
}
}
*/
// 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.ObjectClass, T.Object);
CheckOverlap(T.ArrayClass, T.Object);
CheckOverlap(T.ObjectClass, T.ObjectClass);
CheckOverlap(T.ArrayClass, T.ArrayClass);
CheckDisjoint(T.ObjectClass, T.ArrayClass);
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);
CheckOverlap(T.ObjectConstant1, T.ArrayClass);
CheckOverlap(T.ObjectConstant2, T.ArrayClass);
CheckOverlap(T.ArrayConstant, T.ObjectClass);
CheckOverlap(T.NumberArray, T.Receiver);
CheckDisjoint(T.NumberArray, T.AnyArray);
CheckDisjoint(T.NumberArray, T.StringArray);
CheckOverlap(T.MethodFunction, T.Object);
CheckDisjoint(T.SignedFunction1, T.NumberFunction1);
CheckDisjoint(T.SignedFunction1, T.NumberFunction2);
CheckDisjoint(T.NumberFunction1, T.NumberFunction2);
CheckDisjoint(T.SignedFunction1, T.MethodFunction);
CheckOverlap(T.ObjectConstant1, T.ObjectClass); // !!!
CheckOverlap(T.ObjectConstant2, T.ObjectClass); // !!!
CheckOverlap(T.NumberClass, T.Intersect(T.Number, T.Tagged)); // !!!
}
void Union1() {
// Identity: Union(T, None) = T
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
AstType* 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) {
AstType* type = *it;
AstType* 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) {
AstType* type = *it;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* union12 = T.Union(type1, type2);
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* union12 = T.Union(type1, type2);
AstType* union23 = T.Union(type2, type3);
AstType* union1_23 = T.Union(type1, union23);
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* 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) </= Signed32 \/ Range(1,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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* union13 = T.Union(type1, type3);
AstType* union23 = T.Union(type2, type3);
CHECK(!type1->Is(type2) || union13->Is(union23));
}
}
}
*/
}
void Union2() {
// Monotonicity: T1->Is(T3) and T2->Is(T3) implies Union(T1, T2)->Is(T3)
// This does NOT hold. For example:
// Range(-2^33, -2^33) <= OtherNumber
// Range(2^33, 2^33) <= OtherNumber
// Range(-2^33, 2^33) </= OtherNumber
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* union12 = T.Union(type1, type2);
CHECK(!(type1->Is(type3) && type2->Is(type3)) || union12->Is(type3));
}
}
}
*/
}
void Union3() {
// Monotonicity: T1->Is(T2) or T1->Is(T3) implies T1->Is(Union(T2, T3))
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
HandleScope scope(isolate);
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
for (TypeIterator it3 = it2; it3 != T.types.end(); ++it3) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* union23 = T.Union(type2, type3);
CHECK(!(type1->Is(type2) || type1->Is(type3)) || type1->Is(union23));
}
}
}
}
void Union4() {
// Class-class
CheckSub(T.Union(T.ObjectClass, T.ArrayClass), T.Object);
CheckOverlap(T.Union(T.ObjectClass, T.ArrayClass), T.OtherObject);
CheckOverlap(T.Union(T.ObjectClass, T.ArrayClass), T.Receiver);
CheckDisjoint(T.Union(T.ObjectClass, T.ArrayClass), T.Number);
// Constant-constant
CheckSub(T.Union(T.ObjectConstant1, T.ObjectConstant2), T.Object);
CheckOverlap(T.Union(T.ObjectConstant1, T.ArrayConstant), T.OtherObject);
CheckUnordered(T.Union(T.ObjectConstant1, T.ObjectConstant2),
T.ObjectClass);
CheckOverlap(T.Union(T.ObjectConstant1, T.ArrayConstant), T.OtherObject);
CheckDisjoint(T.Union(T.ObjectConstant1, T.ArrayConstant), T.Number);
CheckOverlap(T.Union(T.ObjectConstant1, T.ArrayConstant),
T.ObjectClass); // !!!
// Bitset-array
CHECK(this->IsBitset(T.Union(T.AnyArray, T.Receiver)));
CHECK(this->IsUnion(T.Union(T.NumberArray, T.Number)));
CheckEqual(T.Union(T.AnyArray, T.Receiver), T.Receiver);
CheckEqual(T.Union(T.AnyArray, T.OtherObject), T.OtherObject);
CheckUnordered(T.Union(T.AnyArray, T.String), T.Receiver);
CheckOverlap(T.Union(T.NumberArray, T.String), T.Object);
CheckDisjoint(T.Union(T.NumberArray, T.String), T.Number);
// Bitset-function
CHECK(this->IsBitset(T.Union(T.MethodFunction, T.Object)));
CHECK(this->IsUnion(T.Union(T.NumberFunction1, T.Number)));
CheckEqual(T.Union(T.MethodFunction, T.Object), T.Object);
CheckUnordered(T.Union(T.NumberFunction1, T.String), T.Object);
CheckOverlap(T.Union(T.NumberFunction2, T.String), T.Object);
CheckDisjoint(T.Union(T.NumberFunction1, T.String), T.Number);
// Bitset-class
CheckSub(T.Union(T.ObjectClass, T.SignedSmall),
T.Union(T.Object, T.Number));
CheckSub(T.Union(T.ObjectClass, T.OtherObject), T.Object);
CheckUnordered(T.Union(T.ObjectClass, T.String), T.OtherObject);
CheckOverlap(T.Union(T.ObjectClass, T.String), T.Object);
CheckDisjoint(T.Union(T.ObjectClass, T.String), T.Number);
// Bitset-constant
CheckSub(T.Union(T.ObjectConstant1, T.Signed32),
T.Union(T.Object, T.Number));
CheckSub(T.Union(T.ObjectConstant1, T.OtherObject), T.Object);
CheckUnordered(T.Union(T.ObjectConstant1, T.String), T.OtherObject);
CheckOverlap(T.Union(T.ObjectConstant1, T.String), T.Object);
CheckDisjoint(T.Union(T.ObjectConstant1, T.String), T.Number);
// Class-constant
CheckSub(T.Union(T.ObjectConstant1, T.ArrayClass), T.Object);
CheckUnordered(T.ObjectClass, T.Union(T.ObjectConstant1, T.ArrayClass));
CheckSub(T.Union(T.ObjectConstant1, T.ArrayClass),
T.Union(T.Receiver, T.Object));
CheckUnordered(T.Union(T.ObjectConstant1, T.ArrayClass), T.ArrayConstant);
CheckOverlap(T.Union(T.ObjectConstant1, T.ArrayClass), T.ObjectConstant2);
CheckOverlap(T.Union(T.ObjectConstant1, T.ArrayClass),
T.ObjectClass); // !!!
// Bitset-union
CheckSub(T.NaN,
T.Union(T.Union(T.ArrayClass, T.ObjectConstant1), T.Number));
CheckSub(T.Union(T.Union(T.ArrayClass, T.ObjectConstant1), T.Signed32),
T.Union(T.ObjectConstant1, T.Union(T.Number, T.ArrayClass)));
// Class-union
CheckSub(T.Union(T.ObjectClass, T.Union(T.ObjectConstant1, T.ObjectClass)),
T.Object);
CheckEqual(T.Union(T.Union(T.ArrayClass, T.ObjectConstant2), T.ArrayClass),
T.Union(T.ArrayClass, T.ObjectConstant2));
// Constant-union
CheckEqual(T.Union(T.ObjectConstant1,
T.Union(T.ObjectConstant1, T.ObjectConstant2)),
T.Union(T.ObjectConstant2, T.ObjectConstant1));
CheckEqual(
T.Union(T.Union(T.ArrayConstant, T.ObjectConstant2), T.ObjectConstant1),
T.Union(T.ObjectConstant2,
T.Union(T.ArrayConstant, T.ObjectConstant1)));
// Array-union
CheckEqual(T.Union(T.AnyArray, T.Union(T.NumberArray, T.AnyArray)),
T.Union(T.AnyArray, T.NumberArray));
CheckSub(T.Union(T.AnyArray, T.NumberArray), T.OtherObject);
// Function-union
CheckEqual(T.Union(T.NumberFunction1, T.NumberFunction2),
T.Union(T.NumberFunction2, T.NumberFunction1));
CheckSub(T.Union(T.SignedFunction1, T.MethodFunction), T.Object);
// Union-union
CheckEqual(T.Union(T.Union(T.ObjectConstant2, T.ObjectConstant1),
T.Union(T.ObjectConstant1, T.ObjectConstant2)),
T.Union(T.ObjectConstant2, T.ObjectConstant1));
CheckEqual(T.Union(T.Union(T.Number, T.ArrayClass),
T.Union(T.SignedSmall, T.Receiver)),
T.Union(T.Number, T.Receiver));
}
void Intersect() {
// Identity: Intersect(T, Any) = T
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
AstType* intersect_type = T.Intersect(type, T.Any);
CheckEqual(intersect_type, type);
}
// Domination: Intersect(T, None) = None
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
AstType* intersect_type = T.Intersect(type, T.None);
CheckEqual(intersect_type, T.None);
}
// Idempotence: Intersect(T, T) = T
for (TypeIterator it = T.types.begin(); it != T.types.end(); ++it) {
AstType* type = *it;
AstType* intersect_type = T.Intersect(type, type);
CheckEqual(intersect_type, type);
}
// Commutativity: Intersect(T1, T2) = Intersect(T2, T1)
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* intersect12 = T.Intersect(type1, type2);
AstType* intersect21 = T.Intersect(type2, type1);
CheckEqual(intersect12, intersect21);
}
}
// Associativity:
// Intersect(T1, Intersect(T2, T3)) = Intersect(Intersect(T1, T2), T3)
// This does NOT hold. For example:
// (Class(..stringy1..) /\ Class(..stringy2..)) /\ Constant(..string..) =
// None
// Class(..stringy1..) /\ (Class(..stringy2..) /\ Constant(..string..)) =
// Constant(..string..)
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* intersect12 = T.Intersect(type1, type2);
AstType* intersect23 = T.Intersect(type2, type3);
AstType* intersect1_23 = T.Intersect(type1, intersect23);
AstType* intersect12_3 = T.Intersect(intersect12, type3);
CheckEqual(intersect1_23, intersect12_3);
}
}
}
*/
// Join: Intersect(T1, T2)->Is(T1) and Intersect(T1, T2)->Is(T2)
// This does NOT hold. For example:
// Class(..stringy..) /\ Constant(..string..) = Constant(..string..)
// Currently, not even the disjunction holds:
// Class(Internal/TaggedPtr) /\ (Any/Untagged \/ Context(..)) =
// Class(Internal/TaggedPtr) \/ Context(..)
/*
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* intersect12 = T.Intersect(type1, type2);
CHECK(intersect12->Is(type1));
CHECK(intersect12->Is(type2));
}
}
*/
// Lower Boundedness: T1->Is(T2) implies Intersect(T1, T2) = T1
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* intersect12 = T.Intersect(type1, type2);
if (type1->Is(type2)) CheckEqual(intersect12, type1);
}
}
// Monotonicity: T1->Is(T2) implies Intersect(T1, T3)->Is(Intersect(T2, T3))
// This does NOT hold. For example:
// Class(OtherObject/TaggedPtr) <= Any/TaggedPtr
// Class(OtherObject/TaggedPtr) /\ Any/UntaggedInt1 = Class(..)
// Any/TaggedPtr /\ Any/UntaggedInt1 = None
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* intersect13 = T.Intersect(type1, type3);
AstType* intersect23 = T.Intersect(type2, type3);
CHECK(!type1->Is(type2) || intersect13->Is(intersect23));
}
}
}
*/
// Monotonicity: T1->Is(T3) or T2->Is(T3) implies Intersect(T1, T2)->Is(T3)
// This does NOT hold. For example:
// Class(..stringy..) <= Class(..stringy..)
// Class(..stringy..) /\ Constant(..string..) = Constant(..string..)
// Constant(..string..) </= Class(..stringy..)
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* intersect12 = T.Intersect(type1, type2);
CHECK(!(type1->Is(type3) || type2->Is(type3)) ||
intersect12->Is(type3));
}
}
}
*/
// Monotonicity: T1->Is(T2) and T1->Is(T3) implies T1->Is(Intersect(T2, T3))
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
HandleScope scope(isolate);
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
for (TypeIterator it3 = T.types.begin(); it3 != T.types.end(); ++it3) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* intersect23 = T.Intersect(type2, type3);
CHECK(!(type1->Is(type2) && type1->Is(type3)) ||
type1->Is(intersect23));
}
}
}
// Bitset-class
CheckEqual(T.Intersect(T.ObjectClass, T.Object), T.ObjectClass);
CheckEqual(T.Semantic(T.Intersect(T.ObjectClass, T.Number)), T.None);
// Bitset-array
CheckEqual(T.Intersect(T.NumberArray, T.Object), T.NumberArray);
CheckEqual(T.Semantic(T.Intersect(T.AnyArray, T.Proxy)), T.None);
// Bitset-function
CheckEqual(T.Intersect(T.MethodFunction, T.Object), T.MethodFunction);
CheckEqual(T.Semantic(T.Intersect(T.NumberFunction1, T.Proxy)), T.None);
// Bitset-union
CheckEqual(T.Intersect(T.Object, T.Union(T.ObjectConstant1, T.ObjectClass)),
T.Union(T.ObjectConstant1, T.ObjectClass));
CheckEqual(T.Semantic(T.Intersect(T.Union(T.ArrayClass, T.ObjectConstant1),
T.Number)),
T.None);
// Class-constant
CHECK(T.Intersect(T.ObjectConstant1, T.ObjectClass)->IsInhabited()); // !!!
CHECK(T.Intersect(T.ArrayClass, T.ObjectConstant2)->IsInhabited());
// Array-union
CheckEqual(T.Intersect(T.NumberArray, T.Union(T.NumberArray, T.ArrayClass)),
T.NumberArray);
CheckEqual(T.Intersect(T.AnyArray, T.Union(T.Object, T.SmiConstant)),
T.AnyArray);
CHECK(!T.Intersect(T.Union(T.AnyArray, T.ArrayConstant), T.NumberArray)
->IsInhabited());
// Function-union
CheckEqual(
T.Intersect(T.MethodFunction, T.Union(T.String, T.MethodFunction)),
T.MethodFunction);
CheckEqual(T.Intersect(T.NumberFunction1, T.Union(T.Object, T.SmiConstant)),
T.NumberFunction1);
CHECK(!T.Intersect(T.Union(T.MethodFunction, T.Name), T.NumberFunction2)
->IsInhabited());
// Class-union
CheckEqual(
T.Intersect(T.ArrayClass, T.Union(T.ObjectConstant2, T.ArrayClass)),
T.ArrayClass);
CheckEqual(T.Intersect(T.ArrayClass, T.Union(T.Object, T.SmiConstant)),
T.ArrayClass);
CHECK(T.Intersect(T.Union(T.ObjectClass, T.ArrayConstant), T.ArrayClass)
->IsInhabited()); // !!!
// Constant-union
CheckEqual(T.Intersect(T.ObjectConstant1,
T.Union(T.ObjectConstant1, T.ObjectConstant2)),
T.ObjectConstant1);
CheckEqual(T.Intersect(T.SmiConstant, T.Union(T.Number, T.ObjectConstant2)),
T.SmiConstant);
CHECK(
T.Intersect(T.Union(T.ArrayConstant, T.ObjectClass), T.ObjectConstant1)
->IsInhabited()); // !!!
// Union-union
CheckEqual(T.Intersect(T.Union(T.Number, T.ArrayClass),
T.Union(T.SignedSmall, T.Receiver)),
T.Union(T.SignedSmall, T.ArrayClass));
CheckEqual(T.Intersect(T.Union(T.Number, T.ObjectClass),
T.Union(T.Signed32, T.OtherObject)),
T.Union(T.Signed32, T.ObjectClass));
CheckEqual(T.Intersect(T.Union(T.ObjectConstant2, T.ObjectConstant1),
T.Union(T.ObjectConstant1, T.ObjectConstant2)),
T.Union(T.ObjectConstant2, T.ObjectConstant1));
CheckEqual(
T.Intersect(T.Union(T.ArrayClass,
T.Union(T.ObjectConstant2, T.ObjectConstant1)),
T.Union(T.ObjectConstant1,
T.Union(T.ArrayConstant, T.ObjectConstant2))),
T.Union(T.ArrayConstant,
T.Union(T.ObjectConstant2, T.ObjectConstant1))); // !!!
}
void Distributivity() {
// Union(T1, Intersect(T2, T3)) = Intersect(Union(T1, T2), Union(T1, T3))
// This does NOT hold. For example:
// Untagged \/ (Untagged /\ Class(../Tagged)) = Untagged \/ Class(../Tagged)
// (Untagged \/ Untagged) /\ (Untagged \/ Class(../Tagged)) =
// Untagged /\ (Untagged \/ Class(../Tagged)) = Untagged
// because Untagged <= Untagged \/ Class(../Tagged)
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* union12 = T.Union(type1, type2);
AstType* union13 = T.Union(type1, type3);
AstType* intersect23 = T.Intersect(type2, type3);
AstType* union1_23 = T.Union(type1, intersect23);
AstType* intersect12_13 = T.Intersect(union12, union13);
CHECK(Equal(union1_23, intersect12_13));
}
}
}
*/
// Intersect(T1, Union(T2, T3)) = Union(Intersect(T1, T2), Intersect(T1,T3))
// This does NOT hold. For example:
// Untagged /\ (Untagged \/ Class(../Tagged)) = Untagged
// (Untagged /\ Untagged) \/ (Untagged /\ Class(../Tagged)) =
// Untagged \/ Class(../Tagged)
/*
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) {
AstType* type1 = *it1;
AstType* type2 = *it2;
AstType* type3 = *it3;
AstType* intersect12 = T.Intersect(type1, type2);
AstType* intersect13 = T.Intersect(type1, type3);
AstType* union23 = T.Union(type2, type3);
AstType* intersect1_23 = T.Intersect(type1, union23);
AstType* union12_13 = T.Union(intersect12, intersect13);
CHECK(Equal(intersect1_23, union12_13));
}
}
}
*/
}
void GetRange() {
// GetRange(Range(a, b)) = Range(a, b).
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
AstType* type1 = *it1;
if (type1->IsRange()) {
AstRangeType* range = type1->GetRange()->AsRange();
CHECK(type1->Min() == range->Min());
CHECK(type1->Max() == range->Max());
}
}
// GetRange(Union(Constant(x), Range(min,max))) == Range(min, max).
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {
for (TypeIterator it2 = T.types.begin(); it2 != T.types.end(); ++it2) {
AstType* type1 = *it1;
AstType* type2 = *it2;
if (type1->IsConstant() && type2->IsRange()) {
AstType* u = T.Union(type1, type2);
CHECK(type2->Min() == u->GetRange()->Min());
CHECK(type2->Max() == u->GetRange()->Max());
}
}
}
}
};
} // namespace
TEST(AstIsSomeType_zone) { Tests().IsSomeType(); }
TEST(AstPointwiseRepresentation_zone) { Tests().PointwiseRepresentation(); }
TEST(AstBitsetType_zone) { Tests().Bitset(); }
TEST(AstClassType_zone) { Tests().Class(); }
TEST(AstConstantType_zone) { Tests().Constant(); }
TEST(AstRangeType_zone) { Tests().Range(); }
TEST(AstArrayType_zone) { Tests().Array(); }
TEST(AstFunctionType_zone) { Tests().Function(); }
TEST(AstOf_zone) { Tests().Of(); }
TEST(AstNowOf_zone) { Tests().NowOf(); }
TEST(AstMinMax_zone) { Tests().MinMax(); }
TEST(AstBitsetGlb_zone) { Tests().BitsetGlb(); }
TEST(AstBitsetLub_zone) { Tests().BitsetLub(); }
TEST(AstIs1_zone) { Tests().Is1(); }
TEST(AstIs2_zone) { Tests().Is2(); }
TEST(AstNowIs_zone) { Tests().NowIs(); }
TEST(AstContains_zone) { Tests().Contains(); }
TEST(AstNowContains_zone) { Tests().NowContains(); }
TEST(AstMaybe_zone) { Tests().Maybe(); }
TEST(AstUnion1_zone) { Tests().Union1(); }
TEST(AstUnion2_zone) { Tests().Union2(); }
TEST(AstUnion3_zone) { Tests().Union3(); }
TEST(AstUnion4_zone) { Tests().Union4(); }
TEST(AstIntersect_zone) { Tests().Intersect(); }
TEST(AstDistributivity_zone) { Tests().Distributivity(); }
TEST(AstGetRange_zone) { Tests().GetRange(); }
......@@ -2698,17 +2698,6 @@ TEST(TransitionAccessorConstantToSameAccessorConstant) {
TestTransitionTo(transition_op, transition_op, checker);
}
TEST(FieldTypeConvertSimple) {
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
Isolate* isolate = CcTest::i_isolate();
Zone zone(isolate->allocator(), ZONE_NAME);
CHECK_EQ(FieldType::Any()->Convert(&zone), AstType::NonInternal());
CHECK_EQ(FieldType::None()->Convert(&zone), AstType::None());
}
// TODO(ishell): add this test once IS_ACCESSOR_FIELD_SUPPORTED is supported.
// TEST(TransitionAccessorConstantToAnotherAccessorConstant)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment