# HOWTOs

# 1. 怎么进行调试

默认情况下,合法的合约调用会在代理节点执行,但是普通节点也可以手动打开合约虚拟机,验证区块链中的合约调用,执行合约调用。

简单的调试方法可以通过pprint输出变量,对于基本类型pprint会输出多个参数值的字符串表示, 对于函数会输出函数的内存地址,对于table类型的值会转成json字符串输出。

之后会增加远程调试功能,在远程代理节点上运行过程中断点调试。

# 2. 怎么使用event

合约中可以通过emit EventName(EventArg)抛出,EventName是自定义event名称, event名称词法要求和变量名要求一样,但是最长支持49字符, EventArg是event的参数,要求值是最长1024字符的字符串

调用合约的操作执行过程中抛出的event,会被记录到区块链中,区块链本地节点可以设置监控某合约的某event的回调脚本, 然后在区块链同步到包含被监控的event的块时,会触发设置的脚本,回调脚本也是用hvm编写。

# 3. 常见编译错误有哪些

  • 变量类型和赋值不一致
  • 函数调用的参数类型和实际传参类型不一致
  • 函数参数类型和函数体中使用时的类型不一致,建议函数参数加上显式类型声明
  • 一些代码块漏加 end
  • 使用了没有申明过的变量
  • 合约中定义了全局变量(合约中不允许定义新的全局变量只能读取,但是脚本中可以定义新全局变量)
  • 对nil值进行一些不允许的操作符操作,比如加减乘除等
  • 访问非table类型值的属性
  • 其他

# 4. 对中文或者其他非英文的支持如何

变量名,函数名,event名称不可以使用中文,只能英文字符或者下划线开头,跟着若干个英文字符或者下划线或者数字, 但是字符串中的内容可以用中文或者其他语言的文字,支持unicode。

# 5. 支持多线程吗

因为hvm主要是为了在区块链的节点上运行的,考虑到区块链上的一些特性尤其是为了达成共识,不支持多线程编程

# 6. hvm中怎么使用随机数

提供两种获取随机数的方式

  • 全局函数get_chain_random() 获取链上伪随机数,这个函数返回的随机数是可被推算出来的结果,仅用于只需要返回均匀分布的数字的地方

  • 全局函数get_waited(blocknumber) 可以获取根据指定块的块号上的二进制内容产生的一个int32数字,参数可以是过去块也可以是未来块的块号要用这种方式获取随机数,可以调用全局函数get_header_block_num()获取到前一个块的块号, 然后用未来的某个块号(当前块号加上未来块的数量,大概10秒一个块)作为参数调用get_waited函数,如果执行的时候当前区块链还没有到这个块号,返回-1, 如果执行的时候当前区块链已经超过了这个块号,返回根据那个块上数据产生的一个int32数字。以彩票为例,设置get_waited的参数为预计开奖时间的未来块号, 然后在开奖前一段时间前允许投注,这时候get_waited参数的块号还没有到这个块,返回类型是-1,所有人都不知道到开奖时间后这个函数调用的返回结果会是多少。 当开始时间到了后,调用get_waited的返回结果固定下来一个int32类型的正数,并且以后任何节点每次用同一个参数调用结果都是固定的,随机数被确定下来了。 可以根据需要用这个返回值 (result % 10000) / 10000 来得到[0, 1)之间的精度4位小数的随机数

# 7. 怎么实现面向对象的类型继承和多态

可以使用table类型和record类型模拟对象,record类型有默认属性值,并且属性可以是有默认实现的函数

比如

type Person = {
    id: string default "123",
    name: string default "hvm",
    age ?: int = 24,  -- record属性默认值既可以用default也可以用=区分
    fn: (number, number) => number default 
            function (a: number, b: number) 
                return a + b
            end
}

let p = Person()
pprint(p.id, p.name, p.age)
let n = p.fn(p.age, 2016)
pprint(n)

如果需要实现类似面向对象语言中的类型继承和多态功能的话,可以实现一个extend函数,调用子record类型的构造函数后,用extend函数 接受子对象和父类型,在extend函数中创建新的父类型的对象,然后给把父类型对象中子类型对象没有的属性赋值给子类型对象。用这种 方法可以起到继承和多态的效果。目前没有给出标准的extend函数,给出一个示例实现

let function extend(obj, parent_class)
    let parent = parent_class(obj)
    for k, v in pairs(parent) do
        obj[k] = v
    return totable(obj)
end

type A = {
    name1: string,
    age1: int default 100
}

type B = {
    name: string,
    age: int
}

let b = B()
extend(b, A)

-- or

let c = extend(B(), A)

还有一种实现类型继承和多态的方法是使用setmetatable和元表,这以后会给出更多文档和例子