• 基于虚拟机源码分析move合约(六):整数、布尔值的引用


    合约:

    1. public fun test_reference(){
    2. let a = 1;
    3. let ref_a = &a;
    4. let b = *ref_a +1;
    5. let mut_ref_b = &mut b;
    6. *mut_ref_b = 3;
    7. let owner = false;
    8. let _ref_owner = &owner;
    9. let _mut_ref_owner = &mut owner;
    10. }

    这个合约演示了针对整数、布尔值的引用操作。引用分为两种:

    & : 表示不可修改的引用

    &mut :表示可以进行修改的引用

    * :解引用

    我们通过下面的命令执行反编译:

    move disassemble --name test_move

    我们可以得到如下指令:

    1. // Move bytecode v5
    2. module f2.test_move {
    3. public test_reference() {
    4. L0: _mut_ref_owner: &mut bool
    5. L1: _mut_ref_v: &mut vector<u8>
    6. L2: _ref_owner: &bool
    7. L3: _ref_v: &vector<u8>
    8. L4: a: u64
    9. L5: b: u64
    10. L6: mut_ref_b: &mut u64
    11. L7: owner: bool
    12. L8: ref_a: &u64
    13. L9: v: vector<u8>
    14. B0:
    15. 0: LdU64(1)
    16. 1: StLoc[4](a: u64)
    17. 2: ImmBorrowLoc[4](a: u64)
    18. 3: StLoc[8](ref_a: &u64)
    19. 4: MoveLoc[8](ref_a: &u64)
    20. 5: ReadRef
    21. 6: LdU64(1)
    22. 7: Add
    23. 8: StLoc[5](b: u64)
    24. 9: MutBorrowLoc[5](b: u64)
    25. 10: StLoc[6](mut_ref_b: &mut u64)
    26. 11: LdU64(3)
    27. 12: MoveLoc[6](mut_ref_b: &mut u64)
    28. 13: WriteRef
    29. 14: LdFalse
    30. 15: StLoc[7](owner: bool)
    31. 16: ImmBorrowLoc[7](owner: bool)
    32. 17: Pop
    33. 18: MutBorrowLoc[7](owner: bool)
    34. 19: Pop
    35. 20: Ret
    36. }
    37. }

    LdU64(1):加载整数1到栈上

    StLoc[4](a: u64):将整数1从栈上取出,存入寄存器4

    ImmBorrowLoc[4](a: u64)

    这个指令用来对整数进行引用,具体代码如下:

    1. Bytecode::MutBorrowLoc(idx) | Bytecode::ImmBorrowLoc(idx) => {
    2. let instr = match instruction {
    3. Bytecode::MutBorrowLoc(_) => S::MutBorrowLoc,
    4. _ => S::ImmBorrowLoc,
    5. };
    6. gas_meter.charge_simple_instr(instr)?;
    7. interpreter.operand_stack
    8. .push(self.locals.borrow_loc(*idx as usize)?)?;
    9. }

    可以看到两种引用都会走locals.borrow_loc:

    1. impl Locals {
    2. pub fn borrow_loc(&self, idx: usize) -> PartialVMResult {
    3. // TODO: this is very similar to SharedContainer::borrow_elem. Find a way to
    4. // reuse that code?
    5. let v = self.0.borrow();
    6. if idx >= v.len() {
    7. return Err(
    8. PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR).with_message(
    9. format!(
    10. "index out of bounds when borrowing local: got: {}, len: {}",
    11. idx,
    12. v.len()
    13. ),
    14. ),
    15. );
    16. }
    17. match &v[idx] {
    18. ValueImpl::Container(c) => Ok(Value(ValueImpl::ContainerRef(ContainerRef::Local(
    19. c.copy_by_ref(),
    20. )))),
    21. ValueImpl::U8(_)
    22. | ValueImpl::U64(_)
    23. | ValueImpl::U128(_)
    24. | ValueImpl::Bool(_)
    25. | ValueImpl::Address(_) => Ok(Value(ValueImpl::IndexedRef(IndexedRef {
    26. container_ref: ContainerRef::Local(Container::Locals(Rc::clone(&self.0))),
    27. idx,
    28. }))),
    29. ValueImpl::ContainerRef(_) | ValueImpl::Invalid | ValueImpl::IndexedRef(_) => Err(
    30. PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
    31. .with_message(format!("cannot borrow local {:?}", &v[idx])),
    32. ),
    33. }
    34. }
    35. }

    针对U64类型,走的是下面的分支:

    1. ValueImpl::U8(_)
    2. | ValueImpl::U64(_)
    3. | ValueImpl::U128(_)
    4. | ValueImpl::Bool(_)
    5. | ValueImpl::Address(_) => Ok(Value(ValueImpl::IndexedRef(IndexedRef {
    6. container_ref: ContainerRef::Local(Container::Locals(Rc::clone(&self.0))),
    7. idx,
    8. }))),

    可以看到最内层是&self.0,这是rust对整数类型的引用,然后使用Rc::clone返回一个Rc引用,然后生成一个Container::Locals:

    1. enum Container {
    2. Locals(RcVec>>),
    3. Vec(RcVec>>),
    4. Struct(RcVec>>),
    5. VecU8(RcVec<u8>>>),
    6. VecU64(RcVec<u64>>>),
    7. VecU128(RcVec<u128>>>),
    8. VecBool(RcVec<bool>>>),
    9. VecAddress(RcVec>>),
    10. }

    可以看到这是Container中的一种,其实就是基本类型Container,然后生成一个ContainerRef::Local:

    1. enum ContainerRef {
    2. Local(Container),
    3. Global {
    4. status: Rc>,
    5. container: Container,
    6. },
    7. }

    这是对Container生成的引用,分为Local和Global,其中Local是本地变量,Gloabl是链上的变量。

    最后生成IndexedRef:

    1. enum ValueImpl {
    2. Invalid,
    3. U8(u8),
    4. U64(u64),
    5. U128(u128),
    6. Bool(bool),
    7. Address(AccountAddress),
    8. Container(Container),
    9. ContainerRef(ContainerRef),
    10. IndexedRef(IndexedRef),
    11. }
    12. struct IndexedRef {
    13. idx: usize,
    14. container_ref: ContainerRef,
    15. }

    这个IndexedRef才是Move中的引用对应的数据结构。

    最终,这个IndexedRef会被压入栈上。

    StLoc[8](ref_a: &u64):从栈上把上面生成的IndexedRef取出来,放入寄存器8

    MoveLoc[8](ref_a: &u64):从寄存器8取出IndexedRef,也就是ref_a对应的数据结构,然后压入栈上

    ReadRef

    这个指令时用来读取引用所指向的值的,具体代码如下:

    1. Bytecode::ReadRef => {
    2. let reference = interpreter.operand_stack.pop_as::()?;
    3. gas_meter.charge_read_ref(reference.value_view())?;
    4. let value = reference.read_ref()?;
    5. interpreter.operand_stack.push(value)?;
    6. }

    首先从栈上弹出一个引用,这里的Reference其实就是IndexedRef:

    1. pub struct Reference(ReferenceImpl);
    2. enum ReferenceImpl {
    3. IndexedRef(IndexedRef),
    4. ContainerRef(ContainerRef),
    5. }

    然后调用read_ref读取实际的值:

    1. impl Reference {
    2. pub fn read_ref(self) -> PartialVMResult {
    3. self.0.read_ref()
    4. }
    5. }
    6. impl ReferenceImpl {
    7. fn read_ref(self) -> PartialVMResult {
    8. match self {
    9. Self::ContainerRef(r) => r.read_ref(),
    10. Self::IndexedRef(r) => r.read_ref(),
    11. }
    12. }
    13. }
    14. impl IndexedRef {
    15. fn read_ref(self) -> PartialVMResult {
    16. use Container::*;
    17. let res = match self.container_ref.container() {
    18. Locals(r) | Vec(r) | Struct(r) => r.borrow()[self.idx].copy_value()?,
    19. VecU8(r) => ValueImpl::U8(r.borrow()[self.idx]),
    20. VecU64(r) => ValueImpl::U64(r.borrow()[self.idx]),
    21. VecU128(r) => ValueImpl::U128(r.borrow()[self.idx]),
    22. VecBool(r) => ValueImpl::Bool(r.borrow()[self.idx]),
    23. VecAddress(r) => ValueImpl::Address(r.borrow()[self.idx]),
    24. };
    25. Ok(Value(res))
    26. }
    27. }

    我们知道,我们的变量是Container::Locals类型的,因此走的下面的分支:

    Locals(r) | Vec(r) | Struct(r) => r.borrow()[self.idx].copy_value()?,

    这里的r其实是Rc>>,因此最终走的是ValueImpl的copy_value:

    1. impl ValueImpl {
    2. fn copy_value(&self) -> PartialVMResult<Self> {
    3. use ValueImpl::*;
    4. Ok(match self {
    5. Invalid => Invalid,
    6. U8(x) => U8(*x),
    7. U64(x) => U64(*x),
    8. U128(x) => U128(*x),
    9. Bool(x) => Bool(*x),
    10. Address(x) => Address(*x),
    11. ContainerRef(r) => ContainerRef(r.copy_value()),
    12. IndexedRef(r) => IndexedRef(r.copy_value()),
    13. // When cloning a container, we need to make sure we make a deep
    14. // copy of the data instead of a shallow copy of the Rc.
    15. Container(c) => Container(c.copy_value()?),
    16. })
    17. }
    18. }

    我们的是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

    这个指令时用来向引用所指向的数据进行写入操作,具体代码如下:

    1. Bytecode::WriteRef => {
    2. let reference = interpreter.operand_stack.pop_as::()?;
    3. let value = interpreter.operand_stack.pop()?;
    4. gas_meter.charge_write_ref(&value)?;
    5. reference.write_ref(value)?;
    6. }

    首先从栈上取出引用,然后从栈上取出要写入的数据,最后调用write_ref写入数据:

    1. impl Reference {
    2. pub fn write_ref(self, x: Value) -> PartialVMResult<()> {
    3. self.0.write_ref(x)
    4. }
    5. }
    6. impl ReferenceImpl {
    7. fn write_ref(self, x: Value) -> PartialVMResult<()> {
    8. match self {
    9. Self::ContainerRef(r) => r.write_ref(x),
    10. Self::IndexedRef(r) => r.write_ref(x),
    11. }
    12. }
    13. }
    14. impl IndexedRef {
    15. fn write_ref(self, x: Value) -> PartialVMResult<()> {
    16. match &x.0 {
    17. ValueImpl::IndexedRef(_)
    18. | ValueImpl::ContainerRef(_)
    19. | ValueImpl::Invalid
    20. | ValueImpl::Container(_) => {
    21. return Err(
    22. PartialVMError::new(StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR)
    23. .with_message(format!(
    24. "cannot write value {:?} to indexed ref {:?}",
    25. x, self
    26. )),
    27. )
    28. }
    29. _ => (),
    30. }
    31. match (self.container_ref.container(), &x.0) {
    32. (Container::Locals(r), _) | (Container::Vec(r), _) | (Container::Struct(r), _) => {
    33. let mut v = r.borrow_mut();
    34. v[self.idx] = x.0;
    35. }
    36. (Container::VecU8(r), ValueImpl::U8(x)) => r.borrow_mut()[self.idx] = *x,
    37. (Container::VecU64(r), ValueImpl::U64(x)) => r.borrow_mut()[self.idx] = *x,
    38. (Container::VecU128(r), ValueImpl::U128(x)) => r.borrow_mut()[self.idx] = *x,
    39. (Container::VecBool(r), ValueImpl::Bool(x)) => r.borrow_mut()[self.idx] = *x,
    40. (Container::VecAddress(r), ValueImpl::Address(x)) => r.borrow_mut()[self.idx] = *x,
    41. (Container::VecU8(_), _)
    42. | (Container::VecU64(_), _)
    43. | (Container::VecU128(_), _)
    44. | (Container::VecBool(_), _)
    45. | (Container::VecAddress(_), _) => {
    46. return Err(
    47. PartialVMError::new(StatusCode::INTERNAL_TYPE_ERROR).with_message(format!(
    48. "cannot write value {:?} to indexed ref {:?}",
    49. x, self
    50. )),
    51. )
    52. }
    53. }
    54. self.container_ref.mark_dirty();
    55. Ok(())
    56. }
    57. }

    实际走的是下面的分支:

    1. (Container::Locals(r), _) | (Container::Vec(r), _) | (Container::Struct(r), _) => {
    2. let mut v = r.borrow_mut();
    3. v[self.idx] = x.0;
    4. }

    这里的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:函数结束,直接返回

  • 相关阅读:
    如何生成SSH服务器的ed25519公钥SHA256指纹
    Windows2019部署IIS后,浏览文件变成下载的问题解决过程
    数字通信测量仪器5201数据网络测试仪
    Android模拟器报extra space is needed in addition to APK size的错误
    leetcode做题笔记165. 比较版本号
    基于HTML语言的环保网站的设计与实现 前后端分离 Java毕业设计·文档报告·代码讲解·安装调试
    Socks5代理IP在跨境电商与网络游戏中的网络安全应用
    【Hadoop】hive安装
    互联网轻量级框架整合之MyBatis动态SQL
    vue项目 使用axios封装request请求(一)
  • 原文地址:https://blog.csdn.net/biakia0610/article/details/127425659