jruby_visualizer 0.1

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