From a4375436f0f1229a4ed45a756d044754aa139c88 Mon Sep 17 00:00:00 2001 From: Anton Trunov Date: Fri, 14 Jun 2024 11:36:44 +0400 Subject: [PATCH] feat/fix: do not decompile binary code by default (#417) Decompilation of BoC files now happens at the end of the compilation pipeline only if it's explicitly configured --- .github/workflows/tact.yml | 18 +++++++++++ CHANGELOG.md | 2 ++ bin/tact | 29 ++++++++++++----- .../success.config.with.decompilation.json | 11 +++++++ cspell.json | 4 +++ schemas/configSchema.json | 4 +-- src/config/parseConfig.ts | 4 ++- src/pipeline/build.ts | 31 +++++++++---------- 8 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 bin/test/success.config.with.decompilation.json diff --git a/.github/workflows/tact.yml b/.github/workflows/tact.yml index 136e970f0..f5bf91951 100644 --- a/.github/workflows/tact.yml +++ b/.github/workflows/tact.yml @@ -77,14 +77,32 @@ jobs: tact --check bin/test/success.tact tact --func bin/test/success.tact tact bin/test/success.tact + tact --with-decompilation bin/test/success.tact - name: CLI Test | Check compilation via `--config` run: | # should output complete results tact --config bin/test/success.config.json + # should output complete result + decompile binary code + tact --config bin/test/success.config.with.decompilation.json # should only run the syntax and type checking tact --config bin/test/success.config.json --check + - name: CLI Test | Check parsing of mutually exclusive flags - 1 + if: runner.os != 'Windows' + run: | + ! tact --func --check bin/test/success.config.json + + - name: CLI Test | Check parsing of mutually exclusive flags - 2 + if: runner.os != 'Windows' + run: | + ! tact --with-decompilation --check bin/test/success.config.json + + - name: CLI Test | Check parsing of mutually exclusive flags - 3 + if: runner.os != 'Windows' + run: | + ! tact --func --with-decompilation bin/test/success.config.json + - name: CLI Test | Check parsing of a non-existing CLI flag if: runner.os != 'Windows' run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 5001a245d..71e8b58c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Augmented assignment bitwise operators `|=`, `&=`, `^=`: PR [#350](https://github.com/tact-lang/tact/pull/350) - Traversing maps from contract storage and structs is now allowed: PR [#389](https://github.com/tact-lang/tact/pull/389) - The `loadBool` method for `Slice` type: PR [#412](https://github.com/tact-lang/tact/pull/412) +- CLI flag `--with-decompilation` to turn on decompilation of BoC files at the end of the compilation pipeline: PR [#417](https://github.com/tact-lang/tact/pull/417) ### Changed @@ -21,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `let` statements can now be used without an explicit type declaration and determine the type automatically if it was not specified: PR [#198](https://github.com/tact-lang/tact/pull/198) - The outdated TextMate-style grammar files for text editors have been removed (the most recent grammar files can be found in the [tact-sublime](https://github.com/tact-lang/tact-sublime) repo): PR [#404](https://github.com/tact-lang/tact/pull/404) - The JSON schema for `tact.config.json` has been moved to the `json-schemas` project folder: PR [#404](https://github.com/tact-lang/tact/pull/404) +- The default compilation mode does decompile BoC files anymore, to additionally perform decompilation at the end of the pipeline, set the `fullWithDecompilation` mode in the `mode` project properties of `tact.config.json`: PR [#417](https://github.com/tact-lang/tact/pull/417) ### Fixed diff --git a/bin/tact b/bin/tact index 296786cb3..3e5f74096 100755 --- a/bin/tact +++ b/bin/tact @@ -16,6 +16,7 @@ meowModule.then( Flags -c, --config CONFIG Specify path to config file (tact.config.json) -p, --project ...names Build only the specified project name(s) from the config file + --with-decompilation Full compilation followed by decompilation of produced binary code --func Output intermediate FunC code and exit --check Perform syntax and type checking, then exit -v, --version Print Tact compiler version and exit @@ -51,6 +52,7 @@ meowModule.then( }, }, projects: { shortFlag: "p", type: "string", isMultiple: true }, + withDecompilation: { type: "boolean", default: false }, func: { type: "boolean", default: false }, check: { type: "boolean", default: false }, version: { shortFlag: "v", type: "boolean" }, @@ -86,14 +88,24 @@ meowModule.then( cli.showHelp(); } - // Disallow specifying both --func and --check flags at the same time - if (cli.flags.check && cli.flags.func) { - console.log("Error: Flags --func and --check are mutually exclusive!"); + // Disallow specifying several exclusive compilation mode flags + const compilationModeFlags = [ + cli.flags.check, + cli.flags.func, + cli.flags.withDecompilation, + ]; + const numOfCompilationModeFlagsSet = compilationModeFlags.filter( + (flag) => flag, + ).length; + if (numOfCompilationModeFlagsSet > 1) { + console.log( + "Error: Flags --with-decompilation, --func and --check are mutually exclusive!", + ); cli.showHelp(); } - // Disallow running --func and --check flags without a config or a file specified - if (isEmptyConfigAndInput() && (cli.flags.check || cli.flags.func)) { + // Disallow using compilation mode flags without a config or a file specified + if (isEmptyConfigAndInput() && numOfCompilationModeFlagsSet > 0) { console.log("Error: Either config or Tact file have to be specified!"); cli.showHelp(); } @@ -110,8 +122,7 @@ meowModule.then( // Note, that version/help flags are already processed above and don't need to be mentioned here if ( isEmptyConfigAndInput() && - !cli.flags.check && - !cli.flags.func && + numOfCompilationModeFlagsSet === 0 && cli.flags.projects.length === 0 ) { cli.showHelp(0); @@ -122,7 +133,9 @@ meowModule.then( ? "checkOnly" : cli.flags.func ? "funcOnly" - : undefined; + : cli.flags.withDecompilation + ? "fullWithDecompilation" + : undefined; // TODO: all flags on the cli should take precedence over flags in the config // Make a nice model for it in the src/node.ts instead of the current mess diff --git a/bin/test/success.config.with.decompilation.json b/bin/test/success.config.with.decompilation.json new file mode 100644 index 000000000..0f7643107 --- /dev/null +++ b/bin/test/success.config.with.decompilation.json @@ -0,0 +1,11 @@ +{ + "$schema": "../../schemas/configSchema.json", + "projects": [ + { + "name": "success", + "path": "./success.tact", + "output": "./success_output", + "mode": "fullWithDecompilation" + } + ] +} diff --git a/cspell.json b/cspell.json index c8666e42d..700e7c0a1 100644 --- a/cspell.json +++ b/cspell.json @@ -12,7 +12,11 @@ "decompile", "Decompiled", "decompiler", + "decompiles", + "decompilation", + "decompiling", "Descr", + "disasm", "divmod", "dnsresolve", "Fift", diff --git a/schemas/configSchema.json b/schemas/configSchema.json index 4871f51c5..098eb89c3 100644 --- a/schemas/configSchema.json +++ b/schemas/configSchema.json @@ -62,9 +62,9 @@ "mode": { "type": "string", "default": "full", - "enum": ["full", "checkOnly", "funcOnly"], + "enum": ["fullWithDecompilation", "full", "funcOnly", "checkOnly"], "title": "Compilation mode of the project. In Blueprint, it's always set to `full` and cannot be overwritten.", - "description": "Set to `full` by default, which runs the whole pipeline of the compilation and emits FunC code, BoC and various utility files, including wrappers for TypeScript.\nIf set to `checkOnly`, only performs syntax and type checking, preventing further compilation.\nIf set to `funcOnly`, only outputs intermediate FunC code, preventing further compilation." + "description": "Set to `full` by default, which runs the whole pipeline of the compilation and emits FunC code, BoC, and various utility files, including wrappers for TypeScript.\nIf set to `fullWithDecompilation`, does full compilation and also decompiles produced binary code in the BoC format.\nIf set to `funcOnly`, only outputs intermediate FunC code, preventing further compilation.\nIf set to `checkOnly`, only performs syntax and type checking, preventing further compilation." } } } diff --git a/src/config/parseConfig.ts b/src/config/parseConfig.ts index d683556da..6693c90bf 100644 --- a/src/config/parseConfig.ts +++ b/src/config/parseConfig.ts @@ -20,7 +20,9 @@ const projectSchema = z path: z.string(), output: z.string(), options: optionsSchema.optional(), - mode: z.enum(["full", "checkOnly", "funcOnly"]).optional(), + mode: z + .enum(["fullWithDecompilation", "full", "funcOnly", "checkOnly"]) + .optional(), }) .strict(); diff --git a/src/pipeline/build.ts b/src/pipeline/build.ts index bcbdd3c63..c99bfdd51 100644 --- a/src/pipeline/build.ts +++ b/src/pipeline/build.ts @@ -184,27 +184,26 @@ export async function build(args: { continue; } - // Fift decompiler for generated code debug - logger.log(" > " + contract + ": fift decompiler"); - let codeFiftDecompiled: string; - try { - codeFiftDecompiled = decompileAll({ src: codeBoc }); - project.writeFile(pathCodeFifDec, codeFiftDecompiled); - } catch (e) { - logger.error("Fift decompiler crashed"); - logger.error(errorToString(e)); - ok = false; - continue; - } - // Add to built map built[contract] = { - // codeFunc, codeBoc, - // codeFift, - // codeFiftDecompiled, abi, }; + + if (config.mode === "fullWithDecompilation") { + // Fift decompiler for generated code debug + logger.log(" > " + contract + ": fift decompiler"); + let codeFiftDecompiled: string; + try { + codeFiftDecompiled = decompileAll({ src: codeBoc }); + project.writeFile(pathCodeFifDec, codeFiftDecompiled); + } catch (e) { + logger.error("Fift decompiler crashed"); + logger.error(errorToString(e)); + ok = false; + continue; + } + } } if (!ok) { logger.log("💥 Compilation failed. Skipping packaging");