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

Commit c71bb27

Browse files
authored
Merge pull request #11 from huff-language/wsl-support
Wsl support
2 parents 91deefc + cc7fa02 commit c71bb27

File tree

6 files changed

+146
-56
lines changed

6 files changed

+146
-56
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ Open up any .huff file and press `Load Interface` to prepare external functions,
1919

2020
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.
2121

22+
**Notice - *Relevant to WSL users***
23+
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.
24+
```
25+
if [ -d "$HOME/.huff/bin" ]; then
26+
PATH="$HOME/.huff/bin:$PATH"
27+
fi
28+
```
29+
We will look to address this limitation in further releases of huffup.
30+
31+
2232
### Macro Debugging
2333
Macro debugging is the tool's most useful feature. Clicking `Load Macros` will load the macros of the currently open file.
2434
If the macro has no `takes` arguments then the window will look like this:

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "huff-language",
3-
"version": "0.0.22",
3+
"version": "0.0.26",
44
"displayName": "Huff",
55
"preview": true,
66
"icon": "resources/huff.png",
@@ -45,7 +45,7 @@
4545
"esbuild": "npm run esbuild-base -- --sourcemap",
4646
"esbuild-watch": "npm run esbuild-base -- --sourcemap --watch"
4747
},
48-
"main": "./out/main.js",
48+
"main": "./main/out.js",
4949
"extensionKind": [
5050
"ui",
5151
"workspace"

src/features/debugger/debuggerUtils.js

Lines changed: 91 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const vscode = require("vscode");
22
const commandExists = require("command-exists");
33
const fs = require("fs");
4-
const {execSync, exec} = require("child_process");
4+
const {execSync, spawnSync} = require("child_process");
55

66

77
/**Deploy Contract
@@ -24,16 +24,19 @@ function deployContract(
2424
checkStateRepoExistence(config.statePath, cwd)
2525
}
2626

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

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

3335
// execute command
3436
// const result = execSync("`cat " + cwd + "/" + config.tempHevmCommandFilename + "`", {cwd: cwd});
37+
const hevmCommand = craftTerminalCommand(cwd, config.tempHevmCommandFilename);
3538
try{
36-
const result = execSync(command)
39+
const result = executeCommand(cwd, command)
3740
console.log(result)
3841
return result
3942
}catch (e) {
@@ -88,7 +91,6 @@ function checkStateRepoExistence(statePath, cwd) {
8891
*/
8992
function resetStateRepo(statePath, cwd) {
9093
console.log("Creating state repository...")
91-
9294
const fullPath = cwd + "/" + statePath;
9395

9496
// delete old state
@@ -148,10 +150,7 @@ function compileFromFile(source, filename, cwd) {
148150
bytecode = execSync(command, {cwd: cwd });
149151
} catch (e) {
150152
try {
151-
bytecode = execSync(command, {
152-
cwd: cwd,
153-
env: {...process.env, PATH: `${process.env.PATH}:${process.env.HOME}/.huff/bin`}
154-
});
153+
bytecode = executeCommand(cwd, command);
155154
} catch (e) {
156155
console.log("huffc not found");
157156
registerError(
@@ -198,16 +197,7 @@ function writeMacro(cwd, tempMacroFilename, macro) {
198197
* throw error if not found
199198
*/
200199
async function checkHevmInstallation() {
201-
try{
202-
await commandExists("hevm");
203-
return true;
204-
} catch (e){
205-
registerError(
206-
e,
207-
"Hevm installation required - install here: https://github.com/dapphub/dapptools#installation"
208-
)
209-
return false;
210-
}
200+
return await checkInstallation("hevm");
211201
}
212202

213203
/**Check huff installation
@@ -217,18 +207,96 @@ async function checkHevmInstallation() {
217207
* @returns
218208
*/
219209
async function checkHuffcInstallation() {
220-
try{
221-
await commandExists("huffc");
210+
return checkInstallation("hevm")
211+
}
212+
213+
214+
/**Check Installation
215+
*
216+
* Generalise commandExist to support projects that are running in wsl
217+
* @param {*} command
218+
* @returns
219+
*/
220+
async function checkInstallation(command){
221+
try {
222+
// Depending on what enviornment the code is running in, check if a command is installed
223+
if (vscode.env.remoteName === "wsl") {
224+
// check installation using unix command executed in wsl
225+
const exists = spawnSync("wsl", ["bash", "-l", "which", command], {
226+
shell: true
227+
});
228+
// If the std out returns anything, then we can be confident that the exist command succeeded
229+
if (exists.stdout.length > 1) return true;
230+
}
231+
232+
// Fallback to use the commandExists package
233+
await commandExists(command);
222234
return true;
223-
} catch (e){
235+
} catch (e){
224236
registerError(
225237
e,
226-
"Huffc compiler installation required - install here: https://github.com/huff-language/huff-rs"
238+
`${command} installation required - install here: ${getInstallationLink(command)}`
227239
)
228240
return false;
229241
}
230242
}
231243

244+
function craftTerminalCommand(cwd, filename){
245+
const isWsl = vscode.env.remoteName;
246+
return "`cat " + ((isWsl) ? "/mnt/c" : "") + cwd + "/" + filename + "`";
247+
}
248+
249+
/**Execute Command
250+
*
251+
* Exec subprocess respecting wsl
252+
*
253+
* @param {String} cwd
254+
* @param {String} command
255+
* @returns
256+
*/
257+
function executeCommand(cwd, command){
258+
// Depending on what enviornment the code is running in, check if a command is installed
259+
if (vscode.env.remoteName === "wsl") {
260+
// check installation using unix command executed in wsl
261+
console.log(`wsl bash -l -c "${command}"`)
262+
263+
const output = spawnSync('wsl bash -l -c "' + command + '"', {
264+
shell: true,
265+
cwd
266+
});
267+
268+
// If the std out returns anything, then we can be confident that the exist command succeeded
269+
if (output.stdout.length > 1) {
270+
return output.stdout.toString()
271+
}
272+
}
273+
const output = execSync(command, {
274+
cwd: cwd,
275+
env: {...process.env, PATH: `${process.env.PATH}:${process.env.HOME}/.huff/bin`}
276+
});
277+
return output.toString();
278+
}
279+
280+
/**Get InstallationLink
281+
*
282+
* Provide a location to install files from if they are not found locally
283+
* @param {String} command
284+
* @returns {String} link
285+
*/
286+
function getInstallationLink(command){
287+
switch (command){
288+
case ("huffc") : {
289+
return "https://github.com/huff-language/huff-rs";
290+
}
291+
case ("hevm"): {
292+
return "https://github.com/dapphub/dapptools#installation";
293+
}
294+
default: {
295+
return "Unsupported command supplied";
296+
}
297+
}
298+
}
299+
232300
/**Check Installations
233301
*
234302
* Check for both hevm and huffc installations
@@ -277,6 +345,7 @@ module.exports = {
277345
registerError,
278346
compileFromFile,
279347
checkInstallations,
348+
craftTerminalCommand,
280349
purgeCache,
281350
formatEvenBytes
282351
}

src/features/debugger/function/debugger.js

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
const fs = require("fs");
22
const createKeccakHash = require('keccak');
33
const path = require("path");
4+
const vscode = require("vscode");
45

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

1011

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

24+
// Remove /c: for wsl mounts / windows
25+
const cwd = sourceDirectory.replace("/c:","").replace("/mnt/c", "")
26+
2327
// Create deterministic deployment address for each contract for the deployed contract
2428
const config = {
2529
...hevmConfig,
@@ -64,18 +68,20 @@ function flattenFile(cwd, currentFile, imports){
6468

6569
// Get absolute paths
6670
const paths = imports.map(importPath => path.join(`${cwd}/${dirPath}`,importPath.replace(/#include\s?"/, "").replace('"', "")));
67-
paths.push(cwd+ "/" + currentFile);
68-
69-
// Read file contents
70-
const files = paths.map(path => {
71-
console.log(path)
72-
return fs.readFileSync(path)
73-
.toString()
74-
}
75-
);
71+
72+
// Read file contents and remove other instances of main
73+
// main regex
74+
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
75+
const files = [
76+
fs.readFileSync(cwd+ "/" + currentFile).toString(),
77+
...paths.map(path => {
78+
return fs.readFileSync(path)
79+
.toString().replace(mainRegex, "")
80+
})
81+
];
7682

7783
// Flatten and remove imports
78-
return `${files.join("\n")}`.replace(/#include\s".*"/gsm, "");
84+
return `${files.join("\n")}`.replace(/#include\s".*"/gm, "");
7985
}
8086

8187

@@ -91,21 +97,21 @@ function flattenFile(cwd, currentFile, imports){
9197
*/
9298
function runDebugger(bytecode, calldata, flags, config, cwd) {
9399
console.log("Entering debugger...")
94-
100+
const isWsl = vscode.env.remoteName === "wsl";
95101

96102
// Hevm Command
97103
const hevmCommand = `hevm exec \
98104
--code ${bytecode} \
99105
--address ${config.hevmContractAddress} \
100106
--caller ${config.hevmCaller} \
101107
--gas 0xffffffff \
102-
--state ${cwd + "/" + config.statePath} \
108+
--state ${((isWsl) ? "/mnt/c" : "") + cwd + "/" + config.statePath} \
103109
--debug \
104110
${calldata ? "--calldata " + calldata : ""}`
105111

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

src/features/debugger/function/functionDebuggerViewProvider.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ class DebuggerViewProvider{
4040
switch (data.type) {
4141
case "loadDocument":{
4242
const functionSignatures = getFunctionSignaturesAndArgs(vscode.window.activeTextEditor?.document.getText());
43-
console.log(functionSignatures.sighashes);
4443
this.addOptionsToFunctionSelector(functionSignatures.sighashes);
4544

4645
break;
@@ -50,7 +49,6 @@ class DebuggerViewProvider{
5049

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

53-
// TODO: get config from radio buttons
5452
startDebugger(
5553
vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri).uri.path,
5654
vscode.workspace.asRelativePath(vscode.window.activeTextEditor.document.uri),

0 commit comments

Comments
 (0)