// 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 "src/crankshaft/hydrogen-types.h"

#include "test/cctest/cctest.h"

using namespace v8::internal;

static const HType kTypes[] = {
  #define DECLARE_TYPE(Name, mask) HType::Name(),
  HTYPE_LIST(DECLARE_TYPE)
  #undef DECLARE_TYPE
};

static const int kNumberOfTypes = sizeof(kTypes) / sizeof(kTypes[0]);


TEST(HTypeDistinct) {
  for (int i = 0; i < kNumberOfTypes; ++i) {
    for (int j = 0; j < kNumberOfTypes; ++j) {
      CHECK(i == j || !kTypes[i].Equals(kTypes[j]));
    }
  }
}


TEST(HTypeReflexivity) {
  // Reflexivity of =
  for (int i = 0; i < kNumberOfTypes; ++i) {
    CHECK(kTypes[i].Equals(kTypes[i]));
  }

  // Reflexivity of <
  for (int i = 0; i < kNumberOfTypes; ++i) {
    CHECK(kTypes[i].IsSubtypeOf(kTypes[i]));
  }
}


TEST(HTypeTransitivity) {
  // Transitivity of =
  for (int i = 0; i < kNumberOfTypes; ++i) {
    for (int j = 0; j < kNumberOfTypes; ++j) {
      for (int k = 0; k < kNumberOfTypes; ++k) {
        HType ti = kTypes[i];
        HType tj = kTypes[j];
        HType tk = kTypes[k];
        CHECK(!ti.Equals(tj) || !tj.Equals(tk) || ti.Equals(tk));
      }
    }
  }

  // Transitivity of <
  for (int i = 0; i < kNumberOfTypes; ++i) {
    for (int j = 0; j < kNumberOfTypes; ++j) {
      for (int k = 0; k < kNumberOfTypes; ++k) {
        HType ti = kTypes[i];
        HType tj = kTypes[j];
        HType tk = kTypes[k];
        CHECK(!ti.IsSubtypeOf(tj) || !tj.IsSubtypeOf(tk) || ti.IsSubtypeOf(tk));
      }
    }
  }
}


TEST(HTypeCombine) {
  // T < T /\ T' and T' < T /\ T' for all T,T'
  for (int i = 0; i < kNumberOfTypes; ++i) {
    for (int j = 0; j < kNumberOfTypes; ++j) {
      HType ti = kTypes[i];
      HType tj = kTypes[j];
      CHECK(ti.IsSubtypeOf(ti.Combine(tj)));
      CHECK(tj.IsSubtypeOf(ti.Combine(tj)));
    }
  }
}


TEST(HTypeAny) {
  // T < Any for all T
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(ti.IsAny());
  }

  // Any < T implies T = Any for all T
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(!HType::Any().IsSubtypeOf(ti) || HType::Any().Equals(ti));
  }
}


TEST(HTypeTagged) {
  // T < Tagged for all T \ {Any}
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(ti.IsTagged() || HType::Any().Equals(ti));
  }

  // Tagged < T implies T = Tagged or T = Any
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(!HType::Tagged().IsSubtypeOf(ti) ||
          HType::Tagged().Equals(ti) ||
          HType::Any().Equals(ti));
  }
}


TEST(HTypeSmi) {
  // T < Smi implies T = None or T = Smi for all T
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(!ti.IsSmi() ||
          ti.Equals(HType::Smi()) ||
          ti.Equals(HType::None()));
  }
}


TEST(HTypeHeapObject) {
  CHECK(!HType::TaggedPrimitive().IsHeapObject());
  CHECK(!HType::TaggedNumber().IsHeapObject());
  CHECK(!HType::Smi().IsHeapObject());
  CHECK(HType::HeapObject().IsHeapObject());
  CHECK(HType::HeapPrimitive().IsHeapObject());
  CHECK(HType::Null().IsHeapObject());
  CHECK(HType::HeapNumber().IsHeapObject());
  CHECK(HType::String().IsHeapObject());
  CHECK(HType::Boolean().IsHeapObject());
  CHECK(HType::Undefined().IsHeapObject());
  CHECK(HType::JSObject().IsHeapObject());
  CHECK(HType::JSArray().IsHeapObject());
}


TEST(HTypePrimitive) {
  CHECK(HType::TaggedNumber().IsTaggedPrimitive());
  CHECK(HType::Smi().IsTaggedPrimitive());
  CHECK(!HType::HeapObject().IsTaggedPrimitive());
  CHECK(HType::HeapPrimitive().IsTaggedPrimitive());
  CHECK(HType::Null().IsHeapPrimitive());
  CHECK(HType::HeapNumber().IsHeapPrimitive());
  CHECK(HType::String().IsHeapPrimitive());
  CHECK(HType::Boolean().IsHeapPrimitive());
  CHECK(HType::Undefined().IsHeapPrimitive());
  CHECK(!HType::JSObject().IsTaggedPrimitive());
  CHECK(!HType::JSArray().IsTaggedPrimitive());
}


TEST(HTypeJSObject) {
  CHECK(HType::JSArray().IsJSObject());
}


TEST(HTypeNone) {
  // None < T for all T
  for (int i = 0; i < kNumberOfTypes; ++i) {
    HType ti = kTypes[i];
    CHECK(HType::None().IsSubtypeOf(ti));
  }
}
