forked from move-language/move
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib.rs
2084 lines (1876 loc) · 80.7 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0
//! A Move native runtime prototype.
//!
//! This crate is part of the Solana project to port Move to LLVM and SBF.
//!
//! It contains three types of definitions
//!
//! - Types with layouts known to the compiler for the purposes of calling
//! runtime functions. These have simple C-ABI layouts.
//! - "Native" library functions required by the Move standard library, calls to
//! which are generated by the LLVM backend.
//! - Runtime functions specific to the LLVM port as necessary to dynamically
//! implement Move semantics.
//!
//! As of now this crate is concerned only with implementing Move "natives"
//! required by the Move standard library. The basic strategy is to define
//! simple data types for primitives like scalars and vectors, and runtime types
//! like type descriptors; and naming and calling conventions for translating
//! Move native calls to C-ABI calls. The native implementations convert the
//! Move types to existing Rust types like `Vec` to do their work.
//!
//! It contains several modules dividing the crate by concerns:
//!
//! - `rt_types` defines the types shared between LLVM and the runtime.
//! - `rt` defines other runtime calls emitted by the compiler
//! - `std` defines the native functions called declared by Move `std`.
//! - `conv` defines conversions between Move types and Rust types.
//! - `target_defs` defines Solana-specific definitions that differ from Move VM
//! or might reasonably differ for non-Solana targets.
//!
//!
//! # Native functions background
//!
//! Native functions in Move are functions that the standard library relies on
//! the runtime to provide. They are declared with the `native` keyword and no
//! implementation. They are similar to `extern` functions in Rust.
//!
//! Here are some examples:
//!
//! ```move
//! native public fun sha2_256(data: vector<u8>): vector<u8>;
//!
//! /// Add element `e` to the end of the vector `v`.
//! native public fun push_back<Element>(v: &mut vector<Element>, e: Element);
//!
//! /// Pop an element from the end of vector `v`.
//! /// Aborts if `v` is empty.
//! native public fun pop_back<Element>(v: &mut vector<Element>): Element;
//!
//! /// Return the binary representation of `v` in BCS (Binary Canonical Serialization) format
//! native public fun to_bytes<MoveValue>(v: &MoveValue): vector<u8>;
//! ```
//!
//! In the Move VM these are implemented in Rust and registered with the VM at
//! runtime. These implementations are tightly tied to VM internals, and include
//! features like gas accounting that are irrelevant for the LLVM port. They are
//! not directly reusable by the LLVM port at this time.
//!
//!
//! # Generics and type interpretation
//!
//! The translation between Move native declarations and the C ABI is relatively
//! straightforward, with conventions described further in this document.
//!
//! The big complication is that many natives are generic, so there are choices
//! to be made about how to interpret type descriptors.
//!
//! This implementation interprets types at runtime by passing a [`MoveType`]
//! reference for every generic type. The `MoveType` definition closely mirrors
//! the Move compiler's `runtime_type::Type` definition.
//!
//! It appears that the native calls mostly need to know about size and
//! alignment of types to implement things like generic vector operations. There
//! are though functions for serialization and debug printing that would appear
//! to require fully interpreting arbitrary Move types. It is not yet clear
//! what challenges this will entail.
//!
//! Embedding full type interpreters into the runtime means that every Move
//! program will contain code for interpreting every possible type, including
//! those it does not need. There may be ways to mitigate this bloat with clever
//! structure and LTO, but it is unclear.
//!
//! The alternative to runtime type interpretation is to have the compiler emit
//! monomorphizations of native calls. This would appear to be a challenging
//! problem, one that might be easiest solved by having the compiler emit Rust
//! code to instantiate generic natives. This approach could be left to a future
//! optimization.
//!
//!
//! # Reusing move crates and `no-std`
//!
//! For portability it could be desirable to not depend on `std`.
//!
//! It may be necessary to reuse existing move crates, which will force this
//! crate to depend on Rust `std`. For Solana this probably won't present a
//! problem as Solana has a mostly-complete port of `std`.
//!
//! Move serialization at least depends ot the [`bcs`] crate, which has relatively
//! few dependencies, but does depend on `std`. It could conceivably be adopted
//! to a `no-std` environment.
//!
//! This crate itself doesn't directly use `std`.
//!
//!
//! # Platform compatibility
//!
//! Some definitions are different on Solana than on Move VM.
//!
//! - An address is 32 bytes to match Solana pubkeys. On Move VM an address is
//! 16 bytes by default but can be configured as 20 or 32 bytes at compile
//! time.
//!
//!
//! # Naming conventions
//!
//! Within the Move VM natives are identified by a module name plus a function
//! name, like "vector" + "push_back". They do not appear to additionally be
//! identified by a library name like "std".
//!
//! Our native symbol names are a concatenation of the prefix "move_native_",
//! plus the module name and function name. So the vector `push_back` function
//! symbol is
//!
//! > `move_native_vector_push_back`
//!
//! In Rust code, native functions are defined under the `std` module, in
//! submodules that mirror the module heirarchy of the Move standard library,
//! and their final symbol names are given through the `export_name` attribute.
//!
//! Runtime functions needed by the compiler are prefixed "move_rt_",
//! like `move_rt_abort`.
//!
//!
//! # Data types
//!
//! Data types known to both the compiler and the runtime are defined in [`rt_types`].
//! They are all declared with explicit `repr` attributes to have a defined layout that
//! is easy to codegen.
//!
//! The most important types for the compiler and the vector type,
//! [`MoveUntypedVector`] here, and the type descriptor, here [`MoveType`].
//!
//! `MoveType` is similar to `move_vm_types::runtime_types::Type`, but with a
//! structure that does not rely on Rust-specific type layout. `MoveType`
//! contains recursive `&'static` references to other `MoveType`s, necessitating
//! that binaries contain a static table of all needed type descriptors.
//!
//! Generic values are represented by references or pointers to [`AnyType`],
//! which is simply a transparent wrapper around a u8, pointers to which must
//! be unsafely casted based on an accompanying `MoveType`.
//!
//! Move references as interpreted by `TypeDesc` are assumed to always be
//! pointers - no fat pointers.
//!
//! The types defined in `rt_types` will change. The exact definitions will
//! depend on decisions made for code generation.
//!
//!
//! ## Vectors and strings
//!
//! Move vectors are operated on by casting to Rust vectors and reusing the Rust
//! methods. Move vectors have the same layout as Rust vecs, on the theory this
//! may save a few instructions during conversions.
//!
//! Move strings are a library type and not known to the Move compiler†.
//! They are a wrapper around byte vectors, and native string calls are
//! operations on `MoveByteVector`.
//!
//! Vectors must be allocated with the [`move_native_vec_empty`] and deallocated
//! with the [`move_native_vec_destroy_empty`] runtime calls.
//!
//! † - note that although `String` is a library type, the ASCII `String` (move
//! has UTF-8 and ASCII strings) is exposed to the runtime via the `TypeName`
//! library type, which is passed by value in the [`move_native_type_name_get`]
//! native function, the code generator and the runtime need to agree on its
//! layout.
//!
//!
//! ## Structs
//!
//! Structs are difficult for the runtime to handle. Since they have arbitrary
//! layout the runtime has to interpret them dynamically.
//!
//! The struct type descriptor specifies the struct's size, including
//! post-padding as needed for side-by-side layout in arrays, and its alignment;
//! and it specifies the types and offsets of every field.
//!
//! Requirements:
//!
//! - Size must be > 0
//! - Alignment must be > 0
//! - Alignment must be a power of 2
//! - Size rounded up to alignment must not overflow a pointer-sized signed integer
//!
//! Since the vector-of-struct code is necessarily very low level, not reusing
//! the actual Rust Vec methods, we may end up just doing the same for vecs of
//! all types. Super unsafe but more efficient than using typed Rust vectors.
//!
//!
//! # Calling conventions
//!
//! The calling conventions are straightforword, following the C ABI, with some
//! extra patterns necessary for things like Move type parameters and unsized
//! generic values.
//!
//! - type parameters
//! - passed as `&MoveType`
//! - by-ref generic types
//! - passed by `&AnyType`
//! - by-value generic types
//! - passed by `*mut AnyType`
//! - examples: `write_to_event_store`
//! - by-value generic return types
//! - stack-allocated return pointer of `*mut AnyType`
//! - examples: `pop_back`
//!
//!
//! # Drop-bombs
//!
//! `rt_types` that contain allocations also define `Drop` to panic. This
//! ensures that they are always destroyed by calling the correct runtime
//! function, which is helpful in test cases. It does complicate runtime code
//! that deals with these types though, because the runtime has to call
//! `mem::forget` on them at the right moments. These cases call the wrapper
//! function `disarm_drop_bomb` to make clear what is happening.
//!
//!
//! # Panic handling
//!
//! This crate does not handle panics at the FFI boundary. When used in a
//! runtime context it should be compiled such that panics trigger an abort.
//!
//! At this time it does not define its own panic handler, but probably will in
//! the future.
//!
//!
//! # References
//!
//! Useful Move code to read.
//!
//! - `move-stdlib::natives`
//! - `move-vm-types::loaded_data::Type`
//! - `move-vm-types::values::Value`
//! - `move-core-types::value`
//!
//!
//! # todo
//!
//! - type constraints
//! - `write_to_event_store<T: drop + store>`
//! - may not matter at runtime
//! - generic by-value and return arguments might instead be `&mut MaybeUninit<SomeUnsizedPlaceholder>`
//! - llvm would need to write a fat pointer - do they have defined layout?
//! - should serialization maintain compatibility with move vm?
//! - add context to unwrapped errors
//! - vec_empty/destroy_empty can be implemented unsafely for all cases, is in the struct case
//! - _all_ vec ops for all cases can probably be implemented using an
//! adaptation of the vector-of-struct case, at which point we probably don't
//! even need to use Rust vecs at all, at the expense of some readability.
//! - struct serialize / debug don't include struct / field names
//! - add a full test suite
//! - make sure tests can be run under miri
//! - todo add drop-bombs to other rt_types with allocations
//! - remove transmutes to make the semantics clearer to miri?
#![allow(unused)]
#![no_std]
// NB Solana's Rust seems to allow use of unstable features.
// This wouldn't normally be allowed.
#![cfg_attr(feature = "solana", feature(default_alloc_error_handler))]
extern crate alloc;
mod serialization;
/// Types literally shared with the compiler through crate linkage.
pub mod shared {
pub use crate::rt_types::TypeDesc;
pub use crate::rt_types::MOVE_UNTYPED_VEC_DESC_SIZE;
pub use crate::rt_types::MOVE_TYPE_DESC_SIZE;
}
/// Types known to the compiler.
pub(crate) mod rt_types {
use crate::target_defs;
/// A Move vector with an untyped buffer.
///
/// Used in the API for generic vector arguments.
///
/// The only way to interact with these is to convert them from / to Rust
/// vectors or references to Rust vectors, with functions in the [`conv`]
/// module.
///
/// The only way to create and destroy them is with the
/// [`move_native_vec_empty`] and [`move_native_vec_destroy_empty`] native
/// calls.
#[repr(C)]
#[derive(Debug)]
pub struct MoveUntypedVector {
pub ptr: *mut u8, // Safety: must be correctly aligned per type
pub capacity: u64, // in typed elements, not u8
pub length: u64, // in typed elements, not u8
}
pub const MOVE_UNTYPED_VEC_DESC_SIZE: u64 = core::mem::size_of::<MoveUntypedVector>() as u64;
/// A Move vector of bytes.
///
/// These occur in the API enough to warrant their own type, and there are
/// dedicated functions to convert them to Rust vectors.
#[repr(C)]
pub struct MoveByteVector {
pub ptr: *mut u8,
pub capacity: u64,
pub length: u64,
}
/// A Move vector of signers.
///
/// This type occurs in the native API, but it will probably be removed, in
/// favor of just using `MoveUntypedVector`.
#[repr(C)]
pub struct MoveSignerVector {
pub ptr: *mut MoveSigner,
pub capacity: u64,
pub length: u64,
}
/// A reification of the Move runtime type description.
///
/// This is structured as a `TypeDesc` indicating which type a thing is,
/// and an undiscriminated union holding additional information about the
/// type.
///
/// cc runtime_types::Type
///
/// # Safety
///
/// The pointer must be to static memory and never mutated.
#[repr(C)]
#[derive(Copy, Clone)]
pub struct MoveType {
pub name: StaticTypeName,
pub type_desc: TypeDesc,
pub type_info: *const TypeInfo,
}
pub const MOVE_TYPE_DESC_SIZE: u64 = core::mem::size_of::<MoveType>() as u64;
// Needed to make the MoveType, which contains raw pointers,
// Sync, so that it can be stored in statics for test cases.
unsafe impl Sync for MoveType { }
impl core::fmt::Debug for MoveType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// fixme: implement this better
unsafe {
write!(f, "{}", self.name.as_ascii_str());
}
Ok(())
}
}
/// # Safety
///
/// The pointer must be to static memory and never mutated.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct StaticTypeName {
pub ptr: *const u8,
pub len: u64,
}
impl StaticTypeName {
pub unsafe fn as_ascii_str<'a>(&'a self) -> &'a str {
core::str::from_utf8_unchecked(core::slice::from_raw_parts(
self.ptr,
usize::try_from(self.len).expect("overflow"),
))
}
}
pub type StaticName = StaticTypeName;
static DUMMY_TYPE_NAME_SLICE: &[u8] = b"dummy";
pub static DUMMY_TYPE_NAME: StaticTypeName = StaticTypeName {
ptr: DUMMY_TYPE_NAME_SLICE as *const [u8] as *const u8,
len: 5,
};
unsafe impl Sync for StaticTypeName {}
#[repr(u64)]
#[derive(Copy, Clone, Debug)]
pub enum TypeDesc {
Bool = 1,
U8 = 2,
U16 = 3,
U32 = 4,
U64 = 5,
U128 = 6,
U256 = 7,
Address = 8,
Signer = 9,
Vector = 10,
Struct = 11,
Reference = 12,
//MutableReference = 13,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union TypeInfo {
pub nothing: u8, // if no type info is needed
pub vector: VectorTypeInfo,
pub struct_: StructTypeInfo,
pub struct_instantiation: u8, // todo
pub reference: ReferenceTypeInfo,
pub mutable_reference: ReferenceTypeInfo,
pub ty_param: u8, // todo
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct VectorTypeInfo {
pub element_type: &'static MoveType,
}
/// # Safety
///
/// This type is `Sync` so that it can be declared statically. The value
/// pointed to by `field_array_ptr` should not be mutated, or `Sync` will be
/// violated.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct StructTypeInfo {
/// Pointer to an array of field infos.
///
/// This would ideally be a Rust static slice, but the layout is
/// seemingly undefined.
pub field_array_ptr: *const StructFieldInfo,
pub field_array_len: u64,
/// Size of the struct within an array.
pub size: u64,
/// Alignment of the struct.
pub alignment: u64,
}
unsafe impl Sync for StructTypeInfo {}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct StructFieldInfo {
pub type_: MoveType,
/// Offset in bytes within the struct.
pub offset: u64,
pub name: StaticName,
}
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct ReferenceTypeInfo {
pub element_type: &'static MoveType,
}
#[repr(transparent)]
pub struct AnyValue(u8);
#[repr(transparent)]
#[derive(Debug, PartialEq)]
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct MoveSigner(pub MoveAddress);
/// A Move address.
///
/// This is mapped to the address size of the target platform, and may
/// differ from Move VM.
///
/// Bytes are in little-endian order.
#[repr(transparent)]
#[derive(PartialEq)]
#[derive(borsh::BorshSerialize, borsh::BorshDeserialize)]
pub struct MoveAddress(pub [u8; target_defs::ACCOUNT_ADDRESS_LENGTH]);
impl core::fmt::Debug for MoveAddress {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("@")?;
for byte in self.0.iter().rev() {
f.write_fmt(core::format_args!("{byte:02X?}"))?;
}
Ok(())
}
}
// Defined in std::type_name; not a primitive.
//
// todo how is drop glue handled?
#[repr(C)]
pub struct TypeName {
pub name: MoveAsciiString,
}
// Defined in std::ascii; not a primitive.
//
// todo how is drop glue handled?
#[repr(C)]
pub struct MoveAsciiString {
pub bytes: MoveByteVector,
}
// todo this would be more correct with a lifetime attached
#[repr(transparent)]
#[derive(Debug)]
pub struct MoveUntypedReference(pub *const AnyValue);
mod drop_bomb {
// fixme this is pretty awkward - this is intended to be a no-std crate
#[cfg(test)]
extern crate std;
#[cfg(not(test))]
pub fn run(name: &str) {
panic!("forgot to destroy {}", name);
}
#[cfg(test)]
pub fn run(name: &str) {
if !std::thread::panicking() {
panic!("forgot to destroy {}", name);
} else {
std::eprintln!("forgot to destroy {}", name);
}
}
}
// Drop-bomb. Catch errors in test cases.
impl Drop for MoveUntypedVector {
fn drop(&mut self) {
drop_bomb::run("MoveUntypedVector");
}
}
// Drop-bomb. Catch errors in test cases.
impl Drop for MoveByteVector {
fn drop(&mut self) {
drop_bomb::run("MoveByteVector");
}
}
// Drop-bomb. Catch errors in test cases.
impl Drop for MoveSignerVector {
fn drop(&mut self) {
drop_bomb::run("MoveSignerVector");
}
}
/// Don't run destructors.
///
/// Some of these runtime types hold allocations, and need to be destroyed
/// in specific ways. If they are not, then they panic. These types must be
/// destroyed by calling `mem::forget`, for which this function is a
/// synonym.
pub fn disarm_drop_bomb<T>(v: T) {
core::mem::forget(v)
}
}
/// Runtime calls emitted by the compiler.
/// Reference: move/language/documentation/book/src/abort-and-assert.md
mod rt {
use crate::rt_types::{AnyValue, MoveType, MoveUntypedVector, MoveAddress};
#[export_name = "move_rt_abort"]
fn abort(code: u64) -> ! {
crate::target_defs::abort(code);
}
#[export_name = "move_rt_vec_destroy"]
unsafe fn vec_destroy(type_ve: &MoveType, v: MoveUntypedVector) {
assert_eq!(0, v.length, "can't destroy vectors with elements yet");
crate::std::vector::destroy_empty(type_ve, v);
}
#[export_name = "move_rt_vec_empty"]
unsafe fn vec_empty(type_ve: &MoveType) -> MoveUntypedVector {
crate::std::vector::empty(type_ve)
}
#[export_name = "move_rt_vec_copy"]
unsafe fn vec_copy(type_ve: &MoveType, dstv: &mut MoveUntypedVector, srcv: &MoveUntypedVector) {
use crate::std::vector as V;
let src_len = V::length(type_ve, srcv);
let dst_len = V::length(type_ve, dstv);
// Drain the destination first.
for i in 0..dst_len {
crate::rt::pop_back_discard(type_ve, dstv);
}
// Now copy.
for i in 0..src_len {
let se = V::borrow(type_ve, srcv, i);
let septr = se as *const AnyValue as *mut AnyValue;
V::push_back(type_ve, dstv, septr);
}
}
unsafe fn pop_back_discard(
type_ve: &MoveType,
v: &mut MoveUntypedVector,
) {
use crate::conv::{TypedMoveBorrowedRustVecMut, borrow_typed_move_vec_as_rust_vec_mut};
let mut rust_vec = borrow_typed_move_vec_as_rust_vec_mut(type_ve, v);
let msg = "popping from empty vec";
match rust_vec {
TypedMoveBorrowedRustVecMut::Bool(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U8(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U16(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U32(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U64(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U128(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::U256(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::Address(mut v) => { v.pop().expect(msg);}
TypedMoveBorrowedRustVecMut::Signer(mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::Vector(_t, mut v) => { v.pop().expect(msg); }
TypedMoveBorrowedRustVecMut::Struct(mut v) => { todo!(); }
TypedMoveBorrowedRustVecMut::Reference(_t, mut v) => { v.pop().expect(msg); }
};
}
#[export_name = "move_rt_vec_cmp_eq"]
unsafe fn vec_cmp_eq(type_ve: &MoveType, v1: &MoveUntypedVector, v2: &MoveUntypedVector) -> bool {
use crate::conv::borrow_move_vec_as_rust_vec;
use ethnum::U256;
use crate::rt_types::TypeDesc;
use crate::std::vector as V;
use core::ops::Deref;
let v1_len = V::length(type_ve, v1);
let v2_len = V::length(type_ve, v2);
if v1_len != v2_len {
return false;
}
let is_eq = match type_ve.type_desc {
TypeDesc::Bool => {
let mut rv1 = borrow_move_vec_as_rust_vec::<bool>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<bool>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U8 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<u8>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<u8>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U16 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<u16>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<u16>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U32 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<u32>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<u32>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U64 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<u64>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<u64>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U128 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<u128>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<u128>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::U256 => {
let mut rv1 = borrow_move_vec_as_rust_vec::<U256>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<U256>(v2);
rv1.deref().eq(rv2.deref())
}
TypeDesc::Address => {
let mut rv1 = borrow_move_vec_as_rust_vec::<MoveAddress>(v1);
let mut rv2 = borrow_move_vec_as_rust_vec::<MoveAddress>(v2);
rv1.deref().eq(rv2.deref())
}
_ => todo!()
};
is_eq
}
}
/// Implementations of native calls for `std`.
mod std {
mod bcs {
use crate::conv::*;
use crate::rt_types::*;
/// Serialize any value.
///
/// This does not necessarily produce the same serializations that
/// the Move VM does, as the Rust types are not the same, and
/// the serialization format may not actually be bcs.
///
/// # References
///
/// - `move-vm-types::values::Value`
/// - `move-core-types::value`
#[export_name = "move_native_bcs_to_bytes"]
unsafe extern "C" fn to_bytes(type_v: &MoveType, v: &AnyValue) -> MoveByteVector {
crate::serialization::serialize(type_v, v)
}
/// Deserialize any value.
///
/// This is not actually in move std, but is used for testing.
#[export_name = "move_native_bcs_test_from_bytes"]
unsafe extern "C" fn test_from_bytes(type_v: &MoveType, bytes: &MoveByteVector, v: *mut AnyValue) {
crate::serialization::deserialize(type_v, bytes, v)
}
}
// nursery
mod debug {
use crate::conv::*;
use crate::rt_types::*;
use crate::target_defs;
use alloc::format;
use alloc::string::String;
use core::fmt::Write;
#[export_name = "move_native_debug_print"]
unsafe extern "C" fn print(type_x: &MoveType, x: &AnyValue) {
let v = borrow_move_value_as_rust_value(type_x, x);
target_defs::print_string(&format!("{:?}", v));
}
#[export_name = "move_native_print_stack_trace"]
extern "C" fn print_stack_trace() {
target_defs::print_stack_trace();
}
}
// nursery
mod event {
use crate::rt_types::*;
#[export_name = "move_native_event_write_to_event_store"]
unsafe extern "C" fn write_to_event_store(
type_msg: &MoveType,
guid: MoveByteVector,
count: u64,
msg: *mut AnyValue,
) {
todo!()
}
}
mod hash {
use crate::conv::{move_byte_vec_to_rust_vec, rust_vec_to_move_byte_vec};
use crate::rt_types::*;
use sha2::{Digest, Sha256};
use sha3::Sha3_256;
#[export_name = "move_native_hash_sha2_256"]
unsafe extern "C" fn sha2_256(ptr: MoveByteVector) -> MoveByteVector {
let rust_vec = move_byte_vec_to_rust_vec(ptr);
let hash_vec = Sha256::digest(rust_vec.as_slice()).to_vec();
let move_vec = rust_vec_to_move_byte_vec(hash_vec);
move_vec
}
#[export_name = "move_native_hash_sha3_256"]
unsafe extern "C" fn sha3_256(ptr: MoveByteVector) -> MoveByteVector {
let rust_vec = move_byte_vec_to_rust_vec(ptr);
let hash_vec = Sha3_256::digest(rust_vec.as_slice()).to_vec();
let move_vec = rust_vec_to_move_byte_vec(hash_vec);
move_vec
}
}
mod signer {
use crate::rt_types::*;
#[export_name = "move_native_signer_borrow_address"]
extern "C" fn borrow_address(s: &MoveSigner) -> &MoveAddress {
&s.0
}
}
pub(crate) mod string {
use crate::conv::*;
use crate::rt_types::*;
use alloc::vec::Vec;
use core::str;
#[export_name = "move_native_string_internal_check_utf8"]
pub unsafe extern "C" fn internal_check_utf8(v: &MoveByteVector) -> bool {
let rust_vec = borrow_move_byte_vec_as_rust_vec(v);
let res = str::from_utf8(&rust_vec);
match res {
Ok(_) => true,
Err(_) => false,
}
}
#[export_name = "move_native_string_internal_is_char_boundary"]
pub unsafe extern "C" fn internal_is_char_boundary(v: &MoveByteVector, i: u64) -> bool {
let rust_vec = borrow_move_byte_vec_as_rust_vec(v);
let i = usize::try_from(i).expect("usize");
let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8");
rust_str.is_char_boundary(i)
}
#[export_name = "move_native_string_internal_sub_string"]
pub unsafe extern "C" fn internal_sub_string(
v: &MoveByteVector,
i: u64,
j: u64,
) -> MoveByteVector {
let rust_vec = borrow_move_byte_vec_as_rust_vec(v);
let i = usize::try_from(i).expect("usize");
let j = usize::try_from(j).expect("usize");
let rust_str = str::from_utf8(&rust_vec).expect("invalid utf8");
let sub_rust_vec = rust_str[i..j].as_bytes().to_vec();
rust_vec_to_move_byte_vec(sub_rust_vec)
}
#[export_name = "move_native_string_internal_index_of"]
pub unsafe extern "C" fn internal_index_of(s: &MoveByteVector, r: &MoveByteVector) -> u64 {
let s_rust_vec = borrow_move_byte_vec_as_rust_vec(s);
let s_rust_str = str::from_utf8(&s_rust_vec).expect("invalid utf8");
let r_rust_vec = borrow_move_byte_vec_as_rust_vec(r);
let r_rust_str = str::from_utf8(&r_rust_vec).expect("invalid utf8");
let res = s_rust_str.find(r_rust_str);
u64::try_from(match res {
Some(i) => i,
None => s_rust_str.len(),
})
.expect("u64")
}
}
mod type_name {
use crate::conv::*;
use crate::rt_types::*;
#[export_name = "move_native_type_name_get"]
unsafe extern "C" fn get(type_: &MoveType) -> TypeName {
let name_slice = type_.name.as_ascii_str();
let byte_type = MoveType {
name: DUMMY_TYPE_NAME,
type_desc: TypeDesc::U8,
type_info: &TypeInfo { nothing: 0 },
};
let mut byte_vector = super::vector::empty(&byte_type);
{
let mut rust_byte_vector = borrow_move_vec_as_rust_vec_mut::<u8>(&mut byte_vector);
rust_byte_vector.reserve_exact(name_slice.len());
for byte in name_slice.bytes() {
rust_byte_vector.push(byte);
}
}
TypeName {
name: MoveAsciiString {
// safety: MoveUntypedVector and MoveByteVector have the same representation
bytes: core::mem::transmute::<MoveUntypedVector, MoveByteVector>(byte_vector),
},
}
}
}
mod unit_test {
use crate::rt_types::*;
#[export_name = "move_native_unit_test_create_signers_for_testing"]
extern "C" fn create_signers_for_testing(num_signers: u64) -> MoveSignerVector {
todo!()
}
}
pub(crate) mod vector {
use crate::conv::*;
use crate::rt_types::*;
use alloc::vec::Vec;
use core::{mem, ptr};
use ethnum::U256;
// Safety: Even empty Rust vectors have non-null buffer pointers,
// which must be correctly aligned. This function crates empty Rust vecs
// of the correct type and converts them to untyped move vecs.
#[export_name = "move_native_vector_empty"]
pub extern "C" fn empty(type_r: &MoveType) -> MoveUntypedVector {
let move_vec = match type_r.type_desc {
TypeDesc::Bool => rust_vec_to_move_vec::<bool>(Vec::new()),
TypeDesc::U8 => rust_vec_to_move_vec::<u8>(Vec::new()),
TypeDesc::U16 => rust_vec_to_move_vec::<u16>(Vec::new()),
TypeDesc::U32 => rust_vec_to_move_vec::<u32>(Vec::new()),
TypeDesc::U64 => rust_vec_to_move_vec::<u64>(Vec::new()),
TypeDesc::U128 => rust_vec_to_move_vec::<u128>(Vec::new()),
TypeDesc::U256 => rust_vec_to_move_vec::<U256>(Vec::new()),
TypeDesc::Address => rust_vec_to_move_vec::<MoveAddress>(Vec::new()),
TypeDesc::Signer => rust_vec_to_move_vec::<MoveSigner>(Vec::new()),
TypeDesc::Vector => {
// Safety: need correct alignment for the internal vector
// pointer of the outer vector, which is non-null even for
// an unallocated vector. `MoveUntypedVector` has the same
// size and alignment regardless of the type it contains, so
// no need to interpret the vector type.
rust_vec_to_move_vec::<MoveUntypedVector>(Vec::new())
}
TypeDesc::Struct => unsafe {
// Safety: this gets pretty sketchy, and relies on internal
// Vec details that probably are not guaranteed. The most
// _correct_ way to initialize a Vec is to call its
// constructor.
//
// That is pretty tough with a type of any dynamically sized
// layout, so we're going to munge the pointers ourselves.
//
// The critical thing to know about Vec's pointers is:
//
// - They must always be aligned correctly
// - They are _never_ 0, even for empty Vec's, to allow null
// pointer optimizations.
//
// Vec uses `NonNull::dangling` to create invalid non-null
// pointers, but that requires a concrete type of the
// correct alignment. We dig even deeper and use
// `ptr::invalid_mut`, which is an unstable function from
// the pointer provenance project. As it is unstable we just
// duplicate it in our `conv` module until it becomes
// stable.
//
// This should be the only location in this crate where we
// need to fabricate a pointer from an integer.
let size = (*type_r.type_info).struct_.size;
let size = usize::try_from(size).expect("overflow");
let alignment = (*type_r.type_info).struct_.alignment;
let alignment = usize::try_from(alignment).expect("overflow");
assert!(size != 0); // can't handle ZSTs
assert!(alignment != 0); // must have alignment
assert!(alignment.is_power_of_two());
let ptr = invalid_mut::<u8>(alignment);
MoveUntypedVector {
ptr,
capacity: 0,
length: 0,
}
},
TypeDesc::Reference => rust_vec_to_move_vec::<MoveUntypedReference>(Vec::new()),
};
move_vec
}
#[export_name = "move_native_vector_length"]
pub unsafe extern "C" fn length(type_ve: &MoveType, v: &MoveUntypedVector) -> u64 {
// It is not strictly necessary to convert the vec for this op.
// Doing it for consistency.
let rust_vec = borrow_typed_move_vec_as_rust_vec(type_ve, v);
let len = match rust_vec {
TypedMoveBorrowedRustVec::Bool(v) => v.len(),
TypedMoveBorrowedRustVec::U8(v) => v.len(),
TypedMoveBorrowedRustVec::U16(v) => v.len(),
TypedMoveBorrowedRustVec::U32(v) => v.len(),
TypedMoveBorrowedRustVec::U64(v) => v.len(),
TypedMoveBorrowedRustVec::U128(v) => v.len(),
TypedMoveBorrowedRustVec::U256(v) => v.len(),
TypedMoveBorrowedRustVec::Address(v) => v.len(),
TypedMoveBorrowedRustVec::Signer(v) => v.len(),
TypedMoveBorrowedRustVec::Vector(_t, v) => v.len(),
TypedMoveBorrowedRustVec::Struct(s) => {
usize::try_from(s.inner.length).expect("overflow")
}
TypedMoveBorrowedRustVec::Reference(_t, v) => v.len(),
};
u64::try_from(len).expect("u64")
}
#[export_name = "move_native_vector_borrow"]
pub unsafe extern "C" fn borrow<'v>(