This commit is contained in:
Torsten Ruger 2015-07-30 15:18:19 +03:00
parent eba80177ae
commit b81c8e69b7
27 changed files with 23 additions and 657 deletions

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -1,7 +0,0 @@
<:Title>
About
<:Body>
<h1>About</h1>
<p>About page...</p>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -1,7 +0,0 @@
<:Title>
About
<:Body>
<h1>About</h1>
<p>About page...</p>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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