From 45f3e3572ab520617d13180a4e8ec7ba670a4c2c Mon Sep 17 00:00:00 2001 From: Petr Makhnev <51853996+i582@users.noreply.github.com> Date: Fri, 31 Jan 2025 21:30:05 +0400 Subject: [PATCH 1/2] refactor: get rid of `AstCommentValue` in AST (#1664) --- src/ast/ast-helpers.ts | 3 -- src/ast/ast-printer.ts | 4 -- src/ast/ast.ts | 8 ---- src/ast/iterators.ts | 1 - src/ast/util.ts | 10 ----- src/generator/writers/writeConstant.ts | 5 --- src/generator/writers/writeExpression.ts | 6 --- src/optimizer/constEval.ts | 2 - src/optimizer/interpreter.ts | 15 ++++---- src/optimizer/test/partial-eval.spec.ts | 2 - src/test/e2e-emulated/as-comment.spec.ts | 38 +++++++++++++++++++ .../e2e-emulated/contracts/as-comment.tact | 13 +++++++ src/types/resolveExpression.ts | 6 +-- src/types/types.ts | 1 - 14 files changed, 59 insertions(+), 55 deletions(-) create mode 100644 src/test/e2e-emulated/as-comment.spec.ts create mode 100644 src/test/e2e-emulated/contracts/as-comment.tact diff --git a/src/ast/ast-helpers.ts b/src/ast/ast-helpers.ts index f6a196180..4d270ce2f 100644 --- a/src/ast/ast-helpers.ts +++ b/src/ast/ast-helpers.ts @@ -161,8 +161,6 @@ export function eqExpressions( return ast1.value .asCell() .equals((ast2 as A.AstSlice).value.asCell()); - case "comment_value": - return ast1.value === (ast2 as A.AstCommentValue).value; case "simplified_string": return ast1.value === (ast2 as A.AstSimplifiedString).value; case "struct_value": @@ -381,7 +379,6 @@ function checkLiteral( case "address": case "cell": case "slice": - case "comment_value": case "simplified_string": case "struct_value": return t(ast); diff --git a/src/ast/ast-printer.ts b/src/ast/ast-printer.ts index 278d77869..c9bdef1a5 100644 --- a/src/ast/ast-printer.ts +++ b/src/ast/ast-printer.ts @@ -187,8 +187,6 @@ export const ppAstBoolean = ({ value }: A.AstBoolean) => value.toString(); export const ppAstId = ({ text }: A.AstId) => text; export const ppAstNull = (_expr: A.AstNull) => "null"; export const ppAstString = ({ value }: A.AstString) => `"${value}"`; -export const ppAstCommentValue = ({ value }: A.AstCommentValue) => - JSON.stringify(value); export const ppAstSimplifiedString = ({ value }: A.AstSimplifiedString) => JSON.stringify(value); export const ppAstAddress = ({ value }: A.AstAddress) => @@ -262,7 +260,6 @@ export const ppAstExpressionNested = makeVisitor()({ init_of: ppLeaf(ppAstInitOf), string: ppLeaf(ppAstString), static_call: ppLeaf(ppAstStaticCall), - comment_value: ppLeaf(ppAstCommentValue), simplified_string: ppLeaf(ppAstSimplifiedString), address: ppLeaf(ppAstAddress), cell: ppLeaf(ppAstCell), @@ -874,7 +871,6 @@ export const ppAstNode: Printer = makeVisitor()({ id: exprNode(ppAstExpression), boolean: exprNode(ppAstExpression), string: exprNode(ppAstExpression), - comment_value: exprNode(ppAstExpression), simplified_string: exprNode(ppAstExpression), null: exprNode(ppAstExpression), address: exprNode(ppAstExpression), diff --git a/src/ast/ast.ts b/src/ast/ast.ts index b42e35751..d877f22fb 100644 --- a/src/ast/ast.ts +++ b/src/ast/ast.ts @@ -401,7 +401,6 @@ export type AstLiteral = | AstAddress | AstCell | AstSlice - | AstCommentValue | AstStructValue; export type AstBinaryOperation = @@ -599,13 +598,6 @@ export type AstSlice = { loc: SrcInfo; }; -export type AstCommentValue = { - kind: "comment_value"; - value: string; - id: number; - loc: SrcInfo; -}; - export type AstStructValue = { kind: "struct_value"; type: AstId; diff --git a/src/ast/iterators.ts b/src/ast/iterators.ts index b61bc7f31..c5651933b 100644 --- a/src/ast/iterators.ts +++ b/src/ast/iterators.ts @@ -325,7 +325,6 @@ export function traverseAndCheck( case "string": case "null": case "simplified_string": - case "comment_value": case "address": case "cell": case "slice": diff --git a/src/ast/util.ts b/src/ast/util.ts index d2e0112b2..409373c75 100644 --- a/src/ast/util.ts +++ b/src/ast/util.ts @@ -63,15 +63,6 @@ export const getAstUtil = ({ createNode }: FactoryAst) => { return result as A.AstSimplifiedString; } - function makeCommentLiteral(s: string, loc: SrcInfo): A.AstCommentValue { - const result = createNode({ - kind: "comment_value", - value: s, - loc: loc, - }); - return result as A.AstCommentValue; - } - function makeNullLiteral(loc: SrcInfo): A.AstNull { const result = createNode({ kind: "null", @@ -145,7 +136,6 @@ export const getAstUtil = ({ createNode }: FactoryAst) => { makeNumberLiteral, makeBooleanLiteral, makeSimplifiedStringLiteral, - makeCommentLiteral, makeNullLiteral, makeCellLiteral, makeSliceLiteral, diff --git a/src/generator/writers/writeConstant.ts b/src/generator/writers/writeConstant.ts index 7b45bdee6..ee6673a83 100644 --- a/src/generator/writers/writeConstant.ts +++ b/src/generator/writers/writeConstant.ts @@ -6,11 +6,6 @@ export function writeString(str: string, ctx: WriterContext) { return writeRawSlice("string", `String "${str}"`, cell, ctx); } -export function writeComment(str: string, ctx: WriterContext) { - const cell = beginCell().storeUint(0, 32).storeStringTail(str).endCell(); - return writeRawCell("comment", `Comment "${str}"`, cell, ctx); -} - export function writeAddress(address: Address, ctx: WriterContext) { return writeRawSlice( "address", diff --git a/src/generator/writers/writeExpression.ts b/src/generator/writers/writeExpression.ts index 59a2a2f52..a9183c87c 100644 --- a/src/generator/writers/writeExpression.ts +++ b/src/generator/writers/writeExpression.ts @@ -27,7 +27,6 @@ import { resolveFuncType } from "./resolveFuncType"; import { writeAddress, writeCell, - writeComment, writeSlice, writeString, } from "./writeConstant"; @@ -126,11 +125,6 @@ export function writeValue(val: A.AstLiteral, wCtx: WriterContext): string { } case "null": return "null()"; - case "comment_value": { - const id = writeComment(val.value, wCtx); - wCtx.used(id); - return `${id}()`; - } case "struct_value": { // Transform the struct fields into a map for lookup const valMap: Map = new Map(); diff --git a/src/optimizer/constEval.ts b/src/optimizer/constEval.ts index 032d70d63..8c6b90b90 100644 --- a/src/optimizer/constEval.ts +++ b/src/optimizer/constEval.ts @@ -160,8 +160,6 @@ export const getOptimizer = (util: AstUtil) => { return interpreter.interpretNumber(ast); case "string": return interpreter.interpretString(ast); - case "comment_value": - return ast; case "simplified_string": return ast; case "struct_value": diff --git a/src/optimizer/interpreter.ts b/src/optimizer/interpreter.ts index 8af714ada..1a8db4476 100644 --- a/src/optimizer/interpreter.ts +++ b/src/optimizer/interpreter.ts @@ -96,7 +96,6 @@ function ensureArgumentForEquality(val: A.AstLiteral): A.AstLiteral { case "address": case "boolean": case "cell": - case "comment_value": case "null": case "number": case "simplified_string": @@ -853,8 +852,6 @@ export class Interpreter { return this.interpretNumber(ast); case "string": return this.interpretString(ast); - case "comment_value": - return this.interpretCommentValue(ast); case "simplified_string": return this.interpretSimplifiedString(ast); case "address": @@ -931,7 +928,13 @@ export class Interpreter { const comment = ensureSimplifiedString( this.interpretExpression(ast.self), ).value; - return this.util.makeCommentLiteral(comment, ast.loc); + return this.util.makeCellLiteral( + beginCell() + .storeUint(0, 32) + .storeStringTail(comment) + .endCell(), + ast.loc, + ); } default: throwNonFatalErrorConstEval( @@ -967,10 +970,6 @@ export class Interpreter { ); } - public interpretCommentValue(ast: A.AstCommentValue): A.AstCommentValue { - return ast; - } - public interpretSimplifiedString( ast: A.AstSimplifiedString, ): A.AstSimplifiedString { diff --git a/src/optimizer/test/partial-eval.spec.ts b/src/optimizer/test/partial-eval.spec.ts index e7b29f9d6..234813e86 100644 --- a/src/optimizer/test/partial-eval.spec.ts +++ b/src/optimizer/test/partial-eval.spec.ts @@ -374,8 +374,6 @@ function dummyEval( return ast; case "cell": return ast; - case "comment_value": - return ast; case "simplified_string": return ast; case "slice": diff --git a/src/test/e2e-emulated/as-comment.spec.ts b/src/test/e2e-emulated/as-comment.spec.ts new file mode 100644 index 000000000..0fa0570db --- /dev/null +++ b/src/test/e2e-emulated/as-comment.spec.ts @@ -0,0 +1,38 @@ +import { toNano } from "@ton/core"; +import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox"; +import { AsCommentTester } from "./contracts/output/as-comment_AsCommentTester"; +import "@ton/test-utils"; + +describe("asComment", () => { + let blockchain: Blockchain; + let treasure: SandboxContract; + let contract: SandboxContract; + + beforeEach(async () => { + blockchain = await Blockchain.create(); + blockchain.verbosity.print = false; + treasure = await blockchain.treasury("treasure"); + contract = blockchain.openContract(await AsCommentTester.fromInit()); + + const result = await contract.send( + treasure.getSender(), + { + value: toNano("10"), + }, + null, + ); + + expect(result.transactions).toHaveTransaction({ + from: treasure.address, + to: contract.address, + success: true, + deploy: true, + }); + }); + + it("should calculate asComment in compile-time as in runtime", async () => { + expect(await contract.getConstantCell()).toEqualCell( + await contract.getAsCommentRuntimeCell("hello world"), + ); + }); +}); diff --git a/src/test/e2e-emulated/contracts/as-comment.tact b/src/test/e2e-emulated/contracts/as-comment.tact new file mode 100644 index 000000000..9fc1549f2 --- /dev/null +++ b/src/test/e2e-emulated/contracts/as-comment.tact @@ -0,0 +1,13 @@ +const ConstantCell: Cell = "hello world".asComment(); + +contract AsCommentTester { + receive() {} + + get fun constantCell(): Cell { + return ConstantCell; + } + + get fun asCommentRuntimeCell(val: String): Cell { + return val.asComment(); + } +} diff --git a/src/types/resolveExpression.ts b/src/types/resolveExpression.ts index ddf6ada39..06deebc32 100644 --- a/src/types/resolveExpression.ts +++ b/src/types/resolveExpression.ts @@ -126,7 +126,7 @@ function resolveSliceLiteral( } function resolveStringLiteral( - exp: A.AstString | A.AstSimplifiedString | A.AstCommentValue, + exp: A.AstString | A.AstSimplifiedString, sctx: StatementContext, ctx: CompilerContext, ): CompilerContext { @@ -868,10 +868,6 @@ export function resolveExpression( // A simplified string is resolved as a string return resolveStringLiteral(exp, sctx, ctx); } - case "comment_value": { - // A comment value is resolved as a string - return resolveStringLiteral(exp, sctx, ctx); - } case "struct_value": { // A struct value is resolved as a struct instance return resolveStructNew(exp, sctx, ctx); diff --git a/src/types/types.ts b/src/types/types.ts index eeae4e298..7f5b660a4 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -57,7 +57,6 @@ export function showValue(val: A.AstLiteral): string { case "number": return val.value.toString(val.base); case "simplified_string": - case "comment_value": return val.value; case "boolean": return val.value ? "true" : "false"; From be96b4668562f17f3b4c0fea97582b18318c92f4 Mon Sep 17 00:00:00 2001 From: Danil Ovchinnikov Date: Sat, 1 Feb 2025 00:46:23 +0700 Subject: [PATCH 2/2] refactor: remove the `crc-32` library from dependencies (#1565) --- package.json | 1 - src/optimizer/interpreter.ts | 4 ++-- src/utils/crc32.spec.ts | 12 ++++++++++++ src/utils/crc32.ts | 28 ++++++++++++++++++++++++++++ yarn.lock | 5 ----- 5 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 src/utils/crc32.spec.ts create mode 100644 src/utils/crc32.ts diff --git a/package.json b/package.json index fca134d0c..3ddae6753 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "@tonstudio/parser-runtime": "^0.0.1", "blockstore-core": "1.0.5", "change-case": "^4.1.2", - "crc-32": "1.2.2", "ipfs-unixfs-importer": "9.0.10", "json-bigint": "^1.0.0", "ohm-js": "^17.1.0", diff --git a/src/optimizer/interpreter.ts b/src/optimizer/interpreter.ts index 1a8db4476..fe5726f74 100644 --- a/src/optimizer/interpreter.ts +++ b/src/optimizer/interpreter.ts @@ -1,6 +1,6 @@ import { Address, beginCell, BitString, Cell, toNano } from "@ton/core"; import { paddedBufferToBits } from "@ton/core/dist/boc/utils/paddedBits"; -import * as crc32 from "crc-32"; +import { crc32 } from "../utils/crc32"; import * as A from "../ast/ast"; import { evalConstantExpression } from "./constEval"; import { CompilerContext } from "../context/context"; @@ -1377,7 +1377,7 @@ export class Interpreter { this.interpretExpression(ast.args[0]!), ); return this.util.makeNumberLiteral( - BigInt(crc32.str(str.value) >>> 0), + BigInt(crc32(str.value) >>> 0), ast.loc, ); // >>> 0 converts to unsigned } diff --git a/src/utils/crc32.spec.ts b/src/utils/crc32.spec.ts new file mode 100644 index 000000000..afdf95664 --- /dev/null +++ b/src/utils/crc32.spec.ts @@ -0,0 +1,12 @@ +import { crc32 } from "./crc32"; + +describe("crc32", () => { + it("crc32 is correctly calculated from the string", () => { + expect(crc32("")).toBe(0); + expect(crc32("Hello Tact")).toBe(-1612685692); + expect(crc32("Привет Tact")).toBe(-1470995533); + expect(crc32("👋 Tact")).toBe(1855222621); + expect(crc32("\u0000")).toBe(-771559539); + expect(crc32("⚡")).toBe(2136484914); + }); +}); diff --git a/src/utils/crc32.ts b/src/utils/crc32.ts new file mode 100644 index 000000000..56b694359 --- /dev/null +++ b/src/utils/crc32.ts @@ -0,0 +1,28 @@ +function makeCRC32Table(polynomial: number): Int32Array { + let c: number; + const table = new Int32Array(256); + for (let n = 0; n < table.length; n++) { + c = n; + for (let k = 0; k < 8; k++) { + c = c & 1 ? (c >>> 1) ^ polynomial : c >>> 1; + } + table[n] = c; + } + return table; +} + +// Reversed polynomial of ISO3309 CRC32 +const CRC32C_TABLE = makeCRC32Table(0xedb88320); + +export function crc32(data: string | Uint8Array): number { + if (typeof data === "string") { + data = new TextEncoder().encode(data); + } + + let crc = 0xffffffff; + for (const byte of data) { + crc = CRC32C_TABLE[(crc ^ byte) & 0xff]! ^ (crc >>> 8); + } + + return crc ^ 0xffffffff; +} diff --git a/yarn.lock b/yarn.lock index 9364b671b..6da1cc0c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2028,11 +2028,6 @@ core-util-is@^1.0.3: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -crc-32@1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" - integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== - create-jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"