Debugger Iframe plugin (#745)

fix #588
pull/683/head
yann300 4 years ago committed by GitHub
parent 03437f3295
commit acb9e5892a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/debugger/.babelrc
  2. 16
      apps/debugger/.browserslistrc
  3. 17
      apps/debugger/src/app/app.tsx
  4. 157
      apps/debugger/src/app/debugger-api.ts
  5. 28
      apps/debugger/src/app/debugger.ts
  6. 0
      apps/debugger/src/assets/.gitkeep
  7. 3
      apps/debugger/src/environments/environment.prod.ts
  8. 6
      apps/debugger/src/environments/environment.ts
  9. BIN
      apps/debugger/src/favicon.ico
  10. 15
      apps/debugger/src/index.html
  11. 1
      apps/debugger/src/index.ts
  12. 9
      apps/debugger/src/main.tsx
  13. 7
      apps/debugger/src/polyfills.ts
  14. 1
      apps/debugger/src/styles.css
  15. 9
      apps/debugger/tsconfig.app.json
  16. 16
      apps/debugger/tsconfig.json
  17. 15
      apps/debugger/tsconfig.spec.json
  18. 17
      apps/debugger/webpack.config.js
  19. 2
      apps/remix-ide-e2e/src/commands/debugTransaction.ts
  20. 1
      apps/remix-ide-e2e/src/commands/goToVMTraceStep.ts
  21. 3
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  22. 2
      apps/remix-ide-e2e/src/tests/ballot_0_4_11.test.ts
  23. 2
      apps/remix-ide-e2e/src/tests/debugger.test.ts
  24. 7
      apps/remix-ide/src/app.js
  25. 6
      apps/remix-ide/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js
  26. 15
      apps/remix-ide/src/app/components/local-plugin.js
  27. 15
      apps/remix-ide/src/app/editor/editor.js
  28. 91
      apps/remix-ide/src/app/tabs/debugger-tab.js
  29. 2
      apps/remix-ide/src/lib/offsetToLineColumnConverter.js
  30. 6
      libs/remix-debug/src/debugger/debugger.ts
  31. 2
      libs/remix-ui/debugger-ui/src/index.ts
  32. 64
      libs/remix-ui/debugger-ui/src/lib/DebuggerAPI.ts
  33. 51
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  34. 66
      libs/remix-ui/debugger-ui/src/lib/idebugger-api.ts
  35. 3
      nx.json
  36. 805
      package-lock.json
  37. 6
      package.json
  38. 1
      tsconfig.json
  39. 65
      tslint.json
  40. 69
      workspace.json

@ -0,0 +1,4 @@
{
"presets": ["@nrwl/react/babel"],
"plugins": []
}

@ -0,0 +1,16 @@
# This file is used by:
# 1. autoprefixer to adjust CSS to support the below specified browsers
# 2. babel preset-env to adjust included polyfills
#
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# If you need to support different browsers in production, you may tweak the list below.
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major version
last 2 iOS major versions
Firefox ESR
not IE 9-11 # For IE 9-11 support, remove 'not'.

@ -0,0 +1,17 @@
import React, { useState, useEffect } from 'react';
import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line
import { DebuggerClientApi } from './debugger'
const remix = new DebuggerClientApi()
export const App = () => {
return (
<div className="debugger">
<DebuggerUI debuggerAPI={remix} />
</div>
);
};
export default App;

@ -0,0 +1,157 @@
import Web3 from 'web3'
import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug'
import { CompilationOutput, Sources } from '@remix-ui/debugger-ui'
import type { CompilationResult } from '@remix-project/remix-solidity-ts'
export const DebuggerApiMixin = (Base) => class extends Base {
initDebuggerApi () {
this.debugHash = null
const self = this
this.web3Provider = {
sendAsync(payload, callback) {
self.call('web3Provider', 'sendAsync', payload)
.then(result => callback(null, result))
.catch(e => callback(e))
}
}
this._web3 = new Web3(this.web3Provider)
this.offsetToLineColumnConverter = {
async offsetToLineColumn (rawLocation, file, sources, asts) {
return await self.call('offsetToLineColumnConverter', 'offsetToLineColumn', rawLocation, file, sources, asts)
}
}
}
// on()
// call()
// onDebugRequested()
// onRemoveHighlights()
web3 () {
return this._web3
}
async discardHighlight () {
await this.call('editor', 'discardHighlight')
}
async highlight (lineColumnPos, path) {
await this.call('editor', 'highlight', lineColumnPos, path)
}
async getFile (path) {
return await this.call('fileManager', 'getFile', path)
}
async setFile (path, content) {
await this.call('fileManager', 'setFile', path, content)
}
onBreakpointCleared (listener) {
this.onBreakpointClearedListener = listener
}
onBreakpointAdded (listener) {
this.onBreakpointAddedListener = listener
}
onEditorContentChanged (listener) {
this.onEditorContentChangedListener = listener
}
onDebugRequested (listener) {
this.onDebugRequestedListener = listener
}
onRemoveHighlights (listener) {
this.onRemoveHighlightsListener = listener
}
async fetchContractAndCompile (address, receipt) {
const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address
const targetAddress = target || receipt.contractAddress || receipt.to
const codeAtAddress = await this._web3.eth.getCode(targetAddress)
const output = await this.call('fetchAndCompile', 'resolve', targetAddress, codeAtAddress, 'browser/.debug')
return new CompilerAbstract(output.languageversion, output.data, output.source)
}
async getDebugWeb3 () {
let web3
let network
try {
network = await this.call('network', 'detectNetwork')
} catch (e) {
web3 = this.web3()
}
if (!web3) {
const webDebugNode = remixDebug.init.web3DebugNode(network.name)
web3 = !webDebugNode ? this.web3() : webDebugNode
}
remixDebug.init.extendWeb3(web3)
return web3
}
async getTrace (hash) {
if (!hash) return
const web3 = await this.getDebugWeb3()
const currentReceipt = await web3.eth.getTransactionReceipt(hash)
const debug = new Debugger({
web3,
offsetToLineColumnConverter: this.offsetToLineColumnConverter,
compilationResult: async (address) => {
try {
return await this.fetchContractAndCompile(address, currentReceipt)
} catch (e) {
console.error(e)
}
return null
},
debugWithGeneratedSources: false
})
return await debug.debugger.traceManager.getTrace(hash)
}
debug (hash) {
this.debugHash = hash
this.onDebugRequestedListener(hash)
}
onActivation () {
this.on('editor', 'breakpointCleared', (fileName, row) => this.onBreakpointClearedListener(fileName, row))
this.on('editor', 'breakpointAdded', (fileName, row) => this.onBreakpointAddedListener(fileName, row))
this.on('editor', 'contentChanged', () => this.onEditorContentChangedListener())
}
onDeactivation () {
this.onRemoveHighlightsListener()
this.off('editor', 'breakpointCleared')
this.off('editor', 'breakpointAdded')
this.off('editor', 'contentChanged')
}
}
export class CompilerAbstract implements CompilationOutput { // this is a subset of /remix-ide/src/app/compiler/compiler-abstract.js
languageversion
data
source
constructor (languageversion: string, data: CompilationResult, source: { sources: Sources, target: string }) {
this.languageversion = languageversion
this.data = data
this.source = source // source code
}
getSourceName (fileIndex) {
if (this.data && this.data.sources) {
return Object.keys(this.data.sources)[fileIndex]
} else if (Object.keys(this.source.sources).length === 1) {
// if we don't have ast, we return the only one filename present.
const sourcesArray = Object.keys(this.source.sources)
return sourcesArray[0]
}
return null
}
}

@ -0,0 +1,28 @@
import { PluginClient } from "@remixproject/plugin";
import { createClient } from "@remixproject/plugin-webview";
import { IDebuggerApi, RawLocation, Sources, Asts, LineColumnLocation,
onBreakpointClearedListener, onBreakpointAddedListener, onEditorContentChanged, TransactionReceipt } from '@remix-ui/debugger-ui'
import { DebuggerApiMixin, CompilerAbstract} from './debugger-api'
export class DebuggerClientApi extends DebuggerApiMixin(PluginClient) {
constructor () {
super()
createClient(this as any)
this.initDebuggerApi()
}
offsetToLineColumnConverter: IDebuggerApi['offsetToLineColumnConverter']
debugHash: string
debugHashRequest: number
removeHighlights: boolean
onBreakpointCleared: (listener: onBreakpointClearedListener) => void
onBreakpointAdded: (listener: onBreakpointAddedListener) => void
onEditorContentChanged: (listener: onEditorContentChanged) => void
discardHighlight: () => Promise<void>
highlight: (lineColumnPos: LineColumnLocation, path: string) => Promise<void>
fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilerAbstract>
getFile: (path: string) => Promise<string>
setFile: (path: string, content: string) => Promise<void>
getDebugWeb3: () => any // returns an instance of web3.js
}

@ -0,0 +1,3 @@
export const environment = {
production: true
};

@ -0,0 +1,6 @@
// This file can be replaced during build by using the `fileReplacements` array.
// When building for production, this file is replaced with `environment.prod.ts`.
export const environment = {
production: false
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Debugger</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"/>
</head>
<body>
<div id="root"></div>
</body>
</html>

@ -0,0 +1 @@
export * from './app/debugger-api';

@ -0,0 +1,9 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/app';
ReactDOM.render(
<App />,
document.getElementById('root')
);

@ -0,0 +1,7 @@
/**
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
*
* See: https://github.com/zloirock/core-js#babel
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"jsx": "react",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"types": ["node", "jest"],
"resolveJsonModule": true
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.spec.js",
"**/*.spec.jsx",
"**/*.d.ts"
]
}

