jruby_visualizer 0.1
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/bin/jruby_visualizer +5 -0
- data/lib/jruby_visualizer.rb +9 -0
- data/lib/jruby_visualizer/about_page.rb +64 -0
- data/lib/jruby_visualizer/ast_tree_view_builder.rb +77 -0
- data/lib/jruby_visualizer/cfg_visualizer.rb +122 -0
- data/lib/jruby_visualizer/compiler_data.rb +133 -0
- data/lib/jruby_visualizer/control_flow_graph_view.rb +96 -0
- data/lib/jruby_visualizer/core_ext/ir_scope.rb +15 -0
- data/lib/jruby_visualizer/ir_pretty_printer.rb +57 -0
- data/lib/jruby_visualizer/ir_scope_registry.rb +41 -0
- data/lib/jruby_visualizer/ir_visualizer.rb +79 -0
- data/lib/jruby_visualizer/jruby_visualizer.rb +103 -0
- data/lib/jruby_visualizer/ui/about.fxml +35 -0
- data/lib/jruby_visualizer/ui/cfg-view.fxml +42 -0
- data/lib/jruby_visualizer/ui/img/jruby-icon-32.png +0 -0
- data/lib/jruby_visualizer/ui/img/jruby-icon-64.png +0 -0
- data/lib/jruby_visualizer/ui/img/jruby-logo.png +0 -0
- data/lib/jruby_visualizer/ui/ir-view.fxml +18 -0
- data/lib/jruby_visualizer/ui/jruby-visualizer.fxml +81 -0
- data/lib/jruby_visualizer/version.rb +20 -0
- data/lib/jruby_visualizer/visualizer_compiler_pass_listener.rb +40 -0
- data/lib/jruby_visualizer/visualizer_main_app.rb +284 -0
- data/spec/compiler_data_spec.rb +102 -0
- data/spec/ir_scope_registry_spec.rb +49 -0
- data/spec/jruby_visualizer_test_utils.rb +19 -0
- data/spec/spec_helper.rb +20 -0
- metadata +110 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
=begin
|
2
|
+
JRuby Visualizer
|
3
|
+
Copyright (C) 2013 The JRuby Team
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'jrubyfx'
|
19
|
+
|
20
|
+
resource_root :images, File.join(File.dirname(__FILE__), 'ui', 'img'), 'ui/img'
|
21
|
+
fxml_root File.join(File.dirname(__FILE__), 'ui')
|
22
|
+
|
23
|
+
#
|
24
|
+
# JRubyFX Application showing information about the Visualizer
|
25
|
+
#
|
26
|
+
class AboutPage < JRubyFX::Application
|
27
|
+
|
28
|
+
def start(stage)
|
29
|
+
with(stage, title: 'About the JRuby Visualizer') do
|
30
|
+
fxml(AboutPageController)
|
31
|
+
icons.add(Image.new(resource_url(:images, 'jruby-icon-32.png').to_s))
|
32
|
+
end
|
33
|
+
|
34
|
+
stage['#jruby_logo'].set_on_mouse_clicked do |e|
|
35
|
+
open_browser_with('http://jruby.org')
|
36
|
+
end
|
37
|
+
stage['#jruby_hyperlink'].set_on_action do |e|
|
38
|
+
open_browser_with('http://jruby.org')
|
39
|
+
end
|
40
|
+
stage['#visualizer_hyperlink'].set_on_action do |e|
|
41
|
+
open_browser_with('https://github.com/jruby/jruby-visualizer')
|
42
|
+
end
|
43
|
+
stage.show
|
44
|
+
end
|
45
|
+
|
46
|
+
def open_browser_with(url)
|
47
|
+
get_host_services.show_document(url)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Controller loads the fxml file for the About Page
|
54
|
+
#
|
55
|
+
class AboutPageController
|
56
|
+
include JRubyFX::Controller
|
57
|
+
fxml 'about.fxml'
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
if __FILE__ == $PROGRAM_NAME
|
62
|
+
AboutPage.launch
|
63
|
+
end
|
64
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
=begin
|
2
|
+
JRuby Visualizer
|
3
|
+
Copyright (C) 2013 The JRuby Team
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'java'
|
19
|
+
require 'jrubyfx'
|
20
|
+
|
21
|
+
#
|
22
|
+
# Custom TreeItem, to store the JRuby's AST Node within the item
|
23
|
+
#
|
24
|
+
class ASTTreeItem < Java::javafx.scene.control.TreeItem
|
25
|
+
include JRubyFX
|
26
|
+
|
27
|
+
attr_reader :node
|
28
|
+
|
29
|
+
def initialize(node)
|
30
|
+
@node = node
|
31
|
+
super(node_string)
|
32
|
+
set_expanded(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
def node_string
|
36
|
+
node_information = @node.respond_to?(:name) ? ":#{@node.name}" : ''
|
37
|
+
"#{@node.node_name}#{node_information} #{@node.position.start_line}"
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
#
|
44
|
+
# Builder for the custom TreeItem from an usual JRuby AST
|
45
|
+
#
|
46
|
+
class ASTTreeViewBuilder
|
47
|
+
include JRubyFX
|
48
|
+
|
49
|
+
attr_accessor :tree_view
|
50
|
+
|
51
|
+
def initialize(tree_view)
|
52
|
+
@tree_view = tree_view
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_tree_item(node)
|
56
|
+
ASTTreeItem.new(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
def build_view(root)
|
60
|
+
@tree_view.root = build_tree_item(root)
|
61
|
+
root.child_nodes.each { |child| build_view_inner(child, @tree_view.root) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def build_view_inner(node, parent_item)
|
65
|
+
tree_item = build_tree_item(node)
|
66
|
+
parent_item.children << tree_item
|
67
|
+
node.child_nodes.each { |child| build_view_inner(child, tree_item) }
|
68
|
+
end
|
69
|
+
private :build_view_inner
|
70
|
+
end
|
71
|
+
|
72
|
+
if __FILE__ == $PROGRAM_NAME
|
73
|
+
require 'jruby'
|
74
|
+
root = JRuby.parse('def foo; 42; end; foo')
|
75
|
+
builder = ASTTreeViewBuilder.new(nil)
|
76
|
+
builder.build_view(root)
|
77
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
=begin
|
2
|
+
JRuby Visualizer
|
3
|
+
Copyright (C) 2013 The JRuby Team
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'jrubyfx'
|
19
|
+
require_relative 'jruby_visualizer'
|
20
|
+
require_relative 'ir_scope_registry'
|
21
|
+
require_relative 'control_flow_graph_view'
|
22
|
+
|
23
|
+
resource_root :images, File.join(File.dirname(__FILE__), 'ui', 'img'), 'ui/img'
|
24
|
+
fxml_root File.join(File.dirname(__FILE__), 'ui')
|
25
|
+
|
26
|
+
#
|
27
|
+
# UI to visualize the IR's Control Flow Graph (CFG)
|
28
|
+
# Currently as a list view with live links between the basic blocks
|
29
|
+
#
|
30
|
+
class CFGVisualizer < JRubyFX::Application
|
31
|
+
def start(stage)
|
32
|
+
compiler_data = JRubyVisualizer.compiler_data
|
33
|
+
with(stage, title: 'Visualization of Control Flow Graphs (CFG)') do
|
34
|
+
fxml(CFGVisualizerController, initialize: [compiler_data])
|
35
|
+
icons.add(Image.new(resource_url(:images, 'jruby-icon-32.png').to_s))
|
36
|
+
show
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# This controller connects the CompilerData to the CFG View and
|
43
|
+
# handles opening/updating tabs for CFGs
|
44
|
+
#
|
45
|
+
class CFGVisualizerController
|
46
|
+
include JRubyFX::Controller
|
47
|
+
fxml 'cfg-view.fxml'
|
48
|
+
|
49
|
+
attr_reader :compiler_data, :ir_registry
|
50
|
+
|
51
|
+
def initialize(compiler_data)
|
52
|
+
@compiler_data = compiler_data
|
53
|
+
|
54
|
+
# read scopes into the registry
|
55
|
+
@ir_registry = IRScopeRegistry.new(@compiler_data.ir_scope)
|
56
|
+
read_registry_into_selector
|
57
|
+
# listen to changes of the ir_scope property
|
58
|
+
@compiler_data.ir_scope_property.add_invalidation_listener do |new_scope_property|
|
59
|
+
root_scope = new_scope_property.get
|
60
|
+
@ir_registry.clear
|
61
|
+
@ir_registry.fill_registry(root_scope)
|
62
|
+
read_registry_into_selector
|
63
|
+
update_tabs
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def read_registry_into_selector
|
68
|
+
scopes_keys = @ir_registry.scopes.keys.map do |key|
|
69
|
+
key.to_s
|
70
|
+
end
|
71
|
+
scopes_keys.sort!
|
72
|
+
@ir_scope_selector.items = FXCollections.observable_array_list(scopes_keys)
|
73
|
+
@ir_scope_selector.value = @selected_scope = scopes_keys[0]
|
74
|
+
end
|
75
|
+
|
76
|
+
def select_scope
|
77
|
+
@selected_scope = @ir_scope_selector.value
|
78
|
+
open_cfg_tab
|
79
|
+
end
|
80
|
+
|
81
|
+
def open_cfg_tab
|
82
|
+
if @selected_scope.nil?
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
tabs = @cfg_scopes_view.tabs
|
87
|
+
is_tab_opened = tabs.find do |tab|
|
88
|
+
# get string value from StringProperty name
|
89
|
+
tab.text == @selected_scope
|
90
|
+
end
|
91
|
+
|
92
|
+
unless is_tab_opened
|
93
|
+
tab = Tab.new(@selected_scope)
|
94
|
+
cfg = get_selected_scope.cfg!
|
95
|
+
tab.set_content(ControlFlowGraphView.new(cfg))
|
96
|
+
tabs << tab
|
97
|
+
# set focus on selected tab
|
98
|
+
@cfg_scopes_view.selection_model.select(tab)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_selected_scope
|
103
|
+
@ir_registry.scopes[@selected_scope.to_sym]
|
104
|
+
end
|
105
|
+
|
106
|
+
def update_tabs
|
107
|
+
@cfg_scopes_view.tabs.each do |tab|
|
108
|
+
scope_name = tab.text
|
109
|
+
# TODO read and diff on custom cfg objects
|
110
|
+
cfg = @ir_registry.scopes[scope_name.to_sym].cfg!
|
111
|
+
# TODO listen to events if the ir scope changes
|
112
|
+
tab.set_content(ControlFlowGraphView.new(cfg))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
if __FILE__ == $PROGRAM_NAME
|
119
|
+
JRubyVisualizer.compiler_data = CompilerData.new(
|
120
|
+
"\nclass Foo\n\ndef bar; 42; end; end;\nFoo.new.bar")
|
121
|
+
CFGVisualizer.launch
|
122
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
=begin
|
2
|
+
JRuby Visualizer
|
3
|
+
Copyright (C) 2013 The JRuby Team
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'jrubyfx'
|
19
|
+
|
20
|
+
#
|
21
|
+
# JavaFX Property that fires always when the set method is called
|
22
|
+
#
|
23
|
+
class FireChangeObjectProperty < Java::javafx.beans.property.SimpleObjectProperty
|
24
|
+
def initialize(*args)
|
25
|
+
raise ArgumentError.new "wrong number of arguments (#{args.length} for 3)" if args.length > 3
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(new_val)
|
30
|
+
super(new_val)
|
31
|
+
fire_value_changed_event
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Data container for
|
37
|
+
# * Abstract Syntax Tree (AST),
|
38
|
+
# * Intermediate Code (IR)
|
39
|
+
# * Ruby Code
|
40
|
+
# It models and handles the dependencies between those compiler artifacts with
|
41
|
+
# JRubyFX properties
|
42
|
+
#
|
43
|
+
class CompilerData
|
44
|
+
include JRubyFX
|
45
|
+
|
46
|
+
@@ir_builder = nil
|
47
|
+
|
48
|
+
property_accessor :ruby_code, :ast_root, :ir_scope
|
49
|
+
attr_accessor :current_pass, :next_pass
|
50
|
+
|
51
|
+
def self.parse(code)
|
52
|
+
JRuby.parse(code)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.create_ir_builder
|
56
|
+
ir_manager = JRuby::runtime.ir_manager
|
57
|
+
ir_manager.dry_run = true
|
58
|
+
|
59
|
+
org.jruby.ir.IRBuilder::createIRBuilder(JRuby::runtime, ir_manager)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.build_ir(root_node)
|
63
|
+
unless @@ir_builder
|
64
|
+
@@ir_builder = create_ir_builder
|
65
|
+
end
|
66
|
+
@@ir_builder.build_root(root_node)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.pass_to_s(pass)
|
70
|
+
pass.class.to_s.split("::").last
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.compiler_passes_names
|
74
|
+
scheduler = JRuby::runtime.ir_manager.schedule_passes
|
75
|
+
scheduler.map do |pass|
|
76
|
+
pass_to_s(pass)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def reset_scheduler
|
81
|
+
ir_manager = JRuby::runtime.ir_manager
|
82
|
+
@scheduler = ir_manager.schedule_passes.iterator
|
83
|
+
if @scheduler.has_next
|
84
|
+
@current_pass = nil
|
85
|
+
@next_pass = @scheduler.next
|
86
|
+
else
|
87
|
+
@current_pass = @next_pass = nil
|
88
|
+
end
|
89
|
+
# trigger to re build the old ir scope
|
90
|
+
@ir_scope.set(self.class.build_ir(@ast_root.get))
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(ruby_code='')
|
94
|
+
@ruby_code = SimpleStringProperty.new(ruby_code)
|
95
|
+
@ast_root = SimpleObjectProperty.new(self, 'ast_root', self.class.parse(ruby_code))
|
96
|
+
@ir_scope = FireChangeObjectProperty.new(self, 'ir_scope', self.class.build_ir(@ast_root.get))
|
97
|
+
# bind change of Ruby code to reparsing an AST and set the property
|
98
|
+
@ruby_code.add_invalidation_listener do |new_code_property|
|
99
|
+
@ast_root.set(self.class.parse(new_code_property.get))
|
100
|
+
end
|
101
|
+
# bind change of AST to rebuilding IR and set the property
|
102
|
+
@ast_root.add_invalidation_listener do |new_ast_property|
|
103
|
+
@ir_scope.set(self.class.build_ir(new_ast_property.get))
|
104
|
+
end
|
105
|
+
|
106
|
+
# initialize scheduler
|
107
|
+
reset_scheduler
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.run_pass_on_all_scopes(pass, scope)
|
111
|
+
pass.run(scope)
|
112
|
+
scope.lexical_scopes.each do |lex_scope|
|
113
|
+
run_pass_on_all_scopes(pass, lex_scope)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def step_ir_passes
|
118
|
+
if @next_pass
|
119
|
+
@current_pass = @next_pass
|
120
|
+
@next_pass =
|
121
|
+
if @scheduler.has_next
|
122
|
+
@scheduler.next
|
123
|
+
else
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
scope = @ir_scope.get
|
127
|
+
self.class.run_pass_on_all_scopes(@current_pass, scope)
|
128
|
+
@ir_scope.set(scope)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
@@ -0,0 +1,96 @@
|
|
1
|
+
=begin
|
2
|
+
JRuby Visualizer
|
3
|
+
Copyright (C) 2013 The JRuby Team
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
16
|
+
=end
|
17
|
+
|
18
|
+
require 'jrubyfx'
|
19
|
+
|
20
|
+
#
|
21
|
+
# A custom JRubyFX control for a basic block of a CFG. This view is a composite
|
22
|
+
# with a TextArea for the basic blocks and live links to basic blocks that
|
23
|
+
# can succeed after this one
|
24
|
+
#
|
25
|
+
class BasicBlockBox < Java::javafx.scene.layout.VBox
|
26
|
+
include JRubyFX
|
27
|
+
|
28
|
+
attr_reader :basic_block, :instrs_box, :successors, :successor_buttons
|
29
|
+
|
30
|
+
def initialize(basic_block, cfg, cfg_list_view)
|
31
|
+
super(5)
|
32
|
+
@basic_block = basic_block
|
33
|
+
instructions = instrs
|
34
|
+
@instrs_box = TextArea.new(instructions)
|
35
|
+
line_no = instructions.lines.count
|
36
|
+
@instrs_box.set_pref_row_count(line_no)
|
37
|
+
@instrs_box.set_style('-fx-font-family: monospaced')
|
38
|
+
@instrs_box.set_editable(false)
|
39
|
+
@successors = cfg.get_outgoing_destinations(@basic_block).to_a
|
40
|
+
if @successors.empty?
|
41
|
+
get_children << @instrs_box
|
42
|
+
else
|
43
|
+
@successor_buttons = @successors.map do |bb|
|
44
|
+
button = Button.new(bb.to_s)
|
45
|
+
button.set_on_action do
|
46
|
+
# TODO rewrite this ugly code
|
47
|
+
i = button.get_text.scan(/.*\[(\d+):.*\]/)[0][0]
|
48
|
+
index = i.to_i - 1
|
49
|
+
cfg_list_view.selection_model.select(index)
|
50
|
+
cfg_list_view.focus_model.focus(index)
|
51
|
+
cfg_list_view.scroll_to(index)
|
52
|
+
end
|
53
|
+
button
|
54
|
+
end
|
55
|
+
|
56
|
+
successors_layout = HBox.new(5)
|
57
|
+
successors_layout.get_children << Label.new('Successors: ')
|
58
|
+
successors_layout.get_children.add_all(@successor_buttons)
|
59
|
+
|
60
|
+
get_children.add_all(@instrs_box, successors_layout)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def instrs
|
65
|
+
instrs_string = @basic_block.to_string_instrs
|
66
|
+
if instrs_string.end_with?("\n")
|
67
|
+
instrs_string[0...-1]
|
68
|
+
else
|
69
|
+
instrs_string
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# The UI for the CFG as a wrapper for a ListView that is directly built
|
77
|
+
# from JRuby's CFG. In order to get a resizable UI element, a BorderPane is
|
78
|
+
# used
|
79
|
+
#
|
80
|
+
class ControlFlowGraphView < Java::javafx.scene.layout.BorderPane
|
81
|
+
include JRubyFX
|
82
|
+
|
83
|
+
def initialize(cfg)
|
84
|
+
super()
|
85
|
+
@cfg = cfg
|
86
|
+
@cfg_list_view = ListView.new
|
87
|
+
@bb_cells = FXCollections.observable_array_list([])
|
88
|
+
@cfg.sorted_basic_blocks.each do |bb|
|
89
|
+
bb_cell = BasicBlockBox.new(bb, cfg, @cfg_list_view)
|
90
|
+
@bb_cells << bb_cell
|
91
|
+
end
|
92
|
+
@cfg_list_view.set_items(@bb_cells)
|
93
|
+
set_center(@cfg_list_view)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|