xdry 0.1.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.
- data/LICENSE +20 -0
- data/README.md +11 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/bin/xdry +4 -0
- data/lib/xdry.rb +18 -0
- data/lib/xdry/boxing.rb +212 -0
- data/lib/xdry/generators/ctor_from_field.rb +91 -0
- data/lib/xdry/generators/dealloc.rb +53 -0
- data/lib/xdry/generators/dictionary_coding.rb +129 -0
- data/lib/xdry/generators/field_from_property.rb +20 -0
- data/lib/xdry/generators/property-from-field.rb +22 -0
- data/lib/xdry/generators/storing_constructor.rb +72 -0
- data/lib/xdry/generators/synthesize.rb +25 -0
- data/lib/xdry/generators_support.rb +42 -0
- data/lib/xdry/parsing/driver.rb +106 -0
- data/lib/xdry/parsing/model.rb +272 -0
- data/lib/xdry/parsing/nodes.rb +260 -0
- data/lib/xdry/parsing/parsers.rb +166 -0
- data/lib/xdry/parsing/parts/selectors.rb +95 -0
- data/lib/xdry/parsing/parts/var_types.rb +66 -0
- data/lib/xdry/parsing/pos.rb +75 -0
- data/lib/xdry/parsing/scope_stack.rb +68 -0
- data/lib/xdry/parsing/scopes.rb +61 -0
- data/lib/xdry/parsing/scopes_support.rb +143 -0
- data/lib/xdry/patching/emitter.rb +60 -0
- data/lib/xdry/patching/insertion_points.rb +209 -0
- data/lib/xdry/patching/item_patchers.rb +74 -0
- data/lib/xdry/patching/patcher.rb +139 -0
- data/lib/xdry/run.rb +227 -0
- data/lib/xdry/support/enumerable_additions.rb +35 -0
- data/lib/xdry/support/string_additions.rb +27 -0
- data/lib/xdry/support/symbol_additions.rb +14 -0
- data/site/_config.yml +3 -0
- data/site/_example +9 -0
- data/site/_layouts/default.html +30 -0
- data/site/_plugins/example.rb +16 -0
- data/site/_plugins/highlight_unindent.rb +17 -0
- data/site/index.md +417 -0
- data/site/master.css +94 -0
- data/spec/boxing_spec.rb +80 -0
- data/spec/ctor_from_field_spec.rb +251 -0
- data/spec/dealloc_spec.rb +103 -0
- data/spec/dictionary_coding_spec.rb +132 -0
- data/spec/field_from_prop_spec.rb +72 -0
- data/spec/prop_from_field_spec.rb +39 -0
- data/spec/readme_samples_spec.rb +76 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/synthesize_spec.rb +94 -0
- data/xdry.gemspec +103 -0
- metadata +141 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010-2011, Andrey Tarantsov <andreyvit@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Extra DRY: Objective-C boilerplate generator
|
2
|
+
============================================
|
3
|
+
|
4
|
+
Automatically adds:
|
5
|
+
|
6
|
+
* `@property`, `@synthesize`, field declarations,
|
7
|
+
* initializing constructors,
|
8
|
+
* missing release calls in dealloc,
|
9
|
+
* initWithDictionary, dictionaryRepresentation based on the chosen fields.
|
10
|
+
|
11
|
+
See http://andreyvit.github.com/xdry/ for usage and installation instructions.
|
data/Rakefile
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "xdry"
|
8
|
+
gem.summary = %Q{eXtra D.R.Y. for Xcode}
|
9
|
+
gem.description = %Q{
|
10
|
+
Autogenerates all kinds of funky stuff (like accessors) in Xcode projects
|
11
|
+
}.strip
|
12
|
+
gem.email = "andreyvit@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/mockko/xdry"
|
14
|
+
gem.authors = ["Andrey Tarantsov"]
|
15
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
# http://www.rubygems.org/read/chapter/20
|
17
|
+
end
|
18
|
+
Jeweler::GemcutterTasks.new
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rspec/core'
|
24
|
+
require 'rspec/core/rake_task'
|
25
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
26
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => :spec
|
30
|
+
|
31
|
+
namespace :site do
|
32
|
+
desc "Regenerate the site using Jekyll"
|
33
|
+
task :build do
|
34
|
+
Dir.chdir 'site' do
|
35
|
+
system "jekyll"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Open a generated site in a web browser"
|
40
|
+
task :open => [:build] do
|
41
|
+
system "open site/_site/index.html"
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Regenerate the site and update gh-pages branch"
|
45
|
+
task :update => [:build] do
|
46
|
+
mods = `git status --porcelain --untracked-files=no`
|
47
|
+
if mods.strip.size > 0
|
48
|
+
system "git status --short --untracked-files=no"
|
49
|
+
puts
|
50
|
+
puts "** There are uncommitted changes. Please commit or stash them before updating the site."
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
|
54
|
+
source_commit = `git rev-parse HEAD`.strip
|
55
|
+
|
56
|
+
ENV['GIT_INDEX_FILE'] = File.expand_path('.git/index-gh-pages')
|
57
|
+
ENV['GIT_WORK_TREE'] = File.expand_path('site/_site')
|
58
|
+
ENV['GIT_DIR'] = File.expand_path('.git')
|
59
|
+
Dir.chdir 'site/_site' do
|
60
|
+
system "git update-index --add --remove #{Dir["**/*"].join(' ')}"
|
61
|
+
tree=`git write-tree`.strip
|
62
|
+
puts "Tree = #{tree}"
|
63
|
+
parent=`git rev-parse refs/heads/gh-pages`.strip
|
64
|
+
puts "Parent = #{parent}"
|
65
|
+
commit=`echo "Generate the site from source repository commit #{source_commit}" | git commit-tree #{tree} -p #{parent}`.strip
|
66
|
+
puts "Commit = #{commit}"
|
67
|
+
system "git update-ref -m 'Generate the site from source repository commit #{source_commit}' refs/heads/gh-pages #{commit}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Push an updated site to GitHub"
|
72
|
+
task :push do
|
73
|
+
system "git push origin gh-pages"
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/xdry
ADDED
data/lib/xdry.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
INDENT_STEP = "\t" # FIXME: all usages of this should be changed to something smart
|
4
|
+
FIELD_PREFIX = "_" # FIXME: all usages of this should be changed to something smart
|
5
|
+
end
|
6
|
+
|
7
|
+
%w{
|
8
|
+
support/string_additions support/symbol_additions support/enumerable_additions
|
9
|
+
parsing/parts/var_types parsing/parts/selectors
|
10
|
+
parsing/nodes parsing/model parsing/pos parsing/parsers
|
11
|
+
parsing/scopes_support parsing/scopes
|
12
|
+
parsing/scope_stack parsing/driver
|
13
|
+
|
14
|
+
patching/emitter patching/patcher patching/insertion_points patching/item_patchers
|
15
|
+
|
16
|
+
boxing generators_support run
|
17
|
+
}.each { |name| require File.join(File.dirname(__FILE__), 'xdry', name) }
|
18
|
+
Dir[File.join(File.dirname(__FILE__), 'xdry', 'generators', '*.rb')].each { |f| require f }
|
data/lib/xdry/boxing.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
module XDry
|
2
|
+
|
3
|
+
module RetainPolicy
|
4
|
+
|
5
|
+
module ASSIGN_VALUE
|
6
|
+
def self.retain expr
|
7
|
+
expr
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.release out, expr
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.release_and_clear out, var_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ASSIGN_REF
|
18
|
+
def self.retain expr
|
19
|
+
expr
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.release out, expr
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.release_and_clear out, var_name
|
26
|
+
out << "#{var_name} = nil;";
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module COPY
|
31
|
+
def self.retain expr
|
32
|
+
"[#{expr} copy]"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.release out, expr
|
36
|
+
out << "[#{expr} release]";
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.release_and_clear out, var_name
|
40
|
+
out << "[#{var_name} release], #{var_name} = nil;";
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module RETAIN
|
45
|
+
def self.retain expr
|
46
|
+
"[#{expr} retain]"
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.release out, expr
|
50
|
+
out << "[#{expr} release]";
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.release_and_clear out, var_name
|
54
|
+
out << "[#{var_name} release], #{var_name} = nil;";
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
module Boxing
|
61
|
+
|
62
|
+
class Boxer
|
63
|
+
def unbox_retained out, object_expr, tempvar_prefix
|
64
|
+
retain_policy.retain(unbox(out, object_expr, tempvar_prefix))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class NSNumberConverter < Boxer
|
69
|
+
def initialize repr_selector, init_selector
|
70
|
+
@init_selector = init_selector
|
71
|
+
@repr_selector = repr_selector
|
72
|
+
end
|
73
|
+
|
74
|
+
def box out, data_expr, tempvar_prefix
|
75
|
+
"[NSNumber #{@init_selector}:#{data_expr}]"
|
76
|
+
end
|
77
|
+
|
78
|
+
def unbox out, object_expr, tempvar_prefix
|
79
|
+
"[#{object_expr} #{@repr_selector}]"
|
80
|
+
end
|
81
|
+
|
82
|
+
def retain_policy; RetainPolicy::ASSIGN_VALUE; end
|
83
|
+
end
|
84
|
+
|
85
|
+
class DateConverter < Boxer
|
86
|
+
def box out, data_expr, tempvar_prefix
|
87
|
+
SIMPLE_CONVERTIONS['double'].box out, "[#{data_expr} timeIntervalSinceReferenceDate]"
|
88
|
+
end
|
89
|
+
|
90
|
+
def unbox out, object_expr, tempvar_prefix
|
91
|
+
number = SIMPLE_CONVERTIONS['double'].unbox(out, object_expr)
|
92
|
+
"[NSDate dateWithTimeIntervalSinceReferenceDate:#{number}]"
|
93
|
+
end
|
94
|
+
|
95
|
+
def retain_policy; RetainPolicy::RETAIN; end
|
96
|
+
end
|
97
|
+
|
98
|
+
class NopConverter < Boxer
|
99
|
+
def box out, data_expr, tempvar_prefix
|
100
|
+
data_expr
|
101
|
+
end
|
102
|
+
|
103
|
+
def unbox out, object_expr, tempvar_prefix
|
104
|
+
object_expr
|
105
|
+
end
|
106
|
+
|
107
|
+
def retain_policy; RetainPolicy::RETAIN; end
|
108
|
+
end
|
109
|
+
|
110
|
+
class StringConverter < Boxer
|
111
|
+
def box out, data_expr, tempvar_prefix
|
112
|
+
data_expr
|
113
|
+
end
|
114
|
+
|
115
|
+
def unbox out, object_expr, tempvar_prefix
|
116
|
+
object_expr
|
117
|
+
end
|
118
|
+
|
119
|
+
def retain_policy; RetainPolicy::COPY; end
|
120
|
+
end
|
121
|
+
|
122
|
+
class ArrayConverter < Boxer
|
123
|
+
def initialize item_type, init_selector, repr_selector
|
124
|
+
@item_type = item_type
|
125
|
+
@init_selector = init_selector
|
126
|
+
@repr_selector = repr_selector
|
127
|
+
end
|
128
|
+
|
129
|
+
def box out, data_expr, tempvar_prefix
|
130
|
+
array_var = "#{tempvar_prefix}Array"
|
131
|
+
item_var = "#{tempvar_prefix}Item"
|
132
|
+
|
133
|
+
out << "NSMutableArray *#{array_var} = [NSMutableArray array];"
|
134
|
+
out.block "for (#{@item_type} *#{item_var} in #{data_expr})" do
|
135
|
+
out << "[#{array_var} addObject:[#{item_var} #{@repr_selector}]];"
|
136
|
+
end
|
137
|
+
"#{array_var}"
|
138
|
+
end
|
139
|
+
|
140
|
+
def unbox out, object_expr, tempvar_prefix
|
141
|
+
unbox_internal out, object_expr, tempvar_prefix, "[NSMutableArray array]"
|
142
|
+
end
|
143
|
+
|
144
|
+
def unbox_retained out, object_expr, tempvar_prefix
|
145
|
+
unbox_internal out, object_expr, tempvar_prefix, "[[NSMutableArray alloc] init]"
|
146
|
+
end
|
147
|
+
|
148
|
+
def retain_policy; RetainPolicy::COPY; end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def unbox_internal out, object_expr, tempvar_prefix, array_init
|
153
|
+
array_var = "#{tempvar_prefix}Array"
|
154
|
+
item_dict_var = "#{tempvar_prefix}ItemDictionary"
|
155
|
+
item_var = "#{tempvar_prefix}Item"
|
156
|
+
|
157
|
+
out << "NSMutableArray *#{array_var} = #{array_init};"
|
158
|
+
out.block "for (NSDictionary *#{item_dict_var} in (NSArray *) #{object_expr})" do
|
159
|
+
out << "#{@item_type} *#{item_var} = [[#{@item_type} alloc] #{@init_selector}:#{item_dict_var}];"
|
160
|
+
out << "[#{array_var} addObject:#{item_var}];"
|
161
|
+
out << "[#{item_var} release];"
|
162
|
+
end
|
163
|
+
"#{array_var}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
SIMPLE_CONVERTIONS = {
|
168
|
+
'int' => NSNumberConverter.new('intValue', 'numberWithInt'),
|
169
|
+
'NSInteger' => NSNumberConverter.new('integerValue', 'numberWithInteger'),
|
170
|
+
'NSUInteger' => NSNumberConverter.new('unsignedIntegerValue', 'numberWithUnsignedInteger'),
|
171
|
+
'BOOL' => NSNumberConverter.new('boolValue', 'numberWithBool'),
|
172
|
+
'float' => NSNumberConverter.new('floatValue', 'numberWithFloat'),
|
173
|
+
'CGFloat' => NSNumberConverter.new('floatValue', 'numberWithFloat'),
|
174
|
+
'double' => NSNumberConverter.new('doubleValue', 'numberWithDouble'),
|
175
|
+
'NSTimeInterval' => NSNumberConverter.new('doubleValue', 'numberWithDouble'),
|
176
|
+
}
|
177
|
+
|
178
|
+
POINTER_CONVERTIONS = {
|
179
|
+
'NSDate' => DateConverter.new,
|
180
|
+
'NSString' => StringConverter.new,
|
181
|
+
}
|
182
|
+
|
183
|
+
def self.converter_for type
|
184
|
+
case type
|
185
|
+
when SimpleVarType
|
186
|
+
SIMPLE_CONVERTIONS[type.name]
|
187
|
+
when PointerVarType
|
188
|
+
case type.name
|
189
|
+
when 'NSArray', 'NSMutableArray'
|
190
|
+
return ArrayConverter.new(type.type_hint || 'NSObject', 'initWithDictionary', 'dictionaryRepresentation')
|
191
|
+
end
|
192
|
+
POINTER_CONVERTIONS[type.name]
|
193
|
+
else
|
194
|
+
nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def self.retain_policy_of type
|
199
|
+
if conv = converter_for(type)
|
200
|
+
conv.retain_policy
|
201
|
+
else
|
202
|
+
case type
|
203
|
+
when PointerVarType then RetainPolicy::RETAIN
|
204
|
+
when IdVarType then RetainPolicy::ASSIGN_REF
|
205
|
+
when SimpleVarType then RetainPolicy::ASSIGN_VALUE
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class ConstructorFromField < Generator
|
6
|
+
id "ctor-from-field"
|
7
|
+
|
8
|
+
def process_class oclass
|
9
|
+
attributes = oclass.attributes.select { |a| a.wants_constructor? }
|
10
|
+
return if attributes.empty?
|
11
|
+
|
12
|
+
patch_implementation! oclass, attributes
|
13
|
+
patch_interface! oclass, attributes
|
14
|
+
end
|
15
|
+
|
16
|
+
def patch_implementation! oclass, attributes
|
17
|
+
impl_selector_def = impl_selector_def_for(attributes)
|
18
|
+
|
19
|
+
init_code = Emitter.capture do |o|
|
20
|
+
o.method "(id)#{impl_selector_def}" do
|
21
|
+
o.if "self = [super init]" do
|
22
|
+
end
|
23
|
+
o << "return self;"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
MethodPatcher.new(patcher, oclass, impl_selector_def.selector, ImplementationStartIP.new(oclass), init_code) do |omethod|
|
28
|
+
impl = omethod.impl
|
29
|
+
ip = InsideConstructorIfSuperIP.new(impl)
|
30
|
+
|
31
|
+
lines = Emitter.capture do |o|
|
32
|
+
attributes.zip(impl_selector_def.components).each do |oattr, selector_component|
|
33
|
+
name, type = oattr.name, oattr.type
|
34
|
+
capitalized_name = name.capitalized_identifier
|
35
|
+
|
36
|
+
retain_policy = Boxing.retain_policy_of type
|
37
|
+
|
38
|
+
unless impl.children.any? { |n| NLine === n && n.line =~ /^(?:self\s*.\s*#{oattr.name}|#{oattr.field_name})\s*=/ }
|
39
|
+
|
40
|
+
var_name = impl.start_node.selector_def.var_name_after_keyword(selector_component.keyword)
|
41
|
+
|
42
|
+
field_ref = oattr.field_name
|
43
|
+
field_ref = "self->#{field_ref}" if field_ref == var_name
|
44
|
+
|
45
|
+
retained = retain_policy.retain(var_name)
|
46
|
+
o << "#{field_ref} = #{retained};"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ip.insert @patcher, lines unless lines.empty?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def patch_interface! oclass, attributes
|
56
|
+
intf_selector_def = intf_selector_def_for(attributes)
|
57
|
+
|
58
|
+
omethod = oclass.find_method(intf_selector_def.selector)
|
59
|
+
unless omethod && omethod.has_header?
|
60
|
+
|
61
|
+
ip = BeforeInterfaceEndIP.new(oclass)
|
62
|
+
lines = Emitter.capture do |o|
|
63
|
+
o << "- (id)#{intf_selector_def};"
|
64
|
+
end
|
65
|
+
ip.insert patcher, [""] + lines + [""]
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def impl_selector_def_for attributes
|
71
|
+
CompoundSelectorDef.new(attributes.each_with_index.collect { |a, i| selector_component_for(a, i, true) })
|
72
|
+
end
|
73
|
+
|
74
|
+
def intf_selector_def_for attributes
|
75
|
+
CompoundSelectorDef.new(attributes.each_with_index.collect { |a, i| selector_component_for(a, i, false) })
|
76
|
+
end
|
77
|
+
|
78
|
+
def selector_component_for attribute, index, is_impl
|
79
|
+
keyword = attribute.name
|
80
|
+
keyword = "initWith#{keyword.capitalized_identifier}" if index == 0
|
81
|
+
|
82
|
+
arg_name = attribute.name
|
83
|
+
arg_name = arg_name.prefixed_as_arg_name if is_impl && arg_name == attribute.field_name
|
84
|
+
|
85
|
+
SelectorComponent.new("#{keyword}:", arg_name, attribute.type)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
DEALLOC_CODE = [
|
6
|
+
"",
|
7
|
+
"- (void)dealloc {",
|
8
|
+
"\t[super dealloc];",
|
9
|
+
"}",
|
10
|
+
"",
|
11
|
+
]
|
12
|
+
|
13
|
+
class Dealloc < Generator
|
14
|
+
id "dealloc"
|
15
|
+
|
16
|
+
def process_class oclass
|
17
|
+
|
18
|
+
MethodPatcher.new(patcher, oclass, 'dealloc', ImplementationStartIP.new(oclass), DEALLOC_CODE) do |dealloc_method|
|
19
|
+
impl = dealloc_method.impl
|
20
|
+
ip = BeforeSuperCallIP.new(impl)
|
21
|
+
|
22
|
+
existing_releases = impl.children.select { |child| child.is_a? NReleaseCall }
|
23
|
+
lines = generate_release_calls_if(oclass) do |oattr|
|
24
|
+
!existing_releases.any? { |n| n.expr == oattr.field_name || n.expr == "self.#{oattr.name}" }
|
25
|
+
end
|
26
|
+
|
27
|
+
ip.insert @patcher, lines unless lines.empty?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def generate_release_calls_if oclass
|
34
|
+
dealloc_out = Emitter.new
|
35
|
+
|
36
|
+
oclass.attributes.each do |oattr|
|
37
|
+
next if not oattr.has_field_def?
|
38
|
+
|
39
|
+
field_name = oattr.field_name
|
40
|
+
retain_policy = Boxing.retain_policy_of(oattr.type)
|
41
|
+
|
42
|
+
if yield(oattr)
|
43
|
+
retain_policy.release_and_clear dealloc_out, field_name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
dealloc_out.lines
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|