From 013dfb046d6eeb92d53c213c6627879cccfc9acc Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Thu, 26 Aug 2021 05:28:36 -0700 Subject: [PATCH 01/56] niv ic: update e3625301 -> 35dd8f93 (#2751) ## Changelog for ic: Branch: master Commits: [dfinity/ic@e3625301...35dd8f93](https://github.com/dfinity/ic/compare/e362530172c44679313b1b7fca1e90d8967545d8...35dd8f93dec82662ed4df35664a9c0be6dbf203a) * [`35dd8f93`](https://github.com/dfinity/ic/commit/35dd8f93dec82662ed4df35664a9c0be6dbf203a) Update from revision 8a5b9a2e1468dfb286c77084a9b3597b9e3993b5 --- nix/drun.nix | 8 ++++++-- nix/sources.json | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/nix/drun.nix b/nix/drun.nix index 6a628d4f6f0..18ef2f16820 100644 --- a/nix/drun.nix +++ b/nix/drun.nix @@ -5,8 +5,12 @@ pkgs: src = pkgs.sources.ic + "/rs"; - # update this after dependency changes - cargoSha256 = "0xg4wf6m103kn0kqgc4bfdz343lwhi5d3lx14xhq3b5jrnzk30a4"; + # update this after bumping the dfinity/ic pin. + # 1. change the hash to something arbitrary (e.g. flip one digit to 0) + # 2. run nix-build -A drun nix/ + # 3. copy the “expected” hash from the output into this file + # 4. commit and push + cargoSha256 = "0656lxdlr05cjkla1blvpqlxywk7shasiwmycz10nqykdrs4gfgf"; nativeBuildInputs = with pkgs; [ pkg-config diff --git a/nix/sources.json b/nix/sources.json index e67a2310aa0..b789b459c01 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -27,10 +27,10 @@ "homepage": "", "owner": "dfinity", "repo": "ic", - "rev": "e362530172c44679313b1b7fca1e90d8967545d8", - "sha256": "1nn979mhwki4gg4j1xpy8i93hhnzvf1vdq0hmp0jf55gii4kmzs9", + "rev": "35dd8f93dec82662ed4df35664a9c0be6dbf203a", + "sha256": "0w6rz1s43i87abwf3fhi121ym2qxgirvbycsg5ykcz1019gdz803", "type": "tarball", - "url": "https://github.com/dfinity/ic/archive/e362530172c44679313b1b7fca1e90d8967545d8.tar.gz", + "url": "https://github.com/dfinity/ic/archive/35dd8f93dec82662ed4df35664a9c0be6dbf203a.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "ic-hs": { From 2734414efcf9f4ae70fa9d1c1367f196c607b5dc Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Fri, 27 Aug 2021 12:19:16 +0200 Subject: [PATCH 02/56] Nix: Drop ocaml-vlq patch, applied upstream (#2752) --- nix/default.nix | 1 - nix/patches/135687.patch | 57 ---------------------------------------- nix/sources.json | 6 ++--- 3 files changed, 3 insertions(+), 61 deletions(-) delete mode 100644 nix/patches/135687.patch diff --git a/nix/default.nix b/nix/default.nix index 8b9f5069982..92351127475 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -15,7 +15,6 @@ let src = nixpkgs_src; patches = [ ./patches/124498.patch - ./patches/135687.patch # ocaml-vlq ]; }; diff --git a/nix/patches/135687.patch b/nix/patches/135687.patch deleted file mode 100644 index c118c3b8a78..00000000000 --- a/nix/patches/135687.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 322955b6f0d2dd0836bd3048eda9f81b4165daf4 Mon Sep 17 00:00:00 2001 -From: Joachim Breitner -Date: Wed, 25 Aug 2021 20:26:02 +0200 -Subject: [PATCH] ocamlPackages.vlq: init at 0.2.1 - ---- - .../development/ocaml-modules/vlq/default.nix | 27 +++++++++++++++++++ - pkgs/top-level/ocaml-packages.nix | 2 ++ - 2 files changed, 29 insertions(+) - create mode 100644 pkgs/development/ocaml-modules/vlq/default.nix - -diff --git a/pkgs/development/ocaml-modules/vlq/default.nix b/pkgs/development/ocaml-modules/vlq/default.nix -new file mode 100644 -index 00000000000000..8f0f3a8682820f ---- /dev/null -+++ b/pkgs/development/ocaml-modules/vlq/default.nix -@@ -0,0 +1,27 @@ -+{ lib, buildDunePackage, fetchFromGitHub -+, dune-configurator -+}: -+ -+buildDunePackage rec { -+ pname = "vlq"; -+ version = "0.2.1"; -+ -+ src = fetchFromGitHub { -+ owner = "flowtype"; -+ repo = "ocaml-vlq"; -+ rev = "v${version}"; -+ sha256 = "0lgk8j54gsb2jjh4ih72ladlrdxbpbnl0kv5hax696qmznfm8zqf"; -+ }; -+ -+ useDune2 = true; -+ -+ buildInputs = [ dune-configurator ]; -+ -+ meta = { -+ description = "encoding variable-length quantities, in particular base64"; -+ license = lib.licenses.mit; -+ inherit (src.meta) homepage; -+ maintainers = [ lib.maintainers.nomeata ]; -+ }; -+ -+} -diff --git a/pkgs/top-level/ocaml-packages.nix b/pkgs/top-level/ocaml-packages.nix -index 160c810840e7dd..364aed68e22f71 100644 ---- a/pkgs/top-level/ocaml-packages.nix -+++ b/pkgs/top-level/ocaml-packages.nix -@@ -1304,6 +1304,8 @@ let - - vg = callPackage ../development/ocaml-modules/vg { }; - -+ vlq = callPackage ../development/ocaml-modules/vlq { }; -+ - visitors = callPackage ../development/ocaml-modules/visitors { }; - - wasm = callPackage ../development/ocaml-modules/wasm { }; diff --git a/nix/sources.json b/nix/sources.json index b789b459c01..5dc5629ee95 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -116,10 +116,10 @@ "homepage": null, "owner": "NixOS", "repo": "nixpkgs", - "rev": "dcb27f623fe9b2bb1275029af3148ca7156ede2d", - "sha256": "08sdvg2qr801a0v465zyrrbi028b3dm2adb320v7an4878z05nkl", + "rev": "b720376c7c4da1663e155b0b2142348c8caddae9", + "sha256": "1jvrdgrz31dxmhzkdrvxk4hzhajc0404is9s0w35fcmmay9rk9yc", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/dcb27f623fe9b2bb1275029af3148ca7156ede2d.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/b720376c7c4da1663e155b0b2142348c8caddae9.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs-mozilla": { From 10d1a5dbe45dfdd3fd12df4c218c7b0a4af43c96 Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Mon, 30 Aug 2021 20:03:39 +0100 Subject: [PATCH 03/56] Motoko Intro slides (#2732) More comprehensive slides, in adoc, with code that can be run in the online interpreter. Derived from and extending the original overview-slides.md --- doc/modules/language-guide/lang-nav.adoc | 1 + .../language-guide/pages/overview.adoc | 1007 +++++++++++++++++ 2 files changed, 1008 insertions(+) create mode 100644 doc/modules/language-guide/pages/overview.adoc diff --git a/doc/modules/language-guide/lang-nav.adoc b/doc/modules/language-guide/lang-nav.adoc index 1a4257460fe..ac19b81110c 100644 --- a/doc/modules/language-guide/lang-nav.adoc +++ b/doc/modules/language-guide/lang-nav.adoc @@ -20,4 +20,5 @@ ** xref:language-manual.adoc[Language quick reference] ** xref:compiler-ref.adoc[Compiler reference] ** xref:motoko-grammar.adoc[Motoko grammar] +** xref:overview.adoc[Overview] ** xref:style.adoc[Motoko style guidelines] diff --git a/doc/modules/language-guide/pages/overview.adoc b/doc/modules/language-guide/pages/overview.adoc new file mode 100644 index 00000000000..77a0255e40a --- /dev/null +++ b/doc/modules/language-guide/pages/overview.adoc @@ -0,0 +1,1007 @@ +== Overview + +=== Motivation and Goals + +A simple, useful language for the Internet Computer (IC) + +* Familiar syntax +* Safe by default +* Incorporating *actor* model for canisters +* Seamless integration of IC features +* Making most of present and future WebAssembly + +=== Key Design Points + +* Object-oriented, functional & imperative +* Objects as records of functions +* `async`/`await` for sequential programming of asynchronous messaging +* Structural typing with simple generics and subtyping +* Safe arithmetic (both unbounded and checked) +* Non-nullable types by default +* Garbage collected (no manual memory management) +* JavaScript-like syntax but statically typed & sane + +Inspirations: Java, JavaScript, C#, Swift, Pony, ML, Haskell + +=== Semantics + +* call-by-value (like Java, C, JS, ML; unlike Haskell) +* declarations are locally mutually recursive +* parametric, bounded polymorphism +* subtyping as zero-cost subsumption, not coercion +* no dynamic casts +* no inheritance + +=== Implementation(s) + +* implemented in OCaml (leverages `wasm` libary) +* simple reference interpreter +* less simple compiler to WebAssembly +** multipass with typed IR in each pass. +** uniform representation, unboxed arithmetic +** two-space GC or mark-compact GC, invoked after messages (for now) +* polymorphism by erasure + +== The language + +=== Expressions + +* Identifiers: + +`x`, `foo_bar`, `test123`, `List`, `Map` +* Parentheses `( … )` for grouping +* Braces `{ … }` for scoping (and records) +* `;` for sequencing +* Type annotations (to help type inference): + +`(42 : Int)` + + (zero cost) + +=== Libraries + +[source#impDebugInt, motoko] +.... + import Debug "mo:base/Debug"; + import Int "mo:base/Int"; +.... + +(`import MyLib "src/MyLib"` imports a library from the local file system.) + +=== Libraries + +[source, motoko] +.... + import Debug "mo:base/Debug"; + import Int "mo:base/Int"; + import Trie "mo:base/Trie"; + + type Users = Trie.Trie; // reference types + + Debug.print(Int.toText(7)); // reference functions/values +.... + +=== Blocks and declarations + +[source.include_impDebugInt, motoko] +.... + type Delta = Nat; + func print() { + Debug.print(Int.toText(counter)); + }; + let d : Delta = 42; + var counter = 1; + counter := counter + d; + print(); +.... + +* Semicolon after each declaration! +* Mutually recursive +* Mutable variables marked explicitly + +=== Control flow + +The usual suspects... + +* `do { … }` +* `if b …` +* `if b … else …` +* `switch e { case pat1 e1; …; case _ en }` +* `while b …` +* `loop …` +* `loop … while b` +* `for (pat in e) …` +* `return`, `return e` +* `label l e`, `break l e` +* `do ? { … e! … }` +* `async e`, `await e` _(restricted)_ +* `throw`, `try … catch x { … }` _(restricted)_ + +== Primitive types + +=== Unbounded integers + +`Int` + +`{ ..., -2, 1, 0, 1, 2, ... }` + +Inferred by default for negative literals. + +Literals: `13`, `0xf4`, `-20`, `+1`, `1_000_000` + +=== Unbounded naturals + +`Nat` + +`{ 0, 1, 2, ... }` + +Non-negative, trap on underflow. + +Inferred by default for non-negative literals + +Literals: `13`, `0xf4`, `1_000_000` + +`Nat <: Int` + +`Nat` is a _subtype_ of `Int` + +(you can supply a `Nat` wherever an `Int` is expected) + +=== Bounded numbers (trapping) + +`Nat8`, `Nat16`, `Nat32`, `Nat64`, `Int8`, `Int16`, `Int32`, `Int64` + +Trap on over- and underflow; wrap-around and bit-manipulating +operations available separately + +Needs type annotations (somewhere) + +Literals: `13`, `0xf4`, `-20`, `1_000_000` + +=== Floating point numbers + +`Float` + +IEEE 754 double precision (64 bit) semantics, normalized NaN + +Inferred for fractional literals + +Literals: 0, -10, `2.71`, `-0.3e+15`, `3.141_592_653_589_793_12` + +=== Numeric operations + +No surprises here + +`- x` + +`a + b` + +`a % b` + +`a & b` + +`a << b` + +… + +`a +% b, a -% b, …` for wrapping, modular arithmetic (where appropriate) + +=== Characters and Text + +`Char`, `Text` + +Unicode! Character = Unicode scalar value; no random access on text + +* `'x'`, `+'\u{6a}'+`, `'☃'`, +* `"boo"`, `+"foo \u{62}ar ☃"+` +* `"Concat" # "enation"` + +=== Booleans + +`Bool` + +Literals: `true`, `false` + +`a or b` + +`a and b` + +`not b` + +`if (b) e1 else e2` + +== Functions + +=== Function types + +* Simple functions: ++ +[source.no-repl, motoko] +.... +Int.toText : Int -> Text +.... +* multiple arguments and return values ++ +[source.no-repl, motoko] +.... +divRem : (Int, Int) -> (Int, Int) +.... +* can be generic/polymorphic ++ +[source.no-repl, motoko] +.... +Option.unwrapOr : (?T, default : T) -> T +.... +* first-class (can be passed around, stored) ++ +[source.no-repl, motoko] +.... +map : (f : A -> B, xs : [A]) -> [B] +let funcs : [(T) -> T] = … +.... + +=== Function Declarations & Use + +[source.include_impDebugInt, motoko] +.... +func add(x : Int, y : Int) : Int = x + y; + +func applyNTimes(n : Int, x : T, f : T -> ()) { + if (n <= 0) return; + f(x); + applyNTimes(n-1, x, f); +}; + +applyNTimes(3, "Hello!", func(x) { Debug.print(x) } ); +.... + +* `func() { … }` short for `func() : () = { … }` +* Parametric functions +* Type instantiations may sometimes be omitted +* Anonymous functions (a.k.a. lambdas) + +== Composite types + +=== Tuples + +`(Bool, Float, Text)` + +immutable, heterogeneous, fixed size + +[source#tuple, motoko] +.... +let tuple = (true or false, 0.6 * 2.0, "foo" # "bar"); +.... + +[source.include_tuple, motoko] +.... +tuple.1; +.... + +[source.include_tuple, motoko] +.... +let (_,_,t) = tuple; +t +.... + +=== Options + +`?Text` + +is either a value of that type, e.g. `?"hello"`, or `null`. + + +[source#display, motoko] +.... +func display(x : ?Text) : Text { + switch x { + case (null) { "No value" }; + case (?y) { "Value: " # y }; + }; +}; +.... + +[source.include_display, motoko] +.... +(display(null), display(?"Test")) +.... + +=== Option blocks + +Switching on every option value can be inconvenient ... + + +The _option block_, `do ? { … }`, allow you to safely access option values with a postfix _null break_ `!` expression. + +Within `do ? { … }`, which returns an option, +the expression `e!` immediately exits the block with `null` when the value of option `e` is `null` or continues with the option's contents. + +[source, motoko] +.... +func add(x : ?Nat, y: ?Nat) : ?Nat { + do ? { x! + y! }; +}; + +(add(null, null), add (?1,null), add (?1,?2), add (null,?2)); +.... + +=== Arrays (immutable) + +`[Text]` + +[source.include_impDebugInt, motoko] +.... +let days = [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ]; + +assert(days.size() == 7); + +assert(days[1] == "Tue"); + +// days[7] will trap (fixed size) + +for (d in days.vals()) { Debug.print(d) }; +.... + +=== Arrays (mutable) + +`[var Nat]` + +[source, motoko] +.... +let counters = [var 1, 2, 3]; + +assert(counters.size() == 3); + +counters[1] := counters[1] + 1; + +// counters[3] will trap (fixed size) + +counters; +.... + +=== Records + +`{first : Text; last : Text; salary : var Nat}` + +[source.include_impDebugInt, motoko] +.... +let employee = {first = "John"; last = "Doe"; var salary = 81_932}; + +Debug.print( + employee.first # " " # employee.last # " earns " # + Int.toText(employee.salary) # " pounds." +); + +employee.salary += 79_496; + +employee; +.... + +=== Objects + +`{first : Text; last : Text; get : () -> Nat; add : Nat -> ()}` + +[source, motoko] +.... +object self { + public let first = "John"; + public let last = "Doe"; + var salary : Nat = 81_932; // private by default + public func get() : Nat = salary; + public func add(bump : Nat) { salary += bump }; +} +.... + +=== Classes + +[source, motoko] +.... +class Employee(fst : Text, lst : Text) { + public let first = fst; + public let last = lst; + var salary : Nat = 0; + public func get() : Nat = salary; + public func add(bump : Nat) { salary += bump }; +} +.... + +Classes are factories for constructing objects. + +A class introduces a type and a function (for constructing instances). + +Just sugar for: + +[source.no-repl, motoko] +.... +type Employee = {first : Text; last : Text; get : () -> Nat; add : Nat -> ()}; + +func Employee(fst : Text, lst : Text) : Employee = object { … } +.... + + +=== Variants +`{#Sun; #Mon; #Tue; #Wed; #Thu; #Fri; #Sat}` + +[source, motoko] +.... +type Day = {#Sun; #Mon; #Tue; #Wed; #Thu; #Fri; #Sat}; + +func toText(d : Day) : Text { + switch d { + case (#Sun) "Sunday"; + case (#Mon) "Monday"; + case (#Tue) "Tuesday"; + case (#Wed) "Wednesday"; + case (#Thu) "Thursday"; + case (#Fri) "Friday"; + case (#Sat) "Saturday"; + }; +}; + +func sort(d : Day) : { #WeekDay; #WeekEnd } { + switch d { + case (#Sun or #Sat) #WeekEnd; // or pattern + case _ #WeekDay; // wildcard pattern + }; +}; +.... + +=== Recursive Types + +[source#Lists, motoko] +.... +type List = { + #item : {head : Text; tail : List}; // variant with payload! + #empty // ^^^^ recursion! +}; + +func reverse(l : List) : List { + func rev(l : List, r : List) : List { + switch l { + case (#empty) { r }; + case (#item { head; tail }) { // nested patterns + rev(tail, #item {head; tail = r}) + } + } + }; + rev(l, #empty); +}; + +let l = reverse(#item {head = "A"; tail = #item {head = "B"; tail = #empty}}); +.... + +=== Generic types + +[source, motoko] +.... +type List = { + #item : {head : T; tail : List}; + #empty +}; + +func reverse(l : List) : List { + func rev(l : List, r : List) : List { + switch l { + case (#empty) { r }; + case (#item { head; tail }) { // a nested pattern + rev(tail, #item {head; tail = r}) + } + } + }; + rev(l, #empty); +}; + +let s : List = + reverse(#item {head = "A"; tail = #item {head = "B"; tail = #empty}}); + +let ns : List = + reverse(#item {head = 0; tail = #item {head = 1; tail = #empty}}) +.... + +== Packages and modules + +=== Modules + +[source.no-repl, motoko] +.... +// the type of base/Int.mo +module { + type Int = Prim.Types.Int; + toText : Int -> Text; + abs : Int -> Nat; + // ... +} +.... + +modules contain named types and values (like objects), + +but are restricted to _static_ content (pure, no state, …) + +=== Module imports + +[source.no-repl, motoko] +.... +import Debug "mo:base/Debug"; // import from package +import Int "mo:base/Int"; +import MyLib "lib/MyLib"; // import from local file MyLib.mo +.... + +`base` package provides basic features as separate modules. + +More libraries popping up! + +`MyLib.mo` _must_ contain a module or actor class, eg: + +[source.no-repl, motoko] +.... +module { + public type List = …; + + public func reverse(l : List) : List { … }; +} +.... + +== Platform features + +=== Actor types + +Like object types, but marked as `actor`: + +[source#actorTypes, motoko] +.... +type Broadcast = actor { + register : Receiver -> (); + send : Text -> async Nat; +}; + +type Receiver = actor { + recv : query Text -> async Nat +}; +.... + +_sharable_ arguments and _no_ or _async_ result type. + +* `register` is a _oneway_ IC method (unawaitable). +* `send` is an IC _update_ method +* `recv` is IC _query_ method + +IC canister ≈ Motoko actor + +=== sharable ≈ serializable + +**Sharable:** + +* all primitive types +* records, tuples, arrays, variants, options + +with immutable sharable components +* `actor` types +* `shared` function type + +**Not sharable:** + +* mutable things +* local functions +* objects (with methods) + +=== A complete actor + +[source, motoko] +.... +import Array "mo:base/Array"; + +actor Broadcast { + type Receiver = actor {recv : query Text -> async Nat}; + + var r : [Receiver] = []; + + public func register(a : Receiver) { + r := Array.append(r, [a]); + }; + + public func send(t : Text) : async Nat { + var sum = 0; + for (a in r.vals()) { + sum += await a.recv(t); + }; + return sum; + }; +} +.... + +a typical canister main file + +=== Async/await + +`async T` + +asychronous future or promise + +introduced by `async { … }` + +(implicit in async function declaration) + +`await e` + +suspends computation pending `e`’s result: + +if the result is a value, continues with that value, + +if the result is an `Error`, ``throw``s the error. + +[source.no-repl, motoko] +.... + public func send(t : Text) : async Nat { + var sum = 0; + for (a in r.vals()) { + sum += await a.recv(t); // may return Nat or `throw` error + }; + return sum; + }; +.... + +(Errors can be handled using `try … catch …`) + +=== Concurrency Hazards + +Functions that `await` are _not_ atomic. + +Suspension introduces _concurrency hazards_. + +A bad implementation of `send`: +[source.no-repl, motoko] +.... + var sum = 0; // shared state! + public func send(t : Text) : async Nat { + sum := 0; + for (a in r.vals()) { + sum += await a.recv(t); + }; + return sum; + }; +.... + +(Concurrent ``send``s will share and clobber `sum`.) + +Beware of race conditions! + +=== Actor import + +[source, motoko] +.... +import Broadcast "canister:Broadcast"; +/* or +import Broadcast "ic:r7inp-6aaaa-aaaaa-aaabq-cai"; +*/ +actor Self { + + var count = 0; + + public func go() { + Broadcast.register(Self); + }; + + public query func recv(msg : Text) : async Nat { + return count; + } +} +.... + +(assumes there is a Candid file describing the interface of the import) + + +=== A Candid interface file + +``Broadcast``'s Candid file (produced by `moc --idl Broadcast.mo` compiler). + +Broadcast.did: +[source, candid] +.... +type Receiver = + service { + recv: (text) -> (nat) query; + }; +service : { + register: (Receiver) -> () oneway; + send: (text) -> (nat); +} +.... + +A language independent interface definition. + +Could just as easily describe a Rust implementation of `Broadcast`. + +=== Principal and caller + +[source, motoko] +.... +import Principal "mo:base/Principal"; + +actor Self { + + public shared(context) func hello() : async Text { + let myself : Principal = Principal.fromActor(Self); + if (context.caller == myself) { + "Talking to yourself is the first sign of madness"; + } else { + "Hello, nice to see you"; + }; + }; + +} +.... + + +=== Errors + +// breaks interpreter +[source.no-repl, motoko] +.... +import Principal "mo:base/Principal"; +import Error "mo:base/Error"; + +actor Self { + + public shared(context) func hello() : async Text { + let myself : Principal = Principal.fromActor(Self); + if (context.caller == myself) { + throw Error.reject("Talking to yourself is the first sign of madness"); + } else { + "Hello, nice to see you"; + }; + }; + +}; + +async { + let t = try Self.hello() catch (e) { Error.message(e); } +}; +.... + +Similar to exceptions in other languages, + +but _only_ available in async contexts, e.g. shared functions; async blocks + +=== Stable variables + +If we upgrade the `Broadcast` actor, all current registrations are lost. + +To preserve them, declare the state variable `r` as `stable`. + +[source.no-repl, motoko] +.... +import Array "mo:base/Array"; + +actor Broadcast { + + type Receiver = actor {recv : query Text -> async Nat}; + + stable var r : [Receiver] = []; // declare r `stable` + + public func register(a : Receiver) { … } + public func send(t : Text) : async Nat { … } + + // optional pre-upgrade action + system func preupgrade() { Debug.print("saving receivers"); } + + // optional post-upgrade action + system func postupgrade() { Debug.print("restoring receivers"); } +} +.... + +stable variables must have _stable_ types (see manual) + +`system` hooks can't send messages + +== Type system + +=== Structural + +[source.include_Lists, motoko] +.... +/* +type List = { + #item : {head : Text; tail : List}; + #empty +}; + +func reverse(l : List) : List { //... }; +*/ +type Stack = { + #empty; + #item : {tail : Stack; head : Text}; +}; + +let stack : Stack = #empty; + +let revStack = reverse(stack); // works though reverse defined on List (not Stack) +.... + +Type definitions + +do not create types, + +but name existing types + +Despite their different names, `Stack` and `List` are equivalent types. + +=== Subtyping (Variants) + +`WeekDay <: Day` + +[source, motoko] +.... +type WeekDay = {#Mon; #Tue; #Wed; #Thu; #Fri}; + +type Day = {#Sun; #Mon; #Tue; #Wed; #Thu; #Fri; #Sat}; + +func toText(d : Day) : Text { + switch d + { case (#Sun) "Sunday"; + case (#Mon) "Monday"; + //... + }; +}; + +let mon : WeekDay = #Mon; +let t = toText(mon); // also works, since WeekDay <: Day +.... + +`t1 <: t2`: `t1` can be used wherever `t2` is expected + +[source, motoko] +=== Subtyping (Records) + +`Employee <: Person` + +[source, motoko] +.... + +type Employee = {first : Text; last : Text; var salary : Nat}; +type Person = {first : Text; last : Text}; + +func toText(p : Person) : Text { + p.last # "," # p.first; +}; + +let employee : Employee = + { first = "John"; last = "Doe"; var salary = 161_401}; + +let t = toText(employee); // also works, since Employee <: Person +.... + + +== Fin + +=== Not covered + +* Polymorphic functions with type bounds +* User defined iterator objects, supporting `for` loops. +* Actor classes +* `debug_show` for conversion of almost any value to text. +* `debug e` expressions for debug-only compilation +* `do ? { … e! … }` blocks for handling/propagating option values. +* `assert e` expressions for conditional traps +* tools: +** `mo_doc` (generates doc from doc comments), +** `vessel` (package manager) +** `mo_ide` (LSP language server for VSCode, emacs etc) + +//// +== Old slides + +=== Classes + +Classes as functions returning objects: + +.... + class Counter(init : Int) { + private var state : Int = init; + public func inc() { state += 1; }; + public func get() : Int { state; }; + } +.... + +Class instantiation as function call (no `new`): + +.... +let c = Counter(666); +c.inc(); +let 667 = c.get(); +.... + +=== Generic Classes + +.... +class Dict< K, V > (cmp : (K,K)-> Int ) { + add(k: K, v: V) { ... }; + find(k: K) : ? V { ... }; +}; +.... + +.... +let d = Dict (func (i:Int, j:Int) : Int = i - j); +d.add(1,"Alice"); +let ? name = d.find(1); +.... + +=== Language prelude + +* connects internal primitives with surface syntax (types, operations) +* conversions like `intToNat32` +* side-effecting operations `debugPrintInt` (tie into execution +environment) +* utilities like `hashInt`, `clzNat32` + +== Sample App + +=== Implementing _Chat_ + +* type example +* one server actor +* multiple clients, each an instance of (actor) class Client. + +=== Chat Server + +.... +actor Server { + private var clients : List = null; + + private shared broadcast(message : Text) { + var next = clients; + loop { + switch next { + case null { return; } + case (?l) { l.head.send(message); next := l.tail; }; + }; + }; + }; +.... + +.... + public func subscribe(client : Client) : async Post { + let cs = {head = client; var tail = clients}; + clients := ?cs; + return broadcast; + }; +}; +.... + +=== Example: The client class + +.... +type Server = actor {subscribe : Client -> async Post}; + +actor class Client() = this { + private var name : Text = ""; + public func start(n : Text , s : Server) { + name := n; + let _ = async { + let post = await s.subscribe(this); + post("hello from " # name); + post("goodbye from " # name); + } + }; +.... + +.... + public func send(msg : Text) { + debugPrint(name # " received " # msg # "\n"); + }; +}; +.... + +=== Example: test + +test + +.... +let bob = Client(); +let alice = Client(); +let charlie = Client(); + +bob.start("Bob", Server); +alice.start("Alice", Server); +charlie.start("Charlie", Server); +.... + +output + +.... +[nix-shell:~/motoko/guide]$ ../src/moc -r chat.mo +charlie received hello from bob +alice received hello from bob +bob received hello from bob +charlie received goodbye from bob +alice received goodbye from bob +bob received goodbye from bob +charlie received hello from alice +alice received hello from alice +bob received hello from alice +charlie received goodbye from alice +alice received goodbye from alice +bob received goodbye from alice +charlie received hello from charlie +alice received hello from charlie +bob received hello from charlie +charlie received goodbye from charlie +alice received goodbye from charlie +bob received goodbye from charlie +.... + +//// Old slides From e81641986a277451da2fedc09166fb725ae55064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 1 Sep 2021 12:31:05 +0300 Subject: [PATCH 04/56] Update linker in prep for updating LLVM (#2760) - Implement extended name section (https://github.com/WebAssembly/extended-name-section/blob/master/proposals/extended-name-section/Overview.md) - Handle `start` functions in the RTS module in linker These changes are needed to update to LLVM 12: #2542 --- src/codegen/compile.ml | 11 +++--- src/linking/linkModule.ml | 54 +++++++++++++++++------------ src/wasm-exts/customModule.ml | 14 ++++++++ src/wasm-exts/customModuleDecode.ml | 37 +++++++++++++------- 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 4dece116243..165ea9aa02a 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -8205,13 +8205,10 @@ and conclude_module env start_fi_o = let open Wasm_exts.CustomModule in { module_; dylink = None; - name = { - module_ = None; - function_names = - List.mapi (fun i (f,n,_) -> Int32.(add ni' (of_int i), n)) funcs; - locals_names = - List.mapi (fun i (f,_,ln) -> Int32.(add ni' (of_int i), ln)) funcs; - }; + name = { empty_name_section with function_names = + List.mapi (fun i (f,n,_) -> Int32.(add ni' (of_int i), n)) funcs; + locals_names = + List.mapi (fun i (f,_,ln) -> Int32.(add ni' (of_int i), ln)) funcs; }; motoko = { labels = E.get_labs env; }; diff --git a/src/linking/linkModule.ml b/src/linking/linkModule.ml index 772b8a46760..20590794ade 100644 --- a/src/linking/linkModule.ml +++ b/src/linking/linkModule.ml @@ -598,29 +598,38 @@ let fill_table_base_import new_base : module_' -> module_' = fun m -> (* Concatenation of modules *) -let join_modules (em1 : extended_module) (m2 : module_') (ns2 : name_section) : extended_module = - assert (m2.start = None); +let join_modules + (em1 : extended_module) (m2 : module_') (ns2 : name_section) + (type_indices : (Wasm.Types.func_type, int32) Hashtbl.t) : extended_module = let m1 = em1.module_ in - { em1 with - module_ = { - types = m1.types @ m2.types; - globals = m1.globals @ m2.globals; - tables = m1.tables @ m2.tables; - memories = m1.memories @ m2.memories; - funcs = m1.funcs @ m2.funcs; - start = m1.start; - elems = m1.elems @ m2.elems; - data = m1.data @ m2.data; - imports = m1.imports @ m2.imports; - exports = m1.exports @ m2.exports; - }; - name = { - em1.name with - function_names = em1.name.function_names @ ns2.function_names; - locals_names = em1.name.locals_names @ ns2.locals_names; + let joined = + { em1 with + module_ = { + types = m1.types @ m2.types; + globals = m1.globals @ m2.globals; + tables = m1.tables @ m2.tables; + memories = m1.memories @ m2.memories; + funcs = m1.funcs @ m2.funcs; + start = m1.start; + elems = m1.elems @ m2.elems; + data = m1.data @ m2.data; + imports = m1.imports @ m2.imports; + exports = m1.exports @ m2.exports; }; - motoko = em1.motoko; - } + name = { + em1.name with + function_names = em1.name.function_names @ ns2.function_names; + locals_names = em1.name.locals_names @ ns2.locals_names; + }; + motoko = em1.motoko; + } + in + (* If second module has a start, prepend it to the first module's start. + OK to use `Hashtbl.find` below as the first module will have a start, so + we'll have the unit function in the type section already. *) + match m2.start with + | None -> joined + | Some fi -> prepend_to_start fi.it (Hashtbl.find type_indices (Wasm.Types.FuncType ([], []))) joined (* The main linking function *) @@ -873,8 +882,6 @@ let link (em1 : extended_module) libname (em2 : extended_module) = | Some fi -> prepend_to_start (funs2 fi) (add_or_get_ty (Wasm.Types.FuncType ([], []))) in - assert (dm2.globals = []); - let new_table_size = Int32.add (Int32.add lib_table_start dylink.table_size) (Int32.of_int (List.length got_func_imports)) in @@ -904,6 +911,7 @@ let link (em1 : extended_module) libname (em2 : extended_module) = |> remove_fun_imports_name_section fun_resolved21 |> rename_funcs_name_section funs2 ) + type_indices |> add_call_ctors |> remove_non_ic_exports (* only sane if no additional files get linked in *) in diff --git a/src/wasm-exts/customModule.ml b/src/wasm-exts/customModule.ml index d846249c4af..b87834c6d42 100644 --- a/src/wasm-exts/customModule.ml +++ b/src/wasm-exts/customModule.ml @@ -8,12 +8,26 @@ type name_section = { module_ : string option; function_names : (int32 * string) list; locals_names : (int32 * (int32 * string) list) list; + label_names : (int32 * (int32 * string) list) list; + type_names : (int32 * string) list; + table_names : (int32 * string) list; + memory_names : (int32 * string) list; + global_names : (int32 * string) list; + elem_segment_names : (int32 * string) list; + data_segment_names : (int32 * string) list; } let empty_name_section : name_section = { module_ = None; function_names = []; locals_names = []; + label_names = []; + type_names = []; + table_names = []; + memory_names = []; + global_names = []; + elem_segment_names = []; + data_segment_names = []; } type dylink_section = { diff --git a/src/wasm-exts/customModuleDecode.ml b/src/wasm-exts/customModuleDecode.ml index 7350e56db91..dac8dd6de8a 100644 --- a/src/wasm-exts/customModuleDecode.ml +++ b/src/wasm-exts/customModuleDecode.ml @@ -142,7 +142,7 @@ let name s = try Utf8.decode (string s) with Utf8.Utf8 -> error s pos "malformed UTF-8 encoding" -let sized f s = +let sized (f : int -> stream -> 'a) (s : stream) = let size = len32 s in let start = pos s in let x = f size s in @@ -663,7 +663,7 @@ let data_section s = (* Custom sections *) -let custom_section name_pred f default s = +let custom_section (name_pred : int list -> bool) (f : int -> stream -> 'a) (default : 'a) (s : stream) = let p = pos s in match id s with | Some `CustomSection -> @@ -715,7 +715,7 @@ let assoc_list f = vec (fun s -> let name_map = assoc_list string let indirect_name_map = assoc_list name_map -let name_section_subsection (ns : name_section) s = +let name_section_subsection (ns : name_section) (s : stream) : name_section = match u8 s with | 0 -> (* module name *) let mod_name = sized (fun _ -> string) s in @@ -726,17 +726,30 @@ let name_section_subsection (ns : name_section) s = | 2 -> (* local names *) let loc_names = sized (fun _ -> indirect_name_map) s in { ns with locals_names = ns.locals_names @ loc_names } - - (* We ignore additional name subsections for now, despite newer - LLVM seemingly producing them. - - We should check if these sections are indeed as spec'ed in + (* The following subsections are not in the standard yet, but from the extended-name-section proposal https://github.com/WebAssembly/extended-name-section/blob/master/proposals/extended-name-section/Overview.md - and implement them, for better debugging. *) - | 7 -> - let _global_names = sized (fun _ -> name_map) s in - ns + | 3 -> (* label names *) + let label_names = sized (fun _ -> indirect_name_map) s in + { ns with label_names = ns.label_names @ label_names } + | 4 -> (* type names *) + let type_names = sized (fun _ -> name_map) s in + { ns with type_names = ns.type_names @ type_names } + | 5 -> (* table names *) + let table_names = sized (fun _ -> name_map) s in + { ns with table_names = ns.table_names @ table_names } + | 6 -> (* memory names *) + let memory_names = sized (fun _ -> name_map) s in + { ns with memory_names = ns.memory_names @ memory_names } + | 7 -> (* global names *) + let global_names = sized (fun _ -> name_map) s in + { ns with global_names = ns.global_names @ global_names } + | 8 -> (* elem segment names *) + let elem_segment_names = sized (fun _ -> name_map) s in + { ns with elem_segment_names = ns.elem_segment_names @ elem_segment_names } + | 9 -> (* data segment names *) + let data_segment_names = sized (fun _ -> name_map) s in + { ns with data_segment_names = ns.data_segment_names @ data_segment_names } | i -> error s (pos s) (Printf.sprintf "unknown name section subsection id %d" i) let name_section_content p_end s = From a0ad45b24b053c5707ffb76452d3585e08c94b8c Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Wed, 1 Sep 2021 13:16:58 +0200 Subject: [PATCH 05/56] Upgrade LLVM to version 12 (#2542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use nixpkgs master to see what breaks, and also to get https://github.com/NixOS/nixpkgs/commit/5a923e552cba2bb56fca22ee6c01482635c5b908 which may allow us to use `rustc` from `nixpkgs` again (see #2519) * Use buildDunePackage * Less stdenv.lib * Update generated nix files * Bump to get fixes * Bump nixpkgs * Bump ocamlformat * Add nixpkgs patch * More patches * Try building vlq differently * Fix vlq * Build vlq with buildDunePackage and dune2 * Revert "Build vlq with buildDunePackage and dune2" This reverts commit d5e3399f3822e8d97b43b932082b0deb16c206f7. * Disable static ocaml build * Allow requisites for now * Quench warning * More warnings * Better error message * Try GHC-8.8.4 * Actually bump to GHC-8.10.4 * Bump niv * Fix parsing global names * Call bindgen directly * Bump nixpkgs, patches have been applied * Run formatter * Does this work? * More comments * Try static biuld again * Revert "Try static biuld again" This reverts commit ab44bbfba447cfe4a8cee876bc75b364b5f5b48a. * Actually follow the release branch * Include patch from https://github.com/NixOS/nixpkgs/pull/124498 * More patches * Set allowedRequisites again * Remove references to menhir * Refresh patch * Upgrade LLVM to version 12 * Apply suggestions from code review Co-authored-by: Claudio Russo * Start decoding extended name sections * Export apply_global_relocs in RTS module, call it before RTS init * Make __wasm_apply_data_relocs optional in the linker (but not in codegen!), update linker test outputs * Call wasm_apply_global_relocs from link_start * Put the comment back * Add patch from https://github.com/NixOS/nixpkgs/pull/125472/files * Patch now upstream * Linker: remove special case around __wasm_apply_global_relocs, add RTS start to merged module start * Prepend m2 start in join_modules Co-authored-by: Claudio Russo Co-authored-by: Ömer Sinan Ağacan --- default.nix | 16 ++++++++-------- rts/Makefile | 8 ++++---- test/ld/ok/fun-ptr.linked.wat.ok | 4 ++-- test/ld/ok/representative.linked.wat.ok | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/default.nix b/default.nix index 3cc18fd80ae..a2a3c85ae6d 100644 --- a/default.nix +++ b/default.nix @@ -25,11 +25,11 @@ let haskellPackages = nixpkgs.haskellPackages.override { }; in let rtsBuildInputs = with nixpkgs; [ - # pulls in clang (wrapped) and clang-10 (unwrapped) - llvmPackages_10.clang + # pulls in clang (wrapped) and clang-12 (unwrapped) + llvmPackages_12.clang # pulls in wasm-ld - llvmPackages_10.lld - llvmPackages_10.bintools + llvmPackages_12.lld + llvmPackages_12.bintools rustc-nightly cargo-nightly xargo @@ -41,17 +41,17 @@ let llvmEnv = '' # When compiling to wasm, we want to have more control over the flags, # so we do not use the nix-provided wrapper in clang - export WASM_CLANG="clang-10" + export WASM_CLANG="clang-12" export WASM_LD=wasm-ld # because we use the unwrapped clang, we have to pass in some flags/paths # that otherwise the wrapped clang would take care for us - export WASM_CLANG_LIB="${nixpkgs.llvmPackages_10.clang-unwrapped.lib}" + export WASM_CLANG_LIB="${nixpkgs.llvmPackages_12.clang-unwrapped.lib}" # When compiling natively, we want to use `clang` (which is a nixpkgs # provided wrapper that sets various include paths etc). # But for some reason it does not handle building for Wasm well, so - # there we use plain clang-10. There is no stdlib there anyways. - export CLANG="${nixpkgs.clang_10}/bin/clang" + # there we use plain clang-12. There is no stdlib there anyways. + export CLANG="${nixpkgs.clang_12}/bin/clang" ''; in diff --git a/rts/Makefile b/rts/Makefile index ff0fbef840f..4e989b564de 100644 --- a/rts/Makefile +++ b/rts/Makefile @@ -1,8 +1,8 @@ SHELL:=bash -O globstar -CLANG ?= clang-10 -WASM_CLANG ?= clang-10 -WASM_LD ?= wasm-ld-10 +CLANG ?= clang-12 +WASM_CLANG ?= clang-12 +WASM_LD ?= wasm-ld-12 # # We manually list all the .c files of libtommath that we care about. @@ -103,7 +103,7 @@ CLANG_FLAGS = \ --target=wasm32-emscripten \ -fno-builtin -ffreestanding \ --optimize=s \ - -resource-dir=$(WASM_CLANG_LIB)/lib/clang/10.0.1 + -resource-dir=$(wildcard $(WASM_CLANG_LIB)/lib/clang/*) # # Build targets diff --git a/test/ld/ok/fun-ptr.linked.wat.ok b/test/ld/ok/fun-ptr.linked.wat.ok index 63989ee3dca..1dc8da14ab2 100644 --- a/test/ld/ok/fun-ptr.linked.wat.ok +++ b/test/ld/ok/fun-ptr.linked.wat.ok @@ -9,8 +9,8 @@ i32.const 5 call_indirect (type 1)) (func $__wasm_call_ctors (type 2) - call $__wasm_apply_relocs) - (func $__wasm_apply_relocs (type 2)) + call $__wasm_apply_data_relocs) + (func $__wasm_apply_data_relocs (type 2)) (func $f0 (type 1) (param i32 i32) (result i32) local.get 1 local.get 0 diff --git a/test/ld/ok/representative.linked.wat.ok b/test/ld/ok/representative.linked.wat.ok index dca1183fd28..f0cd781fd47 100644 --- a/test/ld/ok/representative.linked.wat.ok +++ b/test/ld/ok/representative.linked.wat.ok @@ -12,8 +12,8 @@ call $not_yet_imported2) (func $resolved_export (type 0)) (func $__wasm_call_ctors (type 0) - call $__wasm_apply_relocs) - (func $__wasm_apply_relocs (type 0)) + call $__wasm_apply_data_relocs) + (func $__wasm_apply_data_relocs (type 0)) (func $square (type 1) (param i32) (result i32) call $resolved_export call $unresolved_import From e22c02d7809faaced40efdd458cb8c570d4aff8b Mon Sep 17 00:00:00 2001 From: Gabor Greif Date: Mon, 6 Sep 2021 15:45:56 +0200 Subject: [PATCH 06/56] Releasing 0.6.8 (#2765) * mention notable features in changelog * add more interesting items * today's date --- Changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index 64f5d48889e..f6fd826b93f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,12 @@ # Motoko compiler changelog +== 0.6.8 (2021-09-06) + +* Introduce primitives for `Int` ⇔ `Float` conversions (#2733) +* Bump LLVM toolchain to version 12 (#2542) +* Support extended name linker sections (#2760) +* Fix crashing bug for formatting huge floats (#2737) + == 0.6.7 (2021-08-16) * moc From cce95e0f4d7e2b89cc46207ab9c496fcc1213045 Mon Sep 17 00:00:00 2001 From: Gabor Greif Date: Tue, 7 Sep 2021 10:18:08 +0200 Subject: [PATCH 07/56] Remove unnecessary attribute passing in release expression (#2766) This might resolve the glitch that threw the last release from the rails: https://github.com/dfinity/motoko/pull/2765#issuecomment-913717691 There was no receive-side match for the argument `internal`, thus the nix call failed. --- release-files.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release-files.nix b/release-files.nix index ad2ce18fb51..bafc23951d7 100644 --- a/release-files.nix +++ b/release-files.nix @@ -7,8 +7,8 @@ { releaseVersion ? "latest" }: let nixpkgs = import ./nix { }; - linux = import ./default.nix { system = "x86_64-linux"; internal = true; inherit releaseVersion; }; - darwin = import ./default.nix { system = "x86_64-darwin"; internal = true; inherit releaseVersion; }; + linux = import ./default.nix { system = "x86_64-linux"; inherit releaseVersion; }; + darwin = import ./default.nix { system = "x86_64-darwin"; inherit releaseVersion; }; as_tarball = dir: derivations: nixpkgs.runCommandNoCC "motoko-${releaseVersion}.tar.gz" { From 59ddaa4520793b6e7038b60e3c30ff267d8415f6 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Tue, 7 Sep 2021 17:11:09 -0700 Subject: [PATCH 08/56] niv motoko-base: update 6d65c866 -> 075fed27 (#2770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for motoko-base: Branch: next-moc Commits: [dfinity/motoko-base@6d65c866...075fed27](https://github.com/dfinity/motoko-base/compare/6d65c866cabba539cd84a1c0f48e5b952dee2bde...075fed27602296a34ad90c244ed27b76b63a541b) * [`aa0fe3fc`](https://github.com/dfinity/motoko-base/commit/aa0fe3fcee2ea6b0a6fd8ff2bb3a268803abac84) Use primitives provided by compiler for `Float` ⇔ `Int` conversions ([dfinity/motoko-base⁠#281](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/motoko-base/issues/281)) --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 5dc5629ee95..b4ff6e7bb41 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "6d65c866cabba539cd84a1c0f48e5b952dee2bde", - "sha256": "1ziqi2cx9vbx65144vlafdmv11cbll1rrxgvqbdm4mjlbhh379gy", + "rev": "075fed27602296a34ad90c244ed27b76b63a541b", + "sha256": "06ssc923kxamfpgwz65kckdf3sakg7g98pnqinji7rav0m3rlq3d", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/6d65c866cabba539cd84a1c0f48e5b952dee2bde.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/075fed27602296a34ad90c244ed27b76b63a541b.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From c82e45cf35f0efc6a79d1a8cfe033df5ff0f7431 Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Thu, 9 Sep 2021 18:34:17 +0100 Subject: [PATCH 09/56] Stable Memory: ugrade mechanism, primitives and test See design/StableMemory.md for design. --- Building.md | 2 +- Changelog.md | 8 + design/StableMemory.md | 290 +++++++ nix/sources.json | 6 +- src/codegen/compile.ml | 725 ++++++++++++++++-- src/prelude/prim.mo | 68 ++ test/run-drun/life-mut.drun | 10 + test/run-drun/life-mut/life-v1.mo | 31 +- test/run-drun/life-mut/life-v2.mo | 13 +- test/run-drun/life-mut/life-v3.mo | 206 +++++ test/run-drun/ok/life-mut.drun.ok | 210 ++++- test/run-drun/ok/stable-mem-blob.drun-run.ok | 24 + test/run-drun/ok/stable-mem-float.drun-run.ok | 24 + test/run-drun/ok/stable-mem-grow.drun-run.ok | 17 + test/run-drun/ok/stable-mem-int16.drun-run.ok | 24 + test/run-drun/ok/stable-mem-int32.drun-run.ok | 24 + test/run-drun/ok/stable-mem-int64.drun-run.ok | 24 + test/run-drun/ok/stable-mem-int8.drun-run.ok | 24 + test/run-drun/ok/stable-mem-nat16.drun-run.ok | 24 + test/run-drun/ok/stable-mem-nat32.drun-run.ok | 24 + test/run-drun/ok/stable-mem-nat64.drun-run.ok | 24 + test/run-drun/ok/stable-mem-nat8.drun-run.ok | 24 + test/run-drun/stable-mem-blob.mo | 105 +++ test/run-drun/stable-mem-float.mo | 92 +++ test/run-drun/stable-mem-grow.mo | 55 ++ test/run-drun/stable-mem-int16.mo | 92 +++ test/run-drun/stable-mem-int32.mo | 92 +++ test/run-drun/stable-mem-int64.mo | 92 +++ test/run-drun/stable-mem-int8.mo | 92 +++ test/run-drun/stable-mem-nat16.mo | 92 +++ test/run-drun/stable-mem-nat32.mo | 92 +++ test/run-drun/stable-mem-nat64.mo | 92 +++ test/run-drun/stable-mem-nat8.mo | 92 +++ test/run-drun/stable-mem/StableMemory.mo | 38 + 34 files changed, 2712 insertions(+), 140 deletions(-) create mode 100644 design/StableMemory.md create mode 100644 test/run-drun/life-mut/life-v3.mo create mode 100644 test/run-drun/ok/stable-mem-blob.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-float.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-grow.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-int16.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-int32.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-int64.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-int8.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-nat16.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-nat32.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-nat64.drun-run.ok create mode 100644 test/run-drun/ok/stable-mem-nat8.drun-run.ok create mode 100644 test/run-drun/stable-mem-blob.mo create mode 100644 test/run-drun/stable-mem-float.mo create mode 100644 test/run-drun/stable-mem-grow.mo create mode 100644 test/run-drun/stable-mem-int16.mo create mode 100644 test/run-drun/stable-mem-int32.mo create mode 100644 test/run-drun/stable-mem-int64.mo create mode 100644 test/run-drun/stable-mem-int8.mo create mode 100644 test/run-drun/stable-mem-nat16.mo create mode 100644 test/run-drun/stable-mem-nat32.mo create mode 100644 test/run-drun/stable-mem-nat64.mo create mode 100644 test/run-drun/stable-mem-nat8.mo create mode 100644 test/run-drun/stable-mem/StableMemory.mo diff --git a/Building.md b/Building.md index 1a9ae4a9044..45a1f48ed0e 100644 --- a/Building.md +++ b/Building.md @@ -97,7 +97,7 @@ squash merge) once CI passes ## Profile the compiler -(This section is currently defuct, and needs to be update to work with the dune +(This section is currently defunct, and needs to be update to work with the dune build system.) 1. Build with profiling within nix-shell (TODO: How to do with dune) diff --git a/Changelog.md b/Changelog.md index f6fd826b93f..4d3b367add7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,13 @@ # Motoko compiler changelog +* moc + +* Add runtime support for low-level, direct access to 32-bit IC stable memory (#2626) + +* motoko-base + + * Add StableMemory library, exposing 32-bit IC stable memory (#280) + == 0.6.8 (2021-09-06) * Introduce primitives for `Int` ⇔ `Float` conversions (#2733) diff --git a/design/StableMemory.md b/design/StableMemory.md new file mode 100644 index 00000000000..d9ddfdeccb6 --- /dev/null +++ b/design/StableMemory.md @@ -0,0 +1,290 @@ +# Stable Memory API + +The current implementation of stable variables is based on +serialization and deserialization of all stable data on upgrade. This +clearly doesn't scale to large amounts of stable data as there may not +be enough cycles to perform (de)serialization. + +To avoid this upgrade hazard, some canisters with low-level API +access, and large stable memory footprints, arrange to store their +persistent data in stable memory at all times, using either a custom +binary encoding or a mixture of candid and raw binary. + +To provide more fine-grained access to stable memory we propose +extending the existing stable variable implementation with an orthogonal, +library providing (almost) direct access to the IC Stable Memory API. + +Since the implementation of stable variables itself makes temporary use of +stable memory, some coordination between these two alternative, co-existing +interfaces to IC stable memory is required. + + +# The IC's Stable Memory API + +The IC provides a very small set of functions for operating on stable memory: + +``` +ic0.stable_size : () -> (page_count : i32); // * +ic0.stable_grow : (new_pages : i32) -> (old_page_count : i32); // * +ic0.stable_write : (offset : i32, src : i32, size : i32) -> (); // * +ic0.stable_read : (dst : i32, offset : i32, size : i32) -> (); // * +``` + +(see https://sdk.dfinity.org/docs/interface-spec/index.html#system-api-stable-memory) + +These grow memory and do bulk transfers between Wasm and stable +memory. The `// *` means that they can be called in all contexts +(e.g. init, update, query etc). Direct reads and writes of word-sized +data to/from the stack are not supported but can be emulated at cost. +The initial size of the stable memory is zero. The contents of fresh pages (after grow) is initially zero. + +Note that, in this API, the client is responsible for growing (both +stable and wasm) memory before access by read or write (out-of-bounds +access will trap). + +# A minimal Stable Memory API + +The minimal Motoko prims could be: + +``` +module StableMemory { + size : () -> (logical_page_count : i32); // <= ic0.stable_size() + grow : () -> (new_pages : i32) -> (old_logical_page_count : i32); + loadNat8 : (offset : Nat32) -> Nat8; + // traps outside logical address space + storeNat8 : (offset : Nat32, n : Nat8) -> (); + // traps outside logical address space + ... + loadBlob : (offset : Nat32, size : Nat) -> Blob + // read Blob contents from memory at [offset,..,offset+size-1] into fresh blob, trapping if exceeding logical address space + storeBlob : (offset : Nat32, b : Blob) -> (); // write contents of blob to memory, trapping if exceeding logical address space +} +``` + +NOTE: Motoko's `Nat32` value are always boxed - it might be more efficient to use `Nat` which is unboxed for 30(?)-bit values. + + +``` +fun loadNat8(offset, b) = + assert (offset < StableMemory.size() * wasm_page_size); + mem[offset] + +fun storeNat8(offset, b) = + assert (offset < StableMemory.size() * wasm_page_size); + mem[offset] := b + +``` + +(To avoid overflow on the rhs, we could implement the check as `assert ((offset >> 16) < StableMemory.size())`.) + +On top of this basic API, users should be able to build more +interesting higher-level APIs for pickling user-defined data. + +REMARK: + +Actually implementing the sketched assignments using the existing IC +API involves writing the contents to memory and then copying stable +memory - even for individual words - this could be optimized by an +improved system API offering direct load and stores from/to the stack: + +``` +ic0.stable_write_i32 : (offset : i32, val: i32) -> (); // * +ic0.stable_read_i32 : (offset : i32, size : i32) -> i32; // * +// similarly for i64, f32, f64 +``` + +## Bikeshedding: + +It might be preferable to arrange the API by type, with one nested +module per type: + +``` +module StableMemory { + Nat8 : module { + load : (offset : Nat32) -> Nat8; + store : (offset : Nat32, n : Nat8) -> (); + }; + Nat16 : module { + load : (offset : Nat32) -> Nat16; + store : (offset : Nat32, n : Nat16) -> (); + }; + // uniformly for all scalar prim types. + ... + Blob : module { + read: (offset : Nat32, size : i32) -> Blob + write : (offset : Nat32, b : Blob) -> (); + } +} +``` + +(I think the compiler will still optimize these nested calls to known +function calls, but it would be worth checking). + +# Maintaining existing Stable Variables. + +Stable memory is currently hidden behind the abstraction of stable +variables, which we will still need to maintain. The current +implementation of stable variables stores all variables as a +Candidish record of _stable_ fields, starting at stable memory address 0 with +initial word encoding size (in bytes?) followed by contents. +(Candidish is the Motoko extension of Candid to support mutable data.) + +Starting from a clean slate, we would extend this so all user-defined StableMemory is +stored at a low address, with _stable variable_ data stored just +beyond the currently used StableMemory content on canister_pre_upgrade +and canister_post_upgrade. That way the StableMemory area need not +move, with stable variables simply serialized and appended in +`canister_pre_upgrade` and deserialized and discarded in +`canister_post_upgrade`, leaving the manual StableMemory unchanged. + +For backwards compatibility reasons, we can't do that. + +Luckily, stable variables always require non-zero bytes to encode, so we +can devise a backwards compatible scheme for upgrading +from pre-StableMemory canisters to post-StableMemory +canisters, as follows. + +During execution, abstract stable memory (StableMemory) is aligned +with IC stable memory, at address 0, for reasonable efficiency (apart +from bound checks against logical `size()`). + +During upgrade, if StableMemory has zero pages, we use the existing format, writing +(non_zero) length and content of any stable variables from address 0 or leaving ic0.stable_mem() +at zero with no pages allocated (if there are no stable variables). +Otherwise, we compute the length and data of the stable variable encoding; +save the first word of StableMemory at a known offset from the end of stable memory; +write a 0x00 marker to the first word; and append length (even if zero) and +data (if any) to the end of StableMem. +The logical size of StableMemory and a version number are also written at +known offsets from the end of StableMemory. + +In post_upgrade, we reverse this process to recover the size of StableMemory, +restore the displaced first word of StableMemory and deserialize any stable vars, +taking care to zero the (logically) free StableMemory occupied by any encoded stable variables +and other metadata (so that initial reads after growing beyond page `size` always return 0). + +This scheme avoids relocating most of StableMem and is constant time when +there are no stable variables. + +# Details: + +Stable memory layout (during execution): + +* aligned with stable-memory, with global word `size` holding logical page count (initially 0 < !size < 2^16). +* user are responsible for allocating logical pages. +* each load/store does a `size`-related bounds check. + +During execution, stable variables aren't maintained in stable memory - they are on the Motoko heap. + +Stable memory layout (between upgrades), assuming stable variable encoding `v == {fs:vs}`, a record value of record type {fs:Ts}. + +NOTE: A program with no stable variables still writes an empty record value `v = {}`. + +``` +(case !size == 0) // hence N = 0 + [0..3] StableVariable data len + [4..4+len-1] StableVariable data + [4+len-1,..M-1] 0...0 // zero padding +(case !size > 0) +[0..3] 0...0 +[4..N-1] StableMemory bytes +[N..N+3] StableVariable data len +[N+4..(N+4)+len-1] StableVariable data +[(N+4)+len..M-13] 0...0 // zero padding +[M-12..M-9] value N/64Ki = !size +[M-8..M-5] saved StableMemory bytes +[M-4..M-1] version word + +where N = !size * pagesize // logical memory size + M = ic0.stable_size() * pagesize // physical memory size + pagesize = 64Kb (2^16 bytes) +where (len, data) = serialize(v,data) + +``` + +On pre_upgrade + +```ocaml +func stabilise {fs:Ts} v : value = + let len, data = serialize(v) + in + if !size == 0 then + mem[0,..,3] := len + mem[4,...,4+len-1] := data + else + let N = !size * page_size in + // if necessary, grow mem to page including address N + 4 + len + 4 + 4 + 4 + let M = pagesize * ic0.stable_size() in + mem[N,..,N+3] := len + mem[N+4,..,N+4+len-1] := data + mem[M-12..M-9] := !size + men[M-8..M-5] := mem[0,...,3] // save StableMemory bytes 0-3 + mem[0,..,3] := 0..0 // write marker + mem[M-4..M-1] := version +``` +on post_upgrade + +```ocaml +// restores StableMemory (size and memory) and deserializes any stable variables, zeroing their storage +fun destabilize {fs:Ts} : value = + let pages = ic0.stable_size() in + if pages == 0 then + size := 0; + {fs = nulls} + else + let marker = mem[0,..,3] in // read zero or size of stable value + mem[0,..,3] = 0; + let (offset, len) = + if marker == 0x0 then + let M = pages * pagesize in + let ver = mem[M-4,..,M-1] in + mem[M-4,..,M-1] := 0; + if (ver > version) assert false + mem[0,..,3] = mem[M-8,..,M-5]; // restore StableMemory bytes 0-3 + size := mem[M-12,..,M-9]; + mem[M-12,..,M-9] := 0; + N = size * pagesize; + let len = mem[N,..,N+3] in + mem[N,..,N+3] := 0; + assert len > 0 + assert (N+4+len-1 <= ic0.stable_size() * pagesize) + (N+4, len) + else + (4, marker) + in + assert (0 < len <= ic0.stable_size() * pagesize) + let v = deserialise(offset, len) in + mem[offset,..,offset+len-1] := 0 // clear serialization memory + v +``` + +We explicitly clear memory used by stable variables so StableMem +doesn't need to clear memory when grabbing logical pages from already +existing physical ones. + + +NOTE: We still need to do some work during updgrade and postupgrade, +but if stable variables and user-defined pre/post upgrade hooks are +avoided, then the work is minimal and highly unlikely to exhaust cycle +budget. + +REMARK: + +* An actor that has no stable variables and allocates no StableMem + should require no physical stable memory + +* An actor that has n > 0 pages of StableMem will (unfortunately) + require at least n+1 pages of physical memory since we need at least + one extra bit to encode the presence or absence of stable variables + (there is no other preserved state that could record this bit). + +FURTHER CONSIDERATIONS: It would be nice if there was some way to +allow a Motoko actor (perhaps intended to upgrade from a foreign +canister with foreign stable memory format) to have unadulterated, +full-speed access to stable memory, without the protocol required by +the possibility of stable variables. Perhaps we could special case +programs that have *no* stable variables to support this raw +semantics. I.e. if the program declares no stable variables, we +install and upgrade by simply setting `(StableMemory.)size := +ic0.stable_size()`, never consulting or altering physical memory and +(ideally) omitting the additional bounds checking. diff --git a/nix/sources.json b/nix/sources.json index b4ff6e7bb41..ba09d2afa56 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "075fed27602296a34ad90c244ed27b76b63a541b", - "sha256": "06ssc923kxamfpgwz65kckdf3sakg7g98pnqinji7rav0m3rlq3d", + "rev": "f799e061b53e7565809da7c558afd7403d2ad836", + "sha256": "0dx9qd3flgyds7qijr9jbsmyi7dl42fy4vw7ddfxqr4lhrqbyf9c", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/075fed27602296a34ad90c244ed27b76b63a541b.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/f799e061b53e7565809da7c558afd7403d2ad836.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 165ea9aa02a..519c858bac3 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -26,6 +26,7 @@ let (^^) = G.(^^) (* is this how we import a single operator from a module that (* WebAssembly pages are 64kb. *) let page_size = Int32.of_int (64*1024) +let page_size_bits = 16 (* Pointers are skewed (translated) -1 relative to the actual offset. @@ -104,7 +105,7 @@ module Const = struct These are values that * are completely known constantly - * do not require Wasm code to be be executed (e.g. in `start`) + * do not require Wasm code to be executed (e.g. in `start`) * can be used directly (e.g. Call, not CallIndirect) * can be turned into Vanilla heap data on demand @@ -994,6 +995,7 @@ module Stack = struct end (* Stack *) + module ContinuationTable = struct (* See rts/motoko-rts/src/closure_table.rs *) let remember env : G.t = E.call_import env "rts" "remember_continuation" @@ -2842,20 +2844,19 @@ module Blob = struct get_x ) - let of_size_copy env get_size_fun copy_fun offset = + let of_size_copy env get_size_fun copy_fun offset_fun = let (set_len, get_len) = new_local env "len" in let (set_blob, get_blob) = new_local env "blob" in get_size_fun env ^^ set_len ^^ get_len ^^ alloc env ^^ set_blob ^^ get_blob ^^ payload_ptr_unskewed ^^ - compile_unboxed_const offset ^^ + offset_fun env ^^ get_len ^^ copy_fun env ^^ get_blob - (* Lexicographic blob comparison. Expects two blobs on the stack *) let rec compare env op = let open Operator in @@ -2947,6 +2948,30 @@ module Blob = struct let dyn_alloc_scratch env = alloc env ^^ payload_ptr_unskewed + (* TODO: rewrite using MemoryFill *) + let clear env = + Func.share_code1 env "blob_clear" ("x", I32Type) [] (fun env get_x -> + let (set_ptr, get_ptr) = new_local env "ptr" in + let (set_len, get_len) = new_local env "len" in + get_x ^^ + as_ptr_len env ^^ + set_len ^^ + set_ptr ^^ + + (* round to word size *) + get_len ^^ + compile_add_const (Int32.sub Heap.word_size 1l) ^^ + compile_divU_const Heap.word_size ^^ + + (* clear all words *) + from_0_to_n env (fun get_i -> + get_ptr ^^ + compile_unboxed_const 0l ^^ + store_unskewed_ptr ^^ + get_ptr ^^ + compile_add_const Heap.word_size ^^ + set_ptr)) + end (* Blob *) module Text = struct @@ -3543,7 +3568,8 @@ module IC = struct Func.share_code0 env "canister_self" [I32Type] (fun env -> Blob.of_size_copy env (fun env -> system_call env "ic0" "canister_self_size") - (fun env -> system_call env "ic0" "canister_self_copy") 0l + (fun env -> system_call env "ic0" "canister_self_copy") + (fun env -> compile_unboxed_const 0l) ) | _ -> E.trap_with env "cannot get self-actor-reference when running locally" @@ -3561,7 +3587,8 @@ module IC = struct | Flags.ICMode | Flags.RefMode -> Blob.of_size_copy env (fun env -> system_call env "ic0" "msg_caller_size") - (fun env -> system_call env "ic0" "msg_caller_copy") 0l + (fun env -> system_call env "ic0" "msg_caller_copy") + (fun env -> compile_unboxed_const 0l) | _ -> E.trap_with env (Printf.sprintf "cannot get caller when running locally") @@ -3595,7 +3622,8 @@ module IC = struct Func.share_code0 env "error_message" [I32Type] (fun env -> Blob.of_size_copy env (fun env -> system_call env "ic0" "msg_reject_msg_size") - (fun env -> system_call env "ic0" "msg_reject_msg_copy") 0l + (fun env -> system_call env "ic0" "msg_reject_msg_copy") + (fun env -> compile_unboxed_const 0l) ) let error_value env = @@ -3709,7 +3737,8 @@ module IC = struct Opt.inject_noop env ( Blob.of_size_copy env (fun env -> system_call env "ic0" "data_certificate_size") - (fun env -> system_call env "ic0" "data_certificate_copy") 0l + (fun env -> system_call env "ic0" "data_certificate_copy") + (fun env -> compile_unboxed_const 0l) ) end (Opt.null_lit env) | _ -> @@ -3717,6 +3746,301 @@ module IC = struct end (* IC *) +module StableMem = struct + + (* start from 1 to avoid accidental reads of 0 *) + let version = Int32.of_int 1 + + let register_globals env = + (* size (in pages) *) + E.add_global32 env "__stablemem_size" Mutable 0l + + let get_mem_size env = + G.i (GlobalGet (nr (E.get_global env "__stablemem_size"))) + let set_mem_size env = + G.i (GlobalSet (nr (E.get_global env "__stablemem_size"))) + + (* stable memory bounds check *) + let guard env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code1 env "__stablemem_guard" + ("offset", I32Type) [] + (fun env get_offset -> + get_offset ^^ + compile_unboxed_const (Int32.of_int page_size_bits) ^^ + G.i (Binary (Wasm.Values.I32 I32Op.ShrU)) ^^ + get_mem_size env ^^ + G.i (Compare (Wasm.Values.I32 I32Op.LtU)) ^^ + E.else_trap_with env "StableMemory offset out of bounds") + | _ -> assert false + + (* check [offset,.., offset + size) within bounds, assumes size > 0 *) + let guard_range env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code2 env "__stablemem_guard_range" + (("offset", I32Type), ("size", I32Type)) [] + (fun env get_offset get_size -> +(* + (* Using ShrU seems worse - need to subtract 1 *) + get_offset ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + get_size ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.Add)) ^^ + compile_sub64_const 1L ^^ + compile_const_64 (Int64.of_int page_size_bits) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.ShrU)) ^^ + G.i (Convert (Wasm.Values.I32 I64Op.WrapI64)) ^^ + get_mem_size env ^^ + G.i (Compare (Wasm.Values.I32 I32Op.LtU)) ^^ + E.else_trap_with env "StableMemory range out of bounds") + *) + get_offset ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + get_size ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.Add)) ^^ + get_mem_size env ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + compile_const_64 (Int64.of_int page_size_bits) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.Shl)) ^^ + G.i (Compare (Wasm.Values.I64 I64Op.LeU)) ^^ + E.else_trap_with env "StableMemory range out of bounds") + | _ -> assert false + + let add_guard env guarded get_offset bytes = + if guarded then + (get_offset ^^ + if bytes = 1l then + guard env + else + compile_unboxed_const bytes ^^ + guard_range env) + else G.nop + + let read env guarded name typ bytes load = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code1 env (Printf.sprintf "__stablemem_%sread_%s" (if guarded then "guarded_" else "") name) + ("offset", I32Type) [typ] + (fun env get_offset -> + let words = Int32.div (Int32.add bytes 3l) 4l in + add_guard env guarded get_offset bytes ^^ + Stack.with_words env "temp_ptr" words (fun get_temp_ptr -> + get_temp_ptr ^^ get_offset ^^ compile_unboxed_const bytes ^^ + IC.system_call env "ic0" "stable_read" ^^ + get_temp_ptr ^^ load)) + | _ -> assert false + + let write env guarded name typ bytes store = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code2 env (Printf.sprintf "__stablemem_%swrite_%s" (if guarded then "guarded_" else "") name) + (("offset", I32Type), ("value", typ)) [] + (fun env get_offset get_value -> + let words = Int32.div (Int32.add bytes 3l) 4l in + add_guard env guarded get_offset bytes ^^ + Stack.with_words env "temp_ptr" words (fun get_temp_ptr -> + get_temp_ptr ^^ get_value ^^ store ^^ + get_offset ^^ + get_temp_ptr ^^ compile_unboxed_const bytes ^^ + IC.system_call env "ic0" "stable_write")) + | _ -> assert false + + let _read_word32 env = + read env false "word32" I32Type 4l load_unskewed_ptr + let write_word32 env = + write env false "word32" I32Type 4l store_unskewed_ptr + + + (* read and clear word32 from stable mem offset on stack *) + let read_and_clear_word32 env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code1 env "__stablemem_read_and_clear_word32" + ("offset", I32Type) [I32Type] + (fun env get_offset -> + Stack.with_words env "temp_ptr" 1l (fun get_temp_ptr -> + let (set_word, get_word) = new_local env "word" in + (* read word *) + get_temp_ptr ^^ get_offset ^^ compile_unboxed_const 4l ^^ + IC.system_call env "ic0" "stable_read" ^^ + get_temp_ptr ^^ load_unskewed_ptr ^^ + set_word ^^ + (* write 0 *) + get_temp_ptr ^^ compile_unboxed_const 0l ^^ store_unskewed_ptr ^^ + get_offset ^^ + get_temp_ptr ^^ compile_unboxed_const 4l ^^ + IC.system_call env "ic0" "stable_write" ^^ + (* return word *) + get_word + )) + | _ -> assert false + + (* ensure_pages : ensure at least num pages allocated, + growing (real) stable memory if needed *) + let ensure_pages env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code1 env "__stablemem_grow" + ("pages", I32Type) [I32Type] + (fun env get_pages -> + let (set_size, get_size) = new_local env "size" in + let (set_pages_needed, get_pages_needed) = new_local env "pages_needed" in + + E.call_import env "ic0" "stable_size" ^^ + set_size ^^ + + get_pages ^^ + get_size ^^ + G.i (Binary (Wasm.Values.I32 I32Op.Sub)) ^^ + set_pages_needed ^^ + + get_pages_needed ^^ + compile_unboxed_zero ^^ + G.i (Compare (Wasm.Values.I32 I32Op.GtS)) ^^ + G.if_ [I32Type] + (get_pages_needed ^^ + E.call_import env "ic0" "stable_grow") + get_size) + | _ -> assert false + + (* ensure stable memory includes [offset..offset+size), assumes size > 0 *) + let ensure env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code2 env "__stablemem_ensure" + (("offset", I32Type), ("size", I32Type)) [] + (fun env get_offset get_size -> + get_offset ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + get_size ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.Add)) ^^ + compile_const_64 (Int64.of_int page_size_bits) ^^ + G.i (Binary (Wasm.Values.I64 I64Op.ShrU)) ^^ + G.i (Convert (Wasm.Values.I32 I64Op.WrapI64)) ^^ + compile_add_const 1l ^^ + ensure_pages env ^^ + (* Check result *) + compile_unboxed_zero ^^ + G.i (Compare (Wasm.Values.I32 I32Op.LtS)) ^^ + E.then_trap_with env "Out of stable memory.") + | _ -> assert false + + (* API *) + + let logical_grow env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code1 env "__stablemem_logical_grow" + ("pages", I32Type) [I32Type] (fun env get_pages -> + let (set_size, get_size) = new_local env "size" in + get_mem_size env ^^ + set_size ^^ + + get_size ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + get_pages ^^ G.i (Convert (Wasm.Values.I64 I64Op.ExtendUI32)) ^^ + G.i (Binary (Wasm.Values.I64 I32Op.Add)) ^^ + compile_const_64 65536L ^^ + G.i (Compare (Wasm.Values.I64 I64Op.GeU)) ^^ + G.if_ [I32Type] + begin + compile_unboxed_const (-1l) ^^ + G.i Return + end + begin + let (set_new_size, get_new_size) = new_local env "new_size" in + get_size ^^ + get_pages ^^ + G.i (Binary (Wasm.Values.I32 I32Op.Add)) ^^ + set_new_size ^^ + + (* physical grow if necessary *) + let (set_ensured, get_ensured) = new_local env "ensured" in + get_new_size ^^ + ensure_pages env ^^ + set_ensured ^^ + + (* Check result *) + get_ensured ^^ + compile_unboxed_zero ^^ + G.i (Compare (Wasm.Values.I32 I32Op.LtS)) ^^ + G.if_ [I32Type] + ((* propagate failure -1; preserve logical size *) + get_ensured) + ((* update logical size *) + get_new_size ^^ + set_mem_size env ^^ + (* return old logical size *) + get_size) + end) + | _ -> assert false + + let load_word32 env = + read env true "word32" I32Type 4l load_unskewed_ptr + let store_word32 env = + write env true "word32" I32Type 4l store_unskewed_ptr + + let load_word8 env = + read env true "word8" I32Type 1l + (G.i (Load {ty = I32Type; align = 0; offset = 0l; sz = Some Wasm.Types.(Pack8, ZX)})) + let store_word8 env = + write env true "word8" I32Type 1l store_unskewed_ptr + + let load_word16 env = + read env true "word16" I32Type 2l + (G.i (Load {ty = I32Type; align = 0; offset = 0l; sz = Some Wasm.Types.(Pack16, ZX)})) + let store_word16 env = + write env true "word16" I32Type 2l store_unskewed_ptr + + let load_word64 env = + read env true "word64" I64Type 8l + (G.i (Load {ty = I64Type; align = 0; offset = 0l; sz = None })) + let store_word64 env = + write env true "word64" I64Type 8l + (G.i (Store {ty = I64Type; align = 0; offset = 0l; sz = None})) + + let load_float64 env = + read env true "float64" F64Type 8l + (G.i (Load {ty = F64Type; align = 0; offset = 0l; sz = None })) + let store_float64 env = + write env true "float64" F64Type 8l + (G.i (Store {ty = F64Type; align = 0; offset = 0l; sz = None})) + + + let load_blob env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code2 env "__stablemem_load_blob" + (("offset", I32Type), ("len", I32Type)) [I32Type] + (fun env get_offset get_len -> + let (set_blob, get_blob) = new_local env "blob" in + get_offset ^^ + get_len ^^ + guard_range env ^^ + get_len ^^ Blob.alloc env ^^ set_blob ^^ + get_blob ^^ Blob.payload_ptr_unskewed ^^ + get_offset ^^ + get_len ^^ + IC.system_call env "ic0" "stable_read" ^^ + get_blob) + | _ -> assert false + + let store_blob env = + match E.mode env with + | Flags.ICMode | Flags.RefMode -> + Func.share_code2 env "__stablemem_store_blob" + (("offset", I32Type), ("blob", I32Type)) [] + (fun env get_offset get_blob -> + let (set_len, get_len) = new_local env "len" in + get_blob ^^ Blob.len env ^^ set_len ^^ + get_offset ^^ + get_len ^^ + guard_range env ^^ + get_offset ^^ + get_blob ^^ Blob.payload_ptr_unskewed ^^ + get_len ^^ + IC.system_call env "ic0" "stable_write") + | _ -> assert false + +end (* Stack *) + module RTS_Exports = struct let system_exports env = let bigint_trap_fi = E.add_fun env "bigint_trap" ( @@ -5025,7 +5349,8 @@ module Serialization = struct let deserialize env ts = Blob.of_size_copy env (fun env -> IC.system_call env "ic0" "msg_arg_data_size") - (fun env -> IC.system_call env "ic0" "msg_arg_data_copy") 0l ^^ + (fun env -> IC.system_call env "ic0" "msg_arg_data_copy") + (fun env -> compile_unboxed_const 0l) ^^ deserialize_from_blob false env ts (* @@ -5100,7 +5425,13 @@ To detect and preserve aliasing, these steps are taken: end (* Serialization *) -(* Stabilization (serialization to/from stable memory) *) +(* Stabilization (serialization to/from stable memory) of both: + * stable variables; and + * virtual stable memory. + c.f. + * ../../design/Stable.md + * ../../design/StableMemory.md +*) module Stabilization = struct @@ -5111,73 +5442,214 @@ module Stabilization = struct set_len ^^ set_dst ^^ - let (set_pages, get_pages) = new_local env "len" in - get_len ^^ - compile_add_const 4l ^^ (* reserve one word for size *) - compile_divU_const page_size ^^ - compile_add_const 1l ^^ - set_pages ^^ - - (* grow stable memory if needed *) - let (set_pages_needed, get_pages_needed) = new_local env "pages_needed" in - get_pages ^^ - E.call_import env "ic0" "stable_size" ^^ - G.i (Binary (Wasm.Values.I32 I32Op.Sub)) ^^ - set_pages_needed ^^ - - get_pages_needed ^^ - compile_unboxed_zero ^^ - G.i (Compare (Wasm.Values.I32 I32Op.GtS)) ^^ + StableMem.get_mem_size env ^^ + G.i (Test (Wasm.Values.I32 I32Op.Eqz)) ^^ G.if_ [] - ( get_pages_needed ^^ - E.call_import env "ic0" "stable_grow" ^^ - (* Check result *) - compile_unboxed_zero ^^ - G.i (Compare (Wasm.Values.I32 I32Op.LtS)) ^^ - E.then_trap_with env "Cannot grow stable memory." - ) G.nop - ^^ + begin( (* ensure [0,..,3,...len+4) *) + compile_unboxed_const 0l ^^ + get_len ^^ + compile_add_const 4l ^^ (* reserve one word for size *) + StableMem.ensure env ^^ - (* write len to initial word of stable memory*) - Stack.with_words env "get_size_ptr" 1l (fun get_size_ptr -> + (* write len to initial word of stable memory*) + compile_unboxed_const 0l ^^ + get_len ^^ + StableMem.write_word32 env ^^ - get_size_ptr ^^ get_len ^^ store_unskewed_ptr ^^ + (* copy data to following stable memory *) + compile_unboxed_const 4l ^^ + get_dst ^^ + get_len ^^ + E.call_import env "ic0" "stable_write") + end + begin + let (set_N, get_N) = new_local env "N" in - compile_unboxed_const 0l ^^ - get_size_ptr ^^ compile_unboxed_const 4l ^^ - IC.system_call env "ic0" "stable_write") ^^ + (* let N = !size * page_size *) + StableMem.get_mem_size env ^^ + compile_shl_const (Int32.of_int page_size_bits) ^^ + set_N ^^ - (* copy data to following stable memory *) - compile_unboxed_const 4l ^^ - get_dst ^^ - get_len ^^ - E.call_import env "ic0" "stable_write" + (* grow mem to page including address + N + 4 + len + 4 + 4 + 4 = N + len + 16 + *) + get_N ^^ + get_len ^^ + compile_add_const 16l ^^ + StableMem.ensure env ^^ + + get_N ^^ + get_len ^^ + StableMem.write_word32 env ^^ + + get_N ^^ + compile_add_const 4l ^^ + get_dst ^^ + get_len ^^ + E.call_import env "ic0" "stable_write" ^^ + + (* let M = pagesize * ic0.stable_size() - 1 *) + (* M is beginning of last page *) + let (set_M, get_M) = new_local env "M" in + E.call_import env "ic0" "stable_size" ^^ + compile_sub_const 1l ^^ + compile_shl_const (Int32.of_int page_size_bits) ^^ + set_M ^^ + + (* store mem_size at M + (pagesize - 12) *) + get_M ^^ + compile_add_const (Int32.sub page_size 12l) ^^ + StableMem.get_mem_size env ^^ + StableMem.write_word32 env ^^ + + (* save first word at M + (pagesize - 8); + mark first word as 0 *) + get_M ^^ + compile_add_const (Int32.sub page_size 8l) ^^ + compile_unboxed_const 0l ^^ + StableMem.read_and_clear_word32 env ^^ + StableMem.write_word32 env ^^ + (* save version at M + (pagesize - 4) *) + get_M ^^ + compile_add_const (Int32.sub page_size 4l) ^^ + compile_unboxed_const StableMem.version ^^ + StableMem.write_word32 env - (* return the initial i32 in stable memory recording the size of the following stable data *) - let stable_data_size env = + end + + let destabilize env ty = match E.mode env with | Flags.ICMode | Flags.RefMode -> - (* read size from initial word of (assumed non-empty) stable memory*) - Stack.with_words env "get_size_ptr" 1l (fun get_size_ptr -> - get_size_ptr ^^ compile_unboxed_const 0l ^^ compile_unboxed_const 4l ^^ - IC.system_call env "ic0" "stable_read" ^^ - get_size_ptr ^^ load_unskewed_ptr) - | _ -> assert false + let (set_pages, get_pages) = new_local env "pages" in + E.call_import env "ic0" "stable_size" ^^ + set_pages ^^ + + get_pages ^^ + G.i (Test (Wasm.Values.I32 I32Op.Eqz)) ^^ + G.if_ [I32Type] + begin + let (_, fs) = Type.as_obj ty in + let fs' = List.map + (fun f -> (f.Type.lab, fun () -> Opt.null_lit env)) + fs + in + StableMem.get_mem_size env ^^ + E.then_trap_with env "StableMem.mem_size non-zero" ^^ + Object.lit_raw env fs' + end + begin + let (set_marker, get_marker) = new_local env "marker" in + let (set_len, get_len) = new_local env "len" in + let (set_offset, get_offset) = new_local env "offset" in + compile_unboxed_const 0l ^^ + StableMem.read_and_clear_word32 env ^^ + set_marker ^^ + + get_marker ^^ + G.i (Test (Wasm.Values.I32 I32Op.Eqz)) ^^ + G.if_ [] + begin + let (set_M, get_M) = new_local env "M" in + let (set_version, get_version) = new_local env "version" in + let (set_N, get_N) = new_local env "N" in + + E.call_import env "ic0" "stable_size" ^^ + compile_sub_const 1l ^^ + compile_shl_const (Int32.of_int page_size_bits) ^^ + set_M ^^ + + (* read version *) + get_M ^^ + compile_add_const (Int32.sub page_size 4l) ^^ + StableMem.read_and_clear_word32 env ^^ + set_version ^^ + + (* check version *) + get_version ^^ + compile_unboxed_const StableMem.version ^^ + G.i (Compare (Wasm.Values.I32 I32Op.GtU)) ^^ + E.then_trap_with env (Printf.sprintf + "higher stable memory version (expected %s)" + (Int32.to_string StableMem.version)) ^^ + + (* restore StableMem bytes [0..4) *) + compile_unboxed_const 0l ^^ + get_M ^^ + compile_add_const (Int32.sub page_size 8l) ^^ + StableMem.read_and_clear_word32 env ^^ + StableMem.write_word32 env ^^ + + (* restore mem_size *) + get_M ^^ + compile_add_const (Int32.sub page_size 12l) ^^ + StableMem.read_and_clear_word32 env ^^ + StableMem.set_mem_size env ^^ + + StableMem.get_mem_size env ^^ + compile_shl_const (Int32.of_int page_size_bits) ^^ + set_N ^^ + + (* set len *) + get_N ^^ + StableMem.read_and_clear_word32 env ^^ + set_len ^^ + + (* set offset *) + get_N ^^ + compile_add_const 4l ^^ + set_offset + end + begin + (* assert mem_size == 0 *) + StableMem.get_mem_size env ^^ + E.then_trap_with env "unexpected, non-zero stable memory size" ^^ + + (* set len *) + get_marker ^^ + set_len ^^ - let destabilize env t = - Blob.of_size_copy env stable_data_size - (* copy the stable data from stable memory from offset 4 *) - (fun env -> IC.system_call env "ic0" "stable_read") 4l ^^ - Serialization.deserialize_from_blob true env [t] + (* set offset *) + compile_unboxed_const 4l ^^ + set_offset + end ^^ (* if_ *) + let (set_val, get_val) = new_local env "val" in + let (set_blob, get_blob) = new_local env "blob" in + + Blob.of_size_copy env + (fun env -> get_len) + (* copy the stable data from stable memory from offset 4 *) + (fun env -> IC.system_call env "ic0" "stable_read") + (fun env -> get_offset) ^^ + set_blob ^^ + + (* deserialize blob to val *) + get_blob ^^ + Serialization.deserialize_from_blob true env [ty] ^^ + set_val ^^ + + (* clear blob contents *) + get_blob ^^ + Blob.clear env ^^ + + (* copy zeros from blob to stable memory *) + get_offset ^^ + get_blob ^^ + Blob.as_ptr_len env ^^ + IC.system_call env "ic0" "stable_write" ^^ + + (* return val *) + get_val + end + | _ -> assert false end module GC = struct let register env static_roots = - let get_static_roots = E.add_fun env "get_static_roots" (Func.of_body env [] [I32Type] (fun env -> + let get_static_roots = E.add_fun env "get_static_roots" (Func.of_body env [] [I32Type] (fun env -> compile_unboxed_const static_roots )) in @@ -7113,7 +7585,6 @@ and compile_exp (env : E.t) ae exp = | _ -> SR.Unreachable, todo_trap env "compile_exp" (Arrange_ir.exp exp) end - (* Other prims, unary *) | SerializePrim ts, [e] -> SR.Vanilla, compile_exp_vanilla env ae e ^^ @@ -7125,6 +7596,8 @@ and compile_exp (env : E.t) ae exp = compile_exp_vanilla env ae e ^^ Serialization.deserialize_from_blob false env ts + (* Other prims, unary *) + | OtherPrim "array_len", [e] -> SR.Vanilla, compile_exp_vanilla env ae e ^^ @@ -7370,6 +7843,110 @@ and compile_exp (env : E.t) ae exp = | OtherPrim ("arrayToBlob"|"arrayMutToBlob"), e -> const_sr SR.Vanilla (Arr.toBlob env) + | OtherPrim ("stableMemoryLoadNat32"|"stableMemoryLoadInt32"), [e] -> + SR.UnboxedWord32, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word32 env + + | OtherPrim ("stableMemoryStoreNat32"|"stableMemoryStoreInt32"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.UnboxedWord32 e2 ^^ + StableMem.store_word32 env + + | OtherPrim ("stableMemoryLoadNat8"), [e] -> + SR.Vanilla, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word8 env ^^ + TaggedSmallWord.msb_adjust Type.Nat8 + + | OtherPrim ("stableMemoryLoadInt8"), [e] -> + SR.Vanilla, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word8 env ^^ + TaggedSmallWord.msb_adjust Type.Int8 + + | OtherPrim ("stableMemoryStoreNat8"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ TaggedSmallWord.lsb_adjust Type.Nat8 ^^ + StableMem.store_word8 env + + | OtherPrim ("stableMemoryStoreInt8"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ TaggedSmallWord.lsb_adjust Type.Int8 ^^ + StableMem.store_word8 env + + | OtherPrim ("stableMemoryLoadNat16"), [e] -> + SR.Vanilla, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word16 env ^^ + TaggedSmallWord.msb_adjust Type.Nat16 + + | OtherPrim ("stableMemoryLoadInt16"), [e] -> + SR.Vanilla, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word16 env ^^ + TaggedSmallWord.msb_adjust Type.Int16 + + | OtherPrim ("stableMemoryStoreNat16"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ TaggedSmallWord.lsb_adjust Type.Nat16 ^^ + StableMem.store_word16 env + + | OtherPrim ("stableMemoryStoreInt16"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ TaggedSmallWord.lsb_adjust Type.Int16 ^^ + StableMem.store_word16 env + + | OtherPrim ("stableMemoryLoadNat64" | "stableMemoryLoadInt64"), [e] -> + SR.UnboxedWord64, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_word64 env + + | OtherPrim ("stableMemoryStoreNat64" | "stableMemoryStoreInt64"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.UnboxedWord64 e2 ^^ + StableMem.store_word64 env + + | OtherPrim ("stableMemoryLoadFloat"), [e] -> + SR.UnboxedFloat64, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.load_float64 env + + | OtherPrim ("stableMemoryStoreFloat"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.UnboxedFloat64 e2 ^^ + StableMem.store_float64 env + + | OtherPrim ("stableMemoryLoadBlob"), [e1; e2] -> + SR.Vanilla, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ + Blob.lit env "Blob size out of bounds" ^^ + BigNum.to_word32_with env ^^ + StableMem.load_blob env + + | OtherPrim ("stableMemoryStoreBlob"), [e1; e2] -> + SR.unit, + compile_exp_as env ae SR.UnboxedWord32 e1 ^^ + compile_exp_as env ae SR.Vanilla e2 ^^ + StableMem.store_blob env + + | OtherPrim ("stableMemorySize"), [] -> + SR.UnboxedWord32, + StableMem.get_mem_size env + + | OtherPrim ("stableMemoryGrow"), [e] -> + SR.UnboxedWord32, + compile_exp_as env ae SR.UnboxedWord32 e ^^ + StableMem.logical_grow env + (* Other prims, binary*) | OtherPrim "Array.init", [_;_] -> const_sr SR.Vanilla (Arr.init env) @@ -7449,24 +8026,15 @@ and compile_exp (env : E.t) ae exp = end | ICStableRead ty, [] -> -(* - * On initial install: - 1. return record of nulls - * On upgrade: - 1. deserialize stable store to v : ty, - (TODO: inserting null values for missing fields.) - 2. return v -*) + (* + * On initial install: + 1. return record of nulls + * On upgrade: + 1. deserialize stable store to v : ty, + 2. return v + *) SR.Vanilla, - E.call_import env "ic0" "stable_size" ^^ - G.if_ [I32Type] - (Stabilization.destabilize env ty) - (let (_, fs) = Type.as_obj ty in - let fs' = List.map - (fun f -> (f.Type.lab, fun () -> Opt.null_lit env)) - fs in - Object.lit_raw env fs') - + Stabilization.destabilize env ty | ICStableWrite ty, [e] -> SR.unit, compile_exp_vanilla env ae e ^^ @@ -8223,6 +8791,7 @@ let compile mode rts (prog : Ir.prog) : Wasm_exts.CustomModule.extended_module = let env = E.mk_global mode rts IC.trap_with Lifecycle.end_ in Stack.register_globals env; + StableMem.register_globals env; IC.system_imports env; RTS.system_imports env; diff --git a/src/prelude/prim.mo b/src/prelude/prim.mo index fcc55c2e8bf..8c50f1e4989 100644 --- a/src/prelude/prim.mo +++ b/src/prelude/prim.mo @@ -280,3 +280,71 @@ func cyclesAdd(amount: Nat64) : () { // certified data func setCertifiedData(data : Blob) = (prim "setCertifiedData" : Blob -> ()) data; func getCertificate() : ?Blob = (prim "getCertificate" : () -> ?Blob) (); + +// stable memory + +func stableMemorySize() : Nat32 = + (prim "stableMemorySize" : () -> Nat32) (); + +func stableMemoryGrow(pages : Nat32) : Nat32 = + (prim "stableMemoryGrow" : Nat32 -> Nat32) pages; + +func stableMemoryLoadNat32(offset : Nat32) : Nat32 = + (prim "stableMemoryLoadNat32" : Nat32 -> Nat32) offset; + +func stableMemoryStoreNat32(offset : Nat32, val : Nat32) : () = + (prim "stableMemoryStoreNat32" : (Nat32, Nat32) -> ()) (offset, val); + +func stableMemoryLoadNat8(offset : Nat32) : Nat8 = + (prim "stableMemoryLoadNat8" : Nat32 -> Nat8) offset; + +func stableMemoryStoreNat8(offset : Nat32, val : Nat8) : () = + (prim "stableMemoryStoreNat8" : (Nat32, Nat8) -> ()) (offset, val); + +func stableMemoryLoadNat16(offset : Nat32) : Nat16 = + (prim "stableMemoryLoadNat16" : Nat32 -> Nat16) offset; + +func stableMemoryStoreNat16(offset : Nat32, val : Nat16) : () = + (prim "stableMemoryStoreNat16" : (Nat32, Nat16) -> ()) (offset, val); + +func stableMemoryLoadNat64(offset : Nat32) : Nat64 = + (prim "stableMemoryLoadNat64" : Nat32 -> Nat64) offset; + +func stableMemoryStoreNat64(offset : Nat32, val : Nat64) : () = + (prim "stableMemoryStoreNat64" : (Nat32, Nat64) -> ()) (offset, val); + +func stableMemoryLoadInt32(offset : Nat32) : Int32 = + (prim "stableMemoryLoadInt32" : Nat32 -> Int32) offset; + +func stableMemoryStoreInt32(offset : Nat32, val : Int32) : () = + (prim "stableMemoryStoreInt32" : (Nat32, Int32) -> ()) (offset, val); + +func stableMemoryLoadInt8(offset : Nat32) : Int8 = + (prim "stableMemoryLoadInt8" : Nat32 -> Int8) offset; + +func stableMemoryStoreInt8(offset : Nat32, val : Int8) : () = + (prim "stableMemoryStoreInt8" : (Nat32, Int8) -> ()) (offset, val); + +func stableMemoryLoadInt16(offset : Nat32) : Int16 = + (prim "stableMemoryLoadInt16" : Nat32 -> Int16) offset; + +func stableMemoryStoreInt16(offset : Nat32, val : Int16) : () = + (prim "stableMemoryStoreInt16" : (Nat32, Int16) -> ()) (offset, val); + +func stableMemoryLoadInt64(offset : Nat32) : Int64 = + (prim "stableMemoryLoadInt64" : Nat32 -> Int64) offset; + +func stableMemoryStoreInt64(offset : Nat32, val : Int64) : () = + (prim "stableMemoryStoreInt64" : (Nat32, Int64) -> ()) (offset, val); + +func stableMemoryLoadFloat(offset : Nat32) : Float = + (prim "stableMemoryLoadFloat" : Nat32 -> Float) offset; + +func stableMemoryStoreFloat(offset : Nat32, val : Float) : () = + (prim "stableMemoryStoreFloat" : (Nat32, Float) -> ()) (offset, val); + +func stableMemoryLoadBlob(offset : Nat32, size : Nat) : Blob = + (prim "stableMemoryLoadBlob" : (Nat32, Nat) -> Blob) (offset, size); + +func stableMemoryStoreBlob(offset : Nat32, val : Blob) : () = + (prim "stableMemoryStoreBlob" : (Nat32, Blob) -> ()) (offset, val); diff --git a/test/run-drun/life-mut.drun b/test/run-drun/life-mut.drun index 32c1658102d..83ca4393e4a 100644 --- a/test/run-drun/life-mut.drun +++ b/test/run-drun/life-mut.drun @@ -20,3 +20,13 @@ upgrade $ID life-mut/life-v2.mo "" query $ID show "DIDL\x00\x00" ingress $ID advance "DIDL\x00\x01\x7d\x0F" query $ID show "DIDL\x00\x00" +# upgrade from life-v2 to life-v3 +upgrade $ID life-mut/life-v3.mo "" +query $ID show "DIDL\x00\x00" +ingress $ID advance "DIDL\x00\x01\x7d\x01" +query $ID show "DIDL\x00\x00" +# self-upgrade from life-v3 +upgrade $ID life-mut/life-v3.mo "" +query $ID show "DIDL\x00\x00" +ingress $ID advance "DIDL\x00\x01\x7d\x0F" +query $ID show "DIDL\x00\x00" diff --git a/test/run-drun/life-mut/life-v1.mo b/test/run-drun/life-mut/life-v1.mo index e37b85aa6de..02c37c228d2 100644 --- a/test/run-drun/life-mut/life-v1.mo +++ b/test/run-drun/life-mut/life-v1.mo @@ -15,22 +15,12 @@ actor Life { #v1 : [[var Cell]]; }; - class Grid(#v1 state : State) { + class Grid(#v1 grid : State) { - let n = state.size(); + let n = grid.size(); public func size() : Nat { n }; - let grid = P.Array_tabulate(n, func (i : Nat) : [var Cell] { - let a = P.Array_init(n, false); - let si = state[i]; - assert (si.size() == n); - for (j in si.keys()) { - a[j] := si[j]; - }; - a - }); - public func get(i : Nat, j : Nat) : Cell { grid[i][j] }; public func set(i : Nat, j : Nat, v : Cell) { grid[i][j] := v }; @@ -79,20 +69,21 @@ actor Life { }; }; - stable var state : State = - do { - let n = 32; + func newState(size : Nat) : State { + let size = 32; #v1 ( - P.Array_tabulate<[var Cell]>(n, + P.Array_tabulate<[var Cell]>(size, func i { - let ai = P.Array_init(n,false); + let ai = P.Array_init(size, false); for (j in ai.keys()) { ai[j] := Random.next() }; ai }) ) - }; + }; + + stable var state : State = newState(32); - flexible var src = Grid(state); - flexible var dst = Grid(state); + var src = Grid(state); + var dst = Grid(newState(src.size())); func update(c : Nat) { var i = c; diff --git a/test/run-drun/life-mut/life-v2.mo b/test/run-drun/life-mut/life-v2.mo index b0a9e3f4e7e..bb92adc5b75 100644 --- a/test/run-drun/life-mut/life-v2.mo +++ b/test/run-drun/life-mut/life-v2.mo @@ -115,9 +115,8 @@ actor Life { }; }; - stable var state : State = do { - let n = 32; - let len = (n * n) / 64 + 1; + func newState(size : Nat) : State { + let len = (size * size) / 64 + 1; let words = P.Array_init(len, 0); for (i in words.keys()) { var word : Nat64 = 0; @@ -128,11 +127,13 @@ actor Life { }; words[i] := word; }; - #v2 { size = n; bits = words }; + #v2 { size = size; bits = words }; }; - flexible var src = Grid(state); - flexible var dst = Grid(state); + stable var state : State = newState(32); + + var src = Grid(state); + var dst = Grid(newState(src.size())); func update(c : Nat) { var i = c; diff --git a/test/run-drun/life-mut/life-v3.mo b/test/run-drun/life-mut/life-v3.mo new file mode 100644 index 00000000000..76adbfb982e --- /dev/null +++ b/test/run-drun/life-mut/life-v3.mo @@ -0,0 +1,206 @@ +import P = "mo:⛔"; +import SM = "../stable-mem/StableMemory"; + +actor Life { + + object Random { + var state = 1; + public func next() : Bool { + state := (123138118391*state + 133489131) % 9999; + (state % 2 == 0) + }; + }; + + class below(u : Nat) { + var i = 0; + public func next() : ?Nat { if (i >= u) null else {let j = i; i += 1; ?j} }; + }; + + func readBitV2(bits : [var Nat64], index : Nat) : Bool { + let bit = P.natToNat64(index); + let mask : Nat64 = 1 << (bit % 64); + (bits[P.nat64ToNat(bit >> 6)] & mask) == mask + }; + + func readBit(offset : Nat32, index : Nat) : Bool { + let bit = P.natToNat32(index); + let mask : Nat32 = 1 << (bit % 32); + (SM.loadNat32(offset + ((bit >> 5)*4)) & mask) == mask + }; + + func writeBit(offset : Nat32, index : Nat, v : Bool) { + let bit = P.natToNat32(index); + let mask : Nat32 = 1 << (bit % 32); + let i = (bit >> 5)*4; + if v { + SM.storeNat32(offset + i, SM.loadNat32(offset + i) | mask) + } + else { + SM.storeNat32(offset + i, SM.loadNat32(offset + i) & ^mask) + }; + assert (readBit(offset, index) == v); + }; + + type Cell = Bool; + + type State = { + #v1 : [[var Cell]]; + #v2 : {size : Nat; bits : [var Nat64]}; + #v3 : {size : Nat; offset : Nat32} + }; + + + func ensureMemory(offset : Nat32) { + let pagesNeeded = ((offset + 65535) / 65536) - SM.size(); + if (pagesNeeded > 0) { + assert (SM.grow(pagesNeeded) != 0xFFFF) + }; + }; + + class Grid(index : Nat, state : State) { + + let (n : Nat, offset) = + switch state { + case (#v1 css) { + let n = css.size(); + let len = (n * n) / 32 + 1; + let offset : Nat32 = P.natToNat32(index * len * 4); + ensureMemory(offset + P.natToNat32(len) * 4); + for (i in css.keys()) { + for (j in css[i].keys()) { + writeBit(offset, i * n + j, css[i][j]); + }; + }; + (n, offset) + }; + case (#v2 {size; bits}) { + let len = (size * size) / 32 + 1; + let offset : Nat32 = P.natToNat32(index * len * 4); + ensureMemory(offset + P.natToNat32(len) * 4); + for (i in below(size)) { + for (j in below(size)) { + let k = i * size + j; + writeBit(offset, k, readBitV2(bits, k)); + } + }; + (size, offset) + }; + case (#v3 {size; offset}) { + let len = (size * size) / 32 + 1; + let newoffset : Nat32 = P.natToNat32(index * len * 4); + ensureMemory(newoffset + P.natToNat32(len) * 4); + if (offset != newoffset) { + for (i in below(size)) { + for (j in below(size)) { + let k = i * size + j; + writeBit(newoffset, k, readBit(offset, k)); + } + }; + }; + (size, newoffset) + }; + }; + + public func size() : Nat { n }; + + public func get(i : Nat, j : Nat) : Cell { + readBit(offset, i * n + j); + }; + + public func set(i : Nat, j : Nat, v : Cell) { + writeBit(offset, i * n + j, v); + }; + + func pred(i : Nat) : Nat { (n + i - 1) % n }; + + func succ(i : Nat) : Nat { (i + 1) % n }; + + func count(i : Nat, j : Nat) : Nat { if (get(i, j)) 1 else 0 }; + + func living(i : Nat, j : Nat) : Nat { + count(pred i, pred j) + count(pred i, j) + count(pred i, succ j) + + count( i, pred j) + count( i, succ j) + + count(succ i, pred j) + count(succ i, j) + count(succ i, succ j) + }; + + func nextCell(i : Nat, j : Nat) : Cell { + let l : Nat = living(i, j); + if (get(i, j)) + l == 2 or l == 3 + else + l == 3; + }; + + public func next(dst : Grid) { + for (i in below(n)) { + for (j in below(n)) { + dst.set(i, j, nextCell(i, j)); + }; + }; + }; + + public func toState() : State { + #v3 { size = n; offset = offset } + }; + + public func toText() : Text { + var t = "\n"; + for (i in below(n)) { + for (j in below(n)) { + t #= if (get(i, j)) "O" else " "; + }; + t #= "\n"; + }; + t + }; + }; + + func newState(index : Nat, size : Nat) : State { + let len = (size * size) / 32 + 1; + let offset : Nat32 = P.natToNat32(index * len * 4); + ensureMemory(offset + P.natToNat32(len) * 4); + for (i in below(len)) { + var word : Nat32 = 0; + for (j in below(32)) { + let bit : Nat32 = if (Random.next()) 0 else 1; + word |= bit; + word <<= 1; + }; + SM.storeNat32(offset + P.natToNat32(i) * 4, word ); + }; + #v3 { size; offset}; + }; + + stable var state : State = newState(0, 32); + + flexible var src = Grid(0, state); + flexible var dst = Grid(32, newState(32, src.size())); + + func update(c : Nat) { + var i = c; + while (i > 0) { + src.next(dst); + let temp = src; + src := dst; + dst := temp; + i -= 1; + }; + }; + + system func preupgrade() { + state := src.toState(); + }; + + system func postupgrade() { + P.debugPrint("upgraded!"); + }; + + public func advance(n : Nat) : async () { + update(n); + }; + + public query func show() : async () { + P.debugPrint(src.toText()); + }; + +}; diff --git a/test/run-drun/ok/life-mut.drun.ok b/test/run-drun/ok/life-mut.drun.ok index ef1671ca4ea..9f49c429700 100644 --- a/test/run-drun/ok/life-mut.drun.ok +++ b/test/run-drun/ok/life-mut.drun.ok @@ -221,37 +221,183 @@ OO O OO O O O OO Ok: Reply: 0x4449444c0000 ingress Completed: Reply: 0x4449444c0000 debug.print: -O O O O O O O -O OOO OOOOO OO O O OO O -OO O O O O O O O - OOO O OOO OOO OO O O -OO OO O O O O - O O O O O O -OO OO OOO O O - O O O O O OO OO O O - OOO O OO - OO O O - O O O O - OO O O OO - O O O - O O OO - OOOO O OO - O OO - O OO - OO O O - O O -O OOOOOO O O O - O OO OO O -O OO O OO - O O O OOO O - O O O OO O - O O O OOO O OO O - OO O O O O O - O OO O O O O - OO O OO OO O O -O O OO O O O OO - O O O O O O O OOO -O O O OO O O O O OOO O O -O O OOOOO OOOOOOO O O O OO O +O + OOO + O + OO + O O + OO + + OO + OO + + O + O + O + OO OOOO O + O O + O O O O OO + O OO O + O O OO + O O OO OO + OO O O + + + + OOO + + O O + O O + O O +OO O O +OO O OOO O + OO O O +O O + +Ok: Reply: 0x4449444c0000 +debug.print: upgraded! +ingress Completed: Reply: 0x4449444c0000 +debug.print: +O + OOO + O + OO + O O + OO + + OO + OO + + O + O + O + OO OOOO O + O O + O O O O OO + O OO O + O O OO + O O OO OO + OO O O + + + + OOO + + O O + O O + O O +OO O O +OO O OOO O + OO O O +O O + +Ok: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: +O O O + O + O OO + OOO + O OO + OO + + OO + OO + + + OOO + + OO OOO + OO O O O OO + OO O O O + OOO OOO O O + OO O OO O + OO OO O + OO OO OO + + + O + O + O + + OOO OOO + + OO O O + OOOO O O O + O OO O O +O O + +Ok: Reply: 0x4449444c0000 +debug.print: upgraded! +ingress Completed: Reply: 0x4449444c0000 +debug.print: +O O O + O + O OO + OOO + O OO + OO + + OO + OO + + + OOO + + OO OOO + OO O O O OO + OO O O O + OOO OOO O O + OO O OO O + OO OO O + OO OO OO + + + O + O + O + + OOO OOO + + OO O O + OOOO O O O + O OO O O +O O + +Ok: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: + O + O + OO + O OOOO + OOOOOOO + O + O + OO O OO + OOOO OO + O O O + O O O + O O O OO + OO OO O + O O O + O OO O + O OOO + OO O OO + O + O OO + OO O O + O O + + O + O O + O O + OOOO + O OOO + O OOO + O O +O OOOOO + + Ok: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-blob.drun-run.ok b/test/run-drun/ok/stable-mem-blob.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-blob.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-float.drun-run.ok b/test/run-drun/ok/stable-mem-float.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-float.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-grow.drun-run.ok b/test/run-drun/ok/stable-mem-grow.drun-run.ok new file mode 100644 index 00000000000..5b71d74f731 --- /dev/null +++ b/test/run-drun/ok/stable-mem-grow.drun-run.ok @@ -0,0 +1,17 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading from 0 +debug.print: to 1 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading from 1 +debug.print: to 2 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading from 2 +debug.print: to 3 +ingress Completed: Reply: 0x4449444c0000 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading from 3 +debug.print: to 4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-int16.drun-run.ok b/test/run-drun/ok/stable-mem-int16.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-int16.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-int32.drun-run.ok b/test/run-drun/ok/stable-mem-int32.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-int32.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-int64.drun-run.ok b/test/run-drun/ok/stable-mem-int64.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-int64.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-int8.drun-run.ok b/test/run-drun/ok/stable-mem-int8.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-int8.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-nat16.drun-run.ok b/test/run-drun/ok/stable-mem-nat16.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-nat16.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-nat32.drun-run.ok b/test/run-drun/ok/stable-mem-nat32.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-nat32.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-nat64.drun-run.ok b/test/run-drun/ok/stable-mem-nat64.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-nat64.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/ok/stable-mem-nat8.drun-run.ok b/test/run-drun/ok/stable-mem-nat8.drun-run.ok new file mode 100644 index 00000000000..4ee32ff971d --- /dev/null +++ b/test/run-drun/ok/stable-mem-nat8.drun-run.ok @@ -0,0 +1,24 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...0 +debug.print: {new = 1; old = 0; size = 1} +debug.print: ...upgraded1 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 1} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...1 +debug.print: {new = 2; old = 1; size = 2} +debug.print: ...upgraded2 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 2} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...2 +debug.print: {new = 3; old = 2; size = 3} +debug.print: ...upgraded3 +ingress Completed: Reply: 0x4449444c0000 +debug.print: {testBounds = 3} +ingress Completed: Reply: 0x4449444c0000 +debug.print: upgrading...3 +debug.print: {new = 4; old = 3; size = 4} +debug.print: ...upgraded4 +ingress Completed: Reply: 0x4449444c0000 diff --git a/test/run-drun/stable-mem-blob.mo b/test/run-drun/stable-mem-blob.mo new file mode 100644 index 00000000000..ae17311613c --- /dev/null +++ b/test/run-drun/stable-mem-blob.mo @@ -0,0 +1,105 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Blob { + let size = P.nat32ToNat(n); + let a = P.Array_tabulate(size, func i { P.natToNat8(i % 256) }); + P.arrayToBlob(a); + }; + + func zeroOfNat32(n : Nat32) : Blob { + let size = P.nat32ToNat(n); + let a = P.Array_tabulate(size, func i { 0 }); + P.arrayToBlob(a); + }; + + let inc : Nat32 = 8; + + var i : Nat32 = 0; + var size : Nat32 = 0; + let max = n * 65536; + while (i + size < max) { + let v = valOfNat32(size); + StableMemory.storeBlob(i, v); + assert (StableMemory.loadBlob(i, P.nat32ToNat(size)) == v); + i += size; + size += 1; + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + var size : Nat32 = 0; + let max = i + 65536; + while (i + size < max) { + assert (StableMemory.loadBlob(i, P.nat32ToNat(size)) == zeroOfNat32(size)); + StableMemory.storeBlob(i, valOfNat32(size)); + i += size; + size += 1; + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 7; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadBlob(i, 8); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeBlob(i, valOfNat32(8)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-float.mo b/test/run-drun/stable-mem-float.mo new file mode 100644 index 00000000000..1abfe513454 --- /dev/null +++ b/test/run-drun/stable-mem-float.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Float { P.intToFloat(P.nat32ToNat(n)); }; + let inc : Nat32 = 8; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadFloat(i) == v); + StableMemory.storeFloat(i, -v); + assert (StableMemory.loadFloat(i) == -v); + StableMemory.storeFloat(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadFloat(i) == 0); + StableMemory.storeFloat(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 7; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadFloat(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeFloat(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-grow.mo b/test/run-drun/stable-mem-grow.mo new file mode 100644 index 00000000000..b2061bbcc03 --- /dev/null +++ b/test/run-drun/stable-mem-grow.mo @@ -0,0 +1,55 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; + +actor { + + stable var n : Nat32 = 0; + + system func preupgrade() { + P.debugPrint("upgrading from " # debug_show n); + let m = StableMemory.grow(1); + + // check all pages clear + var i : Nat32 = 0; + let max = StableMemory.size() * 65536; + while (i < max) { + assert (StableMemory.loadNat32(i) == 0); + i += 4 + }; + + n += 1; + + }; + + public func testGrow() : async () { + var i : Nat32 = 0; + while (i < 10) { + var pre = StableMemory.size(); + var post = StableMemory.grow(i); + assert StableMemory.size() == StableMemory.grow(0); + assert post == pre; + assert StableMemory.size() == pre + i; + i += 1; + } + }; + + system func postupgrade() { + P.debugPrint("to " # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testGrow "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testGrow "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testGrow "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-int16.mo b/test/run-drun/stable-mem-int16.mo new file mode 100644 index 00000000000..4f0d1e22502 --- /dev/null +++ b/test/run-drun/stable-mem-int16.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Int16 { P.intToInt16(P.nat32ToNat(n % 65536) : Int - 32768) }; + let inc : Nat32 = 2; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadInt16(i) == v); + StableMemory.storeInt16(i, ^v); + assert (StableMemory.loadInt16(i) == ^v); + StableMemory.storeInt16(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadInt16(i) == 0); + StableMemory.storeInt16(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 1; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadInt16(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeInt16(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-int32.mo b/test/run-drun/stable-mem-int32.mo new file mode 100644 index 00000000000..7bd49384470 --- /dev/null +++ b/test/run-drun/stable-mem-int32.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Int32 { P.intToInt32(P.nat32ToNat(n) : Int - 32768)}; + let inc : Nat32 = 4; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadInt32(i) == v); + StableMemory.storeInt32(i, ^v); + assert (StableMemory.loadInt32(i) == ^v); + StableMemory.storeInt32(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadInt32(i) == 0); + StableMemory.storeInt32(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 3; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadInt32(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeInt32(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-int64.mo b/test/run-drun/stable-mem-int64.mo new file mode 100644 index 00000000000..5cad8da21d8 --- /dev/null +++ b/test/run-drun/stable-mem-int64.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Int64 { P.intToInt64(P.nat32ToNat(n) : Int - 32768)}; + let inc : Nat32 = 8; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadInt64(i) == v); + StableMemory.storeInt64(i, ^v); + assert (StableMemory.loadInt64(i) == ^v); + StableMemory.storeInt64(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadInt64(i) == 0); + StableMemory.storeInt64(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 7; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadInt64(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeInt64(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-int8.mo b/test/run-drun/stable-mem-int8.mo new file mode 100644 index 00000000000..b2167f14293 --- /dev/null +++ b/test/run-drun/stable-mem-int8.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Int8 { P.intToInt8(P.nat32ToNat(n % 256) : Int - 128); }; + let inc : Nat32 = 1; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadInt8(i) == v); + StableMemory.storeInt8(i, ^v); + assert (StableMemory.loadInt8(i) == ^v); + StableMemory.storeInt8(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadInt8(i) == 0); + StableMemory.storeInt8(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 0; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadInt8(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeInt8(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-nat16.mo b/test/run-drun/stable-mem-nat16.mo new file mode 100644 index 00000000000..386defca3a0 --- /dev/null +++ b/test/run-drun/stable-mem-nat16.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Nat16 { P.natToNat16(P.nat32ToNat(n % 65536)); }; + let inc : Nat32 = 2; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadNat16(i) == v); + StableMemory.storeNat16(i, ^v); + assert (StableMemory.loadNat16(i) == ^v); + StableMemory.storeNat16(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadNat16(i) == 0); + StableMemory.storeNat16(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 1; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadNat16(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeNat16(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-nat32.mo b/test/run-drun/stable-mem-nat32.mo new file mode 100644 index 00000000000..c8accc2a00f --- /dev/null +++ b/test/run-drun/stable-mem-nat32.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Nat32 { n }; + let inc : Nat32 = 4; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadNat32(i) == v); + StableMemory.storeNat32(i, ^v); + assert (StableMemory.loadNat32(i) == ^v); + StableMemory.storeNat32(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadNat32(i) == 0); + StableMemory.storeNat32(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 3; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadNat32(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeNat32(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-nat64.mo b/test/run-drun/stable-mem-nat64.mo new file mode 100644 index 00000000000..5660e31946c --- /dev/null +++ b/test/run-drun/stable-mem-nat64.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Nat64 { P.natToNat64(P.nat32ToNat(n)); }; + let inc : Nat32 = 8; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadNat64(i) == v); + StableMemory.storeNat64(i, ^v); + assert (StableMemory.loadNat64(i) == ^v); + StableMemory.storeNat64(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadNat64(i) == 0); + StableMemory.storeNat64(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 7; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadNat64(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeNat64(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem-nat8.mo b/test/run-drun/stable-mem-nat8.mo new file mode 100644 index 00000000000..5c4a0c012de --- /dev/null +++ b/test/run-drun/stable-mem-nat8.mo @@ -0,0 +1,92 @@ +import P "mo:⛔"; +import StableMemory "stable-mem/StableMemory"; +actor { + + stable var n : Nat32 = 0; + assert (n == StableMemory.size()); + + func valOfNat32(n : Nat32) : Nat8 { P.natToNat8(P.nat32ToNat(n % 256)); }; + let inc : Nat32 = 1; + + var i : Nat32 = 0; + let max = n * 65536; + while (i < max) { + let v = valOfNat32(i); + assert (StableMemory.loadNat8(i) == v); + StableMemory.storeNat8(i, ^v); + assert (StableMemory.loadNat8(i) == ^v); + StableMemory.storeNat8(i, v); + i += inc + }; + + system func preupgrade() { + P.debugPrint("upgrading..." # debug_show n); + let m = StableMemory.grow(1); + + assert (n == m); + + n += 1; + + P.debugPrint(debug_show {old = m; new = n; size = StableMemory.size()}); + + assert (n == StableMemory.size()); + + // check new page is clear + var i : Nat32 = m * 65536; + let max = i + 65536; + while (i < max) { + assert (StableMemory.loadNat8(i) == 0); + StableMemory.storeNat8(i, valOfNat32(i)); + i += inc + }; + + }; + + public func testBounds() : async () { + if (n == 0) return; + assert (n == StableMemory.size()); + P.debugPrint (debug_show {testBounds=n}); + // test bounds check + var i : Nat32 = n * 65536 - 0; + let max = i + 16; + while (i < max) { + try { + await async { + ignore StableMemory.loadNat8(i); + }; + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + try { + await async StableMemory.storeNat8(i, valOfNat32(i)); + assert false; + } + catch e { + assert P.errorCode e == #canister_error; + }; + i += 1; + }; + }; + + system func postupgrade() { + P.debugPrint("...upgraded" # debug_show n); + }; + +} + +//SKIP run +//SKIP run-low +//SKIP run-ir +// too slow on ic-ref-run: +//SKIP comp-ref + +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" +//CALL ingress testBounds "DIDL\x00\x00" +//CALL upgrade "" + diff --git a/test/run-drun/stable-mem/StableMemory.mo b/test/run-drun/stable-mem/StableMemory.mo new file mode 100644 index 00000000000..ef6f485f326 --- /dev/null +++ b/test/run-drun/stable-mem/StableMemory.mo @@ -0,0 +1,38 @@ +import Prim "mo:⛔"; + +module { + + public let size = Prim.stableMemorySize; + public let grow = Prim.stableMemoryGrow; + + public let loadNat32 = Prim.stableMemoryLoadNat32; + public let storeNat32 = Prim.stableMemoryStoreNat32; + + public let loadNat8 = Prim.stableMemoryLoadNat8; + public let storeNat8 = Prim.stableMemoryStoreNat8; + + public let loadNat16 = Prim.stableMemoryLoadNat16; + public let storeNat16 = Prim.stableMemoryStoreNat16; + + public let loadNat64 = Prim.stableMemoryLoadNat64; + public let storeNat64 = Prim.stableMemoryStoreNat64; + + public let loadFloat = Prim.stableMemoryLoadFloat; + public let storeFloat= Prim.stableMemoryStoreFloat; + + public let loadInt32 = Prim.stableMemoryLoadInt32; + public let storeInt32 = Prim.stableMemoryStoreInt32; + + public let loadInt8 = Prim.stableMemoryLoadInt8; + public let storeInt8 = Prim.stableMemoryStoreInt8; + + public let loadInt16 = Prim.stableMemoryLoadInt16; + public let storeInt16 = Prim.stableMemoryStoreInt16; + + public let loadInt64 = Prim.stableMemoryLoadInt64; + public let storeInt64 = Prim.stableMemoryStoreInt64; + + public let loadBlob = Prim.stableMemoryLoadBlob; + public let storeBlob = Prim.stableMemoryStoreBlob; + +} From 0dc1ac9494d26987d52511ac2385bb8503735a34 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Thu, 9 Sep 2021 17:11:59 -0700 Subject: [PATCH 10/56] niv motoko-base: update f799e061 -> e6730a3f (#2775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for motoko-base: Branch: next-moc Commits: [dfinity/motoko-base@f799e061...e6730a3f](https://github.com/dfinity/motoko-base/compare/f799e061b53e7565809da7c558afd7403d2ad836...e6730a3ff6d0852956fcde8b39334e546ecd6c29) * [`ea70e817`](https://github.com/dfinity/motoko-base/commit/ea70e817776cef62150926775e8c4ea6815b7fab) Fix arithmetic overflow in TrieMap. ([dfinity/motoko-base⁠#279](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/motoko-base/issues/279)) --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index ba09d2afa56..ab12200d735 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "f799e061b53e7565809da7c558afd7403d2ad836", - "sha256": "0dx9qd3flgyds7qijr9jbsmyi7dl42fy4vw7ddfxqr4lhrqbyf9c", + "rev": "e6730a3ff6d0852956fcde8b39334e546ecd6c29", + "sha256": "0zkikh6dg5vl270hsx0ilxkwcin47vf1yl6f8r48br4lrmk11n4k", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/f799e061b53e7565809da7c558afd7403d2ad836.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/e6730a3ff6d0852956fcde8b39334e546ecd6c29.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From f3d1a87f3a065fde3cfe50dba2c17f51d3de2dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 10 Sep 2021 12:00:02 +0300 Subject: [PATCH 11/56] RTS: remove repr(packed) attributes from RTS structs (#2764) This is to remove some of the potential undefined behaviors (UB). It will also remove some of the syntactic noise in #2761. Rust has alignment restrictions for types and fields beyond the hardware limitations. This means even on Wasm (which has no alignment restrictions) we have some restrictions to deal with. Recently the compiler started to check for some of the obvious sources of UB (https://github.com/rust-lang/rust/issues/82523). One of these is when taking a pointer or reference to a `packed` struct. Since `packed` means no padding between fields, the fields may be unaligned, in which case getting a reference to them would be UB. To fix this, this PR removes `packed` attributes and refactors the `Bits64` type to make sure that the layout is as before. It turns out for types other than `Bits64` we don't need `packed`: all fields are word sizes so the compiler does not add any padding. For `Bits64`, we had a `u64` field which is aligned on 64-bit boundary. To avoid this we now split 64-bit payload into two 32-bit fields. A new module `static_checks` added to make sure struct sizes are as expected. Assertions in this module are checked in compile time. No extra work needed to align objects. All objects need 4 bytes alignment (checked with `core::mem::align_of`). The compiler already [aligns static objects][2], and in runtime we only allocate whole words. So both static and dynamic objects are always aligned. Debug mode assertions added in GC to check object alignment. [1]: https://doc.rust-lang.org/stable/reference/ [2]: https://github.com/dfinity/motoko/blob/59ddaa4520793b6e7038b60e3c30ff267d8415f6/src/codegen/compile.ml#L453 --- default.nix | 2 +- rts/motoko-rts-tests/Cargo.lock | 7 ++++ rts/motoko-rts/Cargo.lock | 7 ++++ rts/motoko-rts/Cargo.toml | 1 + rts/motoko-rts/native/Cargo.toml | 1 + rts/motoko-rts/src/debug.rs | 2 +- rts/motoko-rts/src/gc/copying.rs | 4 ++ rts/motoko-rts/src/gc/mark_compact.rs | 3 ++ rts/motoko-rts/src/lib.rs | 1 + rts/motoko-rts/src/static_checks.rs | 54 +++++++++++++++++++++++++++ rts/motoko-rts/src/types.rs | 28 +++++--------- 11 files changed, 90 insertions(+), 20 deletions(-) create mode 100644 rts/motoko-rts/src/static_checks.rs diff --git a/default.nix b/default.nix index a2a3c85ae6d..ddbd022641d 100644 --- a/default.nix +++ b/default.nix @@ -159,7 +159,7 @@ rec { name = "motoko-rts-deps"; src = subpath ./rts; sourceRoot = "rts/motoko-rts-tests"; - sha256 = "0sy7jglz9pxw2lz0qjyplchcfn78d7789sd93xwybisamjynlniy"; + sha256 = "0jyp3j8n5bj5cy1fd26d7h55zmc4v14qc2w8adxqwmsv5riqz41g"; copyLockfile = true; }; in diff --git a/rts/motoko-rts-tests/Cargo.lock b/rts/motoko-rts-tests/Cargo.lock index 8d2c9de6ffb..6b60a63f4e4 100644 --- a/rts/motoko-rts-tests/Cargo.lock +++ b/rts/motoko-rts-tests/Cargo.lock @@ -69,6 +69,7 @@ dependencies = [ "compiler_builtins", "libc", "motoko-rts-macros", + "static_assertions", ] [[package]] @@ -192,6 +193,12 @@ version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.73" diff --git a/rts/motoko-rts/Cargo.lock b/rts/motoko-rts/Cargo.lock index 9b2cb23c250..1e4ffb001c7 100644 --- a/rts/motoko-rts/Cargo.lock +++ b/rts/motoko-rts/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ "compiler_builtins", "libc", "motoko-rts-macros", + "static_assertions", ] [[package]] @@ -48,6 +49,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.73" diff --git a/rts/motoko-rts/Cargo.toml b/rts/motoko-rts/Cargo.toml index 4f07d044f69..575b327b100 100644 --- a/rts/motoko-rts/Cargo.toml +++ b/rts/motoko-rts/Cargo.toml @@ -21,6 +21,7 @@ ic = [] [dependencies] libc = { version = "0.2.81", default_features = false } motoko-rts-macros = { path = "../motoko-rts-macros" } +static_assertions = "1.1.0" # Added here so that it ends up in Cargo.lock, so that nix will pre-fetch it [dependencies.compiler_builtins] diff --git a/rts/motoko-rts/native/Cargo.toml b/rts/motoko-rts/native/Cargo.toml index 788ac1c4efb..9c8913e32ff 100644 --- a/rts/motoko-rts/native/Cargo.toml +++ b/rts/motoko-rts/native/Cargo.toml @@ -11,6 +11,7 @@ path = "../src/lib.rs" [dependencies] libc = { version = "0.2.73", default_features = false } motoko-rts-macros = { path = "../../motoko-rts-macros" } +static_assertions = "1.1.0" [dependencies.compiler_builtins] version = "0.1.39" diff --git a/rts/motoko-rts/src/debug.rs b/rts/motoko-rts/src/debug.rs index 6d902a2ea6c..ae187091a27 100644 --- a/rts/motoko-rts/src/debug.rs +++ b/rts/motoko-rts/src/debug.rs @@ -175,7 +175,7 @@ pub(crate) unsafe fn print_boxed_object(buf: &mut WriteBuf, p: usize) { } TAG_BITS64 => { let bits64 = obj as *const Bits64; - let _ = write!(buf, "", (*bits64).bits); + let _ = write!(buf, "", (*bits64).bits()); } TAG_MUTBOX => { let mutbox = obj as *const MutBox; diff --git a/rts/motoko-rts/src/gc/copying.rs b/rts/motoko-rts/src/gc/copying.rs index 12f2fb1a8f7..f68e91e7d29 100644 --- a/rts/motoko-rts/src/gc/copying.rs +++ b/rts/motoko-rts/src/gc/copying.rs @@ -1,3 +1,4 @@ +use crate::constants::WORD_SIZE; use crate::mem_utils::{memcpy_bytes, memcpy_words}; use crate::memory::Memory; use crate::types::*; @@ -125,6 +126,9 @@ unsafe fn evac( let obj = (*ptr_loc).as_obj(); + // Check object alignment to avoid undefined behavior. See also static_checks module. + debug_assert_eq!(obj as u32 % WORD_SIZE, 0); + // Update the field if the object is already evacauted if obj.tag() == TAG_FWD_PTR { let fwd = (*(obj as *const FwdPtr)).fwd; diff --git a/rts/motoko-rts/src/gc/mark_compact.rs b/rts/motoko-rts/src/gc/mark_compact.rs index f5b418145f3..8843bdfeab9 100644 --- a/rts/motoko-rts/src/gc/mark_compact.rs +++ b/rts/motoko-rts/src/gc/mark_compact.rs @@ -127,6 +127,9 @@ unsafe fn mark_object(mem: &mut M, obj: Value, heap_base: u32) { let obj_tag = obj.tag(); let obj = obj.get_ptr() as u32; + // Check object alignment to avoid undefined behavior. See also static_checks module. + debug_assert_eq!(obj % WORD_SIZE, 0); + let obj_idx = (obj - heap_base) / WORD_SIZE; if get_bit(obj_idx) { diff --git a/rts/motoko-rts/src/lib.rs b/rts/motoko-rts/src/lib.rs index 39b4a941540..df68ab06b74 100644 --- a/rts/motoko-rts/src/lib.rs +++ b/rts/motoko-rts/src/lib.rs @@ -32,6 +32,7 @@ pub mod leb128; mod mem_utils; pub mod memory; pub mod principal_id; +mod static_checks; pub mod text; pub mod text_iter; mod tommath_bindings; diff --git a/rts/motoko-rts/src/static_checks.rs b/rts/motoko-rts/src/static_checks.rs new file mode 100644 index 00000000000..7a4cc198655 --- /dev/null +++ b/rts/motoko-rts/src/static_checks.rs @@ -0,0 +1,54 @@ +//! Static assertions to make sure object layouts are as expected + +use crate::types::*; + +use core::mem::{align_of, size_of}; + +use static_assertions::const_assert_eq; + +// TODO: I don't understand why I get a "unused constant" warning for this. Removing it causes +// compilation failures as expected. +#[allow(unused)] +const WORD_SIZE: usize = crate::constants::WORD_SIZE as usize; + +// Check platform word size +const_assert_eq!(size_of::(), size_of::()); +const_assert_eq!(size_of::(), WORD_SIZE); + +// Check that sizes of structs are as expected by the compiler +// (Expectations are all over the place, e.g. `header_size` definitions in `compile.ml`, calls to `static_closure`, etc.) +const_assert_eq!(size_of::(), 1 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 3 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 5 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 3 * WORD_SIZE); +const_assert_eq!(size_of::(), 4 * WORD_SIZE); +const_assert_eq!(size_of::(), 1 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 3 * WORD_SIZE); + +// These aren't used generated by the compiler +const_assert_eq!(size_of::(), 1 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); +const_assert_eq!(size_of::(), 2 * WORD_SIZE); + +// Check that objects need to be aligned on word boundaries. Having a different alignment +// restriction an object type would require changing allocation routines for it. +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); +const_assert_eq!(align_of::(), WORD_SIZE); diff --git a/rts/motoko-rts/src/types.rs b/rts/motoko-rts/src/types.rs index ae8469b8940..29ab1fa8560 100644 --- a/rts/motoko-rts/src/types.rs +++ b/rts/motoko-rts/src/types.rs @@ -309,7 +309,6 @@ pub const TAG_ONE_WORD_FILLER: Tag = 16; pub const TAG_FREE_SPACE: Tag = 17; // Common parts of any object. Other object pointers can be coerced into a pointer to this. -#[repr(packed)] pub struct Obj { pub tag: Tag, } @@ -330,7 +329,6 @@ impl Obj { } } -#[repr(packed)] #[rustfmt::skip] pub struct Array { pub header: Obj, @@ -363,7 +361,6 @@ impl Array { } } -#[repr(packed)] pub struct Object { pub header: Obj, pub size: u32, // Number of elements @@ -385,13 +382,11 @@ impl Object { } } -#[repr(packed)] pub struct ObjInd { pub header: Obj, pub field: Value, } -#[repr(packed)] pub struct Closure { pub header: Obj, pub funid: u32, @@ -409,7 +404,6 @@ impl Closure { } } -#[repr(packed)] pub struct Blob { pub header: Obj, pub len: Bytes, @@ -458,13 +452,11 @@ impl Blob { } /// A forwarding pointer placed by the GC in place of an evacuated object. -#[repr(packed)] pub struct FwdPtr { pub header: Obj, pub fwd: Value, } -#[repr(packed)] pub struct BigInt { pub header: Obj, /// The data following now must describe is the `mp_int` struct. @@ -503,26 +495,22 @@ impl BigInt { } } -#[repr(packed)] pub struct MutBox { pub header: Obj, pub field: Value, } -#[repr(packed)] pub struct Some { pub header: Obj, pub field: Value, } -#[repr(packed)] pub struct Variant { pub header: Obj, pub tag: u32, pub field: Value, } -#[repr(packed)] pub struct Concat { pub header: Obj, pub n_bytes: Bytes, @@ -540,31 +528,35 @@ impl Concat { } } -#[repr(packed)] pub struct Null { pub header: Obj, } -#[repr(packed)] pub struct Bits64 { pub header: Obj, - pub bits: u64, + // We have two 32-bit fields instead of one 64-bit to avoid aligning the fields on 64-bit + // boundary. + bits_lo: u32, + bits_hi: u32, +} + +impl Bits64 { + pub fn bits(&self) -> u64 { + (u64::from(self.bits_hi) << 32) | u64::from(self.bits_lo) + } } -#[repr(packed)] pub struct Bits32 { pub header: Obj, pub bits: u32, } /// Marks one word empty space in heap -#[repr(packed)] pub struct OneWordFiller { pub header: Obj, } /// Marks arbitrary sized emtpy space in heap -#[repr(packed)] pub struct FreeSpace { pub header: Obj, pub words: Words, From 86dabeb5a5d7e1331a20dcc95cf929ae36b427ad Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Fri, 10 Sep 2021 17:15:19 -0700 Subject: [PATCH 12/56] niv motoko-base: update e6730a3f -> 78f6ff60 (#2777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for motoko-base: Branch: next-moc Commits: [dfinity/motoko-base@e6730a3f...78f6ff60](https://github.com/dfinity/motoko-base/compare/e6730a3ff6d0852956fcde8b39334e546ecd6c29...78f6ff60ce853f58c28a7ff116b5d8f6d3694b8f) * [`3bbf2b81`](https://github.com/dfinity/motoko-base/commit/3bbf2b812ce6dd312de65348baf02429e3441e9d) Docs typo: immutable, not mutable ([dfinity/motoko-base⁠#285](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/motoko-base/issues/285)) --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index ab12200d735..6202e3d34fa 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "e6730a3ff6d0852956fcde8b39334e546ecd6c29", - "sha256": "0zkikh6dg5vl270hsx0ilxkwcin47vf1yl6f8r48br4lrmk11n4k", + "rev": "78f6ff60ce853f58c28a7ff116b5d8f6d3694b8f", + "sha256": "1cw5amki2xjimjkwiz38nk9jaii3sj4950cbwzc92yzxl6a831r7", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/e6730a3ff6d0852956fcde8b39334e546ecd6c29.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/78f6ff60ce853f58c28a7ff116b5d8f6d3694b8f.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From f0862c4cabe4eee01a8c20edd8376e2e481dfd70 Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Wed, 15 Sep 2021 12:38:22 +0200 Subject: [PATCH 13/56] Prim.trap: Expose ic0.trap (#2780) the System API provides canisters with a way to explicitly trap with an error message. This is very useful for developing, e.g. to get useful messages in code you think is unreachable, or when checking invariants. This exposes this as `Prim.trap`. Later this can be exposed in `motoko-base` via `Debug.trap` maybe, and also the odd `Prelude.nyi`, `Prelude.xxx` and `Prelude.unreachable` can be implemented that way in a more helpful way. Semantically this is like the existing `assert false`, just with better control over the error message and a more helpful type (return type `None`). --- src/codegen/compile.ml | 7 ++++++- src/mo_values/prim.ml | 1 + src/prelude/prim.mo | 4 ++++ test/run-drun/explicit-trap.mo | 7 +++++++ test/run-drun/ok/explicit-trap.drun-run.ok | 3 +++ test/run-drun/ok/explicit-trap.ic-ref-run.ok | 6 ++++++ test/run-drun/ok/explicit-trap.run-ir.ok | 2 ++ test/run-drun/ok/explicit-trap.run-low.ok | 2 ++ test/run-drun/ok/explicit-trap.run.ok | 2 ++ 9 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/run-drun/explicit-trap.mo create mode 100644 test/run-drun/ok/explicit-trap.drun-run.ok create mode 100644 test/run-drun/ok/explicit-trap.ic-ref-run.ok create mode 100644 test/run-drun/ok/explicit-trap.run-ir.ok create mode 100644 test/run-drun/ok/explicit-trap.run-low.ok create mode 100644 test/run-drun/ok/explicit-trap.run.ok diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 519c858bac3..6438123e3ad 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -3480,7 +3480,7 @@ module IC = struct let trap_with env s = Blob.lit_ptr_len env s ^^ trap_ptr_len env - let _trap_text env = + let trap_text env = Text.to_blob env ^^ Blob.as_ptr_len env ^^ trap_ptr_len env let default_exports env = @@ -7838,6 +7838,11 @@ and compile_exp (env : E.t) ae exp = compile_exp_vanilla env ae e ^^ IC.print_text env + | OtherPrim "trap", [e] -> + SR.unit, + compile_exp_vanilla env ae e ^^ + IC.trap_text env + | OtherPrim ("blobToArray"|"blobToArrayMut"), e -> const_sr SR.Vanilla (Arr.ofBlob env) | OtherPrim ("arrayToBlob"|"arrayMutToBlob"), e -> diff --git a/src/mo_values/prim.ml b/src/mo_values/prim.ml index 51260bba634..7124f809301 100644 --- a/src/mo_values/prim.ml +++ b/src/mo_values/prim.ml @@ -184,6 +184,7 @@ let prim = | code -> Wasm.Utf8.encode [code] in k (Text str) | "print" -> fun _ v k -> Printf.printf "%s\n%!" (as_text v); k unit + | "trap" -> fun _ v k -> Printf.printf "%s\n%!" (as_text v); raise (Invalid_argument "explicit trap") | "rts_version" -> fun _ v k -> as_unit v; k (Text "0.1") | "rts_heap_size" -> fun _ v k -> as_unit v; k (Int (Int.of_int 0)) | "rts_total_allocation" -> fun _ v k -> as_unit v; k (Int (Int.of_int 0)) diff --git a/src/prelude/prim.mo b/src/prelude/prim.mo index 8c50f1e4989..76dcbfdf3b5 100644 --- a/src/prelude/prim.mo +++ b/src/prelude/prim.mo @@ -50,6 +50,10 @@ func debugPrintNat(x : Nat) { debugPrint (@text_of_Nat x) }; func debugPrintInt(x : Int) { debugPrint (@text_of_Int x) }; func debugPrintChar(x : Char) { debugPrint (charToText x) }; +// Trapping + +func trap(x : Text) { (prim "trap" : Text -> None) x }; + // RTS stats func rts_version() : Text { (prim "rts_version" : () -> Text) () }; diff --git a/test/run-drun/explicit-trap.mo b/test/run-drun/explicit-trap.mo new file mode 100644 index 00000000000..efd36b2402e --- /dev/null +++ b/test/run-drun/explicit-trap.mo @@ -0,0 +1,7 @@ +import Prim "mo:⛔"; +actor a { + public func go() { + Prim.trap("This is an explicit trap"); + } +}; +a.go(); //OR-CALL ingress go "DIDL\x00\x00" diff --git a/test/run-drun/ok/explicit-trap.drun-run.ok b/test/run-drun/ok/explicit-trap.drun-run.ok new file mode 100644 index 00000000000..587ab4d85f6 --- /dev/null +++ b/test/run-drun/ok/explicit-trap.drun-run.ok @@ -0,0 +1,3 @@ +ingress Completed: Reply: 0x4449444c016c01b3c4b1f204680100010a00000000000000000101 +ingress Completed: Reply: 0x4449444c0000 +ingress Err: IC0503: Canister rwlgt-iiaaa-aaaaa-aaaaa-cai trapped explicitly: This is an explicit trap diff --git a/test/run-drun/ok/explicit-trap.ic-ref-run.ok b/test/run-drun/ok/explicit-trap.ic-ref-run.ok new file mode 100644 index 00000000000..22d405bbd4d --- /dev/null +++ b/test/run-drun/ok/explicit-trap.ic-ref-run.ok @@ -0,0 +1,6 @@ +→ update create_canister(record {dnczaeh = null}) +← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) +→ update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… +← replied: () +→ update go() +← rejected (RC_CANISTER_ERROR): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: This is an explicit trap" diff --git a/test/run-drun/ok/explicit-trap.run-ir.ok b/test/run-drun/ok/explicit-trap.run-ir.ok new file mode 100644 index 00000000000..35d4377b517 --- /dev/null +++ b/test/run-drun/ok/explicit-trap.run-ir.ok @@ -0,0 +1,2 @@ +This is an explicit trap +prim:___: execution error, explicit trap diff --git a/test/run-drun/ok/explicit-trap.run-low.ok b/test/run-drun/ok/explicit-trap.run-low.ok new file mode 100644 index 00000000000..35d4377b517 --- /dev/null +++ b/test/run-drun/ok/explicit-trap.run-low.ok @@ -0,0 +1,2 @@ +This is an explicit trap +prim:___: execution error, explicit trap diff --git a/test/run-drun/ok/explicit-trap.run.ok b/test/run-drun/ok/explicit-trap.run.ok new file mode 100644 index 00000000000..35d4377b517 --- /dev/null +++ b/test/run-drun/ok/explicit-trap.run.ok @@ -0,0 +1,2 @@ +This is an explicit trap +prim:___: execution error, explicit trap From 675cb577ae9c56e5557188ee8996d7f681bdfee3 Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Wed, 15 Sep 2021 15:11:24 +0100 Subject: [PATCH 14/56] Prim.trap should return None, not () (#2781) --- Changelog.md | 2 +- src/mo_values/prim.ml | 3 ++- src/prelude/prim.mo | 2 +- test/run-drun/explicit-trap.mo | 4 ++-- test/run-drun/ok/explicit-trap.run-ir.ok | 3 +-- test/run-drun/ok/explicit-trap.run-low.ok | 3 +-- test/run-drun/ok/explicit-trap.run.ok | 3 +-- test/run-drun/ok/explicit-trap.run.ret.ok | 1 + 8 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 test/run-drun/ok/explicit-trap.run.ret.ok diff --git a/Changelog.md b/Changelog.md index 4d3b367add7..22f4866b04c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ * moc -* Add runtime support for low-level, direct access to 32-bit IC stable memory (#2626) + * Add runtime support for low-level, direct access to 32-bit IC stable memory (#2626) * motoko-base diff --git a/src/mo_values/prim.ml b/src/mo_values/prim.ml index 7124f809301..62fe3a14f39 100644 --- a/src/mo_values/prim.ml +++ b/src/mo_values/prim.ml @@ -184,7 +184,8 @@ let prim = | code -> Wasm.Utf8.encode [code] in k (Text str) | "print" -> fun _ v k -> Printf.printf "%s\n%!" (as_text v); k unit - | "trap" -> fun _ v k -> Printf.printf "%s\n%!" (as_text v); raise (Invalid_argument "explicit trap") + | "trap" -> fun _ v k -> + raise (Invalid_argument ("explicit trap: "^ (as_text v))) | "rts_version" -> fun _ v k -> as_unit v; k (Text "0.1") | "rts_heap_size" -> fun _ v k -> as_unit v; k (Int (Int.of_int 0)) | "rts_total_allocation" -> fun _ v k -> as_unit v; k (Int (Int.of_int 0)) diff --git a/src/prelude/prim.mo b/src/prelude/prim.mo index 76dcbfdf3b5..a81028ef09b 100644 --- a/src/prelude/prim.mo +++ b/src/prelude/prim.mo @@ -52,7 +52,7 @@ func debugPrintChar(x : Char) { debugPrint (charToText x) }; // Trapping -func trap(x : Text) { (prim "trap" : Text -> None) x }; +func trap(x : Text) : None { (prim "trap" : Text -> None) x }; // RTS stats diff --git a/test/run-drun/explicit-trap.mo b/test/run-drun/explicit-trap.mo index efd36b2402e..edb07b90bf1 100644 --- a/test/run-drun/explicit-trap.mo +++ b/test/run-drun/explicit-trap.mo @@ -1,7 +1,7 @@ import Prim "mo:⛔"; actor a { - public func go() { + public func go() : async Nat { Prim.trap("This is an explicit trap"); } }; -a.go(); //OR-CALL ingress go "DIDL\x00\x00" +await a.go(); //OR-CALL ingress go "DIDL\x00\x00" diff --git a/test/run-drun/ok/explicit-trap.run-ir.ok b/test/run-drun/ok/explicit-trap.run-ir.ok index 35d4377b517..321dacfcaf1 100644 --- a/test/run-drun/ok/explicit-trap.run-ir.ok +++ b/test/run-drun/ok/explicit-trap.run-ir.ok @@ -1,2 +1 @@ -This is an explicit trap -prim:___: execution error, explicit trap +prim:___: execution error, explicit trap: This is an explicit trap diff --git a/test/run-drun/ok/explicit-trap.run-low.ok b/test/run-drun/ok/explicit-trap.run-low.ok index 35d4377b517..321dacfcaf1 100644 --- a/test/run-drun/ok/explicit-trap.run-low.ok +++ b/test/run-drun/ok/explicit-trap.run-low.ok @@ -1,2 +1 @@ -This is an explicit trap -prim:___: execution error, explicit trap +prim:___: execution error, explicit trap: This is an explicit trap diff --git a/test/run-drun/ok/explicit-trap.run.ok b/test/run-drun/ok/explicit-trap.run.ok index 35d4377b517..321dacfcaf1 100644 --- a/test/run-drun/ok/explicit-trap.run.ok +++ b/test/run-drun/ok/explicit-trap.run.ok @@ -1,2 +1 @@ -This is an explicit trap -prim:___: execution error, explicit trap +prim:___: execution error, explicit trap: This is an explicit trap diff --git a/test/run-drun/ok/explicit-trap.run.ret.ok b/test/run-drun/ok/explicit-trap.run.ret.ok new file mode 100644 index 00000000000..69becfa16f9 --- /dev/null +++ b/test/run-drun/ok/explicit-trap.run.ret.ok @@ -0,0 +1 @@ +Return code 1 From fa9156538a7e1da4239435128b7a0c0b81559a5b Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Wed, 15 Sep 2021 18:09:09 +0100 Subject: [PATCH 15/56] update next-moc; hide StableMemory from changelog (#2782) --- Changelog.md | 6 +----- nix/sources.json | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index 22f4866b04c..0d8a9b93592 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,12 +1,8 @@ # Motoko compiler changelog -* moc - - * Add runtime support for low-level, direct access to 32-bit IC stable memory (#2626) - * motoko-base - * Add StableMemory library, exposing 32-bit IC stable memory (#280) + * add Debug.trap : Text -> None == 0.6.8 (2021-09-06) diff --git a/nix/sources.json b/nix/sources.json index 6202e3d34fa..e22c0e97a91 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "78f6ff60ce853f58c28a7ff116b5d8f6d3694b8f", - "sha256": "1cw5amki2xjimjkwiz38nk9jaii3sj4950cbwzc92yzxl6a831r7", + "rev": "fddbd490f0b5dfb7a8674e25dca1253f1dcd31b8", + "sha256": "0bhg6anvfllx8skvxdn085a27wc1dh9hc67hl0zc7mkvbqvzd3zi", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/78f6ff60ce853f58c28a7ff116b5d8f6d3694b8f.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/fddbd490f0b5dfb7a8674e25dca1253f1dcd31b8.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From e1569d74b040c6eb3a1002e6c3978ac1124dd13a Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Wed, 15 Sep 2021 18:43:24 +0100 Subject: [PATCH 16/56] Releasing 0.6.9 (#2783) includes ExperimentalStableMemory.mo in stealth mode --- Changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 0d8a9b93592..a671cde5caa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,10 @@ # Motoko compiler changelog +== 0.6.9 (2021-09-15) + * motoko-base - * add Debug.trap : Text -> None + * add Debug.trap : Text -> None (#288) == 0.6.8 (2021-09-06) From 31357a8965f6765d71b3b3b8df8020a0208429b4 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Wed, 15 Sep 2021 17:11:49 -0700 Subject: [PATCH 17/56] niv motoko-base: update fddbd490 -> ad493242 (#2786) ## Changelog for motoko-base: Branch: next-moc Commits: [dfinity/motoko-base@fddbd490...ad493242](https://github.com/dfinity/motoko-base/compare/fddbd490f0b5dfb7a8674e25dca1253f1dcd31b8...ad493242f5674718c0ddb24e34b79a077597b4bc) * [`da795941`](https://github.com/dfinity/motoko-base/commit/da79594115823c4c3299fab026327119fe5ab774) Motoko 0.6.9 --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index e22c0e97a91..f0f0c315ed9 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "fddbd490f0b5dfb7a8674e25dca1253f1dcd31b8", - "sha256": "0bhg6anvfllx8skvxdn085a27wc1dh9hc67hl0zc7mkvbqvzd3zi", + "rev": "ad493242f5674718c0ddb24e34b79a077597b4bc", + "sha256": "187wm7cx500bcg0a3zdcx2sf09x02vmph5bjb5shnkcfglnxdfp3", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/fddbd490f0b5dfb7a8674e25dca1253f1dcd31b8.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/ad493242f5674718c0ddb24e34b79a077597b4bc.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From 80cbb6d96127037306fc4378e6b8b74768d5879b Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Thu, 16 Sep 2021 03:02:39 -0700 Subject: [PATCH 18/56] niv ic-hs: update c49a2f44 -> fc1250f5 (#2785) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@c49a2f44...fc1250f5](https://github.com/dfinity/ic-hs/compare/c49a2f443d177b6486a1797ca646d8546ac7288c...fc1250f5ba8f9c8b68fa37afa397441c82e6c622) * [`a4550601`](https://github.com/dfinity/ic-hs/commit/a4550601f38833604119bce4ac51de09f8a196b0) Use nix-build-uncached * [`fc1250f5`](https://github.com/dfinity/ic-hs/commit/fc1250f5ba8f9c8b68fa37afa397441c82e6c622) Bump haskell-candid ([dfinity/ic-hs⁠#29](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/ic-hs/issues/29)) --- nix/sources.json | 6 +++--- test/run-drun/ok/AST-66.ic-ref-run.ok | 2 +- test/run-drun/ok/GIT-843.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-arg.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-capture.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-class-arg.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-class-cycles.ic-ref-run.ok | 2 +- .../run-drun/ok/actor-creator-shadow-too.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-creator-shadow.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-creator.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-import.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-reference-bad.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-reference-return.ic-ref-run.ok | 2 +- test/run-drun/ok/actor-reference.ic-ref-run.ok | 2 +- test/run-drun/ok/array-out-of-bounds.ic-ref-run.ok | 2 +- test/run-drun/ok/async-any.ic-ref-run.ok | 2 +- test/run-drun/ok/async-bang.ic-ref-run.ok | 2 +- test/run-drun/ok/async-calls1.ic-ref-run.ok | 2 +- test/run-drun/ok/async-calls2.ic-ref-run.ok | 2 +- test/run-drun/ok/async-calls3.ic-ref-run.ok | 2 +- test/run-drun/ok/async-free-var.ic-ref-run.ok | 2 +- test/run-drun/ok/async-loop-while.ic-ref-run.ok | 2 +- test/run-drun/ok/async-loop.ic-ref-run.ok | 2 +- test/run-drun/ok/async-new-obj.ic-ref-run.ok | 2 +- test/run-drun/ok/async-obj-mut.ic-ref-run.ok | 2 +- test/run-drun/ok/async-while.ic-ref-run.ok | 2 +- test/run-drun/ok/await-sugar.ic-ref-run.ok | 2 +- test/run-drun/ok/await-without-yield.ic-ref-run.ok | 2 +- test/run-drun/ok/await.ic-ref-run.ok | 2 +- test/run-drun/ok/bignum1.ic-ref-run.ok | 2 +- test/run-drun/ok/block.ic-ref-run.ok | 2 +- test/run-drun/ok/call-async-method.ic-ref-run.ok | 2 +- test/run-drun/ok/caller.ic-ref-run.ok | 2 +- test/run-drun/ok/certified-data.ic-ref-run.ok | 2 +- test/run-drun/ok/class-import.ic-ref-run.ok | 2 +- test/run-drun/ok/clone.ic-ref-run.ok | 2 +- test/run-drun/ok/closure-params.ic-ref-run.ok | 2 +- test/run-drun/ok/commit-on-await.ic-ref-run.ok | 2 +- test/run-drun/ok/count-callbacks.ic-ref-run.ok | 2 +- test/run-drun/ok/counter.ic-ref-run.ok | 2 +- test/run-drun/ok/counter2.ic-ref-run.ok | 2 +- test/run-drun/ok/data-params.ic-ref-run.ok | 2 +- test/run-drun/ok/distributor.ic-ref-run.ok | 2 +- test/run-drun/ok/divide-by-zero.ic-ref-run.ok | 2 +- test/run-drun/ok/do-option-bug.ic-ref-run.ok | 2 +- test/run-drun/ok/empty-actor.ic-ref-run.ok | 2 +- test/run-drun/ok/empty-call.ic-ref-run.ok | 2 +- test/run-drun/ok/error-codes.ic-ref-run.ok | 2 +- test/run-drun/ok/eval.ic-ref-run.ok | 2 +- test/run-drun/ok/explicit-trap.ic-ref-run.ok | 2 +- test/run-drun/ok/flatten-awaitables.ic-ref-run.ok | 2 +- test/run-drun/ok/for-await.ic-ref-run.ok | 2 +- test/run-drun/ok/free-callbacks.ic-ref-run.ok | 2 +- test/run-drun/ok/gc.ic-ref-run.ok | 2 +- test/run-drun/ok/general_await.ic-ref-run.ok | 2 +- test/run-drun/ok/general_await_implicit.ic-ref-run.ok | 2 +- test/run-drun/ok/generic-tail-rec.ic-ref-run.ok | 2 +- test/run-drun/ok/hello-world-async.ic-ref-run.ok | 2 +- test/run-drun/ok/hello-world-await.ic-ref-run.ok | 2 +- test/run-drun/ok/hello-world-message.ic-ref-run.ok | 2 +- test/run-drun/ok/hello-world-return.ic-ref-run.ok | 2 +- test/run-drun/ok/hello-world.ic-ref-run.ok | 2 +- test/run-drun/ok/ic-calls.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-any-stable.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-any.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-bad.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-bool.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-buf-size-bug.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-field-escape.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-float.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-func.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-mo.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-nary.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-nat-int.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-option.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-pair.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-principal.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-record.ic-ref-run.ok | 10 +++++----- test/run-drun/ok/idl-shorthand.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-tuple.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-unit.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-variant.ic-ref-run.ok | 2 +- test/run-drun/ok/idl-vector.ic-ref-run.ok | 2 +- test/run-drun/ok/install-empty-actor.ic-ref-run.ok | 2 +- test/run-drun/ok/interleave.ic-ref-run.ok | 2 +- test/run-drun/ok/issue-1174.ic-ref-run.ok | 2 +- test/run-drun/ok/issue-1847.ic-ref-run.ok | 2 +- test/run-drun/ok/issue-2047.ic-ref-run.ok | 2 +- test/run-drun/ok/issue-2464.ic-ref-run.ok | 2 +- test/run-drun/ok/issue-894.ic-ref-run.ok | 2 +- test/run-drun/ok/issue2317.ic-ref-run.ok | 2 +- test/run-drun/ok/live-upgrade.ic-ref-run.ok | 2 +- test/run-drun/ok/local-throw.ic-ref-run.ok | 2 +- test/run-drun/ok/nary-async.ic-ref-run.ok | 2 +- test/run-drun/ok/neg-powers.ic-ref-run.ok | 2 +- test/run-drun/ok/oneshot-callbacks.ic-ref-run.ok | 2 +- test/run-drun/ok/oneway-throw.ic-ref-run.ok | 2 +- test/run-drun/ok/oneway.ic-ref-run.ok | 2 +- test/run-drun/ok/oom.ic-ref-run.ok | 2 +- test/run-drun/ok/overflow.ic-ref-run.ok | 2 +- test/run-drun/ok/print-from-init.ic-ref-run.ok | 2 +- test/run-drun/ok/query.ic-ref-run.ok | 2 +- test/run-drun/ok/query2.ic-ref-run.ok | 2 +- test/run-drun/ok/reinstall.ic-ref-run.ok | 2 +- test/run-drun/ok/reject-bug.ic-ref-run.ok | 2 +- test/run-drun/ok/reject.ic-ref-run.ok | 2 +- test/run-drun/ok/rts-stats.ic-ref-run.ok | 2 +- test/run-drun/ok/rts-stats2.ic-ref-run.ok | 2 +- test/run-drun/ok/self-describe.ic-ref-run.ok | 2 +- test/run-drun/ok/self-upgrade.ic-ref-run.ok | 2 +- test/run-drun/ok/selftail.ic-ref-run.ok | 2 +- test/run-drun/ok/shared-object.ic-ref-run.ok | 2 +- test/run-drun/ok/sharingbug.ic-ref-run.ok | 2 +- test/run-drun/ok/show.ic-ref-run.ok | 2 +- test/run-drun/ok/stable-mutable.ic-ref-run.ok | 2 +- test/run-drun/ok/stable.ic-ref-run.ok | 2 +- test/run-drun/ok/static-call-from-pub.ic-ref-run.ok | 2 +- test/run-drun/ok/static-gc.ic-ref-run.ok | 2 +- test/run-drun/ok/switch-await.ic-ref-run.ok | 2 +- test/run-drun/ok/tailpositions-actor.ic-ref-run.ok | 2 +- test/run-drun/ok/test-cycles-state.ic-ref-run.ok | 2 +- test/run-drun/ok/test-cycles.ic-ref-run.ok | 2 +- test/run-drun/ok/text-iter.ic-ref-run.ok | 2 +- test/run-drun/ok/throw.ic-ref-run.ok | 2 +- test/run-drun/ok/time.ic-ref-run.ok | 2 +- test/run-drun/ok/transpose.ic-ref-run.ok | 2 +- test/run-drun/ok/type-components.ic-ref-run.ok | 2 +- test/run-drun/ok/type-lub.ic-ref-run.ok | 2 +- test/run-drun/ok/upgrade-hooks.ic-ref-run.ok | 2 +- test/run-drun/ok/upgrades.ic-ref-run.ok | 2 +- test/run-drun/ok/utf8.ic-ref-run.ok | 2 +- 131 files changed, 137 insertions(+), 137 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index f0f0c315ed9..09bc3b0391b 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -40,10 +40,10 @@ "homepage": "", "owner": "dfinity", "repo": "ic-hs", - "rev": "c49a2f443d177b6486a1797ca646d8546ac7288c", - "sha256": "0pfnz3x4ls8asnq4x7jpp0ijiss24j2sg77bn4hg6gvbfmczp4x6", + "rev": "fc1250f5ba8f9c8b68fa37afa397441c82e6c622", + "sha256": "0yc4fiz7ppcir38dszf8cckms9jqv3qrv59v1dxfjlvcbmj3qc87", "type": "tarball", - "url": "https://github.com/dfinity/ic-hs/archive/c49a2f443d177b6486a1797ca646d8546ac7288c.tar.gz", + "url": "https://github.com/dfinity/ic-hs/archive/fc1250f5ba8f9c8b68fa37afa397441c82e6c622.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "libtommath": { diff --git a/test/run-drun/ok/AST-66.ic-ref-run.ok b/test/run-drun/ok/AST-66.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/AST-66.ic-ref-run.ok +++ b/test/run-drun/ok/AST-66.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/GIT-843.ic-ref-run.ok b/test/run-drun/ok/GIT-843.ic-ref-run.ok index 55428a4b8c6..419084a63a8 100644 --- a/test/run-drun/ok/GIT-843.ic-ref-run.ok +++ b/test/run-drun/ok/GIT-843.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-arg.ic-ref-run.ok b/test/run-drun/ok/actor-arg.ic-ref-run.ok index d8b51b36100..fa9910f5c13 100644 --- a/test/run-drun/ok/actor-arg.ic-ref-run.ok +++ b/test/run-drun/ok/actor-arg.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob "DIDL\00\00"; kca_xin = blob "\00asm\01\… ← replied: () diff --git a/test/run-drun/ok/actor-capture.ic-ref-run.ok b/test/run-drun/ok/actor-capture.ic-ref-run.ok index 52102479e2f..fb3014dc583 100644 --- a/test/run-drun/ok/actor-capture.ic-ref-run.ok +++ b/test/run-drun/ok/actor-capture.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob "DIDL\00\01q\05Hello"; kca_xin = blob "\… ← replied: () diff --git a/test/run-drun/ok/actor-class-arg.ic-ref-run.ok b/test/run-drun/ok/actor-class-arg.ic-ref-run.ok index 277751891fe..4bd520fe838 100644 --- a/test/run-drun/ok/actor-class-arg.ic-ref-run.ok +++ b/test/run-drun/ok/actor-class-arg.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob "DIDL\00\00"; kca_xin = blob "\00asm\01\… debug.print: hello diff --git a/test/run-drun/ok/actor-class-cycles.ic-ref-run.ok b/test/run-drun/ok/actor-class-cycles.ic-ref-run.ok index cb47c5e1392..58fe18f1cfb 100644 --- a/test/run-drun/ok/actor-class-cycles.ic-ref-run.ok +++ b/test/run-drun/ok/actor-class-cycles.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-creator-shadow-too.ic-ref-run.ok b/test/run-drun/ok/actor-creator-shadow-too.ic-ref-run.ok index 19d9942cca9..562ce16364d 100644 --- a/test/run-drun/ok/actor-creator-shadow-too.ic-ref-run.ok +++ b/test/run-drun/ok/actor-creator-shadow-too.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-creator-shadow.ic-ref-run.ok b/test/run-drun/ok/actor-creator-shadow.ic-ref-run.ok index 7b4a7159e03..7c819dc3608 100644 --- a/test/run-drun/ok/actor-creator-shadow.ic-ref-run.ok +++ b/test/run-drun/ok/actor-creator-shadow.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-creator.ic-ref-run.ok b/test/run-drun/ok/actor-creator.ic-ref-run.ok index d5a4f591d94..a37ba41f501 100644 --- a/test/run-drun/ok/actor-creator.ic-ref-run.ok +++ b/test/run-drun/ok/actor-creator.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-import.ic-ref-run.ok b/test/run-drun/ok/actor-import.ic-ref-run.ok index a276a35d42b..b64c13dd286 100644 --- a/test/run-drun/ok/actor-import.ic-ref-run.ok +++ b/test/run-drun/ok/actor-import.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-reference-bad.ic-ref-run.ok b/test/run-drun/ok/actor-reference-bad.ic-ref-run.ok index 84bbcc412ce..136edd6bfbe 100644 --- a/test/run-drun/ok/actor-reference-bad.ic-ref-run.ok +++ b/test/run-drun/ok/actor-reference-bad.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-reference-return.ic-ref-run.ok b/test/run-drun/ok/actor-reference-return.ic-ref-run.ok index 00b879cc8b6..98ad6dde8c7 100644 --- a/test/run-drun/ok/actor-reference-return.ic-ref-run.ok +++ b/test/run-drun/ok/actor-reference-return.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/actor-reference.ic-ref-run.ok b/test/run-drun/ok/actor-reference.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/actor-reference.ic-ref-run.ok +++ b/test/run-drun/ok/actor-reference.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/array-out-of-bounds.ic-ref-run.ok b/test/run-drun/ok/array-out-of-bounds.ic-ref-run.ok index 2a92be9c3c4..ccf3588bcd9 100644 --- a/test/run-drun/ok/array-out-of-bounds.ic-ref-run.ok +++ b/test/run-drun/ok/array-out-of-bounds.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-any.ic-ref-run.ok b/test/run-drun/ok/async-any.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/async-any.ic-ref-run.ok +++ b/test/run-drun/ok/async-any.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-bang.ic-ref-run.ok b/test/run-drun/ok/async-bang.ic-ref-run.ok index 08b8afaa641..4898f90b22e 100644 --- a/test/run-drun/ok/async-bang.ic-ref-run.ok +++ b/test/run-drun/ok/async-bang.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-calls1.ic-ref-run.ok b/test/run-drun/ok/async-calls1.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/async-calls1.ic-ref-run.ok +++ b/test/run-drun/ok/async-calls1.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-calls2.ic-ref-run.ok b/test/run-drun/ok/async-calls2.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/async-calls2.ic-ref-run.ok +++ b/test/run-drun/ok/async-calls2.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-calls3.ic-ref-run.ok b/test/run-drun/ok/async-calls3.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/async-calls3.ic-ref-run.ok +++ b/test/run-drun/ok/async-calls3.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-free-var.ic-ref-run.ok b/test/run-drun/ok/async-free-var.ic-ref-run.ok index e9a3b8e0533..9b2082ef6ea 100644 --- a/test/run-drun/ok/async-free-var.ic-ref-run.ok +++ b/test/run-drun/ok/async-free-var.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-loop-while.ic-ref-run.ok b/test/run-drun/ok/async-loop-while.ic-ref-run.ok index 76d52094f8e..9efe8a5f612 100644 --- a/test/run-drun/ok/async-loop-while.ic-ref-run.ok +++ b/test/run-drun/ok/async-loop-while.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-loop.ic-ref-run.ok b/test/run-drun/ok/async-loop.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/async-loop.ic-ref-run.ok +++ b/test/run-drun/ok/async-loop.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-new-obj.ic-ref-run.ok b/test/run-drun/ok/async-new-obj.ic-ref-run.ok index fa6a4b8f8b3..216512a84b1 100644 --- a/test/run-drun/ok/async-new-obj.ic-ref-run.ok +++ b/test/run-drun/ok/async-new-obj.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-obj-mut.ic-ref-run.ok b/test/run-drun/ok/async-obj-mut.ic-ref-run.ok index d66579552e0..66306d4294c 100644 --- a/test/run-drun/ok/async-obj-mut.ic-ref-run.ok +++ b/test/run-drun/ok/async-obj-mut.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/async-while.ic-ref-run.ok b/test/run-drun/ok/async-while.ic-ref-run.ok index 76d52094f8e..9efe8a5f612 100644 --- a/test/run-drun/ok/async-while.ic-ref-run.ok +++ b/test/run-drun/ok/async-while.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/await-sugar.ic-ref-run.ok b/test/run-drun/ok/await-sugar.ic-ref-run.ok index b82c4c9a601..1e8e453a057 100644 --- a/test/run-drun/ok/await-sugar.ic-ref-run.ok +++ b/test/run-drun/ok/await-sugar.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/await-without-yield.ic-ref-run.ok b/test/run-drun/ok/await-without-yield.ic-ref-run.ok index d7765d3d409..7b64b45bbd5 100644 --- a/test/run-drun/ok/await-without-yield.ic-ref-run.ok +++ b/test/run-drun/ok/await-without-yield.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/await.ic-ref-run.ok b/test/run-drun/ok/await.ic-ref-run.ok index 5b89394b407..7b264c956c3 100644 --- a/test/run-drun/ok/await.ic-ref-run.ok +++ b/test/run-drun/ok/await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/bignum1.ic-ref-run.ok b/test/run-drun/ok/bignum1.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/bignum1.ic-ref-run.ok +++ b/test/run-drun/ok/bignum1.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/block.ic-ref-run.ok b/test/run-drun/ok/block.ic-ref-run.ok index c2448a000dc..f2950f4ea58 100644 --- a/test/run-drun/ok/block.ic-ref-run.ok +++ b/test/run-drun/ok/block.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/call-async-method.ic-ref-run.ok b/test/run-drun/ok/call-async-method.ic-ref-run.ok index ca0459fb623..b0895d43bef 100644 --- a/test/run-drun/ok/call-async-method.ic-ref-run.ok +++ b/test/run-drun/ok/call-async-method.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/caller.ic-ref-run.ok b/test/run-drun/ok/caller.ic-ref-run.ok index 04af21e0e88..a0e338ba383 100644 --- a/test/run-drun/ok/caller.ic-ref-run.ok +++ b/test/run-drun/ok/caller.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/certified-data.ic-ref-run.ok b/test/run-drun/ok/certified-data.ic-ref-run.ok index cee3e0fa0e3..e924bd346c7 100644 --- a/test/run-drun/ok/certified-data.ic-ref-run.ok +++ b/test/run-drun/ok/certified-data.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/class-import.ic-ref-run.ok b/test/run-drun/ok/class-import.ic-ref-run.ok index c6526d03d16..08fefeaa2eb 100644 --- a/test/run-drun/ok/class-import.ic-ref-run.ok +++ b/test/run-drun/ok/class-import.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/clone.ic-ref-run.ok b/test/run-drun/ok/clone.ic-ref-run.ok index bffc2f61057..2cb9391237c 100644 --- a/test/run-drun/ok/clone.ic-ref-run.ok +++ b/test/run-drun/ok/clone.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/closure-params.ic-ref-run.ok b/test/run-drun/ok/closure-params.ic-ref-run.ok index 6d1c050d937..2d5c5144d6a 100644 --- a/test/run-drun/ok/closure-params.ic-ref-run.ok +++ b/test/run-drun/ok/closure-params.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/commit-on-await.ic-ref-run.ok b/test/run-drun/ok/commit-on-await.ic-ref-run.ok index 42e658c5c6f..c1a1c047108 100644 --- a/test/run-drun/ok/commit-on-await.ic-ref-run.ok +++ b/test/run-drun/ok/commit-on-await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/count-callbacks.ic-ref-run.ok b/test/run-drun/ok/count-callbacks.ic-ref-run.ok index 0d9b4711e37..9c052df0746 100644 --- a/test/run-drun/ok/count-callbacks.ic-ref-run.ok +++ b/test/run-drun/ok/count-callbacks.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/counter.ic-ref-run.ok b/test/run-drun/ok/counter.ic-ref-run.ok index e7c93b7a3fb..6e8d45da0ff 100644 --- a/test/run-drun/ok/counter.ic-ref-run.ok +++ b/test/run-drun/ok/counter.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/counter2.ic-ref-run.ok b/test/run-drun/ok/counter2.ic-ref-run.ok index 1bdaa27bf90..e55adf3a5b7 100644 --- a/test/run-drun/ok/counter2.ic-ref-run.ok +++ b/test/run-drun/ok/counter2.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/data-params.ic-ref-run.ok b/test/run-drun/ok/data-params.ic-ref-run.ok index ea605c26676..302cc5e64df 100644 --- a/test/run-drun/ok/data-params.ic-ref-run.ok +++ b/test/run-drun/ok/data-params.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/distributor.ic-ref-run.ok b/test/run-drun/ok/distributor.ic-ref-run.ok index 4683a0ae9e5..5c8f3e8c559 100644 --- a/test/run-drun/ok/distributor.ic-ref-run.ok +++ b/test/run-drun/ok/distributor.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/divide-by-zero.ic-ref-run.ok b/test/run-drun/ok/divide-by-zero.ic-ref-run.ok index 129c7f48392..a405984848a 100644 --- a/test/run-drun/ok/divide-by-zero.ic-ref-run.ok +++ b/test/run-drun/ok/divide-by-zero.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← rejected (RC_CANISTER_ERROR): Initialization trapped: EvalTrapError region:0xXXX-0xXXX "NumericIntegerDivideByZero" diff --git a/test/run-drun/ok/do-option-bug.ic-ref-run.ok b/test/run-drun/ok/do-option-bug.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/do-option-bug.ic-ref-run.ok +++ b/test/run-drun/ok/do-option-bug.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/empty-actor.ic-ref-run.ok b/test/run-drun/ok/empty-actor.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/empty-actor.ic-ref-run.ok +++ b/test/run-drun/ok/empty-actor.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/empty-call.ic-ref-run.ok b/test/run-drun/ok/empty-call.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/empty-call.ic-ref-run.ok +++ b/test/run-drun/ok/empty-call.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/error-codes.ic-ref-run.ok b/test/run-drun/ok/error-codes.ic-ref-run.ok index 0e6259f79e8..90167c6c680 100644 --- a/test/run-drun/ok/error-codes.ic-ref-run.ok +++ b/test/run-drun/ok/error-codes.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/eval.ic-ref-run.ok b/test/run-drun/ok/eval.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/eval.ic-ref-run.ok +++ b/test/run-drun/ok/eval.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/explicit-trap.ic-ref-run.ok b/test/run-drun/ok/explicit-trap.ic-ref-run.ok index 22d405bbd4d..ff2a6b59fcb 100644 --- a/test/run-drun/ok/explicit-trap.ic-ref-run.ok +++ b/test/run-drun/ok/explicit-trap.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/flatten-awaitables.ic-ref-run.ok b/test/run-drun/ok/flatten-awaitables.ic-ref-run.ok index 224447825b3..7971b6614bb 100644 --- a/test/run-drun/ok/flatten-awaitables.ic-ref-run.ok +++ b/test/run-drun/ok/flatten-awaitables.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/for-await.ic-ref-run.ok b/test/run-drun/ok/for-await.ic-ref-run.ok index 79f0be763ad..c7f82e5f796 100644 --- a/test/run-drun/ok/for-await.ic-ref-run.ok +++ b/test/run-drun/ok/for-await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/free-callbacks.ic-ref-run.ok b/test/run-drun/ok/free-callbacks.ic-ref-run.ok index 77ef4bad489..f41b9abfacc 100644 --- a/test/run-drun/ok/free-callbacks.ic-ref-run.ok +++ b/test/run-drun/ok/free-callbacks.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/gc.ic-ref-run.ok b/test/run-drun/ok/gc.ic-ref-run.ok index e122a4bbd4f..d8f02a5bbd5 100644 --- a/test/run-drun/ok/gc.ic-ref-run.ok +++ b/test/run-drun/ok/gc.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/general_await.ic-ref-run.ok b/test/run-drun/ok/general_await.ic-ref-run.ok index 865f1c5ee63..50333b1d59b 100644 --- a/test/run-drun/ok/general_await.ic-ref-run.ok +++ b/test/run-drun/ok/general_await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/general_await_implicit.ic-ref-run.ok b/test/run-drun/ok/general_await_implicit.ic-ref-run.ok index 865f1c5ee63..50333b1d59b 100644 --- a/test/run-drun/ok/general_await_implicit.ic-ref-run.ok +++ b/test/run-drun/ok/general_await_implicit.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/generic-tail-rec.ic-ref-run.ok b/test/run-drun/ok/generic-tail-rec.ic-ref-run.ok index 7e33bfc88b8..ab165e64cfc 100644 --- a/test/run-drun/ok/generic-tail-rec.ic-ref-run.ok +++ b/test/run-drun/ok/generic-tail-rec.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: done 1 diff --git a/test/run-drun/ok/hello-world-async.ic-ref-run.ok b/test/run-drun/ok/hello-world-async.ic-ref-run.ok index 75dfcce681f..b6aa72e4231 100644 --- a/test/run-drun/ok/hello-world-async.ic-ref-run.ok +++ b/test/run-drun/ok/hello-world-async.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/hello-world-await.ic-ref-run.ok b/test/run-drun/ok/hello-world-await.ic-ref-run.ok index 78eeb778147..2c6177e3e5d 100644 --- a/test/run-drun/ok/hello-world-await.ic-ref-run.ok +++ b/test/run-drun/ok/hello-world-await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/hello-world-message.ic-ref-run.ok b/test/run-drun/ok/hello-world-message.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/hello-world-message.ic-ref-run.ok +++ b/test/run-drun/ok/hello-world-message.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/hello-world-return.ic-ref-run.ok b/test/run-drun/ok/hello-world-return.ic-ref-run.ok index 7426a2b94eb..352b152f100 100644 --- a/test/run-drun/ok/hello-world-return.ic-ref-run.ok +++ b/test/run-drun/ok/hello-world-return.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/hello-world.ic-ref-run.ok b/test/run-drun/ok/hello-world.ic-ref-run.ok index a32bcce3e86..f5ad2fec553 100644 --- a/test/run-drun/ok/hello-world.ic-ref-run.ok +++ b/test/run-drun/ok/hello-world.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: Hello World! diff --git a/test/run-drun/ok/ic-calls.ic-ref-run.ok b/test/run-drun/ok/ic-calls.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/ic-calls.ic-ref-run.ok +++ b/test/run-drun/ok/ic-calls.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-any-stable.ic-ref-run.ok b/test/run-drun/ok/idl-any-stable.ic-ref-run.ok index 328c8baf1fe..8b54ff6469a 100644 --- a/test/run-drun/ok/idl-any-stable.ic-ref-run.ok +++ b/test/run-drun/ok/idl-any-stable.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-any.ic-ref-run.ok b/test/run-drun/ok/idl-any.ic-ref-run.ok index 4ac8ad127a7..71733d84987 100644 --- a/test/run-drun/ok/idl-any.ic-ref-run.ok +++ b/test/run-drun/ok/idl-any.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-bad.ic-ref-run.ok b/test/run-drun/ok/idl-bad.ic-ref-run.ok index 48ce60ddc8b..fd0f722d5da 100644 --- a/test/run-drun/ok/idl-bad.ic-ref-run.ok +++ b/test/run-drun/ok/idl-bad.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-bool.ic-ref-run.ok b/test/run-drun/ok/idl-bool.ic-ref-run.ok index 346003f7a48..ba3dcf61945 100644 --- a/test/run-drun/ok/idl-bool.ic-ref-run.ok +++ b/test/run-drun/ok/idl-bool.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-buf-size-bug.ic-ref-run.ok b/test/run-drun/ok/idl-buf-size-bug.ic-ref-run.ok index 1875f06e61b..440ad45216b 100644 --- a/test/run-drun/ok/idl-buf-size-bug.ic-ref-run.ok +++ b/test/run-drun/ok/idl-buf-size-bug.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-field-escape.ic-ref-run.ok b/test/run-drun/ok/idl-field-escape.ic-ref-run.ok index daf473f8bda..5ba0cc2e8a8 100644 --- a/test/run-drun/ok/idl-field-escape.ic-ref-run.ok +++ b/test/run-drun/ok/idl-field-escape.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-float.ic-ref-run.ok b/test/run-drun/ok/idl-float.ic-ref-run.ok index 91a00cd4f16..dd502c345b6 100644 --- a/test/run-drun/ok/idl-float.ic-ref-run.ok +++ b/test/run-drun/ok/idl-float.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-func.ic-ref-run.ok b/test/run-drun/ok/idl-func.ic-ref-run.ok index 2e80f865979..7ce536a5cb3 100644 --- a/test/run-drun/ok/idl-func.ic-ref-run.ok +++ b/test/run-drun/ok/idl-func.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-mo.ic-ref-run.ok b/test/run-drun/ok/idl-mo.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/idl-mo.ic-ref-run.ok +++ b/test/run-drun/ok/idl-mo.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-nary.ic-ref-run.ok b/test/run-drun/ok/idl-nary.ic-ref-run.ok index fb1544787fa..10fcf494c69 100644 --- a/test/run-drun/ok/idl-nary.ic-ref-run.ok +++ b/test/run-drun/ok/idl-nary.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-nat-int.ic-ref-run.ok b/test/run-drun/ok/idl-nat-int.ic-ref-run.ok index 2adc6a50cfd..b5d2e17bd6a 100644 --- a/test/run-drun/ok/idl-nat-int.ic-ref-run.ok +++ b/test/run-drun/ok/idl-nat-int.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-option.ic-ref-run.ok b/test/run-drun/ok/idl-option.ic-ref-run.ok index 315439e8a1d..a8a7a96cdeb 100644 --- a/test/run-drun/ok/idl-option.ic-ref-run.ok +++ b/test/run-drun/ok/idl-option.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-pair.ic-ref-run.ok b/test/run-drun/ok/idl-pair.ic-ref-run.ok index 985f9dc7ec2..7a1edd90734 100644 --- a/test/run-drun/ok/idl-pair.ic-ref-run.ok +++ b/test/run-drun/ok/idl-pair.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-principal.ic-ref-run.ok b/test/run-drun/ok/idl-principal.ic-ref-run.ok index eae14fdbd39..976af5babcf 100644 --- a/test/run-drun/ok/idl-principal.ic-ref-run.ok +++ b/test/run-drun/ok/idl-principal.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-record.ic-ref-run.ok b/test/run-drun/ok/idl-record.ic-ref-run.ok index a0d43c2b1eb..c3a2d8021de 100644 --- a/test/run-drun/ok/idl-record.ic-ref-run.ok +++ b/test/run-drun/ok/idl-record.ic-ref-run.ok @@ -1,18 +1,18 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () → query pair(record {"Hey!"; +42}) debug.print: ok: +42 ← replied: () -→ query record(record {bhned_q = "Hey!"; value = +42}) +→ query record(record {content = "Hey!"; value = +42}) debug.print: ok: Hey! ← replied: () -→ query record1(record {bhned_q = "Hey!"; value = +42; byte = (25 : int8)}) +→ query record1(record {content = "Hey!"; value = +42; byte = (25 : int8)}) debug.print: ok: +25 ← replied: () -→ query record2(record {bhned_q = "Hey!"; value = +42; byte = (24 : int8)},… +→ query record2(record {content = "Hey!"; value = +42; byte = (24 : int8)},… debug.print: ok: Hey! +25 ← replied: ((25 : int8)) -→ query record3(record {bhned_q = "Hey!"; value = +42}) +→ query record3(record {content = "Hey!"; value = +42}) ← rejected (RC_CANISTER_ERROR): canister trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: IDL error: did not find field extra in record" diff --git a/test/run-drun/ok/idl-shorthand.ic-ref-run.ok b/test/run-drun/ok/idl-shorthand.ic-ref-run.ok index e771b4e979b..77cbe09be5b 100644 --- a/test/run-drun/ok/idl-shorthand.ic-ref-run.ok +++ b/test/run-drun/ok/idl-shorthand.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-tuple.ic-ref-run.ok b/test/run-drun/ok/idl-tuple.ic-ref-run.ok index 416531894da..5c7b21799e3 100644 --- a/test/run-drun/ok/idl-tuple.ic-ref-run.ok +++ b/test/run-drun/ok/idl-tuple.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-unit.ic-ref-run.ok b/test/run-drun/ok/idl-unit.ic-ref-run.ok index 0fdefe4bb04..8e822075b9d 100644 --- a/test/run-drun/ok/idl-unit.ic-ref-run.ok +++ b/test/run-drun/ok/idl-unit.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-variant.ic-ref-run.ok b/test/run-drun/ok/idl-variant.ic-ref-run.ok index c579f1960b3..9526358f9f3 100644 --- a/test/run-drun/ok/idl-variant.ic-ref-run.ok +++ b/test/run-drun/ok/idl-variant.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/idl-vector.ic-ref-run.ok b/test/run-drun/ok/idl-vector.ic-ref-run.ok index 3e909b17bd2..158833d5560 100644 --- a/test/run-drun/ok/idl-vector.ic-ref-run.ok +++ b/test/run-drun/ok/idl-vector.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/install-empty-actor.ic-ref-run.ok b/test/run-drun/ok/install-empty-actor.ic-ref-run.ok index d57c8c811ba..bd7533e991f 100644 --- a/test/run-drun/ok/install-empty-actor.ic-ref-run.ok +++ b/test/run-drun/ok/install-empty-actor.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/interleave.ic-ref-run.ok b/test/run-drun/ok/interleave.ic-ref-run.ok index f3630f29aa7..48628c4adae 100644 --- a/test/run-drun/ok/interleave.ic-ref-run.ok +++ b/test/run-drun/ok/interleave.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/issue-1174.ic-ref-run.ok b/test/run-drun/ok/issue-1174.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/issue-1174.ic-ref-run.ok +++ b/test/run-drun/ok/issue-1174.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/issue-1847.ic-ref-run.ok b/test/run-drun/ok/issue-1847.ic-ref-run.ok index 78cacc8bc00..cb62cf1bc2a 100644 --- a/test/run-drun/ok/issue-1847.ic-ref-run.ok +++ b/test/run-drun/ok/issue-1847.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← rejected (RC_CANISTER_ERROR): Initialization trapped: EvalTrapError region:0xXXX-0xXXX "canister trapped explicitly: IDL error: empty input. Expected Candid-encoded argument, but received a zero-length argument" diff --git a/test/run-drun/ok/issue-2047.ic-ref-run.ok b/test/run-drun/ok/issue-2047.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/issue-2047.ic-ref-run.ok +++ b/test/run-drun/ok/issue-2047.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/issue-2464.ic-ref-run.ok b/test/run-drun/ok/issue-2464.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/issue-2464.ic-ref-run.ok +++ b/test/run-drun/ok/issue-2464.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/issue-894.ic-ref-run.ok b/test/run-drun/ok/issue-894.ic-ref-run.ok index 5b160f32eb0..69062cf2daa 100644 --- a/test/run-drun/ok/issue-894.ic-ref-run.ok +++ b/test/run-drun/ok/issue-894.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/issue2317.ic-ref-run.ok b/test/run-drun/ok/issue2317.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/issue2317.ic-ref-run.ok +++ b/test/run-drun/ok/issue2317.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/live-upgrade.ic-ref-run.ok b/test/run-drun/ok/live-upgrade.ic-ref-run.ok index cccf0f3bf4f..09104d1fcb1 100644 --- a/test/run-drun/ok/live-upgrade.ic-ref-run.ok +++ b/test/run-drun/ok/live-upgrade.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: {version = 0} diff --git a/test/run-drun/ok/local-throw.ic-ref-run.ok b/test/run-drun/ok/local-throw.ic-ref-run.ok index 32bf6255816..f845d8c0b25 100644 --- a/test/run-drun/ok/local-throw.ic-ref-run.ok +++ b/test/run-drun/ok/local-throw.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/nary-async.ic-ref-run.ok b/test/run-drun/ok/nary-async.ic-ref-run.ok index dcd119eb5c4..11829d9ccb3 100644 --- a/test/run-drun/ok/nary-async.ic-ref-run.ok +++ b/test/run-drun/ok/nary-async.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/neg-powers.ic-ref-run.ok b/test/run-drun/ok/neg-powers.ic-ref-run.ok index 8835da07e75..a5858f55095 100644 --- a/test/run-drun/ok/neg-powers.ic-ref-run.ok +++ b/test/run-drun/ok/neg-powers.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/oneshot-callbacks.ic-ref-run.ok b/test/run-drun/ok/oneshot-callbacks.ic-ref-run.ok index 5988b1fc926..facceb4fe9b 100644 --- a/test/run-drun/ok/oneshot-callbacks.ic-ref-run.ok +++ b/test/run-drun/ok/oneshot-callbacks.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/oneway-throw.ic-ref-run.ok b/test/run-drun/ok/oneway-throw.ic-ref-run.ok index 0c477dbf947..8223f171ea1 100644 --- a/test/run-drun/ok/oneway-throw.ic-ref-run.ok +++ b/test/run-drun/ok/oneway-throw.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/oneway.ic-ref-run.ok b/test/run-drun/ok/oneway.ic-ref-run.ok index 688d2fa7b25..0522fe50488 100644 --- a/test/run-drun/ok/oneway.ic-ref-run.ok +++ b/test/run-drun/ok/oneway.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/oom.ic-ref-run.ok b/test/run-drun/ok/oom.ic-ref-run.ok index 77b5f7e385d..a28097fb40e 100644 --- a/test/run-drun/ok/oom.ic-ref-run.ok +++ b/test/run-drun/ok/oom.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/overflow.ic-ref-run.ok b/test/run-drun/ok/overflow.ic-ref-run.ok index db5d5d34879..c154e6c72ce 100644 --- a/test/run-drun/ok/overflow.ic-ref-run.ok +++ b/test/run-drun/ok/overflow.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/print-from-init.ic-ref-run.ok b/test/run-drun/ok/print-from-init.ic-ref-run.ok index 9bab3226719..376566f23e9 100644 --- a/test/run-drun/ok/print-from-init.ic-ref-run.ok +++ b/test/run-drun/ok/print-from-init.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: Debug out diff --git a/test/run-drun/ok/query.ic-ref-run.ok b/test/run-drun/ok/query.ic-ref-run.ok index 335261193f8..684064b861c 100644 --- a/test/run-drun/ok/query.ic-ref-run.ok +++ b/test/run-drun/ok/query.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/query2.ic-ref-run.ok b/test/run-drun/ok/query2.ic-ref-run.ok index 5726c09ff57..257970fa3e7 100644 --- a/test/run-drun/ok/query2.ic-ref-run.ok +++ b/test/run-drun/ok/query2.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/reinstall.ic-ref-run.ok b/test/run-drun/ok/reinstall.ic-ref-run.ok index 604d869fbe4..f60d60098fb 100644 --- a/test/run-drun/ok/reinstall.ic-ref-run.ok +++ b/test/run-drun/ok/reinstall.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/reject-bug.ic-ref-run.ok b/test/run-drun/ok/reject-bug.ic-ref-run.ok index 19eb0205b68..e9746a88b66 100644 --- a/test/run-drun/ok/reject-bug.ic-ref-run.ok +++ b/test/run-drun/ok/reject-bug.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/reject.ic-ref-run.ok b/test/run-drun/ok/reject.ic-ref-run.ok index 078c6ef0f59..039af1b9a3b 100644 --- a/test/run-drun/ok/reject.ic-ref-run.ok +++ b/test/run-drun/ok/reject.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/rts-stats.ic-ref-run.ok b/test/run-drun/ok/rts-stats.ic-ref-run.ok index 26711daf904..8085bafd55c 100644 --- a/test/run-drun/ok/rts-stats.ic-ref-run.ok +++ b/test/run-drun/ok/rts-stats.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: Size and allocation delta: (10_008, 10_008) diff --git a/test/run-drun/ok/rts-stats2.ic-ref-run.ok b/test/run-drun/ok/rts-stats2.ic-ref-run.ok index 4f5bfce69c8..4ca4e15bdf9 100644 --- a/test/run-drun/ok/rts-stats2.ic-ref-run.ok +++ b/test/run-drun/ok/rts-stats2.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/self-describe.ic-ref-run.ok b/test/run-drun/ok/self-describe.ic-ref-run.ok index 29c50d7939e..6e7c569efa7 100644 --- a/test/run-drun/ok/self-describe.ic-ref-run.ok +++ b/test/run-drun/ok/self-describe.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/self-upgrade.ic-ref-run.ok b/test/run-drun/ok/self-upgrade.ic-ref-run.ok index 3d1a2c61313..28ea4fe631f 100644 --- a/test/run-drun/ok/self-upgrade.ic-ref-run.ok +++ b/test/run-drun/ok/self-upgrade.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: init'ed diff --git a/test/run-drun/ok/selftail.ic-ref-run.ok b/test/run-drun/ok/selftail.ic-ref-run.ok index ce729b1e60a..6d7e3e560c4 100644 --- a/test/run-drun/ok/selftail.ic-ref-run.ok +++ b/test/run-drun/ok/selftail.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: ok1 diff --git a/test/run-drun/ok/shared-object.ic-ref-run.ok b/test/run-drun/ok/shared-object.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/shared-object.ic-ref-run.ok +++ b/test/run-drun/ok/shared-object.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/sharingbug.ic-ref-run.ok b/test/run-drun/ok/sharingbug.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/sharingbug.ic-ref-run.ok +++ b/test/run-drun/ok/sharingbug.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/show.ic-ref-run.ok b/test/run-drun/ok/show.ic-ref-run.ok index eeb9301ae21..cc503bff72a 100644 --- a/test/run-drun/ok/show.ic-ref-run.ok +++ b/test/run-drun/ok/show.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: true diff --git a/test/run-drun/ok/stable-mutable.ic-ref-run.ok b/test/run-drun/ok/stable-mutable.ic-ref-run.ok index 8d23fdfae56..1f8920cc52f 100644 --- a/test/run-drun/ok/stable-mutable.ic-ref-run.ok +++ b/test/run-drun/ok/stable-mutable.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/stable.ic-ref-run.ok b/test/run-drun/ok/stable.ic-ref-run.ok index 3e446c2d483..3f6a75c14b0 100644 --- a/test/run-drun/ok/stable.ic-ref-run.ok +++ b/test/run-drun/ok/stable.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: init'ed diff --git a/test/run-drun/ok/static-call-from-pub.ic-ref-run.ok b/test/run-drun/ok/static-call-from-pub.ic-ref-run.ok index c679c6831cc..2443d49198d 100644 --- a/test/run-drun/ok/static-call-from-pub.ic-ref-run.ok +++ b/test/run-drun/ok/static-call-from-pub.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/static-gc.ic-ref-run.ok b/test/run-drun/ok/static-gc.ic-ref-run.ok index 005320800e3..9012a0dc95b 100644 --- a/test/run-drun/ok/static-gc.ic-ref-run.ok +++ b/test/run-drun/ok/static-gc.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/switch-await.ic-ref-run.ok b/test/run-drun/ok/switch-await.ic-ref-run.ok index 2bd67d4fb56..63585c2a765 100644 --- a/test/run-drun/ok/switch-await.ic-ref-run.ok +++ b/test/run-drun/ok/switch-await.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/tailpositions-actor.ic-ref-run.ok b/test/run-drun/ok/tailpositions-actor.ic-ref-run.ok index fb096960bbb..203ae808b0b 100644 --- a/test/run-drun/ok/tailpositions-actor.ic-ref-run.ok +++ b/test/run-drun/ok/tailpositions-actor.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/test-cycles-state.ic-ref-run.ok b/test/run-drun/ok/test-cycles-state.ic-ref-run.ok index 99dd72d2326..443708211bc 100644 --- a/test/run-drun/ok/test-cycles-state.ic-ref-run.ok +++ b/test/run-drun/ok/test-cycles-state.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/test-cycles.ic-ref-run.ok b/test/run-drun/ok/test-cycles.ic-ref-run.ok index e5f67377a8d..a7d72104829 100644 --- a/test/run-drun/ok/test-cycles.ic-ref-run.ok +++ b/test/run-drun/ok/test-cycles.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/text-iter.ic-ref-run.ok b/test/run-drun/ok/text-iter.ic-ref-run.ok index f1bb06a33f3..2d068fc1421 100644 --- a/test/run-drun/ok/text-iter.ic-ref-run.ok +++ b/test/run-drun/ok/text-iter.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: via `debugPrint`: diff --git a/test/run-drun/ok/throw.ic-ref-run.ok b/test/run-drun/ok/throw.ic-ref-run.ok index 32bf6255816..f845d8c0b25 100644 --- a/test/run-drun/ok/throw.ic-ref-run.ok +++ b/test/run-drun/ok/throw.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/time.ic-ref-run.ok b/test/run-drun/ok/time.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/time.ic-ref-run.ok +++ b/test/run-drun/ok/time.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/transpose.ic-ref-run.ok b/test/run-drun/ok/transpose.ic-ref-run.ok index b1f9a9aa063..d3a0b958ae0 100644 --- a/test/run-drun/ok/transpose.ic-ref-run.ok +++ b/test/run-drun/ok/transpose.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/type-components.ic-ref-run.ok b/test/run-drun/ok/type-components.ic-ref-run.ok index 7403c900600..49431906ac6 100644 --- a/test/run-drun/ok/type-components.ic-ref-run.ok +++ b/test/run-drun/ok/type-components.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: {f = +1} diff --git a/test/run-drun/ok/type-lub.ic-ref-run.ok b/test/run-drun/ok/type-lub.ic-ref-run.ok index 7599531ed80..db51d22d7d7 100644 --- a/test/run-drun/ok/type-lub.ic-ref-run.ok +++ b/test/run-drun/ok/type-lub.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () diff --git a/test/run-drun/ok/upgrade-hooks.ic-ref-run.ok b/test/run-drun/ok/upgrade-hooks.ic-ref-run.ok index 6f57ee8a7eb..4c14286a4e2 100644 --- a/test/run-drun/ok/upgrade-hooks.ic-ref-run.ok +++ b/test/run-drun/ok/upgrade-hooks.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: init'ed diff --git a/test/run-drun/ok/upgrades.ic-ref-run.ok b/test/run-drun/ok/upgrades.ic-ref-run.ok index 86e20978aa5..226dff1809e 100644 --- a/test/run-drun/ok/upgrades.ic-ref-run.ok +++ b/test/run-drun/ok/upgrades.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… debug.print: init'ed diff --git a/test/run-drun/ok/utf8.ic-ref-run.ok b/test/run-drun/ok/utf8.ic-ref-run.ok index 37a1e458fe2..31369b7a9d9 100644 --- a/test/run-drun/ok/utf8.ic-ref-run.ok +++ b/test/run-drun/ok/utf8.ic-ref-run.ok @@ -1,4 +1,4 @@ -→ update create_canister(record {dnczaeh = null}) +→ update create_canister(record {settings = null}) ← replied: (record {hymijyo = principal "cvccv-qqaaq-aaaaa-aaaaa-c"}) → update install_code(record {arg = blob ""; kca_xin = blob "\00asm\01\00\00\00\0… ← replied: () From 9b24b7bb56b08913c448e01f6d0e622557e93513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 16 Sep 2021 15:42:44 +0300 Subject: [PATCH 19/56] Minor refactoring in variant tags in the backend (#2787) - Rename tag_field -> variant_tag_field, to make it clear that this is not the object tag but the variant tag. - Typo fix: "top of the stacks" -> "top of the stack" Co-authored-by: Gabor Greif --- src/codegen/compile.ml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 6438123e3ad..2fafb8baef1 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -1387,8 +1387,8 @@ module Variant = struct *) - let tag_field = Tagged.header_size - let payload_field = Int32.add Tagged.header_size 1l + let variant_tag_field = Tagged.header_size + let payload_field = Int32.add variant_tag_field 1l let hash_variant_label env : Mo_types.Type.lab -> int32 = E.hash env @@ -1396,12 +1396,12 @@ module Variant = struct let inject env l e = Tagged.obj env Tagged.Variant [compile_unboxed_const (hash_variant_label env l); e] - let get_tag = Heap.load_field tag_field + let get_variant_tag = Heap.load_field variant_tag_field let project = Heap.load_field payload_field - (* Test if the top of the stacks points to a variant with this label *) + (* Test if the top of the stack points to a variant with this label *) let test_is env l = - get_tag ^^ + get_variant_tag ^^ compile_eq_const (hash_variant_label env l) end (* Variant *) From a71a21b5ebf053d7ddf0b774469ada23f1123caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 16 Sep 2021 18:18:20 +0300 Subject: [PATCH 20/56] Use repr(C) in RTS struct (#2788) See the node added with this commit and https://github.com/dfinity/motoko/pull/2761#issuecomment-920765586 for the problem this solves. --- rts/motoko-rts/src/types.rs | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/rts/motoko-rts/src/types.rs b/rts/motoko-rts/src/types.rs index 29ab1fa8560..e25e9a7683c 100644 --- a/rts/motoko-rts/src/types.rs +++ b/rts/motoko-rts/src/types.rs @@ -1,3 +1,24 @@ +// Note [struct representation] +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// TLDR: Add `#[repr(C)]` in types used by both the runtime system and generated code, and add +// assertions to `static_assertions` module to check that the object size and field offsets are as +// expected. +// +// Rust compiler is free to reorder fields[1]. To avoid this in types that are used by both the +// runtime system and generated code we need a `repr` attribute like `repr(C)` or `repr(packed)`. +// +// We can't use `repr(packed)` because it potentially introduces undefined behavior as getting +// address (reference or pointer) of an unaligned field (or using the address) is an undefined +// behavior in Rust. See Motoko PR #2764. +// +// So to avoid reordering fields without introducing undefined behaviors we use `repr(C)`. See [2] +// for details on how `repr(C)` works. In short: it does not reorder fields. It can add padding, +// but in our case all fields are word-sized so that's not a problem. +// +// [1]: https://github.com/rust-lang/reference/blob/master/src/types/struct.md +// [2]: https://doc.rust-lang.org/stable/reference/type-layout.html#the-c-representation + use crate::tommath_bindings::{mp_digit, mp_int}; use core::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign}; @@ -309,6 +330,7 @@ pub const TAG_ONE_WORD_FILLER: Tag = 16; pub const TAG_FREE_SPACE: Tag = 17; // Common parts of any object. Other object pointers can be coerced into a pointer to this. +#[repr(C)] // See the note at the beginning of this module pub struct Obj { pub tag: Tag, } @@ -330,6 +352,7 @@ impl Obj { } #[rustfmt::skip] +#[repr(C)] // See the note at the beginning of this module pub struct Array { pub header: Obj, pub len: u32, // number of elements @@ -361,6 +384,7 @@ impl Array { } } +#[repr(C)] // See the note at the beginning of this module pub struct Object { pub header: Obj, pub size: u32, // Number of elements @@ -382,11 +406,13 @@ impl Object { } } +#[repr(C)] // See the note at the beginning of this module pub struct ObjInd { pub header: Obj, pub field: Value, } +#[repr(C)] // See the note at the beginning of this module pub struct Closure { pub header: Obj, pub funid: u32, @@ -404,6 +430,7 @@ impl Closure { } } +#[repr(C)] // See the note at the beginning of this module pub struct Blob { pub header: Obj, pub len: Bytes, @@ -452,11 +479,13 @@ impl Blob { } /// A forwarding pointer placed by the GC in place of an evacuated object. +#[repr(C)] // See the note at the beginning of this module pub struct FwdPtr { pub header: Obj, pub fwd: Value, } +#[repr(C)] // See the note at the beginning of this module pub struct BigInt { pub header: Obj, /// The data following now must describe is the `mp_int` struct. @@ -495,22 +524,26 @@ impl BigInt { } } +#[repr(C)] // See the note at the beginning of this module pub struct MutBox { pub header: Obj, pub field: Value, } +#[repr(C)] // See the note at the beginning of this module pub struct Some { pub header: Obj, pub field: Value, } +#[repr(C)] // See the note at the beginning of this module pub struct Variant { pub header: Obj, pub tag: u32, pub field: Value, } +#[repr(C)] // See the note at the beginning of this module pub struct Concat { pub header: Obj, pub n_bytes: Bytes, @@ -528,10 +561,12 @@ impl Concat { } } +#[repr(C)] // See the note at the beginning of this module pub struct Null { pub header: Obj, } +#[repr(C)] // See the note at the beginning of this module pub struct Bits64 { pub header: Obj, // We have two 32-bit fields instead of one 64-bit to avoid aligning the fields on 64-bit @@ -546,17 +581,20 @@ impl Bits64 { } } +#[repr(C)] // See the note at the beginning of this module pub struct Bits32 { pub header: Obj, pub bits: u32, } /// Marks one word empty space in heap +#[repr(C)] // See the note at the beginning of this module pub struct OneWordFiller { pub header: Obj, } /// Marks arbitrary sized emtpy space in heap +#[repr(C)] // See the note at the beginning of this module pub struct FreeSpace { pub header: Obj, pub words: Words, From 423b0923eb6f609358778cd0072cc25bf097e475 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Sun, 19 Sep 2021 17:11:15 -0700 Subject: [PATCH 21/56] niv niv: update e0ca65c8 -> 65a61b14 (#2792) ## Changelog for niv: Branch: master Commits: [nmattia/niv@e0ca65c8...65a61b14](https://github.com/nmattia/niv/compare/e0ca65c81a2d7a4d82a189f1e23a48d59ad42070...65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e) * [`65a61b14`](https://github.com/nmattia/niv/commit/65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e) Update documentation --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 09bc3b0391b..6053a4f9cfc 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -103,10 +103,10 @@ "homepage": "https://github.com/nmattia/niv", "owner": "nmattia", "repo": "niv", - "rev": "e0ca65c81a2d7a4d82a189f1e23a48d59ad42070", - "sha256": "1pq9nh1d8nn3xvbdny8fafzw87mj7gsmp6pxkdl65w2g18rmcmzx", + "rev": "65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e", + "sha256": "17mirpsx5wyw262fpsd6n6m47jcgw8k2bwcp1iwdnrlzy4dhcgqh", "type": "tarball", - "url": "https://github.com/nmattia/niv/archive/e0ca65c81a2d7a4d82a189f1e23a48d59ad42070.tar.gz", + "url": "https://github.com/nmattia/niv/archive/65a61b147f307d24bfd0a5cd56ce7d7b7cc61d2e.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs": { From 90d4367db4a85794fac6ee89a22f7b160014addb Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Mon, 20 Sep 2021 17:40:40 -0700 Subject: [PATCH 22/56] niv ic-hs: update fc1250f5 -> fb558eb5 (#2795) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@fc1250f5...fb558eb5](https://github.com/dfinity/ic-hs/compare/fc1250f5ba8f9c8b68fa37afa397441c82e6c622...fb558eb58aed501a6b46768baf0a50ece6bedeaa) * [`fb558eb5`](https://github.com/dfinity/ic-hs/commit/fb558eb58aed501a6b46768baf0a50ece6bedeaa) Bump nixpkgs to latest master ([dfinity/ic-hs⁠#30](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/ic-hs/issues/30)) --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 6053a4f9cfc..43a3e674796 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -40,10 +40,10 @@ "homepage": "", "owner": "dfinity", "repo": "ic-hs", - "rev": "fc1250f5ba8f9c8b68fa37afa397441c82e6c622", - "sha256": "0yc4fiz7ppcir38dszf8cckms9jqv3qrv59v1dxfjlvcbmj3qc87", + "rev": "fb558eb58aed501a6b46768baf0a50ece6bedeaa", + "sha256": "1y1h4s3nwk88bdnqxsph9hsh5qc32ji6bj4h31ykj9n7b0f2hxsh", "type": "tarball", - "url": "https://github.com/dfinity/ic-hs/archive/fc1250f5ba8f9c8b68fa37afa397441c82e6c622.tar.gz", + "url": "https://github.com/dfinity/ic-hs/archive/fb558eb58aed501a6b46768baf0a50ece6bedeaa.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "libtommath": { From 53a8dbe07ff8a780d5b91f39a4d4771f6ceb0d3f Mon Sep 17 00:00:00 2001 From: Joachim Breitner Date: Tue, 21 Sep 2021 19:39:45 +0200 Subject: [PATCH 23/56] nix/drun.nix: Mention nix-update (#2798) * nix/drun.nix: Mention nix-update * Update nix/drun.nix Co-authored-by: Gabor Greif Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- nix/drun.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nix/drun.nix b/nix/drun.nix index 18ef2f16820..c6bed2210bd 100644 --- a/nix/drun.nix +++ b/nix/drun.nix @@ -10,6 +10,10 @@ pkgs: # 2. run nix-build -A drun nix/ # 3. copy the “expected” hash from the output into this file # 4. commit and push + + # To automate this, try running the following in the nix/ directory + # nix run -f https://github.com/Mic92/nix-update/archive/master.tar.gz -c nix-update --version=skip drun + cargoSha256 = "0656lxdlr05cjkla1blvpqlxywk7shasiwmycz10nqykdrs4gfgf"; nativeBuildInputs = with pkgs; [ From 01b3fff16f3d0e97fc76412e82ac3b072637ed27 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Tue, 21 Sep 2021 17:11:50 -0700 Subject: [PATCH 24/56] niv motoko-base: update ad493242 -> b19e5b0f (#2800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changelog for motoko-base: Branch: next-moc Commits: [dfinity/motoko-base@ad493242...b19e5b0f](https://github.com/dfinity/motoko-base/compare/ad493242f5674718c0ddb24e34b79a077597b4bc...b19e5b0f95d5bec0a83a4f75725ee5950c85a77f) * [`63232940`](https://github.com/dfinity/motoko-base/commit/632329408fbb78e27bc00fb2d42e7c5d36ec9b75) Remove repetition in README ([dfinity/motoko-base⁠#290](http://r.duckduckgo.com/l/?uddg=https://github.com/dfinity/motoko-base/issues/290)) --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 43a3e674796..396f92a7bb0 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -64,10 +64,10 @@ "homepage": null, "owner": "dfinity", "repo": "motoko-base", - "rev": "ad493242f5674718c0ddb24e34b79a077597b4bc", - "sha256": "187wm7cx500bcg0a3zdcx2sf09x02vmph5bjb5shnkcfglnxdfp3", + "rev": "b19e5b0f95d5bec0a83a4f75725ee5950c85a77f", + "sha256": "0vgqa7fdyhbmcx1rr0gm8gansfqcqqb9q5jclhjpp3arlgb4sn0g", "type": "tarball", - "url": "https://github.com/dfinity/motoko-base/archive/ad493242f5674718c0ddb24e34b79a077597b4bc.tar.gz", + "url": "https://github.com/dfinity/motoko-base/archive/b19e5b0f95d5bec0a83a4f75725ee5950c85a77f.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "motoko-matchers": { From 30aa6f9f1a635458aa6a31afe73b7ce1d02df8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 22 Sep 2021 14:29:54 +0300 Subject: [PATCH 25/56] Remove redundant parens in backend (#2803) --- src/codegen/compile.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 2fafb8baef1..0f6f02870d6 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -5445,7 +5445,7 @@ module Stabilization = struct StableMem.get_mem_size env ^^ G.i (Test (Wasm.Values.I32 I32Op.Eqz)) ^^ G.if_ [] - begin( (* ensure [0,..,3,...len+4) *) + begin (* ensure [0,..,3,...len+4) *) compile_unboxed_const 0l ^^ get_len ^^ compile_add_const 4l ^^ (* reserve one word for size *) @@ -5460,7 +5460,7 @@ module Stabilization = struct compile_unboxed_const 4l ^^ get_dst ^^ get_len ^^ - E.call_import env "ic0" "stable_write") + E.call_import env "ic0" "stable_write" end begin let (set_N, get_N) = new_local env "N" in From 604c89d6a35c6b1fc3148c8789b3d07866e84a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 22 Sep 2021 15:33:44 +0300 Subject: [PATCH 26/56] RTS tests: add a helper for printing heap in tests (#2802) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- rts/motoko-rts-tests/src/gc/heap.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rts/motoko-rts-tests/src/gc/heap.rs b/rts/motoko-rts-tests/src/gc/heap.rs index 4620c76a414..3d895b18090 100644 --- a/rts/motoko-rts-tests/src/gc/heap.rs +++ b/rts/motoko-rts-tests/src/gc/heap.rs @@ -93,6 +93,19 @@ impl MotokoHeap { pub fn heap(&self) -> Ref> { Ref::map(self.inner.borrow(), |heap| &heap.heap) } + + /// Print heap contents to stdout, for debugging purposes. + #[allow(unused)] + pub fn dump(&self) { + unsafe { + motoko_rts::debug::dump_heap( + self.heap_base_address() as u32, + self.heap_ptr_address() as u32, + Value::from_ptr(self.static_root_array_address()), + self.continuation_table_ptr_address() as *mut Value, + ); + } + } } struct MotokoHeapInner { From 031dddb2c08684386afa3a2a101fbc84a9993bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 22 Sep 2021 17:04:58 +0300 Subject: [PATCH 27/56] Set the lowest bit in object tags (#2801) In compacting GC we need to distinguish a heap location (object or field address) from object headers. Currently this is done by checking if the value is smaller than or equal to the largest tag. Because first 64 KiB of the heap is for Rust stack, as long as the largest tag is smaller than 65,536, we can assume that values smaller than 65,536 are headers. This way of checking if a value is a header or an address causes problems when we want to use rest of the object headers to store more information. Examples: - In #2706 we will use one bit in the header to mark large objects. At least initially, we won't be compacting large objects, so mark-compact GC won't see large objects and so won't have to care about large header values. But we may want to do compaction on large objects, or store other information (maybe mark bits, or generation numbers). - We may want to store number of untagged (scalar) and tagged fields in object headers and merge some of the different object types. For example, instead of having 3 tags for `Variant`, `Some`, and `MutBox`, we could have one tag, and use rest of the headers to indicate that variants will have one scalar, one tagged fields, mutable objects will have just one tagged field, etc. - We could have `SmallBlob` and `SmallArray` types for blobs and arrays with lenghts smaller than 65,535 (16 bits length field). This would save us one word for small blobs and arrays. - We don't have to rely on Rust stack being large enough so that largest tag will still be small enough to be a valid address in heap. In this PR we update tags so that they always have the lowest bit set. Since objects and fields are all word aligned (so have the lowest 2 bits unset, this invariant was established in #2764), this allows checking the lowest bit to distinguish an address from a header. With this we can freely use the rest of the bits in headers. While this PR currently does not unblock any PRs, it's nice to have this flexibility for the future changes, and these changes do not have any downsides. (mo-rts.wasm grows 0.03%, 58 bytes) --- rts/motoko-rts/src/gc/mark_compact.rs | 10 ++++--- rts/motoko-rts/src/types.rs | 32 +++++++++++----------- src/codegen/compile.ml | 38 ++++++++++++++++----------- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/rts/motoko-rts/src/gc/mark_compact.rs b/rts/motoko-rts/src/gc/mark_compact.rs index 8843bdfeab9..455bb7c9659 100644 --- a/rts/motoko-rts/src/gc/mark_compact.rs +++ b/rts/motoko-rts/src/gc/mark_compact.rs @@ -231,16 +231,18 @@ unsafe fn thread(field: *mut Value) { /// Unthread all references at given header, replacing with `new_loc`. Restores object header. unsafe fn unthread(obj: *mut Obj, new_loc: u32) { - // NOTE: For this to work heap addresses need to be greater than the largest value for object - // headers. Currently this holds. TODO: Document this better. let mut header = (*obj).tag; - while header > TAG_NULL { - // TODO: is `header > TAG_NULL` the best way to distinguish a tag from a pointer? + + // All objects and fields are word-aligned, and tags have the lowest bit set, so use the lowest + // bit to distinguish a header (tag) from a field address. + while header & 0b1 == 0 { let tmp = (*(header as *mut Obj)).tag; (*(header as *mut Value)) = Value::from_ptr(new_loc as usize); header = tmp; } + // At the end of the chain is the original header for the object debug_assert!(header >= TAG_OBJECT && header <= TAG_NULL); + (*obj).tag = header; } diff --git a/rts/motoko-rts/src/types.rs b/rts/motoko-rts/src/types.rs index e25e9a7683c..6397629fee7 100644 --- a/rts/motoko-rts/src/types.rs +++ b/rts/motoko-rts/src/types.rs @@ -312,22 +312,24 @@ pub const fn unskew(value: usize) -> usize { // of an unsafe API usage). pub type Tag = u32; +// Tags need to have the lowest bit set, to allow distinguishing a header (tag) from object +// locations in mark-compact GC. (Reminder: objects and fields are word aligned) pub const TAG_OBJECT: Tag = 1; -pub const TAG_OBJ_IND: Tag = 2; -pub const TAG_ARRAY: Tag = 3; -pub const TAG_BITS64: Tag = 5; -pub const TAG_MUTBOX: Tag = 6; -pub const TAG_CLOSURE: Tag = 7; -pub const TAG_SOME: Tag = 8; -pub const TAG_VARIANT: Tag = 9; -pub const TAG_BLOB: Tag = 10; -pub const TAG_FWD_PTR: Tag = 11; -pub const TAG_BITS32: Tag = 12; -pub const TAG_BIGINT: Tag = 13; -pub const TAG_CONCAT: Tag = 14; -pub const TAG_NULL: Tag = 15; -pub const TAG_ONE_WORD_FILLER: Tag = 16; -pub const TAG_FREE_SPACE: Tag = 17; +pub const TAG_OBJ_IND: Tag = 3; +pub const TAG_ARRAY: Tag = 5; +pub const TAG_BITS64: Tag = 7; +pub const TAG_MUTBOX: Tag = 9; +pub const TAG_CLOSURE: Tag = 11; +pub const TAG_SOME: Tag = 13; +pub const TAG_VARIANT: Tag = 15; +pub const TAG_BLOB: Tag = 17; +pub const TAG_FWD_PTR: Tag = 19; +pub const TAG_BITS32: Tag = 21; +pub const TAG_BIGINT: Tag = 23; +pub const TAG_CONCAT: Tag = 25; +pub const TAG_NULL: Tag = 27; +pub const TAG_ONE_WORD_FILLER: Tag = 29; +pub const TAG_FREE_SPACE: Tag = 31; // Common parts of any object. Other object pointers can be coerced into a pointer to this. #[repr(C)] // See the note at the beginning of this module diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index 0f6f02870d6..a0ef4f7e947 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -1168,24 +1168,30 @@ module Tagged = struct | OneWordFiller (* Only used by the RTS *) | FreeSpace (* Only used by the RTS *) - (* Let's leave out tag 0 to trap earlier on invalid memory *) + (* Tags needs to have the lowest bit set, to allow distinguishing object + headers from heap locations (object or field addresses). + + (Reminder: objects and fields are word-aligned so will have the lowest two + bits unset) *) let int_of_tag = function | Object -> 1l - | ObjInd -> 2l - | Array -> 3l - | Bits64 -> 5l - | MutBox -> 6l - | Closure -> 7l - | Some -> 8l - | Variant -> 9l - | Blob -> 10l - | Indirection -> 11l - | Bits32 -> 12l - | BigInt -> 13l - | Concat -> 14l - | Null -> 15l - | OneWordFiller -> 16l - | FreeSpace -> 17l + | ObjInd -> 3l + | Array -> 5l + | Bits64 -> 7l + | MutBox -> 9l + | Closure -> 11l + | Some -> 13l + | Variant -> 15l + | Blob -> 17l + | Indirection -> 19l + | Bits32 -> 21l + | BigInt -> 23l + | Concat -> 25l + | Null -> 27l + | OneWordFiller -> 29l + | FreeSpace -> 31l + (* Next two tags won't be seen by the GC, so no need to set the lowest bit + for `CoercionFailure` and `StableSeen` *) | CoercionFailure -> 0xfffffffel | StableSeen -> 0xffffffffl From 0f01570f7fec0c65e89e3f4a8e544297a30fc0de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Wed, 22 Sep 2021 18:42:49 +0300 Subject: [PATCH 28/56] Use real tags in mark stack tests (#2804) Previously for the tags in mark stack tests we were using `value - 1` where `value` is the value being pushed to the stack. Using actual tags in these tests is more resilient to changes so we now use actual tags in mark stack tests. --- rts/motoko-rts-tests/src/mark_stack.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/rts/motoko-rts-tests/src/mark_stack.rs b/rts/motoko-rts-tests/src/mark_stack.rs index 669fd597001..9147ad249a8 100644 --- a/rts/motoko-rts-tests/src/mark_stack.rs +++ b/rts/motoko-rts-tests/src/mark_stack.rs @@ -5,7 +5,7 @@ use motoko_rts::gc::mark_compact::mark_stack::{ INIT_STACK_SIZE, STACK_BASE, STACK_PTR, STACK_TOP, }; use motoko_rts::memory::Memory; -use motoko_rts::types::{size_of, Blob, Words}; +use motoko_rts::types::*; use proptest::test_runner::{Config, TestCaseError, TestCaseResult, TestRunner}; @@ -33,6 +33,23 @@ fn test_push_pop() { .unwrap(); } +static TAGS: [Tag; 14] = [ + TAG_OBJECT, + TAG_OBJ_IND, + TAG_ARRAY, + TAG_BITS64, + TAG_MUTBOX, + TAG_CLOSURE, + TAG_SOME, + TAG_VARIANT, + TAG_BLOB, + TAG_FWD_PTR, + TAG_BITS32, + TAG_BIGINT, + TAG_CONCAT, + TAG_NULL, +]; + fn test_(mem: &mut M, n_objs: u32) -> TestCaseResult { let objs: Vec = (0..n_objs).collect(); @@ -40,13 +57,12 @@ fn test_(mem: &mut M, n_objs: u32) -> TestCaseResult { alloc_mark_stack(mem); for obj in &objs { - // Pushing a dummy argument derived from `obj` for tag - push_mark_stack(mem, *obj as usize, obj.wrapping_sub(1)); + push_mark_stack(mem, *obj as usize, TAGS[(*obj as usize) % TAGS.len()]); } for obj in objs.iter().copied().rev() { let popped = pop_mark_stack(); - if popped != Some((obj as usize, obj.wrapping_sub(1))) { + if popped != Some((obj as usize, TAGS[(obj as usize) % TAGS.len()])) { free_mark_stack(); return Err(TestCaseError::Fail( format!( From e9b35cde3bb45f7a0db2e54c8dcc2a81d86f6229 Mon Sep 17 00:00:00 2001 From: DFINITY bot <58022693+dfinity-bot@users.noreply.github.com> Date: Wed, 22 Sep 2021 17:45:38 -0700 Subject: [PATCH 29/56] niv ic-hs: update fb558eb5 -> 0d5fde1f (#2807) ## Changelog for ic-hs: Branch: master Commits: [dfinity/ic-hs@fb558eb5...0d5fde1f](https://github.com/dfinity/ic-hs/compare/fb558eb58aed501a6b46768baf0a50ece6bedeaa...0d5fde1fa57ed4aae10f33f8d441f342f18f1771) * [`e0ea5075`](https://github.com/dfinity/ic-hs/commit/e0ea5075d528e2154c10c0ca2595a915c2e7edf9) feat: implement 64-bit stable memory support * [`86f0241d`](https://github.com/dfinity/ic-hs/commit/86f0241d721359fef9d99f1cb8553073790f31bf) fix compile errors in Spec * [`e3ac04ee`](https://github.com/dfinity/ic-hs/commit/e3ac04ee854d318c6a4fd666825221ee869b995b) use different implementations for stable_* and stable64_* * [`71a5491d`](https://github.com/dfinity/ic-hs/commit/71a5491d81e8fd1f0a859a6589037b39f61bf601) Re-implement stable memory using MutableByteArray * [`bb1f582c`](https://github.com/dfinity/ic-hs/commit/bb1f582c4b7be3420749f7d884d68f629b352b9f) Add more bound checks * [`b4b5ff18`](https://github.com/dfinity/ic-hs/commit/b4b5ff181d849dd0207dd23306fc52f2093d7e66) Fix stable memory bugs + ic-ref-test suite * [`156bf0d5`](https://github.com/dfinity/ic-hs/commit/156bf0d509c562d84eaa21b2fd9ae5192ac40c28) Use sparse stable memory representation --- nix/sources.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 396f92a7bb0..b973212be03 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -40,10 +40,10 @@ "homepage": "", "owner": "dfinity", "repo": "ic-hs", - "rev": "fb558eb58aed501a6b46768baf0a50ece6bedeaa", - "sha256": "1y1h4s3nwk88bdnqxsph9hsh5qc32ji6bj4h31ykj9n7b0f2hxsh", + "rev": "0d5fde1fa57ed4aae10f33f8d441f342f18f1771", + "sha256": "011ww1skb03rjjfibng7hz4i6mylsiv9ynqnzr8gdx1qr0qqpzvz", "type": "tarball", - "url": "https://github.com/dfinity/ic-hs/archive/fb558eb58aed501a6b46768baf0a50ece6bedeaa.tar.gz", + "url": "https://github.com/dfinity/ic-hs/archive/0d5fde1fa57ed4aae10f33f8d441f342f18f1771.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "libtommath": { From 168077a59129ccc59f4d10fdc9d6554d113fa4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 23 Sep 2021 17:16:49 +0300 Subject: [PATCH 30/56] Refactor alloc_array size check (#2808) No need to use bitwise tricks and then document, simple code compiles to the same, efficient Wasm. --- rts/motoko-rts/src/memory.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rts/motoko-rts/src/memory.rs b/rts/motoko-rts/src/memory.rs index 38882f9c217..66590a91486 100644 --- a/rts/motoko-rts/src/memory.rs +++ b/rts/motoko-rts/src/memory.rs @@ -1,6 +1,7 @@ #[cfg(feature = "ic")] pub mod ic; +use crate::constants::WASM_HEAP_SIZE; use crate::rts_trap_with; use crate::types::*; @@ -43,8 +44,7 @@ pub unsafe fn alloc_blob(mem: &mut M, size: Bytes) -> Value { #[ic_mem_fn] pub unsafe fn alloc_array(mem: &mut M, len: u32) -> Value { // Array payload should not be larger than half of the memory - if len > 1 << (32 - 2 - 1) { - // 2 for word size, 1 to divide by two + if len > (WASM_HEAP_SIZE / 2).0 { rts_trap_with("Array allocation too large"); } From 53d9778dc97c13c4390dc0d73f20a979b92b3271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 23 Sep 2021 18:44:23 +0300 Subject: [PATCH 31/56] Typo fix: office -> offset (#2810) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- src/codegen/compile.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen/compile.ml b/src/codegen/compile.ml index a0ef4f7e947..3b27acf07e5 100644 --- a/src/codegen/compile.ml +++ b/src/codegen/compile.ml @@ -4500,7 +4500,7 @@ module Serialization = struct get_offset ^^ compile_unboxed_const 0l ^^ G.i (Compare (Wasm.Values.I32 I32Op.LtS)) ^^ E.else_trap_with env "Odd offset" ^^ - (* Write the office to the output buffer *) + (* Write the offset to the output buffer *) write_word32 get_offset end in From 6febb3fde1766656df540635cc15ace623faf230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Thu, 23 Sep 2021 19:48:17 +0300 Subject: [PATCH 32/56] Refactor ascriptions (`as`) in RTS, avoid accessing Bytes/Words fields (#2809) Ascriptions can be lossy, so every usage of `as` can potentially cause problems as we refactor the code. So in this PR we reduce the usage of `as`: - For `Bytes` and `Words` values, instead of `x.0 as usize` we now use `x.as_usize()`. `usize` has the same size as `u32` on Wasm (which is the only target we support), so `as_usize()` method uses `as` without any checks in debug mode. - For casting `u32` to `u64`, use `u64::from(x)`. `from` methods cannot fail, and for numeric types they are only implemented for casting a smaller type to a larger type. - To aid readbility this PR also introduces `Words::as_u32` and `Bytes::u32` and uses these methods instead of accessing the unnamed field of `Words` and `Bytes`. - Also remove a redundant unwrapping/wrapping of `Bytes` value when dividing it by a scalar. (`Bytes(bytes.0 / x)` -> `bytes / x`) --- rts/motoko-rts-tests/src/gc.rs | 4 ++-- rts/motoko-rts-tests/src/gc/heap.rs | 24 ++++++++++--------- rts/motoko-rts-tests/src/main.rs | 2 +- rts/motoko-rts-tests/src/memory.rs | 6 ++--- rts/motoko-rts/src/bigint.rs | 10 ++++---- rts/motoko-rts/src/debug.rs | 8 +++---- rts/motoko-rts/src/gc/copying.rs | 4 ++-- rts/motoko-rts/src/gc/mark_compact.rs | 4 ++-- rts/motoko-rts/src/gc/mark_compact/bitmap.rs | 6 ++--- .../src/gc/mark_compact/mark_stack.rs | 4 ++-- rts/motoko-rts/src/mem_utils.rs | 6 ++--- rts/motoko-rts/src/memory/ic.rs | 6 ++--- rts/motoko-rts/src/principal_id.rs | 16 ++++++------- rts/motoko-rts/src/text.rs | 16 ++++++------- rts/motoko-rts/src/text_iter.rs | 4 ++-- rts/motoko-rts/src/types.rs | 10 +++++++- 16 files changed, 70 insertions(+), 60 deletions(-) diff --git a/rts/motoko-rts-tests/src/gc.rs b/rts/motoko-rts-tests/src/gc.rs index 13ac14220a7..de8a1f3a3ce 100644 --- a/rts/motoko-rts-tests/src/gc.rs +++ b/rts/motoko-rts-tests/src/gc.rs @@ -167,7 +167,7 @@ fn check_dynamic_heap( check_continuation_table(object_offset, continuation_table, heap); offset += (size_of::() + Words(continuation_table.len() as u32)) .to_bytes() - .0 as usize; + .as_usize(); continue; } @@ -306,7 +306,7 @@ fn check_continuation_table(mut offset: usize, continuation_table: &[ObjectIdx], offset += WORD_SIZE; // Skip object header for idx - let idx_address = ptr as usize + size_of::().to_bytes().0 as usize; + let idx_address = ptr as usize + size_of::().to_bytes().as_usize(); let idx = get_scalar_value(read_word(heap, idx_address - heap.as_ptr() as usize)); assert_eq!(idx, *obj); diff --git a/rts/motoko-rts-tests/src/gc/heap.rs b/rts/motoko-rts-tests/src/gc/heap.rs index 3d895b18090..d2911a35249 100644 --- a/rts/motoko-rts-tests/src/gc/heap.rs +++ b/rts/motoko-rts-tests/src/gc/heap.rs @@ -193,7 +193,7 @@ impl MotokoHeapInner { let dynamic_heap_size_bytes = dynamic_heap_size_without_continuation_table_bytes + (size_of::() + Words(continuation_table.len() as u32)) .to_bytes() - .0 as usize; + .as_usize(); let total_heap_size_bytes = static_heap_size_bytes + dynamic_heap_size_bytes; @@ -234,7 +234,7 @@ impl MotokoHeapInner { // Update heap pointer let old_hp = self.heap_ptr_address(); - let new_hp = old_hp + bytes.0 as usize; + let new_hp = old_hp + bytes.as_usize(); self.heap_ptr_offset = new_hp - self.heap.as_ptr() as usize; // Grow memory if needed @@ -279,12 +279,12 @@ fn heap_size_for_gc( // The bitmap implementation rounds up to 64-bits to be able to read as many // bits as possible in one instruction and potentially skip 64 words in the // heap with single 64-bit comparison - (((mark_bit_bytes.0 + 7) / 8) * 8) + size_of::().to_bytes().0 + (((mark_bit_bytes.as_u32() + 7) / 8) * 8) + size_of::().to_bytes().as_u32() }; // In the worst case the entire heap will be pushed to the mark stack, but in tests // we limit the size - let mark_stack_words = n_objects.clamp(INIT_STACK_SIZE.0 as usize, MAX_MARK_STACK_SIZE) - + size_of::().0 as usize; + let mark_stack_words = n_objects.clamp(INIT_STACK_SIZE.as_usize(), MAX_MARK_STACK_SIZE) + + size_of::().as_usize(); total_heap_size_bytes + bitmap_size_bytes as usize + (mark_stack_words * WORD_SIZE) } @@ -343,7 +343,7 @@ fn create_dynamic_heap( let field_offset = obj_offset + (size_of::() + Words(1 + ref_idx as u32)) .to_bytes() - .0 as usize; + .as_usize(); write_word(dynamic_heap, field_offset, u32::try_from(ref_addr).unwrap()); } } @@ -352,8 +352,10 @@ fn create_dynamic_heap( let n_objects = refs.len(); // fields+1 for the scalar field (idx) let n_fields: usize = refs.iter().map(|(_, fields)| fields.len() + 1).sum(); - let continuation_table_offset = - (size_of::() * n_objects as u32).to_bytes().0 as usize + n_fields * WORD_SIZE; + let continuation_table_offset = (size_of::() * n_objects as u32) + .to_bytes() + .as_usize() + + n_fields * WORD_SIZE; { let mut heap_offset = continuation_table_offset; @@ -395,10 +397,10 @@ fn create_static_heap( write_word(heap, WORD_SIZE, u32::try_from(roots.len()).unwrap()); // Current offset in the heap for the next static roots array element - let mut root_addr_offset = size_of::().to_bytes().0 as usize; + let mut root_addr_offset = size_of::().to_bytes().as_usize(); // Current offset in the heap for the MutBox of the next root - let mut mutbox_offset = (size_of::().0 as usize + roots.len()) * WORD_SIZE; + let mut mutbox_offset = (size_of::().as_usize() + roots.len()) * WORD_SIZE; for root_address in root_addresses { // Add a MutBox for the object @@ -417,7 +419,7 @@ fn create_static_heap( ); root_addr_offset += WORD_SIZE; - mutbox_offset += size_of::().to_bytes().0 as usize; + mutbox_offset += size_of::().to_bytes().as_usize(); } // Write continuation table pointer as the last word in static heap diff --git a/rts/motoko-rts-tests/src/main.rs b/rts/motoko-rts-tests/src/main.rs index 2b27579ce88..f18df6ac569 100644 --- a/rts/motoko-rts-tests/src/main.rs +++ b/rts/motoko-rts-tests/src/main.rs @@ -37,7 +37,7 @@ fn main() { // Called by the RTS to panic #[no_mangle] extern "C" fn rts_trap(ptr: *const u8, len: Bytes) -> ! { - let msg = unsafe { std::slice::from_raw_parts(ptr, len.0 as usize) }; + let msg = unsafe { std::slice::from_raw_parts(ptr, len.as_usize()) }; match core::str::from_utf8(msg) { Err(err) => panic!( "rts_trap_with called with non-UTF8 string (error={:?}, string={:?})", diff --git a/rts/motoko-rts-tests/src/memory.rs b/rts/motoko-rts-tests/src/memory.rs index 1df0d355d26..19dd3daf6e6 100644 --- a/rts/motoko-rts-tests/src/memory.rs +++ b/rts/motoko-rts-tests/src/memory.rs @@ -8,8 +8,8 @@ pub struct TestMemory { impl TestMemory { pub fn new(size: Words) -> TestMemory { - let bytes = size.to_bytes().0; - let heap = vec![0u8; bytes as usize].into_boxed_slice(); + let bytes = size.to_bytes().as_usize(); + let heap = vec![0u8; bytes].into_boxed_slice(); let hp = heap.as_ptr() as usize; TestMemory { heap, hp } } @@ -32,7 +32,7 @@ impl Memory for TestMemory { // Update heap pointer let old_hp = self.hp; - let new_hp = old_hp + bytes.0 as usize; + let new_hp = old_hp + bytes.as_usize(); self.hp = new_hp; // Grow memory if needed diff --git a/rts/motoko-rts/src/bigint.rs b/rts/motoko-rts/src/bigint.rs index 5cca79425bd..1f0110eec7c 100644 --- a/rts/motoko-rts/src/bigint.rs +++ b/rts/motoko-rts/src/bigint.rs @@ -44,10 +44,10 @@ unsafe fn mp_alloc(mem: &mut M, size: Bytes) -> *mut u8 { // NB. Cannot use as_bigint() here as header is not written yet let blob = ptr.get_ptr() as *mut BigInt; (*blob).header.tag = TAG_BIGINT; - // libtommath stores the size of the object in alloc - // as count of mp_digits (u64) - debug_assert_eq!((size.0 as usize % core::mem::size_of::()), 0); - (*blob).mp_int.alloc = (size.0 as usize / core::mem::size_of::()) as i32; + // libtommath stores the size of the object in alloc as count of mp_digits (u64) + let size = size.as_usize(); + debug_assert_eq!((size % core::mem::size_of::()), 0); + (*blob).mp_int.alloc = (size / core::mem::size_of::()) as i32; blob.payload_addr() as *mut u8 } @@ -66,7 +66,7 @@ pub unsafe fn mp_calloc( let payload = mp_alloc(mem, size) as *mut u32; // NB. alloc_bytes rounds up to words so we do the same here to set the whole buffer - for i in 0..size.to_words().0 { + for i in 0..size.to_words().as_usize() { *payload.add(i as usize) = 0; } diff --git a/rts/motoko-rts/src/debug.rs b/rts/motoko-rts/src/debug.rs index ae187091a27..92dbd523987 100644 --- a/rts/motoko-rts/src/debug.rs +++ b/rts/motoko-rts/src/debug.rs @@ -110,7 +110,7 @@ unsafe fn print_heap(heap_start: u32, heap_end: u32) { write_buf.reset(); let obj_size = object_size(p as usize); - p += obj_size.to_bytes().0; + p += obj_size.to_bytes().as_u32(); i += obj_size; } } @@ -200,7 +200,7 @@ pub(crate) unsafe fn print_boxed_object(buf: &mut WriteBuf, p: usize) { } TAG_BLOB => { let blob = obj as *const Blob; - let _ = write!(buf, "", (*blob).len.0); + let _ = write!(buf, "", (*blob).len.as_u32()); } TAG_FWD_PTR => { let ind = obj as *const FwdPtr; @@ -219,7 +219,7 @@ pub(crate) unsafe fn print_boxed_object(buf: &mut WriteBuf, p: usize) { let _ = write!( buf, "", - (*concat).n_bytes.0, + (*concat).n_bytes.as_u32(), (*concat).text1.get_raw(), (*concat).text2.get_raw() ); @@ -229,7 +229,7 @@ pub(crate) unsafe fn print_boxed_object(buf: &mut WriteBuf, p: usize) { } TAG_FREE_SPACE => { let free_space = obj as *const FreeSpace; - let _ = write!(buf, "", (*free_space).words.0); + let _ = write!(buf, "", (*free_space).words.as_u32()); } other => { let _ = write!(buf, "", other); diff --git a/rts/motoko-rts/src/gc/copying.rs b/rts/motoko-rts/src/gc/copying.rs index f68e91e7d29..7aaf531cc3d 100644 --- a/rts/motoko-rts/src/gc/copying.rs +++ b/rts/motoko-rts/src/gc/copying.rs @@ -28,7 +28,7 @@ unsafe fn copying_gc(mem: &mut M) { // note_live_size |live_size| ic::MAX_LIVE = ::core::cmp::max(ic::MAX_LIVE, live_size), // note_reclaimed - |reclaimed| ic::RECLAIMED += Bytes(reclaimed.0 as u64), + |reclaimed| ic::RECLAIMED += Bytes(u64::from(reclaimed.as_u32())), ); ic::LAST_HP = ic::HP; @@ -73,7 +73,7 @@ pub unsafe fn copying_gc_internal< while p < get_hp() { let size = object_size(p); scav(mem, begin_from_space, begin_to_space, p); - p += size.to_bytes().0 as usize; + p += size.to_bytes().as_usize(); } let end_to_space = get_hp(); diff --git a/rts/motoko-rts/src/gc/mark_compact.rs b/rts/motoko-rts/src/gc/mark_compact.rs index 455bb7c9659..74404c80734 100644 --- a/rts/motoko-rts/src/gc/mark_compact.rs +++ b/rts/motoko-rts/src/gc/mark_compact.rs @@ -39,7 +39,7 @@ unsafe fn compacting_gc(mem: &mut M) { // note_live_size |live_size| ic::MAX_LIVE = ::core::cmp::max(ic::MAX_LIVE, live_size), // note_reclaimed - |reclaimed| ic::RECLAIMED += Bytes(reclaimed.0 as u64), + |reclaimed| ic::RECLAIMED += Bytes(u64::from(reclaimed.as_u32())), ); ic::LAST_HP = ic::HP; @@ -200,7 +200,7 @@ unsafe fn update_refs(set_hp: SetHp, heap_base: u32) { memcpy_words(p_new as usize, p as usize, p_size_words); } - free += p_size_words.to_bytes().0; + free += p_size_words.to_bytes().as_u32(); // Thread forward pointers of the object thread_fwd_pointers(p_new as *mut Obj, heap_base); diff --git a/rts/motoko-rts/src/gc/mark_compact/bitmap.rs b/rts/motoko-rts/src/gc/mark_compact/bitmap.rs index 007583b2992..90cd7dcff44 100644 --- a/rts/motoko-rts/src/gc/mark_compact/bitmap.rs +++ b/rts/motoko-rts/src/gc/mark_compact/bitmap.rs @@ -7,7 +7,7 @@ static mut BITMAP_PTR: *mut u8 = core::ptr::null_mut(); pub unsafe fn alloc_bitmap(mem: &mut M, heap_size: Bytes) { // We will have at most this many objects in the heap, each requiring a bit - let n_bits = heap_size.to_words().0; + let n_bits = heap_size.to_words().as_u32(); // Each byte will hold 8 bits. let bitmap_bytes = (n_bits + 7) / 8; // Also round allocation up to 8-bytes to make iteration efficient. We want to be able to read @@ -54,10 +54,10 @@ pub struct BitmapIter { } pub unsafe fn iter_bits() -> BitmapIter { - let blob_len_bytes = (BITMAP_PTR.sub(size_of::().to_bytes().0 as usize) as *mut Obj) + let blob_len_bytes = (BITMAP_PTR.sub(size_of::().to_bytes().as_usize()) as *mut Obj) .as_blob() .len() - .0; + .as_u32(); debug_assert_eq!(blob_len_bytes % 8, 0); diff --git a/rts/motoko-rts/src/gc/mark_compact/mark_stack.rs b/rts/motoko-rts/src/gc/mark_compact/mark_stack.rs index 45720ab4236..ca977fd3ed3 100644 --- a/rts/motoko-rts/src/gc/mark_compact/mark_stack.rs +++ b/rts/motoko-rts/src/gc/mark_compact/mark_stack.rs @@ -28,7 +28,7 @@ pub unsafe fn alloc_mark_stack(mem: &mut M) { STACK_BLOB_PTR = alloc_blob(mem, INIT_STACK_SIZE.to_bytes()).get_ptr() as *mut Blob; STACK_BASE = STACK_BLOB_PTR.payload_addr() as *mut usize; STACK_PTR = STACK_BASE; - STACK_TOP = STACK_BASE.add(INIT_STACK_SIZE.0 as usize); + STACK_TOP = STACK_BASE.add(INIT_STACK_SIZE.as_usize()); } pub unsafe fn free_mark_stack() { @@ -46,7 +46,7 @@ pub unsafe fn grow_stack(mem: &mut M) { // Make sure nothing was allocated after the stack debug_assert_eq!(STACK_TOP, p); - let new_cap: Words = Words(stack_cap.0 * 2); + let new_cap: Words = stack_cap * 2; (*STACK_BLOB_PTR).len = new_cap.to_bytes(); STACK_TOP = STACK_BASE.add(new_cap.as_usize()); } diff --git a/rts/motoko-rts/src/mem_utils.rs b/rts/motoko-rts/src/mem_utils.rs index 5cb820e1626..9cdab95bca7 100644 --- a/rts/motoko-rts/src/mem_utils.rs +++ b/rts/motoko-rts/src/mem_utils.rs @@ -1,13 +1,13 @@ use crate::types::{Bytes, Words}; pub(crate) unsafe fn memcpy_words(to: usize, from: usize, n: Words) { - libc::memcpy(to as *mut _, from as *const _, n.to_bytes().0 as usize); + libc::memcpy(to as *mut _, from as *const _, n.to_bytes().as_usize()); } pub(crate) unsafe fn memcpy_bytes(to: usize, from: usize, n: Bytes) { - libc::memcpy(to as *mut _, from as *const _, n.0 as usize); + libc::memcpy(to as *mut _, from as *const _, n.as_usize()); } pub(crate) unsafe fn memzero(to: usize, n: Words) { - libc::memset(to as *mut _, 0, n.to_bytes().0 as usize); + libc::memset(to as *mut _, 0, n.to_bytes().as_usize()); } diff --git a/rts/motoko-rts/src/memory/ic.rs b/rts/motoko-rts/src/memory/ic.rs index 9a1dc0ae5f1..39bb474e832 100644 --- a/rts/motoko-rts/src/memory/ic.rs +++ b/rts/motoko-rts/src/memory/ic.rs @@ -63,11 +63,11 @@ impl Memory for IcMemory { unsafe fn alloc_words(&mut self, n: Words) -> Value { let bytes = n.to_bytes(); // Update ALLOCATED - ALLOCATED += Bytes(bytes.0 as u64); + ALLOCATED += Bytes(u64::from(bytes.as_u32())); // Update heap pointer let old_hp = HP; - let new_hp = old_hp + bytes.0; + let new_hp = old_hp + bytes.as_u32(); HP = new_hp; // Grow memory if needed @@ -80,7 +80,7 @@ impl Memory for IcMemory { /// Page allocation. Ensures that the memory up to, but excluding, the given pointer is allocated. #[inline(never)] unsafe fn grow_memory(ptr: usize) { - let page_size = u64::from(WASM_PAGE_SIZE.0); + let page_size = u64::from(WASM_PAGE_SIZE.as_u32()); let total_pages_needed = (((ptr as u64) + page_size - 1) / page_size) as usize; let current_pages = wasm32::memory_size(0); if total_pages_needed > current_pages { diff --git a/rts/motoko-rts/src/principal_id.rs b/rts/motoko-rts/src/principal_id.rs index 927c2fad047..4849c8d99a4 100644 --- a/rts/motoko-rts/src/principal_id.rs +++ b/rts/motoko-rts/src/principal_id.rs @@ -21,7 +21,7 @@ pub unsafe extern "C" fn compute_crc32(blob: Value) -> u32 { let mut crc: u32 = !0; - for i in 0..len.0 { + for i in 0..len.as_u32() { let octet = blob.get(i); crc = (crc >> 8) ^ CRC_TABLE[usize::from((crc & 0xFF) as u8 ^ octet)]; } @@ -97,7 +97,7 @@ pub unsafe fn base32_of_checksummed_blob(mem: &mut M, b: Value) -> Va let n = b.as_blob().len(); let mut data = b.as_blob().payload_addr(); - let r = alloc_blob(mem, Bytes((n.0 + 4 + 4) / 5 * 8)); // contains padding + let r = alloc_blob(mem, Bytes((n.as_u32() + 4 + 4) / 5 * 8)); // contains padding let blob = r.as_blob(); let dest = blob.payload_addr(); @@ -113,7 +113,7 @@ pub unsafe fn base32_of_checksummed_blob(mem: &mut M, b: Value) -> Va enc_stash(&mut pump, (checksum >> 8) as u8); enc_stash(&mut pump, checksum as u8); - for _ in 0..n.0 { + for _ in 0..n.as_u32() { enc_stash(&mut pump, *data); data = data.add(1); } @@ -184,7 +184,7 @@ pub unsafe fn base32_to_blob(mem: &mut M, b: Value) -> Value { let mut data = b.as_blob().payload_addr(); // Every group of 8 characters will yield 5 bytes - let r = alloc_blob(mem, Bytes(((n.0 + 7) / 8) * 5)); // we deal with padding later + let r = alloc_blob(mem, Bytes(((n.as_u32() + 7) / 8) * 5)); // we deal with padding later let blob = r.as_blob(); let dest = blob.payload_addr(); @@ -196,7 +196,7 @@ pub unsafe fn base32_to_blob(mem: &mut M, b: Value) -> Value { pending_data: 0, }; - for _ in 0..n.0 { + for _ in 0..n.as_u32() { dec_stash(&mut pump, *data); data = data.add(1); } @@ -223,12 +223,12 @@ unsafe fn base32_to_principal(mem: &mut M, b: Value) -> Value { let mut data = blob.payload_addr(); // Every group of 5 characters will yield 6 bytes (due to the hypen) - let r = alloc_blob(mem, Bytes(((n.0 + 4) / 5) * 6)); + let r = alloc_blob(mem, Bytes(((n.as_u32() + 4) / 5) * 6)); let blob = r.as_blob(); let mut dest = blob.payload_addr(); let mut n_written = 0; - for i in 0..n.0 { + for i in 0..n.as_u32() { let mut byte = *data; data = data.add(1); @@ -242,7 +242,7 @@ unsafe fn base32_to_principal(mem: &mut M, b: Value) -> Value { n_written += 1; // If quintet done, add hyphen - if n_written % 5 == 0 && i + 1 < n.0 { + if n_written % 5 == 0 && i + 1 < n.as_u32() { n_written = 0; *dest = b'-'; dest = dest.add(1); diff --git a/rts/motoko-rts/src/text.rs b/rts/motoko-rts/src/text.rs index 854251f0b92..5553571faf3 100644 --- a/rts/motoko-rts/src/text.rs +++ b/rts/motoko-rts/src/text.rs @@ -86,7 +86,7 @@ pub unsafe fn text_concat(mem: &mut M, s1: Value, s2: Value) -> Value let r_payload: *const u8 = r.as_blob().payload_addr(); memcpy_bytes(r_payload as usize, blob1.payload_addr() as usize, blob1_len); memcpy_bytes( - r_payload.add(blob1_len.0 as usize) as usize, + r_payload.add(blob1_len.as_usize()) as usize, blob2.payload_addr() as usize, blob2_len, ); @@ -147,11 +147,11 @@ unsafe extern "C" fn text_to_buf(mut s: Value, mut buf: *mut u8) { if s2_len < Bytes(core::mem::size_of::() as u32) { // If second string is smaller than size of a crumb just do it directly - text_to_buf(s2, buf.add(s1_len.0 as usize)); + text_to_buf(s2, buf.add(s1_len.as_usize())); s = s1; } else { // Otherwise leave a breadcrumb to the location of the second string - let new_crumb: *mut Crumb = buf.add(s1_len.0 as usize) as *mut Crumb; + let new_crumb: *mut Crumb = buf.add(s1_len.as_usize()) as *mut Crumb; (*new_crumb).t = s2; (*new_crumb).next = next_crumb; next_crumb = new_crumb; @@ -237,9 +237,9 @@ unsafe fn text_compare_range( let s2_blob = s2_obj.as_blob(); let cmp = libc::memcmp( - s1_blob.payload_addr().add(offset1.0 as usize) as *const _, - s2_blob.payload_addr().add(offset2.0 as usize) as *const _, - n.0 as usize, + s1_blob.payload_addr().add(offset1.as_usize()) as *const _, + s2_blob.payload_addr().add(offset2.as_usize()) as *const _, + n.as_usize(), ); if cmp < 0 { @@ -318,7 +318,7 @@ pub(crate) unsafe fn blob_compare(s1: Value, s2: Value) -> i32 { let payload1 = s1.as_blob().payload_addr(); let payload2 = s2.as_blob().payload_addr(); - let cmp = libc::memcmp(payload1 as *const _, payload2 as *const _, n.0 as usize); + let cmp = libc::memcmp(payload1 as *const _, payload2 as *const _, n.as_usize()); if cmp == 0 { if n1 < n2 { @@ -343,7 +343,7 @@ pub unsafe extern "C" fn text_len(text: Value) -> u32 { str::from_utf8_unchecked(slice::from_raw_parts( payload_addr as *const u8, - len.0 as usize, + len.as_usize(), )) .chars() .count() as u32 diff --git a/rts/motoko-rts/src/text_iter.rs b/rts/motoko-rts/src/text_iter.rs index 15639ef7978..e4e7a74e844 100644 --- a/rts/motoko-rts/src/text_iter.rs +++ b/rts/motoko-rts/src/text_iter.rs @@ -72,7 +72,7 @@ pub unsafe extern "C" fn text_iter_done(iter: Value) -> u32 { let blob = array.get(ITER_BLOB_IDX).as_blob(); let todo = array.get(ITER_TODO_IDX); - if pos >= blob.len().0 && todo.get_raw() == 0 { + if pos >= blob.len().as_u32() && todo.get_raw() == 0 { 1 } else { 0 @@ -88,7 +88,7 @@ pub unsafe fn text_iter_next(mem: &mut M, iter: Value) -> u32 { let pos = iter_array.get(ITER_POS_IDX).get_scalar(); // If we are at the end of the current blob, find the next blob - if pos >= blob.len().0 { + if pos >= blob.len().as_u32() { let todo = iter_array.get(ITER_TODO_IDX); if todo.get_raw() == 0 { diff --git a/rts/motoko-rts/src/types.rs b/rts/motoko-rts/src/types.rs index 6397629fee7..348c055af4f 100644 --- a/rts/motoko-rts/src/types.rs +++ b/rts/motoko-rts/src/types.rs @@ -39,6 +39,10 @@ impl Words { Bytes(self.0 * WORD_SIZE) } + pub fn as_u32(self) -> u32 { + self.0 + } + pub fn as_usize(self) -> usize { self.0 as usize } @@ -106,6 +110,10 @@ impl Bytes { Words((self.0 + WORD_SIZE - 1) / WORD_SIZE) } + pub fn as_u32(self) -> u32 { + self.0 + } + pub fn as_usize(self) -> usize { self.0 as usize } @@ -508,7 +516,7 @@ impl BigInt { } pub unsafe fn from_payload(ptr: *mut mp_digit) -> *mut Self { - (ptr as *mut u32).sub(size_of::().0 as usize) as *mut BigInt + (ptr as *mut u32).sub(size_of::().as_usize()) as *mut BigInt } /// Returns pointer to the `mp_int` struct From 1e51875182ff0f568f67b6ee6448fe2e409641f1 Mon Sep 17 00:00:00 2001 From: Claudio Russo Date: Thu, 23 Sep 2021 21:14:17 +0100 Subject: [PATCH 33/56] Doc revamp and cleanup (#2806) * lots of terminology changes, general updates and clarifications. --- doc/modules/language-guide/lang-nav.adoc | 2 +- .../pages/about-this-guide.adoc | 26 ++--- .../language-guide/pages/actor-classes.adoc | 2 +- .../language-guide/pages/basic-concepts.adoc | 96 ++++++++++++++++--- .../language-guide/pages/caller-id.adoc | 8 +- .../language-guide/pages/compiler-ref.adoc | 4 +- doc/modules/language-guide/pages/cycles.adoc | 12 +-- doc/modules/language-guide/pages/errors.adoc | 19 ++-- .../language-guide/pages/extrastuff.adoc | 10 +- .../language-guide/pages/language-manual.adoc | 2 + .../pages/modules-and-imports.adoc | 18 ++-- .../pages/motoko-introduction.adoc | 50 +++++----- doc/modules/language-guide/pages/motoko.adoc | 20 ++-- .../language-guide/pages/mutable-state.adoc | 2 +- .../language-guide/pages/overview.adoc | 15 ++- .../pages/pattern-matching.adoc | 20 ++-- doc/modules/language-guide/pages/sharing.adoc | 2 +- .../pages/structural-equality.adoc | 2 +- doc/modules/language-guide/pages/style.adoc | 17 ++-- .../language-guide/pages/upgrades.adoc | 18 ++-- 20 files changed, 211 insertions(+), 134 deletions(-) diff --git a/doc/modules/language-guide/lang-nav.adoc b/doc/modules/language-guide/lang-nav.adoc index ac19b81110c..1b60120d4fb 100644 --- a/doc/modules/language-guide/lang-nav.adoc +++ b/doc/modules/language-guide/lang-nav.adoc @@ -20,5 +20,5 @@ ** xref:language-manual.adoc[Language quick reference] ** xref:compiler-ref.adoc[Compiler reference] ** xref:motoko-grammar.adoc[Motoko grammar] -** xref:overview.adoc[Overview] +** xref:overview.adoc[Concise overview of Motoko] ** xref:style.adoc[Motoko style guidelines] diff --git a/doc/modules/language-guide/pages/about-this-guide.adoc b/doc/modules/language-guide/pages/about-this-guide.adoc index 6e2382b1b63..f55a0100196 100644 --- a/doc/modules/language-guide/pages/about-this-guide.adoc +++ b/doc/modules/language-guide/pages/about-this-guide.adoc @@ -1,7 +1,7 @@ = About this guide ifdef::env-github,env-browser[:outfilesuffix:.adoc] :proglang: Motoko -:platform: Internet Computer platform +:platform: Internet Computer blockchain network :IC: Internet Computer :company-id: DFINITY :sdk-short-name: DFINITY Canister SDK @@ -10,14 +10,14 @@ ifdef::env-github,env-browser[:outfilesuffix:.adoc] The _{proglang} Programming Language Guide_ introduces key features of the general-purpose {proglang} programming language and provides examples and reference information to help you learn the nuances of the language and the practical implications of how to apply it. -The {proglang} programming language is optimized for developing programs that run on the {platform} and to work with the {sdk-long-name}. -However, you can also write programs using {proglang} for other platforms and to run in other contexts. -This guide attempts to strike a balance between highlighting features that are uniquely suited to running on the {IC} and features that are generally-applicable or well-suited for programs running on other platforms. +The {proglang} programming language is optimized for developing programs that run on the {platform} and to work with the {sdk-long-name}. +You could, in principle, also write programs using {proglang} for more traditional platforms and to run in other contexts, though support for this is currently best-effort and incomplete. +This guide attempts to strike a balance between highlighting features that are uniquely suited to running on the {IC} and features that are generally-applicable or well-suited for programs running on all targets. == Intended audience This guide provides reference information and examples for programmers who want to explore or plan to use the {proglang} programming language. -Most of the information in this guide is applicable independent of whether you are developing programs to run on the {platform} or working with the {sdk-short-name}. +Most of the information in this guide is applicable independent of whether you are developing programs to run on the {IC} or working with the {sdk-short-name}. The guide assumes you are familiar with basic programming principles and terminology and have at least some experience writing programs in a high-level programming language such as C++ or Rust, or have practical experience working with a scripting language such as JavaScript or TypeScript. In addition, {proglang} incorporates some aspects of functional programming, so you might find some knowledge of functional programming design principles helpful in learning to use {proglang}. @@ -26,7 +26,7 @@ Although this guide is intended to help readers from different backgrounds under == Using this guide -To provide a framework for learning {proglang}, you might want to start by reviewing <>. +To provide a framework for learning {proglang}, you might want to start by reviewing <>. The <> describe the core design considerations for the development and evolution of the {proglang} programming language. With those considerations in mind, you can start to explore fundamental concepts, including the role of types and type annotations, in simple code examples and small programs. @@ -59,7 +59,7 @@ For transparency into the principles that guide the engineering effort, the engi The following guiding principles represent the core values of the engineering organization in prioritized order: -. Seamless integration with the link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC} platform] to ensure that {proglang} provides full language support for the actor-based model, asynchronous messaging, data persistence, interface description language interoperability, and other features. +. Seamless integration with the link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC} blockchain network] to ensure that {proglang} provides full language support for the actor-based model, asynchronous messaging, data persistence, interface description language interoperability, and other features. . Ergonomics to ensure that {proglang} embraces familiarity, simplicity, clarity, explicitness, and other human factors. . Formal correctness to ensure that {proglang} maintains state isolation, a sound type system and type safety, precision, pattern matching, appropriate defaults, and coding best-practices. @@ -68,18 +68,18 @@ The following guiding principles represent the core values of the engineering or The following principles represent the secondary values of the engineering organization that are deemed important but not primary driving factors: . Expressiveness, so that {proglang} provides first-class functions, polymorphism, pattern matching, and more as the language evolves. -. Performance, so that {proglang} provides reasonably fast operation initially and continues to improves as the language evolves. -. Readiness, so the {proglang} comes with "batteries included" in the form of libraries and examples and out-of-the-box integration wit the {sdk-short-name}. +. Performance, so that {proglang} provides reasonably fast operation initially and continues to improves as the language evolves. +. Readiness, so the {proglang} comes with "batteries included" in the form of libraries and examples and out-of-the-box integration with the {sdk-short-name}. === Non-goals As a counterpoint to the core values and goals, the engineering organization also identified the following as "non-goals" that are outside of the scope of the engineering effort: -. Having a more advanced type system, with more complex types. +. Having a more advanced type system, with cutting-edge features. . Simplicity over functionality in design or implementation (the "Worse is Better" approach). -. Interoperability or support for running {proglang} programs on blockchain platforms other than the {IC}. +. Interoperability or support for running {proglang} programs on blockchains other than the {IC}. -== Finding more information +== Finding more information For information about using {proglang} with the {sdk-short-name}, see the link:../developers-guide/sdk-guide{outfilesuffix}[SDK Developer Tools]. @@ -99,7 +99,7 @@ For background information on various topics relevant to the design, use, or dep == Getting additional support -If you are looking for more information or technical support, the {company-id} website provides quick access to frequently-asked questions, technical articles, developer updates, and other resources. +If you are looking for more information or technical support, the {company-id} website provides quick access to frequently-asked questions, technical articles, developer updates, and other resources. From the website, you can search knowledge base articles, open and view support cases, sign up for the newsletter, read the latest blog posts, view how-to videos, download software updates, or exchange ideas with members of the community. In addition to the resources available on the website, you can connect with {company-id} or other developers using social media or by visiting the {company-id} Community Forum on Discourse and joining the conversation. diff --git a/doc/modules/language-guide/pages/actor-classes.adoc b/doc/modules/language-guide/pages/actor-classes.adoc index 5ccb7115be4..22d5b6ecb3b 100644 --- a/doc/modules/language-guide/pages/actor-classes.adoc +++ b/doc/modules/language-guide/pages/actor-classes.adoc @@ -1,7 +1,7 @@ [#actor_classes] == Actor classes -Actor classes enable you to create networks of actors programmatically. +Actor classes enable you to create networks of actors (canister smart contracts) _programmatically_. Currently, 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` to values of type `Text`. It provides simple insert and lookup functions, `put(k, v)` and `get(k)`, for working with these keys and values. diff --git a/doc/modules/language-guide/pages/basic-concepts.adoc b/doc/modules/language-guide/pages/basic-concepts.adoc index d10fd1cf83e..7251f498bfc 100644 --- a/doc/modules/language-guide/pages/basic-concepts.adoc +++ b/doc/modules/language-guide/pages/basic-concepts.adoc @@ -4,7 +4,9 @@ {proglang} is designed for distributed programming with actors. -Before you begin writing programs for distributed processing using actors, you should be familiar with a few of the basic building blocks of any programming language and with {proglang} in particular. +When programming on the Internet Computer in {proglang}, each **actor** represents an **{IC} canister smart contract** with a Candid interface, whether written in {proglang}, Rust, Wasm or some other language that compiles to Wasm. Within {proglang}, we use the term *actor* to refer to any canister, authored in any language that deploys to the {IC}. The role of {proglang} is to make these actors easy to author, and easy to use programmatically, once deployed. + +Before you begin writing distributed applications using actors, you should be familiar with a few of the basic building blocks of any programming language and with {proglang} in particular. To get you started, this section introduces the following key concepts and terms that are used throughout the remainder of the documentation and that are essential to learning to program in {proglang}: * program @@ -44,7 +46,7 @@ Rather, these tiny programs illustrate snippets of {proglang} for writing those The examples in this section illustrate basic principles using simple expressions, such as arithmetic. For an overview of the full expression syntax of {proglang}, see the link:langauge-manual{outfilesuffix[Lanaguage quick reference]. -As a starting point, the following code snippet consists of two declarations—for the variables `x` and `y`—followed by an expression to form a single program: +As a starting point, the following code snippet consists of two declarations — for the variables `x` and `y` — followed by an expression to form a single program: [source, motoko] .... @@ -142,7 +144,7 @@ In so doing, we produce the following expression, which is also a program: 1 * (1 + 1) + 1 .... -This is also a valid program—of the same type and with the same behavior (result value `3`)—as the original program. +This is also a valid program — of the same type and with the same behavior (result value `3`) — as the original program. We can also form a single expression using a block. @@ -221,9 +223,9 @@ As a broader language overview, however, we briefly summarize the other value fo {proglang} permits the following primitive value forms: - Boolean values (`true` and `false`). - - Integers (...,`-2`, `-1`, `0`, `1`, `2`, ...); Bounded and _unbounded_ variants. - - Natural numbers (`0`, `1`, `2`, ...); Bounded and _unbounded_ variants. - - Text values --- strings of unicode characters. + - Integers (...,`-2`, `-1`, `0`, `1`, `2`, ...) - bounded and _unbounded_ variants. + - Natural numbers (`0`, `1`, `2`, ...) - bounded and _unbounded_ variants. + - Text values - strings of unicode characters. By default, **integers** and **natural numbers** are _unbounded_ and do not overflow. Instead, they use representations that grow to accommodate any finite number. @@ -261,11 +263,11 @@ In practical terms, like `void`, the unit value usually carries zero representat Unlike the `void` type, there _is_ a unit value, but like the `void` return value, the unit value carries no values internally, and as such, it always carries zero _information_. -Another mathematical way to think of the unit value is as a tuple with no elements---the nullary ("`zero-ary`") tuple. There is only one value with these properties, so it is mathematically unique, and thus need not be represented at runtime. +Another mathematical way to think of the unit value is as a tuple with no elements - the nullary ("`zero-ary`") tuple. There is only one value with these properties, so it is mathematically unique, and thus need not be represented at runtime. === Natural numbers -The members of this type consist of the usual values ---`0`, `1`, `2`, ...----but, as in mathematics, the members of are not bound to a special maximum size. +The members of this type consist of the usual values - `0`, `1`, `2`, ... - but, as in mathematics, the members of are not bound to a special maximum size. Rather, the runtime representation of these values accommodates arbitrary-sized numbers, making their "overflow" (nearly) impossible. (_nearly_ because it is the same event as running out of program memory, which can always happen for some programs in extreme situations). @@ -462,14 +464,82 @@ P.unreachable() As in the situations above, this function type-checks in all contexts, and when evaluated, traps in all contexts. [[overview-traps]] -=== Execution traps stop the program +=== Traps due to execution failure + +Some errors, such as division by zero, out-of-bounds array indexing, and pattern match failure are not prevented by the type system, but can cause dynamic failures called _traps_. + +[source, motoko] +.... +1/0; // traps due to division by 0 +.... + +[source, motoko] +.... +let a = ["hello", "world"]; +a[2]; // traps due to out-of-bounds indexing +.... + +[source, motoko] +.... +let true = false; // pattern match failure +.... + +We say that code _traps_ when its exection causes a _trap_. + +Execution of code is aborted at the first trap and makes no further progress. + +NOTE: Traps that occur within actor messages are more subtle: they don't abort the entire actor, but prevent that particular message from proceeding, rolling back any yet uncommitted state changes. Other messages on the actor will continue execution. + +[[overview-debug-trap]] +=== Explicit traps + +Occasionally it can be useful to force an unconditional trap, with a user-defined message. + +The `Debug` library provides the function `trap(t)` for this purpose, which can be used in any context. + +[source, motoko] +.... +import Debug "mo:base/Debug"; + +Debug.trap("oops!"); +.... + + +[source, motoko] +.... +import Debug "mo:base/Debug"; + +let swear : Text = Debug.trap("oh my!"); +.... + + +(The `Prelude` functions `nyi()`, `unreachable()` and `xxx()` discussed above are simple wrappers around `Debug.trap`.) -Each form above is a simple wrapper around the always-fail use of the link:language-manual{outfilesuffix}#exp-assert[assert primitive]: +[[overview-assertions]] +=== Assertions + +Assertions allow you to conditionally trap when some Boolean test fails to hold, +but continue execution otherwise. For example, + +[source, motoko] +.... +let n = 65535; +assert n % 2 == 0; // traps when n not even +.... [source, motoko] .... -assert false +assert false; // unconditionally traps +.... + + +[source, motoko] +.... +import Debug "mo:base/Debug"; + +assert 1 > 0; // never traps +Debug.print "bingo!"; .... -Dynamically, we call this program-halting behavior a _program(-generated) trap_, and we say that the program _traps_ when it executes this code. -It will cease to progress further. +Because an assertion may succeed, and thus proceed with execution, +it may only be used in context where a value of type `()` is expected. \ No newline at end of file diff --git a/doc/modules/language-guide/pages/caller-id.adoc b/doc/modules/language-guide/pages/caller-id.adoc index 332654dd3ef..0d1bb5ab1d3 100644 --- a/doc/modules/language-guide/pages/caller-id.adoc +++ b/doc/modules/language-guide/pages/caller-id.adoc @@ -4,12 +4,12 @@ {proglang}'s shared functions support a simple form of caller identification that allows you to inspect the {IC} **principal** associated with the caller of a function. -The principal associated with a call is a value that identifies a unique user or canister. +The principal associated with a call is a value that identifies a unique user or canister smart contract. You can use the **principal** associated with the caller of a function to implement a basic form of _access-control_ in your program. -In {proglang}, the `+shared+` keyword is used to declare a shared function. -The shared function can also declare an optional parameter of type `+{caller : Principal}+`. +In {proglang}, the `+shared+` keyword is used to declare a shared function. +The shared function can also declare an optional parameter of type `+{caller : Principal}+`. //// (The type is a record to accommodate future extension.) //// @@ -25,7 +25,7 @@ shared(msg) func inc() : async () { In this example, the shared function `+inc()+` specifies a `+msg+` parameter, a record, and the `+msg.caller+` accesses the principal field of `+msg+`. -The calls to the `+inc()+` function do not change— at each call site, the caller's principal is provided by the system, not the user—so the principal cannot be forged or spoofed by a malicious user. +The calls to the `+inc()+` function do not change — at each call site, the caller's principal is provided by the system, not the user — so the principal cannot be forged or spoofed by a malicious user. To access the caller of an actor class constructor, you use the same (optional) syntax on the actor class declaration. For example: diff --git a/doc/modules/language-guide/pages/compiler-ref.adoc b/doc/modules/language-guide/pages/compiler-ref.adoc index 662e40fe04a..68871cbe6fa 100644 --- a/doc/modules/language-guide/pages/compiler-ref.adoc +++ b/doc/modules/language-guide/pages/compiler-ref.adoc @@ -4,7 +4,7 @@ :company-id: DFINITY :!page-repl: -The Motoko compiler (`+moc+`) is the primary tool for compiling Motoko programs into executable WebAssembly (Wasm) modules. +The {proglang} compiler (`+moc+`) is the primary tool for compiling {proglang} programs into executable WebAssembly (Wasm) modules. The compiler runs in the background when you build projects using the {sdk-short-name}. If you invoke the compiler directly on the command-line, you can press CTRL-C to exit. @@ -12,7 +12,7 @@ This section provides compiler command-line reference information. == moc -Use the Motoko compiler (`+moc+`) to compile Motoko programs into executable WebAssembly (Wasm) modules. +Use the {proglang} compiler (`+moc+`) to compile {proglang} programs into executable WebAssembly (Wasm) modules. === Basic usage diff --git a/doc/modules/language-guide/pages/cycles.adoc b/doc/modules/language-guide/pages/cycles.adoc index b814311af64..251b5deae2b 100644 --- a/doc/modules/language-guide/pages/cycles.adoc +++ b/doc/modules/language-guide/pages/cycles.adoc @@ -2,11 +2,11 @@ :proglang: Motoko :company-id: DFINITY -Usage of the Internet Computer is measured, and paid for, in _cycles_. -The {company-id} platform maintains a per canister balance of cycles -and cycles can be transferred between canisters. +Usage of the {IC} is measured, and paid for, in _cycles_. +The {IC} maintains a balance of cycles per canister smart contract. +In addition, cycles can be transferred between canisters. -In {proglang}, each actor has a balance of cycles. +In {proglang} programs targeting the {IC}, each actor represents an {IC} canister, and has an associated balance of cycles. The ownership of cycles can be transferred between actors. Cycles are selectively sent and received through messages, that is, shared function calls. A caller can choose to transfer cycles with a call, and a callee can choose to @@ -26,7 +26,7 @@ provided by the link:../base-libraries/ExperimentalCycles{outfilesuffix}[Experim library in package `+base+`. NOTE: This library is subject to change and likely to be replaced by -more high-level support for cycles in later versions of Motoko. +more high-level support for cycles in later versions of {proglang}. == The `+ExperimentalCycles+` Library @@ -118,7 +118,7 @@ will not exceed `+capacity+`, breaking the piggy bank. Because the deposit function only accepts a portion of the available amount, a caller whose deposit exceeds the limit will receive an implicit refund of any unaccepted cycles. Refunds are automatic and -ensured by the platform. +ensured by the {IC} infrastructure. Since transfer of cycles is one-directional (from caller to callee), retrieving cycles requires the use of an explicit callback (the diff --git a/doc/modules/language-guide/pages/errors.adoc b/doc/modules/language-guide/pages/errors.adoc index 495f7de76f7..ed73dbee344 100644 --- a/doc/modules/language-guide/pages/errors.adoc +++ b/doc/modules/language-guide/pages/errors.adoc @@ -3,15 +3,16 @@ :base: https://github.com/dfinity/motoko-base[base] :!page-repl: -== Different ways to represent errors in Motoko == -. Option -. Result -. Asynchronous Exceptions +There are three primary ways to represent and handle errors values in {proglang}: + +- Option values (with a non-informative `null` indicated _some_ error); +- `Result` variants (with a descriptive `#err value` providing more information about the error); and +- `Error` values (that, in an asynchronous context, can be thrown and caught - similar to exceptions - and contain a numeric code and message). == Our Example API == Let's assume we're building an API for a Todo application and want to expose a function that lets a user mark one of their Todo's as *Done*. To keep it simple we'll accept a `TodoId` and return an `Int` that represents how many seconds the Todo has been open. -We're also assuming we're running in our own canister so we return an async value. +We're also assuming we're running in our own actor so we return an async value. If nothing would ever go wrong that would leave us with the following API: [source.no-repl, motoko] @@ -41,7 +42,7 @@ We now realize that there are conditions under which marking a Todo as done fail * The `id` could reference a non-existing Todo * The Todo might already be marked as done -We'll now talk about the different ways to communicate these errors in Motoko and slowly improve our solution. +We'll now talk about the different ways to communicate these errors in {proglang} and slowly improve our solution. == What error type to prefer == @@ -156,14 +157,14 @@ The {base} library exposes a collection of higher-order functions from the `Opti === Converting back and forth between Option/Result === Sometimes you'll want to move between Options and Results. A Hashmap lookup returns `+null+` on failure and that's fine, but maybe the caller has more context and can turn that lookup failure into a meaningful `+Result+`. -At other times you don't need the additional information a `+Result+ provides and just want to convert all `#err` cases into `null`. +At other times you don't need the additional information a `+Result+` provides and just want to convert all `#err` cases into `null`. For these situations {base} provides the `fromOption` and `toOption` functions in the `Result` module. == Asynchronous Errors == -The last way of dealing with errors in Motoko is to use asynchronous `Error` handling, a restricted form of the exception handling familiar from other languages. +The last way of dealing with errors in {proglang} is to use asynchronous `Error` handling, a restricted form of the exception handling familiar from other languages. Unlike the exceptions of other languages, {proglang} _errors_ values, can only be thrown and caught in asynchronous contexts, typically the body of a `shared` function or `async` expression. Non-`shared` functions cannot employ structured error handling. -This means you can exit a shared function by `throw`ing an `Error` value and `+try+` some code calling a shared function on another canister, `catch`ing its failure as a result of type `Error`, but you can't use these error handling constructs in regular code, outside of an asynchronous context. +This means you can exit a shared function by ``throw``ing an `Error` value and `+try+` some code calling a shared function on another actor, ``catch``ing its failure as a result of type `Error`, but you can't use these error handling constructs in regular code, outside of an asynchronous context. Asynchronous ``Error``s should generally only be used to signal unexpected failures that you cannot recover from, and that you don't expect many consumers of your API to handle. If a failure should be handled by your caller you should make it explicit in your signature by returning a `Result` instead. For completeness here is the `markDone` example with exceptions: diff --git a/doc/modules/language-guide/pages/extrastuff.adoc b/doc/modules/language-guide/pages/extrastuff.adoc index 198213ec60b..ea38c9f2014 100644 --- a/doc/modules/language-guide/pages/extrastuff.adoc +++ b/doc/modules/language-guide/pages/extrastuff.adoc @@ -3,7 +3,7 @@ {proglang} provides: -* A high-level language for programming applications to run on the internet computer platform. +* A high-level language for programming applications to run on the {IC} blockchain network. * A simple design that uses familiar syntax that is easy for programmers to learn. @@ -15,11 +15,11 @@ ==== Why a new language? -The Internet Computer provides a network platform that can support programs written in different languages. +The Internet Computer provides a blockchain network that can support programs written in different languages. The only requirement is that the program must support compilation to WebAssembly code. WebAssembly (commonly-abbreviated as Wasm) is a low-level computer instruction format for virtual machines. -Because WebAssembly code is designed to provide portable low-level instructions that enable applications to be deployed on platforms such as the web, it is a natural fit for deploying applications that are intended to run on the internet computer platform. -However, most of the higher-level languages--like C, C++, and Rust--that support compiling to WebAssembly are either too unsafe (for example, C or C++) or too complex (for example, Rust) for developers who want to deliver secure applications without a long learning curve. +Because WebAssembly code is designed to provide portable low-level instructions that enable applications to be deployed on many platforms such as the web, it is a natural fit for deploying applications that are intended to run on the {IC}. +However, most of the higher-level languages - like C, C++, and Rust - that support compiling to WebAssembly are either too unsafe (for example, C or C++) or too complex (for example, Rust) for developers who want to deliver secure applications without a long learning curve. To address the need for correctness without complexity, {company-id} has designed its own *{proglang}* programming language. *{proglang}* provides a simple and expressive alternative to other programming languages that is easy to learn whether you are a new or experienced programmer. @@ -27,7 +27,7 @@ To address the need for correctness without complexity, {company-id} has designe WebAssembly is language-agnostic. It does not require a high-level type system for language inter-operation. -Although {proglang} is specifically designed to compile to WebAssembly and make it easy to write programs to run on the internet computer, it is just one of many languages you can eventually use to develop applications for the internet computer platform. +Although {proglang} is specifically designed to compile to WebAssembly and make it easy to write programs to run on the internet computer, it is just one of many languages you can eventually use to develop applications for the {IC} blockchain network. To support multiple languages and typed, cross-language communication, {company-id} also provides an *Interface Definition Language* (IDL). The {proglang} compiler automates the production and consumption of IDL files using the type signatures in {proglang} programs and the structure of imported IDL interfaces. diff --git a/doc/modules/language-guide/pages/language-manual.adoc b/doc/modules/language-guide/pages/language-manual.adoc index fd28b0626e2..4014d2364b8 100644 --- a/doc/modules/language-guide/pages/language-manual.adoc +++ b/doc/modules/language-guide/pages/language-manual.adoc @@ -45,6 +45,8 @@ For example, this section provides technical details of interest to the followin The language quick reference is intended to provide complete reference information about {proglang}, but this section does _not_ provide explanatory text or usage information. Therefore, this section is typically not suitable for readers who are new to programming languages or who are looking for a general introduction to using {proglang}. +Throughout, we use the term canister to refer to an {IC} canister smart contract. + == Basic language syntax This section describes the basic language conventions you need to know for programming in {proglang}. diff --git a/doc/modules/language-guide/pages/modules-and-imports.adoc b/doc/modules/language-guide/pages/modules-and-imports.adoc index 8a4fea3086a..393b4399241 100644 --- a/doc/modules/language-guide/pages/modules-and-imports.adoc +++ b/doc/modules/language-guide/pages/modules-and-imports.adoc @@ -91,19 +91,21 @@ The call to `+Counters.Counter(1)+` installs a fresh counter on the network. Ins The type annotation `+: Counters.Counter+` is redundant here. It's included only to illustrate that the type of the actor class is available when required. -== Importing from another canister +== Importing from another canister smart contract -In addition to the examples above that import {proglang} modules, you can also import functions from canisters by using the `+canister:+` prefix in place of the `+mo:+` prefix. +In addition to the examples above that import {proglang} modules, you can also import actors (and their shared functions) from canister smart constracts by using the `+canister:+` prefix in place of the `+mo:+` prefix. + +NOTE: Unlike a {proglang} library, an imported canister can be implemented in any other {IC} language that emits Candid interfaces for its canister smart contracts (for instance Rust). It could even be an older or newer version of {proglang}. For example, you might have a project that produces the following three canisters: -* BigMap -* Connectd -* LinkedUp +* BigMap (implemented in Rust) +* Connectd (implemented in {proglang}) +* LinkedUp (implemented in {proglang}) These three canisters are declared in the project's `+dfx.json+` configuration file and compiled by running `+dfx build+`. -You can then use the following lines to import the functions from the `+BigMap+` and `+Connectd+` canisters in the LinkedUp program: +You can then use the following lines to import the `+BigMap+` and `+Connectd+` canisters as actors in the {proglang} LinkedUp actor: [source.no-repl,motoko] ---- @@ -114,15 +116,13 @@ import Connectd "canister:connectd"; When importing canisters, it is important to note that the type for the imported canister corresponds to a **{proglang} actor** instead of a **{proglang} module**. This distinction can affect how some data structures are typed. -For the imported canister actor, types are derived from the Candid file—the _project-name_.did file—for the canister rather than from {proglang} itself. +For the imported canister actor, types are derived from the Candid file — the _project-name_.did file — for the canister rather than from {proglang} itself. The translation from {proglang} actor type to Candid service type is mostly, but not entirely, one-to-one, and there are some distinct {proglang} types that map to the same Candid type. For example, the {proglang} `Nat32` and `Char` types both exported as Candid type `nat32`, but `nat32` is canonically imported as {proglang} `Nat32`, not `Char`. The type of an imported canister function, therefore, might differ from the type of the original {proglang} code that implements it. For example, if the {proglang} function had type `+shared Nat32 -> async Char+` in the implementation, its exported Candid type would be `+(nat32) -> (nat32)+` but the {proglang} type imported from this Candid type will actually be the correct—but perhaps unexpected—type `+shared Nat32 -> async Nat32+`. -These type differences are the result of the Candid-to-{proglang} composition layer inherent to the canister abstraction. - == Naming imported modules Although the most common convention is to identify imported modules by the module name as illustrated in the examples above, there's no requirement for you to do so. diff --git a/doc/modules/language-guide/pages/motoko-introduction.adoc b/doc/modules/language-guide/pages/motoko-introduction.adoc index 87eb991e63b..37d2115b2b1 100644 --- a/doc/modules/language-guide/pages/motoko-introduction.adoc +++ b/doc/modules/language-guide/pages/motoko-introduction.adoc @@ -4,7 +4,8 @@ :sdk-short-name: DFINITY Canister SDK :sdk-long-name: DFINITY Canister Software Development Kit (SDK) -{proglang} is a modern programming language you can use specifically for link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC} platform] programs or as a general-purpose language for programs running on other platforms. +{proglang} is a modern, general-purpose programming language you can use specifically to author link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[Internet Computer] canister smart contracts. +Although aimed squarely at the {IC}, its design is general enough to support future compilation to other targets. == Approachability @@ -12,27 +13,27 @@ == Asynchronous messaging and type sound execution -{proglang} permits modern programming idioms, including special programming abstractions for distributed applications. -Each application consists of an _actor_ that communicates with other actors _without_ using shared state, but instead by using (asynchronous) message passing. +{proglang} permits modern programming idioms, including special programming abstractions for distributed applications (dapps). +Each dapp consists of one or more _actor_s that communicate solely by asynchronous message passing. The state of an actor is isolated from all other actors, supporting distribution. There is no way to share state between several actors. The actor-based programming abstractions of {proglang} permit human-readable message-passing patterns, and they enforce that each network interaction obeys certain rules and avoids certain common mistakes. -Specifically, {proglang} programs are _type sound_ since {proglang} includes a practical, modern type system that checks each one before it executes. -The {proglang} type system statically checks that each {proglang} program will execute safely, without internal type errors, on all possible inputs. -Consequently, entire classes of common programming pitfalls that are common in other languages are ruled out, including null pointer errors, mis-matched argument and result types and many others. +Specifically, {proglang} programs are _type sound_ since {proglang} includes a practical, modern type system that checks each one before it executes. +The {proglang} type system statically checks that each {proglang} program will execute safely, without dynamic type errors, on all possible inputs. +Consequently, entire classes of common programming pitfalls that are common in other languages, and web programming languages in particular, are ruled out. This includes null reference errors, mis-matched argument or result types, missing field errors and many others. To execute, {proglang} statically compiles to link:about-this-guide{outfilesuffix}#wasm[WebAssembly], a portable binary format that abstracts cleanly over modern computer hardware, and thus permits its execution broadly on the Internet, and the link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC}]. [[pitch-actors]] -== Each microservice as an _actor_ +== Each canister smart constract as an _actor_ -{proglang} provides an *actor-based* programming model to developers to express _server behavior_, including that of _micro services_ on the Internet and the link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC}]. +{proglang} provides an *actor-based* programming model to developers to express _services_, including those of canister smart contracts on the link:../developers-guide/concepts/what-is-ic{outfilesuffix}#ic-overview[{IC}]. -An actor is similar to an object, but is special in that its isolated state exists _remotely_, and its interactions with the world are _asynchronous_. +An actor is similar to an object, but is special in that its state is completely isolated, and all its interactions with the world are by _asynchronous_ messaging. All communication with and between actors involves passing messages asynchronously over the network using the Internet Computer's messaging protocol. -An actor’s messages are processed in sequence, so state modifications never cause race conditions. +An actor’s messages are processed in sequence, so state modifications never admit race conditions (unless explicitly allowed by punctuating `await`s). -The underlying computing platform provided by the Internet Computer ensures that each message-based synchronization will reach a close, but in doing so each one may also fail due to the usual reasons that arise in distributed systems, including time out. +The Internet Computer ensures that each message that is sent receives a response. The response is either success with some value, or an error. An error can be the explicit rejection of the message by the receiving canister, a trap due to an illegal instruction such as division by zero, or a system error due to distribution or resource constraints. For example, a system error might be the transient or permanent unavailability of the receiver (either because the receiving actor is oversubscribed or has been deleted). [[pitch-async-actors]] === Asynchronous actors @@ -53,7 +54,7 @@ finalStep(await result1, await result2) We can summarize the program's behavior with three steps: . The program makes two requests (lines 1 and 2) to two -distinct services, each implemented internally as an actor (object). +distinct services, each implemented as a {proglang} actor or canister smart contract implemented in some other language. . The program waits for each result to be ready (line 3) using the keyword `await` on each result value. @@ -82,9 +83,9 @@ However, it requires special compiler and type-system support, as we discuss in In an _asynchronous_ computing setting, a program and its running environment are permitted to perform _internal computations_ that occur _concurrently_ with one another. Specifically, asynchronous programs are ones where the program's requests of its environment do not (necessarily) require the program to wait for the environment. -In the meantime, the program is permitted to make internal progress within this environment while it waits, perhaps _actively_. In the example, above, the program makes the second request while still waiting for the first micro service. +In the meantime, the program is permitted to make internal progress within this environment while the environment proceeds to complete the request. In the example, above, the program issues the second request before waiting for the first request to complete. -Symmetrically, the environment's requests of the program do not (necessarily) require the environment to wait for the program's answer to make external progress around it while it waits for this answer. +Symmetrically, the environment's requests of the program do not (necessarily) require the environment to wait for the program's answer: the environment can make external progress while the answer is produced. We do not show an example of this "`notify`" pattern above, since it uses callbacks (and _higher-order_ functions and control flow) and is thus more complex. @@ -100,12 +101,12 @@ You'll learn more about futures when we introduce actors in link:actors-async{ou Here, we merely use the ones that arise from calling `service1.computeAnswer(params)` and `service2.computeAnswer(params)`. -The syntax `await` synchronizes on a future, and potentially suspends computation until the future is completed by its producer. -We show two uses of the `await` form in the example above, which await the results of the two services. +The syntax `await` synchronizes on a future, and suspends computation until the future is completed by its producer. +We see two uses of `await` in the example above, +to obtain the results from two calls to services. -When the developer uses these keywords, the compiler transforms the program as necessary, often doing complex transformations to the program's control- and data-flow that would be tedious to perform by hand. -Meanwhile, the type system of {proglang} enforces certain correct usage patterns for these constructs, including that types flowing between consumers and producers always agree, and that the types of data sent among services are permitted to flow there, and do -not (for example) contain link:mutable-state{outfilesuffix}[private mutable state]. +When the developer uses these keywords, the compiler transforms the program as necessary, often doing complex transformations to the program's control- and data-flow that would be tedious to perform by hand in a purely synchronous language. +Meanwhile, the type system of {proglang} enforces certain correct usage patterns for these constructs, including that types flowing between consumers and producers always agree, and that the types of data sent among services are permitted to flow there, and do not (for example) contain link:mutable-state{outfilesuffix}[private mutable state]. [[pitch-types]] === Types are static @@ -114,13 +115,14 @@ Like other modern programming languages, {proglang} permits each variable to car Other link:basic-concepts{outfilesuffix}#intro-values[types of values] exist too, including records, tuples, and "`tagged data`" called _variants_. {proglang} enjoys the formal property of type safety, also known as _type soundness_. -We often summarize this idea with the phrase: link:basic-concepts{outfilesuffix}#intro-type-soundness[Well-typed {proglang} programs don't go wrong], meaning that they do not misuse program constructs by treating them as if they have the wrong type. +We often summarize this idea with the phrase: link:basic-concepts{outfilesuffix}#intro-type-soundness[Well-typed {proglang} programs don't go wrong], meaning that the only operations that will be performed on data are those permitted by its +static type. For example, each variable in a {proglang} program carries an associated _type_, and this type is known _statically_, before the program executes. -Each use of each variable is checked by the compiler to prevent runtime type errors, including null pointer errors. +Each use of each variable is checked by the compiler to prevent runtime type errors, including null reference errors, invalid field access and the like. -In this sense, {proglang} types provide a form of _trusted **compiler-verified** documentation_ in the program source code. +In this sense, {proglang} types provide a form of _trustworthy, **compiler-verified** documentation_ in the program source code. As usual, dynamic testing can check properties that are beyond the reach of the {proglang} type system. -While modern, the {proglang} type system is intentionally _not_ "`advanced`" or exotic in any new ways. -Rather, the type system of {proglang} integrates lessons from modern, but well-understood, link:about-this-guide{outfilesuffix}#modern-types[practical type systems] of today to provide an approachable, yet _mathematically precise_ language for general-purpose, distributed programming. +While modern, the {proglang} type system is intentionally _not_ "`advanced`" or particularly exotic. +Rather, the type system of {proglang} integrates standard concepts from modern, but well-understood, link:about-this-guide{outfilesuffix}#modern-types[practical type systems] to provide an approachable, expressive yet safe language for programming general-purpose, distributed applications. diff --git a/doc/modules/language-guide/pages/motoko.adoc b/doc/modules/language-guide/pages/motoko.adoc index bb20adaaab1..8047f0e58e6 100644 --- a/doc/modules/language-guide/pages/motoko.adoc +++ b/doc/modules/language-guide/pages/motoko.adoc @@ -1,7 +1,7 @@ = Motoko Programming Language :page-layout: default :proglang: Motoko -:platform: Internet Computer platform +:platform: Internet Computer blockchain network :IC: Internet Computer :ext: .mo :company-id: DFINITY @@ -11,15 +11,15 @@ ifdef::env-github,env-browser[:outfilesuffix:.adoc] [IMPORTANT] ===================================================================== -The Motoko programming language continues to evolve with each release of the {sdk-short-name} and with ongoing updates to the Motoko compiler. +The {proglang} programming language continues to evolve with each release of the {sdk-short-name} and with ongoing updates to the {proglang} compiler. Check back regularly to try new features and see what's changed. ===================================================================== The {proglang} programming language is a new, modern, type-sound language designed for developers who want to build the next generation of apps and services to run directly on the internet. -Motoko is specifically designed to support the unique features of the Internet Computer and to provide a familiar yet robust programming environment. +{proglang} is specifically designed to support the unique features of the Internet Computer and to provide a familiar yet robust programming environment. As a new language {proglang} is constantly evolving with support for new features and improvements. -The Motoko compiler, documentation and other tooling is https://github.com/dfinity/motoko[open source] and released under the Apache 2.0 license. Contributions are welcome. +The {proglang} compiler, documentation and other tooling is https://github.com/dfinity/motoko[open source] and released under the Apache 2.0 license. Contributions are welcome. [.cards.cards-4.personas.conceal-title] {empty} @@ -27,7 +27,7 @@ The Motoko compiler, documentation and other tooling is https://github.com/dfini [.card] == Native canister support -Motoko has native support for Internet Computer software canisters, which are expressed as actors, autonomous objects that encapsulate their state and communicate through asynchronous messages. +{proglang} has native support for Internet Computer software canisters, which are expressed as actors, autonomous objects that encapsulate their state and communicate through asynchronous messages. [source#counter,motoko] ---- @@ -37,7 +37,7 @@ include::../examples/Counter.mo[] [.card] == Code sequentially in direct style -On the Internet Computer, software canisters call into other canisters asynchronously, but Motoko enables you to program your systems sequentially in direct style. Asynchronous messages are function calls that return a future, and the await constructs allows chaining calls as if they were synchronous. +On the Internet Computer, software canisters call into other canisters asynchronously, but {proglang} enables you to program your systems sequentially in direct style. Asynchronous messages are function calls that return a future, and the await constructs allows chaining calls as if they were synchronous. [source.include_counter,motoko] ---- @@ -48,7 +48,7 @@ include::../examples/factorial.mo[lines=9..-1] [.card] == Modern type system -Motoko has been designed to be intuitive to those familiar with JavaScript and other popular languages, but offers modern features such as sound structural types, generics, variant types, and checked pattern matching. +{proglang} has been designed to be intuitive to those familiar with JavaScript and other popular languages, but offers modern features such as sound structural types, generics, variant types, and checked pattern matching. [source,motoko] ---- @@ -58,7 +58,7 @@ include::../examples/tree.mo[] [.card] == Autogenerated IDL files -The SDK exports your interface definition in a language neutral format called `+Candid+`, so other canisters, browser resident code and smart phone apps that have permission can call into your functions. The Motoko compiler can also read and write interface definition files, allowing Motoko to seamlessly interact with canisters programmed in other languages. +The SDK exports your interface definition in a language neutral format called `+Candid+`, so other canisters, browser resident code and smart phone apps that have permission can call into your functions. The {proglang} compiler can also read and write interface definition files, allowing {proglang} to seamlessly interact with canisters programmed in other languages. For example, the previous `+Counter+` actor produces the following `+Candid+` IDL: @@ -85,9 +85,9 @@ include::../examples/Registry.mo[] [.card] == Upgrades -Motoko provides numerous features to help you leverage orthogonal persistence, including language features that allow your heap to self-migrate when you upgrade the software of a canister. +{proglang} provides numerous features to help you leverage orthogonal persistence, including language features that allow your heap to self-migrate when you upgrade the software of a canister. -For example, Motoko lets you declare certain variables as `stable`. The values of `stable` variables are automatically preserved across software upgrades. +For example, {proglang} lets you declare certain variables as `stable`. The values of `stable` variables are automatically preserved across software upgrades. Consider a stable counter: diff --git a/doc/modules/language-guide/pages/mutable-state.adoc b/doc/modules/language-guide/pages/mutable-state.adoc index 3601555f0d5..beaa58c9757 100644 --- a/doc/modules/language-guide/pages/mutable-state.adoc +++ b/doc/modules/language-guide/pages/mutable-state.adoc @@ -2,7 +2,7 @@ :proglang: Motoko :company-id: DFINITY -Each actor in {proglang} may use, but may _never directly share_, an +Each actor in {proglang} may use, but may _never directly share_, internal mutable state. Later, we discuss link:sharing{outfilesuffix}[sharing among actors], where diff --git a/doc/modules/language-guide/pages/overview.adoc b/doc/modules/language-guide/pages/overview.adoc index 77a0255e40a..1a78477398d 100644 --- a/doc/modules/language-guide/pages/overview.adoc +++ b/doc/modules/language-guide/pages/overview.adoc @@ -1,4 +1,8 @@ -== Overview +== Concise overview of Motoko + +This is terse, slide-like introduction to Motoko and its features. + +(For a gentler introduction, visit the other sections on this site.) === Motivation and Goals @@ -6,7 +10,7 @@ A simple, useful language for the Internet Computer (IC) * Familiar syntax * Safe by default -* Incorporating *actor* model for canisters +* Incorporating *actor* model for canister smart contracts * Seamless integration of IC features * Making most of present and future WebAssembly @@ -551,7 +555,7 @@ _sharable_ arguments and _no_ or _async_ result type. * `send` is an IC _update_ method * `recv` is IC _query_ method -IC canister ≈ Motoko actor +IC canister with Candid interface ≈ Motoko actor === sharable ≈ serializable @@ -594,7 +598,7 @@ actor Broadcast { } .... -a typical canister main file +a typical actor/canister main file === Async/await @@ -1004,4 +1008,5 @@ alice received goodbye from charlie bob received goodbye from charlie .... -//// Old slides +//// + diff --git a/doc/modules/language-guide/pages/pattern-matching.adoc b/doc/modules/language-guide/pages/pattern-matching.adoc index 352777f50a0..7219de6c386 100644 --- a/doc/modules/language-guide/pages/pattern-matching.adoc +++ b/doc/modules/language-guide/pages/pattern-matching.adoc @@ -109,25 +109,19 @@ The last kind of pattern is the `or` pattern. As its name suggests, these are tw Since pattern matching has a rich history and interesting mechanics, a few additional comments are justified. -- nomenclature -The (usually structured) expression that is being matched is frequently called the _scrutinee_ and the patterns appearing behind the keyword `case` are the _alternatives_. When every possible scrutinee is matched by (at least one) alternative, then we say that the scrutinee is _covered_. The patterns are tried in top-down fashion and thus in case of _overlapping_ patterns the one higher-up is selected. An alternative is considered _dead_ (or _inactive_), if for every value that it matches there is higher-up alternative that is also matched. +terminology:: The (usually structured) expression that is being matched is frequently called the _scrutinee_ and the patterns appearing behind the keyword `case` are the _alternatives_. When every possible scrutinee is matched by (at least one) alternative, then we say that the scrutinee is _covered_. The patterns are tried in top-down fashion and thus in case of _overlapping_ patterns the one higher-up is selected. An alternative is considered _dead_ (or _inactive_), if for every value that it matches there is higher-up alternative that is also matched. -- booleans -The data type `Bool` can be regarded as two disjointed altenatives (`true` and `false`) and {proglang}'s built-in `if` construct will _eliminate_ the data and turn it into _control_ flow. `if` expressions are a form of pattern matching that abbreviates the general `switch` expression for the special case of boolean scrutinees. +booleans:: The data type `Bool` can be regarded as two disjointed altenatives (`true` and `false`) and {proglang}'s built-in `if` construct will _eliminate_ the data and turn it into _control_ flow. `if` expressions are a form of pattern matching that abbreviates the general `switch` expression for the special case of boolean scrutinees. -- variant patterns +variant patterns:: {proglang}'s variant types are a form of _disjoint union_ (sometimes also called a _sum type_). A value of variant type always has exactly one _discriminator_ and a payload which can vary from discriminator to discriminator. When matching a variant pattern with a variant value, the discriminators must be the same (in order to select the alternative) and if so, the payload gets exposed for further matching. -- enumerated types -Other programming languages—for example C, but not {proglang}—often use a keyword `enum` to introduce enumerations. These are impoverished relatives of Motoko's variant types, as the alternatives are not allowed to carry any payload. Correspondingly, in those languages the `switch`-like statements lack the full power of pattern matching. {proglang} provides the short-hand syntax (as in `type Weekday = { #mon; #tue; ... }`) to define enumerations, where payload for the variants is not desired. +enumerated types:: Other programming languages — for example C, but not {proglang} — often use a keyword `enum` to introduce enumerations. These are impoverished relatives of {proglang}'s variant types, as the alternatives are not allowed to carry any payload. Correspondingly, in those languages the `switch`-like statements lack the full power of pattern matching. {proglang} provides the short-hand syntax (as in `type Weekday = { #mon; #tue; ... }`) to define basic enumerations, for which no payloads are required. -- error handling -Error handling can be considered a use-case for pattern matching. When a function returns a value that has an alternative for success and one for failure (for example, an option value or a variant), pattern matching can be used to distinguish between the two as discussed in xref:errors{outfilesuffix}[Error handling]. +error handling:: Error handling can be considered a use-case for pattern matching. When a function returns a value that has an alternative for success and one for failure (for example, an option value or a variant), pattern matching can be used to distinguish between the two as discussed in xref:errors{outfilesuffix}[Error handling]. -- non-failable matching -Some types admit only a single value and we call these _singleton types_. Examples of these are the unit type (also known as an empty tuple) or tuples that only contain singleton types. Variants with a single tag and no payload (or singleton-typed payload) are singleton types too. Pattern matching on singleton types is particularly straightforward, as it only has one possible outcome: a successful match. +irrefutable matching:: Some types contain just a single value. We call these _singleton types_. Examples of these are the unit type (also known as an empty tuple) or tuples of singleton types. Variants with a single tag and no (or singleton-typed) payload are singleton types too. Pattern matching on singleton types is particularly straightforward, as it only has one possible outcome: a successful match. -- exhaustiveness (coverage) checking -When a pattern check alternative has the potential to fail, then it becomes important to find out whether the whole `switch` expression can fail. If this can happen the execution of the program can trap for certain inputs, posing an operational threat. To this end, the compiler checks for the exhaustiveness of pattern matching by keeping track of the covered shape of the scrutinee. The compiler issues a warning for any non-covered scrutinees ({proglang} even constructs a helpful example of a scrutinee that is not matched). As by-product of the exhaustiveness check, warnings are also issued for dead alternatives. +exhaustiveness (coverage) checking:: When a pattern check alternative has the potential to fail, then it becomes important to find out whether the whole `switch` expression can fail. If this can happen the execution of the program can trap for certain inputs, posing an operational threat. To this end, the compiler checks for the exhaustiveness of pattern matching by keeping track of the covered shape of the scrutinee. The compiler issues a warning for any non-covered scrutinees ({proglang} even constructs a helpful example of a scrutinee that is not matched). A useful by-product of the exhaustiveness check is that it identifies and warns about dead alternatives that can never be matched. In summary, pattern checking is a great tool with several use-cases. By statically analyzing patterns, the compiler assists the programmer by pointing out unhandled cases and unreachable code, both of which often indicate programmer error. The static, compile-time nature of coverage checking reliably rules out runtime failures. diff --git a/doc/modules/language-guide/pages/sharing.adoc b/doc/modules/language-guide/pages/sharing.adoc index b5da1bcfc24..a85cdcba528 100644 --- a/doc/modules/language-guide/pages/sharing.adoc +++ b/doc/modules/language-guide/pages/sharing.adoc @@ -119,7 +119,7 @@ To permit this flexibility, an actor needs to share a single _function_ that per The ability to share a function requires that it be pre-designated as `shared`, and the type system enforces that these functions follow certain rules around the types of data that these functions accept, return, and over which their closures close. -Motoko lets you omit this keyword for _public_ actor methods since, implicitly, _any public function of an actor must be `shared`_, whether marked explicitly +{proglang} lets you omit this keyword for _public_ actor methods since, implicitly, _any public function of an actor must be `shared`_, whether marked explicitly or not. // More generally, a `shared` function is one that is _either_ part of the public interface of an actor, _or_ it is not mentioned in the public interface, but it does not close over the actor's mutable state, and it adheres to the same argument and return-type typing restrictions as a public actor function (that is, no mutable data in the arguments or results). diff --git a/doc/modules/language-guide/pages/structural-equality.adoc b/doc/modules/language-guide/pages/structural-equality.adoc index 610666d80fb..432d56e9197 100644 --- a/doc/modules/language-guide/pages/structural-equality.adoc +++ b/doc/modules/language-guide/pages/structural-equality.adoc @@ -1,6 +1,6 @@ = Structural equality -Equality (`+==+`) —and by extension inequality (`+!=+`)— is *structural*: two values `+a+` and `+b+` are equal, `+a == b+`, whenever they have equal contents, regardless of the physical representation, or identity, of those values in memory. +Equality (`+==+`) — and by extension inequality (`+!=+`) — is *structural*: two values `+a+` and `+b+` are equal, `+a == b+`, whenever they have equal contents, regardless of the physical representation, or identity, of those values in memory. For example, the strings `+"hello world"+` and `+"hello " # "world"+` are equal, even though they are most likely represented by different objects in memory. diff --git a/doc/modules/language-guide/pages/style.adoc b/doc/modules/language-guide/pages/style.adoc index 1854944a7e5..f5e2a2d0dd1 100644 --- a/doc/modules/language-guide/pages/style.adoc +++ b/doc/modules/language-guide/pages/style.adoc @@ -1,7 +1,8 @@ = Motoko style guidelines +:proglang: Motoko :!page-repl: -To increase readability and uniformity of Motoko source code, the style guide provides suggestions for formatting Motoko sources and other basic conventions. +To increase readability and uniformity of {proglang} source code, the style guide provides suggestions for formatting {proglang} sources and other basic conventions. == Layout @@ -378,7 +379,7 @@ func f() { === Semicolons -* Motoko uniformly requires a semicolon to separate expressions or local declarations in a block, regardless of whether the preceding declaration ends in a closing '}'. +* {proglang} uniformly requires a semicolon to separate expressions or local declarations in a block, regardless of whether the preceding declaration ends in a closing '}'. + Rationale: This is unlike other C-style languages, which tend to have rather ad-hoc rules. @@ -462,7 +463,7 @@ switch (opt) { [[parens]] === Parentheses -* Motoko supports "parenless" style, meaning that parentheses are optional in most places, such as function parameter lists, or statement operands, when they enclose an expression that either is bracketed already (for example, a tuple, object, or array) or is a simple constant or identifier. +* {proglang} supports "parenless" style, meaning that parentheses are optional in most places, such as function parameter lists, or statement operands, when they enclose an expression that either is bracketed already (for example, a tuple, object, or array) or is a simple constant or identifier. + [source, motoko] .... @@ -637,7 +638,7 @@ class Cart(length_ : Nat) { } .... + -Rationale: In Motoko, functions are first-class values, so functions and other value identifiers share the same name space. +Rationale: In {proglang}, functions are first-class values, so functions and other value identifiers share the same name space. + Identifiers with a leading `_` should _not_ be used for private state, since that indicates an unused name (see <