Merge pull request #694 from ethereum/fixCompilationTargets

Fixes for the debugger
pull/713/head
yann300 4 years ago committed by GitHub
commit e3f2947ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      apps/remix-ide/src/app/compiler/compiler-helpers.js
  2. 7
      apps/remix-ide/src/app/compiler/compiler-imports.js
  3. 17
      apps/remix-ide/src/app/compiler/compiler-sourceVerifier-fetchAndCompile.js
  4. 4
      apps/remix-ide/src/app/tabs/debugger-tab.js
  5. 13
      libs/remix-debug/src/code/breakpointManager.ts
  6. 2
      libs/remix-debug/src/debugger/debugger.ts
  7. 48
      libs/remix-debug/src/solidity-decoder/internalCallTree.ts
  8. 2
      libs/remix-debug/src/trace/traceHelper.ts
  9. 8
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx

@ -3,10 +3,10 @@ import { canUseWorker, urlFromVersion } from './compiler-utils'
import { Compiler } from '@remix-project/remix-solidity' import { Compiler } from '@remix-project/remix-solidity'
import CompilerAbstract from './compiler-abstract' import CompilerAbstract from './compiler-abstract'
export const compile = async (compilationTargets, settings) => { export const compile = async (compilationTargets, settings, contentResolverCallback) => {
const res = await (() => { const res = await (() => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const compiler = new Compiler(() => {}) const compiler = new Compiler(contentResolverCallback)
compiler.set('evmVersion', settings.evmVersion) compiler.set('evmVersion', settings.evmVersion)
compiler.set('optimize', settings.optimize) compiler.set('optimize', settings.optimize)
compiler.set('language', settings.language) compiler.set('language', settings.language)

@ -12,7 +12,7 @@ const profile = {
name: 'contentImport', name: 'contentImport',
displayName: 'content import', displayName: 'content import',
version: packageJson.version, version: packageJson.version,
methods: ['resolve', 'resolveAndSave'] methods: ['resolve', 'resolveAndSave', 'isExternalUrl']
} }
module.exports = class CompilerImports extends Plugin { module.exports = class CompilerImports extends Plugin {
@ -30,6 +30,11 @@ module.exports = class CompilerImports extends Plugin {
return /^([^/]+)/.exec(url) return /^([^/]+)/.exec(url)
} }
isExternalUrl (url) {
const handlers = this.handlers()
return handlers.some(handler => handler.match.exec(url))
}
/** /**
* resolve the content of @arg url. This only resolves external URLs. * resolve the content of @arg url. This only resolves external URLs.
* *

@ -92,10 +92,14 @@ export default class FetchAndCompile extends Plugin {
if (url.includes('ipfs')) { if (url.includes('ipfs')) {
const stdUrl = `ipfs://${url.split('/')[2]}` const stdUrl = `ipfs://${url.split('/')[2]}`
const source = await this.call('contentImport', 'resolve', stdUrl) const source = await this.call('contentImport', 'resolve', stdUrl)
file = file.replace('browser/', '') // should be fixed in the remix IDE end. if (await this.call('contentImport', 'isExternalUrl', file)) {
const path = `${targetPath}/${name}/${contractAddress}/${file}` // nothing to do, the compiler callback will handle those
await this.call('fileManager', 'setFile', path, source.content) } else {
compilationTargets[path] = { content: source.content } file = file.replace('browser/', '') // should be fixed in the remix IDE end.
const path = `${targetPath}/${name}/${contractAddress}/${file}`
await this.call('fileManager', 'setFile', path, source.content)
compilationTargets[path] = { content: source.content }
}
break break
} }
} }
@ -111,7 +115,10 @@ export default class FetchAndCompile extends Plugin {
} }
try { try {
setTimeout(_ => this.emit('compiling', settings), 0) setTimeout(_ => this.emit('compiling', settings), 0)
const compData = await compile(compilationTargets, settings) const compData = await compile(
compilationTargets,
settings,
(url, cb) => this.call('contentImport', 'resolveAndSave', url).then((result) => cb(null, result)).catch((error) => cb(error.message)))
compilersartefacts.addResolvedContract(contractAddress, compData) compilersartefacts.addResolvedContract(contractAddress, compData)
return compData return compData
} catch (e) { } catch (e) {

@ -140,8 +140,8 @@ class DebuggerTab extends ViewPlugin {
fetchContractAndCompile (address, receipt) { fetchContractAndCompile (address, receipt) {
const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address const target = (address && remixDebug.traceHelper.isContractCreation(address)) ? receipt.contractAddress : address
const targetAddress = target || receipt.contractAddress || receipt.to
return this.call('fetchAndCompile', 'resolve', target || receipt.contractAddress || receipt.to, '.debug', this.blockchain.web3()) return this.call('fetchAndCompile', 'resolve', targetAddress, 'browser/.debug', this.blockchain.web3())
} }
// debugger () { // debugger () {

@ -15,8 +15,7 @@ export class BreakpointManager {
solidityProxy solidityProxy
breakpoints breakpoints
locationToRowConverter locationToRowConverter
previousLine
/** /**
* constructor * constructor
* *
@ -92,22 +91,26 @@ export class BreakpointManager {
let previousSourceLocation let previousSourceLocation
let currentStep = fromStep + direction let currentStep = fromStep + direction
let lineHadBreakpoint = false let lineHadBreakpoint = false
let initialLine
while (currentStep > 0 && currentStep < trace.length) { while (currentStep > 0 && currentStep < trace.length) {
try { try {
previousSourceLocation = sourceLocation previousSourceLocation = sourceLocation
sourceLocation = await this.callTree.extractValidSourceLocation(currentStep) sourceLocation = await this.callTree.extractValidSourceLocation(currentStep)
} catch (e) { } catch (e) {
return console.log('cannot jump to breakpoint ' + e) console.log('cannot jump to breakpoint ' + e)
currentStep += direction
continue
} }
const lineColumn = await this.locationToRowConverter(sourceLocation) const lineColumn = await this.locationToRowConverter(sourceLocation)
if (this.previousLine !== lineColumn.start.line) { if (!initialLine) initialLine = lineColumn
if (initialLine.start.line !== lineColumn.start.line) {
if (direction === -1 && lineHadBreakpoint) { // TODO : improve this when we will build the correct structure before hand if (direction === -1 && lineHadBreakpoint) { // TODO : improve this when we will build the correct structure before hand
lineHadBreakpoint = false lineHadBreakpoint = false
if (this.hitLine(currentStep + 1, previousSourceLocation, sourceLocation, trace)) { if (this.hitLine(currentStep + 1, previousSourceLocation, sourceLocation, trace)) {
return return
} }
} }
this.previousLine = lineColumn.start.line
if (this.hasBreakpointAtLine(sourceLocation.file, lineColumn.start.line)) { if (this.hasBreakpointAtLine(sourceLocation.file, lineColumn.start.line)) {
lineHadBreakpoint = true lineHadBreakpoint = true
if (direction === 1 && this.hitLine(currentStep, sourceLocation, previousSourceLocation, trace)) { if (direction === 1 && this.hitLine(currentStep, sourceLocation, previousSourceLocation, trace)) {

@ -82,7 +82,7 @@ export class Debugger {
} }
} }
var lineColumnPos = this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources) var lineColumnPos = this.offsetToLineColumnConverter.offsetToLineColumn(rawLocation, rawLocation.file, sources, astSources)
this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources]) this.event.trigger('newSourceLocation', [lineColumnPos, rawLocation, generatedSources, address])
} else { } else {
this.event.trigger('newSourceLocation', [null]) this.event.trigger('newSourceLocation', [null])
} }

@ -224,7 +224,7 @@ async function buildTree (tree, step, scopeId, isExternalCall, isCreation) {
// if not, we are in the current scope. // if not, we are in the current scope.
// We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step` // We check in `includeVariableDeclaration` if there is a new local variable in scope for this specific `step`
if (tree.includeLocalVariables) { if (tree.includeLocalVariables) {
includeVariableDeclaration(tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation) await includeVariableDeclaration(tree, step, sourceLocation, scopeId, newLocation, previousSourceLocation)
} }
previousSourceLocation = sourceLocation previousSourceLocation = sourceLocation
step++ step++
@ -249,32 +249,36 @@ async function includeVariableDeclaration (tree, step, sourceLocation, scopeId,
const contractObj = await tree.solidityProxy.contractObjectAt(step) const contractObj = await tree.solidityProxy.contractObjectAt(step)
let states = null let states = null
const generatedSources = getGeneratedSources(tree, scopeId, contractObj) const generatedSources = getGeneratedSources(tree, scopeId, contractObj)
const variableDeclarations = resolveVariableDeclaration(tree, sourceLocation, generatedSources)
const variableDeclaration = resolveVariableDeclaration(tree, sourceLocation, generatedSources)
// using the vm trace step, the current source location and the ast, // using the vm trace step, the current source location and the ast,
// we check if the current vm trace step target a new ast node of type VariableDeclaration // we check if the current vm trace step target a new ast node of type VariableDeclaration
// that way we know that there is a new local variable from here. // that way we know that there is a new local variable from here.
if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.name]) { if (variableDeclarations && variableDeclarations.length) {
try { for (const variableDeclaration of variableDeclarations) {
const stack = tree.traceManager.getStackAt(step) if (variableDeclaration && !tree.scopes[scopeId].locals[variableDeclaration.name]) {
// the stack length at this point is where the value of the new local variable will be stored. try {
// so, either this is the direct value, or the offset in memory. That depends on the type. const stack = tree.traceManager.getStackAt(step)
if (variableDeclaration.name !== '') { // the stack length at this point is where the value of the new local variable will be stored.
states = tree.solidityProxy.extractStatesDefinitions() // so, either this is the direct value, or the offset in memory. That depends on the type.
var location = extractLocationFromAstVariable(variableDeclaration) if (variableDeclaration.name !== '') {
location = location === 'default' ? 'storage' : location states = tree.solidityProxy.extractStatesDefinitions()
// we push the new local variable in our tree var location = extractLocationFromAstVariable(variableDeclaration)
tree.scopes[scopeId].locals[variableDeclaration.name] = { location = location === 'default' ? 'storage' : location
name: variableDeclaration.name, // we push the new local variable in our tree
type: parseType(variableDeclaration.typeDescriptions.typeString, states, contractObj.name, location), tree.scopes[scopeId].locals[variableDeclaration.name] = {
stackDepth: stack.length, name: variableDeclaration.name,
sourceLocation: sourceLocation type: parseType(variableDeclaration.typeDescriptions.typeString, states, contractObj.name, location),
stackDepth: stack.length,
sourceLocation: sourceLocation
}
}
} catch (error) {
console.log(error)
} }
} }
} catch (error) {
console.log(error)
} }
} }
// we check here if we are at the beginning inside a new function. // we check here if we are at the beginning inside a new function.
// if that is the case, we have to add to locals tree the inputs and output params // if that is the case, we have to add to locals tree the inputs and output params
const functionDefinition = resolveFunctionDefinition(tree, previousSourceLocation, generatedSources) const functionDefinition = resolveFunctionDefinition(tree, previousSourceLocation, generatedSources)
@ -351,8 +355,10 @@ function extractVariableDeclarations (ast, astWalker) {
const ret = {} const ret = {}
astWalker.walkFull(ast, (node) => { astWalker.walkFull(ast, (node) => {
if (node.nodeType === 'VariableDeclaration' || node.nodeType === 'YulVariableDeclaration') { if (node.nodeType === 'VariableDeclaration' || node.nodeType === 'YulVariableDeclaration') {
ret[node.src] = node ret[node.src] = [node]
} }
const hasChild = node.initialValue && (node.nodeType === 'VariableDeclarationStatement' || node.nodeType === 'YulVariableDeclarationStatement')
if (hasChild) ret[node.initialValue.src] = node.declarations
}) })
return ret return ret
} }

@ -15,7 +15,7 @@ export function resolveCalledAddress (vmTraceIndex, trace) {
} }
export function isCallInstruction (step) { export function isCallInstruction (step) {
return step.op === 'CALL' || step.op === 'CALLCODE' || step.op === 'CREATE' || step.op === 'DELEGATECALL' return ['CALL', 'STATICCALL', 'CALLCODE', 'CREATE', 'DELEGATECALL'].includes(step.op)
} }
export function isCreateInstruction (step) { export function isCreateInstruction (step) {

@ -74,12 +74,11 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
}) })
}) })
debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources) => { debuggerInstance.event.register('newSourceLocation', async (lineColumnPos, rawLocation, generatedSources, address) => {
if (!lineColumnPos) return if (!lineColumnPos) return
const contracts = await debuggerModule.fetchContractAndCompile( const contracts = await debuggerModule.fetchContractAndCompile(
currentReceipt.contractAddress || currentReceipt.to, address || currentReceipt.contractAddress || currentReceipt.to,
currentReceipt) currentReceipt)
if (contracts) { if (contracts) {
let path = contracts.getSourceName(rawLocation.file) let path = contracts.getSourceName(rawLocation.file)
if (!path) { if (!path) {
@ -153,7 +152,8 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter, offsetToLineColumnConverter: debuggerModule.offsetToLineColumnConverter,
compilationResult: async (address) => { compilationResult: async (address) => {
try { try {
return await debuggerModule.fetchContractAndCompile(address, currentReceipt) const ret = await debuggerModule.fetchContractAndCompile(address, currentReceipt)
return ret
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

Loading…
Cancel
Save