@ -0,0 +1,17 @@
const nxWebpack = require('@nrwl/react/plugins/webpack')
module.exports = config => {
const nxWebpackConfig = nxWebpack(config)
return {
...nxWebpackConfig,
node: {
fs: 'empty',
tls: 'empty',
readline: 'empty',
net: 'empty',
module: 'empty',
child_process: 'empty'
}
}
}

@ -19,7 +19,7 @@ function checkStyle (browser: NightwatchBrowser, index: number, callback: VoidFu
debugBtn && debugBtn.click() debugBtn && debugBtn.click()
}, [index], function () { }, [index], function () {
callback() browser.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]').perform(() => callback())
}) })
} }

@ -14,6 +14,7 @@ function goToVMtraceStep (browser: NightwatchBrowser, step: number, incr: number
browser.execute(function () { browser.execute(function () {
return document.querySelector('#stepdetail').innerHTML return document.querySelector('#stepdetail').innerHTML
}, [], function (result) { }, [], function (result) {
console.log('goToVMtraceStep', result)
if (typeof result.value === 'string' && ( result.value.indexOf('vm trace step:') !== -1 && result.value.indexOf(step.toString()) !== -1)) { if (typeof result.value === 'string' && ( result.value.indexOf('vm trace step:') !== -1 && result.value.indexOf(step.toString()) !== -1)) {
done() done()
} else if (incr > 1000) { } else if (incr > 1000) {

@ -39,10 +39,11 @@ module.exports = {
'Debug Ballot / delegate': function (browser: NightwatchBrowser) { 'Debug Ballot / delegate': function (browser: NightwatchBrowser) {
browser.pause(500) browser.pause(500)
.click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]') .click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]')
.pause(2000) .waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
// .clickLaunchIcon('debugger') // .clickLaunchIcon('debugger')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000) .pause(2000)
.waitForElementVisible('#stepdetail')
.goToVMTraceStep(79) .goToVMTraceStep(79)
.pause(1000) .pause(1000)
.checkVariableDebug('soliditystate', stateCheck) .checkVariableDebug('soliditystate', stateCheck)

@ -50,8 +50,10 @@ module.exports = {
browser.pause(500) browser.pause(500)
.click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]') .click('*[data-id="txLoggerDebugButton0x41fab8ea5b1d9fba5e0a6545ca1a2d62fff518578802c033c2b9a031a01c31b3"]')
.pause(2000) .pause(2000)
.waitForElementVisible('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]') .click('*[data-id="buttonNavigatorJumpPreviousBreakpoint"]')
.pause(2000) .pause(2000)
.waitForElementVisible('#stepdetail')
.goToVMTraceStep(20) .goToVMTraceStep(20)
.pause(1000) .pause(1000)
.checkVariableDebug('callstackpanel', ["0x692a70D2e424a56D2C6C27aA97D1a86395877b3A"]) .checkVariableDebug('callstackpanel', ["0x692a70D2e424a56D2C6C27aA97D1a86395877b3A"])

@ -89,6 +89,7 @@ module.exports = {
.createContract('"tokenName", "symbol"') .createContract('"tokenName", "symbol"')
.debugTransaction(2) .debugTransaction(2)
.pause(2000) .pause(2000)
.waitForElementVisible('#stepdetail')
.goToVMTraceStep(10) .goToVMTraceStep(10)
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.indexOf(`constructor (string memory name_, string memory symbol_) public { browser.assert.ok(content.indexOf(`constructor (string memory name_, string memory symbol_) public {
@ -119,6 +120,7 @@ module.exports = {
.clickFunction('test1 - transact (not payable)', {types: 'bytes userData', values: '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000015b38da6a701c568545dcfcb03fcb875f56beddc4'}) .clickFunction('test1 - transact (not payable)', {types: 'bytes userData', values: '0x000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000015b38da6a701c568545dcfcb03fcb875f56beddc4'})
.debugTransaction(4) .debugTransaction(4)
.pause(2000) .pause(2000)
.waitForElementVisible('#stepdetail')
.goToVMTraceStep(261) .goToVMTraceStep(261)
.pause(1000) .pause(1000)
/* /*

@ -50,7 +50,7 @@ const CompilersArtefacts = require('./app/compiler/compiler-artefacts')
const CompileTab = require('./app/tabs/compile-tab') const CompileTab = require('./app/tabs/compile-tab')
const SettingsTab = require('./app/tabs/settings-tab') const SettingsTab = require('./app/tabs/settings-tab')
const AnalysisTab = require('./app/tabs/analysis-tab') const AnalysisTab = require('./app/tabs/analysis-tab')
const DebuggerTab = require('./app/tabs/debugger-tab') const { DebuggerTab } = require('./app/tabs/debugger-tab')
const TestTab = require('./app/tabs/test-tab') const TestTab = require('./app/tabs/test-tab')
const FilePanel = require('./app/panels/file-panel') const FilePanel = require('./app/panels/file-panel')
const Editor = require('./app/editor/editor') const Editor = require('./app/editor/editor')
@ -370,10 +370,7 @@ Please make a backup of your contracts and start using http://remix.ethereum.org
registry.get('fileproviders/browser').api registry.get('fileproviders/browser').api
) )
const analysis = new AnalysisTab(registry) const analysis = new AnalysisTab(registry)
const debug = new DebuggerTab( const debug = new DebuggerTab()
blockchain,
registry.get('editor').api,
registry.get('offsettolinecolumnconverter').api)
const test = new TestTab( const test = new TestTab(
registry.get('filemanager').api, registry.get('filemanager').api,
registry.get('offsettolinecolumnconverter').api, registry.get('offsettolinecolumnconverter').api,

@ -26,10 +26,11 @@ export default class FetchAndCompile extends Plugin {
* Returns compilation data * Returns compilation data
* *
* @param {string} contractAddress - Address of the contrac to resolve * @param {string} contractAddress - Address of the contrac to resolve
* @param {string} compilersartefacts - Object containing a mapping of compilation results (byContractAddress and __last) * @param {string} deployedBytecode - deployedBytecode of the contract
* @param {string} targetPath - Folder where to save the compilation arfefacts
* @return {CompilerAbstract} - compilation data targeting the given @arg contractAddress * @return {CompilerAbstract} - compilation data targeting the given @arg contractAddress
*/ */
async resolve (contractAddress, targetPath, web3) { async resolve (contractAddress, codeAtAddress, targetPath) {
contractAddress = ethutil.toChecksumAddress(contractAddress) contractAddress = ethutil.toChecksumAddress(contractAddress)
const compilersartefacts = globalRegistry.get('compilersartefacts').api const compilersartefacts = globalRegistry.get('compilersartefacts').api
@ -52,7 +53,6 @@ export default class FetchAndCompile extends Plugin {
if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation() if (!this.sourceVerifierNetWork.includes(network.name)) return localCompilation()
// check if the contract if part of the local compilation result // check if the contract if part of the local compilation result
const codeAtAddress = await web3.eth.getCode(contractAddress)
const compilation = localCompilation() const compilation = localCompilation()
if (compilation) { if (compilation) {
let found = false let found = false

@ -67,11 +67,20 @@ module.exports = class LocalPlugin {
this.profile[key] = e.target.value this.profile[key] = e.target.value
} }
updateMethods ({ target }) {
if (target.value) {
try {
this.profile.methods = target.value.split(',')
} catch (e) {}
}
}
/** The form to create a local plugin */ /** The form to create a local plugin */
form () { form () {
const name = this.profile.name || '' const name = this.profile.name || ''
const url = this.profile.url || '' const url = this.profile.url || ''
const displayName = this.profile.displayName || '' const displayName = this.profile.displayName || ''
const methods = (this.profile.methods && this.profile.methods.join(',')) || ''
const radioSelection = (key, label, message) => { const radioSelection = (key, label, message) => {
return this.profile[key] === label return this.profile[key] === label
? yo`<div class="radio"> ? yo`<div class="radio">
@ -94,6 +103,12 @@ module.exports = class LocalPlugin {
<label for="plugin-displayname">Display Name</label> <label for="plugin-displayname">Display Name</label>
<input class="form-control" onchange="${e => this.updateDisplayName(e)}" value="${displayName}" id="plugin-displayname" data-id="localPluginDisplayName" placeholder="Name in the header"> <input class="form-control" onchange="${e => this.updateDisplayName(e)}" value="${displayName}" id="plugin-displayname" data-id="localPluginDisplayName" placeholder="Name in the header">
</div> </div>
<div class="form-group">
<label for="plugin-methods">Api (comma separated list of methods name)</label>
<input class="form-control" onchange="${e => this.updateMethods(e)}" value="${methods}" id="plugin-methods" data-id="localPluginMethods" placeholder="Name in the header">
</div>
<div class="form-group"> <div class="form-group">
<label for="plugin-url">Url <small>(required)</small></label> <label for="plugin-url">Url <small>(required)</small></label>
<input class="form-control" onchange="${e => this.updateUrl(e)}" value="${url}" id="plugin-url" data-id="localPluginUrl" placeholder="ex: https://localhost:8000"> <input class="form-control" onchange="${e => this.updateUrl(e)}" value="${url}" id="plugin-url" data-id="localPluginUrl" placeholder="ex: https://localhost:8000">

@ -174,29 +174,34 @@ class Editor extends Plugin {
const breakpoints = e.editor.session.getBreakpoints() const breakpoints = e.editor.session.getBreakpoints()
for (const k in breakpoints) { for (const k in breakpoints) {
if (k === row.toString()) { if (k === row.toString()) {
this.event.trigger('breakpointCleared', [this.currentSession, row]) this.triggerEvent('breakpointCleared', [this.currentSession, row])
e.editor.session.clearBreakpoint(row) e.editor.session.clearBreakpoint(row)
e.stop() e.stop()
return return
} }
} }
this.setBreakpoint(row) this.setBreakpoint(row)
this.event.trigger('breakpointAdded', [this.currentSession, row]) this.triggerEvent('breakpointAdded', [this.currentSession, row])
e.stop() e.stop()
}) })
// Do setup on initialisation here // Do setup on initialisation here
this.editor.on('changeSession', () => { this.editor.on('changeSession', () => {
this._onChange() this._onChange()
this.event.trigger('sessionSwitched', []) this.triggerEvent('sessionSwitched', [])
this.editor.getSession().on('change', () => { this.editor.getSession().on('change', () => {
this._onChange() this._onChange()
this.sourceHighlighters.discardAllHighlights() this.sourceHighlighters.discardAllHighlights()
this.event.trigger('contentChanged', []) this.triggerEvent('contentChanged', [])
}) })
}) })
} }
triggerEvent (name, params) {
this.event.trigger(name, params) // internal stack
this.emit(name, ...params) // plugin stack
}
onActivation () { onActivation () {
this.on('sidePanel', 'focusChanged', (name) => this.sourceHighlighters.hideHighlightsExcept(name)) this.on('sidePanel', 'focusChanged', (name) => this.sourceHighlighters.hideHighlightsExcept(name))
this.on('sidePanel', 'pluginDisabled', (name) => this.sourceHighlighters.discardHighlight(name)) this.on('sidePanel', 'pluginDisabled', (name) => this.sourceHighlighters.discardHighlight(name))
@ -247,7 +252,7 @@ class Editor extends Plugin {
window.clearTimeout(this.saveTimeout) window.clearTimeout(this.saveTimeout)
} }
this.saveTimeout = window.setTimeout(() => { this.saveTimeout = window.setTimeout(() => {
this.event.trigger('requiringToSaveCurrentfile', []) this.triggerEvent('requiringToSaveCurrentfile', [])
}, 5000) }, 5000)
} }

@ -1,7 +1,7 @@
import toaster from '../ui/tooltip' import toaster from '../ui/tooltip'
import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line import { DebuggerUI } from '@remix-ui/debugger-ui' // eslint-disable-line
import { DebuggerApiMixin } from '@remixproject/debugger-plugin'
import { ViewPlugin } from '@remixproject/engine-web' import { ViewPlugin } from '@remixproject/engine-web'
import remixDebug, { TransactionDebugger as Debugger } from '@remix-project/remix-debug'
import * as packageJson from '../../../../../package.json' import * as packageJson from '../../../../../package.json'
import React from 'react' // eslint-disable-line import React from 'react' // eslint-disable-line
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
@ -21,16 +21,11 @@ const profile = {
version: packageJson.version version: packageJson.version
} }
class DebuggerTab extends ViewPlugin { export class DebuggerTab extends DebuggerApiMixin(ViewPlugin) {
constructor (blockchain, editor, offsetToLineColumnConverter) { constructor () {
super(profile) super(profile)
this.el = null this.el = null
this.editor = editor this.initDebuggerApi()
this.offsetToLineColumnConverter = offsetToLineColumnConverter
this.blockchain = blockchain
this.debugHash = null
this.removeHighlights = false
this.debugHashRequest = 0
} }
render () { render () {
@ -63,90 +58,12 @@ class DebuggerTab extends ViewPlugin {
this.renderComponent() this.renderComponent()
// this.call('manager', 'activatePlugin', 'udapp')
return this.el return this.el
} }
async discardHighlight () {
await this.call('editor', 'discardHighlight')
}
async highlight (lineColumnPos, path) {
await this.call('editor', 'highlight', lineColumnPos, path)
}
async getFile (path) {
await this.call('fileManager', 'getFile', path)
}
async setFile (path, content) {
await this.call('fileManager', 'setFile', path, content)
}
renderComponent () { renderComponent () {
ReactDOM.render( ReactDOM.render(
<DebuggerUI debuggerAPI={this} /> <DebuggerUI debuggerAPI={this} />
, this.el) , this.el)
} }
deactivate () {
this.removeHighlights = true
this.renderComponent()
super.deactivate()
}
debug (hash) {
this.debugHash = hash
this.debugHashRequest++ // so we can trigger a debug using the same hash 2 times in a row. that's needs to be improved
this.renderComponent()
}
getDebugWeb3 () {
return new Promise((resolve, reject) => {
this.blockchain.detectNetwork((error, network) => {
let web3
if (error || !network) {
web3 = remixDebug.init.web3DebugNode(this.blockchain.web3())
} else {
const webDebugNode = remixDebug.init.web3DebugNode(network.name)
web3 = !webDebugNode ? this.blockchain.web3() : webDebugNode
}
remixDebug.init.extendWeb3(web3)
resolve(web3)
})
})
}
async getTrace (hash) {
if (!hash) return
const web3 = await this.getDebugWeb3()
const currentReceipt = await web3.eth.getTransactionReceipt(hash)
const debug = new Debugger({
web3,
offsetToLineColumnConverter: this.offsetToLineColumnConverter,
compilationResult: async (address) => {
try {
return await this.fetchContractAndCompile(address, currentReceipt)
} catch (e) {
console.error(e)
}
return null
},
debugWithGeneratedSources: false
})
return await debug.debugger.traceManager.getTrace(hash)
}
fetchContractAndCompile (address, receipt) {
const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address
const targetAddress = target || receipt.contractAddress || receipt.to
return this.call('fetchAndCompile', 'resolve', targetAddress, 'browser/.debug', this.blockchain.web3())
}
// debugger () {
// return this.debuggerUI
// }
} }
module.exports = DebuggerTab

@ -5,7 +5,7 @@ import { sourceMappingDecoder } from '@remix-project/remix-debug'
const profile = { const profile = {
name: 'offsetToLineColumnConverter', name: 'offsetToLineColumnConverter',
methods: [], methods: ['offsetToLineColumn'],
events: [], events: [],
version: packageJson.version version: packageJson.version
} }

@ -37,7 +37,7 @@ export class Debugger {
locationToRowConverter: async (sourceLocation) => { locationToRowConverter: async (sourceLocation) => {
const compilationResult = await this.compilationResult() const compilationResult = await this.compilationResult()
if (!compilationResult) return { start: null, end: null } if (!compilationResult) return { start: null, end: null }
return this.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, compilationResult.source.sources, compilationResult.data.sources) return await this.offsetToLineColumnConverter.offsetToLineColumn(sourceLocation, sourceLocation.file, compilationResult.source.sources, compilationResult.data.sources)
} }
}) })
@ -70,7 +70,7 @@ export class Debugger {
const compilationResultForAddress = await this.compilationResult(address) const compilationResultForAddress = await this.compilationResult(address)
if (!compilationResultForAddress) return if (!compilationResultForAddress) return
this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then((rawLocation) => { this.debugger.callTree.sourceLocationTracker.getValidSourceLocationFromVMTraceIndex(address, index, compilationResultForAddress.data.contracts).then(async (rawLocation) => {
if (compilationResultForAddress && compilationResultForAddress.data) { if (compilationResultForAddress && compilationResultForAddress.data) {
const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address) const generatedSources = this.debugger.callTree.sourceLocationTracker.getGeneratedSourcesFromAddress(address)
const astSources = Object.assign({}, compilationResultForAddress.data.sources) const astSources = Object.assign({}, compilationResultForAddress.data.sources)
@ -81,7 +81,7 @@ export class Debugger {
sources[genSource.name] = { content: genSource.contents } sources[genSource.name] = { content: genSource.contents }
} }
} }
var lineColumnPos = this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) var lineColumnPos = await this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources)
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address]) this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address])
} else { } else {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])

@ -1 +1,3 @@
export * from './lib/debugger-ui' export * from './lib/debugger-ui'
export * from './lib/idebugger-api'
export * from './lib/idebugger-api'

@ -1,64 +0,0 @@
import type { CompilationResult, CompilationSource } from '@remix-project/remix-solidity-ts'
export interface DebuggerUIProps {
debuggerAPI: DebuggerAPI
}
interface EditorEvent {
event: {
register(eventName: 'breakpointCleared' | 'breakpointAdded' | 'contentChanged',
callback: (fileName: string, row: string | number) => void)
}
}
interface LineColumnLocation {
start: {
line: number, column: number
},
end: {
line: number, column: number
}
}
interface RawLocation {
start: number, length: number
}
interface Sources {
[fileName: string] : {content: string}
}
interface CompilationOutput {
source: { sources: Sources, target: string }
data: CompilationResult
getSourceName: (id: number) => string
}
interface Asts {
[fileName: string] : CompilationSource // ast
}
interface TransactionReceipt {
blockHash: string
blockNumber: number
transactionHash: string
transactionIndex: number
from: string
to: string
contractAddress: string | null
}
export interface DebuggerAPI {
offsetToLineColumnConverter: { offsetToLineColumn: (sourceLocation: RawLocation, file: number, contents: Sources, asts: Asts) => LineColumnLocation }
debugHash: string
debugHashRequest: string
removeHighlights: boolean
editor: EditorEvent
discardHighlight: () => void
highlight: (lineColumnPos: LineColumnLocation, path: string) => void
fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => CompilationOutput
getFile: (path: string) => string
setFile: (path: string, content: string) => void
getDebugWeb3: () => any // returns an instance of web3.js
}

@ -4,7 +4,7 @@ import StepManager from './step-manager/step-manager'
import VmDebugger from './vm-debugger/vm-debugger' import VmDebugger from './vm-debugger/vm-debugger'
import VmDebuggerHead from './vm-debugger/vm-debugger-head' import VmDebuggerHead from './vm-debugger/vm-debugger-head'
import { TransactionDebugger as Debugger } from '@remix-project/remix-debug' import { TransactionDebugger as Debugger } from '@remix-project/remix-debug'
import { DebuggerUIProps } from './DebuggerAPI' import { DebuggerUIProps } from './idebugger-api'
import { Toaster } from '@remix-ui/toaster' import { Toaster } from '@remix-ui/toaster'
/* eslint-disable-next-line */ /* eslint-disable-next-line */
import './debugger-ui.css' import './debugger-ui.css'
@ -25,39 +25,35 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
opt: { opt: {
debugWithGeneratedSources: false debugWithGeneratedSources: false
}, },
toastMessage: '' toastMessage: '',
currentDebugTransaction: ''
}) })
useEffect(() => { useEffect(() => {
return unLoad() return unLoad()
}, []) }, [])
useEffect(() => { debuggerModule.onDebugRequested((hash) => {
if (debuggerModule.debugHash) { if (hash) debug(hash)
debug(debuggerModule.debugHash) })
}
}, [debuggerModule.debugHashRequest])
useEffect(() => { debuggerModule.onRemoveHighlights(async () => {
if (debuggerModule.removeHighlights) deleteHighlights() await debuggerModule.discardHighlight()
}, [debuggerModule.removeHighlights]) })
useEffect(() => { useEffect(() => {
const setEditor = () => { const setEditor = () => {
const editor = debuggerModule.editor
editor.event.register('breakpointCleared', (fileName, row) => { debuggerModule.onBreakpointCleared((fileName, row) => {
if (state.debugger) state.debugger.breakPointManager.remove({fileName: fileName, row: row}) if (state.debugger) state.debugger.breakPointManager.remove({fileName: fileName, row: row})
}) })
editor.event.register('breakpointAdded', (fileName, row) => { debuggerModule.onBreakpointAdded((fileName, row) => {
if (state.debugger) { if (state.debugger) state.debugger.breakPointManager.add({fileName: fileName, row: row})
state.debugger.breakPointManager.add({fileName: fileName, row: row})
}
}) })
editor.event.register('contentChanged', () => { debuggerModule.onEditorContentChanged(() => {
unLoad() if (state.debugger) unLoad()
}) })
} }
@ -117,10 +113,6 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
unLoad() unLoad()
} }
const isDebuggerActive = () => {
return state.isActive
}
const unLoad = () => { const unLoad = () => {
if (state.debugger) state.debugger.unload() if (state.debugger) state.debugger.unload()
setState(prevState => { setState(prevState => {
@ -138,13 +130,20 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
vmDebugger: false, vmDebugger: false,
vmDebuggerHead: false vmDebuggerHead: false
}, },
debugging: false debugging: false,
currentDebugTransaction: ''
} }
}) })
} }
const startDebugging = async (blockNumber, txNumber, tx) => { const startDebugging = async (blockNumber, txNumber, tx) => {
if (state.debugger) unLoad() if (state.debugger) unLoad()
if (!txNumber) return if (!txNumber) return
setState(prevState => {
return {
...prevState,
currentDebugTransaction: txNumber
}
})
const web3 = await debuggerModule.getDebugWeb3() const web3 = await debuggerModule.getDebugWeb3()
const currentReceipt = await web3.eth.getTransactionReceipt(txNumber) const currentReceipt = await web3.eth.getTransactionReceipt(txNumber)
const debuggerInstance = new Debugger({ const debuggerInstance = new Debugger({
@ -189,12 +188,6 @@ const debug = (txHash) => {
startDebugging(null, txHash, null) startDebugging(null, txHash, null)
} }
const deleteHighlights = async () => {
await debuggerModule.discardHighlight()
}
const stepManager = { const stepManager = {
jumpTo: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager) : null, jumpTo: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.jumpTo.bind(state.debugger.step_manager) : null,
stepOverBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverBack.bind(state.debugger.step_manager) : null, stepOverBack: state.debugger && state.debugger.step_manager ? state.debugger.step_manager.stepOverBack.bind(state.debugger.step_manager) : null,

@ -0,0 +1,66 @@
import type { CompilationResult, CompilationSource } from '@remix-project/remix-solidity-ts'
export interface DebuggerUIProps {
debuggerAPI: IDebuggerApi
}
export interface LineColumnLocation {
start: {
line: number, column: number
},
end: {
line: number, column: number
}
}
export interface RawLocation {
start: number, length: number
}
export interface Sources {
[fileName: string] : {content: string}
}
export interface CompilationOutput {
source: { sources: Sources, target: string }
data: CompilationResult
getSourceName: (id: number) => string
}
export interface Asts {
[fileName: string] : CompilationSource // ast
}
export interface TransactionReceipt {
blockHash: string
blockNumber: number
transactionHash: string
transactionIndex: number
from: string
to: string
contractAddress: string | null
}
export type onBreakpointClearedListener = (params: string, row: number) => void
export type onBreakpointAddedListener = (params: string, row: number) => void
export type onEditorContentChanged = () => void
export type onDebugRequested = (hash: string) => void
export interface IDebuggerApi {
offsetToLineColumnConverter: { offsetToLineColumn: (sourceLocation: RawLocation, file: number, contents: Sources, asts: Asts) => Promise<LineColumnLocation> }
debugHash: string
debugHashRequest: number
removeHighlights: boolean
onRemoveHighlights: (listener: VoidFunction) => void
onDebugRequested: (listener: onDebugRequested) => void
onBreakpointCleared: (listener: onBreakpointClearedListener) => void
onBreakpointAdded: (listener: onBreakpointAddedListener) => void
onEditorContentChanged: (listener: onEditorContentChanged) => void
discardHighlight: () => Promise<void>
highlight: (lineColumnPos: LineColumnLocation, path: string) => Promise<void>
fetchContractAndCompile: (address: string, currentReceipt: TransactionReceipt) => Promise<CompilationOutput>
getFile: (path: string) => Promise<string>
setFile: (path: string, content: string) => Promise<void>
getDebugWeb3: () => any // returns an instance of web3.js
}

@ -89,6 +89,9 @@
}, },
"remix-ui-file-explorer": { "remix-ui-file-explorer": {
"tags": [] "tags": []
},
"debugger": {
"tags": []
} }
} }
} }

