Skip to content
Open
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
4 changes: 4 additions & 0 deletions dev-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Code generation

- Optimized context field access in internal receivers: PR [#3329](https://github.com/tact-lang/tact/pull/3329)

### Docs

- Described off-chain calls and mention exit code 11 for getters: PR [#3314](https://github.com/tact-lang/tact/pull/3314)
Expand Down
9 changes: 9 additions & 0 deletions src/benchmarks/notcoin/gas.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@
"burn": "11739",
"discovery": "5818"
}
},
{
"label": "1.6.13 with optimized context access",
"pr": "https://github.com/tact-lang/tact/pull/3329",
"gas": {
"transfer": "15139",
"burn": "11413",
"discovery": "5818"
}
}
]
}
10 changes: 10 additions & 0 deletions src/benchmarks/notcoin/size.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@
"wallet cells": "13",
"wallet bits": "7708"
}
},
{
"label": "1.6.13 with optimized context access",
"pr": "https://github.com/tact-lang/tact/pull/3329",
"size": {
"minter cells": "29",
"minter bits": "15423",
"wallet cells": "13",
"wallet bits": "7628"
}
}
]
}
9 changes: 9 additions & 0 deletions src/benchmarks/wallet-v4/gas.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@
"addPlugin": "7399",
"pluginTransfer": "3709"
}
},
{
"label": "1.6.13 with optimized context access",
"pr": "https://github.com/tact-lang/tact/pull/3329",
"gas": {
"externalTransfer": "3625",
"addPlugin": "7399",
"pluginTransfer": "3619"
}
}
]
}
19 changes: 17 additions & 2 deletions src/generator/Writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export class WriterContext {
#nextId = 0;
// #headers: string[] = [];
#rendered: Set<string> = new Set();
private _currentReceiverType:
| "internal"
| "external"
| "bounced"
| undefined = undefined;

constructor(ctx: CompilerContext, name: string) {
this.ctx = ctx;
Expand Down Expand Up @@ -287,8 +292,18 @@ export class WriterContext {
}
}

currentContext() {
return this.#pendingName;
currentContext(): string {
return this.#pendingName ?? "";
}

currentReceiverType(): "internal" | "external" | "bounced" | undefined {
return this._currentReceiverType;
}

setReceiverType(
receiverType: "internal" | "external" | "bounced" | undefined,
) {
this._currentReceiverType = receiverType;
}

//
Expand Down
6 changes: 6 additions & 0 deletions src/generator/writers/writeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ export function writeMainContract(
wCtx.inBlock(
"() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure",
() => {
wCtx.setReceiverType("internal");
wCtx.context("internal-receiver");
wCtx.append();
wCtx.append(`;; Context`);
wCtx.append(`var cs = in_msg_cell.begin_parse();`);
Expand Down Expand Up @@ -435,6 +437,7 @@ export function writeMainContract(
contractReceivers.internal,
contract,
wCtx,
"internal",
);
},
);
Expand All @@ -453,6 +456,8 @@ export function writeMainContract(
wCtx.append();

wCtx.inBlock("() recv_external(slice in_msg) impure", () => {
wCtx.setReceiverType("external");
wCtx.context("external-receiver");
if (contract.globalVariables.has("inMsg")) {
wCtx.append(`__tact_in_msg = in_msg;`);
}
Expand All @@ -463,6 +468,7 @@ export function writeMainContract(
contractReceivers.external,
contract,
wCtx,
"external",
);
});
}
Expand Down
46 changes: 38 additions & 8 deletions src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,19 +470,44 @@ const writeUnaryExpr =
* NOTE: this branch resolves "a.b", where "a" is an expression and "b" is a field name
*/
const writeFieldExpr =
(f: Ast.FieldAccess) =>
(f: Ast.FieldAccess, receiverType?: "internal" | "external" | "bounced") =>
(wCtx: WriterContext): string => {
// Optimize Context().sender to sender()
// This is a special case to improve gas efficiency
// Optimize context() field accesses inside receivers
// Use local variables where possible for better efficiency
if (
f.aggregate.kind === "static_call" &&
f.aggregate.function.text === "context" &&
f.aggregate.args.length === 0 &&
f.field.text === "sender"
f.aggregate.args.length === 0
) {
// Use sender() directly instead of context().sender
wCtx.used("__tact_context_get_sender");
return `__tact_context_get_sender()`;
const field = f.field.text;

// Check if we're in an internal receiver context
const currentContext = wCtx.currentContext();
const writerReceiverType = wCtx.currentReceiverType();
const isInternalReceiver =
currentContext === "internal-receiver" ||
receiverType === "internal" ||
writerReceiverType === "internal";

// Only use direct variable access inside internal receivers where variables exist
if (isInternalReceiver) {
if (field === "sender") {
return "msg_sender_addr";
} else if (field === "value") {
return "msg_value";
} else if (field === "bounceable") {
return "msg_bounceable";
} else if (field === "raw") {
return "cs";
}
} else {
// For external receivers, getters, and other contexts, use function calls
if (field === "sender") {
wCtx.used("__tact_context_get_sender");
return `__tact_context_get_sender()`;
}
// Other fields (value, bounceable, raw) fall through to default field access logic
}
}

// Resolve the type of the expression
Expand Down Expand Up @@ -931,6 +956,7 @@ const writeExpressionAux: (
export function writeExpression(
f: Ast.Expression,
wCtx: WriterContext,
receiverType?: "internal" | "external" | "bounced",
): string {
// literals and constant expressions are covered here

Expand All @@ -953,6 +979,10 @@ export function writeExpression(
if (!(error instanceof TactConstEvalError) || error.fatal) throw error;
}

if (f.kind === "field_access") {
return writeFieldExpr(f, receiverType)(wCtx);
}

return writeExpressionAux(f)(wCtx);
}

Expand Down
Loading