Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ton-assembly",
"version": "0.3.1",
"version": "0.3.2",
"description": "TON assembler and disassembler",
"author": "TON Studio & TON Core",
"repository": {
Expand All @@ -26,12 +26,6 @@
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"sideEffects": false,
"scripts": {
"build": "tsc --declaration",
Expand Down
53 changes: 45 additions & 8 deletions src/runtime/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ import {matchingRule} from "./layout"
import {IF, IFELSE, IFJMP, IFNOT, IFNOTJMP, PUSHCONT, PUSHCONT_SHORT} from "./types"
import type {StoreOptions} from "./util"

function isLastInstruction(index: number, instructions: Instr[]) {
if (index === instructions.length - 1) {
return true
}

if (instructions[index + 1]?.$ !== "DEBUGMARK") {
// Fast path, if:
// POPCTR
// ADD
//
// instruction is not the last one
return false
}

// if:
// POPCTR
// DEBUGMARK
//
// check that all remaining instructions are DEBUGMARK

for (let i = index + 1; i < instructions.length; i++) {
const instruction = instructions[i]
if (instruction?.$ !== "DEBUGMARK") {
// found non DEBUGMARK instruction in the tail, so instruction is not a last
return false
}
}

return true
}

export const compileInstructions = (
b: CodeBuilder,
instructions: Instr[],
Expand All @@ -17,7 +48,7 @@ export const compileInstructions = (
if (!instruction) break
const builderBefore = new CodeBuilder().storeBuilder(b)

const isLast = index === instructions.length - 1
const isLast = isLastInstruction(index, instructions)
const overflow = safeStore(b, instruction, isLast, options)
if (!overflow) {
// fast path
Expand Down Expand Up @@ -173,14 +204,20 @@ function compileIf(t: Instr, b: CodeBuilder, options: StoreOptions) {
t.$ === "IFJMPREF" ||
t.$ === "IFNOTJMPREF"
) {
const b2 = new CodeBuilder()
compilePushcont(b2, t.arg0, t.loc, options)
compileNonRefIf(b2, t, options)
try {
const b2 = new CodeBuilder()

if (b2.bits + b.bits <= 1023 && b2.refs + b.refs <= 4) {
compilePushcont(b, t.arg0, t.loc, options)
compileNonRefIf(b, t, options)
return true
// Try to compile, this code may throw an exception if PUSHCONT and IF doesn't fit into a single cell
compilePushcont(b2, t.arg0, t.loc, options)
compileNonRefIf(b2, t, options)

if (b2.bits + b.bits <= 1023 && b2.refs + b.refs <= 4) {
compilePushcont(b, t.arg0, t.loc, options)
compileNonRefIf(b, t, options)
return true
}
} catch {
return false
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/runtime/test/__snapshots__/compile.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Compile should compile Vault with skipRefs option enabled 1`] = `"606d59fd3f7551db56c690541e13026a47ed5df8c2c945177aeca543a8449027"`;

exports[`Compile should compile Vault with skipRefs option enabled 2`] = `"606d59fd3f7551db56c690541e13026a47ed5df8c2c945177aeca543a8449027"`;
46 changes: 46 additions & 0 deletions src/runtime/test/compile.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {compileCellWithMapping, decompileCell} from "../instr"
import {parse, print} from "../../text"
import * as fs from "node:fs"
import {diffLines} from "diff"

const test = (text: string): (() => void) => {
return () => {
const res = parse("test.asm", text)
if (res.$ === "ParseFailure") {
return
}

const [compiled] = compileCellWithMapping(res.instructions, {skipRefs: true})
expect(compiled.hash().toString("hex")).toMatchSnapshot()

const withoutDebugMarks = text
.split(/\n/)
.filter(line => !line.includes("DEBUGMARK"))
.join("\n")

const res2 = parse("test.asm", withoutDebugMarks)
if (res2.$ === "ParseFailure") {
return
}

const [compiled2] = compileCellWithMapping(res2.instructions, {skipRefs: true})
expect(compiled2.hash().toString("hex")).toMatchSnapshot()

expect(compiled.hash().toString("hex")).toEqual(compiled2.hash().toString("hex"))

const compiledAssembly = print(decompileCell(compiled))
const compiledAssembly2 = print(decompileCell(compiled2))

if (compiledAssembly !== compiledAssembly2) {
console.log(diffLines(compiledAssembly, compiledAssembly2))
expect(true).toBe(false)
}
}
}

describe("Compile", () => {
it(
"should compile Vault with skipRefs option enabled",
test(fs.readFileSync(`${__dirname}/testdata/Vault.tasm`, "utf8")),
)
})
Loading
Loading