diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8f1b29d8c54..15bd79c8276 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -@dfinity/languages +* @dfinity/languages diff --git a/.github/repo_policies/BOT_APPROVED_FILES b/.github/repo_policies/BOT_APPROVED_FILES new file mode 100644 index 00000000000..c9aba6b6290 --- /dev/null +++ b/.github/repo_policies/BOT_APPROVED_FILES @@ -0,0 +1,5 @@ +# List of approved files that can be changed by a bot via an automated PR + +Changelog.md +doc/docusaurus/package-lock.json +nix/sources.json diff --git a/Changelog.md b/Changelog.md index 35686efe229..8b68e981490 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,34 @@ # Motoko compiler changelog +## 0.13.7 (FUTURE) + +* motoko (`moc`) + + * Support passing cycles in primitive `call_raw` (resp. `ExperimentalInternetComputer.call`) (#4868). + +## 0.13.6 (2025-01-21) + +* motoko (`moc`) + + * Support the low Wasm memory hook: `system func lowmemory() : async* () { ... }` (#4849). + + * Breaking change (minor) (#4854): + + * For enhanced orthogonal persistence: The Wasm persistence modes used internally for canister upgrades have been changed to lower case names, + `keep` and `replace` and instead of `Keep` and `Replace`: + + If using actor class instances with enhanced orthogonal persistence, you would need to recompile the program and upgrade with latest `moc` and `dfx`. + Otherwise, no action is needed. + + * bugfix: Checks and mitigations that timer servicing works (#4846). + + * bugfix: Some valid upgrades deleting a stable variable could fail the `--enhanced-orthogonal-persistence` stable compatibility check due to a bug (#4855). + ## 0.13.5 (2024-12-06) * motoko (`moc`) - * Breaking change (minor): + * Breaking change (minor) (#4786): * Add new keyword `transient` with exactly the same meaning as the old keyword `flexible` (but a more familiar reading). @@ -14,10 +38,8 @@ `let` or `var` declaration is `stable` (not `flexible` or `transient`). For example, a stateful counter can now be declared as: - ``` motoko persistent actor { - // counts increments since last upgrade transient var invocations = 0; @@ -28,10 +50,8 @@ value += 1; invocations += 1; } - } ``` - On upgrade, the transient variable `invocations` will be reset to `0` and `value`, now implicitly `stable`, will retain its current value. Legacy actors and classes declared without the `persistent` keyword have the same semantics as before. diff --git a/doc/docusaurus/package-lock.json b/doc/docusaurus/package-lock.json index 13dfb59a064..88dba11c435 100644 --- a/doc/docusaurus/package-lock.json +++ b/doc/docusaurus/package-lock.json @@ -9104,9 +9104,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -20300,9 +20300,9 @@ } }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" }, "negotiator": { "version": "0.6.3", diff --git a/doc/md/base/Debug.md b/doc/md/base/Debug.md index 4ce0de70adb..37c12215ae2 100644 --- a/doc/md/base/Debug.md +++ b/doc/md/base/Debug.md @@ -13,8 +13,9 @@ func print(text : Text) Prints `text` to output stream. -NOTE: The output is placed in the replica log. When running on mainnet, -this function has no effect. +NOTE: When running on an ICP network, all output is written to the [canister log](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/maintain/logs) with the exclusion of any output +produced during the execution of non-replicated queries and composite queries. +In other environments, like the interpreter and stand-alone wasm engines, the output is written to standard out. ```motoko include=import Debug.print "Hello New World!"; diff --git a/doc/md/base/Error.md b/doc/md/base/Error.md index e85da2273e2..a58f03d4ca6 100644 --- a/doc/md/base/Error.md +++ b/doc/md/base/Error.md @@ -23,6 +23,8 @@ type ErrorCode = { #system_fatal; // Transient error. #system_transient; + // Response unknown due to missed deadline. + #system_unknown; // Destination invalid. #destination_invalid; // Explicit reject by canister code. diff --git a/doc/md/base/ExperimentalInternetComputer.md b/doc/md/base/ExperimentalInternetComputer.md index 1a5f511d7bf..6a51ab2e48c 100644 --- a/doc/md/base/ExperimentalInternetComputer.md +++ b/doc/md/base/ExperimentalInternetComputer.md @@ -82,3 +82,12 @@ let c1 = IC.performanceCounter(1); work(); let diff : Nat64 = IC.performanceCounter(1) - c1; ``` + +## Function `replyDeadline` +``` motoko no-repl +func replyDeadline() : Nat +``` + +Returns the time (in nanoseconds from the epoch start) by when the update message should +reply to the best effort message so that it can be received by the requesting canister. +Queries and non-best-effort update messages return zero. diff --git a/doc/md/base/OrderedMap.md b/doc/md/base/OrderedMap.md index e5c371c2971..6a2302f3fc8 100644 --- a/doc/md/base/OrderedMap.md +++ b/doc/md/base/OrderedMap.md @@ -19,7 +19,7 @@ Credits: The core of this implementation is derived from: * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). +* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). ## Type `Map` ``` motoko no-repl diff --git a/doc/md/base/OrderedSet.md b/doc/md/base/OrderedSet.md index cbe98e20b8c..97495b55ef3 100644 --- a/doc/md/base/OrderedSet.md +++ b/doc/md/base/OrderedSet.md @@ -16,7 +16,7 @@ Credits: The core of this implementation is derived from: * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). +* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). ## Type `Set` ``` motoko no-repl diff --git a/doc/md/base/RBTree.md b/doc/md/base/RBTree.md index 7f5c17f8f24..c767a247325 100644 --- a/doc/md/base/RBTree.md +++ b/doc/md/base/RBTree.md @@ -38,7 +38,7 @@ Credits: The core of this implementation is derived from: * Ken Friis Larsen's [RedBlackMap.sml](https://github.com/kfl/mosml/blob/master/src/mosmllib/Redblackmap.sml), which itself is based on: -* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](https://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). +* Stefan Kahrs, "Red-black trees with types", Journal of Functional Programming, 11(4): 425-432 (2001), [version 1 in web appendix](http://www.cs.ukc.ac.uk/people/staff/smk/redblack/rb.html). ## Type `Color` ``` motoko no-repl diff --git a/doc/md/base/Text.md b/doc/md/base/Text.md index ef7e9e8c764..3bc4d5171f6 100644 --- a/doc/md/base/Text.md +++ b/doc/md/base/Text.md @@ -45,6 +45,34 @@ Converts the given `Char` to a `Text` value. let text = Text.fromChar('A'); // "A" ``` +## Function `fromArray` +``` motoko no-repl +func fromArray(a : [Char]) : Text +``` + +Converts the given `[Char]` to a `Text` value. + +```motoko include=import +let text = Text.fromArray(['A', 'v', 'o', 'c', 'a', 'd', 'o']); // "Avocado" +``` + +Runtime: O(a.size()) +Space: O(a.size()) + +## Function `fromVarArray` +``` motoko no-repl +func fromVarArray(a : [var Char]) : Text +``` + +Converts the given `[var Char]` to a `Text` value. + +```motoko include=import +let text = Text.fromVarArray([var 'E', 'g', 'g', 'p', 'l', 'a', 'n', 't']); // "Eggplant" +``` + +Runtime: O(a.size()) +Space: O(a.size()) + ## Function `toIter` ``` motoko no-repl func toIter(t : Text) : Iter.Iter @@ -105,6 +133,36 @@ Creates a `Text` value from a `Char` iterator. let text = Text.fromIter(['a', 'b', 'c'].vals()); // "abc" ``` +## Function `fromList` +``` motoko no-repl +func fromList(cs : List.List) : Text +``` + +Create a text from a character list. +Example: +```motoko include=initialize +fromList(?('H', ?('e', ?('l', ?('l', ?('o', null)))))); +// => "Hello" +``` + +Runtime: O(size cs) +Space: O(size cs) + +## Function `toList` +``` motoko no-repl +func toList(t : Text) : List.List +``` + +Create a character list from a text. +Example: +```motoko include=initialize +toList("Hello"); +// => ?('H', ?('e', ?('l', ?('l', ?('o', null))))) +``` + +Runtime: O(t.size()) +Space: O(t.size()) + ## Function `size` ``` motoko no-repl func size(t : Text) : Nat diff --git a/doc/md/base/Timer.md b/doc/md/base/Timer.md index 99423889438..f685f3f9885 100644 --- a/doc/md/base/Timer.md +++ b/doc/md/base/Timer.md @@ -1,28 +1,26 @@ # Timer - Timers for one-off or periodic tasks. Applicable as part of the default mechanism. +If `moc` is invoked with `-no-timer`, the importing will fail. Furthermore, if passed `--trap-on-call-error`, a congested canister send queue may prevent timer expirations to execute at runtime. It may also deactivate the global timer. -Note: If `moc` is invoked with `-no-timer`, the importing will fail. - -Note: The resolution of the timers is in the order of the block rate, - so durations should be chosen well above that. For frequent - canister wake-ups the heartbeat mechanism should be considered. +The resolution of the timers is similar to the block rate, +so durations should be chosen well above that. For frequent +canister wake-ups, consider using the [heartbeat](https://internetcomputer.org/docs/current/motoko/main/writing-motoko/heartbeats) mechanism; however, when possible, canisters should prefer timers. -Note: The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`. +The functionality described below is enabled only when the actor does not override it by declaring an explicit `system func timer`. -Note: Timers are _not_ persisted across upgrades. One possible strategy - to re-establish timers after an upgrade is to walk stable variables - in the `post_upgrade` hook and distill necessary timer information - from there. +Timers are _not_ persisted across upgrades. One possible strategy +to re-establish timers after an upgrade is to use stable variables +in the `post_upgrade` hook and distill necessary timer information +from there. -Note: Basing security (e.g. access control) on timers is almost always - the wrong choice. Be sure to inform yourself about state-of-the art - dApp security. If you _must use_ timers for security controls, be sure - to consider reentrancy issues, and the vanishing of timers on upgrades - and reinstalls. +Using timers for security (e.g., access control) is strongly discouraged. +Make sure to inform yourself about state-of-the-art dapp security. +If you must use timers for security controls, be sure +to consider reentrancy issues as well as the vanishing of timers on upgrades +and reinstalls. -Note: For further usage information for timers on the IC please consult - https://internetcomputer.org/docs/current/developer-docs/backend/periodic-tasks#timers-library-limitations +For further usage information for timers on the IC, please consult +[the documentation](https://internetcomputer.org/docs/current/developer-docs/backend/periodic-tasks#timers-library-limitations). ## Type `Duration` ``` motoko no-repl @@ -79,8 +77,8 @@ Cancels a still active timer with `(id : TimerId)`. For expired timers and not recognised `id`s nothing happens. ```motoko no-repl -func deleteAppt(appt : Appointment) { - cancelTimer (appt.reminder); +func deleteAppointment(appointment : Appointment) { + cancelTimer (appointment.reminder); // ... }; ``` diff --git a/doc/md/base/index.md b/doc/md/base/index.md index 0807634109a..190e0920a68 100644 --- a/doc/md/base/index.md +++ b/doc/md/base/index.md @@ -45,7 +45,7 @@ * [Stack](Stack.md) Class `Stack` provides a Minimal LIFO stack of elements of type `X`. * [Text](Text.md) Utility functions for `Text` values. * [Time](Time.md) System time -* [Timer](Timer.md) Timers for one-off or periodic tasks. +* [Timer](Timer.md) Timers for one-off or periodic tasks. Applicable as part of the default mechanism. * [Trie](Trie.md) Functional key-value hash maps. * [TrieMap](TrieMap.md) Class `TrieMap` provides a map from keys of type `K` to values of type `V`. * [TrieSet](TrieSet.md) Functional set diff --git a/doc/md/canister-maintenance/compatibility.md b/doc/md/canister-maintenance/compatibility.md index 56f89b7fb6a..06c9a65e320 100644 --- a/doc/md/canister-maintenance/compatibility.md +++ b/doc/md/canister-maintenance/compatibility.md @@ -4,14 +4,14 @@ sidebar_position: 4 # Verifying upgrade compatibility -## Overview + When upgrading a canister, it is important to verify that the upgrade can proceed without: - Introducing an incompatible change in stable declarations. - Breaking clients due to a Candid interface change. -`dfx` checks these properties statically before attempting the upgrade. +`dfx` checks these properties statically before attempting the upgrade. Moreover, with [enhanced orthogonal persistence](orthogonal-persistence/enhanced.md), Motoko rejects incompatible changes of stable declarations. ## Upgrade example @@ -233,14 +233,14 @@ cannot be consumed at new type Do you want to proceed? yes/No ``` -It is recommended not to continue, as you will lose the state in older versions of Motoko that use [classical orthogonal persistence](orthogonal-persistence/classical.md). +It is recommended not to continue, as you will lose the state in older versions of Motoko that use [classical orthogonal persistence](orthogonal-persistence/classical.md). Upgrading with [enhanced orthogonal persistence](orthogonal-persistence/enhanced.md) will trap and roll back, keeping the old state. Adding a new record field to the type of existing stable variable is not supported. The reason is simple: The upgrade would need to supply values for the new field out of thin air. In this example, the upgrade would need to conjure up some value for the `description` field of every existing `card` in `map`. Moreover, allowing adding optional fields is also a problem, as a record can be shared from various variables with different static types, some of them already declaring the added field or adding a same-named optional field with a potentially different type (and/or different semantics). ### Solution -To resolve this issue, an [explicit](#explicit-migration) is needed: +To resolve this issue, an [explicit migration](#explicit-migration) is needed: 1. You must keep the old variable `map` with the same structural type. However, you are allowed to change type alias name (`Card` to `OldCard`). 2. You can introduce a new variable `newMap` and copy the old state to the new one, initializing the new field as needed. @@ -257,7 +257,6 @@ persistent actor { title : Text; description : Text; }; - var map : [(Nat32, OldCard)] = []; var newMap : [(Nat32, NewCard)] = Array.map<(Nat32, OldCard), (Nat32, NewCard)>( map, @@ -278,7 +277,7 @@ persistent actor { }; ``` -`dfx` will issue a warning that `map` will be dropped. +`dfx` will issue a warning that `map` will be dropped. Make sure, you have previously migrated the old state to `newMap` before applying this final reduced version. diff --git a/doc/md/canister-maintenance/cycles.md b/doc/md/canister-maintenance/cycles.md index 1c05399b116..229ad715513 100644 --- a/doc/md/canister-maintenance/cycles.md +++ b/doc/md/canister-maintenance/cycles.md @@ -5,7 +5,7 @@ sidebar_position: 1 # Cycles -## Overview + Usage of a canister's resources on ICP is measured and paid for in [cycles](/docs/current/developer-docs/defi/cycles/converting_icp_tokens_into_cycles). diff --git a/doc/md/canister-maintenance/memory.md b/doc/md/canister-maintenance/memory.md new file mode 100644 index 00000000000..5294c5b2d44 --- /dev/null +++ b/doc/md/canister-maintenance/memory.md @@ -0,0 +1,26 @@ +--- +sidebar_position: 5 +--- + +# Memory diagnostics + +## Low memory hook + +The IC allows to implement a low memory hook, which is a warning trigger when main memory is becoming scarce. + +For this purpose, a Motoko actor or actor class instance can implement the system function `lowmemory()`. This system function is scheduled when canister's free main memory space has fallen below the defined threshold `wasm_memory_threshold`, that is is part of the canister settings. In Motoko, `lowmemory()` implements the `canister_on_low_wasm_memory` hook defined in the IC specification. + +Example of using the low memory hook: +``` +actor { + system func lowmemory() : async* () { + Debug.print("Low memory!"); + } +} +``` + +The following properties apply to the low memory hook: +* The execution of `lowmemory` happens with a certain delay, as it is scheduled as a separate asynchronous message that runs after the message in which the threshold was crossed. +* Once executed, `lowmemory` is only triggered again when the main memory free space first exceeds and then falls below the threshold. +* Traps or unhandled errors in `lowmemory` are ignored. Traps only revert the changes done in `lowmemory`. +* Due to its `async*` return type, the `lowmemory` function may send further messages and `await` results. diff --git a/doc/md/canister-maintenance/optimization.md b/doc/md/canister-maintenance/optimization.md index f92d08939f0..acf44cf5dfd 100644 --- a/doc/md/canister-maintenance/optimization.md +++ b/doc/md/canister-maintenance/optimization.md @@ -4,14 +4,14 @@ sidebar_position: 2 # Optimizing canisters -## Overview -The Motoko compiler produces small binaries with reasonably efficient code, but is not a highly optimized compiler. + +The Motoko compiler produces small binaries with reasonably efficient code, but is not a highly optimized compiler. It is possible to further optimize Motoko binaries, both for code size and cycle usage, using additional tools such as `wasm-opt`. ## Using `wasm-opt` -`Wasm-opt` is a general purpose Wasm optimizer that is now available in dfx, versions 0.14.0 and newer. +`Wasm-opt` is a general purpose Wasm optimizer that is now available in dfx, versions 0.14.0 and newer. `Wasm-opt` can be used to enable canister optimizations through a configuration option in the project's `dfx.json` file, such as: @@ -27,9 +27,9 @@ It is possible to further optimize Motoko binaries, both for code size and cycle ### Optimization levels for cycle usage -Using the `"optimize": "cycles"` option, you can expect a rough estimate of decreased cycles usage for Motoko canisters by around 10%. +Using the `"optimize": "cycles"` option, you can expect a rough estimate of decreased cycles usage for Motoko canisters by around 10%. -The `"optimize": "cycles"` option is the recommended default, as it maps to optimization level 3 in the `wasm-opt` package. +The `"optimize": "cycles"` option is the recommended default, as it maps to optimization level 3 in the `wasm-opt` package. The optimization levels for cycles usage are as follows: @@ -43,7 +43,7 @@ O0 (performs no optimizations) ### Optimization levels for binary size -To optimize the binary size instead, you can use the `"optimize": "size"` option. By using the size option, binary sizes can be reduced by roughly 16%. +To optimize the binary size instead, you can use the `"optimize": "size"` option. By using the size option, binary sizes can be reduced by roughly 16%. The optimization levels for binary size are as follows: @@ -52,7 +52,7 @@ Oz (equivalent to “size”) Os ``` -Each optimization preserves the Internet Computer specific metadata sections of each canister. +Each optimization preserves the Internet Computer specific metadata sections of each canister. :::info Note that in certain cases the optimizations can increase the complexity of certain functions in your Wasm module such that they are rejected by the replica. If you run into this issue, it is recommended to use a less aggressive optimization level such that you do not exceed the complexity limit. diff --git a/doc/md/canister-maintenance/upgrades.md b/doc/md/canister-maintenance/upgrades.md index 2bb278d5907..1e788cf7a69 100644 --- a/doc/md/canister-maintenance/upgrades.md +++ b/doc/md/canister-maintenance/upgrades.md @@ -4,7 +4,7 @@ sidebar_position: 3 # Stable variables and upgrade methods -## Overview + One key feature of Motoko is its ability to automatically persist the program's state without explicit user instruction, called **orthogonal persistence**. This not only covers persistence across transactions but also includes canister upgrades. For this purpose, Motoko features a bespoke compiler and runtime system that manages upgrades in a sophisticated way such that a new program version can pick up the state left behind by a previous program version. As a result, Motoko data persistence is not simple but also prevents data corruption or loss, while being efficient at the same time. No database, stable memory API, or stable data structure is required to retain state across upgrades. Instead, a simple `stable` keyword is sufficient to declare an data structure of arbitrary shape persistent, even if the structure uses sharing, has a deep complexity, or contains cycles. @@ -92,7 +92,6 @@ For example, the stable type `TemperatureSeries` covers the persistent data, whi ``` motoko no-repl file=../examples/WeatherActor.mo ``` - 3. __Discouraged and not recommended__: [Pre- and post-upgrade hooks](#preupgrade-and-postupgrade-system-methods) allow copying non-stable types to stable types during upgrades. This approach is error-prone and does not scale for large data. **Per best practices, using these methods should be avoided if possible.** Conceptually, it also does not align well with the idea of orthogonal persistence. ## Stable type signatures @@ -212,7 +211,7 @@ The following aspects are retained for historical reasons and backwards compatib Using the pre- and post-upgrade system methods is discouraged. It is error-prone and can render a canister unusable. In particular, if a `preupgrade` method traps and cannot be prevented from trapping by other means, then your canister may be left in a state in which it can no longer be upgraded. Per best practices, using these methods should be avoided if possible. ::: -Motoko supports user-defined upgrade hooks that run immediately before and after an upgrade. These upgrade hooks allow triggering additional logic on upgrade. +Motoko supports user-defined upgrade hooks that run immediately before and after an upgrade. These upgrade hooks allow triggering additional logic on upgrade. These hooks are declared as `system` functions with special names, `preugrade` and `postupgrade`. Both functions must have type `: () → ()`. :::danger diff --git a/doc/md/getting-started/basic-concepts.md b/doc/md/getting-started/basic-concepts.md index 31eb8967a82..b98cec333fa 100644 --- a/doc/md/getting-started/basic-concepts.md +++ b/doc/md/getting-started/basic-concepts.md @@ -4,7 +4,7 @@ sidebar_position: 2 # Basic concepts and terms -## Overview + Motoko is designed for distributed programming with **actors**. When programming on ICP in Motoko, each actor represents an ICP canister smart contract with a Candid interface. Within Motoko, the term actor is used to refer to any canister authored in any language that deploys to ICP. The role of Motoko is to make these actors easy to author and use programmatically once deployed. diff --git a/doc/md/getting-started/dev-env.md b/doc/md/getting-started/dev-env.md index ea923eba016..3ab06a8cddf 100644 --- a/doc/md/getting-started/dev-env.md +++ b/doc/md/getting-started/dev-env.md @@ -5,7 +5,7 @@ sidebar_position: 3 # Developer environment -## Overview + To develop and deploy Motoko canister smart contracts, you will need a developer environment that contains the Motoko compiler and base library. It is recommended to use the [IC SDK](https://github.com/dfinity/sdk#readme), which includes Motoko, along with `dfx`, a command-line tool used to create, build, and deploy canisters on ICP. @@ -20,13 +20,13 @@ Here are some starter projects for online Motoko canister development: * [ICP Hello World Motoko](https://github.com/dfinity/icp-hello-world-motoko#readme) * [Vite + React + Motoko](https://github.com/rvanasa/vite-react-motoko#readme) -Learn more about [Gitpod](/docs/current/developer-docs/developer-tools/ide/gitpod) and [GitHub Codespaces](/docs/current/developer-docs/developer-tools/ide/codespaces) for Motoko development. +Learn more about [Gitpod](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/gitpod) and [GitHub Codespaces](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/codespaces) for Motoko development. ## Container environments Developers may want to setup a containerized environment for Motoko and other ICP-related development. Container environments are especially useful for Windows-based systems, since `dfx` is not natively supported on Windows. -Learn more about [developer containers](/docs/current/developer-docs/developer-tools/ide/dev-containers) and [Docker containers](/docs/current/developer-docs/developer-tools/ide/dev-containers#using-docker-directly) for Motoko development. +Learn more about [developer containers](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/dev-containers) and [Docker containers](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/dev-containers#using-docker-directly) for Motoko development. ## Motoko playground @@ -34,7 +34,7 @@ Learn more about [developer containers](/docs/current/developer-docs/developer-t Canisters deployed to the Motoko playground use borrowed resources from a canister pool and are limited to a deployment length of 20 minutes. Therefore, the playground is not recommended for long-term development. -Learn more about the [Motoko playground](/docs/current/developer-docs/developer-tools/ide/playground). +Learn more about the [Motoko playground](https://internetcomputer.org/docs/current/developer-docs/developer-tools/ide/playground). ## Local developer environment @@ -44,7 +44,7 @@ Before you start developing Motoko, verify the following: - [x] You have a command line interface (CLI) window open. This window is also referred to as the 'terminal' window. -- [x] You have downloaded and installed the IC SDK package as described in the [installing the IC SDK](/docs/current/developer-docs/getting-started/install) page. +- [x] You have downloaded and installed the IC SDK package as described in the [installing the IC SDK](https://internetcomputer.org/docs/current/developer-docs/getting-started/install) page. - [x] You have a code editor installed. The [VS Code IDE](https://code.visualstudio.com/download) (with the [Motoko extension](https://marketplace.visualstudio.com/items?itemName=dfinity-foundation.vscode-motoko)) is a popular choice. @@ -54,14 +54,14 @@ Before you start developing Motoko, verify the following: ## Motoko version -The following table details which version of Motoko shipped with each major version of the IC SDK. +The following table details which version of Motoko shipped with each major version of the IC SDK. | IC SDK version | Motoko version | |-----------------|------------------| | 0.20.0 | 0.11.1 | | 0.19.0 | 0.11.1 | | 0.18.0 | 0.11.0 | -| 0.17.0 | 0.10.4 | +| 0.17.0 | 0.10.4 | | 0.16.0 | 0.10.4 | | 0.15.0 | 0.9.7 | | 0.14.0 | 0.8.7 | @@ -116,7 +116,7 @@ mops add base@0.9.0 ### Specifying a custom version of `dfx` -To specify a custom version of `dfx`, you can use the [`dfxvm` tool](/docs/current/developer-docs/developer-tools/cli-tools/dfxvm/docs/cli-reference/dfxvm/dfxvm-default). To set a default `dfx` version to be used in your project, run the command: +To specify a custom version of `dfx`, you can use the [`dfxvm` tool](https://internetcomputer.org/docs/current/developer-docs/developer-tools/cli-tools/dfxvm/docs/cli-reference/dfxvm/dfxvm-default). To set a default `dfx` version to be used in your project, run the command: ``` $ dfxvm default 0.7.2 diff --git a/doc/md/getting-started/examples.md b/doc/md/getting-started/examples.md index c22bd82875f..6554ef5b0e7 100644 --- a/doc/md/getting-started/examples.md +++ b/doc/md/getting-started/examples.md @@ -4,7 +4,7 @@ sidebar_position: 5 # Sample projects and resources -## Overview + Want to get started building with Motoko? Check out some of these sample projects and Motoko resources to help you get started. diff --git a/doc/md/getting-started/motoko-introduction.md b/doc/md/getting-started/motoko-introduction.md index b4d450cade0..81ae2c49b42 100644 --- a/doc/md/getting-started/motoko-introduction.md +++ b/doc/md/getting-started/motoko-introduction.md @@ -2,14 +2,28 @@ sidebar_position: 1 --- -# Introduction +# Meet Motoko: The language shaping the future of Web3 -## Overview +Logo Motoko is a modern, general-purpose programming language you can use specifically to author ICP canister smart contracts. Although aimed primarily at ICP canister development, its design is general enough to support future compilation to other targets. Motoko is designed to be approachable for programmers who have some basic familiarity with object-oriented and/or functional programming idioms in either JavaScript, or another modern programming language, such as Rust, Swift, TypeScript, C#, or Java. + +```motoko +// A simple Motoko smart contract. + +actor Main { + public query func hello() : async Text { + "Hello, world!" + }; +}; + +await Main.hello(); +``` + + Motoko provides: - A high-level language for programming applications to run on ICP. @@ -47,7 +61,7 @@ Its core is a call-by-value, impure functional language with imperative and obje Motoko is strongly typed and offers option types, records, variants, generics and subtyping. The type system is structural so that types with different names but equivalent definition are interchangeable; subtyping allows values to be used at their precise type but also at any more general supertype. -- Motoko is safety-oriented. +- Motoko is safety-oriented. - Memory management is automated using a garbage collector. - Implicit null values are avoided. Nulls must be handled explicitly using option types. - The type system cannot be circumvented using unsafe casts. @@ -93,6 +107,4 @@ Once upgraded, the new interface is compatible with the previous one, meaning ex ## Getting started -Get started with Motoko by [setting up your developer environment](dev-env.md) and creating a simple [Hello, world!](quickstart.md) program. - -Logo +Get started with Motoko by [setting up your developer environment](dev-env.md) and creating a simple [Hello, world!](quickstart.md) program. \ No newline at end of file diff --git a/doc/md/getting-started/quickstart.md b/doc/md/getting-started/quickstart.md index ea2f52183bb..061812ca07a 100644 --- a/doc/md/getting-started/quickstart.md +++ b/doc/md/getting-started/quickstart.md @@ -5,7 +5,7 @@ sidebar_position: 4 # Motoko quickstart -## Overview + This quickstart guide showcases how to deploy a simple 'Hello, world!' Motoko smart contract. @@ -73,8 +73,8 @@ actor { ## Starting the deployment environment Start the Internet Computer for local development or check your connection to the Internet Computer for network deployment: -- [Local deployment](/docs/current/developer-docs/getting-started/deploy-and-manage). -- [Mainnet deployment](/docs/current/developer-docs/getting-started/deploy-and-manage). +- [Local deployment](https://internetcomputer.org/docs/current/developer-docs/getting-started/deploy-and-manage). +- [Mainnet deployment](https://internetcomputer.org/docs/current/developer-docs/getting-started/deploy-and-manage). ## Register, build, and deploy locally or on the mainnet diff --git a/doc/md/migration-guides/0.11.0-migration-guide.md b/doc/md/migration-guides/0.11.0-migration-guide.md index e5c58de492a..d9f864bc275 100644 --- a/doc/md/migration-guides/0.11.0-migration-guide.md +++ b/doc/md/migration-guides/0.11.0-migration-guide.md @@ -5,7 +5,7 @@ sidebar_position: 2 # Motoko v0.11.0: Safety feature migration guide -## Overview + Motoko v0.11.0 and newer introduces a new safety feature that could cause breaking changes to existing code. diff --git a/doc/md/migration-guides/overview.md b/doc/md/migration-guides/overview.md index a11f4211582..764c79bf069 100644 --- a/doc/md/migration-guides/overview.md +++ b/doc/md/migration-guides/overview.md @@ -2,12 +2,10 @@ sidebar_position: 1 --- -# Overview +# Migration guides New versions of Motoko may introduce breaking changes that are not backwards compatible with older versions. In these scenarios, there are migration guides created to explain how to update your code to migrate to the new version properly. -## Migration guides - - [0.11.0 Migration guide](0.11.0-migration-guide.md) Logo diff --git a/doc/md/motoko-tools/embed-motoko.md b/doc/md/motoko-tools/embed-motoko.md index 872886fdb3a..f5189ee0593 100644 --- a/doc/md/motoko-tools/embed-motoko.md +++ b/doc/md/motoko-tools/embed-motoko.md @@ -4,7 +4,7 @@ sidebar_position: 1 # Embed Motoko -## Overview + The Embed Motoko tool enables Motoko smart contracts to be embedded in web pages such as a blog, web application, or even a Medium article. Simply insert your Motoko smart contract into the Embed Motoko tool and copy either the generated embedded URL or the HTML iframe to use in your web page. diff --git a/doc/md/motoko-tools/mo-dev.md b/doc/md/motoko-tools/mo-dev.md index 8d036095709..5162bb0b6e1 100644 --- a/doc/md/motoko-tools/mo-dev.md +++ b/doc/md/motoko-tools/mo-dev.md @@ -4,7 +4,7 @@ sidebar_position: 3 # Motoko dev server -## Overview + Motoko dev server, or `mo-dev` for short, is a command line tool that features a development server with live reloading for Motoko. @@ -87,13 +87,13 @@ To configure the runtime of an individual unit test, include the following comme ## Examples -The [Vite + React + Motoko](https://github.com/rvanasa/vite-react-motoko#readme) project showcases how to integrate `mo-dev` into a full-stack dapp. +The [Vite + React + Motoko](https://github.com/rvanasa/vite-react-motoko#readme) project showcases how to integrate `mo-dev` into a full-stack dapp. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rvanasa/vite-react-motoko) ## Important notes -`mo-dev` is early in development. Please feel free to report a bug, ask a question, or request a feature on the project's [GitHub issues](https://github.com/dfinity/motoko-dev-server/issues) page. +`mo-dev` is early in development. Please feel free to report a bug, ask a question, or request a feature on the project's [GitHub issues](https://github.com/dfinity/motoko-dev-server/issues) page. Logo diff --git a/doc/md/motoko-tools/motoko-formatter.md b/doc/md/motoko-tools/motoko-formatter.md index 9a305ae6fcf..d4ec78bfc35 100644 --- a/doc/md/motoko-tools/motoko-formatter.md +++ b/doc/md/motoko-tools/motoko-formatter.md @@ -4,7 +4,7 @@ sidebar_position: 2 # Motoko formatting tools -## Overview + The Motoko Prettier plugin can be used to format and validate Motoko source code files. It can be used through the [Prettier CLI](https://prettier.io/docs/en/cli.html) or through VS Code. diff --git a/doc/md/motoko-tools/motoko-js.md b/doc/md/motoko-tools/motoko-js.md index 64d20d547f7..178306eb4a1 100644 --- a/doc/md/motoko-tools/motoko-js.md +++ b/doc/md/motoko-tools/motoko-js.md @@ -4,7 +4,7 @@ sidebar_position: 4 # Motoko.js -## Overview + The Motoko.js package can be used to compile and run Motoko smart contracts in a web browser or Node.js. diff --git a/doc/md/reference/compiler-ref.md b/doc/md/reference/compiler-ref.md index 926cacde603..3e6af6f456d 100644 --- a/doc/md/reference/compiler-ref.md +++ b/doc/md/reference/compiler-ref.md @@ -4,7 +4,7 @@ sidebar_position: 2 # Compiler reference -## Overview + The Motoko compiler (`moc`) is the primary tool for compiling Motoko programs into executable WebAssembly (Wasm) modules. The compiler runs in the background when you build projects using the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/setup/install). If you invoke the compiler directly on the command-line, you can press CTRL-C to exit. diff --git a/doc/md/reference/generating-docs.md b/doc/md/reference/generating-docs.md index 5ac1210701d..20b0322c2d0 100644 --- a/doc/md/reference/generating-docs.md +++ b/doc/md/reference/generating-docs.md @@ -4,7 +4,7 @@ sidebar_position: 4 # Generating Motoko documentation -## Overview + `mo-doc` is a command-line tool for generating documentation for Motoko source code. It processes source files and generates documentation in various formats. @@ -26,7 +26,7 @@ $(dfx cache show)/mo-doc [options] - `html`: Generates HTML format documentation. - `adoc`: Generates AsciiDoc format documentation. - `plain`: Generates Markdown documentation. - + Defaults to `html`. - `--help`: Shows usage information. @@ -59,7 +59,7 @@ Doc comments can be used to provide explanations for functions, classes, types, ```motoko no-repl /// Calculate the factorial of a given positive integer. -/// +/// /// Example: /// ```motoko /// factorial(0); // => null @@ -71,7 +71,7 @@ func factorial(n : Nat) : ?Nat { ``` ## Resources -Check out Motoko's [base library source code](https://github.com/dfinity/motoko-base/tree/master/src) for additional examples and best practices. +Check out Motoko's [base library source code](https://github.com/dfinity/motoko-base/tree/master/src) for additional examples and best practices. The source code for `mo-doc` is available in the [dfinity/motoko](https://github.com/dfinity/motoko/tree/master/src/docs) GitHub repository. Contributions are welcome! diff --git a/doc/md/reference/language-manual.md b/doc/md/reference/language-manual.md index aa8defc8cc4..c184f661342 100644 --- a/doc/md/reference/language-manual.md +++ b/doc/md/reference/language-manual.md @@ -31,7 +31,7 @@ sidebar_position: 1 * [ ] Re-section so headings appear in content outline --> -## Overview + This reference page provides technical details of interest to the following audiences: @@ -2416,7 +2416,7 @@ The expression `return ` has type `None` provided: - `T` is the return type of the nearest enclosing function with no intervening `async` expression. - - `async T` is the type of the nearest enclosing, perhaps implicit, `async` expression with no intervening function declaration. + - `async T` is the type of the nearest enclosing, perhaps implicit, `async` expression with no intervening function declaration. The `return` expression exits the corresponding dynamic function invocation or completes the corresponding dynamic `async` or `async*` expression with the result of ``. diff --git a/doc/md/reference/style.md b/doc/md/reference/style.md index 805c683e4c4..5c205bda521 100644 --- a/doc/md/reference/style.md +++ b/doc/md/reference/style.md @@ -4,7 +4,7 @@ sidebar_position: 3 # Motoko style guidelines -## Overview + To increase readability and uniformity of Motoko source code, the style guide provides suggestions for formatting Motoko sources and other basic conventions. diff --git a/doc/md/stable-memory/_category_.yml b/doc/md/stable-memory/_category_.yml index a3b10a914ef..657d0ec450f 100644 --- a/doc/md/stable-memory/_category_.yml +++ b/doc/md/stable-memory/_category_.yml @@ -1,4 +1,4 @@ position: 4 -label: 'Stable memory' +label: 'Persistent data' collapsible: true # make the category collapsible collapsed: true diff --git a/doc/md/stable-memory/stable-regions.md b/doc/md/stable-memory/stable-regions.md index 7a24ea4667a..2d895e74705 100644 --- a/doc/md/stable-memory/stable-regions.md +++ b/doc/md/stable-memory/stable-regions.md @@ -4,7 +4,7 @@ sidebar_position: 1 # Stable regions -## Overview + The `Region` library provides low-level access to ICP stable memory feature. @@ -79,7 +79,7 @@ module { A stable region exposes low-level linear memory and it is the programmer's task to properly manipulate and interpret this data. This can be very error-prone when managing data in a stable region. However, the safety of Motoko's native values heap objects is always guaranteed, independent of the stable region content. -::: +::: :::note The cost of accessing stable regions is significantly higher than using Motoko's native memory, i.e. regular Motoko values and objects. diff --git a/doc/md/stable-memory/stablememory.md b/doc/md/stable-memory/stablememory.md index 5c0e84d47d3..4f433da677f 100644 --- a/doc/md/stable-memory/stablememory.md +++ b/doc/md/stable-memory/stablememory.md @@ -4,7 +4,7 @@ sidebar_position: 2 # Stable memory -## Overview + The [`Region` library](stable-regions.md) can be used to interact with stable memory on ICP. diff --git a/doc/md/writing-motoko/actor-classes.md b/doc/md/writing-motoko/actor-classes.md index 3c11a4ca3ae..d899e117b1d 100644 --- a/doc/md/writing-motoko/actor-classes.md +++ b/doc/md/writing-motoko/actor-classes.md @@ -5,7 +5,7 @@ sidebar_position: 3 # Actor classes -## Overview + Actor classes enable you to create networks of actors programmatically. Actor classes have to be defined in a separate source file. To illustrate how to define and import actor classes, the following example implements a distributed map of keys of type [`Nat`](../base/Nat.md) to values of type [`Text`](../base/Text.md). It provides simple insert and lookup functions, `put(k, v)` and `get(k)`, for working with these keys and values. diff --git a/doc/md/writing-motoko/actors-async.md b/doc/md/writing-motoko/actors-async.md index 3d96c9307d4..e90b715a23d 100644 --- a/doc/md/writing-motoko/actors-async.md +++ b/doc/md/writing-motoko/actors-async.md @@ -4,7 +4,7 @@ sidebar_position: 2 # Actors -## Overview + The programming model of the Internet Computer consists of memory-isolated canisters communicating by asynchronous message passing of binary data encoding Candid values. A canister processes its messages one-at-a-time, preventing race conditions. A canister uses call-backs to register what needs to be done with the result of any inter-canister messages it issues. diff --git a/doc/md/writing-motoko/arguments.md b/doc/md/writing-motoko/arguments.md index 7f6b684624e..67598bd6c4e 100644 --- a/doc/md/writing-motoko/arguments.md +++ b/doc/md/writing-motoko/arguments.md @@ -4,7 +4,7 @@ sidebar_position: 4 # Arguments -## Overview + Arguments can be passed to an actor's function for the function to use as input. Arguments can be [primitive values](../getting-started/basic-concepts#primitive-values), such as [`Int`](../base/Int.md), [`Nat`](../base/Nat.md), [`Bool`](../base/Bool.md), or [`Text`](../base/Text.md), or they can be non-primitive values such as tuples, arrays, or objects. To showcase a basic example of how an actor can accept an argument, this page will use an example Motoko actor that accepts multiple text arguments. @@ -20,7 +20,7 @@ persistent actor { }; ``` -Once your canister is [deployed](/docs/current/developer-docs/getting-started/deploy-and-manage), you can call the `location` method in the program and pass your `city` argument of type [`Text`](../base/Text.md) by running the following command: +Once your canister is [deployed](https://internetcomputer.org/docs/current/developer-docs/getting-started/deploy-and-manage), you can call the `location` method in the program and pass your `city` argument of type [`Text`](../base/Text.md) by running the following command: ``` dfx canister call location_hello_backend location "San Francisco" @@ -62,4 +62,4 @@ dfx canister call favorite_cities location '(vec {"San Francisco";"Paris";"Rome" The command uses the Candid interface description syntax `(vec { val1; val2; val3; })` to return a vector of values. For more information about the Candid interface description language, see the [Candid](https://internetcomputer.org/docs/current/developer-docs/smart-contracts/candid/candid-concepts) language guide. -Logo \ No newline at end of file +Logo diff --git a/doc/md/writing-motoko/async-data.md b/doc/md/writing-motoko/async-data.md index 5211c14438a..6c4d3c985c5 100644 --- a/doc/md/writing-motoko/async-data.md +++ b/doc/md/writing-motoko/async-data.md @@ -4,7 +4,7 @@ sidebar_position: 5 # Async data -## Overview + On ICP, communication between canisters is asynchronous. Sending a message together with a callback from one canister to another schedules a request in the receiver. Completion of the request triggers the callback to the sender, allowing the sender to process the result. diff --git a/doc/md/writing-motoko/async-star.md b/doc/md/writing-motoko/async-star.md index abc821d201d..0a1c5fd2cc1 100644 --- a/doc/md/writing-motoko/async-star.md +++ b/doc/md/writing-motoko/async-star.md @@ -4,7 +4,7 @@ sidebar_position: 28 # Abstracting asynchronous code -## Overview + Functions are an abstraction mechanism, allowing you to name a computation and re-use that computation in different locations within your code simply by invoking the name of that function. When the function takes parameters, you can tailor the computation to different call sites by providing different arguments. Programmers often improve their code by re-factoring common patterns of code into diff --git a/doc/md/writing-motoko/caller-id.md b/doc/md/writing-motoko/caller-id.md index b033ef496bf..b4ae9b778c3 100644 --- a/doc/md/writing-motoko/caller-id.md +++ b/doc/md/writing-motoko/caller-id.md @@ -4,7 +4,7 @@ sidebar_position: 6 # Caller identification -## Overview + Motoko’s shared functions support a simple form of caller identification that allows you to inspect the ICP **principal** associated with the caller of a function. Principals are a value that identifies a unique user or canister. diff --git a/doc/md/writing-motoko/candid-ui.md b/doc/md/writing-motoko/candid-ui.md index a318e91cdfe..8178b30aa75 100644 --- a/doc/md/writing-motoko/candid-ui.md +++ b/doc/md/writing-motoko/candid-ui.md @@ -2,14 +2,14 @@ sidebar_position: 7 --- -# Candid UI +# Candid UI + -## Overview The canister interface description language, often referred to as Candid or more generally as the IDL, provides a common language for specifying the signature of a canister smart contract. Candid provides a unified way for you to interact with canister smart contracts that are written in different languages or accessed using different tools. -For example, Candid provides a consistent view of a service whether the underlying program is native Rust, JavaScript, or any other programming language. +For example, Candid provides a consistent view of a service whether the underlying program is native Rust, JavaScript, or any other programming language. Candid also enables different tools, such as the `dfx` command-line interface and the Network Nervous System dapp, to share a common description for a service. Based on the type signature of the actor, Candid also provides a web interface that allows you to call canister functions for testing and debugging. diff --git a/doc/md/writing-motoko/candid.md b/doc/md/writing-motoko/candid.md index 096b38bc0b7..5ab31dfbae0 100644 --- a/doc/md/writing-motoko/candid.md +++ b/doc/md/writing-motoko/candid.md @@ -4,7 +4,7 @@ sidebar_position: 28 # Candid serialization -## Overview + Candid is an interface description language and serialization format designed specifically for the Internet Computer protocol. It's a crucial component that enables seamless communication between different services and canister smart contracts on ICP, regardless of the programming languages they're implemented in. diff --git a/doc/md/writing-motoko/control-flow.md b/doc/md/writing-motoko/control-flow.md index d46c9fb5db5..9274a11de23 100644 --- a/doc/md/writing-motoko/control-flow.md +++ b/doc/md/writing-motoko/control-flow.md @@ -4,7 +4,7 @@ sidebar_position: 8 # Control flow -## Overview + There are two key categories of control flow: diff --git a/doc/md/writing-motoko/errors.md b/doc/md/writing-motoko/errors.md index 65212bac916..f11789e624f 100644 --- a/doc/md/writing-motoko/errors.md +++ b/doc/md/writing-motoko/errors.md @@ -4,7 +4,7 @@ sidebar_position: 9 # Error handling -## Overview + There are three primary ways to represent and handle errors values in Motoko: diff --git a/doc/md/writing-motoko/heartbeats.md b/doc/md/writing-motoko/heartbeats.md index 6c18b406f0b..16de70fa570 100644 --- a/doc/md/writing-motoko/heartbeats.md +++ b/doc/md/writing-motoko/heartbeats.md @@ -4,7 +4,7 @@ sidebar_position: 10 # Heartbeats -## Overview + ICP canisters can elect to receive regular heartbeat messages by exposing a particular `canister_heartbeat` function (see [heartbeat](https://smartcontracts.org/docs/interface-spec/index.html#heartbeat)). diff --git a/doc/md/writing-motoko/incomplete-code.md b/doc/md/writing-motoko/incomplete-code.md index 96221f4c189..861b018ba1f 100644 --- a/doc/md/writing-motoko/incomplete-code.md +++ b/doc/md/writing-motoko/incomplete-code.md @@ -3,7 +3,7 @@ sidebar_position: 27 --- # Writing incomplete code -## Overview + In the midst of writing a program, you may want to run an incomplete version or a version where one or more execution paths are either missing or simply invalid. diff --git a/doc/md/writing-motoko/integers.md b/doc/md/writing-motoko/integers.md index e6465b45601..42f251bc3d7 100644 --- a/doc/md/writing-motoko/integers.md +++ b/doc/md/writing-motoko/integers.md @@ -5,7 +5,7 @@ sidebar_position: 11 # Integers and numbers -## Overview + Motoko offers a variety types to represent integers and natural numbers, with the usual suite of arithmetic operators (`+`, `-`, `*`, '/' etc.) and comparison operators (`==`, `!=`, `<`, `>`, `<=`, `>=`). diff --git a/doc/md/writing-motoko/intercanister-calls.md b/doc/md/writing-motoko/intercanister-calls.md index 68e94218050..adecd9b232e 100644 --- a/doc/md/writing-motoko/intercanister-calls.md +++ b/doc/md/writing-motoko/intercanister-calls.md @@ -4,7 +4,7 @@ sidebar_position: 12 # Inter-canister calls -## Overview + One of the most important features of ICP for developers is the ability to call functions in one canister from another canister. This capability to make calls between canisters, also sometimes referred to as **inter-canister calls**, enables you to reuse and share functionality in multiple dapps. diff --git a/doc/md/writing-motoko/local-objects-classes.md b/doc/md/writing-motoko/local-objects-classes.md index 3741dbd7282..ff3be96ad0b 100644 --- a/doc/md/writing-motoko/local-objects-classes.md +++ b/doc/md/writing-motoko/local-objects-classes.md @@ -8,7 +8,7 @@ sidebar_position: 13 TODO: Move examples into doc/modules/language-guide/examples --> -## Overview + In Motoko, an object is just a collection of named fields, holding values. These values can either be plain data, or function values. In addition, each field can be mutable or immutable. A simple object containing just fields of data is like a record in a database. diff --git a/doc/md/writing-motoko/message-inspection.md b/doc/md/writing-motoko/message-inspection.md index 046262c696a..8e61d0c134a 100644 --- a/doc/md/writing-motoko/message-inspection.md +++ b/doc/md/writing-motoko/message-inspection.md @@ -4,7 +4,7 @@ sidebar_position: 14 # Message inspection -## Overview + On ICP, a canister can selectively inspect, then choose to accept or decline ingress messages submitted through the HTTP interface. diff --git a/doc/md/writing-motoko/message-restrictions.md b/doc/md/writing-motoko/message-restrictions.md index ad892c1b2a0..13c023df710 100644 --- a/doc/md/writing-motoko/message-restrictions.md +++ b/doc/md/writing-motoko/message-restrictions.md @@ -4,7 +4,7 @@ sidebar_position: 15 # Messaging restrictions -## Overview + ICP places restrictions on when and how canisters are allowed to communicate. These restrictions are enforced dynamically but prevented statically in Motoko, ruling out a class of dynamic execution errors. Two examples are: @@ -12,7 +12,7 @@ ICP places restrictions on when and how canisters are allowed to communicate. Th - A canister query method cannot send messages. -These ICP restrictions are surfaced in Motoko as constraints on the program context in which certain expressions can be appear. +These ICP restrictions are surfaced in Motoko as constraints on the program context in which certain expressions can be appear. Violations of these constraints are reported as errors by the type checker. In Motoko, an expression occurs in an asynchronous context if it appears in the body of an `async` expression, which may be the body of a shared or local function or a stand-alone expression. The only exception are `query` functions, whose body is not considered to open an asynchronous context. diff --git a/doc/md/writing-motoko/modules-and-imports.md b/doc/md/writing-motoko/modules-and-imports.md index 74322dc3e2a..db9ea81ce77 100644 --- a/doc/md/writing-motoko/modules-and-imports.md +++ b/doc/md/writing-motoko/modules-and-imports.md @@ -4,7 +4,7 @@ sidebar_position: 16 # Modules and imports -## Overview + The design of Motoko strives to minimize built-in types and operations. Instead of built-in types, Motoko provides a base library of modules to handle many kinds of common operations and make the language feel complete. This base library is still evolving with modules that support core features, and the base library APIs are subject to change over time to varying degrees. You should note, in particular, that the size and number of modules and functions included in the base library is likely to increase dramatically. Updates to the base library modules might introduce breaking changes that require you to update your programs to remain compatible. Breaking changes are communicated through the [Motoko migration guides](../migration-guides/overview.md). diff --git a/doc/md/writing-motoko/mutable-state.md b/doc/md/writing-motoko/mutable-state.md index ca60b24c67e..b8d14eadc5c 100644 --- a/doc/md/writing-motoko/mutable-state.md +++ b/doc/md/writing-motoko/mutable-state.md @@ -4,7 +4,7 @@ sidebar_position: 17 # Mutable state -## Overview + Each actor in Motoko may use, but may never directly share, internal mutable state. diff --git a/doc/md/writing-motoko/object-subtyping.md b/doc/md/writing-motoko/object-subtyping.md index e0370754bea..f0830ea4573 100644 --- a/doc/md/writing-motoko/object-subtyping.md +++ b/doc/md/writing-motoko/object-subtyping.md @@ -4,7 +4,7 @@ sidebar_position: 18 # Object subtyping -## Overview + **Object subtyping** : In Motoko, objects have types that may relate by subtyping. Types with more fields are less general and are subtypes of types with fewer fields. Consider the following general types and subtypes: diff --git a/doc/md/writing-motoko/pattern-matching.md b/doc/md/writing-motoko/pattern-matching.md index e4bd0850549..8f4a5c48067 100644 --- a/doc/md/writing-motoko/pattern-matching.md +++ b/doc/md/writing-motoko/pattern-matching.md @@ -4,7 +4,7 @@ sidebar_position: 19 # Pattern matching -## Overview + Pattern matching is a language feature that makes it easy to both test and decompose structured data into its constituent parts. While most programming languages provide familiar ways to build structured data, pattern matching enables you to take apart structured data and bring its fragments into scope by binding them to the names you specify. Syntactically, the patterns resemble the construction of structured data, but generally appear in input-direction positions, such as in function argument positions, after the `case` keyword in `switch` expressions, and after `let` or `var` declarations. @@ -123,11 +123,11 @@ In a `let-else` construct, the expression or block following the `else` keyword ## Option blocks for streamlined processing of optional data -Motoko offers a preferred method for handling optional data (of type `?T`) through pattern matching, which helps avoid the notorious `null`-exception issues common in other programming languages. -However, using multiple switch statements on several options can become cumbersome and result in deeply nested, hard-to-read code. -To address this, Motoko introduces a feature called *option blocks*, written as `do ? { ... }`. +Motoko offers a preferred method for handling optional data (of type `?T`) through pattern matching, which helps avoid the notorious `null`-exception issues common in other programming languages. +However, using multiple switch statements on several options can become cumbersome and result in deeply nested, hard-to-read code. +To address this, Motoko introduces a feature called *option blocks*, written as `do ? { ... }`. These blocks allow for safe unwrapping of optional values using a postfix `!` operator. -Each use of `!` within the block is equivalent to a switch statement on an option, but with an added benefit: if `!` is applied to a `null` value, the entire block immediately abandons execution and returns `null`. +Each use of `!` within the block is equivalent to a switch statement on an option, but with an added benefit: if `!` is applied to a `null` value, the entire block immediately abandons execution and returns `null`. This short-circuiting behavior simplifies the handling of multiple optional values in a more concise and readable manner. For an example, see [option blocks and null breaks](./control-flow#option-blocks-and-null-breaks). diff --git a/doc/md/writing-motoko/pipes.md b/doc/md/writing-motoko/pipes.md index e12435a8076..5fa8392000c 100644 --- a/doc/md/writing-motoko/pipes.md +++ b/doc/md/writing-motoko/pipes.md @@ -4,7 +4,7 @@ sidebar_position: 20 # Piping values into expressions -## Overview + It can sometimes be hard to read deeply nested expressions involving several function applications. diff --git a/doc/md/writing-motoko/query-functions.md b/doc/md/writing-motoko/query-functions.md index d59ee7e5c8a..9003f81dad1 100644 --- a/doc/md/writing-motoko/query-functions.md +++ b/doc/md/writing-motoko/query-functions.md @@ -4,7 +4,7 @@ sidebar_position: 21 # Query functions -## Overview + In ICP terminology, **update** messages, also referred to as calls, can alter the state of the canister when called. Effecting a state change requires agreement amongst the distributed replicas before the network can commit the change and return a result. Reaching consensus is an expensive process with relatively high latency. diff --git a/doc/md/writing-motoko/randomness.md b/doc/md/writing-motoko/randomness.md index 9deb06f2b1d..7c945368e50 100644 --- a/doc/md/writing-motoko/randomness.md +++ b/doc/md/writing-motoko/randomness.md @@ -4,7 +4,7 @@ sidebar_position: 22 # Randomness -## Overview + The Motoko [`Random`](../base/Random.md) base library can be used for generating random values within smart contracts on ICP. Randomness on ICP is an intricate process, since ICP uses deterministic computing to obtain cryptographic random values. diff --git a/doc/md/writing-motoko/recursive-types.md b/doc/md/writing-motoko/recursive-types.md index 01906425196..e73c32f8559 100644 --- a/doc/md/writing-motoko/recursive-types.md +++ b/doc/md/writing-motoko/recursive-types.md @@ -4,7 +4,7 @@ sidebar_position: 29 # Recursive types -## Overview + A recursive type is a type that contains the values of the same type. Recursive types enable you to create complex recursive data structures, such as linked lists or trees. @@ -38,7 +38,7 @@ This generic function `last` takes one argument `l` of type `List`, which In this switch statement, the `last` function is used recursively, since it is called within itself with `t` as the argument. The function is called again each time the case statement is satisfied, and the function receives a list head that it can switch on until the last element is returned. -:::info +:::info Note that you will need to use recursive functions to access all data in a recursive type. ::: diff --git a/doc/md/writing-motoko/sharing.md b/doc/md/writing-motoko/sharing.md index 2592f238712..d418d8546e0 100644 --- a/doc/md/writing-motoko/sharing.md +++ b/doc/md/writing-motoko/sharing.md @@ -4,7 +4,7 @@ sidebar_position: 23 # Sharing data and behavior -## Overview + In Motoko, mutable state is always private to an actor. However, two actors can share message data, and those messages can refer to actors, including themselves and one another. Additionally, messages can refer to individual functions, if those functions are `shared`. diff --git a/doc/md/writing-motoko/static-types.md b/doc/md/writing-motoko/static-types.md index 08268b84111..0cce121c9b8 100644 --- a/doc/md/writing-motoko/static-types.md +++ b/doc/md/writing-motoko/static-types.md @@ -4,7 +4,7 @@ sidebar_position: 24 # Static types -## Overview + Like other modern programming languages, Motoko permits each variable to carry the value of a function, object, or a primitive datum such as a string, word, or integer. Other [types of values](../getting-started/basic-concepts.md#intro-values) exist as well, including records, tuples, and tagged data are called variants. diff --git a/doc/md/writing-motoko/structural-equality.md b/doc/md/writing-motoko/structural-equality.md index 1c57b277a20..b8d4aeb15cb 100644 --- a/doc/md/writing-motoko/structural-equality.md +++ b/doc/md/writing-motoko/structural-equality.md @@ -4,7 +4,7 @@ sidebar_position: 25 # Structural equality -## Overview + Equality (`==`) — and by extension inequality (`!=`) — is **structural**. Two values, `a` and `b`, are equal, `a == b`. They have equal contents regardless of the physical representation or identity of those values in memory. diff --git a/doc/md/writing-motoko/timers.md b/doc/md/writing-motoko/timers.md index 0c37edd9eb5..bfcac4c5b31 100644 --- a/doc/md/writing-motoko/timers.md +++ b/doc/md/writing-motoko/timers.md @@ -4,9 +4,9 @@ sidebar_position: 26 # Timers -## Overview -On ICP, canisters can set recurring timers that execute a piece of code after a specified period of time or regular interval. Times in Motoko are implemented using the [`Timer.mo`](../base/Timer.md) module, and return a `TimerId`. `TimerId`s are unique for each timer instance. A canister can contain multiple timers. + +On ICP, canisters can set recurring timers that execute a piece of code after a specified period of time or regular interval. Times in Motoko are implemented using the [`Timer.mo`](../base/Timer.md) module, and return a `TimerId`. `TimerId`s are unique for each timer instance. A canister can contain multiple timers. ## Example diff --git a/doc/md/writing-motoko/writing-intro.md b/doc/md/writing-motoko/writing-intro.md index a2d5006f121..fae678344fa 100644 --- a/doc/md/writing-motoko/writing-intro.md +++ b/doc/md/writing-motoko/writing-intro.md @@ -2,7 +2,7 @@ sidebar_position: 1 --- -# Overview +# Writing Motoko code The Motoko programming language is a new, modern and type safe language for developers who want to build the next generation of distributed applications on ICP, as it is specifically designed to support the unique features of ICP while providing a familiar, yet robust, programming environment. As a new language, Motoko is constantly evolving with support for new features and other improvements. diff --git a/nix/drun.nix b/nix/drun.nix index 2a0b84b25f8..0f0076617e9 100644 --- a/nix/drun.nix +++ b/nix/drun.nix @@ -20,6 +20,8 @@ pkgs: outputHashes = { "build-info-0.0.27" = "sha256-SkwWwDNrTsntkNiCv6rsyTFGazhpRDnKtVzPpYLKF9U="; "cloudflare-0.12.0" = "sha256-FxCAK7gUKp/63fdvzI5Ufsy4aur74fO4R/K3YFiUw0Y="; + "ic-bn-lib-0.1.0" = "sha256-wqWfF70B+YQWg63yiEvIxOq+LN1AasrNXcyPkDM4/jw="; + "ic-canister-sig-creation-1.1.0" = "sha256-c47Fh4kZbmezWCYVHMci2BMXJfESaOGsyNlWh8YR6oU="; "icrc1-test-env-0.1.1" = "sha256-2PB7e64Owin/Eji3k8UoeWs+pfDfOOTaAyXjvjOZ/4g="; "jsonrpc-0.12.1" = "sha256-3FtdZlt2PqVDkE5iKWYIp1eiIELsaYlUPRSP2Xp8ejM="; "lmdb-rkv-0.14.99" = "sha256-5WcUzapkrc/s3wCBNCuUDhtbp17n67rTbm2rx0qtITg="; @@ -42,19 +44,6 @@ pkgs: EOF cd - - # static linking of libunwind fails under nix Linux - patch rs/monitoring/backtrace/build.rs << EOF -@@ -1,8 +1,2 @@ - fn main() { -- if std::env::var("TARGET").unwrap() == "x86_64-unknown-linux-gnu" { -- println!("cargo:rustc-link-lib=static=unwind"); -- println!("cargo:rustc-link-lib=static=unwind-ptrace"); -- println!("cargo:rustc-link-lib=static=unwind-x86_64"); -- println!("cargo:rustc-link-lib=dylib=lzma"); -- } - } -EOF - mkdir -p .cargo cat > .cargo/config.toml << EOF [target.x86_64-apple-darwin] diff --git a/nix/sources.json b/nix/sources.json index cafef8ee1ff..69e93b8471e 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -6,10 +6,10 @@ "homepage": "", "owner": "dfinity", "repo": "candid", - "rev": "c2419ac6b7dfba4dd5e53f964b99bbdebc0308e2", - "sha256": "1pf0yb8f876n6d14j55x5dwkywnx1fxbznrnz28545bm8nxjc6k0", + "rev": "f7269bc25acf1691411ff91840efe0247ddf6d53", + "sha256": "1bs5jbljjwr9g8qz6crfr5iicmlrary28lhkziqcm1cxz15kb6r4", "type": "tarball", - "url": "https://github.com/dfinity/candid/archive/c2419ac6b7dfba4dd5e53f964b99bbdebc0308e2.tar.gz", + "url": "https://github.com/dfinity/candid/archive/f7269bc25acf1691411ff91840efe0247ddf6d53.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "esm": { @@ -21,15 +21,15 @@ "version": "3.2.25" }, "ic": { - "branch": "luc/adjust-drun", + "branch": "luc/latest-drun", "description": "Internet Computer blockchain source: the client/replica software run by nodes", "homepage": "", "owner": "luc-blaeser", "repo": "ic", - "rev": "bebe89514a6abd26e940b295323823169911a965", - "sha256": "1g68fyi5acbcgs2kjribk97fj8ki5g6pd99nwl5azz1rw1b0xycx", + "rev": "01c8dfda47126bb5f688302e0161f17c2c0cfe6f", + "sha256": "16h037xagxwf00k13y3h0gvfs7f3c45z2ml0j1pgvzg2fx5pzahy", "type": "tarball", - "url": "https://github.com/luc-blaeser/ic/archive/bebe89514a6abd26e940b295323823169911a965.tar.gz", + "url": "https://github.com/luc-blaeser/ic/archive/01c8dfda47126bb5f688302e0161f17c2c0cfe6f.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "ic-hs": { @@ -75,10 +75,10 @@ "homepage": "", "owner": "dfinity", "repo": "motoko-base", - "rev": "4ace26675fc1222daefb24ac160e0d03000429d2", - "sha256": "0v05djp8ymwc5c9y8ms80cqzx8h2jb9fskfmw8l2mszdcwn8ahsw", + "rev": "0719a7e23f58070df7f94dbd5726029e26adac8f", + "sha256": "0cmsqcckzrvfp4l38njbnijk6nv091gj0j4a31ifvnnb8142wlgw", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/4ace26675fc1222daefb24ac160e0d03000429d2.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/0719a7e23f58070df7f94dbd5726029e26adac8f.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { diff --git a/rts/motoko-rts/src/idl.rs b/rts/motoko-rts/src/idl.rs index cfca1b9bfc2..95de54350bd 100644 --- a/rts/motoko-rts/src/idl.rs +++ b/rts/motoko-rts/src/idl.rs @@ -841,49 +841,80 @@ pub(crate) unsafe fn memory_compatible( } true } - (IDL_CON_record, IDL_CON_record) => { + (IDL_CON_record, IDL_CON_record) if !main_actor => { + // plain object/record subtyping + // symmetric to variant case let mut n1 = leb128_decode(&mut tb1); let n2 = leb128_decode(&mut tb2); - let mut tag1 = 0; - let mut t11 = 0; - let mut advance = true; for _ in 0..n2 { let tag2 = leb128_decode(&mut tb2); let t21 = sleb128_decode(&mut tb2); if n1 == 0 { - // Additional fields are only supported in the main actor type. - if variance == TypeVariance::Invariance || !main_actor { - return false; - } - continue; - }; - if advance { - loop { - tag1 = leb128_decode(&mut tb1); - t11 = sleb128_decode(&mut tb1); - n1 -= 1; - // Do not skip fields during invariance check. - if variance == TypeVariance::Invariance || !(tag1 < tag2 && n1 > 0) { - break; - } - } + return false; }; - if tag1 > tag2 { - // Additional fields are only supported in the main actor type. - if variance == TypeVariance::Invariance || !main_actor { - return false; + let mut tag1: u32; + let mut t11: i32; + loop { + tag1 = leb128_decode(&mut tb1); + t11 = sleb128_decode(&mut tb1); + n1 -= 1; + if variance == TypeVariance::Invariance || !(tag1 < tag2 && n1 > 0) { + break; } - advance = false; // reconsider this field in next round - continue; + } + if tag1 != tag2 { + return false; }; if !memory_compatible(rel, variance, typtbl1, typtbl2, end1, end2, t11, t21, false) { return false; } - advance = true; } variance != TypeVariance::Invariance || n1 == 0 } + (IDL_CON_record, IDL_CON_record) if main_actor => { + // memory compatibility + assert!(variance == TypeVariance::Covariance); + let mut n1 = leb128_decode(&mut tb1); + let mut n2 = leb128_decode(&mut tb2); + let mut tag1: u32; + let mut t11: i32; + let mut tag2: u32; + let mut t21: i32; + while n1 > 0 && n2 > 0 { + tag1 = leb128_decode(&mut tb1); + t11 = sleb128_decode(&mut tb1); + tag2 = leb128_decode(&mut tb2); + t21 = sleb128_decode(&mut tb2); + n1 -= 1; + n2 -= 1; + while tag1 != tag2 { + if tag1 < tag2 { + if n1 > 0 { + tag1 = leb128_decode(&mut tb1); + t11 = sleb128_decode(&mut tb1); + n1 -= 1; + continue; + }; + return true; + }; + if tag1 > tag2 { + if n2 > 0 { + tag2 = leb128_decode(&mut tb2); + t21 = sleb128_decode(&mut tb2); + n2 -= 1; + continue; + }; + return true; + }; + } + if !memory_compatible(rel, variance, typtbl1, typtbl2, end1, end2, t11, t21, false) + { + return false; + } + } + return true; + } (IDL_CON_variant, IDL_CON_variant) => { let n1 = leb128_decode(&mut tb1); let mut n2 = leb128_decode(&mut tb2); diff --git a/src/codegen/compile_classical.ml b/src/codegen/compile_classical.ml index a16282ca4db..3b6a6105c48 100644 --- a/src/codegen/compile_classical.ml +++ b/src/codegen/compile_classical.ml @@ -5277,6 +5277,18 @@ module IC = struct edesc = nr (FuncExport (nr fi)) }) + let export_low_memory env = + assert (E.mode env = Flags.ICMode || E.mode env = Flags.RefMode); + let fi = E.add_fun env "canister_on_low_wasm_memory" + (Func.of_body env [] [] (fun env -> + G.i (Call (nr (E.built_in env "low_memory_exp"))) ^^ + GC.collect_garbage env)) + in + E.add_export env (nr { + name = Lib.Utf8.decode "canister_on_low_wasm_memory"; + edesc = nr (FuncExport (nr fi)) + }) + let export_wasi_start env = assert (E.mode env = Flags.WASIMode); let fi = E.add_fun env "_start" (Func.of_body env [] [] (fun env1 -> @@ -13014,6 +13026,15 @@ and main_actor as_opt mod_env ds fs up = IC.export_inspect env; end; + (* Export low memory hook (but only when required) *) + begin match up.low_memory.it with + | Ir.PrimE (Ir.TupPrim, []) -> () + | _ -> + Func.define_built_in env "low_memory_exp" [] [] (fun env -> + compile_exp_as env ae2 SR.unit up.low_memory); + IC.export_low_memory env; + end; + (* Helper function to build the stable actor wrapper *) Func.define_built_in mod_env IC.get_actor_to_persist_function_name [] [I32Type] (fun env -> compile_exp_as env ae2 SR.Vanilla build_stable_actor diff --git a/src/codegen/compile_enhanced.ml b/src/codegen/compile_enhanced.ml index 61d21e839f5..0f98f94a28b 100644 --- a/src/codegen/compile_enhanced.ml +++ b/src/codegen/compile_enhanced.ml @@ -4972,6 +4972,18 @@ module IC = struct edesc = nr (FuncExport (nr fi)) }) + let export_low_memory env = + assert (E.mode env = Flags.ICMode || E.mode env = Flags.RefMode); + let fi = E.add_fun env "canister_on_low_wasm_memory" + (Func.of_body env [] [] (fun env -> + G.i (Call (nr (E.built_in env "low_memory_exp"))) ^^ + GC.collect_garbage env)) + in + E.add_export env (nr { + name = Lib.Utf8.decode "canister_on_low_wasm_memory"; + edesc = nr (FuncExport (nr fi)) + }) + let initialize_main_actor_function_name = "@initialize_main_actor" let initialize_main_actor env = @@ -13106,6 +13118,15 @@ and main_actor as_opt mod_env ds fs up = IC.export_inspect env; end; + (* Export low memory hook (but only when required) *) + begin match up.low_memory.it with + | Ir.PrimE (Ir.TupPrim, []) -> () + | _ -> + Func.define_built_in env "low_memory_exp" [] [] (fun env -> + compile_exp_as env ae2 SR.unit up.low_memory); + IC.export_low_memory env; + end; + (* Helper function to build the stable actor wrapper *) Func.define_built_in mod_env IC.get_actor_to_persist_function_name [] [I64Type] (fun env -> compile_exp_as env ae2 SR.Vanilla build_stable_actor diff --git a/src/codegen/instrList.ml b/src/codegen/instrList.ml index 886a103cdd1..b885c3c3e62 100644 --- a/src/codegen/instrList.ml +++ b/src/codegen/instrList.ml @@ -125,6 +125,13 @@ let optimize : instr list -> instr list = fun is -> ({it = Const cr; _} as const) :: ({it = Binary opr; _} as op) :: r' when Option.is_some (combine_shifts const op (opl, cl, opr, cr.it)) -> go l' (Option.get (combine_shifts const op (opl, cl, opr, cr.it)) @ r') + (* Examining topmost bit *) + | {it = Binary (I32 I32Op.ShrU); _} as shift :: {it = Const {it = I32 31l; _}; _} :: l', + ({it = If (res,then_,else_); _} as if_) :: r' -> + go l' ({ shift with it = Unary (I32 I32Op.Clz) } :: { if_ with it = If (res,else_,then_) } :: r') + | {it = Binary (I32 I32Op.And); _} as and_ :: {it = Const {it = I32 2147483648l; _}; _} :: l', + ({it = If (res,then_,else_); _} as if_) :: r' -> + go l' ({ and_ with it = Unary (I32 I32Op.Clz) } :: { if_ with it = If (res,else_,then_) } :: r') (* Null shifts can be eliminated *) | l', {it = Const {it = I32 0l; _}; _} :: {it = Binary (I32 I32Op.(Shl|ShrS|ShrU)); _} :: r' -> go l' r' diff --git a/src/ir_def/arrange_ir.ml b/src/ir_def/arrange_ir.ml index b0f110029fb..55a42d9cec9 100644 --- a/src/ir_def/arrange_ir.ml +++ b/src/ir_def/arrange_ir.ml @@ -35,13 +35,14 @@ let rec exp e = match e.it with | TryE (e, cs, None) -> "TryE" $$ [exp e] @ List.map case cs | TryE (e, cs, Some (i, _)) -> "TryE" $$ [exp e] @ List.map case cs @ Atom ";" :: [id i] -and system { meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type} = (* TODO: show meta? *) +and system { meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type} = (* TODO: show meta? *) "System" $$ [ "Pre" $$ [exp preupgrade]; "Post" $$ [exp postupgrade]; "Heartbeat" $$ [exp heartbeat]; "Timer" $$ [exp timer]; "Inspect" $$ [exp inspect]; + "LowMemory" $$ [exp low_memory]; "StableRecord" $$ [exp stable_record]; "StableType" $$ [typ stable_type] ] diff --git a/src/ir_def/check_ir.ml b/src/ir_def/check_ir.ml index 37e84da5623..8d8d23c56d9 100644 --- a/src/ir_def/check_ir.ml +++ b/src/ir_def/check_ir.ml @@ -817,7 +817,7 @@ let rec check_exp env (exp:Ir.exp) : unit = typ exp_r <: T.(Construct.err_contT unit); typ exp_c <: Construct.clean_contT; | ActorE (ds, fs, - { preupgrade; postupgrade; meta; heartbeat; timer; inspect; stable_record; stable_type }, t0) -> + { preupgrade; postupgrade; meta; heartbeat; timer; inspect; low_memory; stable_record; stable_type }, t0) -> (* TODO: check meta *) let env' = { env with async = None } in let scope1 = gather_block_decs env' ds in @@ -828,12 +828,15 @@ let rec check_exp env (exp:Ir.exp) : unit = check_exp env'' heartbeat; check_exp env'' timer; check_exp env'' inspect; + let async_cap = Some Async_cap.top_cap in + check_exp { env'' with async = async_cap } low_memory; check_exp env'' stable_record; typ preupgrade <: T.unit; typ postupgrade <: T.unit; typ heartbeat <: T.unit; typ timer <: T.unit; typ inspect <: T.unit; + typ low_memory <: T.unit; typ stable_record <: stable_type; check (T.is_obj t0) "bad annotation (object type expected)"; let (s0, tfs0) = T.as_obj t0 in @@ -1160,7 +1163,7 @@ let check_comp_unit env = function let env' = adjoin env scope in check_decs env' ds | ActorU (as_opt, ds, fs, - { preupgrade; postupgrade; meta; heartbeat; timer; inspect; stable_type; stable_record }, t0) -> + { preupgrade; postupgrade; meta; heartbeat; timer; inspect; low_memory; stable_type; stable_record }, t0) -> let check p = check env no_region p in let (<:) t1 t2 = check_sub env no_region t1 t2 in let env' = match as_opt with @@ -1179,11 +1182,14 @@ let check_comp_unit env = function check_exp env'' timer; check_exp env'' inspect; check_exp env'' stable_record; + let async_cap = Some Async_cap.top_cap in + check_exp { env'' with async = async_cap } low_memory; typ preupgrade <: T.unit; typ postupgrade <: T.unit; typ heartbeat <: T.unit; typ timer <: T.unit; typ inspect <: T.unit; + typ low_memory <: T.unit; typ stable_record <: stable_type; check (T.is_obj t0) "bad annotation (object type expected)"; let (s0, tfs0) = T.as_obj t0 in diff --git a/src/ir_def/construct.ml b/src/ir_def/construct.ml index 6564490287e..3031fdcc2c2 100644 --- a/src/ir_def/construct.ml +++ b/src/ir_def/construct.ml @@ -112,6 +112,7 @@ let primE prim es = | DeserializePrim ts -> T.seq ts | DeserializeOptPrim ts -> T.Opt (T.seq ts) | OtherPrim "trap" -> T.Non + | OtherPrim "global_timer_set" -> T.nat64 | OtherPrim "call_perform_status" -> T.(Prim Nat32) | OtherPrim "call_perform_message" -> T.text | OtherPrim "array_len" @@ -269,6 +270,12 @@ let nat32E n = note = Note.{ def with typ = T.(Prim Nat32) } } +let nat64E n = + { it = LitE (Nat64Lit n); + at = no_region; + note = Note.{ def with typ = T.nat64 } + } + let natE n = { it = LitE (NatLit n); at = no_region; diff --git a/src/ir_def/construct.mli b/src/ir_def/construct.mli index 26387ef5097..7cc0bef4c3d 100644 --- a/src/ir_def/construct.mli +++ b/src/ir_def/construct.mli @@ -66,6 +66,7 @@ val let_else_switch : pat -> exp -> exp -> exp val natE : Mo_values.Numerics.Nat.t -> exp val intE : Mo_values.Numerics.Int.t -> exp val nat32E : Mo_values.Numerics.Nat32.t -> exp +val nat64E : Mo_values.Numerics.Nat64.t -> exp val textE : string -> exp val blobE : string -> exp val letE : var -> exp -> exp -> exp diff --git a/src/ir_def/freevars.ml b/src/ir_def/freevars.ml index ae8d8309cf5..374053af34d 100644 --- a/src/ir_def/freevars.ml +++ b/src/ir_def/freevars.ml @@ -123,12 +123,13 @@ let rec exp e : f = match e.it with and actor ds fs u = close (decs ds +++ fields fs +++ system u) -and system {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; _} = +and system {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; _} = under_lambda (exp preupgrade) ++ under_lambda (exp postupgrade) ++ under_lambda (exp heartbeat) ++ under_lambda (exp timer) ++ under_lambda (exp inspect) ++ + under_lambda (exp low_memory) ++ under_lambda (exp stable_record) and exps es : f = unions exp es diff --git a/src/ir_def/ir.ml b/src/ir_def/ir.ml index 192a7453446..d2fa9a1f79a 100644 --- a/src/ir_def/ir.ml +++ b/src/ir_def/ir.ml @@ -85,6 +85,7 @@ and system = { heartbeat : exp; timer : exp; (* TODO: use an option type: (Default of exp | UserDefined of exp) option *) inspect : exp; + low_memory : exp; stable_record: exp; stable_type: Type.typ; } diff --git a/src/ir_def/rename.ml b/src/ir_def/rename.ml index f97594fba41..7cb2dd3431b 100644 --- a/src/ir_def/rename.ml +++ b/src/ir_def/rename.ml @@ -33,7 +33,7 @@ and exp' rho = function | VarE (m, i) -> VarE (m, id rho i) | LitE _ as e -> e | PrimE (p, es) -> PrimE (prim rho p, List.map (exp rho) es) - | ActorE (ds, fs, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, t) -> + | ActorE (ds, fs, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, t) -> let ds', rho' = decs rho ds in ActorE (ds', @@ -44,6 +44,7 @@ and exp' rho = function heartbeat = exp rho' heartbeat; timer = exp rho' timer; inspect = exp rho' inspect; + low_memory = exp rho' low_memory; stable_type = stable_type; stable_record = exp rho' stable_record; }, @@ -200,7 +201,7 @@ let comp_unit rho cu = match cu with | LibU (ds, e) -> let ds', rho' = decs rho ds in LibU (ds', exp rho' e) - | ActorU (as_opt, ds, fs, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type }, t) -> + | ActorU (as_opt, ds, fs, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type }, t) -> let as_opt', rho' = match as_opt with | None -> None, rho | Some as_ -> @@ -215,6 +216,7 @@ let comp_unit rho cu = match cu with heartbeat = exp rho'' heartbeat; timer = exp rho'' timer; inspect = exp rho'' inspect; + low_memory = exp rho'' low_memory; stable_record = exp rho'' stable_record; stable_type = stable_type; }, t) diff --git a/src/ir_passes/async.ml b/src/ir_passes/async.ml index b3b52989e7a..45ef1afea57 100644 --- a/src/ir_passes/async.ml +++ b/src/ir_passes/async.ml @@ -442,7 +442,7 @@ let transform prog = | (Returns | Replies), _ -> assert false end end - | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> ActorE (t_decs ds, t_fields fs, {meta; preupgrade = t_exp preupgrade; @@ -450,6 +450,7 @@ let transform prog = heartbeat = t_exp heartbeat; timer = t_exp timer; inspect = t_exp inspect; + low_memory = t_exp low_memory; stable_record = t_exp stable_record; stable_type = t_typ stable_type; }, @@ -523,7 +524,7 @@ let transform prog = and t_comp_unit = function | LibU _ -> raise (Invalid_argument "cannot compile library") | ProgU ds -> ProgU (t_decs ds) - | ActorU (args_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, t) -> + | ActorU (args_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, t) -> ActorU (Option.map t_args args_opt, t_decs ds, t_fields fs, { meta; preupgrade = t_exp preupgrade; @@ -531,6 +532,7 @@ let transform prog = heartbeat = t_exp heartbeat; timer = t_exp timer; inspect = t_exp inspect; + low_memory = t_exp low_memory; stable_record = t_exp stable_record; stable_type = t_typ stable_type; }, diff --git a/src/ir_passes/await.ml b/src/ir_passes/await.ml index 84a2170a8d2..c3896cbbfac 100644 --- a/src/ir_passes/await.ml +++ b/src/ir_passes/await.ml @@ -181,7 +181,7 @@ and t_exp' context exp = assert (not (T.is_shared_func (typ exp))); let context' = LabelEnv.singleton Return Label in FuncE (x, s, c, typbinds, pat, typs, t_exp context' exp1) - | ActorE (ds, ids, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, t) -> + | ActorE (ds, ids, { meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, t) -> ActorE (t_decs context ds, ids, { meta; preupgrade = t_exp LabelEnv.empty preupgrade; @@ -189,6 +189,7 @@ and t_exp' context exp = heartbeat = t_ignore_throw LabelEnv.empty heartbeat; timer = t_ignore_throw LabelEnv.empty timer; inspect = t_exp LabelEnv.empty inspect; + low_memory = t_ignore_throw LabelEnv.empty low_memory; stable_record = t_exp LabelEnv.empty stable_record; stable_type; }, @@ -647,20 +648,21 @@ and t_comp_unit context = function expD (c_block context' ds (tupE []) (meta (T.unit) (fun v1 -> tupE []))) ] end - | ActorU (as_opt, ds, ids, { meta = m; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, t) -> + | ActorU (as_opt, ds, ids, { meta = m; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, t) -> ActorU (as_opt, t_decs context ds, ids, { meta = m; preupgrade = t_exp LabelEnv.empty preupgrade; postupgrade = t_exp LabelEnv.empty postupgrade; heartbeat = t_ignore_throw LabelEnv.empty heartbeat; - timer = t_ignore_throw LabelEnv.empty timer; + timer = t_timer_throw LabelEnv.empty timer; inspect = t_exp LabelEnv.empty inspect; + low_memory = t_ignore_throw LabelEnv.empty low_memory; stable_record = t_exp LabelEnv.empty stable_record; stable_type; }, t) -and t_ignore_throw context exp = +and t_on_throw context exp t_exp = match exp.it with | Ir.PrimE (Ir.TupPrim, []) -> exp @@ -671,7 +673,7 @@ and t_ignore_throw context exp = (LabelEnv.add Throw (Cont throw) context) in let e = fresh_var "e" T.catch in { (blockE [ - funcD throw e (tupE[]); + funcD throw e t_exp; ] (c_exp context' exp (meta (T.unit) (fun v1 -> tupE [])))) (* timer logic requires us to preserve any source location, @@ -679,6 +681,16 @@ and t_ignore_throw context exp = with at = exp.at } +and t_ignore_throw context exp = t_on_throw context exp (tupE[]) + +(* if self-call queue full: expire global timer soon and retry *) +and t_timer_throw context exp = + t_on_throw context exp + (blockE + [expD (primE + (OtherPrim "global_timer_set") + [Mo_values.Numerics.Nat64.of_int 1 |> nat64E])] + (tupE[])) and t_prog (prog, flavor) = (t_comp_unit LabelEnv.empty prog, { flavor with has_await = false }) diff --git a/src/ir_passes/const.ml b/src/ir_passes/const.ml index 9bc4591cad1..e62e23ef575 100644 --- a/src/ir_passes/const.ml +++ b/src/ir_passes/const.ml @@ -164,7 +164,7 @@ let rec exp lvl (env : env) e : Lbool.t = surely_false | NewObjE _ -> (* mutable objects *) surely_false - | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, _typ) -> + | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, _typ) -> (* this may well be “the” top-level actor, so don’t update lvl here *) let (env', _) = decs lvl env ds in exp_ lvl env' preupgrade; @@ -172,6 +172,7 @@ let rec exp lvl (env : env) e : Lbool.t = exp_ lvl env' heartbeat; exp_ lvl env' timer; exp_ lvl env' inspect; + exp_ lvl env' low_memory; exp_ lvl env' stable_record; surely_false in @@ -228,7 +229,7 @@ and block lvl env (ds, body) = and comp_unit = function | LibU _ -> raise (Invalid_argument "cannot compile library") | ProgU ds -> decs_ TopLvl M.empty ds - | ActorU (as_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorU (as_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> let env = match as_opt with | None -> M.empty | Some as_ -> args TopLvl M.empty as_ @@ -239,6 +240,7 @@ and comp_unit = function exp_ TopLvl env' heartbeat; exp_ TopLvl env' timer; exp_ TopLvl env' inspect; + exp_ TopLvl env' low_memory; exp_ TopLvl env' stable_record let analyze ((cu, _flavor) : prog) = diff --git a/src/ir_passes/eq.ml b/src/ir_passes/eq.ml index 52486abcf4b..5987e11a3fc 100644 --- a/src/ir_passes/eq.ml +++ b/src/ir_passes/eq.ml @@ -249,7 +249,7 @@ and t_exp' env = function NewObjE (sort, ids, t) | SelfCallE (ts, e1, e2, e3, e4) -> SelfCallE (ts, t_exp env e1, t_exp env e2, t_exp env e3, t_exp env e4) - | ActorE (ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorE (ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> (* Until Actor expressions become their own units, we repeat what we do in `comp_unit` below *) let env1 = empty_env () in @@ -259,6 +259,7 @@ and t_exp' env = function let heartbeat' = t_exp env1 heartbeat in let timer' = t_exp env1 timer in let inspect' = t_exp env1 inspect in + let low_memory' = t_exp env1 low_memory in let stable_record' = t_exp env1 stable_record in let decls = eq_decls !(env1.params) in ActorE (decls @ ds', fields, @@ -268,6 +269,7 @@ and t_exp' env = function heartbeat = heartbeat'; timer = timer'; inspect = inspect'; + low_memory = low_memory'; stable_record = stable_record'; stable_type; }, @@ -301,7 +303,7 @@ and t_comp_unit = function let ds' = t_decs env ds in let decls = eq_decls !(env.params) in ProgU (decls @ ds') - | ActorU (as_opt, ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorU (as_opt, ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> let env = empty_env () in let ds' = t_decs env ds in let preupgrade' = t_exp env preupgrade in @@ -309,6 +311,7 @@ and t_comp_unit = function let heartbeat' = t_exp env heartbeat in let timer' = t_exp env timer in let inspect' = t_exp env inspect in + let low_memory' = t_exp env low_memory in let stable_record' = t_exp env stable_record in let decls = eq_decls !(env.params) in ActorU (as_opt, decls @ ds', fields, @@ -318,6 +321,7 @@ and t_comp_unit = function heartbeat = heartbeat'; timer = timer'; inspect = inspect'; + low_memory = low_memory'; stable_record = stable_record'; stable_type; }, typ) diff --git a/src/ir_passes/erase_typ_field.ml b/src/ir_passes/erase_typ_field.ml index 2b5daf75d70..f569f0ee765 100644 --- a/src/ir_passes/erase_typ_field.ml +++ b/src/ir_passes/erase_typ_field.ml @@ -126,7 +126,7 @@ let transform prog = DefineE (id, mut, t_exp exp1) | FuncE (x, s, c, typbinds, args, ret_tys, exp) -> FuncE (x, s, c, t_typ_binds typbinds, t_args args, List.map t_typ ret_tys, t_exp exp) - | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorE (ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> ActorE (t_decs ds, t_fields fs, {meta; preupgrade = t_exp preupgrade; @@ -134,6 +134,7 @@ let transform prog = heartbeat = t_exp heartbeat; timer = t_exp timer; inspect = t_exp inspect; + low_memory = t_exp low_memory; stable_record = t_exp stable_record; stable_type = t_typ stable_type; }, @@ -211,7 +212,7 @@ let transform prog = and t_comp_unit = function | LibU _ -> raise (Invalid_argument "cannot compile library") | ProgU ds -> ProgU (t_decs ds) - | ActorU (args_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, t) -> + | ActorU (args_opt, ds, fs, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, t) -> ActorU (Option.map t_args args_opt, t_decs ds, t_fields fs, { meta; preupgrade = t_exp preupgrade; @@ -219,6 +220,7 @@ let transform prog = heartbeat = t_exp heartbeat; timer = t_exp timer; inspect = t_exp inspect; + low_memory = t_exp low_memory; stable_record = t_exp stable_record; stable_type = t_typ stable_type; }, diff --git a/src/ir_passes/show.ml b/src/ir_passes/show.ml index 23f195220d7..132d1e6d5bc 100644 --- a/src/ir_passes/show.ml +++ b/src/ir_passes/show.ml @@ -291,7 +291,7 @@ and t_exp' env = function NewObjE (sort, ids, t) | SelfCallE (ts, e1, e2, e3, e4) -> SelfCallE (ts, t_exp env e1, t_exp env e2, t_exp env e3, t_exp env e4) - | ActorE (ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorE (ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> (* Until Actor expressions become their own units, we repeat what we do in `comp_unit` below *) let env1 = empty_env () in @@ -301,6 +301,7 @@ and t_exp' env = function let heartbeat' = t_exp env1 heartbeat in let timer' = t_exp env1 timer in let inspect' = t_exp env1 inspect in + let low_memory' = t_exp env1 low_memory in let stable_record' = t_exp env1 stable_record in let decls = show_decls !(env1.params) in ActorE (decls @ ds', fields, @@ -310,6 +311,7 @@ and t_exp' env = function heartbeat = heartbeat'; timer = timer'; inspect = inspect'; + low_memory = low_memory'; stable_record = stable_record'; stable_type; }, @@ -342,7 +344,7 @@ and t_comp_unit = function let ds' = t_decs env ds in let decls = show_decls !(env.params) in ProgU (decls @ ds') - | ActorU (as_opt, ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; stable_record; stable_type}, typ) -> + | ActorU (as_opt, ds, fields, {meta; preupgrade; postupgrade; heartbeat; timer; inspect; low_memory; stable_record; stable_type}, typ) -> let env = empty_env () in let ds' = t_decs env ds in let preupgrade' = t_exp env preupgrade in @@ -350,6 +352,7 @@ and t_comp_unit = function let heartbeat' = t_exp env heartbeat in let timer' = t_exp env timer in let inspect' = t_exp env inspect in + let low_memory' = t_exp env low_memory in let stable_record' = t_exp env stable_record in let decls = show_decls !(env.params) in ActorU (as_opt, decls @ ds', fields, @@ -359,6 +362,7 @@ and t_comp_unit = function heartbeat = heartbeat'; timer = timer'; inspect = inspect'; + low_memory = low_memory'; stable_record = stable_record'; stable_type }, typ) diff --git a/src/lowering/desugar.ml b/src/lowering/desugar.ml index c159b9f1270..9c21b34a3f1 100644 --- a/src/lowering/desugar.ml +++ b/src/lowering/desugar.ml @@ -367,8 +367,8 @@ and call_system_func_opt name es obj_typ = let timer = blockE [ expD T.(callE (varE (var id.it note)) [Any] - (varE (var "@set_global_timer" (Func (Local, Returns, [], [Prim Nat64], []))))) ] - (unitE ()) in + (varE (var "@set_global_timer" T.global_timer_set_type))) ] + (unitE()) in { timer with at } | "heartbeat" -> blockE @@ -424,6 +424,9 @@ and call_system_func_opt name es obj_typ = (unitE ()) (primE (Ir.OtherPrim "trap") [textE "canister_inspect_message explicitly refused message"])) + | "lowmemory" -> + awaitE T.Cmp + (callE (varE (var id.it note)) [T.scope_bound] (unitE())) | name -> let inst = match name with | "preupgrade" | "postupgrade" -> [T.scope_bound] @@ -605,13 +608,17 @@ and build_actor at ts self_id es obj_typ = | Some call -> call | None when !Mo_config.Flags.global_timer -> blockE - [ expD T.(callE (varE (var "@timer_helper" Mo_frontend.Typing.heartbeat_type)) [unit] (unitE())) ] - (unitE ()) + [ expD T.(callE (varE (var "@timer_helper" T.heartbeat_type)) [unit] (unitE())) ] + (unitE()) | None -> tupE []); inspect = (match call_system_func_opt "inspect" es obj_typ with | Some call -> call | None -> tupE []); + low_memory = + (match call_system_func_opt "lowmemory" es obj_typ with + | Some call -> call + | None -> tupE []); stable_record = with_stable_vars (fun e -> e); stable_type = ty; }, diff --git a/src/mo_frontend/typing.ml b/src/mo_frontend/typing.ml index a5a4b648b0a..53b61f7610d 100644 --- a/src/mo_frontend/typing.ml +++ b/src/mo_frontend/typing.ml @@ -386,20 +386,13 @@ let infer_mut mut : T.typ -> T.typ = (* System method types *) -let heartbeat_type = - T.(Func (Local, Returns, [scope_bind], [], [Async (Fut, Var (default_scope_var, 0), unit)])) - -let timer_type = - T.(Func (Local, Returns, [scope_bind], - [Func (Local, Returns, [], [Prim Nat64], [])], - [Async (Fut, Var (default_scope_var, 0), unit)])) - let system_funcs tfs = [ - ("heartbeat", heartbeat_type); - ("timer", timer_type); + ("heartbeat", T.heartbeat_type); + ("timer", T.timer_type); T.("preupgrade", Func (Local, Returns, [scope_bind], [], [])); T.("postupgrade", Func (Local, Returns, [scope_bind], [], [])); + ("lowmemory", T.low_memory_type); ("inspect", (let msg_typ = T.decode_msg_typ tfs in let record_typ = diff --git a/src/mo_frontend/typing.mli b/src/mo_frontend/typing.mli index be0d8b40b7e..b5081cf06ca 100644 --- a/src/mo_frontend/typing.mli +++ b/src/mo_frontend/typing.mli @@ -11,5 +11,3 @@ val infer_prog : ?viper_mode:bool -> scope -> string option -> Async_cap.async_c val check_lib : scope -> string option -> Syntax.lib -> scope Diag.result val check_actors : ?viper_mode:bool -> ?check_actors:bool -> scope -> Syntax.prog list -> unit Diag.result val check_stab_sig : scope -> Syntax.stab_sig -> (field list) Diag.result - -val heartbeat_type : typ diff --git a/src/mo_types/type.ml b/src/mo_types/type.ml index 3c0b25609ff..f46a9e79d0b 100644 --- a/src/mo_types/type.ml +++ b/src/mo_types/type.ml @@ -307,7 +307,7 @@ let compare_field f1 f2 = | {lab = l1; typ = _; _}, {lab = l2; typ = _; _} -> compare l1 l2 -(* Short-hands *) +(* Shorthands *) let unit = Tup [] let bool = Prim Bool @@ -321,6 +321,7 @@ let char = Prim Char let principal = Prim Principal let region = Prim Region + let fields flds = List.sort compare_field (List.map (fun (lab, typ) -> {lab; typ; src = empty_src}) flds) @@ -1341,6 +1342,21 @@ let default_scope_var = scope_var "" let scope_bound = Any let scope_bind = { var = default_scope_var; sort = Scope; bound = scope_bound } +(* Shorthands for replica callbacks *) + +let heartbeat_type = + Func (Local, Returns, [scope_bind], [], [Async (Fut, Var (default_scope_var, 0), unit)]) + +let global_timer_set_type = Func (Local, Returns, [], [Prim Nat64], []) + +let timer_type = + Func (Local, Returns, [scope_bind], + [global_timer_set_type], + [Async (Fut, Var (default_scope_var, 0), unit)]) + +let low_memory_type = + Func (Local, Returns, [scope_bind], [], [Async (Cmp, Var (default_scope_var, 0), unit)]) + (* Well-known fields *) let motoko_async_helper_fld = @@ -1420,8 +1436,8 @@ let canister_settings_typ = let wasm_memory_persistence_typ = sum [ - ("Keep", unit); - ("Replace", unit); + ("keep", unit); + ("replace", unit); ] let upgrade_with_persistence_option_typ = diff --git a/src/mo_types/type.mli b/src/mo_types/type.mli index b94c9eb7468..f309dfc4502 100644 --- a/src/mo_types/type.mli +++ b/src/mo_types/type.mli @@ -84,7 +84,7 @@ end val is_shared_sort : 'a shared -> bool -(* Short-hands *) +(* Shorthands *) val unit : typ val bool : typ @@ -97,6 +97,10 @@ val error : typ val char : typ val principal : typ val region : typ +val heartbeat_type : typ +val timer_type : typ +val global_timer_set_type : typ +val low_memory_type : typ val sum : (lab * typ) list -> typ val obj : obj_sort -> (lab * typ) list -> typ diff --git a/src/prelude/internals.mo b/src/prelude/internals.mo index 4e66c81ce54..d8558e8ffed 100644 --- a/src/prelude/internals.mo +++ b/src/prelude/internals.mo @@ -17,7 +17,7 @@ func @add_cycles() { let cycles = @cycles; @reset_cycles(); if (cycles != 0) { - (prim "cyclesAdd" : Nat -> ()) (cycles); + (prim "cyclesAdd" : Nat -> ()) cycles; } }; @@ -401,8 +401,8 @@ module @ManagementCanister = { }; type @WasmMemoryPersistence = { - #Keep; - #Replace; + #keep; + #replace; }; type @UpgradeOptions = { @@ -445,7 +445,7 @@ func @install_actor_helper( switch install_arg { case (#new settings) { let available = (prim "cyclesAvailable" : () -> Nat) (); - let accepted = (prim "cyclesAccept" : Nat -> Nat) (available); + let accepted = (prim "cyclesAccept" : Nat -> Nat) available; let sender_canister_version = ?(prim "canister_version" : () -> Nat64)(); @cycles += accepted; let { canister_id } = @@ -460,7 +460,7 @@ func @install_actor_helper( }; case (#upgrade actor2) { let wasm_memory_persistence = if enhanced_orthogonal_persistence { - ?(#Keep) + ?(#keep) } else { null }; @@ -507,6 +507,10 @@ func @create_actor_helper(wasm_module : Blob, arg : Blob) : async Principal = as // raw calls func @call_raw(p : Principal, m : Text, a : Blob) : async Blob { + let available = (prim "cyclesAvailable" : () -> Nat) (); + if (available != 0) { + @cycles := (prim "cyclesAccept" : Nat -> Nat) available; + }; await (prim "call_raw" : (Principal, Text, Blob) -> async Blob) (p, m, a); }; @@ -544,7 +548,7 @@ func @prune(n : ?@Node) : ?@Node = switch n { if (n.expire[0] == 0) { @prune(n.post) // by corollary } else { - ?{ n with pre = @prune(n.pre); post = @prune(n.post) } + ?{ n with pre = @prune(n.pre) } } } }; @@ -612,9 +616,25 @@ func @timer_helper() : async () { ignore (prim "global_timer_set" : Nat64 -> Nat64) exp; if (exp == 0) @timers := null; + var failed : Nat64 = 0; + func reinsert(job : () -> async ()) { + if (failed == 0) { + @timers := @prune @timers; + ignore (prim "global_timer_set" : Nat64 -> Nat64) 1 + }; + failed += 1; + @timers := ?(switch @timers { + case (?{ id = 0; pre; post; job = j; expire; delay }) + // push top node's contents into pre + ({ expire = [var failed]; id = 0; delay; job; post + ; pre = ?{ id = 0; expire; pre; post = null; delay; job = j } }); + case _ ({ expire = [var failed]; id = 0; delay = null; job; pre = null; post = @timers }) + }) + }; + for (o in thunks.values()) { switch o { - case (?thunk) ignore thunk(); + case (?thunk) try ignore thunk() catch _ reinsert thunk; case _ return } } @@ -675,5 +695,4 @@ func @cancelTimer(id : Nat) { } }; - func @set_global_timer(time : Nat64) = ignore (prim "global_timer_set" : Nat64 -> Nat64) time; diff --git a/test/bench/ok/heap-32.drun-run.ok b/test/bench/ok/heap-32.drun-run.ok index 8ae164a705f..5d3d216fe42 100644 --- a/test/bench/ok/heap-32.drun-run.ok +++ b/test/bench/ok/heap-32.drun-run.ok @@ -1,5 +1,5 @@ ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 ingress Completed: Reply: 0x4449444c0000 -debug.print: (50_227, +29_863_068, 723_684_961) +debug.print: (50_227, +29_863_068, 723_584_961) debug.print: (50_070, +32_992_212, 785_200_313) ingress Completed: Reply: 0x4449444c0000 diff --git a/test/bench/ok/region-mem.drun-run.ok b/test/bench/ok/region-mem.drun-run.ok index 28c1137fc32..3cd8271667f 100644 --- a/test/bench/ok/region-mem.drun-run.ok +++ b/test/bench/ok/region-mem.drun-run.ok @@ -1,4 +1,4 @@ ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 ingress Completed: Reply: 0x4449444c0000 -debug.print: {heap_diff = 0; instr_diff = 5_234_491_753} +debug.print: {heap_diff = 0; instr_diff = 5_240_635_753} ingress Completed: Reply: 0x4449444c0000 diff --git a/test/bench/ok/region0-mem.drun-run.ok b/test/bench/ok/region0-mem.drun-run.ok index 1d45643d325..1b1eccc80c7 100644 --- a/test/bench/ok/region0-mem.drun-run.ok +++ b/test/bench/ok/region0-mem.drun-run.ok @@ -1,4 +1,4 @@ ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 ingress Completed: Reply: 0x4449444c0000 -debug.print: {heap_diff = 0; instr_diff = 5_662_310_761} +debug.print: {heap_diff = 0; instr_diff = 5_668_454_761} ingress Completed: Reply: 0x4449444c0000 diff --git a/test/bench/ok/stable-mem.drun-run.ok b/test/bench/ok/stable-mem.drun-run.ok index 5e3eeec3bce..13861dd869a 100644 --- a/test/bench/ok/stable-mem.drun-run.ok +++ b/test/bench/ok/stable-mem.drun-run.ok @@ -1,4 +1,4 @@ ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 ingress Completed: Reply: 0x4449444c0000 -debug.print: {heap_diff = 0; instr_diff = 3_875_537_257} +debug.print: {heap_diff = 0; instr_diff = 3_881_681_257} ingress Completed: Reply: 0x4449444c0000 diff --git a/test/drun-wrapper.sh b/test/drun-wrapper.sh index 648f8b2be7e..c7bbbd6455a 100755 --- a/test/drun-wrapper.sh +++ b/test/drun-wrapper.sh @@ -37,11 +37,15 @@ export RUST_MIN_STACK=$((10*1024*1024)) # drun creates canisters with this ID: ID=rwlgt-iiaaa-aaaaa-aaaaa-cai +# encoded `{ canister_id = $ID }` +# this is useful for `ingress aaaaa-aa start/stop_canister $PRINCIPAL` +PRINCIPAL=0x4449444c016c01b3c4b1f204680100010a00000000000000000101 + if [ "${1: -5}" = ".drun" ] then # work around different IDs in ic-ref-run and drun ( echo "create" - LANG=C perl -npe 's,\$ID,'$ID',g' $1 + LANG=C perl -npe 's,\$ID,'$ID',g; s,\$PRINCIPAL,'$PRINCIPAL',g' $1 ) | drun -c "$CONFIG" --extra-batches $EXTRA_BATCHES /dev/stdin else ( echo "create" diff --git a/test/fail/ok/M0129.tc.ok b/test/fail/ok/M0129.tc.ok index 14f666c0262..aafbcc41faf 100644 --- a/test/fail/ok/M0129.tc.ok +++ b/test/fail/ok/M0129.tc.ok @@ -1 +1 @@ -M0129.mo:2.15-2.21: type error [M0129], unexpected system method named foobar, expected heartbeat or timer or preupgrade or postupgrade or inspect +M0129.mo:2.15-2.21: type error [M0129], unexpected system method named foobar, expected heartbeat or timer or preupgrade or postupgrade or lowmemory or inspect diff --git a/test/fail/ok/illegal-await.tc.ok b/test/fail/ok/illegal-await.tc.ok index 3e32e84fd34..0e695b1bc02 100644 --- a/test/fail/ok/illegal-await.tc.ok +++ b/test/fail/ok/illegal-await.tc.ok @@ -24,14 +24,14 @@ illegal-await.mo:24.11: info, start of scope $@anon-async-24.11 mentioned in err illegal-await.mo:26.5: info, end of scope $@anon-async-24.11 mentioned in error at illegal-await.mo:25.7-25.14 illegal-await.mo:22.10: info, start of scope $@anon-async-22.10 mentioned in error at illegal-await.mo:25.7-25.14 illegal-await.mo:27.3: info, end of scope $@anon-async-22.10 mentioned in error at illegal-await.mo:25.7-25.14 -illegal-await.mo:35.11-35.12: type error [M0087], ill-scoped await: expected async type from current scope $Rec, found async type from other scope $__15 +illegal-await.mo:35.11-35.12: type error [M0087], ill-scoped await: expected async type from current scope $Rec, found async type from other scope $__19 scope $Rec is illegal-await.mo:33.44-40.2 - scope $__15 is illegal-await.mo:33.1-40.2 + scope $__19 is illegal-await.mo:33.1-40.2 illegal-await.mo:33.44: info, start of scope $Rec mentioned in error at illegal-await.mo:35.5-35.12 illegal-await.mo:40.1: info, end of scope $Rec mentioned in error at illegal-await.mo:35.5-35.12 -illegal-await.mo:33.1: info, start of scope $__15 mentioned in error at illegal-await.mo:35.5-35.12 -illegal-await.mo:40.1: info, end of scope $__15 mentioned in error at illegal-await.mo:35.5-35.12 +illegal-await.mo:33.1: info, start of scope $__19 mentioned in error at illegal-await.mo:35.5-35.12 +illegal-await.mo:40.1: info, end of scope $__19 mentioned in error at illegal-await.mo:35.5-35.12 illegal-await.mo:38.20-38.21: type error [M0096], expression of type - async<$__15> () + async<$__19> () cannot produce expected type async<$Rec> () diff --git a/test/run-drun/actor-class-mgmt-enhanced.mo b/test/run-drun/actor-class-mgmt-enhanced.mo index 17a82939001..730ad569bf4 100644 --- a/test/run-drun/actor-class-mgmt-enhanced.mo +++ b/test/run-drun/actor-class-mgmt-enhanced.mo @@ -123,7 +123,7 @@ actor a { assert (c5 == c4); let c6 = await - (system Cs.C)(#upgrade_with_persistence { wasm_memory_persistence = #Keep ; canister = c5 })(6, null); + (system Cs.C)(#upgrade_with_persistence { wasm_memory_persistence = #keep ; canister = c5 })(6, null); assert ({args = 6; upgrades = 3} == (await c6.observe())); assert (c6 == c5); @@ -135,7 +135,7 @@ actor a { // no need to add cycles let c8 = await - (system Cs.C)(#upgrade_with_persistence { wasm_memory_persistence = #Replace ; canister = c7 })(8, null); + (system Cs.C)(#upgrade_with_persistence { wasm_memory_persistence = #replace ; canister = c7 })(8, null); assert ({args = 8; upgrades = 0} == (await c8.observe())); assert (c8 == c7); diff --git a/test/run-drun/call-raw.mo b/test/run-drun/call-raw.mo index c4bf3c19f08..a329e0acf1d 100644 --- a/test/run-drun/call-raw.mo +++ b/test/run-drun/call-raw.mo @@ -1,4 +1,5 @@ import P "mo:⛔"; +import Cycles = "cycles/cycles"; actor self { @@ -41,6 +42,10 @@ actor self { P.trap("ohoh"); }; + public shared func cycles() : async () { + assert P.cyclesAvailable() == 424242 + }; + public shared func supercalifragilisticexpialidocious() : async () { P.debugPrint("supercalifragilisticexpialidocious"); }; @@ -50,38 +55,38 @@ actor self { do { let arg : Blob = "DIDL\00\00"; - let res = await P.call_raw(p,"unit", arg); - assert (res == arg); + let res = await P.call_raw(p, "unit", arg); + assert res == arg; }; do { let arg : Blob = "DIDL\00\01\7c\01"; - let res = await P.call_raw(p,"int", arg); - assert (res == arg); + let res = await P.call_raw(p, "int", arg); + assert res == arg; }; do { let arg : Blob = "DIDL\00\01\7c\02"; - let res = await P.call_raw(p,"int", arg); - assert (res == arg); + let res = await P.call_raw(p, "int", arg); + assert res == arg; }; do { let arg : Blob = "DIDL\00\01\71\05\68\65\6c\6c\6f"; - let res = await P.call_raw(p,"text", arg); - assert (res == arg); + let res = await P.call_raw(p, "text", arg); + assert res == arg; }; do { let arg : Blob = "DIDL\00\03\7d\7e\79\01\01\61\00\00\00"; - let res = await P.call_raw(p,"tuple", arg); - assert (res == arg); + let res = await P.call_raw(p, "tuple", arg); + assert res == arg; }; do { let arg : Blob = "DIDL\00\01\7c\01"; try { - let res = await P.call_raw(p,"trapInt", arg); + let res = await P.call_raw(p, "trapInt", arg); assert false; } catch e { @@ -93,9 +98,15 @@ actor self { let m = "super"#"cali"#"fragilisticexpialidocious"; let arg : Blob = "DIDL\00\00"; let res = await P.call_raw(p, m, arg); - assert (res == arg); + assert res == arg; }; + do { + let arg : Blob = "DIDL\00\00"; + Cycles.add 424242; + let res = await P.call_raw(p, "cycles", arg); + assert res == arg; + } } }; diff --git a/test/run-drun/low-memory.mo b/test/run-drun/low-memory.mo new file mode 100644 index 00000000000..6b76c80ee8d --- /dev/null +++ b/test/run-drun/low-memory.mo @@ -0,0 +1,61 @@ +import Prim "mo:⛔"; +import LowMemoryActor "low-memory/low-memory-actor"; +import Cycles = "cycles/cycles"; + +actor Self { + type canister_settings = { + wasm_memory_threshold : ?Nat; + }; + + func setMemoryThreshold(a : actor {}, threshold : Nat) : async () { + let ic00 = actor "aaaaa-aa" : actor { + update_settings : shared { + canister_id : Principal; + settings : canister_settings; + } -> async (); + }; + let settings : canister_settings = { + wasm_memory_threshold = ?threshold; + }; + await ic00.update_settings({ + canister_id = Prim.principalOfActor(a); + settings; + }); + }; + + let kb = 1024; + let mb = 1024 * kb; + let gb = 1024 * mb; + let maxMemory = 4 * gb; // adjust when canister limits inspection is supported + + public shared func lowMemoryCallback() : async () { + Prim.debugPrint("Low memory callback"); + }; + + public shared func run() : async () { + Cycles.add(2_000_000_000_000); + let lowMemoryActor1 = await LowMemoryActor.LowMemoryActor(lowMemoryCallback); + // await lowMemoryActor1.allocateMemory(); + + let threshold1 = maxMemory - (await lowMemoryActor1.memorySize()) - mb : Nat; + await setMemoryThreshold(lowMemoryActor1, threshold1); + await lowMemoryActor1.allocateMemory(); + + // Not yet implemented on IC: Should retrigger when shrinking memory by reinstallation. + // let lowMemoryActor2 = await (system LowMemoryActor.LowMemoryActor)(#reinstall lowMemoryActor1)(lowMemoryCallback); + // await lowMemoryActor2.allocateMemory(); + // await lowMemoryActor2.allocateMemory(); + + // Not yet implemented on IC: Should retrigger when lowering threshold. + // let threshold2 = maxMemory - (await lowMemoryActor2.memorySize()) - mb : Nat; + // await setMemoryThreshold(lowMemoryActor2, threshold2); + // await lowMemoryActor2.allocateMemory(); + }; +}; + +//SKIP run +//SKIP run-low +//SKIP run-ir +//SKIP ic-ref-run + +//CALL ingress run "DIDL\x00\x00" diff --git a/test/run-drun/low-memory/low-memory-actor.mo b/test/run-drun/low-memory/low-memory-actor.mo new file mode 100644 index 00000000000..b059518bd4b --- /dev/null +++ b/test/run-drun/low-memory/low-memory-actor.mo @@ -0,0 +1,27 @@ +import Prim "mo:⛔"; + +actor class LowMemoryActor(callback : shared () -> async ()) { + system func lowmemory() : async* () { + Prim.debugPrint("Low memory!"); + await callback(); + Prim.debugPrint("Low memory callback done"); + }; + + type Node = { + array : [var Nat]; + next : ?Node; + }; + + var root : ?Node = null; + + public func allocateMemory() : async () { + let array = Prim.Array_init(8 * 1024 * 1024, 0); // 32 GB on 32-bit, 64 GB on 64-bit. + let node : Node = { array; next = root }; + root := ?node; + }; + + public func memorySize() : async Nat { + await async {}; // Allocate GC reserve (because of `--force-gc` flag during drun testing). + Prim.rts_memory_size(); + }; +}; diff --git a/test/run-drun/map-mixed-upgrades/map0.mo b/test/run-drun/map-mixed-upgrades/map0.mo index 96ac9dea3a9..3a73ef81682 100644 --- a/test/run-drun/map-mixed-upgrades/map0.mo +++ b/test/run-drun/map-mixed-upgrades/map0.mo @@ -68,7 +68,7 @@ actor a { case null {}; case (?n) { nodes[i] := - ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #Keep; canister = n })(i)); // upgrade! + ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #keep; canister = n })(i)); // upgrade! } } } @@ -80,7 +80,7 @@ actor a { case null {}; case (?n) { nodes[i] := - ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #Replace; canister = n })(i)); // upgrade! + ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #replace; canister = n })(i)); // upgrade! } } } diff --git a/test/run-drun/map-mixed-upgrades/map1.mo b/test/run-drun/map-mixed-upgrades/map1.mo index f85e7439064..60c7f00fc37 100644 --- a/test/run-drun/map-mixed-upgrades/map1.mo +++ b/test/run-drun/map-mixed-upgrades/map1.mo @@ -79,7 +79,7 @@ actor a { case null {}; case (?n) { nodes[i] := - ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #Keep; canister = n })(i)); // upgrade! + ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #keep; canister = n })(i)); // upgrade! } } } @@ -91,7 +91,7 @@ actor a { case null {}; case (?n) { nodes[i] := - ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #Replace; canister = n })(i)); // upgrade! + ? (await (system Lib.Node)(#upgrade_with_persistence { wasm_memory_persistence = #replace; canister = n })(i)); // upgrade! } } } diff --git a/test/run-drun/migration-paths/new-installer.mo b/test/run-drun/migration-paths/new-installer.mo index de73aae5e23..777fda0ab5d 100644 --- a/test/run-drun/migration-paths/new-installer.mo +++ b/test/run-drun/migration-paths/new-installer.mo @@ -19,7 +19,7 @@ actor { switch testCanister { case null Prim.trap("null canister"); case (?canister) { - ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #Keep; canister })(); + ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #keep; canister })(); Prim.debugPrint("Upgraded (keep main memory)"); }; }; @@ -29,7 +29,7 @@ actor { switch testCanister { case null Prim.trap("null canister"); case (?canister) { - ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #Replace; canister })(); + ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #replace; canister })(); Prim.debugPrint("Upgraded (replace main memory)"); }; }; diff --git a/test/run-drun/migration-paths/old-installer.mo b/test/run-drun/migration-paths/old-installer.mo index 461e2f459e7..a5708fca3d7 100644 --- a/test/run-drun/migration-paths/old-installer.mo +++ b/test/run-drun/migration-paths/old-installer.mo @@ -31,7 +31,7 @@ actor installer { switch testCanister { case null Prim.trap("null canister"); case (?canister) { - ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #Keep; canister })(); + ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #keep; canister })(); Prim.debugPrint("Upgraded (keep main memory)"); } } @@ -41,7 +41,7 @@ actor installer { switch testCanister { case null Prim.trap("null canister"); case (?canister) { - ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #Replace; canister })(); + ignore await (system TestCanister.TestCanister)(#upgrade_with_persistence { wasm_memory_persistence = #replace; canister })(); Prim.debugPrint("Upgraded (replace main memory)"); } } diff --git a/test/run-drun/ok/call-raw.tc.ok b/test/run-drun/ok/call-raw.tc.ok index aaa6053f34c..86e626056c8 100644 --- a/test/run-drun/ok/call-raw.tc.ok +++ b/test/run-drun/ok/call-raw.tc.ok @@ -1,2 +1,2 @@ -call-raw.mo:40.30-40.31: warning [M0194], unused identifier n (delete or rename to wildcard `_` or `_n`) -call-raw.mo:84.13-84.16: warning [M0194], unused identifier res (delete or rename to wildcard `_` or `_res`) +call-raw.mo:41.30-41.31: warning [M0194], unused identifier n (delete or rename to wildcard `_` or `_n`) +call-raw.mo:89.13-89.16: warning [M0194], unused identifier res (delete or rename to wildcard `_` or `_res`) diff --git a/test/run-drun/ok/low-memory.drun-run.ok b/test/run-drun/ok/low-memory.drun-run.ok new file mode 100644 index 00000000000..8727e74bd91 --- /dev/null +++ b/test/run-drun/ok/low-memory.drun-run.ok @@ -0,0 +1,6 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: Low memory! +debug.print: Low memory callback +debug.print: Low memory callback done +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/tmp_upgrade.drun.ok b/test/run-drun/ok/tmp_upgrade.drun.ok new file mode 100644 index 00000000000..c76c471c7d5 --- /dev/null +++ b/test/run-drun/ok/tmp_upgrade.drun.ok @@ -0,0 +1,3 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/try-finally-trap.drun.ok b/test/run-drun/ok/try-finally-trap.drun.ok index 80205580c08..34f5a735fba 100644 --- a/test/run-drun/ok/try-finally-trap.drun.ok +++ b/test/run-drun/ok/try-finally-trap.drun.ok @@ -1,5 +1,9 @@ ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 ingress Completed: Reply: 0x4449444c0000 +debug.print: {rts_callback_table_count = 0} +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {rts_callback_table_count = 0} ingress Completed: Reply: 0x4449444c0000 debug.print: trap in finally! ingress Err: IC0503: Error from Canister rwlgt-iiaaa-aaaaa-aaaaa-cai: Canister called `ic0.trap` with message: assertion failed at try-finally-trap.mo:8.7-8.19 @@ -13,3 +17,8 @@ ingress Err: IC0503: Error from Canister rwlgt-iiaaa-aaaaa-aaaaa-cai: Canister c Consider gracefully handling failures from this canister or altering the canister to handle exceptions. See documentation: http://internetcomputer.org/docs/current/references/execution-errors#trapped-explicitly debug.print: {rts_callback_table_count = 1} ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {rts_callback_table_count = 0} +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/tmp_upgrade.drun b/test/run-drun/tmp_upgrade.drun new file mode 100644 index 00000000000..75542076835 --- /dev/null +++ b/test/run-drun/tmp_upgrade.drun @@ -0,0 +1,3 @@ +# SKIP ic-ref-run +install $ID tmp_upgrade/version0.mo "" +upgrade $ID tmp_upgrade/version1.mo "" diff --git a/test/run-drun/tmp_upgrade/version0.mo b/test/run-drun/tmp_upgrade/version0.mo new file mode 100644 index 00000000000..bec0a350e1a --- /dev/null +++ b/test/run-drun/tmp_upgrade/version0.mo @@ -0,0 +1,6 @@ +persistent actor { + + var four : [var (Nat, Text)] = [var]; + var zero = 0; + +} diff --git a/test/run-drun/tmp_upgrade/version1.mo b/test/run-drun/tmp_upgrade/version1.mo new file mode 100644 index 00000000000..95b855fdec4 --- /dev/null +++ b/test/run-drun/tmp_upgrade/version1.mo @@ -0,0 +1,6 @@ +persistent actor { + + var three : [var (Nat, Text)] = [var]; + var zero = 0; + +} diff --git a/test/run-drun/try-finally-trap.drun b/test/run-drun/try-finally-trap.drun index 74ab7a2ae62..62b091f96c8 100644 --- a/test/run-drun/try-finally-trap.drun +++ b/test/run-drun/try-finally-trap.drun @@ -1,6 +1,12 @@ install $ID try-finally-trap/try-finally-trap.mo "" +ingress $ID show "DIDL\x00\x00" upgrade $ID try-finally-trap/try-finally-trap.mo "" +ingress $ID show "DIDL\x00\x00" ingress $ID go "DIDL\x00\x00" ingress $ID show "DIDL\x00\x00" upgrade $ID try-finally-trap/try-finally-trap.mo "" ingress $ID show "DIDL\x00\x00" +ingress aaaaa-aa stop_canister $PRINCIPAL +upgrade $ID try-finally-trap/try-finally-trap.mo "" +ingress aaaaa-aa start_canister $PRINCIPAL +ingress $ID show "DIDL\x00\x00" diff --git a/test/run/top-bit.mo b/test/run/top-bit.mo new file mode 100644 index 00000000000..7981ea7a0db --- /dev/null +++ b/test/run/top-bit.mo @@ -0,0 +1,12 @@ +//CLASSICAL-PERSISTENCE-ONLY + +var x : Nat32 = 4294967295; +assert (x >> 31 != 0); +assert (x & 2147483648 != 0); + +// CHECK-LABEL: (func $init +// CHECK: i32.clz +// CHECK-NEXT: if ;; +// CHECK: i32.clz +// CHECK-NEXT: if ;; +// CHECK: end)