aboutsummaryrefslogtreecommitdiffstats
path: root/ethereal/ui
diff options
context:
space:
mode:
Diffstat (limited to 'ethereal/ui')
-rw-r--r--ethereal/ui/ext_app.go126
-rw-r--r--ethereal/ui/gui.go92
-rw-r--r--ethereal/ui/html_container.go73
-rw-r--r--ethereal/ui/library.go102
-rw-r--r--ethereal/ui/ui_lib.go130
5 files changed, 457 insertions, 66 deletions
diff --git a/ethereal/ui/ext_app.go b/ethereal/ui/ext_app.go
new file mode 100644
index 000000000..93db0ade1
--- /dev/null
+++ b/ethereal/ui/ext_app.go
@@ -0,0 +1,126 @@
+package ethui
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethpub"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/go-qml/qml"
+)
+
+type AppContainer interface {
+ Create() error
+ Destroy()
+
+ Window() *qml.Window
+ Engine() *qml.Engine
+
+ NewBlock(*ethchain.Block)
+ ObjectChanged(*ethchain.StateObject)
+ StorageChanged(*ethchain.StorageState)
+}
+
+type ExtApplication struct {
+ *ethpub.PEthereum
+
+ blockChan chan ethutil.React
+ changeChan chan ethutil.React
+ quitChan chan bool
+
+ container AppContainer
+ lib *UiLib
+ registeredEvents []string
+}
+
+func NewExtApplication(container AppContainer, lib *UiLib) *ExtApplication {
+ app := &ExtApplication{
+ ethpub.NewPEthereum(lib.eth.StateManager(), lib.eth.BlockChain(), lib.eth.TxPool()),
+ make(chan ethutil.React, 1),
+ make(chan ethutil.React, 1),
+ make(chan bool),
+ container,
+ lib,
+ nil,
+ }
+
+ return app
+}
+
+func (app *ExtApplication) run() {
+ // Set the "eth" api on to the containers context
+ context := app.container.Engine().Context()
+ context.SetVar("eth", app)
+ context.SetVar("ui", app.lib)
+
+ err := app.container.Create()
+ if err != nil {
+ fmt.Println(err)
+
+ return
+ }
+
+ // Call the main loop
+ go app.mainLoop()
+
+ // Subscribe to events
+ reactor := app.lib.eth.Reactor()
+ reactor.Subscribe("newBlock", app.blockChan)
+
+ win := app.container.Window()
+ win.Show()
+ win.Wait()
+
+ app.stop()
+}
+
+func (app *ExtApplication) stop() {
+ // Clean up
+ reactor := app.lib.eth.Reactor()
+ reactor.Unsubscribe("newBlock", app.blockChan)
+ for _, event := range app.registeredEvents {
+ reactor.Unsubscribe(event, app.changeChan)
+ }
+
+ // Kill the main loop
+ app.quitChan <- true
+
+ close(app.blockChan)
+ close(app.quitChan)
+ close(app.changeChan)
+
+ app.container.Destroy()
+}
+
+func (app *ExtApplication) mainLoop() {
+out:
+ for {
+ select {
+ case <-app.quitChan:
+ break out
+ case block := <-app.blockChan:
+ if block, ok := block.Resource.(*ethchain.Block); ok {
+ app.container.NewBlock(block)
+ }
+ case object := <-app.changeChan:
+ if stateObject, ok := object.Resource.(*ethchain.StateObject); ok {
+ app.container.ObjectChanged(stateObject)
+ } else if storageObject, ok := object.Resource.(*ethchain.StorageState); ok {
+ app.container.StorageChanged(storageObject)
+ }
+ }
+ }
+
+}
+
+func (app *ExtApplication) Watch(addr, storageAddr string) {
+ var event string
+ if len(storageAddr) == 0 {
+ event = "object:" + string(ethutil.FromHex(addr))
+ app.lib.eth.Reactor().Subscribe(event, app.changeChan)
+ } else {
+ event = "storage:" + string(ethutil.FromHex(addr)) + ":" + string(ethutil.FromHex(storageAddr))
+ app.lib.eth.Reactor().Subscribe(event, app.changeChan)
+ }
+
+ app.registeredEvents = append(app.registeredEvents, event)
+}
diff --git a/ethereal/ui/gui.go b/ethereal/ui/gui.go
index 89736ac29..522c081a3 100644
--- a/ethereal/ui/gui.go
+++ b/ethereal/ui/gui.go
@@ -2,42 +2,17 @@ package ethui
import (
"bytes"
- "encoding/hex"
"fmt"
"github.com/ethereum/eth-go"
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
- "github.com/niemeyer/qml"
+ "github.com/go-qml/qml"
"math/big"
"strings"
)
-// Block interface exposed to QML
-type Block struct {
- Number int
- Hash string
-}
-
-type Tx struct {
- Value, Hash, Address string
-}
-
-func NewTxFromTransaction(tx *ethchain.Transaction) *Tx {
- hash := hex.EncodeToString(tx.Hash())
- sender := hex.EncodeToString(tx.Recipient)
-
- return &Tx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender}
-}
-
-// Creates a new QML Block from a chain block
-func NewBlockFromBlock(block *ethchain.Block) *Block {
- info := block.BlockInfo()
- hash := hex.EncodeToString(block.Hash())
-
- return &Block{Number: int(info.Number), Hash: hash}
-}
-
type Gui struct {
// The main application window
win *qml.Window
@@ -53,7 +28,6 @@ type Gui struct {
txDb *ethdb.LDBDatabase
addr []byte
-
}
// Create GUI, but doesn't start it
@@ -64,10 +38,16 @@ func New(ethereum *eth.Ethereum) *Gui {
panic(err)
}
- key := ethutil.Config.Db.GetKeys()[0]
- addr := key.Address()
+ data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+ // On first run we won't have any keys yet, so this would crash.
+ // Therefor we check if we are ready to actually start this process
+ var addr []byte
+ if len(data) > 0 {
+ key := ethutil.Config.Db.GetKeys()[0]
+ addr = key.Address()
- ethereum.StateManager().WatchAddr(addr)
+ ethereum.StateManager().WatchAddr(addr)
+ }
return &Gui{eth: ethereum, lib: lib, txDb: db, addr: addr}
}
@@ -77,12 +57,12 @@ func (ui *Gui) Start(assetPath string) {
// Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
- Init: func(p *Block, obj qml.Object) { p.Number = 0; p.Hash = "" },
+ Init: func(p *ethpub.PBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
}, {
- Init: func(p *Tx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
+ Init: func(p *ethpub.PTx, obj qml.Object) { p.Value = ""; p.Hash = ""; p.Address = "" },
}})
- ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.1"))
+ ethutil.Config.SetClientString(fmt.Sprintf("/Ethereal v%s", "0.2"))
ethutil.Config.Log.Infoln("[GUI] Starting GUI")
// Create a new QML engine
ui.engine = qml.NewEngine()
@@ -94,14 +74,27 @@ func (ui *Gui) Start(assetPath string) {
context.SetVar("ui", uiLib)
// Load the main QML interface
- component, err := ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
+ data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
+ var err error
+ var component qml.Object
+ firstRun := len(data) == 0
+
+ if firstRun {
+ component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/first_run.qml"))
+ } else {
+ component, err = ui.engine.LoadFile(uiLib.AssetPath("qml/wallet.qml"))
+ }
if err != nil {
- ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
+ ethutil.Config.Log.Infoln("FATAL: asset not found: you can set an alternative asset path on on the command line using option 'asset_path'")
+
panic(err)
}
- ui.engine.LoadFile(uiLib.AssetPath("qml/transactions.qml"))
ui.win = component.CreateWindow(nil)
+ uiLib.win = ui.win
+ db := &Debugger{ui.win, make(chan bool)}
+ ui.lib.Db = db
+ uiLib.Db = db
// Register the ui as a block processor
//ui.eth.BlockManager.SecondaryBlockProcessor = ui
@@ -111,9 +104,11 @@ func (ui *Gui) Start(assetPath string) {
ethutil.Config.Log.AddLogSystem(ui)
// Loads previous blocks
- go ui.setInitialBlockChain()
- go ui.readPreviousTransactions()
- go ui.update()
+ if firstRun == false {
+ go ui.setInitialBlockChain()
+ go ui.readPreviousTransactions()
+ go ui.update()
+ }
ui.win.Show()
ui.win.Wait()
@@ -135,13 +130,13 @@ func (ui *Gui) readPreviousTransactions() {
for it.Next() {
tx := ethchain.NewTransactionFromBytes(it.Value())
- ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
}
it.Release()
}
func (ui *Gui) ProcessBlock(block *ethchain.Block) {
- ui.win.Root().Call("addBlock", NewBlockFromBlock(block))
+ ui.win.Root().Call("addBlock", ethpub.NewPBlock(block))
}
// Simple go routine function that updates the list of peers in the GUI
@@ -149,23 +144,26 @@ func (ui *Gui) update() {
txChan := make(chan ethchain.TxMsg, 1)
ui.eth.TxPool().Subscribe(txChan)
- account := ui.eth.StateManager().GetAddrState(ui.addr).Account
+ account := ui.eth.StateManager().GetAddrState(ui.addr).Object
unconfirmedFunds := new(big.Int)
ui.win.Root().Call("setWalletValue", fmt.Sprintf("%v", ethutil.CurrencyToString(account.Amount)))
+
+ addrState := ui.eth.StateManager().GetAddrState(ui.addr)
+
for {
select {
case txMsg := <-txChan:
tx := txMsg.Tx
if txMsg.Type == ethchain.TxPre {
- if bytes.Compare(tx.Sender(), ui.addr) == 0 {
- ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ if bytes.Compare(tx.Sender(), ui.addr) == 0 && addrState.Nonce <= tx.Nonce {
+ ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
- ui.eth.StateManager().GetAddrState(ui.addr).Nonce += 1
+ addrState.Nonce += 1
unconfirmedFunds.Sub(unconfirmedFunds, tx.Value)
} else if bytes.Compare(tx.Recipient, ui.addr) == 0 {
- ui.win.Root().Call("addTx", NewTxFromTransaction(tx))
+ ui.win.Root().Call("addTx", ethpub.NewPTx(tx))
ui.txDb.Put(tx.Hash(), tx.RlpEncode())
unconfirmedFunds.Add(unconfirmedFunds, tx.Value)
diff --git a/ethereal/ui/html_container.go b/ethereal/ui/html_container.go
new file mode 100644
index 000000000..4f12aaaf6
--- /dev/null
+++ b/ethereal/ui/html_container.go
@@ -0,0 +1,73 @@
+package ethui
+
+import (
+ "errors"
+ "github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethpub"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/go-qml/qml"
+ "path/filepath"
+)
+
+type HtmlApplication struct {
+ win *qml.Window
+ webView qml.Object
+ engine *qml.Engine
+ lib *UiLib
+ path string
+}
+
+func NewHtmlApplication(path string, lib *UiLib) *HtmlApplication {
+ engine := qml.NewEngine()
+
+ return &HtmlApplication{engine: engine, lib: lib, path: path}
+
+}
+
+func (app *HtmlApplication) Create() error {
+ component, err := app.engine.LoadFile(app.lib.AssetPath("qml/webapp.qml"))
+ if err != nil {
+ return err
+ }
+
+ if filepath.Ext(app.path) == "eth" {
+ return errors.New("Ethereum package not yet supported")
+
+ // TODO
+ ethutil.OpenPackage(app.path)
+ }
+
+ win := component.CreateWindow(nil)
+ win.Set("url", app.path)
+ webView := win.ObjectByName("webView")
+
+ app.win = win
+ app.webView = webView
+
+ return nil
+}
+
+func (app *HtmlApplication) Engine() *qml.Engine {
+ return app.engine
+}
+
+func (app *HtmlApplication) Window() *qml.Window {
+ return app.win
+}
+
+func (app *HtmlApplication) NewBlock(block *ethchain.Block) {
+ b := &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+ app.webView.Call("onNewBlockCb", b)
+}
+
+func (app *HtmlApplication) ObjectChanged(stateObject *ethchain.StateObject) {
+ app.webView.Call("onObjectChangeCb", ethpub.NewPStateObject(stateObject))
+}
+
+func (app *HtmlApplication) StorageChanged(storageObject *ethchain.StorageState) {
+ app.webView.Call("onStorageChangeCb", ethpub.NewPStorageState(storageObject))
+}
+
+func (app *HtmlApplication) Destroy() {
+ app.engine.Destroy()
+}
diff --git a/ethereal/ui/library.go b/ethereal/ui/library.go
index 05fffd579..1328cd6b7 100644
--- a/ethereal/ui/library.go
+++ b/ethereal/ui/library.go
@@ -4,7 +4,10 @@ import (
"encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethchain"
+ "github.com/ethereum/eth-go/ethpub"
"github.com/ethereum/eth-go/ethutil"
+ "github.com/ethereum/go-ethereum/utils"
+ "github.com/obscuren/secp256k1-go"
"strings"
)
@@ -12,49 +15,114 @@ type EthLib struct {
stateManager *ethchain.StateManager
blockChain *ethchain.BlockChain
txPool *ethchain.TxPool
+ Db *Debugger
}
-func (lib *EthLib) CreateTx(receiver, a, data string) string {
+func (lib *EthLib) ImportAndSetPrivKey(privKey string) bool {
+ fmt.Println(privKey)
+ mnemonic := strings.Split(privKey, " ")
+ if len(mnemonic) == 24 {
+ fmt.Println("Got mnemonic key, importing.")
+ key := ethutil.MnemonicDecode(mnemonic)
+ utils.ImportPrivateKey(key)
+ } else if len(mnemonic) == 1 {
+ fmt.Println("Got hex key, importing.")
+ utils.ImportPrivateKey(privKey)
+ } else {
+ fmt.Println("Did not recognise format, exiting.")
+ return false
+ }
+ return true
+}
+
+func (lib *EthLib) CreateAndSetPrivKey() (string, string, string, string) {
+ pub, prv := secp256k1.GenerateKeyPair()
+ pair := &ethutil.Key{PrivateKey: prv, PublicKey: pub}
+ ethutil.Config.Db.Put([]byte("KeyRing"), pair.RlpEncode())
+ mne := ethutil.MnemonicEncode(ethutil.Hex(prv))
+ mnemonicString := strings.Join(mne, " ")
+ return mnemonicString, fmt.Sprintf("%x", pair.Address()), fmt.Sprintf("%x", prv), fmt.Sprintf("%x", pub)
+}
+
+func (lib *EthLib) GetKey() string {
+ return ethutil.Hex(ethutil.Config.Db.GetKeys()[0].Address())
+}
+
+func (lib *EthLib) GetStateObject(address string) *ethpub.PStateObject {
+ stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address))
+ if stateObject != nil {
+ return ethpub.NewPStateObject(stateObject)
+ }
+
+ // See GetStorage for explanation on "nil"
+ return ethpub.NewPStateObject(nil)
+}
+
+func (lib *EthLib) Watch(addr, storageAddr string) {
+ // lib.stateManager.Watch(ethutil.FromHex(addr), ethutil.FromHex(storageAddr))
+}
+
+func (lib *EthLib) CreateTx(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
+ return lib.Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr)
+}
+
+func (lib *EthLib) Transact(recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) {
var hash []byte
- if len(receiver) == 0 {
- hash = ethchain.ContractAddr
+ var contractCreation bool
+ if len(recipient) == 0 {
+ contractCreation = true
} else {
var err error
- hash, err = hex.DecodeString(receiver)
+ hash, err = hex.DecodeString(recipient)
if err != nil {
- return err.Error()
+ return "", err
}
}
- k, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
- keyPair := ethutil.NewKeyFromBytes(k)
+ keyPair := ethutil.Config.Db.GetKeys()[0]
+ value := ethutil.Big(valueStr)
+ gas := ethutil.Big(gasStr)
+ gasPrice := ethutil.Big(gasPriceStr)
+ var tx *ethchain.Transaction
+ // Compile and assemble the given data
+ if contractCreation {
+ // Compile script
+ mainScript, initScript, err := utils.CompileScript(dataStr)
+ if err != nil {
+ return "", err
+ }
- amount := ethutil.Big(a)
- code := ethchain.Compile(strings.Split(data, "\n"))
- tx := ethchain.NewTransaction(hash, amount, code)
- tx.Nonce = lib.stateManager.GetAddrState(keyPair.Address()).Nonce
+ tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript)
+ } else {
+ lines := strings.Split(dataStr, "\n")
+ var data []byte
+ for _, line := range lines {
+ data = append(data, ethutil.BigToBytes(ethutil.Big(line), 256)...)
+ }
+ tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, data)
+ }
+ acc := lib.stateManager.GetAddrState(keyPair.Address())
+ tx.Nonce = acc.Nonce
tx.Sign(keyPair.PrivateKey)
-
lib.txPool.QueueTransaction(tx)
- if len(receiver) == 0 {
+ if contractCreation {
ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:])
} else {
ethutil.Config.Log.Infof("Tx hash %x", tx.Hash())
}
- return ethutil.Hex(tx.Hash())
+ return ethutil.Hex(tx.Hash()), nil
}
-func (lib *EthLib) GetBlock(hexHash string) *Block {
+func (lib *EthLib) GetBlock(hexHash string) *ethpub.PBlock {
hash, err := hex.DecodeString(hexHash)
if err != nil {
return nil
}
block := lib.blockChain.GetBlock(hash)
- fmt.Println(block)
- return &Block{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
+ return &ethpub.PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())}
}
diff --git a/ethereal/ui/ui_lib.go b/ethereal/ui/ui_lib.go
index 4441a7238..0feb522bc 100644
--- a/ethereal/ui/ui_lib.go
+++ b/ethereal/ui/ui_lib.go
@@ -2,21 +2,33 @@ package ethui
import (
"bitbucket.org/kardianos/osext"
+ "fmt"
"github.com/ethereum/eth-go"
+ "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
- "github.com/niemeyer/qml"
+ "github.com/ethereum/go-ethereum/utils"
+ "github.com/go-qml/qml"
+ "github.com/obscuren/mutan"
"os"
"path"
"path/filepath"
"runtime"
)
+type memAddr struct {
+ Num string
+ Value string
+}
+
// UI Library that has some basic functionality exposed
type UiLib struct {
engine *qml.Engine
eth *eth.Ethereum
connected bool
assetPath string
+ // The main application window
+ win *qml.Window
+ Db *Debugger
}
func NewUiLib(engine *qml.Engine, eth *eth.Ethereum, assetPath string) *UiLib {
@@ -40,6 +52,40 @@ func (ui *UiLib) Open(path string) {
}()
}
+func (ui *UiLib) OpenHtml(path string) {
+ container := NewHtmlApplication(path, ui)
+ app := NewExtApplication(container, ui)
+
+ go app.run()
+}
+
+func (ui *UiLib) Watch(addr, storageAddr string) {
+ if len(storageAddr) == 0 {
+ ui.eth.Reactor().Subscribe("storage:"+string(ethutil.FromHex(addr))+":"+string(ethutil.FromHex(storageAddr)), nil)
+ } else {
+ ui.eth.Reactor().Subscribe("object:"+string(ethutil.FromHex(addr)), nil)
+ }
+}
+
+func (ui *UiLib) Muted(content string) {
+ component, err := ui.engine.LoadFile(ui.AssetPath("qml/muted.qml"))
+ if err != nil {
+ ethutil.Config.Log.Debugln(err)
+
+ return
+ }
+ win := component.CreateWindow(nil)
+ go func() {
+ path := "file://" + ui.AssetPath("muted/index.html")
+ win.Set("url", path)
+ //debuggerPath := "file://" + ui.AssetPath("muted/debugger.html")
+ //win.Set("debugUrl", debuggerPath)
+
+ win.Show()
+ win.Wait()
+ }()
+}
+
func (ui *UiLib) Connect(button qml.Object) {
if !ui.connected {
ui.eth.Start()
@@ -58,7 +104,6 @@ func (ui *UiLib) AssetPath(p string) string {
func DefaultAssetPath() string {
var base string
-
// If the current working directory is the go-ethereum dir
// assume a debug build and use the source directory as
// asset directory.
@@ -82,3 +127,84 @@ func DefaultAssetPath() string {
return base
}
+
+func (ui *UiLib) DebugTx(recipient, valueStr, gasStr, gasPriceStr, data string) {
+ state := ui.eth.BlockChain().CurrentBlock.State()
+
+ mainInput, _ := mutan.PreProcess(data)
+ callerScript, err := utils.Compile(mainInput)
+ if err != nil {
+ ethutil.Config.Log.Debugln(err)
+
+ return
+ }
+
+ dis := ethchain.Disassemble(callerScript)
+ ui.win.Root().Call("clearAsm")
+
+ for _, str := range dis {
+ ui.win.Root().Call("setAsm", str)
+ }
+ callerTx := ethchain.NewContractCreationTx(ethutil.Big(valueStr), ethutil.Big(gasStr), ethutil.Big(gasPriceStr), callerScript, nil)
+
+ // Contract addr as test address
+ keyPair := ethutil.Config.Db.GetKeys()[0]
+ account := ui.eth.StateManager().GetAddrState(keyPair.Address()).Object
+ c := ethchain.MakeContract(callerTx, state)
+ callerClosure := ethchain.NewClosure(account, c, c.Script(), state, ethutil.Big(gasStr), ethutil.Big(gasPriceStr), ethutil.Big(valueStr))
+
+ block := ui.eth.BlockChain().CurrentBlock
+ vm := ethchain.NewVm(state, ui.eth.StateManager(), ethchain.RuntimeVars{
+ Origin: account.Address(),
+ BlockNumber: block.BlockInfo().Number,
+ PrevHash: block.PrevHash,
+ Coinbase: block.Coinbase,
+ Time: block.Time,
+ Diff: block.Difficulty,
+ TxData: nil,
+ })
+
+ go func() {
+ callerClosure.Call(vm, nil, ui.Db.halting)
+
+ state.Reset()
+ }()
+}
+
+func (ui *UiLib) Next() {
+ ui.Db.Next()
+}
+
+type Debugger struct {
+ win *qml.Window
+ N chan bool
+}
+
+func (d *Debugger) halting(pc int, op ethchain.OpCode, mem *ethchain.Memory, stack *ethchain.Stack) {
+ d.win.Root().Call("setInstruction", pc)
+ d.win.Root().Call("clearMem")
+ d.win.Root().Call("clearStack")
+
+ addr := 0
+ for i := 0; i+32 <= mem.Len(); i += 32 {
+ d.win.Root().Call("setMem", memAddr{fmt.Sprintf("%03d", addr), fmt.Sprintf("% x", mem.Data()[i:i+32])})
+ addr++
+ }
+
+ for _, val := range stack.Data() {
+ d.win.Root().Call("setStack", val.String())
+ }
+
+out:
+ for {
+ select {
+ case <-d.N:
+ break out
+ default:
+ }
+ }
+}
+
+func (d *Debugger) Next() {
+ d.N <- true
+}