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.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ require 'jruby_visualizer'
4
+
5
+ JRubyVisualizer.visualize_with_argv
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ require 'java'
4
+
5
+ require_relative 'jruby_visualizer/jruby_visualizer'
6
+
7
+ if __FILE__ == $PROGRAM_NAME
8
+ JRubyVisualizer.visualize_with_argv
9
+ end
@@ -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