// Copyright 2013 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_TYPES_H_ #define V8_TYPES_H_ #include "v8.h" #include "objects.h" namespace v8 { namespace internal { // 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). // // The following equations and inequations hold: // // None <= T // T <= Any // // Oddball = Boolean \/ Null \/ Undefined // Number = Signed32 \/ Unsigned32 \/ Double // Smi <= Signed32 // Name = String \/ Symbol // UniqueName = InternalizedString \/ Symbol // InternalizedString < String // // Allocated = Receiver \/ Number \/ Name // Detectable = Allocated - Undetectable // Undetectable < Object // Receiver = Object \/ Proxy // Array < Object // Function < Object // RegExp < Object // // Class(map) < T iff instance_type(map) < T // Constant(x) < T iff instance_type(map(x)) < T // // Note 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. // // 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(Integer31())), and the 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 use pointer equality for type tests, always use Is! // // Internally, all 'primitive' types, and their unions, are represented as // bitsets via smis. Class is a heap pointer to the respective map. Only // Constant's, or unions containing Class'es or Constant's, require allocation. // Note that the bitset representation is closed under both Union and Intersect. // // The type representation is heap-allocated, so cannot (currently) be used in // a concurrent compilation context. #define PRIMITIVE_TYPE_LIST(V) \ V(None, 0) \ V(Null, 1 << 0) \ V(Undefined, 1 << 1) \ V(Boolean, 1 << 2) \ V(Smi, 1 << 3) \ V(OtherSigned32, 1 << 4) \ V(Unsigned32, 1 << 5) \ V(Double, 1 << 6) \ V(Symbol, 1 << 7) \ V(InternalizedString, 1 << 8) \ V(OtherString, 1 << 9) \ V(Undetectable, 1 << 10) \ V(Array, 1 << 11) \ V(Function, 1 << 12) \ V(RegExp, 1 << 13) \ V(OtherObject, 1 << 14) \ V(Proxy, 1 << 15) \ V(Internal, 1 << 16) #define COMPOSED_TYPE_LIST(V) \ V(Oddball, kBoolean | kNull | kUndefined) \ V(Signed32, kSmi | kOtherSigned32) \ V(Number, kSigned32 | kUnsigned32 | kDouble) \ V(String, kInternalizedString | kOtherString) \ V(UniqueName, kSymbol | kInternalizedString) \ V(Name, kSymbol | kString) \ V(NumberOrString, kNumber | kString) \ V(Object, kUndetectable | kArray | kFunction | \ kRegExp | kOtherObject) \ V(Receiver, kObject | kProxy) \ V(Allocated, kDouble | kName | kReceiver) \ V(Any, kOddball | kNumber | kAllocated | kInternal) \ V(Detectable, kAllocated - kUndetectable) #define TYPE_LIST(V) \ PRIMITIVE_TYPE_LIST(V) \ COMPOSED_TYPE_LIST(V) class Type : public Object { public: #define DEFINE_TYPE_CONSTRUCTOR(type, value) \ static Type* type() { return from_bitset(k##type); } TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR) #undef DEFINE_TYPE_CONSTRUCTOR static Type* Class(Handle map) { return from_handle(map); } static Type* Constant(Handle value) { return Constant(value, value->GetIsolate()); } static Type* Constant(Handle value, Isolate* isolate) { return from_handle(isolate->factory()->NewBox(value)); } static Type* Union(Handle type1, Handle type2); static Type* Intersect(Handle type1, Handle type2); static Type* Optional(Handle type); // type \/ Undefined bool Is(Type* that) { return (this == that) ? true : IsSlowCase(that); } bool Is(Handle that) { return this->Is(*that); } bool Maybe(Type* that); bool Maybe(Handle that) { return this->Maybe(*that); } bool IsClass() { return is_class(); } bool IsConstant() { return is_constant(); } Handle AsClass() { return as_class(); } Handle AsConstant() { return as_constant(); } int NumClasses(); int NumConstants(); template class Iterator { public: bool Done() const { return index_ < 0; } Handle Current(); void Advance(); private: friend class Type; Iterator() : index_(-1) {} explicit Iterator(Handle type) : type_(type), index_(-1) { Advance(); } inline bool matches(Handle type); inline Handle get_type(); Handle type_; int index_; }; Iterator Classes() { if (this->is_bitset()) return Iterator(); return Iterator(this->handle()); } Iterator Constants() { if (this->is_bitset()) return Iterator(); return Iterator(this->handle()); } static Type* cast(v8::internal::Object* object) { Type* t = static_cast(object); ASSERT(t->is_bitset() || t->is_class() || t->is_constant() || t->is_union()); return t; } #ifdef OBJECT_PRINT void TypePrint(); void TypePrint(FILE* out); #endif private: // A union is a fixed array containing types. 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 typedef FixedArray Unioned; enum { #define DECLARE_TYPE(type, value) k##type = (value), TYPE_LIST(DECLARE_TYPE) #undef DECLARE_TYPE kUnusedEOL = 0 }; bool is_bitset() { return this->IsSmi(); } bool is_class() { return this->IsMap(); } bool is_constant() { return this->IsBox(); } bool is_union() { return this->IsFixedArray(); } bool IsSlowCase(Type* that); int as_bitset() { return Smi::cast(this)->value(); } Handle as_class() { return Handle::cast(handle()); } Handle as_constant() { Handle box = Handle::cast(handle()); return v8::internal::handle(box->value(), box->GetIsolate()); } Handle as_union() { return Handle::cast(handle()); } Handle handle() { return handle_via_isolate_of(this); } Handle handle_via_isolate_of(Type* type) { ASSERT(type->IsHeapObject()); return v8::internal::handle(this, HeapObject::cast(type)->GetIsolate()); } static Type* from_bitset(int bitset) { return static_cast(Object::cast(Smi::FromInt(bitset))); } static Type* from_handle(Handle handle) { return static_cast(Object::cast(*handle)); } static Handle union_get(Handle unioned, int i) { Type* type = static_cast(unioned->get(i)); ASSERT(!type->is_union()); return type->handle_via_isolate_of(from_handle(unioned)); } int LubBitset(); // least upper bound that's a bitset int GlbBitset(); // greatest lower bound that's a bitset bool InUnion(Handle unioned, int current_size); int ExtendUnion(Handle unioned, int current_size); int ExtendIntersection( Handle unioned, Handle type, int current_size); static const char* GetComposedName(int type) { switch (type) { #define PRINT_COMPOSED_TYPE(type, value) \ case k##type: \ return # type; COMPOSED_TYPE_LIST(PRINT_COMPOSED_TYPE) #undef PRINT_COMPOSED_TYPE } return NULL; } static const char* GetPrimitiveName(int type) { switch (type) { #define PRINT_PRIMITIVE_TYPE(type, value) \ case k##type: \ return # type; PRIMITIVE_TYPE_LIST(PRINT_PRIMITIVE_TYPE) #undef PRINT_PRIMITIVE_TYPE default: UNREACHABLE(); return "InvalidType"; } } }; // A simple struct to represent a pair of lower/upper type bounds. struct Bounds { Handle lower; Handle upper; Bounds() {} Bounds(Handle l, Handle u) : lower(l), upper(u) {} Bounds(Type* l, Type* u, Isolate* isl) : lower(l, isl), upper(u, isl) {} explicit Bounds(Handle t) : lower(t), upper(t) {} Bounds(Type* t, Isolate* isl) : lower(t, isl), upper(t, isl) {} // Unrestricted bounds. static Bounds Unbounded(Isolate* isl) { return Bounds(Type::None(), Type::Any(), isl); } // Meet: both b1 and b2 are known to hold. static Bounds Both(Bounds b1, Bounds b2, Isolate* isl) { return Bounds( handle(Type::Union(b1.lower, b2.lower), isl), handle(Type::Intersect(b1.upper, b2.upper), isl)); } // Join: either b1 or b2 is known to hold. static Bounds Either(Bounds b1, Bounds b2, Isolate* isl) { return Bounds( handle(Type::Intersect(b1.lower, b2.lower), isl), handle(Type::Union(b1.upper, b2.upper), isl)); } static Bounds NarrowLower(Bounds b, Handle t, Isolate* isl) { return Bounds(handle(Type::Union(b.lower, t), isl), b.upper); } static Bounds NarrowUpper(Bounds b, Handle t, Isolate* isl) { return Bounds(b.lower, handle(Type::Intersect(b.upper, t), isl)); } }; } } // namespace v8::internal #endif // V8_TYPES_H_