Skip to content

Commit

Permalink
Final changes after review.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeshecdom committed Jan 9, 2025
1 parent 2ddbfde commit 8578be7
Show file tree
Hide file tree
Showing 21 changed files with 340 additions and 208 deletions.
132 changes: 73 additions & 59 deletions src/abi/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,22 @@ import {
writeAddress,
writeCell,
writeSlice,
writeString,
} from "../generator/writers/writeConstant";
import {
writeExpression,
writeValue,
} from "../generator/writers/writeExpression";
import { TactConstEvalError, throwCompilationError } from "../errors";
import { evalConstantExpression } from "../constEval";
import { writeExpression } from "../generator/writers/writeExpression";
import { throwCompilationError } from "../errors";
import { getErrorId } from "../types/resolveErrors";
import { AbiFunction } from "./AbiFunction";
import { sha256_sync } from "@ton/crypto";
import path from "path";
import { cwd } from "process";
import { posixNormalize } from "../utils/filePath";
import { ensureSimplifiedString } from "../interpreter";
import { getAstFactory } from "../grammar/ast";
import { getAstUtil } from "../optimizer/util";
import { dummySrcInfo } from "../grammar";

const util = getAstUtil(getAstFactory());
import {
ensureSimplifiedString,
ensureString,
interpretEscapeSequences,
} from "../interpreter";
import { isLiteral } from "../grammar/ast";

export const GlobalFunctions: Map<string, AbiFunction> = new Map([
[
Expand Down Expand Up @@ -58,10 +55,14 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
ref,
);
}
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);
return toNano(str.value).toString(10);
return toNano(str).toString(10);
},
},
],
Expand Down Expand Up @@ -111,10 +112,14 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
ref,
);
}
const str = ensureSimplifiedString(
evalConstantExpression(resolved[1]!, ctx.ctx),
const resolved1 = resolved[1]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved1).value;
const str = interpretEscapeSequences(
ensureString(resolved1).value,
resolved1.loc,
);
return `throw_unless(${getErrorId(str.value, ctx.ctx)}, ${writeExpression(resolved[0]!, ctx)})`;
return `throw_unless(${getErrorId(str, ctx.ctx)}, ${writeExpression(resolved[0]!, ctx)})`;
},
},
],
Expand Down Expand Up @@ -151,9 +156,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
ref,
);
}
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);
let address: Address;
try {
address = Address.parse(str);
Expand Down Expand Up @@ -203,9 +212,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
}

