Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Commit

Permalink
Merge pull request #11 from huff-language/wsl-support
Browse files Browse the repository at this point in the history
Wsl support
  • Loading branch information
Maddiaa0 authored Jul 31, 2022
2 parents 91deefc + cc7fa02 commit c71bb27
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 56 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ Open up any .huff file and press `Load Interface` to prepare external functions,

The example above is for an erc721 mint function, entering from the function debugger will allow you to step through execution from the MAIN entrypoint. To select another function, click the drop down and select another function! The drop down will be populated with functions that are defined at the top of the file that you are currently debugging.

**Notice - *Relevant to WSL users***
To get the extension to run in wsl there is a workaround that you will have to make. Unfortunately, vscode extensions do not run in the same environment as your wsl shell so do not have access to the same path (even when mounted with wsl-remote). If the extension detects that you are working in a remote code space it will attempt to execute huffc and hevm within the wsl shell. The wsl api does not run the ~/.bashrc script (the file which huffup adds to the path), therefore to use this extension in wsl you will have to copy the following into your ~/.profile.
```
if [ -d "$HOME/.huff/bin" ]; then
PATH="$HOME/.huff/bin:$PATH"
fi
```
We will look to address this limitation in further releases of huffup.


### Macro Debugging
Macro debugging is the tool's most useful feature. Clicking `Load Macros` will load the macros of the currently open file.
If the macro has no `takes` arguments then the window will look like this:
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "huff-language",
"version": "0.0.22",
"version": "0.0.26",
"displayName": "Huff",
"preview": true,
"icon": "resources/huff.png",
Expand Down Expand Up @@ -45,7 +45,7 @@
"esbuild": "npm run esbuild-base -- --sourcemap",
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch"
},
"main": "./out/main.js",
"main": "./main/out.js",
"extensionKind": [
"ui",
"workspace"
Expand Down
113 changes: 91 additions & 22 deletions src/features/debugger/debuggerUtils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const vscode = require("vscode");
const commandExists = require("command-exists");
const fs = require("fs");
const {execSync, exec} = require("child_process");
const {execSync, spawnSync} = require("child_process");


/**Deploy Contract
Expand All @@ -24,16 +24,19 @@ function deployContract(
checkStateRepoExistence(config.statePath, cwd)
}

const command = `hevm exec --code ${bytecode} --address ${config.hevmContractAddress} --create --caller ${config.hevmCaller} --gas 0xffffffff ${(config.stateChecked || config.storageChecked) ? "--state " + cwd + "/" + config.statePath : ""}`
const isWsl = vscode.env.remoteName === "wsl";
const statePath = `${(isWsl) ? "/mnt/c" : ""}${cwd}/${config.statePath}`
const command = `hevm exec --code ${bytecode} --address ${config.hevmContractAddress} --create --caller ${config.hevmCaller} --gas 0xffffffff ${(config.stateChecked || config.storageChecked) ? "--state " + statePath : ""}`
console.log(command)

// cache command
writeHevmCommand(command, config.tempHevmCommandFilename, cwd);

// execute command
// const result = execSync("`cat " + cwd + "/" + config.tempHevmCommandFilename + "`", {cwd: cwd});
const hevmCommand = craftTerminalCommand(cwd, config.tempHevmCommandFilename);
try{
const result = execSync(command)
const result = executeCommand(cwd, command)
console.log(result)
return result
}catch (e) {
Expand Down Expand Up @@ -88,7 +91,6 @@ function checkStateRepoExistence(statePath, cwd) {
*/
function resetStateRepo(statePath, cwd) {
console.log("Creating state repository...")

const fullPath = cwd + "/" + statePath;

// delete old state
Expand Down Expand Up @@ -148,10 +150,7 @@ function compileFromFile(source, filename, cwd) {
bytecode = execSync(command, {cwd: cwd });
} catch (e) {
try {
bytecode = execSync(command, {
cwd: cwd,
env: {...process.env, PATH: `${process.env.PATH}:${process.env.HOME}/.huff/bin`}
});
bytecode = executeCommand(cwd, command);
} catch (e) {
console.log("huffc not found");
registerError(
Expand Down Expand Up @@ -198,16 +197,7 @@ function writeMacro(cwd, tempMacroFilename, macro) {
* throw error if not found
*/
async function checkHevmInstallation() {
try{
await commandExists("hevm");
return true;
} catch (e){
registerError(
e,
"Hevm installation required - install here: https://github.com/dapphub/dapptools#installation"
)
return false;
}
return await checkInstallation("hevm");
}

/**Check huff installation
Expand All @@ -217,18 +207,96 @@ async function checkHevmInstallation() {
* @returns
*/
async function checkHuffcInstallation() {
try{
await commandExists("huffc");
return checkInstallation("hevm")
}


/**Check Installation
*
* Generalise commandExist to support projects that are running in wsl
* @param {*} command
* @returns
*/
async function checkInstallation(command){
try {
// Depending on what enviornment the code is running in, check if a command is installed
if (vscode.env.remoteName === "wsl") {
// check installation using unix command executed in wsl
const exists = spawnSync("wsl", ["bash", "-l", "which", command], {
shell: true
});
// If the std out returns anything, then we can be confident that the exist command succeeded
if (exists.stdout.length > 1) return true;
}

// Fallback to use the commandExists package
await commandExists(command);
return true;
} catch (e){
} catch (e){
registerError(
e,
"Huffc compiler installation required - install here: https://github.com/huff-language/huff-rs"
`${command} installation required - install here: ${getInstallationLink(command)}`
)
return false;
}
}

function craftTerminalCommand(cwd, filename){
const isWsl = vscode.env.remoteName;
return "`cat " + ((isWsl) ? "/mnt/c" : "") + cwd + "/" + filename + "`";
}

/**Execute Command
*
* Exec subprocess respecting wsl
*
* @param {String} cwd
* @param {String} command
* @returns
*/
function executeCommand(cwd, command){
// Depending on what enviornment the code is running in, check if a command is installed
if (vscode.env.remoteName === "wsl") {
// check installation using unix command executed in wsl
console.log(`wsl bash -l -c "${command}"`)

const output = spawnSync('wsl bash -l -c "' + command + '"', {
shell: true,
cwd
});

// If the std out returns anything, then we can be confident that the exist command succeeded
if (output.stdout.length > 1) {
return output.stdout.toString()
}
}
const output = execSync(command, {
cwd: cwd,
env: {...process.env, PATH: `${process.env.PATH}:${process.env.HOME}/.huff/bin`}
});
return output.toString();
}

/**Get InstallationLink
*
* Provide a location to install files from if they are not found locally
* @param {String} command
* @returns {String} link
*/
function getInstallationLink(command){
switch (command){
case ("huffc") : {
return "https://github.com/huff-language/huff-rs";
}
case ("hevm"): {
return "https://github.com/dapphub/dapptools#installation";
}
default: {
return "Unsupported command supplied";
}
}
}

/**Check Installations
*
* Check for both hevm and huffc installations
Expand Down Expand Up @@ -277,6 +345,7 @@ module.exports = {
registerError,
compileFromFile,
checkInstallations,
craftTerminalCommand,
purgeCache,
formatEvenBytes
}
38 changes: 22 additions & 16 deletions src/features/debugger/function/debugger.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
const fs = require("fs");
const createKeccakHash = require('keccak');
const path = require("path");
const vscode = require("vscode");

// TODO: use a slimmer abicoder
const { AbiCoder } = require("@ethersproject/abi");
const { hevmConfig } = require("../../../options");
const { deployContract, runInUserTerminal, writeHevmCommand, resetStateRepo, registerError, compileFromFile, checkInstallations, purgeCache } = require("../debuggerUtils");
const { deployContract, runInUserTerminal, writeHevmCommand, resetStateRepo, registerError, compileFromFile, checkInstallations, purgeCache, craftTerminalCommand } = require("../debuggerUtils");


/**Start function debugger
*
* @param {String} cwd The current working directory of selected files workspace
* @param {String} sourceDirectory The current working directory of selected files workspace
* @param {String} currentFile The path to the currently selected file
* @param {String} functionSelector The 4byte function selector of the transaction being debugged
* @param {Array<Array<String>>} argsArray Each arg is provided in the format [type, value] so that they can easily be parsed with abi encoder
* @param {Object} options Options - not explicitly defined
*/
async function startDebugger(cwd, currentFile, imports, functionSelector, argsArray, options={state:true}){
async function startDebugger(sourceDirectory, currentFile, imports, functionSelector, argsArray, options={state:true}){
try {
if (!(await checkInstallations())) return;

// Remove /c: for wsl mounts / windows
const cwd = sourceDirectory.replace("/c:","").replace("/mnt/c", "")

// Create deterministic deployment address for each contract for the deployed contract
const config = {
...hevmConfig,
Expand Down Expand Up @@ -64,18 +68,20 @@ function flattenFile(cwd, currentFile, imports){

// Get absolute paths
const paths = imports.map(importPath => path.join(`${cwd}/${dirPath}`,importPath.replace(/#include\s?"/, "").replace('"', "")));
paths.push(cwd+ "/" + currentFile);

// Read file contents
const files = paths.map(path => {
console.log(path)
return fs.readFileSync(path)
.toString()
}
);

// Read file contents and remove other instances of main
// main regex
const mainRegex = /#define\s+macro\s+MAIN\s?\((?<args>[^\)]*)\)\s?=\s?takes\s?\((?<takes>[\d])\)\s?returns\s?\((?<returns>[\d])\)\s?{(?<body>[\s\S]*?(?=}))}/gsm
const files = [
fs.readFileSync(cwd+ "/" + currentFile).toString(),
...paths.map(path => {
return fs.readFileSync(path)
.toString().replace(mainRegex, "")
})
];

// Flatten and remove imports
return `${files.join("\n")}`.replace(/#include\s".*"/gsm, "");
return `${files.join("\n")}`.replace(/#include\s".*"/gm, "");
}


Expand All @@ -91,21 +97,21 @@ function flattenFile(cwd, currentFile, imports){
*/
function runDebugger(bytecode, calldata, flags, config, cwd) {
console.log("Entering debugger...")

const isWsl = vscode.env.remoteName === "wsl";

// Hevm Command
const hevmCommand = `hevm exec \
--code ${bytecode} \
--address ${config.hevmContractAddress} \
--caller ${config.hevmCaller} \
--gas 0xffffffff \
--state ${cwd + "/" + config.statePath} \
--state ${((isWsl) ? "/mnt/c" : "") + cwd + "/" + config.statePath} \
--debug \
${calldata ? "--calldata " + calldata : ""}`

// command is cached into a file as execSync has a limit on the command size that it can execute
writeHevmCommand(hevmCommand, config.tempHevmCommandFilename, cwd);
const terminalCommand = "`cat " + cwd + "/" + config.tempHevmCommandFilename + "`"
const terminalCommand = craftTerminalCommand(cwd, config.tempHevmCommandFilename)
runInUserTerminal(terminalCommand);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class DebuggerViewProvider{
switch (data.type) {
case "loadDocument":{
const functionSignatures = getFunctionSignaturesAndArgs(vscode.window.activeTextEditor?.document.getText());
console.log(functionSignatures.sighashes);
this.addOptionsToFunctionSelector(functionSignatures.sighashes);

break;
Expand All @@ -50,7 +49,6 @@ class DebuggerViewProvider{

const imports = getImports(vscode.window.activeTextEditor?.document.getText())

// TODO: get config from radio buttons
startDebugger(
vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri).uri.path,
vscode.workspace.asRelativePath(vscode.window.activeTextEditor.document.uri),
Expand Down
Loading

0 comments on commit c71bb27

Please sign in to comment.