cleanup
This commit is contained in:
parent
eba80177ae
commit
b81c8e69b7
1
Gemfile
1
Gemfile
@ -18,6 +18,7 @@ gem 'uglifier', '>= 2.4.0', require: false
|
||||
|
||||
group :test do
|
||||
# Testing dependencies
|
||||
gem "minitest"
|
||||
gem 'rspec', '~> 3.2.0'
|
||||
gem 'opal-rspec', '~> 0.4.2'
|
||||
gem 'capybara', '~> 2.4.2'
|
||||
|
@ -68,6 +68,7 @@ GEM
|
||||
method_source (0.8.2)
|
||||
mime-types (2.6.1)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.7.0)
|
||||
mongo (1.9.2)
|
||||
bson (~> 1.9.2)
|
||||
multi_json (1.11.2)
|
||||
@ -161,6 +162,7 @@ DEPENDENCIES
|
||||
chromedriver2-helper (~> 0.0.8)
|
||||
concurrent-ruby-ext (~> 0.8.0)
|
||||
csso-rails (~> 0.3.4)
|
||||
minitest
|
||||
opal-rspec (~> 0.4.2)
|
||||
parslet!
|
||||
poltergeist (~> 1.5.0)
|
||||
|
21
README.md
21
README.md
@ -1,9 +1,22 @@
|
||||
### statement of intent
|
||||
|
||||
just starting and not quite sure, but here is the direction
|
||||
|
||||
# Debugger
|
||||
|
||||
After some tryouts it ended up being an Opal application. That is ruby as javascript in the browser.
|
||||
|
||||
![Debugger](https://raw.githubusercontent.com/salama/salama-debugger/master/static/debugger.png)
|
||||
|
||||
- On the left are the classes of the system. Next idea is to have hover info about them.
|
||||
- Next a source code view (not implemented)
|
||||
- next a view of the Virtual Instructions
|
||||
- last section, current block with current Register Instruction highlighted
|
||||
- step (next) button for single stepping
|
||||
- status: starting , running , exited
|
||||
- bottom row are the registers. If the register hold an object the variables are shown.
|
||||
(also should have hover info) , the first letter indicates the class, the number is the address
|
||||
|
||||
So lots to do, but a good start.
|
||||
|
||||
|
||||
|
||||
I don't want to use gdb anymore, and it would be easier without using the qemu setup, so:
|
||||
|
||||
- single step debugging of the register machine level (as close to arm as need be)
|
||||
|
16
app/main.rb
16
app/main.rb
@ -1,16 +0,0 @@
|
||||
require 'opal'
|
||||
require "opal/parser"
|
||||
|
||||
require "salama"
|
||||
require "interpreter"
|
||||
|
||||
require 'opal-react'
|
||||
|
||||
require "debugger"
|
||||
|
||||
require 'opal-jquery'
|
||||
|
||||
Document.ready? do # Document.ready? is a opal-jquery method.
|
||||
machine = Virtual.machine.boot
|
||||
React.render( React.create_element( Debugger , :machine => machine ), Element['#content'] )
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
<:Title>
|
||||
About
|
||||
|
||||
<:Body>
|
||||
<h1>About</h1>
|
||||
|
||||
<p>About page...</p>
|
@ -1,40 +0,0 @@
|
||||
@import "susy" ;
|
||||
|
||||
$susy: (
|
||||
columns: 24 ,
|
||||
gutter-position: split ,
|
||||
);
|
||||
|
||||
.debugger-view { @include container(90%); }
|
||||
|
||||
.classes { @include span(3); }
|
||||
|
||||
.file-view {
|
||||
@include span(4);
|
||||
margin: span(1);
|
||||
}
|
||||
|
||||
.source-view { @include span(6); }
|
||||
|
||||
.block-view {
|
||||
@include span(4);
|
||||
margin-right: span(3);
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.status-view {
|
||||
@include span(2 at 22);
|
||||
}
|
||||
|
||||
.registers-view {
|
||||
@include span(20 at 3);
|
||||
}
|
||||
|
||||
.register-view {
|
||||
@include gallery(3);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.bright {
|
||||
background-color: orange ;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 237 B |
@ -1,69 +0,0 @@
|
||||
|
||||
require "opal/parser" # to get eval to work
|
||||
|
||||
$LOAD_PATH.unshift("/Users/raisa/salama/salama-debugger/app/main/lib")
|
||||
|
||||
require "salama"
|
||||
|
||||
Virtual::Machine.boot
|
||||
|
||||
module Main
|
||||
class MainController < Volt::ModelController
|
||||
|
||||
def index
|
||||
page._registers!.clear
|
||||
page._classes!.clear
|
||||
page._objects!.clear
|
||||
page._source = InstructionModel.new nil
|
||||
page._block = BlockModel.new nil
|
||||
fill_regs
|
||||
parse_and_fill
|
||||
end
|
||||
|
||||
def about
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_and_fill
|
||||
ParseTask.parse(1).then do |result|
|
||||
is = Ast::Expression.from_basic(result)
|
||||
Virtual::Compiler.compile( is , Virtual.machine.space.get_main )
|
||||
Virtual.machine.run_before "Register::CallImplementation"
|
||||
fill_classes
|
||||
end.fail do |error|
|
||||
raise "Error: #{error}"
|
||||
end
|
||||
end
|
||||
def fill_classes
|
||||
Virtual.machine.space.classes.each do |name , claz|
|
||||
next if [:Kernel,:Module,:MetaClass,:BinaryCode].index name
|
||||
c = Volt::Model.new :name => name
|
||||
page._classes << c
|
||||
end
|
||||
b = Virtual.machine.init
|
||||
page._block = BlockModel.new b
|
||||
page._source = InstructionModel.new b.codes.first.source
|
||||
end
|
||||
def fill_regs
|
||||
register_names = (0..8).collect {|i| "r#{i}"}
|
||||
register_names.each do |reg_name|
|
||||
reg = Volt::Model.new :name => reg_name
|
||||
page._registers << reg
|
||||
end
|
||||
end
|
||||
# The main template contains a #template binding that shows another
|
||||
# template. This is the path to that template. It may change based
|
||||
# on the params._component, params._controller, and params._action values.
|
||||
def main_path
|
||||
"#{params._component || 'main'}/#{params._controller || 'main'}/#{params._action || 'index'}"
|
||||
end
|
||||
|
||||
# Determine if the current nav component is the active one by looking
|
||||
# at the first part of the url against the href attribute.
|
||||
def active_tab?
|
||||
url.path.split('/')[1] == attrs.href.split('/')[1]
|
||||
end
|
||||
end
|
||||
end
|
@ -1,12 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Salama Debugger</title>
|
||||
<link rel="stylesheet" href="/assets/styles.css">
|
||||
<script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
|
||||
<script src="/assets/react-with-addons.js"></script>
|
||||
<%= javascript_include_tag 'main' %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content"></div>
|
||||
</body>
|
||||
</html>
|
@ -1,29 +0,0 @@
|
||||
|
||||
class ObjectView
|
||||
|
||||
attr_accessor :text , :object , :attributes
|
||||
|
||||
def initialize o
|
||||
super()
|
||||
puts "NO O " unless o
|
||||
self.object = o
|
||||
self.text = short
|
||||
@attributes = {}
|
||||
end
|
||||
|
||||
def short
|
||||
object.class.name.split("::").last[0 .. 3]
|
||||
end
|
||||
|
||||
def is_parfait
|
||||
object.class.name.split("::").first == "Parfait"
|
||||
end
|
||||
def set name , val
|
||||
@attributes[name] = val
|
||||
self.text.text = short + @attributes.length.to_s
|
||||
end
|
||||
def get(name)
|
||||
@attributes[name]
|
||||
end
|
||||
|
||||
end
|
@ -1,99 +0,0 @@
|
||||
|
||||
class SpaceView
|
||||
include Sof::Util
|
||||
|
||||
def initialize max
|
||||
super()
|
||||
|
||||
space = Virtual.machine.space
|
||||
# just a way to get the space into a list. objects is an id => occurence mapping.
|
||||
# occurence.object is the object
|
||||
objects = Sof::Members.new(space).objects
|
||||
@objects = objects
|
||||
puts "Objects #{objects.length}"
|
||||
# create a mapping from id to volt models
|
||||
@view_objects = {}
|
||||
|
||||
@objects.each do |i , o|
|
||||
ob = o.object
|
||||
next unless ob
|
||||
next if basic?(ob)
|
||||
next if ob.class.name.include? "Binary"
|
||||
next if ob.class.name.include? "Array"
|
||||
puts "object #{ob.class.name}"
|
||||
|
||||
view = ObjectView.new ob
|
||||
@view_objects[i] = view
|
||||
add_child view.text
|
||||
end
|
||||
fill_attributes
|
||||
end
|
||||
|
||||
|
||||
def fill_attributes
|
||||
@view_objects.each do |i , view|
|
||||
ob = view.object
|
||||
next if is_value?(ob)
|
||||
case ob.class.name
|
||||
when "Array" , "Parfait::List"
|
||||
fill_array view
|
||||
when "Hash" , "Parfait::Dictionary"
|
||||
fill_hash view
|
||||
else
|
||||
# next if basic?(ob)
|
||||
|
||||
attributes = attributes_for(ob)
|
||||
attributes.each do |a|
|
||||
next if a == "html_safe"
|
||||
next if a == "constructor"
|
||||
next if a == "toString"
|
||||
next if a == "position"
|
||||
val = get_value( ob , a)
|
||||
if( @view_objects[val.object_id])
|
||||
val = @view_objects[val.object_id]
|
||||
end
|
||||
#puts "set #{a}"
|
||||
view.set(a , val )
|
||||
end
|
||||
superclasses = [ob.class.superclass.name]
|
||||
if superclasses.include?( "Array") or superclasses.include?( "Parfait::List")
|
||||
fill_array view
|
||||
end
|
||||
if superclasses.include?( "Hash") or superclasses.include?( "Parfait::Dictionary")
|
||||
fill_hash view
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def basic? ob
|
||||
return true if ob.class.name.include?("::") and !ob.class.name.include?("Parfait")
|
||||
return true if ob.class.name == "Proc"
|
||||
return true if ob.class.name == "String"
|
||||
return true if ob.class.name == "Numeric"
|
||||
return true if ob.class.name == "Class"
|
||||
false
|
||||
end
|
||||
# and hash keys/values
|
||||
def fill_hash view_hash
|
||||
view_hash.object.each do |k , val|
|
||||
if( @view_objects[val.object_id])
|
||||
val = @view_objects[val.object_id]
|
||||
end
|
||||
view_hash.set(k , val )
|
||||
end
|
||||
end
|
||||
# and array values
|
||||
def fill_array view_array
|
||||
index = 0
|
||||
view_array.object.each do |val|
|
||||
if( @view_objects[val.object_id])
|
||||
val = @view_objects[val.object_id]
|
||||
end
|
||||
view_array.set("#{index}" , val )
|
||||
index += 1
|
||||
end
|
||||
#puts "set #{a}"
|
||||
end
|
||||
|
||||
end
|
@ -1,13 +0,0 @@
|
||||
|
||||
# represent an instruction and hold the actual instance (as transient)
|
||||
|
||||
class InstructionModel < Volt::Model
|
||||
field :name
|
||||
attr_accessor :instruction
|
||||
|
||||
def initialize(i)
|
||||
super()
|
||||
@instruction = i
|
||||
self.name = i.class.name
|
||||
end
|
||||
end
|
@ -1,7 +1,8 @@
|
||||
require 'rubygems'
|
||||
require 'bundler'
|
||||
|
||||
begin
|
||||
Bundler.setup(:default, :development)
|
||||
Bundler.setup(:default, :test)
|
||||
rescue Bundler::BundlerError => e
|
||||
$stderr.puts e.message
|
||||
$stderr.puts "Run `bundle install` to install missing gems"
|
||||
@ -14,7 +15,7 @@ end
|
||||
|
||||
require "minitest/autorun"
|
||||
|
||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', "app" , "main",'lib'))
|
||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'test'))
|
||||
|
||||
require "salama"
|
||||
|
@ -1,7 +0,0 @@
|
||||
<:Title>
|
||||
About
|
||||
|
||||
<:Body>
|
||||
<h1>About</h1>
|
||||
|
||||
<p>About page...</p>
|
@ -1,43 +0,0 @@
|
||||
<:Title>
|
||||
Debugger
|
||||
|
||||
<:Body>
|
||||
<div class="row">
|
||||
{{ page._classes.each do |c| }}
|
||||
<div class="col-md-1">
|
||||
{{ c._name }} <br/>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
Messages
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
Source Code
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="row">
|
||||
Virtual Machine Instruction : {{ page._source._name}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="row">
|
||||
Current block: {{ page._block._name}}
|
||||
</div>
|
||||
<div class="row">
|
||||
{{ if page._block.block }}
|
||||
{{ page._block.block.codes.each do |c| }}
|
||||
{{ c.to_s }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
{{ page._registers.each do |r| }}
|
||||
<div class="col-md-1">
|
||||
{{ r._name }} : {{ r._content }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
@ -1,27 +0,0 @@
|
||||
<:Title>
|
||||
{{ view main_path, "title", {controller_group: 'main'} }}
|
||||
|
||||
<:Body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<:nav href="/">Home</:nav>
|
||||
<:nav href="/about">About</:nav>
|
||||
</ul>
|
||||
<h3 class="text-muted">Salama Debugger</h3>
|
||||
</div>
|
||||
|
||||
<:volt:notices />
|
||||
|
||||
{{ view main_path, 'body', {controller_group: 'main'} }}
|
||||
|
||||
<div class="footer">
|
||||
<p> <a href="http://salama-vm.org/" >Salama</a> {{ Time.now.year }}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<:Nav>
|
||||
<li class="{{ if active_tab? }}active{{ end }}">
|
||||
<a href="{{ attrs.href }}">{{ yield }}</a>
|
||||
</li>
|
@ -1,49 +0,0 @@
|
||||
|
||||
require "instruction_view"
|
||||
|
||||
class BlockView
|
||||
|
||||
include React::Component
|
||||
required_param :interpreter
|
||||
|
||||
define_state :block => []
|
||||
define_state :block_name => ""
|
||||
|
||||
before_mount do
|
||||
interpreter.register_event(:instruction_changed, self)
|
||||
update_block
|
||||
end
|
||||
|
||||
def update_block
|
||||
return unless interpreter.instruction
|
||||
block_name! interpreter.block.name
|
||||
codes = interpreter.block.codes.dup
|
||||
slice = codes.index(interpreter.instruction) #- 1
|
||||
codes.shift( slice ) if slice >= 0
|
||||
codes.pop while(codes.length > 4)
|
||||
block! codes
|
||||
end
|
||||
|
||||
def instruction_changed
|
||||
update_block
|
||||
end
|
||||
|
||||
def render
|
||||
return unless block
|
||||
div.block_view do
|
||||
div do
|
||||
h4 { method_name}
|
||||
h4 {"Block: #{block_name}"}
|
||||
end
|
||||
block.each do |code|
|
||||
InstructionView :interpreter => interpreter , :instruction => code
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def method_name
|
||||
bl = interpreter.block
|
||||
return bl.method if bl.method.is_a? String
|
||||
"#{bl.method.for_class.name}.#{bl.method.name}"
|
||||
end
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
class ClassView
|
||||
include React::Component
|
||||
|
||||
required_param :classes, type: {}
|
||||
|
||||
def render
|
||||
div.classes do
|
||||
h4 { "Classes" }
|
||||
classes.each do |name , clas|
|
||||
div.one_class do
|
||||
clas.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,37 +0,0 @@
|
||||
|
||||
require "register_view"
|
||||
require "class_view"
|
||||
require "source_view"
|
||||
require "block_view"
|
||||
require "status_view"
|
||||
|
||||
class Debugger
|
||||
|
||||
include React::Component
|
||||
required_param :machine , :type => Virtual::Machine
|
||||
define_state :interpreter => Interpreter.new
|
||||
|
||||
before_mount do
|
||||
code = Ast::ExpressionList.new( [Ast::CallSiteExpression.new(:putstring, [] ,Ast::StringExpression.new("Hello again"))])
|
||||
Virtual::Compiler.compile( code , machine.space.get_main )
|
||||
machine.run_before "Register::CallImplementation"
|
||||
interpreter.start machine.init
|
||||
end
|
||||
def render
|
||||
div.debugger_view do
|
||||
ClassView classes: machine.space.classes
|
||||
div.file_view do
|
||||
"Future Source code view"
|
||||
end
|
||||
SourceView :interpreter => interpreter
|
||||
BlockView :interpreter => interpreter
|
||||
StatusView :interpreter => interpreter
|
||||
div.registers_view do
|
||||
interpreter.registers.each do |r , oid|
|
||||
RegisterView interpreter: interpreter , register: r
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,28 +0,0 @@
|
||||
|
||||
|
||||
class InstructionView
|
||||
|
||||
include React::Component
|
||||
required_param :interpreter
|
||||
required_param :instruction
|
||||
|
||||
define_state :active => ""
|
||||
|
||||
before_mount do
|
||||
check_active interpreter.instruction
|
||||
end
|
||||
|
||||
def check_active i
|
||||
active! instruction == i ? "bright" : ""
|
||||
|
||||
end
|
||||
def instruction_changed old , ins
|
||||
check_active ins
|
||||
end
|
||||
|
||||
def render
|
||||
div :class => active do
|
||||
instruction.to_s if instruction
|
||||
end
|
||||
end
|
||||
end
|
16
volt/main.rb
16
volt/main.rb
@ -1,16 +0,0 @@
|
||||
require 'opal'
|
||||
require "opal/parser"
|
||||
|
||||
require "salama"
|
||||
require "interpreter"
|
||||
|
||||
require 'opal-react'
|
||||
|
||||
require "debugger"
|
||||
|
||||
require 'opal-jquery'
|
||||
|
||||
Document.ready? do # Document.ready? is a opal-jquery method.
|
||||
machine = Virtual.machine.boot
|
||||
React.render( React.create_element( Debugger , :machine => machine ), Element['#content'] )
|
||||
end
|
@ -1,64 +0,0 @@
|
||||
|
||||
class RegisterView
|
||||
|
||||
include React::Component
|
||||
required_param :interpreter
|
||||
required_param :register
|
||||
|
||||
define_state :objects_id
|
||||
define_state :fields => []
|
||||
|
||||
before_mount do
|
||||
interpreter.register_event(:register_changed, self)
|
||||
interpreter.register_event(:object_changed, self)
|
||||
register_changed( register , nil , interpreter.registers[register])
|
||||
end
|
||||
|
||||
def register_changed reg , old , value
|
||||
reg = reg.symbol unless reg.is_a? Symbol
|
||||
return unless reg == register
|
||||
objects_id! value
|
||||
calc_fields
|
||||
end
|
||||
|
||||
def object_changed reg
|
||||
reg = reg.symbol unless reg.is_a? Symbol
|
||||
return unless reg == register
|
||||
#puts "Object changed in #{reg}"
|
||||
calc_fields
|
||||
end
|
||||
|
||||
def calc_fields
|
||||
#puts "My id #{objects_id} , #{objects_id.class}"
|
||||
object = Virtual.machine.objects[objects_id]
|
||||
if object and ! object.is_a?(String)
|
||||
has_fields = []
|
||||
clazz = object.class.name.split("::").last
|
||||
#puts "found #{clazz}"
|
||||
has_fields << clazz
|
||||
object.get_instance_variables.each do |variable|
|
||||
f = object.get_instance_variable(variable)
|
||||
has_fields << f
|
||||
end
|
||||
fields! has_fields
|
||||
end
|
||||
end
|
||||
|
||||
def render
|
||||
div.register_view do
|
||||
div do
|
||||
objects_id.to_s
|
||||
end
|
||||
fields.each do |attribute|
|
||||
div.col_md_12 do
|
||||
"#{marker(attribute)} - #{attribute.object_id}".span
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def marker var
|
||||
return "W" if var.is_a? String
|
||||
var.class.name.split("::").last[0]
|
||||
end
|
||||
end
|
@ -1,38 +0,0 @@
|
||||
class SourceView
|
||||
|
||||
include React::Component
|
||||
|
||||
required_param :interpreter
|
||||
|
||||
define_state :sources => []
|
||||
|
||||
before_mount do
|
||||
interpreter.register_event(:instruction_changed, self)
|
||||
instruction_changed nil , interpreter.instruction
|
||||
end
|
||||
|
||||
def instruction_changed old , ins
|
||||
text = ins ? source_text(ins.source) : "exit"
|
||||
return if sources.last == text
|
||||
sources << text
|
||||
sources.shift if sources.length > 5
|
||||
sources! sources
|
||||
end
|
||||
|
||||
def render
|
||||
div.source_view do
|
||||
h4 {"Virtual Machine Instruction"}
|
||||
sources.each do |s|
|
||||
s.br
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def source_text source
|
||||
if source.is_a? Virtual::Instruction
|
||||
return source.class.name
|
||||
else
|
||||
return "Method: #{source.name}"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,41 +0,0 @@
|
||||
|
||||
class StatusView
|
||||
|
||||
include React::Component
|
||||
required_param :interpreter
|
||||
|
||||
define_state :state => "starting"
|
||||
define_state :stdout
|
||||
|
||||
before_mount do
|
||||
interpreter.register_event(:instruction_changed, self)
|
||||
end
|
||||
|
||||
def update_state
|
||||
state! interpreter.state
|
||||
stdout! interpreter.stdout
|
||||
end
|
||||
|
||||
def instruction_changed old , nex
|
||||
update_state
|
||||
end
|
||||
|
||||
def render
|
||||
div.status_view do
|
||||
div do
|
||||
button.bright { "next" }.on(:click) { interpreter.tick }
|
||||
" ".br
|
||||
end
|
||||
div do
|
||||
h4 {"Status:"}
|
||||
state.to_s.br
|
||||
end
|
||||
div do
|
||||
h4 {"Stdout:"}
|
||||
end
|
||||
div do
|
||||
interpreter.stdout.br
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user