// Load cell data
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);
let c: Cell;
try {
c = Cell.fromBase64(str);
Expand Down Expand Up @@ -241,13 +254,9 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
: "unknown";
const lineCol = ref.interval.getLineAndColumn();
const debugPrint1 = `File ${filePath}:${lineCol.lineNum}:${lineCol.colNum}:`;
const debugPrint2 = writeValue(
util.makeSimplifiedStringLiteral(
ref.interval.contents,
dummySrcInfo,
),
ctx,
);
const contentsId = writeString(ref.interval.contents, ctx);
ctx.used(contentsId);
const debugPrint2 = `${contentsId}()`;

if (arg0.kind === "map") {
const exp = writeExpression(resolved[0]!, ctx);
Expand Down Expand Up @@ -368,23 +377,12 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([

// String case
if (arg0.name === "String") {
let str: string | undefined;
const resolved0 = resolved[0]!;

// Try const-eval
try {
str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
} catch (error) {
if (
!(error instanceof TactConstEvalError) ||
error.fatal
)
throw error;
}

// If const-eval did succeed
if (str !== undefined) {
if (isLiteral(resolved0)) {
// FIXME: This one does not need fixing, because it is carried out inside a "isLiteral" check.
// Remove this comment once the optimization step is added
const str = ensureSimplifiedString(resolved0).value;
return BigInt(
"0x" + sha256_sync(str).toString("hex"),
).toString(10);
Expand Down Expand Up @@ -437,9 +435,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
}

// Load slice data
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);
let c: Cell;
try {
c = Cell.fromBase64(str);
Expand Down Expand Up @@ -488,9 +490,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
}

// Load slice data
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);
let c: Cell;
try {
c = beginCell().storeBuffer(Buffer.from(str)).endCell();
Expand Down Expand Up @@ -533,9 +539,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
}

// Load slice data
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);

if (str.length > 32) {
throwCompilationError(
Expand Down Expand Up @@ -577,9 +587,13 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
}

// Load slice data
const str = ensureSimplifiedString(
evalConstantExpression(resolved[0]!, ctx.ctx),
).value;
const resolved0 = resolved[0]!;
// FIXME: When optimizer step added, change the following line to:
// const str = ensureSimplifiedString(resolved0).value;
const str = interpretEscapeSequences(
ensureString(resolved0).value,
resolved0.loc,
);

return `"${str}"c`;
},
Expand Down
8 changes: 5 additions & 3 deletions src/constEval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const getOptimizer = (util: AstUtil) => {
const simplOperand = partiallyEvalExpression(operand, ctx);

if (isLiteral(simplOperand)) {
const result = evalUnaryOp(op, simplOperand, source);
const result = evalUnaryOp(op, simplOperand, source, util);
return result;
} else {
const newAst = util.makeUnaryExpression(op, simplOperand);
Expand Down Expand Up @@ -94,6 +94,7 @@ export const getOptimizer = (util: AstUtil) => {
}
},
source,
util,
);

return result;
Expand Down Expand Up @@ -130,7 +131,7 @@ export const getOptimizer = (util: AstUtil) => {
ctx: CompilerContext,
interpreterConfig?: InterpreterConfig,
): AstExpression {
const interpreter = new Interpreter(ctx, interpreterConfig);
const interpreter = new Interpreter(util, ctx, interpreterConfig);
switch (ast.kind) {
case "id":
try {
Expand Down Expand Up @@ -210,9 +211,10 @@ export const getOptimizer = (util: AstUtil) => {
export function evalConstantExpression(
ast: AstExpression,
ctx: CompilerContext,
util: AstUtil,
interpreterConfig?: InterpreterConfig,
): AstLiteral {
const interpreter = new Interpreter(ctx, interpreterConfig);
const interpreter = new Interpreter(util, ctx, interpreterConfig);
const result = interpreter.interpretExpression(ast);
return result;
}
2 changes: 1 addition & 1 deletion src/generator/writers/writeExpression.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe("writeExpression", () => {
getParser(ast, defaultParser),
);
ctx = resolveDescriptors(ctx, ast);
ctx = resolveStatements(ctx);
ctx = resolveStatements(ctx, ast);
const main = getStaticFunction(ctx, "main");
if (main.ast.kind !== "function_def") {
throw Error("Unexpected function kind");
Expand Down
13 changes: 11 additions & 2 deletions src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AstId,
AstLiteral,
eqNames,
getAstFactory,
idText,
tryExtractPath,
} from "../../grammar/ast";
Expand Down Expand Up @@ -40,8 +41,9 @@ import {
} from "./writeConstant";
import { ops } from "./ops";
import { writeCastedExpression } from "./writeFunction";
import { evalConstantExpression } from "../../constEval";
import { isLvalue } from "../../types/resolveStatements";
import { evalConstantExpression } from "../../constEval";
import { getAstUtil } from "../../optimizer/util";

function isNull(wCtx: WriterContext, expr: AstExpression): boolean {
return getExpType(wCtx.ctx, expr).kind === "null";
Expand Down Expand Up @@ -171,12 +173,19 @@ export function writePathExpression(path: AstId[]): string {

export function writeExpression(f: AstExpression, wCtx: WriterContext): string {
// literals and constant expressions are covered here

// FIXME: Once optimization step is added, remove this try and replace it with this
// conditional:
// if (isLiteral(f)) {
// return writeValue(f, wCtx);
// }
try {
const util = getAstUtil(getAstFactory());
// Let us put a limit of 2 ^ 12 = 4096 iterations on loops to increase compiler responsiveness.
// If a loop takes more than such number of iterations, the interpreter will fail evaluation.
// I think maxLoopIterations should be a command line option in case a user wants to wait more
// during evaluation.
const value = evalConstantExpression(f, wCtx.ctx, {
const value = evalConstantExpression(f, wCtx.ctx, util, {
maxLoopIterations: 2n ** 12n,
});
return writeValue(value, wCtx);
Expand Down
15 changes: 10 additions & 5 deletions src/grammar/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -999,11 +999,18 @@ function eqArrays<T>(
}

export function isLiteral(ast: AstExpression): ast is AstLiteral {
return checkLiteral(ast, () => true, () => false);
return checkLiteral(
ast,
() => true,
() => false,
);
}

export function checkLiteral<T>(ast: AstExpression, t: (node: AstLiteral) => T,
f: (node: Exclude<AstExpression, AstLiteral>) => T): T {
export function checkLiteral<T>(
ast: AstExpression,
t: (node: AstLiteral) => T,
f: (node: Exclude<AstExpression, AstLiteral>) => T,
): T {
switch (ast.kind) {
case "null":
case "boolean":
Expand Down Expand Up @@ -1032,5 +1039,3 @@ f: (node: Exclude<AstExpression, AstLiteral>) => T): T {
throwInternalCompilerError("Unrecognized expression kind");
}
}


19 changes: 10 additions & 9 deletions src/grammar/test/expr-is-value.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import { getAstFactory, isLiteral } from "../ast";
import { getParser } from "../";
import { defaultParser } from "../grammar";

const valueExpressions: string[] = [
"1",
"true",
"false",
"null",
const valueExpressions: string[] = ["1", "true", "false", "null"];

const notValueExpressions: string[] = [
"g",

// Raw strings are not literals: they need to go through the interpreter to get transformed into simplified strings, which are literals.
'"one"',

// Even if these three struct instances have literal fields, raw struct instances are not literals because they need to go through
// the interpreter to get transformed into struct values.
"Test {f1: 0, f2: true}",
"Test {f1: 0, f2: true, f3: null}",
"Test {f1: Test2 {c:0}, f2: true}",
];

const notValueExpressions: string[] = [
"g",
'"one"', // A raw string cannot be determined to be a literal because it is not possible to know if some of its characters are already escaped or not
"Test {f1: 0, f2: b}",
"Test {f1: a, f2: true}",
"f(1)",
Expand Down
Loading

0 comments on commit 8578be7

Please sign in to comment.