require "./object/properties" # `Object` is the base type of all Crystal objects. # # ## Getters # # Multiple macros are available to easily declare, initialize and expose # instance variables as well as class variables on an `Object` by generating # simple accessor methods. # # For example writing: # # ``` # class Person # getter name # end # ``` # # Is the same as writing: # # ``` # class Person # def name # @name # end # end # ``` # # Similarly, we can write `class_getter name` to define a class variable, which # generates a `def self.name` class method returning `@@name`. # # We can define as many variables as necessary in a single call. For example # `getter name, age, city` will create a getter method for each of `name`, `age` # and `city`. # # ### Type and initial value # # Instead of plain arguments, we can specify a type as well as an initial value. # If the initial value is simple enough Crystal should be able to infer the type # of the instance or class variable! # # Specifying a type will also declare the instance or class variable with said # type and type the accessor method arguments and return type accordingly. # # For example writing: # # ``` # class Person # getter name : String # getter age = 0 # getter city : String = "unspecified" # end # ``` # # Is the same as writing: # # ``` # class Person # @name : String # @age = 0 # @city : String = "unspecified" # # def name : String # @name # end # # def age # @age # end # # def city : String # @city # end # end # ``` # # The initial value of an instance variable is automatically set when the object # is constructed. The initial value of a class variable will be set when the # program starts up. # # ### Lazy initialization # # Instead of eagerly initializing the value, we can lazily initialize it the # first time the accessor method is called. # # Since the variable will be lazily initialized the type of the variable will be # a nilable type. The generated method however will return the specified type # only (not a nilable). # # For example writing: # # ``` # class Person # getter(city : City) { City.unspecified } # end # ``` # # Is equivalent to writing: # # ``` # class Person # @city : City? # # def city : City # if (city = @city).nil? # @city = City.unspecified # else # city # end # end # end # ``` # # ### Variants # # Please refer to the different variants to understand how they differ from the # general overview presented above: # # - `getter` # - `getter?` # - `getter!` # - `class_getter` # - `class_getter?` # - `class_getter!` # # ## Setters # # The `setter` and `class_setter` macros are the write counterparts of the # getter macros. They declare `name=(value)` accessor methods. The arguments # behave just as for the getter macros. Each setter can have a type as well as # an initial value. There is no lazy initialization however since the macro # doesn't generate a getter method. # # For example writing: # # ``` # class Person # setter name # setter age = 0 # setter city : String = "unspecified" # end # ``` # # Is the same as writing: # # ``` # class Person # @age = 0 # @city : String = "unspecified" # # def name=(@name) # end # # def age=(@age) # end # # def city=(@city : String) : String # end # end # ``` # # For class variables we'd have called `class_setter name` that would have # generated a `def self.name=(@@name)` class method instead. # # ## Properties # # The property macros define both getter and setter methods at once. # # For example writing: # # ``` # class Person # property name # end # ``` # # Is equivalent to writing: # # ``` # class Person # getter name # setter name # end # ``` # # Which is the same as writing: # # ``` # class Person # def name # @name # end # # def name=(@name) # end # end # ``` # # Refer to [Getters](#getters) and [Setters](#setters) above for details. The # macros take the exact same arguments. class Object # Returns `true` if this object is equal to *other*. # # Subclasses override this method to provide class-specific meaning. abstract def ==(other) # Returns `true` if this object is not equal to *other*. # # By default this method is implemented as `!(self == other)` # so there's no need to override this unless there's a more efficient # way to do it. def !=(other) !(self == other) end # Shortcut to `!(self =~ other)`. def !~(other) !(self =~ other) end # Case equality. # # The `===` method is used in a `case ... when ... end` expression. # # For example, this code: # # ``` # case value # when x # # something when x # when y # # something when y # end # ``` # # Is equivalent to this code: # # ``` # if x === value # # something when x # elsif y === value # # something when y # end # ``` # # Object simply implements `===` by invoking `==`, but subclasses # (notably `Regex`) can override it to provide meaningful case-equality semantics. def ===(other) self == other end # Pattern match. # # Overridden by descendants (notably `Regex` and `String`) to provide meaningful # pattern-match semantics. def =~(other) nil end # Appends this object's value to *hasher*, and returns the modified *hasher*. # # Usually the macro `def_hash` can be used to generate this method. # Otherwise, invoke `hash(hasher)` on each object's instance variables to # accumulate the result: # # ``` # def hash(hasher) # hasher = @some_ivar.hash(hasher) # hasher = @some_other_ivar.hash(hasher) # hasher # end # ``` abstract def hash(hasher) # Generates an `UInt64` hash value for this object. # # This method must have the property that `a == b` implies `a.hash == b.hash`. # # The hash value is used along with `==` by the `Hash` class to determine if two objects # reference the same hash key. # # Subclasses must not override this method. Instead, they must define `hash(hasher)`, # though usually the macro `def_hash` can be used to generate this method. def hash hash(Crystal::Hasher.new).result end # Returns a nicely readable and concise string representation of this object, # typically intended for users. # # This method should usually **not** be overridden. It delegates to # `#to_s(IO)` which can be overridden for custom implementations. # # Also see `#inspect`. def to_s : String String.build do |io| to_s io end end # Prints a nicely readable and concise string representation of this object, # typically intended for users, to *io*. # # This method is called when an object is interpolated in a string literal: # ``` # "foo #{bar} baz" # calls bar.to_io with the builder for this string # ``` # # `IO#<<` calls this method to append an object to itself: # ``` # io << bar # calls bar.to_s(io) # ``` # # Thus implementations must not interpolate `self` in a string literal or call # `io << self` which both would lead to an endless loop. # # Also see `#inspect(IO)`. abstract def to_s(io : IO) : Nil # Returns an unambiguous and information-rich string representation of this # object, typically intended for developers. # # This method should usually **not** be overridden. It delegates to # `#inspect(IO)` which can be overridden for custom implementations. # # Also see `#to_s`. def inspect : String String.build do |io| inspect io end end # Prints to *io* an unambiguous and information-rich string representation of this # object, typically intended for developers. # # It is similar to `#to_s(IO)`, but often provides more information. Ideally, it should # contain sufficient information to be able to recreate an object with the same value # (given an identical environment). # # For types that don't provide a custom implementation of this method, # default implementation delegates to `#to_s(IO)`. This said, it is advisable to # have an appropriate `#inspect` implementation on every type. Default # implementations are provided by `Struct#inspect` and `Reference#inspect`. # # `::p` and `::p!` use this method to print an object in `STDOUT`. def inspect(io : IO) : Nil to_s io end # Pretty prints `self` into the given printer. # # By default appends a text that is the result of invoking # `#inspect` on `self`. Subclasses should override # for custom pretty printing. def pretty_print(pp : PrettyPrint) : Nil pp.text(inspect) end # Returns a pretty printed version of `self`. def pretty_inspect(width = 79, newline = "\n", indent = 0) : String String.build do |io| PrettyPrint.format(self, io, width, newline, indent) end end # Yields `self` to the block, and then returns `self`. # # The primary purpose of this method is to "tap into" a method chain, # in order to perform operations on intermediate results within the chain. # # ``` # (1..10).tap { |x| puts "original: #{x.inspect}" } # .to_a.tap { |x| puts "array: #{x.inspect}" } # .select { |x| x % 2 == 0 }.tap { |x| puts "evens: #{x.inspect}" } # .map { |x| x*x }.tap { |x| puts "squares: #{x.inspect}" } # ``` def tap(&) yield self self end # Yields `self`. `Nil` overrides this method and doesn't yield. # # This method is useful for dealing with nilable types, to safely # perform operations only when the value is not `nil`. # # ``` # # First program argument in downcase, or nil # ARGV[0]?.try &.downcase # ``` def try(&) yield self end # Returns `true` if `self` is included in the *collection* argument. # # ``` # 10.in?(0..100) # => true # 10.in?({0, 1, 10}) # => true # 10.in?(0, 1, 10) # => true # 10.in?(:foo, :bar) # => false # ``` def in?(collection : Object) : Bool collection.includes?(self) end # :ditto: def in?(*values : Object) : Bool in?(values) end # Returns `self`. # # `Nil` overrides this method and raises `NilAssertionError`, see `Nil#not_nil!`. # # This method can be used to remove `Nil` from a union type. # However, it should be avoided if possible and is often considered a code smell. # Usually, you can write code in a way that the compiler can safely exclude `Nil` types, # for example using [`if var`](https://crystal-lang.org/reference/syntax_and_semantics/if_var.html). # `not_nil!` is only meant as a last resort when there's no other way to explain this to the compiler. # Either way, consider instead raising a concrete exception with a descriptive message. def not_nil! self end # :ditto: # # *message* has no effect. It is only used by `Nil#not_nil!(message = nil)`. def not_nil!(message) # FIXME: the above param-less overload cannot be expressed as an optional # parameter here, because that would copy the receiver if it is a struct; # see https://github.com/crystal-lang/crystal/issues/13263#issuecomment-1492885817 # and also #13265 self end # Returns `self`. # # ``` # str = "hello" # str.itself.object_id == str.object_id # => true # ``` def itself self end # Returns a shallow copy (“duplicate”) of this object. # # In order to create a new object with the same value as an existing one, there # are two possible routes: # # * create a *shallow copy* (`#dup`): Constructs a new object with all its # properties' values identical to the original object's properties. They # are shared references. That means for mutable values that changes to # either object's values will be present in both's. # * create a *deep copy* (`#clone`): Constructs a new object with all its # properties' values being recursive deep copies of the original object's # properties. # There is no shared state and the new object is a completely independent # copy, including everything inside it. This may not be available for every # type. # # A shallow copy is only one level deep whereas a deep copy copies everything # below. # # This distinction is only relevant for compound values. Primitive types # do not have any properties that could be shared or cloned. # In that case, `dup` and `clone` are exactly the same. # # The `#clone` method can't be defined on `Object`. It's not # generically available for every type because cycles could be involved, and # the clone logic might not need to clone everything. # # Many types in the standard library, like `Array`, `Hash`, `Set` and # `Deque`, and all primitive types, define `dup` and `clone`. # # Example: # # ``` # original = {"foo" => [1, 2, 3]} # shallow_copy = original.dup # deep_copy = original.clone # # # "foo" references the same array object for both original and shallow copy, # # but not for a deep copy: # original["foo"] << 4 # shallow_copy["foo"] # => [1, 2, 3, 4] # deep_copy["foo"] # => [1, 2, 3] # # # Assigning new value does not share it to either copy: # original["foo"] = [1] # shallow_copy["foo"] # => [1, 2, 3, 4] # deep_copy["foo"] # => [1, 2, 3] # ``` abstract def dup # Unsafely reinterprets the bytes of an object as being of another `type`. # # This method is useful to treat a type that is represented as a chunk of # bytes as another type where those bytes convey useful information. As an # example, you can check the individual bytes of an `Int32`: # # ``` # 0x01020304.unsafe_as(StaticArray(UInt8, 4)) # => StaticArray[4, 3, 2, 1] # ``` # # Or treat the bytes of a `Float64` as an `Int64`: # # ``` # 1.234_f64.unsafe_as(Int64) # => 4608236261112822104 # ``` # # This method is **unsafe** because it behaves unpredictably when the given # `type` doesn't have the same bytesize as the receiver, or when the given # `type` representation doesn't semantically match the underlying bytes. # # Also note that because `unsafe_as` is a regular method, unlike the pseudo-method # `as`, you can't specify some types in the type grammar using a short notation, so # specifying a static array must always be done as `StaticArray(T, N)`, a tuple # as `Tuple(...)` and so on, never as `UInt8[4]` or `{Int32, Int32}`. def unsafe_as(type : T.class) forall T x = self pointerof(x).as(T*).value end # Delegate *methods* to *to*. # # Note that due to current language limitations this is only useful # when no captured blocks are involved. # # ``` # class StringWrapper # def initialize(@string : String) # end # # delegate downcase, to: @string # delegate gsub, to: @string # delegate empty?, capitalize, to: @string # delegate :[], to: @string # end # # wrapper = StringWrapper.new "HELLO" # wrapper.downcase # => "hello" # wrapper.gsub(/E/, "A") # => "HALLO" # wrapper.empty? # => false # wrapper.capitalize # => "Hello" # ``` macro delegate(*methods, to object) {% if compare_versions(::Crystal::VERSION, "1.12.0-dev") >= 0 %} {% eq_operators = %w(<= >= == != []= ===) %} {% for method in methods %} {% if method.id.ends_with?('=') && !eq_operators.includes?(method.id.stringify) %} def {{method.id}}(arg) {{object.id}}.{{method.id}} arg end {% else %} def {{method.id}}(*args, **options) {{object.id}}.{{method.id}}(*args, **options) end def {{method.id}}(*args, **options) {{object.id}}.{{method.id}}(*args, **options) do |*yield_args| yield *yield_args end end {% end %} {% end %} {% else %} {% for method in methods %} {% if method.id.ends_with?('=') && method.id != "[]=" %} def {{method.id}}(arg) {{object.id}}.{{method.id}} arg end {% else %} def {{method.id}}(*args, **options) {{object.id}}.{{method.id}}(*args, **options) end {% if method.id != "[]=" %} def {{method.id}}(*args, **options) {{object.id}}.{{method.id}}(*args, **options) do |*yield_args| yield *yield_args end end {% end %} {% end %} {% end %} {% end %} end # Defines a `hash(hasher)` that will append a hash value for the given fields. # # ``` # class Person # def initialize(@name, @age) # end # # # Define a hash(hasher) method based on @name and @age # def_hash @name, @age # end # ``` macro def_hash(*fields) def hash(hasher) {% for field in fields %} hasher = {{field.id}}.hash(hasher) {% end %} hasher end end # Defines an `==` method by comparing the given fields. # # The generated `==` method has a `self` restriction. # For classes it will first compare by reference and return `true` # when an object instance is compared with itself, without comparing # any of the fields. # # ``` # class Person # def initialize(@name, @age) # end # # # Define a `==` method that compares @name and @age # def_equals @name, @age # end # ``` macro def_equals(*fields) def ==(other : self) {% if @type.class? %} return true if same?(other) {% end %} {% for field in fields %} return false unless {{field.id}} == other.{{field.id}} {% end %} true end end # Defines `hash` and `==` method from the given fields. # # The generated `==` method has a `self` restriction. # # ``` # class Person # def initialize(@name, @age) # end # # # Define a hash method based on @name and @age # # Define a `==` method that compares @name and @age # def_equals_and_hash @name, @age # end # ``` macro def_equals_and_hash(*fields) def_equals {{fields.splat}} def_hash {{fields.splat}} end # Forwards missing methods to *delegate*. # # ``` # class StringWrapper # def initialize(@string : String) # end # # forward_missing_to @string # end # # wrapper = StringWrapper.new "HELLO" # wrapper.downcase # => "hello" # wrapper.gsub(/E/, "A") # => "HALLO" # ``` macro forward_missing_to(delegate) macro method_missing(call) {{delegate}}.\{{call}} end end # Defines a `clone` method that returns a copy of this object with all # instance variables cloned (`clone` is in turn invoked on them). macro def_clone # Returns a copy of `self` with all instance variables cloned. def clone \{% if @type < ::Reference && !@type.instance_vars.map(&.type).all? { |t| t == ::Bool || t == ::Char || t == ::Symbol || t == ::String || t < ::Number::Primitive } %} exec_recursive_clone do |hash| clone = \{{@type}}.allocate hash[object_id] = clone.object_id clone.initialize_copy(self) ::GC.add_finalizer(clone) if clone.responds_to?(:finalize) clone end \{% else %} clone = \{{@type}}.allocate clone.initialize_copy(self) ::GC.add_finalizer(clone) if clone.responds_to?(:finalize) clone \{% end %} end protected def initialize_copy(other) \{% for ivar in @type.instance_vars %} @\{{ivar.id}} = other.@\{{ivar.id}}.clone \{% end %} end end protected def self.set_crystal_type_id(ptr) ptr.as(Pointer(typeof(crystal_instance_type_id))).value = crystal_instance_type_id ptr end # :nodoc: # # Generates a class getter with a lazy initializer for a thread local # variable. # # Unlike the `ThreadLocal` annotation, the value is guaranteed to always be # reachable by the GC that won't collect it until it's really unreachable. We # achieve that by using the `ThreadLocal` annocation (unreachable by GC) to # speedup direct accesses while still keeping a direct reference on the # current `Thread` object whose lifetime outlives the actual thread. # # For example: # # ``` # class Foo # thread_local(instance : Foo) { Foo.new } # end # # # thread 1: # Foo.instance # => # Foo.instance # => # # # thread 2: # Foo.instance # => # ``` macro thread_local(decl, &constructor) {% raise "The thread_local macro expects a TypeDeclaration" unless decl.is_a?(TypeDeclaration) %} {% name = decl.var.id %} {% tls_name = "__tls_#{@type.name.gsub(/:/, "_")}__#{name}".id %} {% emulated_tls = flag?(:android) || flag?(:openbsd) || (flag?(:win32) && flag?(:gnu)) %} {% unless emulated_tls %} @[ThreadLocal] @@{{name}} : {{decl.type}} | Nil {% end %} def self.{{name}} : {{decl.type}} {% if emulated_tls %} Thread.current.{{tls_name}} ||= {{yield}} {% else %} if (value = @@{{name}}).nil? Thread.current.{{tls_name}} = @@{{name}} = {{yield}} else value end {% end %} end # :nodoc: class ::Thread # :nodoc: property {{tls_name}} : {{decl.type}} | Nil end end end