Skip to content

Commit

Permalink
feat/fix: do not decompile binary code by default (tact-lang#417)
Browse files Browse the repository at this point in the history
Decompilation of BoC files now happens at the end of the compilation
pipeline only if it's explicitly configured
  • Loading branch information
anton-trunov authored Jun 14, 2024
1 parent c66a567 commit a437543
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 27 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/tact.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
29 changes: 21 additions & 8 deletions bin/tact
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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" },
Expand Down Expand Up @@ -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();
}
Expand All @@ -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);
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions bin/test/success.config.with.decompilation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "../../schemas/configSchema.json",
"projects": [
{
"name": "success",
"path": "./success.tact",
"output": "./success_output",
"mode": "fullWithDecompilation"
}
]
}
4 changes: 4 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
"decompile",
"Decompiled",
"decompiler",
"decompiles",
"decompilation",
"decompiling",
"Descr",
"disasm",
"divmod",
"dnsresolve",
"Fift",
Expand Down
4 changes: 2 additions & 2 deletions schemas/configSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/config/parseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
31 changes: 15 additions & 16 deletions src/pipeline/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down

0 comments on commit a437543

Please sign in to comment.