合约:publicfuntest_reference(){leta1;letref_a&a;letb*ref_a1;letmut_ref_b&mutb;*mut_re
合约:
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
L2: _ref_owner: &bool
L3: _ref_v: &vector
L4: a: u64
L5: b: u64
L6: mut_ref_b: &mut u64
L7: owner: bool
L8: ref_a: &u64
L9: v: vector
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: ReadRef6: LdU64(1)7: Add8: 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: WriteRef14: LdFalse15: StLoc[7](owner: bool)16: ImmBorrowLoc[7](owner: bool)17: Pop18: MutBorrowLoc[7](owner: bool)19: Pop20: 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(Rc>>),Struct(Rc>>),VecU8(Rc>>),VecU64(Rc>>),VecU128(Rc>>),VecBool(Rc>>),VecAddress(Rc>>),
}
可以看到这是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>>,因此最终走的是ValueImpl的copy_value:
impl ValueImpl {fn copy_value(&self) -> PartialVMResult {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>>,通过borrow_mut获得引用针对Vec的可写引用,然后直接覆盖对应位置的值。
LdFalse:加载布尔值false到栈上
StLoc[7](owner: bool):将布尔值从栈上取出,存入寄存器7
ImmBorrowLoc[7](owner: bool):由上面的分析可以知道,最终会生成一个IndexedRef存入栈上
Pop:由于这个引用没有用到,因此生命周期结束,直接pop掉
MutBorrowLoc[7](owner: bool):由上面的分析可以知道,最终会生成一个IndexedRef存入栈上
Pop:由于这个引用没有用到,因此生命周期结束,直接pop掉
Ret:函数结束,直接返回