xdry 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|