Class: Wrapture::ClassSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/wrapture/class_spec.rb

Overview

A description of a class, including its constants, functions, and other details.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(spec, scope: Scope.new) ⇒ ClassSpec

Creates a class spec based on the provided hash spec.

The scope can be provided if available.

The hash must have the following keys:

name

the name of the class

namespace

the namespace to put the class into

equivalent-struct

a hash describing the struct this class wraps

The following keys are optional:

doc

a string containing the documentation for this class

constructors

a list of function specs that can create this class

destructor

a function spec for the destructor of the class

functions

a list of function specs

constants

a list of constant specs



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/wrapture/class_spec.rb', line 92

def initialize(spec, scope: Scope.new)
  @spec = Marshal.load(Marshal.dump(spec))
  TemplateSpec.replace_all_uses(@spec, *scope.templates)

  @spec = ClassSpec.normalize_spec_hash(@spec)

  @struct = if @spec.key?(EQUIVALENT_STRUCT_KEYWORD)
              StructSpec.new(@spec[EQUIVALENT_STRUCT_KEYWORD])
            end

  @functions = @spec['constructors'].map do |constructor_spec|
    full_spec = constructor_spec.dup
    full_spec['name'] = @spec['name']
    full_spec['params'] = constructor_spec['wrapped-function']['params']

    FunctionSpec.new(full_spec, self, constructor: true)
  end

  if @spec.key?('destructor')
    destructor_spec = @spec['destructor'].dup
    destructor_spec['name'] = "~#{@spec['name']}"

    @functions << FunctionSpec.new(destructor_spec, self, destructor: true)
  end

  @spec['functions'].each do |function_spec|
    @functions << FunctionSpec.new(function_spec, self)
  end

  @constants = @spec['constants'].map do |constant_spec|
    ConstantSpec.new(constant_spec)
  end

  @doc = @spec.key?('doc') ? Comment.new(@spec['doc']) : nil

  scope << self
  @scope = scope
end

Instance Attribute Details

#structObject (readonly)

The underlying struct of this class.



75
76
77
# File 'lib/wrapture/class_spec.rb', line 75

def struct
  @struct
end

Class Method Details

.effective_type(spec) ⇒ Object

Gives the effective type of the given class spec hash.



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/wrapture/class_spec.rb', line 54

def self.effective_type(spec)
  inferred_pointer_wrapper = spec['constructors'].any? do |func|
    func['wrapped-function']['return']['type'] == EQUIVALENT_POINTER_KEYWORD
  end

  if spec.key?('type')
    valid_types = %w[pointer struct]
    unless valid_types.include?(spec['type'])
      type_message = "#{spec['type']} is not a valid class type"
      raise InvalidSpecKey.new(type_message, valid_keys: valid_types)
    end

    spec['type']
  elsif inferred_pointer_wrapper
    'pointer'
  else
    'struct'
  end
end

.normalize_spec_hash(spec) ⇒ Object

Normalizes a hash specification of a class. Normalization will check for things like invalid keys, duplicate entries in include lists, and will set missing keys to their default values (for example, an empty list if no includes are given).

If this spec cannot be normalized, for example because it is invalid or it uses an unsupported version type, then an exception is raised.

Raises:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/wrapture/class_spec.rb', line 32

def self.normalize_spec_hash(spec)
  raise NoNamespace unless spec.key?('namespace')
  raise MissingSpecKey, 'name key is required' unless spec.key?('name')

  Comment.validate_doc(spec['doc']) if spec.key?('doc')

  normalized = spec.dup
  normalized.default = []

  normalized['version'] = Wrapture.spec_version(spec)
  normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
  normalized['type'] = ClassSpec.effective_type(normalized)

  if spec.key?('parent')
    includes = Wrapture.normalize_includes(spec['parent']['includes'])
    normalized['parent']['includes'] = includes
  end

  normalized
end

Instance Method Details

#cast(instance, to, from = name) ⇒ Object

Returns a cast of an instance of this class with the provided name to the specified type. Optionally the from parameter may hold the type of the instance, either a reference or a pointer.



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/wrapture/class_spec.rb', line 134

def cast(instance, to, from = name)
  member_access = from.pointer? ? '->' : '.'

  struct = "struct #{@struct.name}"

  if [EQUIVALENT_STRUCT_KEYWORD, struct].include?(to)
    "#{'*' if pointer_wrapper?}#{instance}#{member_access}equivalent"
  elsif [EQUIVALENT_POINTER_KEYWORD, "#{struct} *"].include?(to)
    "#{'&' unless pointer_wrapper?}#{instance}#{member_access}equivalent"
  end
end

#generate_wrappersObject

Generates the wrapper class declaration and definition files.



147
148
149
# File 'lib/wrapture/class_spec.rb', line 147

def generate_wrappers
  [generate_declaration_file, generate_definition_file]
end

#nameObject

The name of the class



152
153
154
# File 'lib/wrapture/class_spec.rb', line 152

def name
  @spec['name']
end

#overloads?(parent_spec) ⇒ Boolean

True if this class overloads the given one. A class is considered an overload if its parent is the given class, it has the same equivalent struct name, and the equivalent struct has a set of rules. The overloaded class cannot have any rules in its equivalent struct, or it will not be overloaded.

Returns:

  • (Boolean)


161
162
163
164
165
166
167
# File 'lib/wrapture/class_spec.rb', line 161

def overloads?(parent_spec)
  return false unless parent_spec.struct&.rules&.empty?

  parent_spec.struct.name == struct_name &&
    parent_spec.name == parent_name &&
    !@struct.rules.empty?
end

#parent_nameObject

The name of the parent of this class, or nil if there is no parent.



170
171
172
# File 'lib/wrapture/class_spec.rb', line 170

def parent_name
  @spec['parent']['name'] if child?
end

#pointer_wrapper?Boolean

Determines if this class is a wrapper for a struct pointer or not.

Returns:

  • (Boolean)


175
176
177
# File 'lib/wrapture/class_spec.rb', line 175

def pointer_wrapper?
  @spec['type'] == 'pointer'
end

#struct_nameObject

The name of the equivalent struct of this class.



180
181
182
# File 'lib/wrapture/class_spec.rb', line 180

def struct_name
  @struct.name
end

#this_structObject

Gives a code snippet that accesses the equivalent struct from within the class using the 'this' keyword.



186
187
188
189
190
191
192
# File 'lib/wrapture/class_spec.rb', line 186

def this_struct
  if pointer_wrapper?
    '*(this->equivalent)'
  else
    'this->equivalent'
  end
end

#this_struct_pointerObject

Gives a code snippet that accesses the equivalent struct pointer from within the class using the 'this' keyword.



196
197
198
# File 'lib/wrapture/class_spec.rb', line 196

def this_struct_pointer
  "#{'&' unless pointer_wrapper?}this->equivalent"
end

#type(type) ⇒ Object

Returns the ClassSpec for the given type in this class's scope.



201
202
203
# File 'lib/wrapture/class_spec.rb', line 201

def type(type)
  @scope.type(type)
end

#type?(type) ⇒ Boolean

Returns true if the given type exists in this class's scope.

Returns:

  • (Boolean)


206
207
208
# File 'lib/wrapture/class_spec.rb', line 206

def type?(type)
  @scope.type?(type)
end