diff --git a/docs.json b/docs.json index d89495e5..43952a3e 100644 --- a/docs.json +++ b/docs.json @@ -397,9 +397,18 @@ "language/func/operators", "language/func/expressions", "language/func/statements", - "language/func/functions", - "language/func/global-variables", - "language/func/compiler-directives", + { + "group": "Program declarations", + "expanded": true, + "pages": [ + "language/func/declarations-overview", + "language/func/functions", + "language/func/special-functions", + "language/func/asm-functions", + "language/func/global-variables", + "language/func/compiler-directives" + ] + }, "language/func/built-ins", "language/func/dictionaries" ] diff --git a/language/func/asm-functions.mdx b/language/func/asm-functions.mdx new file mode 100644 index 00000000..0979a81f --- /dev/null +++ b/language/func/asm-functions.mdx @@ -0,0 +1,200 @@ +--- +title: "Assembler functions" +sidebarTitle: "Assembler functions" +noindex: "true" +--- + +import { Aside } from '/snippets/aside.jsx'; + +## Assembler function definition + +In FunC, functions can be defined directly using assembler code. This is done by declaring the function body using the `asm` keyword, +followed by one or more assembler commands written inside double quotes `"`, and finalizing with the symbol `;`. +For example, the following function increments an integer and then negates it: + +```func +int inc_then_negate(int x) asm "INC" "NEGATE"; +``` + +Calls to `inc_then_negate` are translated to 2 assembler commands `INC` and `NEGATE`. + +Alternatively, the function can be written as: + +```func +int inc_then_negate'(int x) asm "INC NEGATE"; +``` + +Here, `INC NEGATE` is treated as a single assembler command by FunC, but the Fift assembler correctly interprets it as two separate commands. + + + +### Multi-line asms + +Multi-line assembler commands, including Fift code snippets, can be defined using triple-quoted strings `"""`. +For instance: + +```func +slice hello_world() asm """ + "Hello" + " " + "World" + $+ $+ $>s + PUSHSLICE +"""; +``` + +## Stack calling conventions + +The syntax for arguments and returns is the same as for standard functions, but there is one caveat - argument values are pushed onto the +stack before the function body is executed, and the return type is what is captured from the stack afterward. + +### Arguments + +When calling an asm function, the first argument is pushed onto the stack first, the second one second, and so on, +so that the first argument is at the bottom of the stack and the last one at the top. + +```func +builder storeCoins(builder b, int value) asm "STVARUINT16"; + ;; | | + ;; | Pushed last, sits on top of the stack + ;; | + ;; Pushed first, sits at the bottom of the stack + + ;; The instruction "STVARUINT16" stores + ;; integer "value" into builder "b", + ;; by taking the builder from the bottom of the stack + ;; and the integer from the top of the stack, + ;; producing a new builder at the top of the stack. +``` + +### Returns + +An assembler function's return type attempts to grab relevant values from the resulting stack after the function execution +and any [result rearrangements](#rearranging-stack-entries). + +Specifying an [atomic type](./types#atomic-types), such as an `int`, `cell`, or `builder`, will make the assembler function +capture the top value from the stack. + +For example, in the function, + +```func +builder storeCoins(builder b, int value) asm "STVARUINT16"; +``` + +the instruction `STVARUINT16` produces a final builder at the top of the stack, which is returned by the `storeCoins` function. + +Specifying a [tensor type](./types#tensor-types) as a return type, such as `(int, int)`, will cause the assembler function to take as many elements from the stack as +the number of components in the tensor type. If the tensor type has nested tensor types, like `((int, int), int)`, +it is interpreted as if it was the flattened tensor type `(int, int, int)`. + +For example, this function duplicates its input, so that if the input is `5`, it returns the tensor `(5, 5)`. + +```func +(int, int) duplicate(int a) asm "DUP"; + ;; DUP reads the value at the top of the stack + ;; and pushes a copy. + ;; Since the return type is (int, int), + ;; the function takes the first two values in the stack + ;; and returns them. +``` + +# Stack registers + +The so-called _stack registers_ are a way of referring to the values at the top of the stack. In total, +there are 256 stack registers, i.e., values held on the stack at any given time. + +Register `s0` is the value at the top of the stack, register `s1` is the value immediately after it, and so on, +until we reach the bottom of the stack, represented by `s255`, i.e., the 256th stack register. +When a value `x` is pushed onto the stack, it becomes the new `s0`. At the same time, the old `s0` becomes the new `s1`, the old `s1` becomes the new `s2`, and so on. + +```tact +int takeSecond(int a, int b) { + ;; ↑ ↑ + ;; | Pushed last, sits on top of the stack + ;; Pushed first, sits second from the top of the stack + + ;; Now, let's swap s0 (top of the stack) with s1 (second-to-top) + + ;; Before │ After + ;; ───────┼─────── + ;; s0 = b │ s0 = a + ;; s1 = a │ s1 = b + SWAP + + ;; Then, let's drop the value from the top of the stack + + ;; Before │ After + ;; ───────┼─────── + ;; s0 = a │ s0 = b + ;; s1 = b │ s1 is now either some value deeper or just blank + DROP + + ;; At the end, we have only one value on the stack, which is b + ;; Thus, it is captured by our return type `Int` +} + +fun showcase() { + takeSecond(5, 10); // 10, i.e., b +} +``` + +## Rearranging stack entries + + + +Sometimes, the order in which function arguments are passed may not match the expected order of an assembler command. +Similarly, the returned values may need to be arranged differently. +While this can be done manually using stack manipulation primitives, FunC has special syntax to handle this. + +Considering arrangements, the evaluation flow of the assembly function can be thought of in these 5 steps: + +1. The function takes arguments in the order specified by the parameters. +1. If an argument arrangement is present, arguments are reordered before being pushed onto the stack. +1. The function body is executed. +1. If a result arrangement is present, resulting values are reordered on the stack. +1. The resulting values are captured (partially or fully) by the return type of the function. + +The argument arrangement has the syntax `asm(arg2 arg1)`, where `arg1` and `arg2` are some arguments of the function arranged in the order we want to push them +onto the stack: `arg1` will be pushed first and placed at the bottom of the stack, while `arg2` will be pushed last and placed at the top of the stack. +Arrangements are not limited to two arguments and operate on all parameters of the function. + +```func +;; Changing the order of arguments to match the STDICT signature: +;; `c` will be pushed first and placed at the bottom of the stack, +;; while `b` will be pushed last and placed at the top of the stack +builder asmStoreDict(builder b, cell c) asm(c b) "STDICT"; +``` + +The return arrangement has the syntax `asm(-> 1 0)`, where 1 and 0 represent a +left-to-right reordering of [stack registers](#stack-registers) `s1` and `s0`, respectively. +The contents of `s1` will be at the top of the stack, followed by the contents of `s0`. +Arrangements are not limited to two return values and operate on captured values. + +```func +;; Changing the order of return values of LDVARUINT16 instruction, +;; since originally it would place the modified Slice on top of the stack +(slice, int) asmLoadCoins(slice s) asm(-> 1 0) "LDVARUINT16"; +;; ↑ ↑ +;; | Value of the stack register 0, +;; | which is the topmost value on the stack +;; Value of the stack register 1, +;; which is the second-to-top value on the stack +``` + +Both argument and return arrangement can be combined together and written as follows: `asm(arg2 arg1 -> 1 0)`. + +```func +;; Changing the order of return values compared to the stack +;; and switching the order of arguments as well +(slice, int) asmLoadInt(int len, slice s): asm(s len -> 1 0) "LDIX"; +;; ↑ ↑ +;; | Value of the stack register 0, +;; | which is the topmost value on the stack +;; Value of the stack register 1, +;; which is the second-to-top value on the stack +``` diff --git a/language/func/declarations-overview.mdx b/language/func/declarations-overview.mdx new file mode 100644 index 00000000..9584d217 --- /dev/null +++ b/language/func/declarations-overview.mdx @@ -0,0 +1,13 @@ +--- +title: "Program declarations" +sidebarTitle: "Overview" +noindex: "true" +--- + +import { Aside } from '/snippets/aside.jsx'; + +A FunC program is a list of: + +- [Function declarations and definitions](./functions) +- [Global variable declarations](./global-variables) +- [Compiler directives](./compiler-directives) diff --git a/language/func/functions.mdx b/language/func/functions.mdx index b1075a07..25b7b378 100644 --- a/language/func/functions.mdx +++ b/language/func/functions.mdx @@ -6,466 +6,420 @@ noindex: "true" import { Aside } from '/snippets/aside.jsx'; -A FunC program is a list of function declarations, function definitions, and global variable declarations. This section focuses on function declarations and definitions. +Every function declaration or definition follows a common pattern. The general form is: -Every function declaration or definition follows a common pattern, after which one of three elements appears: +```func +[] () +``` -- A single semicolon `;` indicates that the function is declared but not yet defined. Its definition must appear later in the same file or a different file processed before the current one by the FunC compiler. For example: - ```func - int add(int x, int y); - ``` - This declares a function named `add` with the type `(int, int) → int` but does not define it. +where `[ ... ]` represents an optional entry. Here, -- An assembler function body definition defines the function using low-level TVM primitives for use in a FunC program. For example: - ```func - int add(int x, int y) asm "ADD"; - ``` - This defines the function `add` using the TVM opcode `ADD`, keeping its type as `(int, int) → int`. +- `` is the [`forall` declarator](#forall-declarator), which declares that the function is [polymorphic](https://en.wikipedia.org/wiki/Parametric_polymorphism). This is optional. +- `` is the [return type](#return-type) of the function. +- `` is the [function name](#function-name). +- `` is a comma separated list of [function arguments](#function-arguments), each argument consisting on a type and the argument's name. +- `` are [specifiers](#specifiers) that instruct the compiler on how to process the function. +- `` is the actual [function body](#function-body), which can be of three kinds: an [empty body](#empty-body), + an [assembler body](#assembler-body), or a [standard body](#standard-body). -- A standard function body uses a block statement, the most common way to define functions. For example: - ```func - int add(int x, int y) { - return x + y; - } - ``` - This is a standard definition of the `add` function. +## Return type + +The return type can be any atomic or composite type, as described in the [Types](./types) section. For example, the following functions are valid: -## Function declaration -As mentioned earlier, every function declaration or definition follows a common pattern. The general form is: ```func -[] () +int foo() { return 0; } +(int, int) foo'() { return (0, 0); } +[int, int] foo''() { return [0, 0]; } +(() -> int) foo'''() { return foo; } +() foo''''() { return (); } ``` -where `[ ... ]` represents an optional entry. - -### Function name -A function name can be any valid [identifier](/language/func/literals#identifiers). Additionally, it may start with the symbols `.` or `~`, which have specific meanings explained in the [Statements](/language/func/statements#methods-calls) section. +FunC also supports **type inference** with the use of underscore `_` as the return type. For example: -For example, `udict_add_builder?`, `dict_set`, and `~dict_set` are all valid function names, and each is distinct. These functions are defined in [stdlib.fc](/language/func/stdlib). +```func +_ divAndMod(int m, int n) { + return (m /% n); +} +``` -#### Special function names -FunC (specifically, the Fift assembler) reserves several function names with predefined [IDs](/language/func/functions#method_id): +The function `divAndMod` has the inferred type `(int, int) -> (int, int)`. +The function computes the division and modulo of the parameters `m` and `n` by using the [division and modulo](./operators#division-and-modulo%2C-%2F%25) operator `/%`, +which always returns a two-element tensor `(int, int)`. -- `main` and `recv_internal` have `id = 0` -- `recv_external` has `id = -1` -- `run_ticktock` has `id = -2` +## Function name -Every program must include a function with `id = 0`, meaning it must define either `main` or `recv_internal`.The `run_ticktock` function is used in ticktock transactions of special smart contracts. +A function name can be any valid [identifier](./literals#identifiers). +Additionally, it may start with the symbols `.` or `~`, which have specific meanings explained in the [special function call notation](./expressions#special-function-call-notation) section. +Specifically, refer to this [section](./expressions#and-in-function-names) to understand how the symbols `.` or `~` affect the function name. -#### Receive internal +For example, `udict_add_builder?`, `dict_set`, and `~dict_set` are all valid function names, and each is distinct. These functions are defined in [stdlib.fc](./stdlib). -The `recv_internal` function is invoked when a smart contract receives **an inbound internal message**. When the [TVM initializes](/tvm/initialization), certain variables are automatically placed on the stack. By specifying arguments in `recv_internal`, the smart contract can access some of these values. Any values not explicitly referenced in the function parameters will remain unused at the bottom of the stack. +FunC reserves several function names. See the [reserved functions](./special-functions) article for more details. -The following `recv_internal` function declarations are all valid. Functions with fewer parameters consume slightly less gas, as each unused argument results in an additional `DROP` instruction: +## Function arguments -```func +A function can receive zero or more argument declarations, each declaration separated by a comma. The following kinds of argument declarations are allowed: -() recv_internal(int balance, int msg_value, cell in_msg_cell, slice in_msg) {} -() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) {} -() recv_internal(cell in_msg_cell, slice in_msg) {} -() recv_internal(slice in_msg) {} -``` +- Ordinary declaration: an argument is declared using **its type** followed by **its name**. + Example: -#### Receive external -The `recv_external` function handles **inbound external messages**. + ```func + int foo(int x) { + return x + 2; + } + ``` -### Return type + Here, `int x` declares an argument named `x` of type `int` in function `foo`. -The return type can be any atomic or composite type, as described in the [Types](/language/func/types) section. For example, the following function declarations are valid: -```func -int foo(); -(int, int) foo'(); -[int, int] foo''(); -(int → int) foo'''(); -() foo''''(); -``` + An example that declares multiple arguments: -FunC also supports **type inference**. For example: -```func -_ pyth(int m, int n) { - return (m * m - n * n, 2 * m * n, m * m + n * n); -} -``` -This is a valid definition of the function `pyth`, which has the inferred type `(int, int) → (int, int, int)`. -It computes Pythagorean triples based on the given input values. + ```func + int foo(int x, int y) { + return x + y; + } + ``` -### Function arguments + An example that declares no arguments: -In function arguments, commas separate it. The following types of argument declarations are valid: + ```func + int foo() { + return 0; + } -- Ordinary declaration: an argument is declared using **its type** followed by **its name**. Example: `int x` declares an argument named `x` of type `int` in the function declaration: `() foo(int x);`. + ``` - Unused argument declaration: only its type needs to be specified. Example: + ```func int first(int x, int) { return x; } ``` - This is a valid function definition of type `(int, int) → int`. + This is a valid function of type `(int, int) -> int`, but the function does not use its second argument. - Argument with inferred type declaration: If an argument's type is not explicitly declared, it is inferred by the type-checker. For example, + ```func int inc(x) { return x + 1; } ``` - This defines a function `inc` with the inferred type `int → int`, meaning `x` is automatically recognized as an `int`. - -**Argument tensor representation** + This defines a function `inc` with the inferred type `int -> int`, meaning `x` is automatically recognized as an `int`. -Even though a function may appear to take multiple arguments, it takes a single [tensor-type](/language/func/types#tensor-types) argument. For more details on this distinction, refer to the [Function application](/language/func/statements#function-application) section. -However, for convenience, the individual components of this tensor are conventionally referred to as "function arguments." - -### Function calls + -#### Non-modifying methods +## Specifiers - +One, multiple, or none can be used in a function declaration. However, they must appear in the order of the above list (e.g., `impure` must come before `inline` and `method_id`; +`inline_ref` must come before `method_id`, etc). -```func -example(a); -a.example(); -``` +### `impure` specifier -A function with at least **one argument**, it can be called a **non-modifying method**. For example, the function `store_uint` has the type `(builder, int, int) → builder`, where: -- The second argument is the value to store. -- The third argument is the bit length. +The `impure` specifier indicates that a function has side effects, such as modifying contract storage, sending messages, or throwing exceptions. +If a function is not marked as `impure` and its result is unused, the FunC compiler may delete the function call for optimization. -The function `begin_cell` creates a new `builder`. The following two code snippets are equivalent: +For example, the [stdlib.fc](./stdlib) function [`random`](./stdlib#random) changes the internal state of the random number generator: ```func -builder b = begin_cell(); -b = store_uint(b, 239, 8); -``` -```func -builder b = begin_cell(); -b = b.store_uint(239, 8); +int random() impure asm "RANDU256"; ``` -So the first argument of a function can be passed to it being located before the function name, if separated by `.`. The code can be further simplified: -The function's first argument is passed before the function name, separated by `.`. The syntax can be further condensed into a single statement: +The `impure` keyword prevents the compiler from removing calls to this function: ```func -builder b = begin_cell().store_uint(239, 8); +var n = 0; +random(); ;; Even though the result of random is not used, + ;; the compiler will not remove this call + ;; because random has the impure specifier. ``` -It is also possible to chain multiple method calls: -```func -builder b = begin_cell().store_uint(239, 8) - .store_int(-1, 16) - .store_uint(0xff, 10); -``` +### Inline specifier -#### Modifying functions +A function marked as `inline` is directly substituted into the code wherever it is called, eliminating the function call overhead. +Recursive calls are not allowed for inline functions. - +Since the `add` function is marked with the `inline` specifier, the compiler substitutes `add(a, b)` with `a + b` directly in the code. -If: -- The first argument of a function has type `A`. -- The function's return type is `(A, B)`, where `B` is any arbitrary type. +For instance, the compiler will replace the following code: -Then, the function can be called a modifying method. +```func +var a = 1; +var b = 2; +var n = add(a, b); +``` -Modifying functions change their first argument. They assign the first component of the returned value to the variable initially passed as the first argument. -The following calls are equivalent: +with this code: ```func -a~example(); ;;Modifying method syntax -a = example(a); ;;Standard function call +var a = 1; +var b = 2; +var n = a + b; ``` -**Example:** `load_uint` +### `inline_ref` specifier -Suppose `cs` is a cell slice, and `load_uint` has type `(slice, int) → (slice, int)`. It means: -- `load_uint` takes a cell slice and several bits to load. -- It returns the remaining slice and the loaded value. +When a function is marked with the `inline_ref` specifier, its code is stored in a separate cell. +Each time the function is called, the TVM executes a `CALLREF` command, which loads the code stored in the referenced cell and executes the function code. -The following calls are equivalent: +To give you a very high level idea on how to visualize this, think how programs are stored in the blockchain. Anything in the blockchain is a cell. A program is +a [directed acyclic graph (DAG)](/ton/tblkch#1-1-1-tvm-cells) of cells. Each cell stores TVM instructions, and can have up to 4 references to other cells. +Each one of those references represent code that the TVM can jump to. So, you can picture a program like this: -```func -(cs, int x) = load_uint(cs, 8); ;; Standard function call -``` -```func -(cs, int x) = cs.load_uint(8); ;; Method call syntax -``` -```func -int x = cs~load_uint(8); ;; Modifying method syntax +```text +Cell 1 + +instruction 1 +instruction 2 +..... +call reference A +..... +instruction n +---------------------------------------- +Reference to cell A | Reference to cell B | ``` -**Modifying methods with no return value** +where `Reference to cell A`, and `Reference to cell B` are references to other cells containing further code of the program. +When the TVM executes the instruction `call reference A`, +the TVM loads the cell referenced by `Reference to cell A` and executes the cell. -Sometimes, a function only modifies its first argument without returning a meaningful value. To enable modifying method syntax, such functions should return a unit type () as the second component. +When a function is marked as `inline_ref`, its code is placed in a separate cell, name it `C`. Then, everywhere the function is called in the original program, +it is replaced with a `call reference C`. Then, the reference to `C` is added to the original program as a cell reference. -For example, suppose we want to define a function `inc` of type `int → int`, which increments an integer. To use it as a modifying method, we define it as follows: +More concretely, imagine the following program: ```func -(int, ()) inc(int x) { - return (x + 1, ()); +int foo() inline_ref { + return 1; } -``` - -Now, the function can be used in modifying method syntax: -```func -x~inc(); ;;Equivalent to x = inc(x); +int main() { + return (foo() + foo()); +} ``` -This will increment `x` in place. -#### `.` and `~` in function names +Then, this would create two cells, one storing the code of the `main` function, call it cell `M`; and another cell storing the code of +the `foo` function, because it is marked as `inline_ref`, call it cell `F`. The two calls to `foo` inside `main` will be replaced by reference calls to `F`. +And the reference to `F` is added as a reference in cell `M`: -Suppose we want to use `inc` as a non-modifying method. We can write: - -```func -(int y, _) = inc(x); -``` +```text +Cell M -However, we can also define `inc` as a modifying method: +call reference to F +call reference to F +ADD +---------------------------------------- +Reference to F | -```func -int inc(int x) { - return x + 1; -} -(int, ()) ~inc(int x) { - return (x + 1, ()); -} -``` -Now, we can call it in different ways: -```func -x~inc(); ;; Modifies x -int y = inc(x); ;; Doesn't modify x -int z = x.inc(); ;; Also doesn't modify x -``` -**How FunC resolves function calls** -- If a function is called with `.` (e.g., `x.foo()`), the compiler looks for a `.foo` definition. -- If a function is called with `~` (e.g., `x~foo()`), the compiler looks for a `~foo` definition. -- If neither `.foo` nor `~foo` is defined, the compiler falls back to the regular `foo` definition. -### Specifiers +Cell F -In FunC, function specifiers modify the behavior of functions. There are three types: +1 PUSHINT +``` -1. `impure` -2. `inline`/ `inline_ref` -3. `method_id` +When `call reference to F` executes, the TVM loads the cell for `F` and executes it. -One, multiple, or none can be used in a function declaration. However, they must appear in a specific order (e.g., `impure` must come before `inline`). +As the example suggests, contrary to the `inline` specifier, the code for `foo` is not duplicated, because the two calls for `foo` are loading +the same cell. As such, `inline_ref` is generally more efficient regarding code size. -#### `impure` specifier +The only case where `inline` might be preferable is if the function is called just once, because loading cell references costs gas. -The `impure` specifier indicates that a function has side effects, such as modifying contract storage, sending messages, or throwing exceptions. -If a function is not marked as `impure` and its result is unused, the FunC compiler may delete the function call for optimization. +However, recursive calls to `inline_ref` functions remain impossible, as TVM cells do not support cyclic references. -For example, in the [stdlib.fc](/language/func/stdlib) function: +### `method_id` specifier -```func -int random() impure asm "RANDU256"; -``` -Here, `RANDU256` changes the internal state of the random number generator. The `impure` keyword prevents the compiler from removing this function call. +In a TVM program, every function has an internal integer ID that identifies it uniquely. These IDs are necessary because of the way +the TVM calls functions within a program: it uses a dictionary where each key is a function ID that maps to the corresponding function code. +When the TVM needs to invoke a particular function, the TVM looks up the ID in the dictionary and executes the corresponding code. -#### `inline` specifier +By default, functions are assigned sequential numbers starting from `1`. +If a function has the `method_id` specifier, the compiler will compute an ID using the formula `(crc16() & 0xffff) | 0x10000` instead. +Additionally, such function becomes a get-method (or getter method), which are functions that can be invoked by its name in lite client or TON explorer. -A function marked as `inline` is directly substituted into the code wherever it is called. -Recursive calls are not allowed for inline functions. +The `method_id` specifier has the variant `method_id()`, which allows you to set a function's ID to a specific number manually. -**Example** +For example, this defines a function whose ID is computed by the compiler and the function is available as a get-method in TON blockchain explorers: ```func -(int) add(int a, int b) inline { - return a + b; +int get_counter() method_id { + load_data(); + return ctx_counter; ;; Some global variable } ``` -Since the `add` function is marked with the `inline` specifier, the compiler substitutes `add(a, b)` with `a + b` directly in the code, eliminating the function call overhead. -Another example of using `inline` from [ICO-Minter.fc](https://github.com/ton-blockchain/token-contract/blob/f2253cb0f0e1ae0974d7dc0cef3a62cb6e19f806/ft/jetton-minter-ICO.fc#L16): +This other example defines the same function, but this time it sets the specific ID `65536`. Again, the function is available as a get-method in TON explorers. ```func -() save_data(int total_supply, slice admin_address, cell content, cell jetton_wallet_code) impure inline { - set_data(begin_cell() - .store_coins(total_supply) - .store_slice(admin_address) - .store_ref(content) - .store_ref(jetton_wallet_code) - .end_cell() - ); +int get_counter() method_id(65536) { + load_data(); + return ctx_counter; ;; Some global variable } ``` -#### `inline_ref` specifier - -When a function is marked with the `inline_ref` specifier, its code is stored in a separate cell. Each time the function is called, TVM executes a `CALLREF` command. This works similarly to `inline`, but with a key difference—since the same cell can be reused multiple times without duplication, `inline_ref` is generally more efficient regarding code size. The only case where `inline` might be preferable is if the function is called just once. However, recursive calls to `inline_ref` functions remain impossible, as TVM cells do not support cyclic references. - -#### `method_id` - -In a TVM program, every function has an internal integer ID that determines how it can be called. -By default, ordinary functions are assigned sequential numbers starting from `1`, while contract get-methods use `crc16` hashes of their names. -The `method_id()` specifier allows you to set a function's ID to a specific value manually. -If no ID is specified, the default is calculated as `(crc16() & 0xffff) | 0x10000`. -If a function has the `method_id` specifier, it can be invoked by its name as a get-method in lite client or TON explorer. - -