diff --git a/assembly-script/README.md b/assembly-script/README.md new file mode 100644 index 00000000..a1324e9d --- /dev/null +++ b/assembly-script/README.md @@ -0,0 +1,124 @@ +# AssemblyScript_on_WAMR +This project is based on [Wasm Micro Runtime](https://github.com/bytecodealliance/wasm-micro-runtime) (WAMR) and [AssemblyScript](https://github.com/AssemblyScript/assemblyscript). It implements some of the `wamr app framework` in *assemblyscript*, which allows you to write some applications in *assemblyscript* and dynamically installed on *WAMR Runtime* + +## Building +To build the samples in this repo, you need `npm` on your system +``` bash +sudo apt install npm +``` + +Then install all the dependencies under the repo's root dir +``` bash +cd $repo_root +npm install +``` + +Use the command to build all samples: +``` bash +npm run build:all +``` +or you can build every sample individually: +``` bash +npm run build:timer +npm run build:publisher +npm run build:subscriber +# ... +``` +You will get the compiled wasm file under `build` folder + +Please refer to [package.json](./package.json) for more commands. + +## Run +These applications require WAMR's application framework, you need to build WAMR first. + +``` bash +cd ${WAMR_ROOT}/samples/simple +./build.sh +``` + +You will get two executable files under `out` folder: + +`simple`: The wamr runtime with application framework + +`host_tool`: The tool used to dynamically install/uninstall applications + +1. Start the runtime: + ``` bash + ./simple -s + ``` + +2. Install the compiled wasm file using `host_tool`: + ``` bash + ./host_tool -i app_name -f your_compiled_wasm_file.wasm + ``` +You can also use the WAMR's AoT compiler `wamrc` to compile the wasm bytecode into native code before you run them. Please refer to this [guide](../README.md#build-wamrc-aot-compiler) to build and install `WAMR AoT compiler`. + +After installing `wamrc`, you can compile the wasm file using command: +``` bash +wamrc -o file_name.aot file_name.wasm +``` +and you can install the AoT file to the runtime: +``` bash +./host_tool -i app_name -f your_compiled_aot_file.aot +``` + +## Development +You can develop your own application based on the `wamr_app_lib` APIs. + +### Console APIs +``` typescript +function log(a: string): void; +function log_number(a: number): void; +``` + +### Timer APIs +``` typescript +function setTimeout(cb: () => void, timeout: i32): user_timer; +function setInterval(cb: () => void, timeout: i32): user_timer; +function timer_cancel(timer: user_timer): void; +function timer_restart(timer: user_timer, interval: number): void; +function now(): i32; + +// export to runtime +function on_timer_callback(on_timer_id: i32): void; +``` + +### Request APIs +``` typescript +// register handler +function register_resource_handler(url: string, + request_handle: request_handler_f): void; +// request +function post(url: string, payload: ArrayBuffer, payload_len: number, + tag: string, cb: (resp: wamr_response) => void): void; +function get(url: string, tag: string, + cb: (resp: wamr_response) => void): void; +function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void; +function del(url: string, tag: string, + cb: (resp: wamr_response) => void): void; + +// response +function make_response_for_request(req: wamr_request): wamr_response; +function api_response_send(resp: wamr_response): void; + +// event +function publish_event(url: string, fmt: number, + payload: ArrayBuffer, payload_len: number): void; +function subscribe_event(url: string, cb: request_handler_f): void; + +// export to runtime +function on_request(buffer_offset: i32, size: i32): void; +function on_response(buffer_offset : i32, size: i32): void; +``` + +You should export the `on_timer_callback`, `on_request` and `on_response` in your application entry file, refer to the samples for example. + +To build your application, you can use `asc`: +``` bash +asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize +``` +or you can add a command into [package.json](./package.json): +``` json +"build:app": "asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize", +``` diff --git a/assembly-script/package-lock.json b/assembly-script/package-lock.json new file mode 100644 index 00000000..97d4e290 --- /dev/null +++ b/assembly-script/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "assembly_script", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assemblyscript": { + "version": "0.8.1", + "resolved": "https://registry.npm.taobao.org/assemblyscript/download/assemblyscript-0.8.1.tgz", + "integrity": "sha1-xcYnSSQG5th/QmiXs9kr0qUz9/4=", + "dev": true, + "requires": { + "binaryen": "89.0.0-nightly.20191113", + "long": "^4.0.0" + } + }, + "binaryen": { + "version": "89.0.0-nightly.20191113", + "resolved": "https://registry.npm.taobao.org/binaryen/download/binaryen-89.0.0-nightly.20191113.tgz", + "integrity": "sha1-oNORTzXJKXhzQeApELf/rrfYl6k=", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz", + "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", + "dev": true + } + } +} diff --git a/assembly-script/package.json b/assembly-script/package.json new file mode 100644 index 00000000..fa8343d8 --- /dev/null +++ b/assembly-script/package.json @@ -0,0 +1,20 @@ +{ + "name": "assembly_script", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build:request_handler": "asc samples/request_handler.ts -b build/request_handler.wasm -t build/request_handler.wat --sourceMap --validate --optimize", + "build:request_sender": "asc samples/request_sender.ts -b build/request_sender.wasm -t build/request_sender.wat --sourceMap --validate --optimize", + "build:timer": "asc samples/timer.ts -b build/timer.wasm -t build/timer.wat --sourceMap --validate --optimize", + "build:publisher": "asc samples/event_publisher.ts -b build/event_publisher.wasm -t build/event_publisher.wat --sourceMap --validate --optimize", + "build:subscriber": "asc samples/event_subscriber.ts -b build/event_subscriber.wasm -t build/event_subscriber.wat --sourceMap --validate --optimize", + "build:all": "npm run build:request_handler; npm run build:request_sender; npm run build:timer; npm run build:subscriber; npm run build:publisher" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.8.1" + } +} diff --git a/assembly-script/samples/event_publisher.ts b/assembly-script/samples/event_publisher.ts new file mode 100644 index 00000000..3ca133fd --- /dev/null +++ b/assembly-script/samples/event_publisher.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +function publish_overheat_event(): void { + var payload = String.UTF8.encode("warning: temperature is over high"); + request.publish_event("alert/overheat", 0, payload, payload.byteLength); +} + +export function on_init() : void { + timer.setInterval(publish_overheat_event, 2000); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/assembly-script/samples/event_subscriber.ts b/assembly-script/samples/event_subscriber.ts new file mode 100644 index 00000000..c9aa52a8 --- /dev/null +++ b/assembly-script/samples/event_subscriber.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + request.subscribe_event("alert/overheat", (req) => { + console.log("### user over heat event handler called:"); + + console.log(""); + console.log(" " + String.UTF8.decode(req.payload) + "\n"); + }) +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/assembly-script/samples/request_handler.ts b/assembly-script/samples/request_handler.ts new file mode 100644 index 00000000..ef9f58c5 --- /dev/null +++ b/assembly-script/samples/request_handler.ts @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + // The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + request.register_resource_handler("/test", (req) => { + console.log("### Req: /test " + String.UTF8.decode(req.payload)); + + console.log(" request payload:"); + console.log(" " + String.UTF8.decode(req.payload) + "\n"); + + var resp = request.make_response_for_request(req); + resp.set_payload(String.UTF8.encode("Ok"), 2); + request.api_response_send(resp); + }); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/assembly-script/samples/request_sender.ts b/assembly-script/samples/request_sender.ts new file mode 100644 index 00000000..5648985e --- /dev/null +++ b/assembly-script/samples/request_sender.ts @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + var payload = String.UTF8.encode("test message"); + request.post("/test", payload, payload.byteLength, "", (resp) => { + if (resp != null) { + console.log("Post Success"); + + if (resp.payload != null) { + console.log(" response payload:") + console.log(" " + String.UTF8.decode(resp.payload!) + "\n"); + } + } + else + console.log("Post Timeout"); + }); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/assembly-script/samples/timer.ts b/assembly-script/samples/timer.ts new file mode 100644 index 00000000..2e3f69d2 --- /dev/null +++ b/assembly-script/samples/timer.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from '../wamr_app_lib/console' +import * as timer from '../wamr_app_lib/timer' + +/* clousure is not implemented yet, we need to declare global variables + so that they can be accessed inside a callback function */ +var cnt = 0; +var my_timer: timer.user_timer; + +export function on_init(): void { + /* The callback function will be called every 2 second, + and will stop after 10 calls */ + my_timer = timer.setInterval(() => { + cnt ++; + console.log((cnt * 2).toString() + " seconds passed"); + + if (cnt >= 10) { + timer.timer_cancel(my_timer); + console.log("Stop Timer"); + } + }, 2000); +} + +export function on_destroy(): void { + +} + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} \ No newline at end of file diff --git a/assembly-script/samples/tsconfig.json b/assembly-script/samples/tsconfig.json new file mode 100644 index 00000000..c614e5c8 --- /dev/null +++ b/assembly-script/samples/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/console.ts b/assembly-script/wamr_app_lib/console.ts new file mode 100644 index 00000000..0b06c07d --- /dev/null +++ b/assembly-script/wamr_app_lib/console.ts @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +@external("env", "printf") +declare function printf(a: ArrayBuffer): i32; + +export function log(a: string): void { + printf(String.UTF8.encode(a + '\n', true)); +} + +export function log_number(a: number): void { + printf(String.UTF8.encode(a.toString() + '\n')); +} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/request.ts b/assembly-script/wamr_app_lib/request.ts new file mode 100644 index 00000000..2125e045 --- /dev/null +++ b/assembly-script/wamr_app_lib/request.ts @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +import * as console from './console' +import * as timer from './timer' + +@external("env", "wasm_response_send") +declare function wasm_response_send(buffer: ArrayBuffer, size: i32): void; + +@external("env", "wasm_register_resource") +declare function wasm_register_resource(url: ArrayBuffer): void; + +@external("env", "wasm_post_request") +declare function wasm_post_request(buffer: ArrayBuffer, size: i32): void; + +@external("env", "wasm_sub_event") +declare function wasm_sub_event(url: ArrayBuffer): void; + +var COAP_GET = 1; +var COAP_POST = 2; +var COAP_PUT = 3; +var COAP_DELETE = 4; +var COAP_EVENT = COAP_DELETE + 2; + +/* CoAP response codes */ +export enum CoAP_Status { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, PING_RESPONSE +}; + +var g_mid: i32 = 0; +class wamr_request { + mid: i32 = 0; + url: string = ""; + action: i32 = 0; + fmt: i32 = 0; + payload: ArrayBuffer; + payload_len: i32 = 0; + + sender: i32 = 0; + + constructor(mid: i32, url: string, action: i32, fmt: i32, + payload: ArrayBuffer, payload_len: number) { + this.mid = mid; + this.url = url; + this.action = action; + this.fmt = fmt; + this.payload = payload; + this.payload_len = i32(payload_len); + } +} + +class wamr_response { + mid: i32 = 0; + status: i32 = 0; + fmt: i32 = 0; + payload: ArrayBuffer | null; + payload_len: i32 = 0; + + receiver: i32 = 0; + + constructor(mid: i32, status: i32, fmt: i32, + payload: ArrayBuffer | null, payload_len: i32) { + this.mid = mid; + this.status = status; + this.fmt = fmt; + this.payload = payload; + this.payload_len = payload_len; + } + + set_status(status: number): void { + this.status = i32(status); + } + + set_payload(payload: ArrayBuffer, payload_len: number): void { + this.payload = payload; + this.payload_len = i32(payload_len); + } +} + +class wamr_resource { + url: string; + type: number; + cb: request_handler_f; + + constructor(url: string, type: number, cb: request_handler_f) { + this.url = url; + this.type = type; + this.cb = cb; + } +} + +function is_expire(trans: wamr_transaction, index: i32, array: Array): bool { + var now = timer.now(); + + var elapsed_ms = (now < trans.time) ? + (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); + + return elapsed_ms >= TRANSACTION_TIMEOUT_MS; +} + +function not_expire(trans: wamr_transaction, index: i32, array: Array): bool { + var now = timer.now(); + + var elapsed_ms = (now < trans.time) ? + (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); + + return elapsed_ms >= TRANSACTION_TIMEOUT_MS; +} + +function transaction_timeout_handler(): void { + var now = timer.now(); + + var expired = transaction_list.filter(is_expire); + transaction_list = transaction_list.filter(not_expire); + + expired.forEach(item => { + item.cb(null); + transaction_remove(item); + }) + + if (transaction_list.length > 0) { + var elpased_ms: number, ms_to_expiry: number; + now = timer.now(); + if (now < transaction_list[0].time) { + elpased_ms = now + (0xFFFFFFFF - transaction_list[0].time) + 1; + } else { + elpased_ms = now - transaction_list[0].time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + timer.timer_restart(g_trans_timer, ms_to_expiry); + } else { + timer.timer_cancel(g_trans_timer); + } +} + +function transaction_find(mid: number): wamr_transaction | null { + for (let i = 0; i < transaction_list.length; i++) { + if (transaction_list[i].mid == mid) + return transaction_list[i]; + } + return null; +} + +function transaction_add(trans: wamr_transaction): void { + transaction_list.push(trans); + + if (transaction_list.length == 1) { + g_trans_timer = timer.setTimeout( + transaction_timeout_handler, + TRANSACTION_TIMEOUT_MS + ); + } +} + +function transaction_remove(trans: wamr_transaction): void { + var index = transaction_list.indexOf(trans); + transaction_list.splice(index, 1); +} + +var transaction_list = new Array(); +class wamr_transaction { + mid: number; + time: number; + cb: (resp: wamr_response | null) => void; + + constructor(mid: number, time: number, cb: (resp: wamr_response) => void) { + this.mid = mid; + this.time = time; + this.cb = cb; + } +} + +var REQUEST_PACKET_FIX_PART_LEN = 18; +var RESPONSE_PACKET_FIX_PART_LEN = 16; +var TRANSACTION_TIMEOUT_MS = 5000; +var g_trans_timer: timer.user_timer; + +var Reg_Event = 0; +var Reg_Request = 1; + +function pack_request(req: wamr_request): DataView { + var url_len = req.url.length + 1; + var len = REQUEST_PACKET_FIX_PART_LEN + url_len + req.payload_len + var buf = new ArrayBuffer(len); + + var dataview = new DataView(buf, 0, len); + + dataview.setUint8(0, 1); + dataview.setUint8(1, u8(req.action)); + dataview.setUint16(2, u16(req.fmt)); + dataview.setUint32(4, req.mid); + dataview.setUint32(8, req.sender); + dataview.setUint16(12, u16(url_len)) + dataview.setUint32(14, req.payload_len); + + var i = 0; + for (i = 0; i < url_len - 1; i++) { + dataview.setUint8(i + 18, u8(req.url.codePointAt(i))); + } + dataview.setUint8(i + 18, 0); + + var payload_view = new DataView(req.payload); + for (i = 0; i < req.payload_len; i++) { + dataview.setUint8(i + 18 + url_len, u8(payload_view.getUint8(i))); + } + + return dataview; +} + +function unpack_request(packet: ArrayBuffer, size: i32): wamr_request { + var dataview = new DataView(packet, 0, size); + + if (dataview.getUint8(0) != 1) + throw new Error("packet version mismatch"); + + if (size < REQUEST_PACKET_FIX_PART_LEN) + throw new Error("packet size error"); + + var url_len = dataview.getUint16(12); + var payload_len = dataview.getUint32(14); + + if (size != (REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) + throw new Error("packet size error"); + + var action = dataview.getUint8(1); + var fmt = dataview.getUint16(2); + var mid = dataview.getUint32(4); + var sender = dataview.getUint32(8); + + var url = packet.slice(REQUEST_PACKET_FIX_PART_LEN, REQUEST_PACKET_FIX_PART_LEN + url_len - 1); + var payload = packet.slice(REQUEST_PACKET_FIX_PART_LEN + url_len, REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len); + + var req = new wamr_request(mid, String.UTF8.decode(url), action, fmt, payload, payload_len); + req.sender = sender; + + return req; +} + +function pack_response(resp: wamr_response): DataView { + var len = RESPONSE_PACKET_FIX_PART_LEN + resp.payload_len + var buf = new ArrayBuffer(len); + + var dataview = new DataView(buf, 0, len); + + dataview.setUint8(0, 1); + dataview.setUint8(1, u8(resp.status)); + dataview.setUint16(2, u16(resp.fmt)); + dataview.setUint32(4, resp.mid); + dataview.setUint32(8, resp.receiver); + dataview.setUint32(12, resp.payload_len) + + if (resp.payload != null) { + var payload_view = new DataView(resp.payload!); + for (let i = 0; i < resp.payload_len; i++) { + dataview.setUint8(i + 16, payload_view.getUint8(i)); + } + } + + return dataview; +} + +function unpack_response(packet: ArrayBuffer, size: i32): wamr_response { + var dataview = new DataView(packet, 0, size); + + if (dataview.getUint8(0) != 1) + throw new Error("packet version mismatch"); + + if (size < RESPONSE_PACKET_FIX_PART_LEN) + throw new Error("packet size error"); + + var payload_len = dataview.getUint32(12); + if (size != RESPONSE_PACKET_FIX_PART_LEN + payload_len) + throw new Error("packet size error"); + + var status = dataview.getUint8(1); + var fmt = dataview.getUint16(2); + var mid = dataview.getUint32(4); + var receiver = dataview.getUint32(8); + + var payload = packet.slice(RESPONSE_PACKET_FIX_PART_LEN); + + var resp = new wamr_response(mid, status, fmt, payload, payload_len); + resp.receiver = receiver; + + return resp; +} + +function do_request(req: wamr_request, cb: (resp: wamr_response) => void): void { + var trans = new wamr_transaction(req.mid, timer.now(), cb); + var msg = pack_request(req); + + transaction_add(trans); + + wasm_post_request(msg.buffer, msg.byteLength); +} + +function do_response(resp: wamr_response): void { + var msg = pack_response(resp); + + wasm_response_send(msg.buffer, msg.byteLength); +} + +var resource_list = new Array(); +type request_handler_f = (req: wamr_request) => void; + +function registe_url_handler(url: string, cb: request_handler_f, type: number): void { + for (let i = 0; i < resource_list.length; i++) { + if (resource_list[i].type == type && resource_list[i].url == url) { + resource_list[i].cb = cb; + return; + } + } + + var res = new wamr_resource(url, type, cb); + resource_list.push(res); + + if (type == Reg_Request) + wasm_register_resource(String.UTF8.encode(url)); + else + wasm_sub_event(String.UTF8.encode(url)); +} + +function is_event_type(req: wamr_request): bool { + return req.action == COAP_EVENT; +} + +function check_url_start(url: string, leading_str: string): bool { + return url.split('/')[0] == leading_str.split('/')[0]; +} + +/* User APIs below */ +export function post(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_POST, 0, payload, payload_len); + + do_request(req, cb); +} + +export function get(url: string, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_GET, 0, new ArrayBuffer(0), 0); + + do_request(req, cb); +} + +export function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_PUT, 0, payload, payload_len); + + do_request(req, cb); +} + +export function del(url: string, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_PUT, 0, new ArrayBuffer(0), 0); + + do_request(req, cb); +} + +export function make_response_for_request(req: wamr_request): wamr_response { + var resp = new wamr_response(req.mid, CoAP_Status.CONTENT_2_05, 0, null, 0); + resp.receiver = req.sender; + + return resp; +} + +export function api_response_send(resp: wamr_response): void { + do_response(resp); +} + +export function register_resource_handler(url: string, + request_handle: request_handler_f): void { + registe_url_handler(url, request_handle, Reg_Request); +} + +export function publish_event(url: string, fmt: number, + payload: ArrayBuffer, payload_len: number): void { + var req = new wamr_request(g_mid++, url, COAP_EVENT, i32(fmt), payload, payload_len); + + var msg = pack_request(req); + + wasm_post_request(msg.buffer, msg.byteLength); +} + +export function subscribe_event(url: string, cb: request_handler_f): void { + registe_url_handler(url, cb, Reg_Event); +} + + +/* These two APIs are required by wamr runtime, + use a wrapper to export them in the entry file + + e.g: + + import * as request from '.wamr_app_lib/request' + + // Your code here ... + + export function _on_request(buffer_offset: i32, size: i32): void { + on_request(buffer_offset, size); + } + + export function _on_response(buffer_offset: i32, size: i32): void { + on_response(buffer_offset, size); + } +*/ +export function on_request(buffer_offset: i32, size: i32): void { + var buffer = new ArrayBuffer(size); + var dataview = new DataView(buffer); + + for (let i = 0; i < size; i++) { + dataview.setUint8(i, load(buffer_offset + i, 0, 1)); + } + + var req = unpack_request(buffer, size); + + var is_event = is_event_type(req); + + for (let i = 0; i < resource_list.length; i++) { + if ((is_event && resource_list[i].type == Reg_Event) + || (!is_event && resource_list[i].type == Reg_Request)) { + if (check_url_start(req.url, resource_list[i].url)) { + resource_list[i].cb(req); + return; + } + } + } + + console.log("on_request: exit. no service handler."); +} + +export function on_response(buffer_offset: i32, size: i32): void { + var buffer = new ArrayBuffer(size); + var dataview = new DataView(buffer); + + for (let i = 0; i < size; i++) { + dataview.setUint8(i, load(buffer_offset + i, 0, 1)); + } + + var resp = unpack_response(buffer, size); + var trans = transaction_find(resp.mid); + + if (trans != null) { + if (transaction_list.indexOf(trans) == 0) { + if (transaction_list.length >= 2) { + var elpased_ms: number, ms_to_expiry: number; + var now = timer.now(); + if (now < transaction_list[1].time) { + elpased_ms = now + (0xFFFFFFFF - transaction_list[1].time) + 1; + } else { + elpased_ms = now - transaction_list[1].time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + timer.timer_restart(g_trans_timer, ms_to_expiry); + } else { + timer.timer_cancel(g_trans_timer); + } + } + + trans.cb(resp); + } +} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/timer.ts b/assembly-script/wamr_app_lib/timer.ts new file mode 100644 index 00000000..ea8363e8 --- /dev/null +++ b/assembly-script/wamr_app_lib/timer.ts @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +@external("env", "wasm_create_timer") +declare function wasm_create_timer(a: i32, b: bool, c: bool): i32; + +@external("env", "wasm_timer_cancel") +declare function wasm_timer_cancel(a: i32): void; + +@external("env", "wasm_timer_restart") +declare function wasm_timer_restart(a: i32, b: i32): void; + +@external("env", "wasm_get_sys_tick_ms") +declare function wasm_get_sys_tick_ms(): i32; + +export var timer_list = new Array(); + +export class user_timer { + timer_id: i32 = 0; + timeout: i32; + period: bool = false; + cb: () => void; + + constructor(cb: () => void, timeout: i32, period: bool) { + this.cb = cb; + this.timeout = timeout; + this.period = period + this.timer_id = timer_create(this.timeout, this.period, true); + } +} + +export function timer_create(a: i32, b: bool, c: bool): i32 { + return wasm_create_timer(a, b, c); +} + +export function setTimeout(cb: () => void, timeout: i32): user_timer { + var timer = new user_timer(cb, timeout, false); + timer_list.push(timer); + + return timer; +} + +export function setInterval(cb: () => void, timeout: i32): user_timer { + var timer = new user_timer(cb, timeout, true); + timer_list.push(timer); + + return timer; +} + +export function timer_cancel(timer: user_timer): void { + wasm_timer_cancel(timer.timer_id); + + var i = 0; + for (i = 0; i < timer_list.length; i++) { + if (timer_list[i].timer_id == timer.timer_id) + break; + } + + timer_list.splice(i, 1); +} + +export function timer_restart(timer: user_timer, interval: number): void { + wasm_timer_restart(timer.timer_id, i32(interval)); +} + +export function now(): i32 { + return wasm_get_sys_tick_ms(); +} + +// This export function need to be copied to the top application file +// +export function on_timer_callback(on_timer_id: i32): void { + for (let i = 0; i < timer_list.length; i++) { + if (timer_list[i].timer_id == on_timer_id) { + timer_list[i].cb(); + } + } +} \ No newline at end of file diff --git a/assembly-script/wamr_app_lib/tsconfig.json b/assembly-script/wamr_app_lib/tsconfig.json new file mode 100644 index 00000000..c614e5c8 --- /dev/null +++ b/assembly-script/wamr_app_lib/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index ae151257..8b779240 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/core/iwasm/compilation/aot_emit_memory.c @@ -245,6 +245,7 @@ fail: aot_set_last_error("llvm build load failed."); \ goto fail; \ } \ + LLVMSetAlignment(value, 1); \ } while (0) #define BUILD_TRUNC(data_type) do { \ @@ -256,10 +257,12 @@ fail: } while (0) #define BUILD_STORE() do { \ - if (!LLVMBuildStore(comp_ctx->builder, value, maddr)) { \ + LLVMValueRef res; \ + if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ aot_set_last_error("llvm build store failed."); \ goto fail; \ } \ + LLVMSetAlignment(res, 1); \ } while (0) #define BUILD_SIGN_EXT(dst_type) do { \ @@ -599,12 +602,7 @@ aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) return false; } - /* convert call result from i8 to i1 */ - if (!(ret_value = LLVMBuildIntCast(comp_ctx->builder, ret_value, - INT1_TYPE, "mem_grow_ret"))) { - aot_set_last_error("llvm build bit cast failed."); - return false; - } + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_grow_ret"); /* ret_value = ret_value == true ? delta : pre_page_count */ if (!(ret_value = LLVMBuildSelect(comp_ctx->builder, ret_value, diff --git a/core/iwasm/compilation/aot_emit_numberic.c b/core/iwasm/compilation/aot_emit_numberic.c index 3d079d78..46ed1cc3 100644 --- a/core/iwasm/compilation/aot_emit_numberic.c +++ b/core/iwasm/compilation/aot_emit_numberic.c @@ -405,14 +405,23 @@ aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, CHECK_LLVM_CONST(zero_undef); /* Call the LLVM intrinsic function */ - DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, - bit_cnt_llvm_intrinsic[type], - ret_type, - param_types, - 2, - operand, - zero_undef), - NULL); + if (type < POP_CNT32) + DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, + bit_cnt_llvm_intrinsic[type], + ret_type, + param_types, + 2, + operand, + zero_undef), + NULL); + else + DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, + bit_cnt_llvm_intrinsic[type], + ret_type, + param_types, + 1, + operand), + NULL); return true; diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index d240b7e3..e752e824 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -42,6 +42,58 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) } \ } while (0) +static bool +skip_leb(const uint8 *buf, const uint8 *buf_end, + uint32 *p_offset, uint32 maxbits, + char* error_buf, uint32 error_buf_size) +{ + uint32 bcnt = 0; + uint64 byte; + + while (true) { + if (bcnt + 1 > (maxbits + 6) / 7) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: " + "integer representation too long"); + return false; + } + + CHECK_BUF(buf, buf_end, *p_offset + 1); + byte = buf[*p_offset]; + *p_offset += 1; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + return true; +} + +#define skip_leb_int64(p, p_end) do { \ + uint32 off = 0; \ + if (!skip_leb(p, p_end, &off, 64, \ + error_buf, error_buf_size)) \ + return false; \ + p += off; \ +} while (0) + +#define skip_leb_uint32(p, p_end) do { \ + uint32 off = 0; \ + if (!skip_leb(p, p_end, &off, 32, \ + error_buf, error_buf_size)) \ + return false; \ + p += off; \ +} while (0) + +#define skip_leb_int32(p, p_end) do { \ + uint32 off = 0; \ + if (!skip_leb(p, p_end, &off, 32, \ + error_buf, error_buf_size)) \ + return false; \ + p += off; \ +} while (0) + static bool read_leb(const uint8 *buf, const uint8 *buf_end, uint32 *p_offset, uint32 maxbits, @@ -122,17 +174,18 @@ fail_integer_too_large: #define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) #define read_bool(p) TEMPLATE_READ_VALUE(bool, p) -#define read_leb_uint64(p, p_end, res) do { \ - uint32 off = 0; \ - uint64 res64; \ - if (!read_leb(p, p_end, &off, 64, false, &res64, \ - error_buf, error_buf_size)) \ - return false; \ - p += off; \ - res = (uint64)res64; \ -} while (0) - #define read_leb_int64(p, p_end, res) do { \ + if (p < p_end) { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int64)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFFFFFFFFFF80LL; \ + p++; \ + break; \ + } \ + } \ uint32 off = 0; \ uint64 res64; \ if (!read_leb(p, p_end, &off, 64, true, &res64, \ @@ -143,6 +196,14 @@ fail_integer_too_large: } while (0) #define read_leb_uint32(p, p_end, res) do { \ + if (p < p_end) { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = _val; \ + p++; \ + break; \ + } \ + } \ uint32 off = 0; \ uint64 res64; \ if (!read_leb(p, p_end, &off, 32, false, &res64, \ @@ -153,6 +214,17 @@ fail_integer_too_large: } while (0) #define read_leb_int32(p, p_end, res) do { \ + if (p < p_end) { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int32)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFF80; \ + p++; \ + break; \ + } \ + } \ uint32 off = 0; \ uint64 res64; \ if (!read_leb(p, p_end, &off, 32, true, &res64, \ @@ -1878,8 +1950,7 @@ wasm_loader_find_block_addr(WASMModule *module, { const uint8 *p = start_addr, *p_end = code_end_addr; uint8 *else_addr = NULL; - uint32 block_nested_depth = 1, count, i, u32; - uint64 u64; + uint32 block_nested_depth = 1, count, i; uint8 opcode, u8; BlockAddr block_stack[16] = { 0 }, *block; @@ -1969,24 +2040,24 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_BR: case WASM_OP_BR_IF: - read_leb_uint32(p, p_end, u32); /* labelidx */ + skip_leb_uint32(p, p_end); /* labelidx */ break; case WASM_OP_BR_TABLE: read_leb_uint32(p, p_end, count); /* lable num */ for (i = 0; i <= count; i++) /* lableidxs */ - read_leb_uint32(p, p_end, u32); + skip_leb_uint32(p, p_end); break; case WASM_OP_RETURN: break; case WASM_OP_CALL: - read_leb_uint32(p, p_end, u32); /* funcidx */ + skip_leb_uint32(p, p_end); /* funcidx */ break; case WASM_OP_CALL_INDIRECT: - read_leb_uint32(p, p_end, u32); /* typeidx */ + skip_leb_uint32(p, p_end); /* typeidx */ CHECK_BUF(p, p_end, 1); u8 = read_uint8(p); /* 0x00 */ break; @@ -2004,7 +2075,7 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_TEE_LOCAL: case WASM_OP_GET_GLOBAL: case WASM_OP_SET_GLOBAL: - read_leb_uint32(p, p_end, u32); /* localidx */ + skip_leb_uint32(p, p_end); /* localidx */ break; case WASM_OP_GET_LOCAL_FAST: @@ -2039,20 +2110,20 @@ wasm_loader_find_block_addr(WASMModule *module, case WASM_OP_I64_STORE8: case WASM_OP_I64_STORE16: case WASM_OP_I64_STORE32: - read_leb_uint32(p, p_end, u32); /* align */ - read_leb_uint32(p, p_end, u32); /* offset */ + skip_leb_uint32(p, p_end); /* align */ + skip_leb_uint32(p, p_end); /* offset */ break; case WASM_OP_MEMORY_SIZE: case WASM_OP_MEMORY_GROW: - read_leb_uint32(p, p_end, u32); /* 0x00 */ + skip_leb_uint32(p, p_end); /* 0x00 */ break; case WASM_OP_I32_CONST: - read_leb_int32(p, p_end, u32); + skip_leb_int32(p, p_end); break; case WASM_OP_I64_CONST: - read_leb_int64(p, p_end, u64); + skip_leb_int64(p, p_end); break; case WASM_OP_F32_CONST: p += sizeof(float32); @@ -2195,8 +2266,6 @@ wasm_loader_find_block_addr(WASMModule *module, } } - (void)u32; - (void)u64; (void)u8; return false; } diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index d4252cf0..59ee56ba 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -200,7 +200,7 @@ wasi_environ_get(wasm_exec_env_t exec_env, &environ_count, &environ_buf_size); WASI_CHECK_ERR(); - total_size = sizeof(uint32) * ((uint64)environ_count + 1); + total_size = sizeof(char*) * ((uint64)environ_count + 1); if (total_size >= UINT32_MAX || !validate_app_addr(environ_offset, (uint32)total_size) || environ_buf_size >= UINT32_MAX