wrapture 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/wrapture +26 -2
- data/lib/wrapture.rb +6 -1
- data/lib/wrapture/action_spec.rb +2 -3
- data/lib/wrapture/class_spec.rb +101 -53
- data/lib/wrapture/comment.rb +106 -0
- data/lib/wrapture/constant_spec.rb +29 -4
- data/lib/wrapture/constants.rb +9 -1
- data/lib/wrapture/enum_spec.rb +154 -0
- data/lib/wrapture/errors.rb +27 -1
- data/lib/wrapture/function_spec.rb +198 -94
- data/lib/wrapture/normalize.rb +2 -2
- data/lib/wrapture/param_spec.rb +132 -0
- data/lib/wrapture/rule_spec.rb +14 -9
- data/lib/wrapture/scope.rb +46 -14
- data/lib/wrapture/struct_spec.rb +5 -3
- data/lib/wrapture/template_spec.rb +432 -0
- data/lib/wrapture/type_spec.rb +156 -0
- data/lib/wrapture/version.rb +19 -1
- data/lib/wrapture/wrapped_function_spec.rb +2 -0
- metadata +9 -3
@@ -1,6 +1,22 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
1
3
|
# frozen_string_literal: true
|
2
4
|
|
3
|
-
|
5
|
+
#--
|
6
|
+
# Copyright 2019-2020 Joel E. Anderson
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#++
|
4
20
|
|
5
21
|
module Wrapture
|
6
22
|
# A description of a constant.
|
@@ -10,6 +26,8 @@ module Wrapture
|
|
10
26
|
# will set missing keys to their default value (for example, an empty list
|
11
27
|
# if no includes are given).
|
12
28
|
def self.normalize_spec_hash(spec)
|
29
|
+
Comment.validate_doc(spec['doc']) if spec.key?('doc')
|
30
|
+
|
13
31
|
normalized = spec.dup
|
14
32
|
|
15
33
|
normalized['version'] = Wrapture.spec_version(spec)
|
@@ -26,8 +44,13 @@ module Wrapture
|
|
26
44
|
# value:: the value to assign to the constant
|
27
45
|
# includes:: a list of includes that need to be added in order for this
|
28
46
|
# constant to be valid (for example, includes for the type and value).
|
47
|
+
#
|
48
|
+
# The following keys are optional:
|
49
|
+
# doc:: a string containing the documentation for this constant
|
29
50
|
def initialize(spec)
|
30
|
-
@spec = ConstantSpec.normalize_spec_hash
|
51
|
+
@spec = ConstantSpec.normalize_spec_hash(spec)
|
52
|
+
@doc = @spec.key?('doc') ? Comment.new(@spec['doc']) : nil
|
53
|
+
@type = TypeSpec.new(@spec['type'])
|
31
54
|
end
|
32
55
|
|
33
56
|
# A list of includes needed for the declaration of this constant.
|
@@ -40,9 +63,11 @@ module Wrapture
|
|
40
63
|
@spec['includes'].dup
|
41
64
|
end
|
42
65
|
|
43
|
-
#
|
66
|
+
# Yields each line of the declaration of this constant, including any
|
67
|
+
# documentation.
|
44
68
|
def declaration
|
45
|
-
|
69
|
+
@doc&.format_as_doxygen(max_line_length: 76) { |line| yield line }
|
70
|
+
yield "static const #{@type.variable(@spec['name'])};"
|
46
71
|
end
|
47
72
|
|
48
73
|
# The definition of this constant.
|
data/lib/wrapture/constants.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
#--
|
5
6
|
# Copyright 2019-2020 Joel E. Anderson
|
6
7
|
#
|
7
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,6 +16,7 @@
|
|
15
16
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
17
|
# See the License for the specific language governing permissions and
|
17
18
|
# limitations under the License.
|
19
|
+
#++
|
18
20
|
|
19
21
|
module Wrapture
|
20
22
|
# A string denoting an equivalent struct type or value.
|
@@ -26,7 +28,13 @@ module Wrapture
|
|
26
28
|
# A string denoting the return value of a wrapped function call.
|
27
29
|
RETURN_VALUE_KEYWORD = 'return-value'
|
28
30
|
|
31
|
+
# A string denoting a reference to the object a method is called on.
|
32
|
+
SELF_REFERENCE_KEYWORD = 'self-reference'
|
33
|
+
|
34
|
+
# A string denoting a reference to a template.
|
35
|
+
TEMPLATE_USE_KEYWORD = 'use-template'
|
36
|
+
|
29
37
|
# A list of all keywords.
|
30
38
|
KEYWORDS = [EQUIVALENT_STRUCT_KEYWORD, EQUIVALENT_POINTER_KEYWORD,
|
31
|
-
RETURN_VALUE_KEYWORD].freeze
|
39
|
+
RETURN_VALUE_KEYWORD, TEMPLATE_USE_KEYWORD].freeze
|
32
40
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
5
|
+
#--
|
6
|
+
# Copyright 2020 Joel E. Anderson
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#++
|
20
|
+
|
21
|
+
module Wrapture
|
22
|
+
# A description of an enumeration.
|
23
|
+
class EnumSpec
|
24
|
+
# Returns a normalized copy of a hash specification of an enumeration in
|
25
|
+
# place. See normalize_spec_hash! for details.
|
26
|
+
def self.normalize_spec_hash(spec)
|
27
|
+
normalize_spec_hash!(Marshal.load(Marshal.dump(spec)))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Normalizes a hash specification of an enumeration in place. Normalization
|
31
|
+
# will remove duplicate entries in include lists and check for a name key.
|
32
|
+
def self.normalize_spec_hash!(spec)
|
33
|
+
unless spec.key?('name')
|
34
|
+
raise MissingSpecKey, 'a name is required for enumerations'
|
35
|
+
end
|
36
|
+
|
37
|
+
if spec.key?('elements')
|
38
|
+
unless spec['elements'].is_a?(Array)
|
39
|
+
raise InvalidSpecKey, 'the elements key must be an array'
|
40
|
+
end
|
41
|
+
else
|
42
|
+
raise MissingSpecKey, 'elements are required for enumerations'
|
43
|
+
end
|
44
|
+
|
45
|
+
spec['includes'] = Wrapture.normalize_includes(spec['includes'])
|
46
|
+
spec['elements'].each do |element|
|
47
|
+
element['includes'] = Wrapture.normalize_includes(element['includes'])
|
48
|
+
end
|
49
|
+
|
50
|
+
spec
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates an enumeration specification based on the provided hash spec.
|
54
|
+
def initialize(spec)
|
55
|
+
@spec = EnumSpec.normalize_spec_hash(spec)
|
56
|
+
|
57
|
+
@doc = Comment.new(@spec.fetch('doc', nil))
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generates the wrapper definition file.
|
61
|
+
def generate_wrapper
|
62
|
+
filename = "#{@spec['name']}.hpp"
|
63
|
+
|
64
|
+
File.open(filename, 'w') do |file|
|
65
|
+
definition_contents do |line|
|
66
|
+
file.puts(line)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
[filename]
|
71
|
+
end
|
72
|
+
|
73
|
+
# The name of the enumeration.
|
74
|
+
def name
|
75
|
+
@spec['name']
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Yields each line of the definition of the wrapper for this enum.
|
81
|
+
def definition_contents
|
82
|
+
indent = 0
|
83
|
+
|
84
|
+
yield "#ifndef #{header_guard}"
|
85
|
+
yield "#define #{header_guard}"
|
86
|
+
yield
|
87
|
+
|
88
|
+
definition_includes.each { |filename| yield "#include <#{filename}>" }
|
89
|
+
yield
|
90
|
+
|
91
|
+
if @spec.key?('namespace')
|
92
|
+
yield "namespace #{@spec['namespace']} {"
|
93
|
+
yield
|
94
|
+
indent += 2
|
95
|
+
end
|
96
|
+
|
97
|
+
@doc.format_as_doxygen(max_line_length: 76) do |line|
|
98
|
+
yield "#{' ' * indent}#{line}"
|
99
|
+
end
|
100
|
+
|
101
|
+
yield "#{' ' * indent}enum class #{name} {"
|
102
|
+
indent += 2
|
103
|
+
|
104
|
+
elements = @spec['elements']
|
105
|
+
elements[0...-1].each do |element|
|
106
|
+
element_doc(element) { |line| yield "#{' ' * indent}#{line}" }
|
107
|
+
element_definition(element) { |line| yield "#{' ' * indent}#{line}," }
|
108
|
+
end
|
109
|
+
|
110
|
+
element_doc(elements.last) { |line| yield "#{' ' * indent}#{line}" }
|
111
|
+
element_definition(elements.last) do |line|
|
112
|
+
yield "#{' ' * indent}#{line}"
|
113
|
+
end
|
114
|
+
|
115
|
+
indent -= 2
|
116
|
+
yield "#{' ' * indent}};"
|
117
|
+
yield
|
118
|
+
yield '}' if @spec.key?('namespace')
|
119
|
+
yield
|
120
|
+
yield "#endif /* #{header_guard} */"
|
121
|
+
end
|
122
|
+
|
123
|
+
# A list of the includes needed for the definition of the enumeration.
|
124
|
+
def definition_includes
|
125
|
+
includes = @spec['includes'].dup
|
126
|
+
|
127
|
+
@spec['elements'].each do |element|
|
128
|
+
includes.concat(element['includes'])
|
129
|
+
end
|
130
|
+
|
131
|
+
includes.uniq
|
132
|
+
end
|
133
|
+
|
134
|
+
# Yields each line of the definition of an element.
|
135
|
+
def element_definition(element)
|
136
|
+
if element.key?('value')
|
137
|
+
yield "#{element['name']} = #{element['value']}"
|
138
|
+
else
|
139
|
+
yield element['name']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Yields each line of the documentation for an element.
|
144
|
+
def element_doc(element)
|
145
|
+
doc = Comment.new(element.fetch('doc', nil))
|
146
|
+
doc.format_as_doxygen(max_line_length: 74) { |line| yield line }
|
147
|
+
end
|
148
|
+
|
149
|
+
# The header guard for the enumeration.
|
150
|
+
def header_guard
|
151
|
+
"__#{@spec['name'].upcase}_HPP"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/wrapture/errors.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
-
|
5
|
+
#--
|
6
|
+
# Copyright 2019-2020 Joel E. Anderson
|
6
7
|
#
|
7
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
9
|
# you may not use this file except in compliance with the License.
|
@@ -15,12 +16,29 @@
|
|
15
16
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
17
|
# See the License for the specific language governing permissions and
|
17
18
|
# limitations under the License.
|
19
|
+
#++
|
18
20
|
|
19
21
|
module Wrapture
|
20
22
|
# An error from the Wrapture library
|
21
23
|
class WraptureError < StandardError
|
22
24
|
end
|
23
25
|
|
26
|
+
# A documentation string is invalid.
|
27
|
+
class InvalidDoc < WraptureError
|
28
|
+
# Creates an InvalidDoc with the given message.
|
29
|
+
def initialize(message)
|
30
|
+
super(message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# A template has been invoked in an unsupported way.
|
35
|
+
class InvalidTemplateUsage < WraptureError
|
36
|
+
# Creates an InvalidTemplateUsage with the given message.
|
37
|
+
def initialize(message)
|
38
|
+
super(message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
24
42
|
# The spec has a key that is not valid.
|
25
43
|
class InvalidSpecKey < WraptureError
|
26
44
|
# Creates an InvalidSpecKey with the given message. A list of valid values
|
@@ -51,6 +69,14 @@ module Wrapture
|
|
51
69
|
class NoNamespace < WraptureError
|
52
70
|
end
|
53
71
|
|
72
|
+
# The spec cannot be defined due to missing information.
|
73
|
+
class UndefinableSpec < WraptureError
|
74
|
+
# Creates an UndefinableSpec with error +message+.
|
75
|
+
def initialize(message)
|
76
|
+
super(message)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
54
80
|
# The spec version is not supported by this version of Wrapture.
|
55
81
|
class UnsupportedSpecVersion < WraptureError
|
56
82
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# frozen_string_literal: true
|
4
4
|
|
5
|
+
#--
|
5
6
|
# Copyright 2019-2020 Joel E. Anderson
|
6
7
|
#
|
7
8
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,43 +16,38 @@
|
|
15
16
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
17
|
# See the License for the specific language governing permissions and
|
17
18
|
# limitations under the License.
|
18
|
-
|
19
|
-
require 'wrapture/constants'
|
20
|
-
require 'wrapture/errors'
|
21
|
-
require 'wrapture/scope'
|
22
|
-
require 'wrapture/wrapped_function_spec'
|
19
|
+
#++
|
23
20
|
|
24
21
|
module Wrapture
|
25
22
|
# A description of a function to be generated, including details about the
|
26
23
|
# underlying implementation.
|
27
24
|
class FunctionSpec
|
25
|
+
# Returns a copy of the return type specification +spec+.
|
26
|
+
def self.normalize_return_hash(spec)
|
27
|
+
if spec.nil?
|
28
|
+
{ 'type' => 'void', 'includes' => [] }
|
29
|
+
else
|
30
|
+
normalized = Marshal.load(Marshal.dump(spec))
|
31
|
+
Comment.validate_doc(spec['doc']) if spec.key?('doc')
|
32
|
+
normalized['type'] ||= 'void'
|
33
|
+
normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
|
34
|
+
normalized
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
28
38
|
# Normalizes a hash specification of a function. Normalization will check
|
29
39
|
# for things like invalid keys, duplicate entries in include lists, and will
|
30
40
|
# set missing keys to their default values (for example, an empty list if no
|
31
41
|
# includes are given).
|
32
42
|
def self.normalize_spec_hash(spec)
|
43
|
+
Comment.validate_doc(spec['doc']) if spec.key?('doc')
|
44
|
+
|
33
45
|
normalized = spec.dup
|
34
|
-
param_types = {}
|
35
46
|
|
36
47
|
normalized['version'] = Wrapture.spec_version(spec)
|
37
48
|
normalized['virtual'] = Wrapture.normalize_boolean(spec, 'virtual')
|
38
|
-
|
39
|
-
normalized['
|
40
|
-
normalized['params'].each do |param_spec|
|
41
|
-
param_types[param_spec['name']] = param_spec['type']
|
42
|
-
includes = Wrapture.normalize_includes(param_spec['includes'])
|
43
|
-
param_spec['includes'] = includes
|
44
|
-
end
|
45
|
-
|
46
|
-
if normalized['return'].nil?
|
47
|
-
normalized['return'] = {}
|
48
|
-
normalized['return']['type'] = 'void'
|
49
|
-
normalized['return']['includes'] = []
|
50
|
-
else
|
51
|
-
normalized['return']['type'] ||= 'void'
|
52
|
-
includes = Wrapture.normalize_includes(spec['return']['includes'])
|
53
|
-
normalized['return']['includes'] = includes
|
54
|
-
end
|
49
|
+
normalized['params'] = ParamSpec.normalize_param_list(spec['params'])
|
50
|
+
normalized['return'] = normalize_return_hash(spec['return'])
|
55
51
|
|
56
52
|
overload = Wrapture.normalize_boolean(normalized['return'], 'overloaded')
|
57
53
|
normalized['return']['overloaded'] = overload
|
@@ -66,22 +62,55 @@ module Wrapture
|
|
66
62
|
# params:: a list of parameter specifications
|
67
63
|
# wrapped-function:: a hash describing the function to be wrapped
|
68
64
|
#
|
65
|
+
# Each parameter specification must have a 'name' key with the name of the
|
66
|
+
# parameter and a 'type' key with its type. The type key may be ommitted
|
67
|
+
# if the name of the parameter is '...' in which case the generated function
|
68
|
+
# will be made variadic. It may optionally have an 'includes' key with
|
69
|
+
# includes that are required (for example to support the type) and/or a
|
70
|
+
# 'doc' key with documentation of the parameter.
|
71
|
+
#
|
72
|
+
# Only one parameter named '...' is allowed in a specification. If more than
|
73
|
+
# one is provided, then only the first encountered will be used. This
|
74
|
+
# parameter should also be last - if it is not, it will be moved to the end
|
75
|
+
# of the parameter list during normalization.
|
76
|
+
#
|
69
77
|
# The wrapped-function must have a 'name' key with the name of the function,
|
70
78
|
# and a 'params' key with a list of parameters (each a hash with a 'name'
|
71
79
|
# and 'type' key). Optionally, it may also include an 'includes' key with a
|
72
|
-
# list of includes that are needed for this function to compile.
|
80
|
+
# list of includes that are needed for this function to compile. The wrapped
|
81
|
+
# function may be left out entirely, but the function will not be definable
|
82
|
+
# if this is the case.
|
73
83
|
#
|
74
84
|
# The following keys are optional:
|
75
|
-
#
|
85
|
+
# doc:: a string containing the documentation for this function
|
86
|
+
# return:: a specification of the return value for this function
|
87
|
+
# static:: set to true if this is a static function
|
88
|
+
#
|
89
|
+
# The return specification may have either a 'type' key with the name of the
|
90
|
+
# type the function returns, and/or a 'doc' key with documentation on the
|
91
|
+
# return value itself. If neither of these is needed, then the return
|
92
|
+
# specification may simply be omitted.
|
93
|
+
#
|
94
|
+
# The 'type' key of the return spec may also be set to 'self-reference'
|
95
|
+
# which will have the function return a reference to the instance it was
|
96
|
+
# called on. Of course, this cannot be used from a function that is not a
|
97
|
+
# class method.
|
76
98
|
def initialize(spec, owner = Scope.new, constructor: false,
|
77
99
|
destructor: false)
|
78
100
|
@owner = owner
|
79
101
|
@spec = FunctionSpec.normalize_spec_hash(spec)
|
80
|
-
@wrapped =
|
102
|
+
@wrapped = if @spec.key?('wrapped-function')
|
103
|
+
WrappedFunctionSpec.new(@spec['wrapped-function'])
|
104
|
+
end
|
105
|
+
@params = ParamSpec.new_list(@spec['params'])
|
106
|
+
@return_type = TypeSpec.new(@spec['return']['type'])
|
81
107
|
@constructor = constructor
|
82
108
|
@destructor = destructor
|
83
109
|
end
|
84
110
|
|
111
|
+
# A TypeSpec describing the return type of this function.
|
112
|
+
attr_reader :return_type
|
113
|
+
|
85
114
|
# True if the function is a constructor, false otherwise.
|
86
115
|
def constructor?
|
87
116
|
@constructor
|
@@ -90,61 +119,89 @@ module Wrapture
|
|
90
119
|
# A list of includes needed for the declaration of the function.
|
91
120
|
def declaration_includes
|
92
121
|
includes = @spec['return']['includes'].dup
|
93
|
-
includes.concat(
|
122
|
+
@params.each { |param| includes.concat(param.includes) }
|
123
|
+
includes.concat(@return_type.includes)
|
94
124
|
includes.uniq
|
95
125
|
end
|
96
126
|
|
127
|
+
# True if this function can be defined.
|
128
|
+
def definable?
|
129
|
+
definable_check
|
130
|
+
rescue UndefinableSpec
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
97
134
|
# A list of includes needed for the definition of the function.
|
98
135
|
def definition_includes
|
99
136
|
includes = @wrapped.includes
|
100
137
|
includes.concat(@spec['return']['includes'])
|
101
|
-
includes.concat(
|
138
|
+
@params.each { |param| includes.concat(param.includes) }
|
139
|
+
includes.concat(@return_type.includes)
|
140
|
+
includes << 'stdarg.h' if variadic?
|
102
141
|
includes.uniq
|
103
142
|
end
|
104
143
|
|
105
|
-
#
|
106
|
-
|
107
|
-
|
108
|
-
|
144
|
+
# The name of the function.
|
145
|
+
def name
|
146
|
+
@spec['name']
|
147
|
+
end
|
109
148
|
|
110
|
-
|
149
|
+
# A string with the parameter list for this function.
|
150
|
+
def param_list
|
151
|
+
ParamSpec.signature(@params, self)
|
152
|
+
end
|
111
153
|
|
112
|
-
|
113
|
-
|
114
|
-
|
154
|
+
# The name of the function with the class name, if it exists.
|
155
|
+
def qualified_name
|
156
|
+
if @owner.is_a?(ClassSpec)
|
157
|
+
"#{@owner.name}::#{name}"
|
158
|
+
else
|
159
|
+
name
|
115
160
|
end
|
116
|
-
|
117
|
-
params.join(', ')
|
118
161
|
end
|
119
162
|
|
120
163
|
# Gives an expression for calling a given parameter within this function.
|
121
164
|
# Equivalent structs and pointers are resolved, as well as casts between
|
122
165
|
# types if they are known within the scope of this function.
|
123
166
|
def resolve_wrapped_param(param_spec)
|
124
|
-
used_param = @
|
167
|
+
used_param = @params.find { |p| p.name == param_spec['value'] }
|
125
168
|
|
126
169
|
if param_spec['value'] == EQUIVALENT_STRUCT_KEYWORD
|
127
170
|
@owner.this_struct
|
128
171
|
elsif param_spec['value'] == EQUIVALENT_POINTER_KEYWORD
|
129
172
|
@owner.this_struct_pointer
|
130
|
-
elsif
|
131
|
-
|
132
|
-
|
133
|
-
param_class = @owner.type(used_param
|
134
|
-
param_class.
|
173
|
+
elsif param_spec['value'] == '...'
|
174
|
+
'variadic_args'
|
175
|
+
elsif castable?(param_spec)
|
176
|
+
param_class = @owner.type(used_param.type)
|
177
|
+
param_class.cast(used_param.name,
|
178
|
+
param_spec['type'],
|
179
|
+
used_param.type)
|
135
180
|
else
|
136
181
|
param_spec['value']
|
137
182
|
end
|
138
183
|
end
|
139
184
|
|
185
|
+
# Calls return_expression on the return type of this function. +func_name+
|
186
|
+
# is passed to return_expression if provided.
|
187
|
+
def return_expression(func_name: name)
|
188
|
+
resolved_return.return_expression(self, func_name: func_name)
|
189
|
+
end
|
190
|
+
|
140
191
|
# The signature of the function.
|
141
192
|
def signature
|
142
|
-
"#{
|
193
|
+
"#{name}( #{param_list} )"
|
143
194
|
end
|
144
195
|
|
145
|
-
#
|
196
|
+
# Yields each line of the declaration of the function, including any
|
197
|
+
# documentation.
|
146
198
|
def declaration
|
147
|
-
|
199
|
+
doc.format_as_doxygen(max_line_length: 76) { |line| yield line }
|
200
|
+
|
201
|
+
if @constructor || @destructor
|
202
|
+
yield "#{signature};"
|
203
|
+
return
|
204
|
+
end
|
148
205
|
|
149
206
|
modifier_prefix = if @spec['static']
|
150
207
|
'static '
|
@@ -153,39 +210,72 @@ module Wrapture
|
|
153
210
|
else
|
154
211
|
''
|
155
212
|
end
|
156
|
-
|
213
|
+
|
214
|
+
yield "#{modifier_prefix}#{return_expression};"
|
157
215
|
end
|
158
216
|
|
159
|
-
# Gives the definition of the function
|
160
|
-
def definition
|
161
|
-
|
217
|
+
# Gives the definition of the function in a block, line by line.
|
218
|
+
def definition
|
219
|
+
definable_check
|
162
220
|
|
163
|
-
|
164
|
-
yield " #{@wrapped.return_val_type} return_val;"
|
165
|
-
yield
|
166
|
-
end
|
221
|
+
yield "#{return_expression(func_name: qualified_name)} {"
|
167
222
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
elsif @wrapped.error_check?
|
172
|
-
"return_val = #{call}"
|
173
|
-
elsif returns_value?
|
174
|
-
"return #{return_cast(call)}"
|
175
|
-
else
|
176
|
-
call
|
177
|
-
end
|
178
|
-
|
179
|
-
yield " #{call_line};"
|
223
|
+
locals { |declaration| yield " #{declaration}" }
|
224
|
+
|
225
|
+
yield " va_start( variadic_args, #{@params[-2].name} );" if variadic?
|
180
226
|
|
181
227
|
if @wrapped.error_check?
|
228
|
+
yield
|
229
|
+
yield " #{wrapped_call_expression};"
|
182
230
|
yield
|
183
231
|
@wrapped.error_check { |line| yield " #{line}" }
|
232
|
+
else
|
233
|
+
yield " #{wrapped_call_expression};"
|
184
234
|
end
|
185
235
|
|
236
|
+
yield ' va_end( variadic_args );' if variadic?
|
237
|
+
|
238
|
+
yield ' return *this;' if @return_type.self?
|
239
|
+
|
186
240
|
yield '}'
|
187
241
|
end
|
188
242
|
|
243
|
+
# A Comment holding the function documentation.
|
244
|
+
def doc
|
245
|
+
comment = String.new
|
246
|
+
comment << @spec['doc'] if @spec.key?('doc')
|
247
|
+
|
248
|
+
@params
|
249
|
+
.reject { |param| param.doc.empty? }
|
250
|
+
.each { |param| comment << "\n\n" << param.doc.text }
|
251
|
+
|
252
|
+
if @spec['return'].key?('doc')
|
253
|
+
comment << "\n\n@return " << @spec['return']['doc']
|
254
|
+
end
|
255
|
+
|
256
|
+
Comment.new(comment)
|
257
|
+
end
|
258
|
+
|
259
|
+
# A resolved type, given a TypeSpec +type+. Resolved types will not have any
|
260
|
+
# keywords like +equivalent-struct+, which will be resolved to their
|
261
|
+
# effective type.
|
262
|
+
def resolve_type(type)
|
263
|
+
if type.equivalent_struct?
|
264
|
+
TypeSpec.new("struct #{@owner.struct_name}")
|
265
|
+
elsif type.equivalent_pointer?
|
266
|
+
TypeSpec.new("struct #{@owner.struct_name} *")
|
267
|
+
elsif type.self?
|
268
|
+
TypeSpec.new("#{@owner.name}&")
|
269
|
+
else
|
270
|
+
type
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# True if the function is variadic.
|
275
|
+
def variadic?
|
276
|
+
@params.last&.variadic?
|
277
|
+
end
|
278
|
+
|
189
279
|
# True if the function is virtual.
|
190
280
|
def virtual?
|
191
281
|
@spec['virtual']
|
@@ -193,26 +283,35 @@ module Wrapture
|
|
193
283
|
|
194
284
|
private
|
195
285
|
|
196
|
-
#
|
197
|
-
def
|
198
|
-
|
286
|
+
# The resolved type of the return type.
|
287
|
+
def resolved_return
|
288
|
+
@return_type.resolve(self)
|
289
|
+
end
|
199
290
|
|
200
|
-
|
201
|
-
|
202
|
-
|
291
|
+
# True if the provided wrapped param spec can be cast to when used in this
|
292
|
+
# function.
|
293
|
+
def castable?(wrapped_param)
|
294
|
+
param = @params.find { |p| p.name == wrapped_param['value'] }
|
203
295
|
|
204
|
-
|
296
|
+
!param.nil? &&
|
297
|
+
!wrapped_param['type'].nil? &&
|
298
|
+
@owner.type?(param.type)
|
205
299
|
end
|
206
300
|
|
207
|
-
#
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
"struct #{@owner.struct_name} *"
|
213
|
-
else
|
214
|
-
type
|
301
|
+
# Raises an exception if this function cannot be defined as is. Returns
|
302
|
+
# true otherwise.
|
303
|
+
def definable_check
|
304
|
+
if @wrapped.nil?
|
305
|
+
raise UndefinableSpec, 'no wrapped function was specified'
|
215
306
|
end
|
307
|
+
|
308
|
+
true
|
309
|
+
end
|
310
|
+
|
311
|
+
# Yields a declaration of each local variable used by the function.
|
312
|
+
def locals
|
313
|
+
yield 'va_list variadic_args;' if variadic?
|
314
|
+
yield "#{@wrapped.return_val_type} return_val;" if @wrapped.error_check?
|
216
315
|
end
|
217
316
|
|
218
317
|
# The function to use to create the return value of the function.
|
@@ -226,20 +325,25 @@ module Wrapture
|
|
226
325
|
end
|
227
326
|
end
|
228
327
|
|
229
|
-
#
|
230
|
-
def
|
231
|
-
|
232
|
-
''
|
233
|
-
elsif @spec['return']['type'].end_with?('*')
|
234
|
-
@spec['return']['type']
|
235
|
-
else
|
236
|
-
"#{@spec['return']['type']} "
|
237
|
-
end
|
328
|
+
# True if the function returns the result of the wrapped function call.
|
329
|
+
def returns_call_result?
|
330
|
+
!@constructor && !@destructor &&
|
331
|
+
!%w[void self-reference].include?(@spec['return']['type'])
|
238
332
|
end
|
239
333
|
|
240
|
-
#
|
241
|
-
def
|
242
|
-
|
334
|
+
# The expression containing the call to the underlying wrapped function.
|
335
|
+
def wrapped_call_expression
|
336
|
+
call = @wrapped.call_from(self)
|
337
|
+
|
338
|
+
if @constructor
|
339
|
+
"this->equivalent = #{call}"
|
340
|
+
elsif @wrapped.error_check?
|
341
|
+
"return_val = #{call}"
|
342
|
+
elsif returns_call_result?
|
343
|
+
"return #{return_cast(call)}"
|
344
|
+
else
|
345
|
+
call
|
346
|
+
end
|
243
347
|
end
|
244
348
|
end
|
245
349
|
end
|