diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9fa98..2dc431b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the "Simbolik VSCode" extension will be documented in this file. +## [4.1.0] - 2024-12-19 + +- The web version now offers experimental support for debugging transactions from BuildBear sandboxes. + ## [4.0.1] - 2024-11-11 - Show progress updates while starting a debugging session diff --git a/README.md b/README.md index 26e501a..06a6f89 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # Solidity Debugger for Visual Studio Code > [!IMPORTANT] -> This extension is currently in beta. If you encounter any issues, please report them on [GitHub](https://github.com/runtimeverification/simbolik-vscode/issues). -> You need to have a free API key from the [Simbolik Website](https://simbolik.runtimeverification.com) to use this extension, see below for more information. +> Please follow the instructions in our [Getting Started Guide](https://docs.runtimeverification.com/simbolik/overview/getting-started) to set up the extension correctly. > [!IMPORTANT] > The extension connects to the Simbolik API to provide debugging capabilities, and it transfers your compilation artifacts to our servers. @@ -13,20 +12,29 @@ Simbolik is a powerful extension that allows developers to debug their Solidity smart contracts directly within Visual Studio Code. With this extension, you can easily set breakpoints, inspect variables, step through code, and debug your Solidity contracts with ease. +Do you have questions, or need help? + +Visit our Documentation: https://docs.runtimeverification.com/simbolik +Join our Discord: https://discord.gg/jnvEeDxW +Join our TG group: https://t.me/rv_simbolik + ## Features - **Step-by-step debugging**: Debug your Solidity smart contracts line by line. +- **Variable inspection**: View the current values of variables while debugging. - **Inspect EVM state**: View the current state of the EVM while debugging. - **Breakpoints**: Set breakpoints in your Solidity code to pause execution and inspect the state. - **Bytecode debugging**: Debug the compiled bytecode of your Solidity contracts. ## Coming Soon -- **Variable inspection**: View the current values of variables while debugging. - **Foundry Cheatcodes**: Use Foundry's cheatcodes to quickly find bugs in your Solidity code. - **Symbolic Execution**: Enter an advanced symbolic execution mode to explore all possible paths through your Solidity code. You can get a sneak-peek of this feature at [try.simbolik.runtimeverification.com](try.simbolik.runtimeverification.com). -## Requirements +## Getting started + +For detailed instructions and troubleshooting tips, please refer to our [Getting Started Guide](https://docs.runtimeverification.com/simbolik/overview/getting-started). +Here is the quick version: To use the Solidity Debugger for Visual Studio Code, you need Foundry installed on your machine. Furthermore, you need a free API key from the Simbolik Website. diff --git a/package-lock.json b/package-lock.json index 715271c..3ad2543 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "simbolik", - "version": "4.0.1", + "version": "4.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "simbolik", - "version": "4.0.1", + "version": "4.1.0", "dependencies": { "@solidity-parser/parser": "^0.18.0", "@types/ws": "^8.5.10", "smol-toml": "^1.2.0", + "vscode-uri": "^3.0.8", "ws": "^8.16.0" }, "devDependencies": { @@ -520,9 +521,9 @@ "dev": true }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "peer": true, "dependencies": { @@ -693,9 +694,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", - "integrity": "sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==", + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", "dev": true }, "node_modules/@types/node": { @@ -719,9 +720,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.95.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.95.0.tgz", - "integrity": "sha512-0LBD8TEiNbet3NvWsmn59zLzOFu/txSlGxnv5yAFHCrhG9WvAnR3IvfHzMOs2aeWqgvNjq9pO99IUw8d3n+unw==", + "version": "1.96.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.96.0.tgz", + "integrity": "sha512-qvZbSZo+K4ZYmmDuaodMbAa67Pl6VDQzLKFka6rq+3WUTY4Kro7Bwoi0CuZLO/wema0ygcmpwow7zZfPJTs5jg==", "dev": true }, "node_modules/@types/ws": { @@ -921,9 +922,9 @@ } }, "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz", + "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==", "dev": true }, "node_modules/@vscode/test-electron": { @@ -1139,13 +1140,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -1166,6 +1164,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -1365,9 +1405,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -1385,9 +1425,9 @@ ], "peer": true, "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -1473,9 +1513,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001690", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", + "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", "dev": true, "funding": [ { @@ -1643,9 +1683,9 @@ "dev": true }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", @@ -1657,9 +1697,9 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { "ms": "^2.1.3" @@ -1753,9 +1793,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.55", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.55.tgz", - "integrity": "sha512-6maZ2ASDOTBtjt9FhqYPRnbvKU5tjG0IN9SztUOWYw2AzNDNpKJYLJmlK0/En4Hs/aiWnB+JZ+gW19PIGszgKg==", + "version": "1.5.74", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.74.tgz", + "integrity": "sha512-ck3//9RC+6oss/1Bh9tiAVFy5vfSKbRHAFh7Z3/eTRkEqJeWgymloShB17Vg3Z4nmDNp35vAd1BZ6CMW4Wt6Iw==", "dev": true, "peer": true }, @@ -2272,6 +2312,13 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", + "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", + "dev": true, + "peer": true + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -2369,9 +2416,9 @@ } }, "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true }, "node_modules/fs.realpath": { @@ -2733,12 +2780,12 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -2895,9 +2942,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", "dev": true, "dependencies": { "hasown": "^2.0.2" @@ -3479,9 +3526,9 @@ "peer": true }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "peer": true }, @@ -3598,9 +3645,9 @@ } }, "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.0.tgz", + "integrity": "sha512-ZkD35Mx92acjB2yNJgziGqT9oKHEOxjTBTDRpOsRWtdecL/0jM3z5kM/CTzHWvHIen1GvkM85p6TuFfDGfc8/Q==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -4138,13 +4185,23 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.9", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", + "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4363,9 +4420,9 @@ } }, "node_modules/smol-toml": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.0.tgz", - "integrity": "sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.1.tgz", + "integrity": "sha512-tEYNll18pPKHroYSmLLrksq233j021G0giwW7P3D24jC54pQ5W5BXMsQ/Mvw1OJCmEYDgY+lrzT+3nNUtoNfXQ==", "engines": { "node": ">= 18" }, @@ -4574,9 +4631,9 @@ } }, "node_modules/terser": { - "version": "5.36.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", - "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", + "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, "peer": true, "dependencies": { @@ -4593,17 +4650,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.11", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", + "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -4627,6 +4684,63 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4738,9 +4852,9 @@ } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -4811,6 +4925,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, "node_modules/watchpack": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", @@ -4826,17 +4945,17 @@ } }, "node_modules/webpack": { - "version": "5.96.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", - "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", diff --git a/package.json b/package.json index fa1b6ea..9893a7a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "publisher": "runtimeverification", "description": "Advanced Solidity and EVM Debugger", - "version": "4.0.1", + "version": "4.1.0", "engines": { "vscode": "^1.79.0" }, @@ -23,7 +23,8 @@ ], "activationEvents": [ "onDebug", - "onLanguage:solidity" + "onLanguage:solidity", + "onFileSystem:buildbear" ], "main": "./build/extension.js", "browser": "./build/extension.web.js", @@ -70,7 +71,7 @@ "simbolik.incremental-build": { "type": "boolean", "default": false, - "description": "If autobuild is eanbled and incremental-build is set to true, the debugger will use incremental builds. Notice, that the support for incremental builds is experimental and sometimes leads to unexpected behavior.", + "description": "If autobuild is enabled and incremental-build is set to true, the debugger will use incremental builds. Notice, that the support for incremental builds is experimental and sometimes leads to unexpected behavior.", "order": 4 }, "simbolik.stop-at-first-opcode": { @@ -204,6 +205,17 @@ } } } + ], + "resourceLabelFormatters": [ + { + "authority": "*", + "scheme": "buildbear", + "formatting": { + "workspaceSuffix": "Simbolik", + "label": "${scheme}://${authority}${path}", + "separator": "/" + } + } ] }, "scripts": { @@ -242,6 +254,7 @@ "@solidity-parser/parser": "^0.18.0", "@types/ws": "^8.5.10", "smol-toml": "^1.2.0", - "ws": "^8.16.0" + "ws": "^8.16.0", + "vscode-uri": "^3.0.8" } } diff --git a/src/extension.web.ts b/src/extension.web.ts index 8a23374..18c00c0 100644 --- a/src/extension.web.ts +++ b/src/extension.web.ts @@ -6,6 +6,8 @@ import {SolidityDebugAdapterDescriptorFactory} from './DebugAdapter.web'; import {startDebugging} from './startDebugging'; import {KastProvider, viewKast} from './KastProvider'; import {getConfigValue} from './utils'; +import { Uri, FileStat, FileType, FileSystemError } from 'vscode'; +import { Entry, MemFileSystemProvider, File, Directory } from './fsProvider'; console.log("Hello from Simbolik!"); @@ -20,38 +22,38 @@ export function activate(context: vscode.ExtensionContext) { // Use the console to output diagnostic information (console.log) and errors (console.error) // This line of code will only be executed once when your extension is activated console.log('Congratulations, your extension "simbolik" is now active!'); - + let disposable: vscode.Disposable; - + const codelensProvider = new CodelensProvider(); disposable = vscode.languages.registerCodeLensProvider( 'solidity', codelensProvider ); context.subscriptions.push(disposable); - + const factory = new SolidityDebugAdapterDescriptorFactory(); disposable = vscode.debug.registerDebugAdapterDescriptorFactory( 'solidity', factory ); context.subscriptions.push(disposable); - + disposable = vscode.commands.registerCommand( 'simbolik.startDebugging', (contract, method) => startDebugging(contract, method), ); context.subscriptions.push(disposable); - + disposable = vscode.commands.registerCommand('simbolik.viewKast', viewKast); context.subscriptions.push(disposable); - + const kastProvider = new KastProvider(); disposable = vscode.workspace.registerTextDocumentContentProvider( KastProvider.scheme, kastProvider ); - + vscode.debug.onDidStartDebugSession(session => { outputChannel.info(`Debug session started: ${session.id}`); if (session.type === 'solidity') { @@ -60,11 +62,84 @@ export function activate(context: vscode.ExtensionContext) { } } }); - + vscode.debug.onDidTerminateDebugSession(session => { outputChannel.info(`Debug session ended: ${session.id}`); }); + + const emptyDirectory = new EmptyDirectory(''); + const memFsProvider = new MemFileSystemProvider('buildbear', emptyDirectory, context.extensionUri); + disposable = vscode.workspace.registerFileSystemProvider('buildbear', memFsProvider); + context.subscriptions.push(disposable); + + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (workspaceFolder) { + const path = workspaceFolder.uri.path; + const sandboxName = workspaceFolder.uri.authority; + const pattern = '/chain/{chainId}/tx/{txHash}'; + const match = matchUri(pattern, path); + if (match) { + const chainId = match.chainId; + const txHash = match.txHash; + const debugConfig = { + "name": "Debug Tx", + "type": "solidity", + "request": "attach", + "txHash": txHash, + "jsonRpcUrl": `https://rpc.buildbear.io/${sandboxName}`, + "sourcifyUrl": `https://rpc.buildbear.io/verify/sourcify/server/${sandboxName}`, + "stopAtFirstOpcode": true, + "chainId": chainId, + } + vscode.debug.startDebugging( + workspaceFolder, + debugConfig, + ); + } + } + } // This method is called when your extension is deactivated export function deactivate() {} + + +/** +* Math a URI path against a pattern. +* For example, if the pattern is /build-bear/{sandbox_name}/{tx_hash} +* and the URI is /build-bear/lorem-ipsum/0xabcdef1234567890 +* then the result should be {sandbox_name: 'lorem-ipsum', tx_hash: '0xabcdef1234567890'} +* +* Returns null if the URI does not match the pattern. +*/ +function matchUri(pattern: string, path: string): { [key: string]: string } | null { + const patternParts = pattern.split('/'); + const uriParts = path.split('/'); + if (patternParts.length !== uriParts.length) { + return null; + } + const result: { [key: string]: string } = {}; + for (let i = 0; i < patternParts.length; i++) { + const patternPart = patternParts[i]; + const uriPart = uriParts[i]; + if (patternPart.startsWith('{') && patternPart.endsWith('}')) { + const key = patternPart.slice(1, -1); + result[key] = uriPart; + } else if (patternPart !== uriPart) { + return null; + } + } + return result; +} + +class EmptyDirectory implements Directory { + readonly type = FileType.Directory; + constructor(public name: string) { + } + get stats(): Promise { + return Promise.resolve({ type: FileType.Directory, ctime: Date.now(), mtime: Date.now(), size: 0 }); + } + get entries(): Promise> { + return Promise.resolve(new Map()); + } +} \ No newline at end of file diff --git a/src/fsProvider.ts b/src/fsProvider.ts new file mode 100644 index 0000000..b721e86 --- /dev/null +++ b/src/fsProvider.ts @@ -0,0 +1,227 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EventEmitter, Event, Uri, FileSystemProvider, Disposable, FileType, FileStat, FileSystemError, FileChangeType, FileChangeEvent, CancellationToken, ProviderResult, } from 'vscode'; +import { Utils } from 'vscode-uri'; +import { Minimatch } from 'minimatch'; + +export interface File { + readonly type: FileType.File; + name: string; + stats: Promise; + content: Promise; +} + +export interface Directory { + readonly type: FileType.Directory; + name: string; + stats: Promise; + entries: Promise>; +} + +export type Entry = File | Directory; + + +function newFileStat(type: FileType, size: number): Promise { + return Promise.resolve({ type, ctime: Date.now(), mtime: Date.now(), size }); +} + +function modifiedFileStat(stats: FileStat, size?: number): Promise { + return Promise.resolve({ type: stats.type, ctime: stats.ctime, mtime: Date.now(), size: size ?? stats.size }); +} + +export class MemFileSystemProvider implements FileSystemProvider { + constructor(private readonly scheme: string, private readonly root: Directory, private readonly extensionUri: Uri) { + } + + // --- manage file metadata + + async stat(resource: Uri): Promise { + const entry = await this._lookup(resource, false); + return entry.stats; + } + + async readDirectory(resource: Uri): Promise<[string, FileType][]> { + const entry = await this._lookupAsDirectory(resource, false); + const entries = await entry.entries; + const result: [string, FileType][] = []; + entries.forEach((child, name) => result.push([name, child.type])); + return result; + } + + // --- manage file contents + + async readFile(resource: Uri): Promise { + const entry = await this._lookupAsFile(resource, false); + return entry.content; + } + + async writeFile(uri: Uri, content: Uint8Array, opts: { create: boolean; overwrite: boolean; }): Promise { + const basename = Utils.basename(uri); + const parent = await this._lookupParentDirectory(uri); + const entries = await parent.entries; + let entry = entries.get(basename); + if (entry && entry.type === FileType.Directory) { + throw FileSystemError.FileIsADirectory(uri); + } + if (!entry && !opts.create) { + throw FileSystemError.FileNotFound(uri); + } + if (entry && opts.create && !opts.overwrite) { + throw FileSystemError.FileExists(uri); + } + const stats = newFileStat(FileType.File, content.byteLength); + if (!entry) { + entry = { type: FileType.File, name: basename, stats, content: Promise.resolve(content) }; + entries.set(basename, entry); + this._fireSoon({ type: FileChangeType.Created, uri }); + } else { + entry.stats = stats; + entry.content = Promise.resolve(content); + } + this._fireSoon({ type: FileChangeType.Changed, uri }); + } + + // --- manage files/folders + + async rename(from: Uri, to: Uri, opts: { overwrite: boolean; }): Promise { + if (!opts.overwrite && await this._lookup(to, true)) { + throw FileSystemError.FileExists(to); + } + + const entry = await this._lookup(from, false); + const oldParent = await this._lookupParentDirectory(from); + + const newParent = await this._lookupParentDirectory(to); + const newName = Utils.basename(to); + + const oldParentEntries = await oldParent.entries; + + oldParentEntries.delete(entry.name); + + entry.name = newName; + + const newParentEntries = await newParent.entries; + newParentEntries.set(newName, entry); + + this._fireSoon( + { type: FileChangeType.Deleted, uri: from }, + { type: FileChangeType.Created, uri: to } + ); + } + + async delete(uri: Uri, opts: { recursive: boolean; }): Promise { + const dirname = Utils.dirname(uri); + const basename = Utils.basename(uri); + const parent = await this._lookupAsDirectory(dirname, false); + const parentEntries = await parent.entries; + if (parentEntries.has(basename)) { + parentEntries.delete(basename); + parent.stats = newFileStat(parent.type, -1); + this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { uri, type: FileChangeType.Deleted }); + } + } + + async createDirectory(uri: Uri): Promise { + const basename = Utils.basename(uri); + const dirname = Utils.dirname(uri); + const parent = await this._lookupAsDirectory(dirname, false); + const parentEntries = await parent.entries; + + const entry: Directory = { type: FileType.Directory, name: basename, stats: newFileStat(FileType.Directory, 0), entries: Promise.resolve(new Map()) }; + parentEntries.set(entry.name, entry); + const stats = await parent.stats; + parent.stats = modifiedFileStat(stats, stats.size + 1); + this._fireSoon({ type: FileChangeType.Changed, uri: dirname }, { type: FileChangeType.Created, uri }); + } + + // --- lookup + + private async _lookup(uri: Uri, silent: false): Promise; + private async _lookup(uri: Uri, silent: boolean): Promise; + private async _lookup(uri: Uri, silent: boolean): Promise { + if (uri.scheme !== this.scheme) { + if (!silent) { + throw FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + let entry: Entry | undefined = this.root; + const parts = uri.path.split('/'); + for (const part of parts) { + if (!part) { + continue; + } + let child: Entry | undefined; + if (entry.type === FileType.Directory) { + child = (await entry.entries).get(part); + } + if (!child) { + if (!silent) { + throw FileSystemError.FileNotFound(uri); + } else { + return undefined; + } + } + entry = child; + } + return entry; + } + + private async _lookupAsDirectory(uri: Uri, silent: boolean): Promise { + const entry = await this._lookup(uri, silent); + if (entry?.type === FileType.Directory) { + return entry; + } + throw FileSystemError.FileNotADirectory(uri); + } + + private async _lookupAsFile(uri: Uri, silent: boolean): Promise { + const entry = await this._lookup(uri, silent); + if (!entry) { + throw FileSystemError.FileNotFound(uri); + } + if (entry.type === FileType.File) { + return entry; + } + throw FileSystemError.FileIsADirectory(uri); + } + + private _lookupParentDirectory(uri: Uri): Promise { + const dirname = Utils.dirname(uri); + return this._lookupAsDirectory(dirname, false); + } + + // --- manage file events + + private readonly _onDidChangeFile = new EventEmitter(); + readonly onDidChangeFile: Event = this._onDidChangeFile.event; + + private _bufferedChanges: FileChangeEvent[] = []; + private _fireSoonHandle?: NodeJS.Timeout; + + watch(resource: Uri, opts: { recursive: boolean; excludes: string[]; }): Disposable { + // ignore, fires for all changes... + return Disposable.from(); + } + + private _fireSoon(...changes: FileChangeEvent[]): void { + this._bufferedChanges.push(...changes); + + if (this._fireSoonHandle) { + clearTimeout(this._fireSoonHandle); + } + + this._fireSoonHandle = setTimeout(() => { + this._onDidChangeFile.fire(this._bufferedChanges); + this._bufferedChanges.length = 0; + }, 5); + } + + dispose() { + this._onDidChangeFile.dispose(); + } +} \ No newline at end of file