class EL_INTERNAL

(source code)

description

Internal reflection routines accessible via EL_MODULE_EIFFEL

notes

The INTERNAL class has a problem with routines that use the once function reflected_object because the call will retain a reference to the argument inside the once object. Calling dynamic_type for example will retain a reference to the object being queryed.

This can be a problem in situations which require that the object be GC collected when all references are removed. The object could be associated with a Java VM object for example.

note
	description: "Internal reflection routines accessible via ${EL_MODULE_EIFFEL}"
	notes: "[
		The ${INTERNAL} class has a problem with routines that use the once function
		`reflected_object' because the call will retain a reference to the argument inside
		the once object. Calling `dynamic_type' for example will retain a reference to the
		object being queryed.
		
		This can be a problem in situations which require that the object be GC collected when
		all references are removed. The object could be associated with a Java VM object for example.
	]"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-10-09 11:50:08 GMT (Wednesday 9th October 2024)"
	revision: "38"

class
	EL_INTERNAL

inherit
	INTERNAL
		redefine
			dynamic_type
		end

	SED_UTILITIES
		rename
			abstract_type as abstract_type_of_type
		export
			{ANY} abstract_type_of_type
		end

	EL_EIFFEL_C_API

	EL_REFLECTION_CONSTANTS; EL_STRING_8_CONSTANTS

	EL_SHARED_CLASS_ID
		rename
			Class_id as Class_id_
		end

	EL_SHARED_FACTORIES

create
	make

feature {NONE} -- Initialization

	make
		do
			class_id := Class_id_
		end

feature -- Status type flag

	is_type_composite (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_composite) > 0
		end

	is_type_dead (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_dead) > 0
		end

	is_type_declared_expanded (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_declared_expanded) > 0
		end

	is_type_deferred (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_deferred) > 0
		end

	is_type_expanded (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_expanded) > 0
		end

	is_type_frozen (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_frozen) > 0
		end

	is_type_special (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_special) > 0
		end

	is_type_tuple (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Is_tuple) > 0
		end

	type_has_dispose (type_flags: NATURAL_16): BOOLEAN
		do
			Result := (type_flags & {EL_TYPE_FLAG}.Has_dispose) > 0
		end

feature -- Type status

	field_conforms_to_collection (basic_type, type_id: INTEGER): BOOLEAN
		-- True if `type_id' conforms to COLLECTION [X] where x is a string or an expanded type
		do
			if is_reference (basic_type) then
				Result := type_conforms_to (type_id, class_id.COLLECTION__ANY)
			end
		end

	field_conforms_to_date_time (basic_type, type_id: INTEGER): BOOLEAN
		do
			Result := is_reference (basic_type) and then field_conforms_to (type_id, class_id.DATE_TIME)
		end

	field_conforms_to_one_of (basic_type, type_id: INTEGER; types: ARRAY [INTEGER]): BOOLEAN
		-- True if `type_id' conforms to one of `types'
		do
			Result := is_reference (basic_type) and then conforms_to_one_of (type_id, types)
		end

	is_generic (type_id: INTEGER): BOOLEAN
		do
		-- quick test
			if eif_generic_parameter_count (type_id) > 0  then
				Result := True
			else
		-- slow test
				Result := type_of_type (type_id).generic_parameter_count > 0
			end
		end

	is_reference (basic_type: INTEGER): BOOLEAN
		do
			Result := basic_type = Reference_type
		end

	is_storable_collection_type (type_id: INTEGER): BOOLEAN
		local
			item_type_id: INTEGER
		do
			item_type_id := collection_item_type (type_id)
			Result := is_storable_type (abstract_type_of_type (item_type_id), item_type_id)
		end

	is_storable_type (basic_type, type_id: INTEGER): BOOLEAN
		-- `True' if type is storable using `EL_STORABLE' interface
		local
			tuple_types: EL_TUPLE_TYPE_ARRAY
		do
			if basic_type = Reference_type then
				if type_conforms_to (type_id, class_id.TUPLE) then
					create tuple_types.make_from_static (type_id)
