合约:
- public fun test_reference(){
- let a = 1;
- let ref_a = &a;
- let b = *ref_a +1;
- let mut_ref_b = &mut b;
- *mut_ref_b = 3;
- let owner = false;
- let _ref_owner = &owner;
- let _mut_ref_owner = &mut owner;
- }
这个合约演示了针对整数、布尔值的引用操作。引用分为两种:
& : 表示不可修改的引用
&mut :表示可以进行修改的引用
* :解引用
我们通过下面的命令执行反编译:
move disassemble --name test_move
我们可以得到如下指令:
- // Move bytecode v5
- module f2.test_move {
-
-
- public test_reference() {
- L0: _mut_ref_owner: &mut bool
- L1: _mut_ref_v: &mut vector<u8>
- L2: _ref_owner: &bool
- L3: _ref_v: &vector<u8>
- L4: a: u64
- L5: b: u64
- L6: mut_ref_b: &mut u64
- L7: owner: bool
- L8: ref_a: &u64
- L9: v: vector<u8>
- B0:
- 0: LdU64(1)
- 1: StLoc[4](a: u64)
- 2: ImmBorrowLoc[4](a: u64)
- 3: StLoc[8](ref_a: &u64)
- 4: MoveLoc[8](ref_a: &u64)
- 5: ReadRef
- 6: LdU64(1)
- 7: Add
- 8: StLoc[5](b: u64)
- 9: MutBorrowLoc[5](b: u64)
- 10: StLoc[6](mut_ref_b: &mut u64)
- 11: LdU64(3)
- 12: MoveLoc[6](mut_ref_b: &mut u64)
- 13: WriteRef
- 14: LdFalse
- 15: StLoc[7](owner: bool)
- 16: ImmBorrowLoc[7](owner: bool)
- 17: Pop
- 18: MutBorrowLoc[7](owner: bool)
- 19: Pop
- 20: Ret
- }
- }
LdU64(1):加载整数1到栈上
StLoc[4](a: u64):将整数1从栈上取出,存入寄存器4
ImmBorrowLoc[4](a: u64)
这个指令用来对整数进行引用,具体代码如下:
- Bytecode::MutBorrowLoc(idx) | Bytecode::ImmBorrowLoc(idx) => {
- let instr = match instruction {
- Bytecode::MutBorrowLoc(_) => S::MutBorrowLoc,
- _ => S::ImmBorrowLoc,
- };
- gas_meter.charge_simple_instr(instr)?;
- interpreter.operand_stack
- .push(self.locals.borrow_loc(*idx as usize)?)?;
- }
可以看到两种引用都会走locals.borrow_loc:
- impl Locals {
- pub fn borrow_loc(&self, idx: usize) -> PartialVMResult
{ - // TODO: this is very similar to SharedContainer::borrow_elem. Find a way to
- // reuse that code?
-
- let v = self.0.borrow();
- if idx >= v.len() {
- return Err(
- PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(
- format!(
- "index out of bounds when borrowing local: got: {}, len: {}",
- idx,
- v.len()
- ),
- ),
- );
- }
-
- match &v[idx] {
- ValueImpl::Container(c) => Ok(Value(ValueImpl::ContainerRef(ContainerRef::Local(
- c.copy_by_ref(),
- )))),
-
- ValueImpl::U8(_)
- | ValueImpl::U64(_)
- | ValueImpl::U128(_)
- | ValueImpl::Bool(_)
- | ValueImpl::Address(_) => Ok(Value(ValueImpl::IndexedRef(IndexedRef {
- container_ref: ContainerRef::Local(Container::Locals(Rc::clone(&self.0))),
- idx,
- }))),
-
- ValueImpl::ContainerRef(_) | ValueImpl::Invalid | ValueImpl::IndexedRef(_) => Err(
- PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
- .with_message(format!("cannot borrow local {:?}", &v[idx])),
- ),
- }
- }
- }
针对U64类型,走的是下面的分支:
- ValueImpl::U8(_)
- | ValueImpl::U64(_)
- | ValueImpl::U128(_)
- | ValueImpl::Bool(_)
- | ValueImpl::Address(_) => Ok(Value(ValueImpl::IndexedRef(IndexedRef {
- container_ref: ContainerRef::Local(Container::Locals(Rc::clone(&self.0))),
- idx,
- }))),
可以看到最内层是&self.0,这是rust对整数类型的引用,然后使用Rc::clone返回一个Rc引用,然后生成一个Container::Locals:
- enum Container {
- Locals(Rc
Vec>>), - Vec(Rc
Vec>>), - Struct(Rc
Vec>>), - VecU8(Rc
Vec<u8>>>), - VecU64(Rc
Vec<u64>>>), - VecU128(Rc
Vec<u128>>>), - VecBool(Rc
Vec<bool>>>), - VecAddress(Rc
Vec>>), - }
可以看到这是Container中的一种,其实就是基本类型Container,然后生成一个ContainerRef::Local:
- enum ContainerRef {
- Local(Container),
- Global {
- status: Rc
>, - container: Container,
- },
- }
这是对Container生成的引用,分为Local和Global,其中Local是本地变量,Gloabl是链上的变量。
最后生成IndexedRef:
- enum ValueImpl {
- Invalid,
-
- U8(u8),
- U64(u64),
- U128(u128),
- Bool(bool),
- Address(AccountAddress),
-
- Container(Container),
-
- ContainerRef(ContainerRef),
- IndexedRef(IndexedRef),
- }
-
- struct IndexedRef {
- idx: usize,
- container_ref: ContainerRef,
- }
这个IndexedRef才是Move中的引用对应的数据结构。
最终,这个IndexedRef会被压入栈上。
StLoc[8](ref_a: &u64):从栈上把上面生成的IndexedRef取出来,放入寄存器8
MoveLoc[8](ref_a: &u64):从寄存器8取出IndexedRef,也就是ref_a对应的数据结构,然后压入栈上
ReadRef
这个指令时用来读取引用所指向的值的,具体代码如下:
- Bytecode::ReadRef => {
- let reference = interpreter.operand_stack.pop_as::
()?; - gas_meter.charge_read_ref(reference.value_view())?;
- let value = reference.read_ref()?;
- interpreter.operand_stack.push(value)?;
- }
首先从栈上弹出一个引用,这里的Reference其实就是IndexedRef:
- pub struct Reference(ReferenceImpl);
-
- enum ReferenceImpl {
- IndexedRef(IndexedRef),
- ContainerRef(ContainerRef),
- }
然后调用read_ref读取实际的值:
- impl Reference {
- pub fn read_ref(self) -> PartialVMResult
{ - self.0.read_ref()
- }
- }
- impl ReferenceImpl {
- fn read_ref(self) -> PartialVMResult
{ - match self {
- Self::ContainerRef(r) => r.read_ref(),
- Self::IndexedRef(r) => r.read_ref(),
- }
- }
- }
-
- impl IndexedRef {
- fn read_ref(self) -> PartialVMResult
{ - use Container::*;
-
- let res = match self.container_ref.container() {
- Locals(r) | Vec(r) | Struct(r) => r.borrow()[self.idx].copy_value()?,
- VecU8(r) => ValueImpl::U8(r.borrow()[self.idx]),
- VecU64(r) => ValueImpl::U64(r.borrow()[self.idx]),
- VecU128(r) => ValueImpl::U128(r.borrow()[self.idx]),
- VecBool(r) => ValueImpl::Bool(r.borrow()[self.idx]),
- VecAddress(r) => ValueImpl::Address(r.borrow()[self.idx]),
- };
-
- Ok(Value(res))
- }
- }
我们知道,我们的变量是Container::Locals类型的,因此走的下面的分支:
Locals(r) | Vec(r) | Struct(r) => r.borrow()[self.idx].copy_value()?,
这里的r其实是Rc
- impl ValueImpl {
- fn copy_value(&self) -> PartialVMResult<Self> {
- use ValueImpl::*;
-
- Ok(match self {
- Invalid => Invalid,
-
- U8(x) => U8(*x),
- U64(x) => U64(*x),
- U128(x) => U128(*x),
- Bool(x) => Bool(*x),
- Address(x) => Address(*x),
-
- ContainerRef(r) => ContainerRef(r.copy_value()),
- IndexedRef(r) => IndexedRef(r.copy_value()),
-
- // When cloning a container, we need to make sure we make a deep
- // copy of the data instead of a shallow copy of the Rc.
- Container(c) => Container(c.copy_value()?),
- })
- }
- }
我们的是U64类型,因此直接使用*x进行解引用,拿到实际的值返回。
最终这个值会压入栈上。
LdU64(1):将数据1压入栈上
Add:栈上两个数据取出,进行加法运算,结果存入栈上
StLoc[5](b: u64):取出栈上的数据,存入寄存器5
MutBorrowLoc[5](b: u64):这个和上面的ImmBorrowLoc一样,最终会生成IndexedRef存入栈上
StLoc[6](mut_ref_b: &mut u64):从栈上取出引用,存入寄存器6
LdU64(3):将数据3压入栈上
MoveLoc[6](mut_ref_b: &mut u64):从寄存器6取出引用,压入栈上
WriteRef
这个指令时用来向引用所指向的数据进行写入操作,具体代码如下:
- Bytecode::WriteRef => {
- let reference = interpreter.operand_stack.pop_as::
()?; - let value = interpreter.operand_stack.pop()?;
- gas_meter.charge_write_ref(&value)?;
- reference.write_ref(value)?;
- }
首先从栈上取出引用,然后从栈上取出要写入的数据,最后调用write_ref写入数据:
- impl Reference {
- pub fn write_ref(self, x: Value) -> PartialVMResult<()> {
- self.0.write_ref(x)
- }
- }
-
- impl ReferenceImpl {
- fn write_ref(self, x: Value) -> PartialVMResult<()> {
- match self {
- Self::ContainerRef(r) => r.write_ref(x),
- Self::IndexedRef(r) => r.write_ref(x),
- }
- }
- }
-
- impl IndexedRef {
- fn write_ref(self, x: Value) -> PartialVMResult<()> {
- match &x.0 {
- ValueImpl::IndexedRef(_)
- | ValueImpl::ContainerRef(_)
- | ValueImpl::Invalid
- | ValueImpl::Container(_) => {
- return Err(
- PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
- .with_message(format!(
- "cannot write value {:?} to indexed ref {:?}",
- x, self
- )),
- )
- }
- _ => (),
- }
-
- match (self.container_ref.container(), &x.0) {
- (Container::Locals(r), _) | (Container::Vec(r), _) | (Container::Struct(r), _) => {
- let mut v = r.borrow_mut();
- v[self.idx] = x.0;
- }
- (Container::VecU8(r), ValueImpl::U8(x)) => r.borrow_mut()[self.idx] = *x,
- (Container::VecU64(r), ValueImpl::U64(x)) => r.borrow_mut()[self.idx] = *x,
- (Container::VecU128(r), ValueImpl::U128(x)) => r.borrow_mut()[self.idx] = *x,
- (Container::VecBool(r), ValueImpl::Bool(x)) => r.borrow_mut()[self.idx] = *x,
- (Container::VecAddress(r), ValueImpl::Address(x)) => r.borrow_mut()[self.idx] = *x,
-
- (Container::VecU8(_), _)
- | (Container::VecU64(_), _)
- | (Container::VecU128(_), _)
- | (Container::VecBool(_), _)
- | (Container::VecAddress(_), _) => {
- return Err(
- PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR).with_message(format!(
- "cannot write value {:?} to indexed ref {:?}",
- x, self
- )),
- )
- }
- }
- self.container_ref.mark_dirty();
- Ok(())
- }
- }
实际走的是下面的分支:
- (Container::Locals(r), _) | (Container::Vec(r), _) | (Container::Struct(r), _) => {
- let mut v = r.borrow_mut();
- v[self.idx] = x.0;
- }
这里的r其实是Rc
LdFalse:加载布尔值false到栈上
StLoc[7](owner: bool):将布尔值从栈上取出,存入寄存器7
ImmBorrowLoc[7](owner: bool):由上面的分析可以知道,最终会生成一个IndexedRef存入栈上
Pop:由于这个引用没有用到,因此生命周期结束,直接pop掉
MutBorrowLoc[7](owner: bool):由上面的分析可以知道,最终会生成一个IndexedRef存入栈上
Pop:由于这个引用没有用到,因此生命周期结束,直接pop掉
Ret:函数结束,直接返回