805
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -159,7 +159,8 @@
"signale": "^1.4.0", "signale": "^1.4.0",
"time-stamp": "^2.2.0", "time-stamp": "^2.2.0",
"winston": "^3.3.3", "winston": "^3.3.3",
"ws": "^7.3.0" "ws": "^7.3.0",
"document-register-element": "1.13.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.5", "@babel/core": "^7.4.5",
@ -283,6 +284,7 @@
"worker-loader": "^2.0.0", "worker-loader": "^2.0.0",
"yo-yo": "github:ioedeveloper/yo-yo", "yo-yo": "github:ioedeveloper/yo-yo",
"yo-yoify": "^3.7.3", "yo-yoify": "^3.7.3",
"@types/jest": "25.1.4" "@types/jest": "25.1.4",
"@testing-library/react": "10.4.1"
} }
} }

@ -26,6 +26,7 @@
"@remix-project/remix-url-resolver": [ "@remix-project/remix-url-resolver": [
"dist/libs/remix-url-resolver/index.js" "dist/libs/remix-url-resolver/index.js"
], ],
"@remixproject/debugger-plugin": ["apps/debugger/src/index.ts"],
"@remix-project/remixd": ["dist/libs/remixd/index.js"], "@remix-project/remixd": ["dist/libs/remixd/index.js"],
"@remix-ui/tree-view": ["libs/remix-ui/tree-view/src/index.ts"], "@remix-ui/tree-view": ["libs/remix-ui/tree-view/src/index.ts"],
"@remix-ui/debugger-ui": ["libs/remix-ui/debugger-ui/src/index.ts"], "@remix-ui/debugger-ui": ["libs/remix-ui/debugger-ui/src/index.ts"],