--					TUPLE items must be expanded or strings
					Result := across tuple_types as type all
						type.item.is_expanded
							or else String_reference_types.there_exists (agent type_conforms_to (type.item.type_id, ?))
					end
				else
					Result := Storable_reference_types.there_exists (agent type_conforms_to (type_id, ?))
				end
			else
				Result := True
			end
		end

	is_string_or_expanded_type (basic_type, type_id: INTEGER): BOOLEAN
		do
			inspect basic_type
				when Reference_type then
					Result := String_reference_types.there_exists (agent type_conforms_to (type_id, ?))

				when Pointer_type then
					-- Exclude pointer
			else
				Result := True
			end
		end

	is_table_type (basic_type, type_id: INTEGER): BOOLEAN
		do
			if basic_type = Reference_type then
				Result := type_conforms_to (type_id, ({HASH_TABLE [ANY, HASHABLE]}).type_id)
			end
		end

	is_type_convertable_from_string (basic_type, type_id: INTEGER): BOOLEAN
		-- True if field is either an expanded type (with the exception of POINTER) or conforms to one of following types
		-- 	STRING_GENERAL, EL_DATE_TIME, EL_MAKEABLE_FROM_STRING_GENERAL, BOOLEAN_REF, EL_PATH
		do
			inspect basic_type
				when Reference_type then
					Result := Reference_field_list.has_conforming (type_id)

				when Pointer_type then
					-- Exclude pointer
			else
				-- is expanded type
				Result := True
			end
		end

	is_type_in_set (type_id: INTEGER; set: SPECIAL [INTEGER]): BOOLEAN
		local
			i: INTEGER
		do
			from until i = set.count or Result loop
				Result := type_id = set [i]
				i := i + 1
			end
		end

feature -- Conformance checking

	is_bag_type (type_id: INTEGER): BOOLEAN
		do
			Result := field_conforms_to (type_id, class_id.BAG__ANY)
		end

	is_comparable_type (type_id: INTEGER): BOOLEAN
		do
			Result := field_conforms_to (type_id, class_id.COMPARABLE)
		end

	is_conforming_to (object: ANY; type_id: INTEGER): BOOLEAN
		do
			Result := field_conforms_to ({ISE_RUNTIME}.dynamic_type (object), type_id)
		end

	is_readable_string_32 (str: ANY): BOOLEAN
		do
			Result := is_type_in_set (dynamic_type (str), class_id.readable_string_32_types)
		end

feature -- Access

	abstract_type (object: ANY): INTEGER
		do
			Result := abstract_type_of_type ({ISE_RUNTIME}.dynamic_type (object))
		end

	collection_item_type (type_id: INTEGER): INTEGER
		require
			valid_id: is_collection_type (type_id)
		do
			if generic_count_of_type (type_id) > 0 then
				Result := generic_dynamic_type_of_type (type_id, 1)

			elseif type_conforms_to (type_id, class_id.ARRAYED_LIST__ANY)
				and then attached Arrayed_list_factory.new_item_from_type_id (type_id) as list
			then
				Result := list.area.generating_type.generic_parameter_type (1).type_id
			end
		end

	dynamic_type (object: separate ANY): INTEGER
		do
			Result := {ISE_RUNTIME}.dynamic_type (object)
		end

	new_object (type: TYPE [ANY]): detachable ANY
		do
			Result := new_instance_of (type.type_id)
		end

	reflected (a_object: ANY): EL_REFLECTED_REFERENCE_OBJECT
		do
			create Result.make (a_object)
		end

	type_from_string (class_type: READABLE_STRING_GENERAL): detachable TYPE [ANY]
		local
			type_id: INTEGER
		do
			type_id := dynamic_type_from_string (class_type)
			if type_id > 0 then
				Result := type_of_type (type_id)
			end
		end

	type_flag_names (type_flags: NATURAL_16): EL_STRING_8_LIST
		local
			i: INTEGER
		do
			create Result.make (7)
			from i := 1 until i > Type_status_array.count loop
				if type_flags & (1 |<< (i + 5)).to_natural_16 > 0 then
					Result.extend (Type_status_array [i])
				end
				i := i + 1
			end
		end

feature -- Constants

	Basic_types: EL_ARRAYED_LIST [TYPE [ANY]]
		once
			create Result.make (Special_type_mapping.count)
			across Special_type_mapping as table loop
				Result.extend (type_of_type (table.key))
			end
		end

	Object_overhead: INTEGER = 32
		-- memory overhead for any object excluding all attributes

feature -- Contract Support

	is_collection_type (type_id: INTEGER): BOOLEAN
		do
			Result := type_conforms_to (type_id, class_id.COLLECTION__ANY)
		end

feature {NONE} -- Implementation

	conforms_to_one_of (type_id: INTEGER; types: ARRAY [INTEGER]): BOOLEAN
		-- True if `type_id' conforms to one of `types'
		local
			i: INTEGER
		do
			from i := 1 until Result or i > types.count loop
				Result := field_conforms_to (type_id, types [i])
				i := i + 1
			end
		end

feature {NONE} -- Internal attributes

	class_id: EL_CLASS_TYPE_ID_ENUM

feature {NONE} -- Constants

	Type_status_array: EL_STRING_8_LIST
		once
			Result := "tuple, special, declared-expanded, expanded, has-dispose, composite, deferred, frozen, dead"
		end

end