2021-09-24 17:43:11 +00:00
|
|
|
|
YJS语法
|
|
|
|
|
=======
|
|
|
|
|
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
概述
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
YJS源文件包括任意数量的\ **import声明**\ 和一个\ **contract定义**\ 。
|
|
|
|
|
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
import声明
|
|
|
|
|
----------
|
|
|
|
|
|
|
|
|
|
与JavaScript(ES6)类似,YJS也支持import声明语句,在全局层面,开发者可以使用如下import声明来导入其他文件。
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
import "filename";
|
|
|
|
|
|
|
|
|
|
内容
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
import声明语句将包含在“filename”文件中的所有全局符号(单元)导入到当前文件,且全局范围内有效。
|
|
|
|
|
|
|
|
|
|
路径
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
**filename**\ 通常用\ **/**\ 做目录分隔符来表示文件的路径,例如,从同一目录下导入\ **x.yjs**\ 文件到当前文件,可以使用\ **import
|
|
|
|
|
“x.yjs”**\ 语句;从其他目录下导入\ **x.yjs**\ 使用\ **import
|
|
|
|
|
“lib/x.yjs”**\ 语句。
|
|
|
|
|
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
Contract定义
|
|
|
|
|
------------
|
|
|
|
|
|
|
|
|
|
示例
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
以下是一个合约示例,用于JSON处理,此YJS源文件以合约名称命名。
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
contract ScoreAdder{
|
|
|
|
|
//arg = {"action":"main","arg":"[{\"score\":20},{\"score\":20}]"}
|
|
|
|
|
export function main(arg){
|
|
|
|
|
//JSON is a build-in object.
|
|
|
|
|
var point = JSON.parse(arg);
|
|
|
|
|
var s = 0;
|
|
|
|
|
print(point[0].score);
|
|
|
|
|
print(point.length);
|
|
|
|
|
for (var i=0;i<point.length;i++){
|
|
|
|
|
s+=point[i].score/1.0;
|
|
|
|
|
}
|
|
|
|
|
print("total score= "+s);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
注释
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
YJS源文件支持以(\ **//**\ )表示的单行注释和以(\ **/*…*/**\ )表示的多行注释,如下所示:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
// 这是一个单行注释
|
|
|
|
|
/*
|
|
|
|
|
* 这是一个
|
|
|
|
|
* 多行注释
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
注解
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
与Java类似,YJS也支持注解声明合约和函数。
|
|
|
|
|
|
|
|
|
|
在YJS中,有几种内建注解:LogType、LogLocation、Access、Permission。
|
|
|
|
|
当然,可以自定义注解。
|
|
|
|
|
内建注解的详细使用方式可以通过YJS内置API文档查看。
|
|
|
|
|
开发者可以使用如下注解来声明合约和函数。
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
@LogType("Arg","Result","Branch")
|
|
|
|
|
@SelfDefinedAnntation("dad",1)
|
|
|
|
|
@Access
|
|
|
|
|
function xxx(){}
|
|
|
|
|
|
|
|
|
|
结构
|
|
|
|
|
~~~~
|
|
|
|
|
|
|
|
|
|
YJS中的合约类似于Java中的类。每个合约都定义了一定数量的\ **变量**\ 、\ **函数**\ 和\ **事件**\ 。
|
|
|
|
|
|
|
|
|
|
变量
|
|
|
|
|
^^^^
|
|
|
|
|
|
|
|
|
|
YJS中的变量类似于JavaScript(ES6)中的变量,分为\ **全局变量**\ 和\ **局部变量**\ 。
|
|
|
|
|
|
|
|
|
|
全局变量:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
judge = true;
|
|
|
|
|
|
|
|
|
|
局部变量:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
var vs = "This is a string";
|
|
|
|
|
|
|
|
|
|
所有的变量都是\ **动态类型**\ ,因为变量的具体类型是根据变量值来决定的。如上示例中,全局变量judge是bool类型,局部变量vs是字符串类型。
|
|
|
|
|
|
|
|
|
|
函数
|
|
|
|
|
^^^^
|
|
|
|
|
|
|
|
|
|
函数是合约中的可执行单元。YJS中有两种类型的函数:普通函数和用\ **export**\ 关键字修饰的exported函数。
|
|
|
|
|
|
|
|
|
|
以下是exported函数和普通函数的区别:
|
|
|
|
|
|
|
|
|
|
1. 只有exported函数能被其他合约调用。
|
|
|
|
|
2. exported函数只能有一个参数且参数类型必须为String。
|
|
|
|
|
3. exported函数的返回类型必须是String。
|
|
|
|
|
4. 内置对象requester(请求者的公钥)只能存在于exported函数或onCreate函数,因为onCreate()虽然没被\ **export**\ 关键字修饰,但它自带requester对象且该函数可以自动执行。
|
|
|
|
|
|
|
|
|
|
事件
|
|
|
|
|
^^^^
|
|
|
|
|
|
2021-11-05 06:21:27 +00:00
|
|
|
|
YJS中包含两种事件,全局(Global)事件和本地(Local)事件:
|
|
|
|
|
|
|
|
|
|
* 全局事件类似于一般事件中间件中的事件,发布的事件由事件主题进行标识,订阅者通过订阅事件主题获取相应的事件。
|
|
|
|
|
* 本地事件类似于以太坊Solidity中的事件,由智能合约定义并发布,订阅者需要使用合约名/合约ID和事件名来订阅特定合约的相应事件。
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
|
|
|
|
**发布者**\ 定义一个包含事件发布函数的事件,然后通过调用事件发布函数发布事件。
|
2021-11-05 06:21:27 +00:00
|
|
|
|
定义的事件默认为本地事件,可使用global或local关键词来标识事件的类型。
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
|
|
|
|
**订阅者**\ 订阅并处理事件。
|
|
|
|
|
|
|
|
|
|
事件示例如下:
|
|
|
|
|
|
2021-11-05 06:21:27 +00:00
|
|
|
|
EventLocalPuber.yjs
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2021-11-05 06:21:27 +00:00
|
|
|
|
contract EventLocalPuber {
|
|
|
|
|
event abcEvent;
|
|
|
|
|
// 等价于
|
|
|
|
|
// event local abcEvent
|
|
|
|
|
export function pub(arg){
|
|
|
|
|
abcEvent(arg);
|
|
|
|
|
return "done!";
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
2021-11-05 06:21:27 +00:00
|
|
|
|
EventLocalSuber.yjs
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
2021-11-05 06:21:27 +00:00
|
|
|
|
contract EventLocalSuber {
|
|
|
|
|
function onCreate() {
|
|
|
|
|
Global.topic2event = {};
|
|
|
|
|
}
|
|
|
|
|
export function init(arg) {
|
|
|
|
|
var topic = YancloudUtil.subscribe("EventPuber", "abcEvent", handler);
|
|
|
|
|
Global.topic2event[topic] = "abcEvent";
|
|
|
|
|
print("Handler:" + handler);
|
|
|
|
|
}
|
|
|
|
|
function handler(e) {
|
|
|
|
|
var ret = "ReceiveEvent: " + (Global.topic2event[e.topic]) + " " + (e.content);
|
|
|
|
|
print(ret);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventGlobalPuber.yjs
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
contract EventGlobalPuber {
|
|
|
|
|
event global globalEvent;
|
|
|
|
|
export function pub(arg){
|
|
|
|
|
globalEvent(arg);
|
|
|
|
|
return "done!";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EventGlobalSuber.yjs
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
contract EventGlobalSuber {
|
|
|
|
|
export function init(arg) {
|
|
|
|
|
var topic = YancloudUtil.subscribe("globalEvent", handler);
|
|
|
|
|
print("Handler:" + handler);
|
|
|
|
|
}
|
|
|
|
|
function handler(e) {
|
|
|
|
|
var ret = "ReceiveEvent: " + (e.topic) + " " + (e.content);
|
|
|
|
|
print(ret);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
YJS的两种事件都支持事件语义:
|
|
|
|
|
|
|
|
|
|
* 至少一次(AT_LEAST_ONCE):事件至少被处理一次,该事件将被发送给所有订阅者。
|
|
|
|
|
* 至多一次(AT_MOST_ONCE):事件至多被处理一次,该事件只会被发送给一个随机的订阅者。
|
|
|
|
|
* 只有一次(ONLY_ONCE):该事件被且仅被一个合约订阅者处理一次。
|
|
|
|
|
|
|
|
|
|
默认的事件语义为至少一次,其他事件语义需要在事件声明中指定,例如:
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
event local alo(AT_LEAST_ONCE);
|
|
|
|
|
event global amo(AT_MOST_ONCE);
|
|
|
|
|
event global oo(ONLY_ONCE);
|
2021-09-24 17:43:11 +00:00
|
|
|
|
|
|
|
|
|
--------------
|
|
|
|
|
|
|
|
|
|
YJS项目
|
|
|
|
|
-------
|
|
|
|
|
|
|
|
|
|
除了只包含一个contract定义的YJS源文件,YJS引擎还支持\ **YJS项目**\ 。
|
|
|
|
|
|
|
|
|
|
每个YJS
|
|
|
|
|
project包含了合约执行过程中需用到的各种文件,包括yjs源文件\ **“.yjs”**\ 和其他资源文件,如\ **“mainfest.json”**,
|
|
|
|
|
**“.js”**, **“.txt”**,\ **“.jar”**, …
|
|
|
|
|
|
|
|
|
|
Manifest.json
|
|
|
|
|
~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
每个YJS项目在项目的根目录下必须有一个\ **mainfest.json**\ 文件,此文件描述了合约对于YJS的编译工具(YJS引擎)所需的必要信息。
|
|
|
|
|
|
|
|
|
|
Manifest结构
|
|
|
|
|
^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
manifest文件需包含以下信息:
|
|
|
|
|
|
|
|
|
|
1. **main**: 项目中将要被执行的合约文件。
|
|
|
|
|
2. **type**: 合约的类型,如数据合约/算法合约…
|
|
|
|
|
3. **builder**: 构建YJS项目的开发者姓名。
|
|
|
|
|
4. **insnLimit**: 运行合约需要消耗的值。
|
|
|
|
|
5. **pyDependences**: 项目所需的Python依赖。
|
|
|
|
|
|
|
|
|
|
Manifest示例
|
|
|
|
|
^^^^^^^^^^^^
|
|
|
|
|
|
|
|
|
|
::
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
"main": "contract.js",
|
|
|
|
|
"type": "Data",
|
|
|
|
|
"builder": "caihq",
|
|
|
|
|
"permissions": 0L,
|
|
|
|
|
"pyDependences": [
|
|
|
|
|
{
|
|
|
|
|
"name": "yjsexample",
|
|
|
|
|
"modules": [
|
|
|
|
|
{
|
|
|
|
|
"name": "sample"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
YJS-Java
|
|
|
|
|
~~~~~~~~
|
|
|
|
|
|
|
|
|
|
Jar文件实现了YJS与其他编程语言间的\ **跨语言调用**\ ,如YJS-Java,
|
|
|
|
|
YJS-Python, …
|
|
|
|
|
通过将Java文件包成jar包的方式,使得合约可直接调用Java中的方法。
|
|
|
|
|
|
|
|
|
|
Java class:
|
|
|
|
|
|
|
|
|
|
.. code:: java
|
|
|
|
|
|
|
|
|
|
package your.own.pkg;
|
|
|
|
|
public class HelloWorld {
|
|
|
|
|
public static int fun(String arg){
|
|
|
|
|
return arg.length;
|
|
|
|
|
}
|
|
|
|
|
public int fun2(String arg){
|
|
|
|
|
return arg.length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InvokeJava.yjs
|
|
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
|
|
contract InvokeJava{
|
|
|
|
|
export function main(arg){
|
|
|
|
|
var Hello = Java.type("your.own.package.HelloWorld");
|
|
|
|
|
var hello = new Hello();
|
|
|
|
|
return Hello.fun(arg)+hello.fun2(arg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
YJS-前端
|
|
|
|
|
~~~~~~~~
|
|
|
|
|
|
|
|
|
|
使用数瑞客户端来访问智能合约支持从智能合约中获取html/js/css等资源文件,
|
|
|
|
|
并在\ `BDWareWebClient <./_static/BDWareWebClient.zip>`__\ 中渲染。
|
|
|
|
|
首先在合约中import以下模块。
|
|
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
|
|
module Viewable{
|
|
|
|
|
export function loadResource(arg){
|
|
|
|
|
return Global.Resources.loadAsString(arg);
|
|
|
|
|
}
|
|
|
|
|
export function needRender(arg){
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
同时,import以后,定义一个getMainFrame方法,以便数瑞客户端识别主页:
|
|
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
|
|
export function getMainFrame(arg){
|
|
|
|
|
return "/html/main.html";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
该方法的返回结果为一个资源文件的路径。
|
|
|
|
|
示例的资源文件“/html/main.html”如下:
|
|
|
|
|
|
|
|
|
|
.. code:: html
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
<button onclick="queryDataFromContract()">Hello,</button> Data from contract:
|
|
|
|
|
<span id="resultText"></span>
|
|
|
|
|
<script fromContract="/html/hello.js"></script>
|
|
|
|
|
<link fromContract="/html/hello.css"/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
示例的资源文件“/html/hello.js”如下:
|
|
|
|
|
|
|
|
|
|
.. code:: javascript
|
|
|
|
|
|
|
|
|
|
var queryDataFromContract = function(){
|
|
|
|
|
//第一个参数为函数名,第二个为参数,第三个参数为回调。
|
|
|
|
|
var data = executeCurrentContract("query","abc",function(argg){
|
|
|
|
|
$("#resultText")[0].innerHTML = argg.result;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
参考示例:
|
|
|
|
|
|
|
|
|
|
YJS-Python
|
|
|
|
|
~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
TODO
|