@ -0,0 +1,65 @@
{
"rulesDirectory": ["node_modules/@nrwl/workspace/src/tslint"],
"linterOptions": {
"exclude": ["**/*"]
},
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"deprecation": {
"severity": "warn"
},
"forin": true,
"import-blacklist": [true, "rxjs/Rx"],
"interface-over-type-literal": true,
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [true, "debug", "info", "time", "timeEnd", "trace"],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [true, "ignore-params"],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"prefer-const": true,
"radix": true,
"triple-equals": [true, "allow-null-check"],
"unified-signatures": true,
"variable-name": false,
"nx-enforce-module-boundaries": [
true,
{
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{ "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }
]
}
]
}
}

@ -636,6 +636,75 @@
} }
} }
} }
},
"debugger": {
"root": "apps/debugger",
"sourceRoot": "apps/debugger/src",
"projectType": "application",
"schematics": {},
"architect": {
"build": {
"builder": "@nrwl/web:build",
"options": {
"outputPath": "dist/apps/debugger",
"index": "apps/debugger/src/index.html",
"main": "apps/debugger/src/main.tsx",
"polyfills": "apps/debugger/src/polyfills.ts",
"tsConfig": "apps/debugger/tsconfig.app.json",
"assets": [
"apps/debugger/src/assets",
"apps/debugger/src/index.html",
"apps/debugger/src/favicon.ico"
],
"styles": [],
"scripts": [],
"webpackConfig": "apps/debugger/webpack.config.js",
"maxWorkers": 2
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/debugger/src/environments/environment.ts",
"with": "apps/debugger/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "@nrwl/web:dev-server",
"options": {
"buildTarget": "debugger:build"
},
"configurations": {
"production": {
"buildTarget": "debugger:build:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["apps/debugger/tsconfig.app.json"],
"exclude": ["**/node_modules/**", "!apps/debugger/**/*"]
}
}
}
} }
}, },
"cli": { "cli": {

Loading…
Cancel
Save