# typed: strict# frozen_string_literal: truemoduleTapiocamoduleRuntime# This class is responsible for storing and looking up information related to generic types.## The class stores 2 different kinds of data, in two separate lookup tables:# 1. a lookup of generic type instances by name: `@generic_instances`# 2. a lookup of type variable serializer by constant and type variable# instance: `@type_variables`## By storing the above data, we can cheaply query each constant against this registry# to see if it declares any generic type variables. This becomes a simple lookup in the# `@type_variables` hash table with the given constant.## If there is no entry, then we can cheaply know that we can skip generic type# information generation for this type.## On the other hand, if we get a result, then the result will be a hash of type# variable to type variable serializers. This allows us to associate type variables# to the constant names that represent them, easily.moduleGenericTypeRegistry@generic_instances={}#: Hash[String, Module]@type_variables={}.compare_by_identity#: Hash[Module, Array[TypeVariableModule]]classGenericType<T::Types::SimpleextendT::Sig#: (Module raw_type, Module underlying_type) -> voiddefinitialize(raw_type,underlying_type)super(raw_type)@underlying_type=underlying_type#: Moduleend# @override#: (untyped obj) -> booldefvalid?(obj)obj.is_a?(@underlying_type)endendclass<<selfextendT::Sig# This method is responsible for building the name of the instantiated concrete type# and cloning the given constant so that we can return a type that is the same# as the current type but is a different instance and has a different name method.## We cache those cloned instances by their name in `@generic_instances`, so that# we don't keep instantiating a new type every single time it is referenced.# For example, `[Foo[Integer], Foo[Integer], Foo[Integer], Foo[String]]` will only# result in 2 clones (1 for `Foo[Integer]` and another for `Foo[String]`) and# 2 hash lookups (for the other two `Foo[Integer]`s).## This method returns the created or cached clone of the constant.#: (untyped constant, untyped types) -> Moduledefregister_type(constant,types)# Build the name of the instantiated generic type,# something like `"Foo[X, Y, Z]"`type_list=types.map{|type|T::Utils.coerce(type).name}.join(", ")name="#{Reflection.name_of(constant)}[#{type_list}]"# Create a generic type with an overridden `name`# method that returns the name we constructed above.## Also, we try to memoize the generic type based on the name, so that# we don't have to keep recreating them all the time.@generic_instances[name]||=create_generic_type(constant,name)end#: (Object instance) -> booldefgeneric_type_instance?(instance)@generic_instances.values.any?{|generic_type|generic_type===instance}end#: (Module constant) -> Array[TypeVariableModule]?deflookup_type_variables(constant)@type_variables[constant]end# This method is called from intercepted calls to `type_member` and `type_template`.# We get passed all the arguments to those methods, as well as the `T::Types::TypeVariable`# instance generated by the Sorbet defined `type_member`/`type_template` call on `T::Generic`.## This method creates a `String` with that data and stores it in the# `@type_variables` lookup table, keyed by the `constant` and `type_variable`.## Finally, the original `type_variable` is returned from this method, so that the caller# can return it from the original methods as well.#: (untyped constant, TypeVariableModule type_variable) -> voiddefregister_type_variable(constant,type_variable)type_variables=lookup_or_initialize_type_variables(constant)type_variables<<type_variableendprivate#: (Module constant, String name) -> Moduledefcreate_generic_type(constant,name)generic_type=caseconstantwhenClass# For classes, we want to create a subclass, so that an instance of# the generic class `Foo[Bar]` is still a `Foo`. That is:# `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case# if we just clone the class. But subclassing works just fine.create_safe_subclass(constant)else# This can only be a module and it is fine to just clone modules# since they can't have instances and will not have `is_a?` relationships.# Moreover, we never `include`/`extend` any generic modules into the# ancestor tree, so this doesn't become a problem with checking the# instance of a class being `is_a?` of a module type.constant.cloneend# Let's set the `name` and `to_s` methods to return the proper generic namename_proc=->{name}generic_type.define_singleton_method(:name,name_proc)generic_type.define_singleton_method(:to_s,name_proc)override_type=GenericType.new(generic_type,constant)override_type_proc=->{override_type}generic_type.define_singleton_method(:__tapioca_override_type,override_type_proc)# We need to define a `<=` method on the cloned constant, so that Sorbet# can do covariance/contravariance checks on the type variables.## Normally, we would be doing proper covariance/contravariance checks here, but# that is not necessary, since we are not implementing a runtime type checker# here. It is just enough for the checks to pass, so that we can serialize the# signatures, assuming the sigs were well-formed.## So we act like all subtype checks pass.generic_type.define_singleton_method(:<=){|_|true}# Return the generic type we createdgeneric_typeend#: (Class[top] constant) -> Class[top]defcreate_safe_subclass(constant)# Lookup the "inherited" class methodinherited_method=constant.method(:inherited)# and the module that defines itowner=inherited_method.owner# If no one has overridden the inherited method yet, just subclassreturnClass.new(constant)ifClass==ownerbegin# Otherwise, some inherited method could be preventing us# from creating subclasses, so let's override it and rescueowner.send(:define_method,:inherited)do|s|inherited_method.call(s)rescue# Ignoring errorsend# return a subclassClass.new(constant)ensure# Reinstate the original inherited method back.owner.send(:define_method,:inherited,inherited_method)endend#: (Module constant) -> Array[TypeVariableModule]deflookup_or_initialize_type_variables(constant)@type_variables[constant]||=[]endendendendend