├── pages ├── index.js ├── index.json ├── index.wxss └── index.wxml ├── app.js ├── go_dev ├── sample.wasm ├── assets │ ├── app.css │ ├── wasm_exec.js │ └── vue.min.js ├── main.go └── lesson1.html ├── package_lesson1 ├── index.json ├── assets │ ├── sample.wasm.br │ └── wasm_exec.js ├── index.wxss ├── index.js └── index.wxml ├── package_lesson2 ├── index.json ├── assets │ ├── sampleImage1.jpg │ └── opencv3.4.16.wasm.br ├── index.wxss ├── index.wxml └── index.js ├── screenshot ├── 1.jpg ├── 2-1.jpg ├── 2-2.jpg ├── 2-3.jpg ├── 3-1.jpg ├── 3-2.jpg ├── 4-1.jpg ├── 4-2.jpg └── 4-3.jpg ├── opencv_dev ├── opencv3.4.16.wasm ├── assets │ ├── sampleImage1.jpg │ └── app.css └── lesson2.html ├── sitemap.json ├── package.json ├── node_dev ├── package.json ├── package-lock.json └── main.js ├── app.json ├── app.wxss ├── LICENSE ├── miniprogram_npm └── text-encoder │ ├── index.js.map │ └── index.js ├── project.config.json ├── README.md └── style └── weui.wxss /pages/index.js: -------------------------------------------------------------------------------- 1 | Page({}) 2 | -------------------------------------------------------------------------------- /pages/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | }, 5 | globalData: { 6 | } 7 | }) -------------------------------------------------------------------------------- /go_dev/sample.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/go_dev/sample.wasm -------------------------------------------------------------------------------- /package_lesson1/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "Lesson 1" 4 | } -------------------------------------------------------------------------------- /package_lesson2/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {}, 3 | "navigationBarTitleText": "Lesson 2" 4 | } -------------------------------------------------------------------------------- /screenshot/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/1.jpg -------------------------------------------------------------------------------- /screenshot/2-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/2-1.jpg -------------------------------------------------------------------------------- /screenshot/2-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/2-2.jpg -------------------------------------------------------------------------------- /screenshot/2-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/2-3.jpg -------------------------------------------------------------------------------- /screenshot/3-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/3-1.jpg -------------------------------------------------------------------------------- /screenshot/3-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/3-2.jpg -------------------------------------------------------------------------------- /screenshot/4-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/4-1.jpg -------------------------------------------------------------------------------- /screenshot/4-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/4-2.jpg -------------------------------------------------------------------------------- /screenshot/4-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/screenshot/4-3.jpg -------------------------------------------------------------------------------- /opencv_dev/opencv3.4.16.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/opencv_dev/opencv3.4.16.wasm -------------------------------------------------------------------------------- /opencv_dev/assets/sampleImage1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/opencv_dev/assets/sampleImage1.jpg -------------------------------------------------------------------------------- /package_lesson1/assets/sample.wasm.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/package_lesson1/assets/sample.wasm.br -------------------------------------------------------------------------------- /package_lesson2/assets/sampleImage1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/package_lesson2/assets/sampleImage1.jpg -------------------------------------------------------------------------------- /package_lesson2/assets/opencv3.4.16.wasm.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-WASM/HEAD/package_lesson2/assets/opencv3.4.16.wasm.br -------------------------------------------------------------------------------- /sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "visit https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /pages/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .button-sp-area{ 3 | margin: 0 auto; 4 | padding-top: 15px; 5 | width: 60%; 6 | } 7 | .mini-btn{ 8 | margin-right: 5px; 9 | } 10 | .weui-panel__hd{ 11 | font-weight:bold; 12 | color:#333; 13 | } -------------------------------------------------------------------------------- /package_lesson1/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .button-sp-area{ 3 | margin: 0 auto; 4 | padding-top: 15px; 5 | width: 60%; 6 | } 7 | .mini-btn{ 8 | margin-right: 5px; 9 | } 10 | .weui-panel__hd{ 11 | font-weight:bold; 12 | color:#333; 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-ar-wasm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "text-encoder": "0.0.4" 13 | }, 14 | "devDependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /package_lesson2/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .button-sp-area{ 3 | margin: 0 auto; 4 | padding-top: 15px; 5 | width: 60%; 6 | } 7 | .mini-btn{ 8 | margin-right: 5px; 9 | } 10 | .weui-panel__hd{ 11 | font-weight:bold; 12 | color:#333; 13 | } 14 | 15 | .visibleCanvas{ 16 | margin:auto; 17 | } 18 | 19 | .inputImage{ 20 | width:375px; 21 | } -------------------------------------------------------------------------------- /node_dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_dev", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "node main.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "wasm-brotli": "^2.0.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /node_dev/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node_dev", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "wasm-brotli": { 8 | "version": "2.0.2", 9 | "resolved": "https://registry.npmmirror.com/wasm-brotli/download/wasm-brotli-2.0.2.tgz", 10 | "integrity": "sha1-9TF6+w0+Ll/pCma21h5Ck5KE8cc=" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index" 4 | ], 5 | "subpackages": [ 6 | { 7 | "root": "package_lesson1", 8 | "pages": [ 9 | "index" 10 | ] 11 | }, 12 | { 13 | "root": "package_lesson2", 14 | "pages": [ 15 | "index" 16 | ] 17 | } 18 | ], 19 | "window": { 20 | "backgroundTextStyle": "light", 21 | "navigationBarBackgroundColor": "#fff", 22 | "navigationBarTitleText": "运行WASM", 23 | "navigationBarTextStyle": "black" 24 | }, 25 | "sitemapLocation": "sitemap.json" 26 | } -------------------------------------------------------------------------------- /go_dev/assets/app.css: -------------------------------------------------------------------------------- 1 | .page{ 2 | background-color: #F8F8F8; 3 | font-size: 16px; 4 | } 5 | .page__hd { 6 | padding: 40px; 7 | } 8 | .page__bd { 9 | padding-bottom: 40px; 10 | } 11 | .page__bd_spacing { 12 | padding-left: 15px; 13 | padding-right: 15px; 14 | } 15 | 16 | .page__ft{ 17 | padding-bottom: 10px; 18 | text-align: center; 19 | } 20 | 21 | .page__title { 22 | text-align: left; 23 | font-size: 20px; 24 | font-weight: 400; 25 | } 26 | 27 | .page__desc { 28 | margin-top: 5px; 29 | color: #888888; 30 | text-align: left; 31 | font-size: 14px; 32 | } 33 | 34 | .marginTop10{ 35 | margin-top: 10px; 36 | } 37 | 38 | .loading{ 39 | text-align: center; 40 | } -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | @import "style/weui.wxss" 3 | 4 | page{ 5 | background-color: #F8F8F8; 6 | font-size: 16px; 7 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 8 | } 9 | .page__hd { 10 | padding: 40px; 11 | } 12 | .page__bd { 13 | padding-bottom: 40px; 14 | } 15 | .page__bd_spacing { 16 | padding-left: 15px; 17 | padding-right: 15px; 18 | } 19 | 20 | .page__ft{ 21 | padding-bottom: 10px; 22 | text-align: center; 23 | } 24 | 25 | .page__title { 26 | text-align: left; 27 | font-size: 20px; 28 | font-weight: 400; 29 | } 30 | 31 | .page__desc { 32 | margin-top: 5px; 33 | color: #888888; 34 | text-align: left; 35 | font-size: 14px; 36 | } 37 | 38 | .marginTop10{ 39 | margin-top: 10px; 40 | } -------------------------------------------------------------------------------- /package_lesson2/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 图像处理 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /opencv_dev/assets/app.css: -------------------------------------------------------------------------------- 1 | .page{ 2 | background-color: #F8F8F8; 3 | font-size: 16px; 4 | } 5 | .page__hd { 6 | padding: 40px; 7 | } 8 | .page__bd { 9 | padding-bottom: 40px; 10 | } 11 | .page__bd_spacing { 12 | padding-left: 15px; 13 | padding-right: 15px; 14 | } 15 | 16 | .page__ft{ 17 | padding-bottom: 10px; 18 | text-align: center; 19 | } 20 | 21 | .page__title { 22 | text-align: left; 23 | font-size: 20px; 24 | font-weight: 400; 25 | } 26 | 27 | .page__desc { 28 | margin-top: 5px; 29 | color: #888888; 30 | text-align: left; 31 | font-size: 14px; 32 | } 33 | 34 | .marginTop10{ 35 | margin-top: 10px; 36 | } 37 | 38 | .loading{ 39 | text-align: center; 40 | } 41 | 42 | .button-sp-area{ 43 | margin: 0 auto; 44 | padding-top: 15px; 45 | width: 60%; 46 | } 47 | .mini-btn{ 48 | margin-right: 5px; 49 | } 50 | .weui-panel__hd{ 51 | font-weight:bold; 52 | color:#333; 53 | } 54 | 55 | .visibleCanvas{ 56 | margin:10px auto; 57 | display: block; 58 | } 59 | 60 | .hiddenCanvas{ 61 | position:fixed; 62 | left:1000px; 63 | width:800%; 64 | height:400%; 65 | } 66 | 67 | .inputImage{ 68 | width:375px; 69 | display: block; 70 | margin: auto; 71 | } -------------------------------------------------------------------------------- /go_dev/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | 使用方法:在终端运行“go build -o sample.wasm main.go”,会生成sample.wasm。 3 | */ 4 | package main 5 | 6 | import ( 7 | "syscall/js" 8 | "time" 9 | ) 10 | 11 | var totalNum int = 0 12 | 13 | // js调用Go。点击按钮一次,增加一次计数。 14 | func addTotal(this js.Value, args []js.Value) interface{} { 15 | totalNum = totalNum + 1 16 | return js.ValueOf(totalNum) 17 | } 18 | 19 | // js调用Go, Go回调js。Go等待2秒后,才回调js。 20 | func asyncAndCallbak(this js.Value, args []js.Value) interface{} { 21 | // js输入参数 22 | input := args[0].String() 23 | // js回调函数 24 | callback := args[1] 25 | // 协程 26 | go func() { 27 | // 等待2秒 28 | time.Sleep(2 * time.Second) 29 | result := "回复:" + input 30 | // 运行js回调函数 31 | callback.Invoke(result) 32 | }() 33 | 34 | return nil 35 | } 36 | 37 | func main() { 38 | // 创建通道 39 | channel := make(chan int) 40 | // 1.Go调用js的console.log()方法,在开发者工具的Consol面板中查看。 41 | console := js.Global().Get("console") 42 | console.Call("log", "hello, world!") 43 | 44 | // 2.js调用Go的addTotal()方法 45 | js.Global().Set("addTotal", js.FuncOf(addTotal)) 46 | 47 | // 3.js调用Go的asyncAndCallbak()方法, Go回调js。 48 | js.Global().Set("asyncAndCallbak", js.FuncOf(asyncAndCallbak)) 49 | 50 | // 通道阻塞了main()方法 51 | <-channel 52 | 53 | // main()方法在结束之前,不会运行到这个位置。 54 | console.Call("log", "exit") 55 | } 56 | -------------------------------------------------------------------------------- /package_lesson1/index.js: -------------------------------------------------------------------------------- 1 | require('./assets/wasm_exec.js'); 2 | const wasm_url = '/package_lesson1/assets/sample.wasm.br' 3 | 4 | Page({ 5 | data: { 6 | notice: '', 7 | inputText: '你好!Go语言。', 8 | test_result1: '0', 9 | test_result2: '', 10 | }, 11 | async onReady() { 12 | // 在小程序基础类库的global对象上,增加console对象。 13 | global.console = console 14 | // 使用小程序类库的WXWebAssembly,初始化Go运行环境。 15 | await this.initGo() 16 | }, 17 | async initGo() { 18 | var _that = this; 19 | const go = new global.Go(); 20 | try { 21 | const result = await WXWebAssembly.instantiate(wasm_url, go.importObject) 22 | var msg = 'Go初始化成功,在小程序调试窗口查看console的信息。' 23 | _that.setData({ 24 | notice: msg, 25 | }) 26 | console.log('initGo', msg) 27 | 28 | // 运行go程序的main()方法 29 | await go.run(result.instance); 30 | // 注意:在go程序的main()方法退出之前,小程序不会运行到这个位置。 31 | console.log('initGo', '运行完成') 32 | } catch (err) { 33 | console.error('initGo', err) 34 | } 35 | }, 36 | btnRun1() { 37 | var _that = this; 38 | var res = global.addTotal() 39 | _that.setData({ 40 | test_result1: res, 41 | }) 42 | console.log(res) 43 | }, 44 | btnRun2() { 45 | var _that = this; 46 | wx.showLoading({ 47 | title: '请等待2秒', 48 | mask:true, 49 | }) 50 | global.asyncAndCallbak(_that.data.inputText, function (res) { 51 | wx.hideLoading() 52 | 53 | _that.setData({ 54 | test_result2: _that.data.test_result2+res+' ', 55 | }) 56 | console.log(res) 57 | }) 58 | }, 59 | }) 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, AR Fashion 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /pages/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go和小程序互操作 6 | 7 | 8 | 9 | 使用WXWebAssembly.instantiate初始化Go运行环境。 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 小程序使用OpenCV 22 | 23 | 24 | 25 | 使用WXWebAssembly.instantiate初始化OpenCV运行环境。 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /node_dev/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | 使用方法:在终端运行“node main.js”,会将../go_dev/sample.wasm压缩为../package_lesson1/assets/sample.wasm.br。 3 | */ 4 | 5 | // 第三方的wasm版本brotli压缩和解压缩,安装方法:npm install wasm-brotli 6 | var brotli = require('wasm-brotli'); 7 | // node自带的库 8 | var fs = require('fs'); 9 | // node自带的库 10 | var util = require('util'); 11 | 12 | // 将方法包装成promise风格,以使用async/await。 13 | const writeFileAsync = util.promisify(fs.writeFile); 14 | const readFileAsync = util.promisify(fs.readFile); 15 | 16 | // 压缩的示例 17 | async function compress(){ 18 | const content = Buffer.from('Hello, world!', 'utf8'); 19 | const compressedContent = await brotli.compress(content); 20 | await writeFileAsync('./hello_world.txt.br', compressedContent); 21 | 22 | console.log('compress completed') 23 | } 24 | 25 | // 解压缩的示例 26 | async function decompress(){ 27 | const compressedContent = await readFileAsync('./hello_world.txt.br'); 28 | const contenArray = await brotli.decompress(compressedContent); 29 | const content = Buffer.from(contenArray).toString('utf8'); 30 | 31 | console.log('decompress result',content) 32 | } 33 | 34 | // 压缩文件的示例 35 | async function compressFile(){ 36 | console.log('compress start...') 37 | var start = Date.now() 38 | const content = await readFileAsync('../go_dev/sample.wasm'); 39 | const compressedContent = await brotli.compress(content); 40 | await writeFileAsync('../package_lesson1/assets/sample.wasm.br', compressedContent); 41 | var end = Date.now()-start 42 | console.log('compress completed. cost',end/1000,'seconds.') 43 | } 44 | 45 | compressFile() 46 | -------------------------------------------------------------------------------- /miniprogram_npm/text-encoder/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["var utf8Encodings = [\n 'utf8',\n 'utf-8',\n 'unicode-1-1-utf-8'\n];\n\nfunction TextEncoder(encoding) {\n if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) {\n throw new RangeError('Invalid encoding type. Only utf-8 is supported');\n } else {\n this.encoding = 'utf-8';\n this.encode = function(str) {\n if (typeof str !== 'string') {\n throw new TypeError('passed argument must be of tye string');\n }\n var binstr = unescape(encodeURIComponent(str)),\n arr = new Uint8Array(binstr.length);\n const split = binstr.split('');\n for (let i = 0; i < split.length; i++) {\n arr[i] = split[i].charCodeAt(0);\n }\n return arr;\n };\n }\n}\n\nfunction TextDecoder(encoding) {\n if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) {\n throw new RangeError('Invalid encoding type. Only utf-8 is supported');\n }\n else {\n this.encoding = 'utf-8';\n this.decode = function (view, options) {\n if (typeof view === 'undefined') {\n return '';\n }\n\n var stream = (typeof options !== 'undefined' && stream in options) ? options.stream : false;\n if (typeof stream !== 'boolean') {\n throw new TypeError('stream option must be boolean');\n }\n\n if (!ArrayBuffer.isView(view)) {\n throw new TypeError('passed argument must be an array buffer view');\n } else {\n var arr = new Uint8Array(view.buffer, view.byteOffset, view.byteLength),\n charArr = new Array(arr.length);\n for (let i = 0; i < arr.length; i++) {\n charArr[i] = String.fromCharCode(arr[i]);\n }\n return decodeURIComponent(escape(charArr.join('')));\n }\n }\n }\n}\n\nmodule.exports = {\n TextEncoder,\n TextDecoder,\n};\n"]} -------------------------------------------------------------------------------- /package_lesson1/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Go调用小程序的函数 5 | 6 | 7 | {{notice}} 8 | 9 | 10 | 11 | 12 | 13 | 小程序调用Go的函数,每次点击增加次数。 14 | 15 | 16 | 点击了{{test_result1}}次 17 | 18 | 19 | 20 | 21 | 22 | 小程序调用Go的函数,Go在等待2秒后回调小程序。 23 | 24 | 25 | 26 | 表单 27 | 28 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "project config file", 3 | "packOptions": { 4 | "ignore": [ 5 | { 6 | "type": "folder", 7 | "value": "screenshot" 8 | }, 9 | { 10 | "type": "folder", 11 | "value": "node_dev" 12 | }, 13 | { 14 | "type": "folder", 15 | "value": "go_dev" 16 | }, 17 | { 18 | "type": "folder", 19 | "value": "opencv_dev" 20 | } 21 | ] 22 | }, 23 | "setting": { 24 | "urlCheck": false, 25 | "es6": false, 26 | "enhance": false, 27 | "postcss": false, 28 | "preloadBackgroundData": false, 29 | "minified": false, 30 | "newFeature": true, 31 | "coverView": true, 32 | "nodeModules": true, 33 | "autoAudits": false, 34 | "showShadowRootInWxmlPanel": true, 35 | "scopeDataCheck": false, 36 | "uglifyFileName": false, 37 | "checkInvalidKey": true, 38 | "checkSiteMap": true, 39 | "uploadWithSourceMap": true, 40 | "compileHotReLoad": false, 41 | "lazyloadPlaceholderEnable": false, 42 | "useMultiFrameRuntime": true, 43 | "useApiHook": true, 44 | "useApiHostProcess": true, 45 | "babelSetting": { 46 | "ignore": [], 47 | "disablePlugins": [], 48 | "outputPath": "" 49 | }, 50 | "enableEngineNative": false, 51 | "useIsolateContext": false, 52 | "userConfirmedBundleSwitch": false, 53 | "packNpmManually": false, 54 | "packNpmRelationList": [], 55 | "minifyWXSS": false, 56 | "disableUseStrict": false, 57 | "minifyWXML": false, 58 | "showES6CompileOption": false, 59 | "useCompilerPlugins": false 60 | }, 61 | "compileType": "miniprogram", 62 | "libVersion": "2.16.1", 63 | "appid": "wxb4cdacc73d2d71c7", 64 | "projectname": "WeChat-WebAR", 65 | "debugOptions": { 66 | "hidedInDevtools": [] 67 | }, 68 | "isGameTourist": false, 69 | "simulatorType": "wechat", 70 | "simulatorPluginLibVersion": {}, 71 | "condition": { 72 | "search": { 73 | "list": [] 74 | }, 75 | "conversation": { 76 | "list": [] 77 | }, 78 | "game": { 79 | "currentL": -1, 80 | "list": [] 81 | }, 82 | "miniprogram": { 83 | "list": [] 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /miniprogram_npm/text-encoder/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1638331248814, function(require, module, exports) { 8 | var utf8Encodings = [ 9 | 'utf8', 10 | 'utf-8', 11 | 'unicode-1-1-utf-8' 12 | ]; 13 | 14 | function TextEncoder(encoding) { 15 | if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) { 16 | throw new RangeError('Invalid encoding type. Only utf-8 is supported'); 17 | } else { 18 | this.encoding = 'utf-8'; 19 | this.encode = function(str) { 20 | if (typeof str !== 'string') { 21 | throw new TypeError('passed argument must be of tye string'); 22 | } 23 | var binstr = unescape(encodeURIComponent(str)), 24 | arr = new Uint8Array(binstr.length); 25 | const split = binstr.split(''); 26 | for (let i = 0; i < split.length; i++) { 27 | arr[i] = split[i].charCodeAt(0); 28 | } 29 | return arr; 30 | }; 31 | } 32 | } 33 | 34 | function TextDecoder(encoding) { 35 | if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) { 36 | throw new RangeError('Invalid encoding type. Only utf-8 is supported'); 37 | } 38 | else { 39 | this.encoding = 'utf-8'; 40 | this.decode = function (view, options) { 41 | if (typeof view === 'undefined') { 42 | return ''; 43 | } 44 | 45 | var stream = (typeof options !== 'undefined' && stream in options) ? options.stream : false; 46 | if (typeof stream !== 'boolean') { 47 | throw new TypeError('stream option must be boolean'); 48 | } 49 | 50 | if (!ArrayBuffer.isView(view)) { 51 | throw new TypeError('passed argument must be an array buffer view'); 52 | } else { 53 | var arr = new Uint8Array(view.buffer, view.byteOffset, view.byteLength), 54 | charArr = new Array(arr.length); 55 | for (let i = 0; i < arr.length; i++) { 56 | charArr[i] = String.fromCharCode(arr[i]); 57 | } 58 | return decodeURIComponent(escape(charArr.join(''))); 59 | } 60 | } 61 | } 62 | } 63 | 64 | module.exports = { 65 | TextEncoder, 66 | TextDecoder, 67 | }; 68 | 69 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 70 | return __REQUIRE__(1638331248814); 71 | })() 72 | //miniprogram-npm-outsideDeps=[] 73 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /opencv_dev/lesson2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenCV WebAssembly 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
图像处理
17 |
18 | 19 | 20 |
21 | 灰度化 23 | 边缘检测 25 | 特征点检测 27 |
28 |
29 |
30 |
31 |
32 | 33 | 37 | 38 | 39 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /go_dev/lesson1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Go WebAssembly 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
Go调用小程序的函数
18 |
19 |
20 |
21 |
22 |
23 |
24 | 25 |
26 |
小程序调用Go的函数,每次点击增加次数。
27 |
28 |
29 | 点击了次 30 | 点击1 31 |
32 |
33 |
34 |
35 |
小程序调用Go的函数,Go在等待2秒后回调小程序。
36 |
37 |
38 |
表单
39 |
40 | 46 | 52 |
53 | 请等待2秒 54 |
55 | 点击2 57 | 58 |
59 | 60 |
61 | 62 |
63 |
64 |
65 |
66 | 67 | 68 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /package_lesson2/index.js: -------------------------------------------------------------------------------- 1 | // 画布 2 | const canvas1 = 'canvas1' 3 | // 示例图片 4 | const sampleImage1 = './assets/sampleImage1.jpg' 5 | // 画布最大宽度 6 | const maxCanvasWidth = 375 7 | // wasm路径 8 | global.wasm_url = '/package_lesson2/assets/opencv3.4.16.wasm.br' 9 | // opencv_exec.js会从global.wasm_url获取wasm路径 10 | let cv = require('./assets/opencv_exec.js'); 11 | 12 | Page({ 13 | // 画布的dom对象 14 | canvasDom: null, 15 | data: { 16 | canvas1Width: 375, 17 | canvas1Height: 150, 18 | // 示例图片 19 | sampleImage1Url: sampleImage1, 20 | }, 21 | onReady() { 22 | // 可见的画布 23 | this.initCanvas(canvas1) 24 | }, 25 | // 获取画布 26 | initCanvas(canvasId) { 27 | var _that = this; 28 | wx.createSelectorQuery() 29 | .select('#' + canvasId) 30 | .fields({ node: true, size: true }) 31 | .exec((res) => { 32 | const canvas2d = res[0].node; 33 | // 设置画布的宽度和高度 34 | canvas2d.width = res[0].width; 35 | canvas2d.height = res[0].height; 36 | _that.canvasDom = canvas2d 37 | }); 38 | }, 39 | // 获取图像数据和调整图像大小 40 | getImageData(image,offscreenCanvas) { 41 | var _that = this 42 | // const ctx = wx.createCanvasContext(canvasId); 43 | var canvasWidth = image.width; 44 | if (canvasWidth > maxCanvasWidth) { 45 | canvasWidth = maxCanvasWidth; 46 | } 47 | // canvas Height 48 | var canvasHeight = Math.floor(canvasWidth * (image.height / image.width)); 49 | // 离屏画布的宽度和高度不能小于图像的 50 | offscreenCanvas.width = canvasWidth; 51 | offscreenCanvas.height = canvasHeight; 52 | // draw image on canvas 53 | var ctx = offscreenCanvas.getContext('2d') 54 | ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight); 55 | // get image data from canvas 56 | var imgData = ctx.getImageData(0, 0, canvasWidth, canvasHeight); 57 | 58 | return imgData 59 | }, 60 | // 创建图像对象 61 | async createImageElement(imgUrl) { 62 | var _that = this 63 | // 创建2d类型的离屏画布(需要微信基础库2.16.1以上) 64 | var offscreenCanvas = wx.createOffscreenCanvas({type: '2d'}); 65 | const image = offscreenCanvas.createImage(); 66 | await new Promise(function (resolve, reject) { 67 | image.onload = resolve 68 | image.onerror = reject 69 | image.src = imgUrl 70 | }) 71 | const imageData = _that.getImageData(image,offscreenCanvas) 72 | return imageData 73 | }, 74 | imgProcess1(imageData, canvasDom) { 75 | // 读取图像 76 | let src = cv.imread(imageData); 77 | let dst = new cv.Mat(); 78 | // 灰度化 79 | cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY, 0); 80 | // 显示图像 81 | cv.imshow(canvasDom, dst); 82 | // 回收对象 83 | src.delete(); 84 | dst.delete() 85 | }, 86 | imgProcess2(imageData, canvasDom) { 87 | let src = cv.imread(imageData); 88 | let dst = new cv.Mat(); 89 | 90 | // 灰度化 91 | cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0); 92 | // 边缘检测 93 | cv.Canny(src, dst, 50, 100, 3, false); 94 | 95 | cv.imshow(canvasDom, dst); 96 | src.delete(); 97 | dst.delete() 98 | }, 99 | imgProcess3(imageData, canvasDom) { 100 | let src = cv.imread(imageData); 101 | let dst = new cv.Mat(); 102 | 103 | // 灰度化 104 | cv.cvtColor(src, src, cv.COLOR_RGBA2GRAY, 0); 105 | 106 | var orb = new cv.ORB(); 107 | var keypoints = new cv.KeyPointVector(); 108 | var descriptors = new cv.Mat(); 109 | // 特征点 110 | orb.detect(src, keypoints) 111 | // 特征点的描述因子 112 | orb.compute(src, keypoints, descriptors) 113 | // 绘制特征点 114 | cv.drawKeypoints(src, keypoints, dst) 115 | 116 | cv.imshow(canvasDom, dst); 117 | src.delete(); 118 | dst.delete() 119 | }, 120 | async btnRun1() { 121 | var _that = this; 122 | // 将图像转换为ImageData 123 | const image1Data = await _that.createImageElement(sampleImage1) 124 | // 设置画布的显示大小 125 | _that.setData({ 126 | canvas1Width: image1Data.width, 127 | canvas1Height: image1Data.height, 128 | }) 129 | _that.imgProcess1(image1Data, _that.canvasDom) 130 | }, 131 | async btnRun2() { 132 | // 同上 133 | var _that = this; 134 | const image1Data = await _that.createImageElement(sampleImage1) 135 | _that.setData({ 136 | canvas1Width: image1Data.width, 137 | canvas1Height: image1Data.height, 138 | }) 139 | _that.imgProcess2(image1Data, _that.canvasDom) 140 | }, 141 | async btnRun3() { 142 | // 同上 143 | var _that = this; 144 | const image1Data = await _that.createImageElement(sampleImage1) 145 | _that.setData({ 146 | canvas1Width: image1Data.width, 147 | canvas1Height: image1Data.height, 148 | }) 149 | _that.imgProcess3(image1Data, _that.canvasDom) 150 | }, 151 | }) 152 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 更新日志 2 | 3 | | 日期   | 内容 | 4 | | -- | -- | 5 | | 2022-08-29 | 修复:如果微信开发者工具设置“将JS编译成ES5”,则opencv_exec.js文件提示“Module未定义”的问题。 | 6 | | 2021-12-05 | 新增:微信小程序运行OpenCV的示例。也包含网页运行OpenCV的示例。 | 7 | | 2021-12-03 | 新增:微信小程序运行Go语言的示例。也包含网页运行Go语言的示例。 | 8 | 9 | ## 介绍 10 | 11 | 1、将Go语言编译为WebAssembly,使用微信小程序的WXWebAssembly功能运行WebAssembly。 12 | 13 | 因为Go语言能快速开发WebAssembly,所以使用Go语言能为小程序快速开发WebAssembly。 14 | 15 | 2、在微信小程序中使用WebAssembly版的OpenCV,让我们开发图像视觉。 16 | 17 | [WXWebAssembly官方文档](https://developers.weixin.qq.com/miniprogram/dev/framework/performance/wasm.html) 18 | 19 | ## 网页版 20 | 21 | 在线预览,和小程序版使用的是相同的wasm文件。 22 | 23 | 运行Go 24 | 25 | [https://sanyuered.github.io/WeChat-MiniProgram-AR-WASM/go_dev/lesson1.html](https://sanyuered.github.io/WeChat-MiniProgram-AR-WASM/go_dev/lesson1.html) 26 | 27 | 运行OpenCV 28 | 29 | [https://sanyuered.github.io/WeChat-MiniProgram-AR-WASM/opencv_dev/lesson2.html](https://sanyuered.github.io/WeChat-MiniProgram-AR-WASM/opencv_dev/lesson2.html) 30 | 31 | ## 小程序版 32 | 33 | 首页 34 | 35 | ![avatar](screenshot/1.jpg) 36 | 37 | ## Go调用小程序的函数 38 | 39 | Go运行时,会调用小程序的console.log()输出信息。 40 | 41 | ![avatar](screenshot/2-1.jpg) 42 | 43 | 在调试窗口的Console面板查看 44 | 45 | ![avatar](screenshot/2-2.jpg) 46 | 47 | ## 小程序调用Go的函数 48 | 49 | 每次点击按钮,次数会增加1。 50 | 51 | ![avatar](screenshot/2-3.jpg) 52 | 53 | ## 小程序调用Go的函数, Go回调小程序。 54 | 55 | 输入内容是可编辑的自定义文本。 56 | 57 | ![avatar](screenshot/3-1.jpg) 58 | 59 | 点击按钮后,输出内容会返回输入的自定义文本。Go语言一侧使用了time.Sleep()和协程,模拟了等待2秒的异步方法调用。 60 | 61 | ![avatar](screenshot/3-2.jpg) 62 | 63 | ## 小程序调用OpenCV的函数。 64 | 65 | 灰度化 66 | 67 | ![avatar](screenshot/4-1.jpg) 68 | 69 | 边缘检测 70 | 71 | ![avatar](screenshot/4-2.jpg) 72 | 73 | 特征点检测 74 | 75 | ![avatar](screenshot/4-3.jpg) 76 | 77 | ## 目录结构 78 | 79 | /:小程序一侧的源代码 80 | 81 | /go_dev:Go一侧的源代码 82 | 83 | /node_dev: Node一侧的源代码 84 | 85 | /miniprogram_npm:微信开发者工具将npm包text-encoder编译后的文件。 86 | 87 | /project.config.json:小程序打包时,排除node_dev、go_dev等目录。 88 | 89 | ```javascript 90 | "packOptions": { 91 | "ignore": [ 92 | { 93 | "type": "folder", 94 | "value": "screenshot" 95 | }, 96 | { 97 | "type": "folder", 98 | "value": "node_dev" 99 | }, 100 | { 101 | "type": "folder", 102 | "value": "go_dev" 103 | } 104 | ] 105 | }, 106 | 107 | ``` 108 | 109 | ## Go一侧的源代码 110 | 111 | Go一侧和网页版wasm的开发完全一样,没有区别。但不要调用fmt.Println()。 112 | 113 | ```javascript 114 | func main() { 115 | // 创建通道 116 | channel := make(chan int) 117 | // Go调用js的console.log()方法,在开发者工具的Consol面板中查看。 118 | console := js.Global().Get("console") 119 | console.Call("log", "hello, world!") 120 | // 其它代码省略 121 | 122 | // 通道阻塞了main()方法 123 | <-channel 124 | 125 | ``` 126 | 127 | 网页端运行Go:/go_dev/lesson1.html 128 | 129 | ```javascript 130 | const go = new global.Go(); 131 | try { 132 | const result = await WebAssembly.instantiateStreaming(fetch(wasm_url), go.importObject) 133 | // 运行go程序的main()方法 134 | await go.run(result.instance); 135 | // 其它代码省略 136 | } catch (err) { 137 | console.error('initGo', err) 138 | } 139 | 140 | ``` 141 | 142 | 小程序端运行Go:/package_lesson1/index.js 143 | 144 | 注意:wasm_url必须是小程序打包的文件,不能是网络文件和下载后保存的文件。wasm文件会占用小程序打包后的大小,所以小程序每个分包中的wasm文件不能大于2MB。 145 | 146 | ```javascript 147 | const go = new global.Go(); 148 | try { 149 | const result = await WXWebAssembly.instantiate(wasm_url, go.importObject) 150 | // 运行go程序的main()方法 151 | await go.run(result.instance); 152 | // 其它代码省略 153 | } catch (err) { 154 | console.error('initGo', err) 155 | } 156 | 157 | ``` 158 | 159 | ## Node一侧的源代码 160 | 161 | Node一侧是将.wasm压缩为.wasm.br文件。小程序每个分包限制大小为2MB,但支持.wasm.br压缩文件。使用brotli压缩.wasm文件,能压缩到原始.wasm文件大小的20%至25%。 162 | 163 | 但是brotli压缩速度很慢。1.4MB的.wasm,压缩为.wasm.br,花费时间约6秒。文件更大的.wasm,压缩时间更长,可能花费几分钟。 164 | 165 | ```javascript 166 | // 第三方的wasm版本brotli压缩和解压缩 167 | var brotli = require('wasm-brotli'); 168 | // 压缩文件的示例 169 | async function compressFile(){ 170 | const content = await readFileAsync('../go_dev/sample.wasm'); 171 | const compressedContent = await brotli.compress(content); 172 | // 其它代码省略 173 | } 174 | 175 | ``` 176 | 177 | ## 技术难点 178 | 179 | ### 1、小程序分包大小不能超过2MB,Go的Hello World示例编译为wasm文件大小就超过2MB了。 180 | 181 | 解决方法:1、使用brotli工具压缩wasm,压缩率约22%,理论上支持最大8MB的未压缩wasm。 182 | 183 | ### 2、如何修改wasm_exec.js文件? 184 | 185 | 解决方法:修改的位置使用了变量IsWechat,判断是否是小程序环境。具体修改请见源代码。 186 | ```javascript 187 | const IsWechat = true; 188 | ``` 189 | 190 | ### 3、小程序没有crypto.getRandomValues()方法。 191 | 解决方法: 192 | ```javascript 193 | if (!global.crypto) { 194 | global.crypto = { 195 | getRandomValues(b) { 196 | let byteRange = 256; 197 | for (var i = 0; i < b.length; i++) { 198 | b[i] = Math.floor(byteRange * Math.random()); 199 | } 200 | }, 201 | }; 202 | } 203 | ``` 204 | 205 | ### 4、小程序没有performance.now()方法。 206 | 解决方法: 207 | ```javascript 208 | if (!global.performance) { 209 | global.performance = { 210 | now() { 211 | return Date.now() 212 | }, 213 | }; 214 | } 215 | ``` 216 | 217 | ### 5、小程序没有TextDecoder和TextDecoder对象。 218 | 解决方法: 219 | ```javascript 220 | if (!global.TextEncoder) { 221 | global.TextEncoder = require("text-encoder").TextEncoder; 222 | } 223 | if (!global.TextDecoder) { 224 | global.TextDecoder = require("text-encoder").TextDecoder; 225 | } 226 | ``` 227 | 228 | ## Go和小程序互操作 229 | 230 | js.Global() :获取js运行环境的global对象。 231 | ```javascript 232 | console := js.Global().Get("console") 233 | ``` 234 | 235 | js.ValueOf :Go对象的值转换为js对象。 236 | ```javascript 237 | func addTotal(this js.Value, args []js.Value) interface{} { 238 | // 其它省略 239 | return js.ValueOf(totalNum) 240 | } 241 | ``` 242 | 243 | js.Value :js对象的值转换为Go对象。 244 | ```javascript 245 | func asyncAndCallbak(this js.Value, args []js.Value) interface{} { 246 | // js输入参数 247 | input := args[0].String() 248 | // js回调函数 249 | callback := args[1] 250 | // 其它省略 251 | } 252 | ``` 253 | 254 | js.FuncOf :Go函数转换为js函数。 255 | ```javascript 256 | // js调用Go的addTotal()方法 257 | js.Global().Set("addTotal", js.FuncOf(addTotal)) 258 | ``` 259 | 260 | Object.Call(function1,arg1,arg2) :在js对象上调用方法function1,输入参数arg1、arg2等。 261 | ```javascript 262 | // Go调用js的console.log()方法,在开发者工具的Consol面板中查看。 263 | console := js.Global().Get("console") 264 | console.Call("log", "hello, world!") 265 | ``` 266 | 267 | function1.Invoke(arg1,arg2) :调用方法function1,输入参数arg1、arg2等。 268 | ```javascript 269 | func asyncAndCallbak(this js.Value, args []js.Value) interface{} { 270 | // js回调函数 271 | callback := args[1] 272 | // 运行js回调函数 273 | callback.Invoke(result) 274 | // 其它省略 275 | } 276 | ``` 277 | 278 | Object.Get(prop1) :获取js对象的子对象、属性、方法等。 279 | ```javascript 280 | console := js.Global().Get("console") 281 | ``` 282 | 283 | 注意:使用js.Global().Get("console"),需要在小程序基础类库的global对象上,增加console对象。 284 | ```javascript 285 | async onReady() { 286 | // 在小程序基础类库的global对象上,增加console对象。 287 | global.console = console 288 | // 使用小程序类库的WXWebAssembly,初始化Go运行环境。 289 | await this.initGo() 290 | }, 291 | ``` 292 | 293 | Object.Set(prop1,value1):设置js对象的子对象、属性、方法等。 294 | ```javascript 295 | // 在js运行环境,设置Go的addTotal()方法 296 | js.Global().Set("addTotal", js.FuncOf(addTotal)) 297 | ``` 298 | 299 | 300 | ## Go和JavaScript的变量类型 301 | 302 | ```javascript 303 | | Go | JavaScript | 304 | | ---------------------- | ---------------------- | 305 | | js.Value | [its value] | 306 | | js.Func | function | 307 | | nil | null | 308 | | bool | boolean | 309 | | integers and floats | number | 310 | | string | string | 311 | | []interface{} | new array | 312 | | map[string]interface{} | new object | 313 | 314 | ``` 315 | 316 | ## 已知问题 317 | 318 | ### Q1:syscall/js: call of Value.Invoke on undefined 319 | 320 | A:使用fmt.Println("hello,world")会发生该错误,但使用fmt.Sprintf("%d", 123) 正常。不要使用fmt.Println()方法,而使用console.log()方法向小程序输出调试信息。 321 | 322 | ### Q2:LinkError: WebAssembly.instantiate(): Import #4 module="go" 323 | 324 | A: wasm_exec.js中的方法有的能去掉,有的不能去掉。 如果修改wasm_exec.js,则importObject.go对象中的所有方法名称需要保留。 325 | 326 | ### Q3:从Go调用小程序的wx.showModal({title:"123"})等API发生错误 327 | 328 | A:如果运行wx := js.Global().Get("wx");wx.Call("showModal", "");,则可以显示一个空白的对话框。Go可以调用小程序API,问题出在wx.Call("showModal", object)的输入参数是Object类型。 329 | 330 | 在网页端WebAssembly,Go能正常传递Object给网页。 331 | 332 | 在小程序端WebAssembly,Go无法正常传递Object给小程序。将方法的输入参数从Object类型暂时换成其他变量类型。 333 | 334 | ### Q4:作者的wasm_exec.js与读者的Go版本不匹配 335 | 336 | A:作者的wasm_exec.js来自 “/Go安装目录/misc/wasm/wasm_exec.js”,版本号是1.16.3。如果与作者的Go版本号不同,则可以根据“\package_lesson1\assets\wasm_exec.js”文件中的变量“IsWechat”,修改不同版本号的wasm_exec.js文件中6处代码即可。 -------------------------------------------------------------------------------- /style/weui.wxss: -------------------------------------------------------------------------------- 1 | /*! 2 | * WeUI v1.1.1 (https://github.com/weui/weui-wxss) 3 | * Copyright 2017 Tencent, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | page{line-height:1.6;font-family:-apple-system-font,Helvetica Neue,sans-serif}icon{vertical-align:middle}.weui-cells{position:relative;margin-top:1.17647059em;background-color:#fff;line-height:1.41176471;font-size:17px}.weui-cells:before{top:0;border-top:1rpx solid #d9d9d9}.weui-cells:after,.weui-cells:before{content:" ";position:absolute;left:0;right:0;height:1px;color:#d9d9d9}.weui-cells:after{bottom:0;border-bottom:1rpx solid #d9d9d9}.weui-cells__title{margin-top:.77em;margin-bottom:.3em;padding-left:15px;padding-right:15px;color:#999;font-size:14px}.weui-cells_after-title{margin-top:0}.weui-cells__tips{margin-top:.3em;color:#999;padding-left:15px;padding-right:15px;font-size:14px}.weui-cell{padding:10px 15px;position:relative;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-cell:before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1rpx solid #d9d9d9;color:#d9d9d9;left:15px}.weui-cell:first-child:before{display:none}.weui-cell_active{background-color:#ececec}.weui-cell_primary{-webkit-box-align:start;-webkit-align-items:flex-start;align-items:flex-start}.weui-cell__bd{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-cell__ft{text-align:right;color:#999}.weui-cell_access{color:inherit}.weui-cell__ft_in-access{padding-right:13px;position:relative}.weui-cell__ft_in-access:after{content:" ";display:inline-block;height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c8c8cd;border-style:solid;-webkit-transform:matrix(.71,.71,-.71,.71,0,0);transform:matrix(.71,.71,-.71,.71,0,0);position:relative;top:-2px;position:absolute;top:50%;margin-top:-4px;right:2px}.weui-cell_link{color:#586c94;font-size:14px}.weui-cell_link:active{background-color:#ececec}.weui-cell_link:first-child:before{display:block}.weui-icon-radio{margin-left:3.2px;margin-right:3.2px}.weui-icon-checkbox_circle,.weui-icon-checkbox_success{margin-left:4.6px;margin-right:4.6px}.weui-check__label:active{background-color:#ececec}.weui-check{position:absolute;left:-9999px}.weui-check__hd_in-checkbox{padding-right:.35em}.weui-cell__ft_in-radio{padding-left:.35em}.weui-cell_input{padding-top:0;padding-bottom:0}.weui-label{width:105px;word-wrap:break-word;word-break:break-all}.weui-input{height:2.58823529em;min-height:2.58823529em;line-height:2.58823529em}.weui-toptips{position:fixed;-webkit-transform:translateZ(0);transform:translateZ(0);top:0;left:0;right:0;padding:5px;font-size:14px;text-align:center;color:#fff;z-index:5000;word-wrap:break-word;word-break:break-all}.weui-toptips_warn{background-color:#e64340}.weui-textarea{display:block;width:100%}.weui-textarea-counter{color:#b2b2b2;text-align:right}.weui-cell_warn,.weui-textarea-counter_warn{color:#e64340}.weui-form-preview{position:relative;background-color:#fff}.weui-form-preview:before{top:0;border-top:1rpx solid #d9d9d9}.weui-form-preview:after,.weui-form-preview:before{content:" ";position:absolute;left:0;right:0;height:1px;color:#d9d9d9}.weui-form-preview:after{bottom:0;border-bottom:1rpx solid #d9d9d9}.weui-form-preview__value{font-size:14px}.weui-form-preview__value_in-hd{font-size:26px}.weui-form-preview__hd{position:relative;padding:10px 15px;text-align:right;line-height:2.5em}.weui-form-preview__hd:after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1rpx solid #d9d9d9;color:#d9d9d9;left:15px}.weui-form-preview__bd{padding:10px 15px;font-size:.9em;text-align:right;color:#999;line-height:2}.weui-form-preview__ft{position:relative;line-height:50px;display:-webkit-box;display:-webkit-flex;display:flex}.weui-form-preview__ft:after{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1rpx solid #d5d5d6;color:#d5d5d6}.weui-form-preview__item{overflow:hidden}.weui-form-preview__label{float:left;margin-right:1em;min-width:4em;color:#999;text-align:justify;text-align-last:justify}.weui-form-preview__value{display:block;overflow:hidden;word-break:normal;word-wrap:break-word}.weui-form-preview__btn{position:relative;display:block;-webkit-box-flex:1;-webkit-flex:1;flex:1;color:#3cc51f;text-align:center}.weui-form-preview__btn:after{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1rpx solid #d5d5d6;color:#d5d5d6}.weui-form-preview__btn:first-child:after{display:none}.weui-form-preview__btn_active{background-color:#eee}.weui-form-preview__btn_default{color:#999}.weui-form-preview__btn_primary{color:#0bb20c}.weui-cell_select{padding:0}.weui-select{position:relative;padding-left:15px;padding-right:30px;height:2.58823529em;min-height:2.58823529em;line-height:2.58823529em;border-right:1rpx solid #d9d9d9}.weui-select:before{content:" ";display:inline-block;height:6px;width:6px;border-width:2px 2px 0 0;border-color:#c8c8cd;border-style:solid;-webkit-transform:matrix(.71,.71,-.71,.71,0,0);transform:matrix(.71,.71,-.71,.71,0,0);position:relative;top:-2px;position:absolute;top:50%;right:15px;margin-top:-4px}.weui-select_in-select-after{padding-left:0}.weui-cell__bd_in-select-before,.weui-cell__hd_in-select-after{padding-left:15px}.weui-cell_vcode{padding-right:0}.weui-vcode-btn,.weui-vcode-img{margin-left:5px;height:2.58823529em;vertical-align:middle}.weui-vcode-btn{display:inline-block;padding:0 .6em 0 .7em;border-left:1px solid #e5e5e5;line-height:2.58823529em;font-size:17px;color:#3cc51f;white-space:nowrap}.weui-vcode-btn:active{color:#52a341}.weui-cell_switch{padding-top:6px;padding-bottom:6px}.weui-uploader__hd{display:-webkit-box;display:-webkit-flex;display:flex;padding-bottom:10px;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-uploader__title{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-uploader__info{color:#b2b2b2}.weui-uploader__bd{margin-bottom:-4px;margin-right:-9px;overflow:hidden}.weui-uploader__file{float:left;margin-right:9px;margin-bottom:9px}.weui-uploader__img{display:block;width:79px;height:79px}.weui-uploader__file_status{position:relative}.weui-uploader__file_status:before{content:" ";position:absolute;top:0;right:0;bottom:0;left:0;background-color:rgba(0,0,0,.5)}.weui-uploader__file-content{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);color:#fff}.weui-uploader__input-box{float:left;position:relative;margin-right:9px;margin-bottom:9px;width:77px;height:77px;border:1px solid #d9d9d9}.weui-uploader__input-box:after,.weui-uploader__input-box:before{content:" ";position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background-color:#d9d9d9}.weui-uploader__input-box:before{width:2px;height:39.5px}.weui-uploader__input-box:after{width:39.5px;height:2px}.weui-uploader__input-box:active{border-color:#999}.weui-uploader__input-box:active:after,.weui-uploader__input-box:active:before{background-color:#999}.weui-uploader__input{position:absolute;z-index:1;top:0;left:0;width:100%;height:100%;opacity:0}.weui-article{padding:20px 15px;font-size:15px}.weui-article__section{margin-bottom:1.5em}.weui-article__h1{font-size:18px;font-weight:400;margin-bottom:.9em}.weui-article__h2{font-size:16px;font-weight:400;margin-bottom:.34em}.weui-article__h3{font-weight:400;font-size:15px;margin-bottom:.34em}.weui-article__p{margin:0 0 .8em}.weui-msg{padding-top:36px;text-align:center}.weui-msg__link{display:inline;color:#586c94}.weui-msg__icon-area{margin-bottom:30px}.weui-msg__text-area{margin-bottom:25px;padding:0 20px}.weui-msg__title{margin-bottom:5px;font-weight:400;font-size:20px}.weui-msg__desc{font-size:14px;color:#999}.weui-msg__opr-area{margin-bottom:25px}.weui-msg__extra-area{margin-bottom:15px;font-size:14px;color:#999}@media screen and (min-height:438px){.weui-msg__extra-area{position:fixed;left:0;bottom:0;width:100%;text-align:center}}.weui-flex{display:-webkit-box;display:-webkit-flex;display:flex}.weui-flex__item{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-btn{margin-top:15px}.weui-btn:first-child{margin-top:0}.weui-btn-area{margin:1.17647059em 15px .3em}.weui-agree{display:block;padding:.5em 15px;font-size:13px}.weui-agree__text{color:#999}.weui-agree__link{display:inline;color:#586c94}.weui-agree__checkbox{position:absolute;left:-9999px}.weui-agree__checkbox-icon{position:relative;top:2px;display:inline-block;border:1px solid #d1d1d1;background-color:#fff;border-radius:3px;width:11px;height:11px}.weui-agree__checkbox-icon-check{position:absolute;top:1px;left:1px}.weui-footer{color:#999;font-size:14px;text-align:center}.weui-footer_fixed-bottom{position:fixed;bottom:.52em;left:0;right:0}.weui-footer__links{font-size:0}.weui-footer__link{display:inline-block;vertical-align:top;margin:0 .62em;position:relative;font-size:14px;color:#586c94}.weui-footer__link:before{content:" ";position:absolute;left:0;top:0;width:1px;bottom:0;border-left:1rpx solid #c7c7c7;color:#c7c7c7;left:-.65em;top:.36em;bottom:.36em}.weui-footer__link:first-child:before{display:none}.weui-footer__text{padding:0 .34em;font-size:12px}.weui-grids{border-top:1rpx solid #d9d9d9;border-left:1rpx solid #d9d9d9;overflow:hidden}.weui-grid{position:relative;float:left;padding:20px 10px;width:33.33333333%;box-sizing:border-box;border-right:1rpx solid #d9d9d9;border-bottom:1rpx solid #d9d9d9}.weui-grid_active{background-color:#ececec}.weui-grid__icon{display:block;width:28px;height:28px;margin:0 auto}.weui-grid__label{margin-top:5px;display:block;text-align:center;color:#000;font-size:14px;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.weui-loading{margin:0 5px;width:20px;height:20px;display:inline-block;vertical-align:middle;-webkit-animation:a 1s steps(12) infinite;animation:a 1s steps(12) infinite;background:transparent url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiB2aWV3Qm94PSIwIDAgMTAwIDEwMCI+PHBhdGggZmlsbD0ibm9uZSIgZD0iTTAgMGgxMDB2MTAwSDB6Ii8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTlFOUU5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAgLTMwKSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iIzk4OTY5NyIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgzMCAxMDUuOTggNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjOUI5OTlBIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDYwIDc1Ljk4IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0EzQTFBMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSg5MCA2NSA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNBQkE5QUEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoMTIwIDU4LjY2IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0IyQjJCMiIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgxNTAgNTQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjQkFCOEI5IiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKDE4MCA1MCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDMkMwQzEiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTE1MCA0NS45OCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNDQkNCQ0IiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTEyMCA0MS4zNCA2NSkiLz48cmVjdCB3aWR0aD0iNyIgaGVpZ2h0PSIyMCIgeD0iNDYuNSIgeT0iNDAiIGZpbGw9IiNEMkQyRDIiIHJ4PSI1IiByeT0iNSIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDM1IDY1KSIvPjxyZWN0IHdpZHRoPSI3IiBoZWlnaHQ9IjIwIiB4PSI0Ni41IiB5PSI0MCIgZmlsbD0iI0RBREFEQSIgcng9IjUiIHJ5PSI1IiB0cmFuc2Zvcm09InJvdGF0ZSgtNjAgMjQuMDIgNjUpIi8+PHJlY3Qgd2lkdGg9IjciIGhlaWdodD0iMjAiIHg9IjQ2LjUiIHk9IjQwIiBmaWxsPSIjRTJFMkUyIiByeD0iNSIgcnk9IjUiIHRyYW5zZm9ybT0icm90YXRlKC0zMCAtNS45OCA2NSkiLz48L3N2Zz4=) no-repeat;background-size:100%}.weui-loading.weui-loading_transparent{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120' viewBox='0 0 100 100'%3E%3Cpath fill='none' d='M0 0h100v100H0z'/%3E%3Crect xmlns='http://www.w3.org/2000/svg' width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.56)' rx='5' ry='5' transform='translate(0 -30)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.5)' rx='5' ry='5' transform='rotate(30 105.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.43)' rx='5' ry='5' transform='rotate(60 75.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.38)' rx='5' ry='5' transform='rotate(90 65 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.32)' rx='5' ry='5' transform='rotate(120 58.66 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.28)' rx='5' ry='5' transform='rotate(150 54.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.25)' rx='5' ry='5' transform='rotate(180 50 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.2)' rx='5' ry='5' transform='rotate(-150 45.98 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.17)' rx='5' ry='5' transform='rotate(-120 41.34 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.14)' rx='5' ry='5' transform='rotate(-90 35 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.1)' rx='5' ry='5' transform='rotate(-60 24.02 65)'/%3E%3Crect width='7' height='20' x='46.5' y='40' fill='rgba(255,255,255,.03)' rx='5' ry='5' transform='rotate(-30 -5.98 65)'/%3E%3C/svg%3E")}@-webkit-keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes a{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.weui-badge{display:inline-block;padding:.15em .4em;min-width:8px;border-radius:18px;background-color:#e64340;color:#fff;line-height:1.2;text-align:center;font-size:12px;vertical-align:middle}.weui-badge_dot{padding:.4em;min-width:0}.weui-loadmore{width:65%;margin:1.5em auto;line-height:1.6em;font-size:14px;text-align:center}.weui-loadmore__tips{display:inline-block;vertical-align:middle}.weui-loadmore_line{border-top:1px solid #e5e5e5;margin-top:2.4em}.weui-loadmore__tips_in-line{position:relative;top:-.9em;padding:0 .55em;background-color:#fff;color:#999}.weui-loadmore__tips_in-dot{position:relative;padding:0 .16em;width:4px;height:1.6em}.weui-loadmore__tips_in-dot:before{content:" ";position:absolute;top:50%;left:50%;margin-top:-1px;margin-left:-2px;width:4px;height:4px;border-radius:50%;background-color:#e5e5e5}.weui-panel{background-color:#fff;margin-top:10px;position:relative;overflow:hidden}.weui-panel:first-child{margin-top:0}.weui-panel:before{top:0;border-top:1rpx solid #e5e5e5}.weui-panel:after,.weui-panel:before{content:" ";position:absolute;left:0;right:0;height:1px;color:#e5e5e5}.weui-panel:after{bottom:0;border-bottom:1rpx solid #e5e5e5}.weui-panel__hd{padding:14px 15px 10px;color:#999;font-size:13px;position:relative}.weui-panel__hd:after{content:" ";position:absolute;left:0;bottom:0;right:0;height:1px;border-bottom:1rpx solid #e5e5e5;color:#e5e5e5;left:15px}.weui-media-box{padding:15px;position:relative}.weui-media-box:before{content:" ";position:absolute;left:0;top:0;right:0;height:1px;border-top:1rpx solid #e5e5e5;color:#e5e5e5;left:15px}.weui-media-box:first-child:before{display:none}.weui-media-box__title{font-weight:400;font-size:17px;width:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal;word-wrap:break-word;word-break:break-all}.weui-media-box__desc{color:#999;font-size:13px;line-height:1.2;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.weui-media-box__info{margin-top:15px;padding-bottom:5px;font-size:13px;color:#cecece;line-height:1em;list-style:none;overflow:hidden}.weui-media-box__info__meta{float:left;padding-right:1em}.weui-media-box__info__meta_extra{padding-left:1em;border-left:1px solid #cecece}.weui-media-box__title_in-text{margin-bottom:8px}.weui-media-box_appmsg{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-media-box__thumb{width:100%;height:100%;vertical-align:top}.weui-media-box__hd_in-appmsg{margin-right:.8em;width:60px;height:60px;line-height:60px;text-align:center}.weui-media-box__bd_in-appmsg{-webkit-box-flex:1;-webkit-flex:1;flex:1;min-width:0}.weui-media-box_small-appmsg{padding:0}.weui-cells_in-small-appmsg{margin-top:0}.weui-cells_in-small-appmsg:before{display:none}.weui-progress{display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-align:center;-webkit-align-items:center;align-items:center}.weui-progress__bar{-webkit-box-flex:1;-webkit-flex:1;flex:1}.weui-progress__opr{margin-left:15px;font-size:0}.weui-navbar{display:-webkit-box;display:-webkit-flex;display:flex;position:absolute;z-index:500;top:0;width:100%;border-bottom:1rpx solid #ccc}.weui-navbar__item{position:relative;display:block;-webkit-box-flex:1;-webkit-flex:1;flex:1;padding:13px 0;text-align:center;font-size:0}.weui-navbar__item.weui-bar__item_on{color:#1aad19}.weui-navbar__slider{position:absolute;content:" ";left:0;bottom:0;width:6em;height:3px;background-color:#1aad19;-webkit-transition:-webkit-transform .3s;transition:-webkit-transform .3s;transition:transform .3s;transition:transform .3s,-webkit-transform .3s}.weui-navbar__title{display:inline-block;font-size:15px;max-width:8em;width:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-wrap:normal}.weui-tab{position:relative;height:100%}.weui-tab__panel{box-sizing:border-box;height:100%;padding-top:50px;overflow:auto;-webkit-overflow-scrolling:touch}.weui-search-bar{position:relative;padding:8px 10px;display:-webkit-box;display:-webkit-flex;display:flex;box-sizing:border-box;background-color:#efeff4;border-top:1rpx solid #d7d6dc;border-bottom:1rpx solid #d7d6dc}.weui-icon-search{margin-right:8px;font-size:inherit}.weui-icon-search_in-box{position:absolute;left:10px;top:7px}.weui-search-bar__text{display:inline-block;font-size:14px;vertical-align:middle}.weui-search-bar__form{position:relative;-webkit-box-flex:1;-webkit-flex:auto;flex:auto;border-radius:5px;background:#fff;border:1rpx solid #e6e6ea}.weui-search-bar__box{position:relative;padding-left:30px;padding-right:30px;width:100%;box-sizing:border-box;z-index:1}.weui-search-bar__input{height:28px;line-height:28px;font-size:14px}.weui-icon-clear{position:absolute;top:0;right:0;padding:7px 8px;font-size:0}.weui-search-bar__label{position:absolute;top:0;right:0;bottom:0;left:0;z-index:2;border-radius:3px;text-align:center;color:#9b9b9b;background:#fff;line-height:28px}.weui-search-bar__cancel-btn{margin-left:10px;line-height:28px;color:#09bb07;white-space:nowrap} -------------------------------------------------------------------------------- /go_dev/assets/wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | (() => { 6 | // Map multiple JavaScript environments to a single common API, 7 | // preferring web standards over Node.js API. 8 | // 9 | // Environments considered: 10 | // - Browsers 11 | // - Node.js 12 | // - Electron 13 | // - Parcel 14 | // - Webpack 15 | 16 | if (typeof global !== "undefined") { 17 | // global already exists 18 | } else if (typeof window !== "undefined") { 19 | window.global = window; 20 | } else if (typeof self !== "undefined") { 21 | self.global = self; 22 | } else { 23 | throw new Error("cannot export Go (neither global, window nor self is defined)"); 24 | } 25 | 26 | if (!global.require && typeof require !== "undefined") { 27 | global.require = require; 28 | } 29 | 30 | if (!global.fs && global.require) { 31 | const fs = require("fs"); 32 | if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { 33 | global.fs = fs; 34 | } 35 | } 36 | 37 | const enosys = () => { 38 | const err = new Error("not implemented"); 39 | err.code = "ENOSYS"; 40 | return err; 41 | }; 42 | 43 | if (!global.fs) { 44 | let outputBuf = ""; 45 | global.fs = { 46 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 47 | writeSync(fd, buf) { 48 | outputBuf += decoder.decode(buf); 49 | const nl = outputBuf.lastIndexOf("\n"); 50 | if (nl != -1) { 51 | console.log(outputBuf.substr(0, nl)); 52 | outputBuf = outputBuf.substr(nl + 1); 53 | } 54 | return buf.length; 55 | }, 56 | write(fd, buf, offset, length, position, callback) { 57 | if (offset !== 0 || length !== buf.length || position !== null) { 58 | callback(enosys()); 59 | return; 60 | } 61 | const n = this.writeSync(fd, buf); 62 | callback(null, n); 63 | }, 64 | chmod(path, mode, callback) { callback(enosys()); }, 65 | chown(path, uid, gid, callback) { callback(enosys()); }, 66 | close(fd, callback) { callback(enosys()); }, 67 | fchmod(fd, mode, callback) { callback(enosys()); }, 68 | fchown(fd, uid, gid, callback) { callback(enosys()); }, 69 | fstat(fd, callback) { callback(enosys()); }, 70 | fsync(fd, callback) { callback(null); }, 71 | ftruncate(fd, length, callback) { callback(enosys()); }, 72 | lchown(path, uid, gid, callback) { callback(enosys()); }, 73 | link(path, link, callback) { callback(enosys()); }, 74 | lstat(path, callback) { callback(enosys()); }, 75 | mkdir(path, perm, callback) { callback(enosys()); }, 76 | open(path, flags, mode, callback) { callback(enosys()); }, 77 | read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, 78 | readdir(path, callback) { callback(enosys()); }, 79 | readlink(path, callback) { callback(enosys()); }, 80 | rename(from, to, callback) { callback(enosys()); }, 81 | rmdir(path, callback) { callback(enosys()); }, 82 | stat(path, callback) { callback(enosys()); }, 83 | symlink(path, link, callback) { callback(enosys()); }, 84 | truncate(path, length, callback) { callback(enosys()); }, 85 | unlink(path, callback) { callback(enosys()); }, 86 | utimes(path, atime, mtime, callback) { callback(enosys()); }, 87 | }; 88 | } 89 | 90 | if (!global.process) { 91 | global.process = { 92 | getuid() { return -1; }, 93 | getgid() { return -1; }, 94 | geteuid() { return -1; }, 95 | getegid() { return -1; }, 96 | getgroups() { throw enosys(); }, 97 | pid: -1, 98 | ppid: -1, 99 | umask() { throw enosys(); }, 100 | cwd() { throw enosys(); }, 101 | chdir() { throw enosys(); }, 102 | } 103 | } 104 | 105 | if (!global.crypto && global.require) { 106 | const nodeCrypto = require("crypto"); 107 | global.crypto = { 108 | getRandomValues(b) { 109 | nodeCrypto.randomFillSync(b); 110 | }, 111 | }; 112 | } 113 | if (!global.crypto) { 114 | throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); 115 | } 116 | 117 | if (!global.performance) { 118 | global.performance = { 119 | now() { 120 | const [sec, nsec] = process.hrtime(); 121 | return sec * 1000 + nsec / 1000000; 122 | }, 123 | }; 124 | } 125 | 126 | if (!global.TextEncoder && global.require) { 127 | global.TextEncoder = require("util").TextEncoder; 128 | } 129 | if (!global.TextEncoder) { 130 | throw new Error("global.TextEncoder is not available, polyfill required"); 131 | } 132 | 133 | if (!global.TextDecoder && global.require) { 134 | global.TextDecoder = require("util").TextDecoder; 135 | } 136 | if (!global.TextDecoder) { 137 | throw new Error("global.TextDecoder is not available, polyfill required"); 138 | } 139 | 140 | // End of polyfills for common API. 141 | 142 | const encoder = new TextEncoder("utf-8"); 143 | const decoder = new TextDecoder("utf-8"); 144 | 145 | global.Go = class { 146 | constructor() { 147 | this.argv = ["js"]; 148 | this.env = {}; 149 | this.exit = (code) => { 150 | if (code !== 0) { 151 | console.warn("exit code:", code); 152 | } 153 | }; 154 | this._exitPromise = new Promise((resolve) => { 155 | this._resolveExitPromise = resolve; 156 | }); 157 | this._pendingEvent = null; 158 | this._scheduledTimeouts = new Map(); 159 | this._nextCallbackTimeoutID = 1; 160 | 161 | const setInt64 = (addr, v) => { 162 | this.mem.setUint32(addr + 0, v, true); 163 | this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); 164 | } 165 | 166 | const getInt64 = (addr) => { 167 | const low = this.mem.getUint32(addr + 0, true); 168 | const high = this.mem.getInt32(addr + 4, true); 169 | return low + high * 4294967296; 170 | } 171 | 172 | const loadValue = (addr) => { 173 | const f = this.mem.getFloat64(addr, true); 174 | if (f === 0) { 175 | return undefined; 176 | } 177 | if (!isNaN(f)) { 178 | return f; 179 | } 180 | 181 | const id = this.mem.getUint32(addr, true); 182 | return this._values[id]; 183 | } 184 | 185 | const storeValue = (addr, v) => { 186 | const nanHead = 0x7FF80000; 187 | 188 | if (typeof v === "number" && v !== 0) { 189 | if (isNaN(v)) { 190 | this.mem.setUint32(addr + 4, nanHead, true); 191 | this.mem.setUint32(addr, 0, true); 192 | return; 193 | } 194 | this.mem.setFloat64(addr, v, true); 195 | return; 196 | } 197 | 198 | if (v === undefined) { 199 | this.mem.setFloat64(addr, 0, true); 200 | return; 201 | } 202 | 203 | let id = this._ids.get(v); 204 | if (id === undefined) { 205 | id = this._idPool.pop(); 206 | if (id === undefined) { 207 | id = this._values.length; 208 | } 209 | this._values[id] = v; 210 | this._goRefCounts[id] = 0; 211 | this._ids.set(v, id); 212 | } 213 | this._goRefCounts[id]++; 214 | let typeFlag = 0; 215 | switch (typeof v) { 216 | case "object": 217 | if (v !== null) { 218 | typeFlag = 1; 219 | } 220 | break; 221 | case "string": 222 | typeFlag = 2; 223 | break; 224 | case "symbol": 225 | typeFlag = 3; 226 | break; 227 | case "function": 228 | typeFlag = 4; 229 | break; 230 | } 231 | this.mem.setUint32(addr + 4, nanHead | typeFlag, true); 232 | this.mem.setUint32(addr, id, true); 233 | } 234 | 235 | const loadSlice = (addr) => { 236 | const array = getInt64(addr + 0); 237 | const len = getInt64(addr + 8); 238 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 239 | } 240 | 241 | const loadSliceOfValues = (addr) => { 242 | const array = getInt64(addr + 0); 243 | const len = getInt64(addr + 8); 244 | const a = new Array(len); 245 | for (let i = 0; i < len; i++) { 246 | a[i] = loadValue(array + i * 8); 247 | } 248 | return a; 249 | } 250 | 251 | const loadString = (addr) => { 252 | const saddr = getInt64(addr + 0); 253 | const len = getInt64(addr + 8); 254 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 255 | } 256 | 257 | const timeOrigin = Date.now() - performance.now(); 258 | this.importObject = { 259 | go: { 260 | // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) 261 | // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported 262 | // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). 263 | // This changes the SP, thus we have to update the SP used by the imported function. 264 | 265 | // func wasmExit(code int32) 266 | "runtime.wasmExit": (sp) => { 267 | sp >>>= 0; 268 | const code = this.mem.getInt32(sp + 8, true); 269 | this.exited = true; 270 | delete this._inst; 271 | delete this._values; 272 | delete this._goRefCounts; 273 | delete this._ids; 274 | delete this._idPool; 275 | this.exit(code); 276 | }, 277 | 278 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 279 | "runtime.wasmWrite": (sp) => { 280 | sp >>>= 0; 281 | const fd = getInt64(sp + 8); 282 | const p = getInt64(sp + 16); 283 | const n = this.mem.getInt32(sp + 24, true); 284 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 285 | }, 286 | 287 | // func resetMemoryDataView() 288 | "runtime.resetMemoryDataView": (sp) => { 289 | sp >>>= 0; 290 | this.mem = new DataView(this._inst.exports.mem.buffer); 291 | }, 292 | 293 | // func nanotime1() int64 294 | "runtime.nanotime1": (sp) => { 295 | sp >>>= 0; 296 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); 297 | }, 298 | 299 | // func walltime1() (sec int64, nsec int32) 300 | "runtime.walltime1": (sp) => { 301 | sp >>>= 0; 302 | const msec = (new Date).getTime(); 303 | setInt64(sp + 8, msec / 1000); 304 | this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); 305 | }, 306 | 307 | // func scheduleTimeoutEvent(delay int64) int32 308 | "runtime.scheduleTimeoutEvent": (sp) => { 309 | sp >>>= 0; 310 | const id = this._nextCallbackTimeoutID; 311 | this._nextCallbackTimeoutID++; 312 | this._scheduledTimeouts.set(id, setTimeout( 313 | () => { 314 | this._resume(); 315 | while (this._scheduledTimeouts.has(id)) { 316 | // for some reason Go failed to register the timeout event, log and try again 317 | // (temporary workaround for https://github.com/golang/go/issues/28975) 318 | console.warn("scheduleTimeoutEvent: missed timeout event"); 319 | this._resume(); 320 | } 321 | }, 322 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 323 | )); 324 | this.mem.setInt32(sp + 16, id, true); 325 | }, 326 | 327 | // func clearTimeoutEvent(id int32) 328 | "runtime.clearTimeoutEvent": (sp) => { 329 | sp >>>= 0; 330 | const id = this.mem.getInt32(sp + 8, true); 331 | clearTimeout(this._scheduledTimeouts.get(id)); 332 | this._scheduledTimeouts.delete(id); 333 | }, 334 | 335 | // func getRandomData(r []byte) 336 | "runtime.getRandomData": (sp) => { 337 | sp >>>= 0; 338 | crypto.getRandomValues(loadSlice(sp + 8)); 339 | }, 340 | 341 | // func finalizeRef(v ref) 342 | "syscall/js.finalizeRef": (sp) => { 343 | sp >>>= 0; 344 | const id = this.mem.getUint32(sp + 8, true); 345 | this._goRefCounts[id]--; 346 | if (this._goRefCounts[id] === 0) { 347 | const v = this._values[id]; 348 | this._values[id] = null; 349 | this._ids.delete(v); 350 | this._idPool.push(id); 351 | } 352 | }, 353 | 354 | // func stringVal(value string) ref 355 | "syscall/js.stringVal": (sp) => { 356 | sp >>>= 0; 357 | storeValue(sp + 24, loadString(sp + 8)); 358 | }, 359 | 360 | // func valueGet(v ref, p string) ref 361 | "syscall/js.valueGet": (sp) => { 362 | sp >>>= 0; 363 | const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); 364 | sp = this._inst.exports.getsp() >>> 0; // see comment above 365 | storeValue(sp + 32, result); 366 | }, 367 | 368 | // func valueSet(v ref, p string, x ref) 369 | "syscall/js.valueSet": (sp) => { 370 | sp >>>= 0; 371 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 372 | }, 373 | 374 | // func valueDelete(v ref, p string) 375 | "syscall/js.valueDelete": (sp) => { 376 | sp >>>= 0; 377 | Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); 378 | }, 379 | 380 | // func valueIndex(v ref, i int) ref 381 | "syscall/js.valueIndex": (sp) => { 382 | sp >>>= 0; 383 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 384 | }, 385 | 386 | // valueSetIndex(v ref, i int, x ref) 387 | "syscall/js.valueSetIndex": (sp) => { 388 | sp >>>= 0; 389 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 390 | }, 391 | 392 | // func valueCall(v ref, m string, args []ref) (ref, bool) 393 | "syscall/js.valueCall": (sp) => { 394 | sp >>>= 0; 395 | try { 396 | const v = loadValue(sp + 8); 397 | const m = Reflect.get(v, loadString(sp + 16)); 398 | const args = loadSliceOfValues(sp + 32); 399 | const result = Reflect.apply(m, v, args); 400 | sp = this._inst.exports.getsp() >>> 0; // see comment above 401 | storeValue(sp + 56, result); 402 | this.mem.setUint8(sp + 64, 1); 403 | } catch (err) { 404 | storeValue(sp + 56, err); 405 | this.mem.setUint8(sp + 64, 0); 406 | } 407 | }, 408 | 409 | // func valueInvoke(v ref, args []ref) (ref, bool) 410 | "syscall/js.valueInvoke": (sp) => { 411 | sp >>>= 0; 412 | try { 413 | const v = loadValue(sp + 8); 414 | const args = loadSliceOfValues(sp + 16); 415 | const result = Reflect.apply(v, undefined, args); 416 | sp = this._inst.exports.getsp() >>> 0; // see comment above 417 | storeValue(sp + 40, result); 418 | this.mem.setUint8(sp + 48, 1); 419 | } catch (err) { 420 | storeValue(sp + 40, err); 421 | this.mem.setUint8(sp + 48, 0); 422 | } 423 | }, 424 | 425 | // func valueNew(v ref, args []ref) (ref, bool) 426 | "syscall/js.valueNew": (sp) => { 427 | sp >>>= 0; 428 | try { 429 | const v = loadValue(sp + 8); 430 | const args = loadSliceOfValues(sp + 16); 431 | const result = Reflect.construct(v, args); 432 | sp = this._inst.exports.getsp() >>> 0; // see comment above 433 | storeValue(sp + 40, result); 434 | this.mem.setUint8(sp + 48, 1); 435 | } catch (err) { 436 | storeValue(sp + 40, err); 437 | this.mem.setUint8(sp + 48, 0); 438 | } 439 | }, 440 | 441 | // func valueLength(v ref) int 442 | "syscall/js.valueLength": (sp) => { 443 | sp >>>= 0; 444 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 445 | }, 446 | 447 | // valuePrepareString(v ref) (ref, int) 448 | "syscall/js.valuePrepareString": (sp) => { 449 | sp >>>= 0; 450 | const str = encoder.encode(String(loadValue(sp + 8))); 451 | storeValue(sp + 16, str); 452 | setInt64(sp + 24, str.length); 453 | }, 454 | 455 | // valueLoadString(v ref, b []byte) 456 | "syscall/js.valueLoadString": (sp) => { 457 | sp >>>= 0; 458 | const str = loadValue(sp + 8); 459 | loadSlice(sp + 16).set(str); 460 | }, 461 | 462 | // func valueInstanceOf(v ref, t ref) bool 463 | "syscall/js.valueInstanceOf": (sp) => { 464 | sp >>>= 0; 465 | this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); 466 | }, 467 | 468 | // func copyBytesToGo(dst []byte, src ref) (int, bool) 469 | "syscall/js.copyBytesToGo": (sp) => { 470 | sp >>>= 0; 471 | const dst = loadSlice(sp + 8); 472 | const src = loadValue(sp + 32); 473 | if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { 474 | this.mem.setUint8(sp + 48, 0); 475 | return; 476 | } 477 | const toCopy = src.subarray(0, dst.length); 478 | dst.set(toCopy); 479 | setInt64(sp + 40, toCopy.length); 480 | this.mem.setUint8(sp + 48, 1); 481 | }, 482 | 483 | // func copyBytesToJS(dst ref, src []byte) (int, bool) 484 | "syscall/js.copyBytesToJS": (sp) => { 485 | sp >>>= 0; 486 | const dst = loadValue(sp + 8); 487 | const src = loadSlice(sp + 16); 488 | if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { 489 | this.mem.setUint8(sp + 48, 0); 490 | return; 491 | } 492 | const toCopy = src.subarray(0, dst.length); 493 | dst.set(toCopy); 494 | setInt64(sp + 40, toCopy.length); 495 | this.mem.setUint8(sp + 48, 1); 496 | }, 497 | 498 | "debug": (value) => { 499 | console.log(value); 500 | }, 501 | } 502 | }; 503 | } 504 | 505 | async run(instance) { 506 | if (!(instance instanceof WebAssembly.Instance)) { 507 | throw new Error("Go.run: WebAssembly.Instance expected"); 508 | } 509 | this._inst = instance; 510 | this.mem = new DataView(this._inst.exports.mem.buffer); 511 | this._values = [ // JS values that Go currently has references to, indexed by reference id 512 | NaN, 513 | 0, 514 | null, 515 | true, 516 | false, 517 | global, 518 | this, 519 | ]; 520 | this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id 521 | this._ids = new Map([ // mapping from JS values to reference ids 522 | [0, 1], 523 | [null, 2], 524 | [true, 3], 525 | [false, 4], 526 | [global, 5], 527 | [this, 6], 528 | ]); 529 | this._idPool = []; // unused ids that have been garbage collected 530 | this.exited = false; // whether the Go program has exited 531 | 532 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 533 | let offset = 4096; 534 | 535 | const strPtr = (str) => { 536 | const ptr = offset; 537 | const bytes = encoder.encode(str + "\0"); 538 | new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); 539 | offset += bytes.length; 540 | if (offset % 8 !== 0) { 541 | offset += 8 - (offset % 8); 542 | } 543 | return ptr; 544 | }; 545 | 546 | const argc = this.argv.length; 547 | 548 | const argvPtrs = []; 549 | this.argv.forEach((arg) => { 550 | argvPtrs.push(strPtr(arg)); 551 | }); 552 | argvPtrs.push(0); 553 | 554 | const keys = Object.keys(this.env).sort(); 555 | keys.forEach((key) => { 556 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 557 | }); 558 | argvPtrs.push(0); 559 | 560 | const argv = offset; 561 | argvPtrs.forEach((ptr) => { 562 | this.mem.setUint32(offset, ptr, true); 563 | this.mem.setUint32(offset + 4, 0, true); 564 | offset += 8; 565 | }); 566 | 567 | this._inst.exports.run(argc, argv); 568 | if (this.exited) { 569 | this._resolveExitPromise(); 570 | } 571 | await this._exitPromise; 572 | } 573 | 574 | _resume() { 575 | if (this.exited) { 576 | throw new Error("Go program has already exited"); 577 | } 578 | this._inst.exports.resume(); 579 | if (this.exited) { 580 | this._resolveExitPromise(); 581 | } 582 | } 583 | 584 | _makeFuncWrapper(id) { 585 | const go = this; 586 | return function () { 587 | const event = { id: id, this: this, args: arguments }; 588 | go._pendingEvent = event; 589 | go._resume(); 590 | return event.result; 591 | }; 592 | } 593 | } 594 | 595 | if ( 596 | typeof module !== "undefined" && 597 | global.require && 598 | global.require.main === module && 599 | global.process && 600 | global.process.versions && 601 | !global.process.versions.electron 602 | ) { 603 | if (process.argv.length < 3) { 604 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); 605 | process.exit(1); 606 | } 607 | 608 | const go = new Go(); 609 | go.argv = process.argv.slice(2); 610 | go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); 611 | go.exit = process.exit; 612 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 613 | process.on("exit", (code) => { // Node.js exits if no event handler is pending 614 | if (code === 0 && !go.exited) { 615 | // deadlock, make Go print error and stack traces 616 | go._pendingEvent = { id: 0 }; 617 | go._resume(); 618 | } 619 | }); 620 | return go.run(result.instance); 621 | }).catch((err) => { 622 | console.error(err); 623 | process.exit(1); 624 | }); 625 | } 626 | })(); 627 | -------------------------------------------------------------------------------- /package_lesson1/assets/wasm_exec.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | (() => { 6 | // Map multiple JavaScript environments to a single common API, 7 | // preferring web standards over Node.js API. 8 | // 9 | // Environments considered: 10 | // - Browsers 11 | // - Node.js 12 | // - Electron 13 | // - Parcel 14 | // - Webpack 15 | const IsWechat = true; 16 | 17 | if (!IsWechat) { 18 | if (typeof global !== "undefined") { 19 | // global already exists 20 | } else if (typeof window !== "undefined") { 21 | window.global = window; 22 | } else if (typeof self !== "undefined") { 23 | self.global = self; 24 | } else { 25 | throw new Error("cannot export Go (neither global, window nor self is defined)"); 26 | } 27 | 28 | if (!global.require && typeof require !== "undefined") { 29 | global.require = require; 30 | } 31 | 32 | if (!global.fs && global.require) { 33 | const fs = require("fs"); 34 | if (typeof fs === "object" && fs !== null && Object.keys(fs).length !== 0) { 35 | global.fs = fs; 36 | } 37 | } 38 | } 39 | 40 | const enosys = () => { 41 | const err = new Error("not implemented"); 42 | err.code = "ENOSYS"; 43 | return err; 44 | }; 45 | 46 | if (!global.fs) { 47 | let outputBuf = ""; 48 | global.fs = { 49 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused 50 | writeSync(fd, buf) { 51 | outputBuf += decoder.decode(buf); 52 | const nl = outputBuf.lastIndexOf("\n"); 53 | if (nl != -1) { 54 | console.log(outputBuf.substr(0, nl)); 55 | outputBuf = outputBuf.substr(nl + 1); 56 | } 57 | return buf.length; 58 | }, 59 | write(fd, buf, offset, length, position, callback) { 60 | if (offset !== 0 || length !== buf.length || position !== null) { 61 | callback(enosys()); 62 | return; 63 | } 64 | const n = this.writeSync(fd, buf); 65 | callback(null, n); 66 | }, 67 | chmod(path, mode, callback) { callback(enosys()); }, 68 | chown(path, uid, gid, callback) { callback(enosys()); }, 69 | close(fd, callback) { callback(enosys()); }, 70 | fchmod(fd, mode, callback) { callback(enosys()); }, 71 | fchown(fd, uid, gid, callback) { callback(enosys()); }, 72 | fstat(fd, callback) { callback(enosys()); }, 73 | fsync(fd, callback) { callback(null); }, 74 | ftruncate(fd, length, callback) { callback(enosys()); }, 75 | lchown(path, uid, gid, callback) { callback(enosys()); }, 76 | link(path, link, callback) { callback(enosys()); }, 77 | lstat(path, callback) { callback(enosys()); }, 78 | mkdir(path, perm, callback) { callback(enosys()); }, 79 | open(path, flags, mode, callback) { callback(enosys()); }, 80 | read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, 81 | readdir(path, callback) { callback(enosys()); }, 82 | readlink(path, callback) { callback(enosys()); }, 83 | rename(from, to, callback) { callback(enosys()); }, 84 | rmdir(path, callback) { callback(enosys()); }, 85 | stat(path, callback) { callback(enosys()); }, 86 | symlink(path, link, callback) { callback(enosys()); }, 87 | truncate(path, length, callback) { callback(enosys()); }, 88 | unlink(path, callback) { callback(enosys()); }, 89 | utimes(path, atime, mtime, callback) { callback(enosys()); }, 90 | }; 91 | } 92 | 93 | if (!global.process) { 94 | global.process = { 95 | getuid() { return -1; }, 96 | getgid() { return -1; }, 97 | geteuid() { return -1; }, 98 | getegid() { return -1; }, 99 | getgroups() { throw enosys(); }, 100 | pid: -1, 101 | ppid: -1, 102 | umask() { throw enosys(); }, 103 | cwd() { throw enosys(); }, 104 | chdir() { throw enosys(); }, 105 | } 106 | } 107 | 108 | if (!IsWechat) { 109 | if (!global.crypto && global.require) { 110 | const nodeCrypto = require("crypto"); 111 | global.crypto = { 112 | getRandomValues(b) { 113 | nodeCrypto.randomFillSync(b); 114 | }, 115 | }; 116 | } 117 | if (!global.crypto) { 118 | throw new Error("global.crypto is not available, polyfill required (getRandomValues only)"); 119 | } 120 | } else { 121 | if (!global.crypto) { 122 | global.crypto = { 123 | getRandomValues(b) { 124 | let byteRange = 256; 125 | for (var i = 0; i < b.length; i++) { 126 | b[i] = Math.floor(byteRange * Math.random()); 127 | } 128 | }, 129 | }; 130 | } 131 | } 132 | if (!IsWechat) { 133 | if (!global.performance) { 134 | global.performance = { 135 | now() { 136 | const [sec, nsec] = process.hrtime(); 137 | return sec * 1000 + nsec / 1000000; 138 | }, 139 | }; 140 | } 141 | } else { 142 | if (!global.performance) { 143 | global.performance = { 144 | now() { 145 | return Date.now() 146 | }, 147 | }; 148 | } 149 | } 150 | 151 | if (!IsWechat) { 152 | if (!global.TextEncoder && global.require) { 153 | global.TextEncoder = require("util").TextEncoder; 154 | } 155 | if (!global.TextEncoder) { 156 | throw new Error("global.TextEncoder is not available, polyfill required"); 157 | } 158 | 159 | if (!global.TextDecoder && global.require) { 160 | global.TextDecoder = require("util").TextDecoder; 161 | } 162 | if (!global.TextDecoder) { 163 | throw new Error("global.TextDecoder is not available, polyfill required"); 164 | } 165 | } else { 166 | if (!global.TextEncoder) { 167 | global.TextEncoder = require("text-encoder").TextEncoder; 168 | } 169 | if (!global.TextDecoder) { 170 | global.TextDecoder = require("text-encoder").TextDecoder; 171 | } 172 | 173 | } 174 | // End of polyfills for common API. 175 | 176 | const encoder = new global.TextEncoder("utf-8"); 177 | const decoder = new global.TextDecoder("utf-8"); 178 | 179 | global.Go = class { 180 | constructor() { 181 | this.argv = ["js"]; 182 | this.env = {}; 183 | this.exit = (code) => { 184 | if (code !== 0) { 185 | console.warn("exit code:", code); 186 | } 187 | }; 188 | this._exitPromise = new Promise((resolve) => { 189 | this._resolveExitPromise = resolve; 190 | }); 191 | this._pendingEvent = null; 192 | this._scheduledTimeouts = new Map(); 193 | this._nextCallbackTimeoutID = 1; 194 | 195 | const setInt64 = (addr, v) => { 196 | this.mem.setUint32(addr + 0, v, true); 197 | this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); 198 | } 199 | 200 | const getInt64 = (addr) => { 201 | const low = this.mem.getUint32(addr + 0, true); 202 | const high = this.mem.getInt32(addr + 4, true); 203 | return low + high * 4294967296; 204 | } 205 | 206 | const loadValue = (addr) => { 207 | const f = this.mem.getFloat64(addr, true); 208 | if (f === 0) { 209 | return undefined; 210 | } 211 | if (!isNaN(f)) { 212 | return f; 213 | } 214 | 215 | const id = this.mem.getUint32(addr, true); 216 | return this._values[id]; 217 | } 218 | 219 | const storeValue = (addr, v) => { 220 | const nanHead = 0x7FF80000; 221 | 222 | if (typeof v === "number" && v !== 0) { 223 | if (isNaN(v)) { 224 | this.mem.setUint32(addr + 4, nanHead, true); 225 | this.mem.setUint32(addr, 0, true); 226 | return; 227 | } 228 | this.mem.setFloat64(addr, v, true); 229 | return; 230 | } 231 | 232 | if (v === undefined) { 233 | this.mem.setFloat64(addr, 0, true); 234 | return; 235 | } 236 | 237 | let id = this._ids.get(v); 238 | if (id === undefined) { 239 | id = this._idPool.pop(); 240 | if (id === undefined) { 241 | id = this._values.length; 242 | } 243 | this._values[id] = v; 244 | this._goRefCounts[id] = 0; 245 | this._ids.set(v, id); 246 | } 247 | this._goRefCounts[id]++; 248 | let typeFlag = 0; 249 | switch (typeof v) { 250 | case "object": 251 | if (v !== null) { 252 | typeFlag = 1; 253 | } 254 | break; 255 | case "string": 256 | typeFlag = 2; 257 | break; 258 | case "symbol": 259 | typeFlag = 3; 260 | break; 261 | case "function": 262 | typeFlag = 4; 263 | break; 264 | } 265 | this.mem.setUint32(addr + 4, nanHead | typeFlag, true); 266 | this.mem.setUint32(addr, id, true); 267 | } 268 | 269 | const loadSlice = (addr) => { 270 | const array = getInt64(addr + 0); 271 | const len = getInt64(addr + 8); 272 | return new Uint8Array(this._inst.exports.mem.buffer, array, len); 273 | } 274 | 275 | const loadSliceOfValues = (addr) => { 276 | const array = getInt64(addr + 0); 277 | const len = getInt64(addr + 8); 278 | const a = new Array(len); 279 | for (let i = 0; i < len; i++) { 280 | a[i] = loadValue(array + i * 8); 281 | } 282 | return a; 283 | } 284 | 285 | const loadString = (addr) => { 286 | const saddr = getInt64(addr + 0); 287 | const len = getInt64(addr + 8); 288 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 289 | } 290 | 291 | const timeOrigin = Date.now() - global.performance.now(); 292 | 293 | this.importObject = { 294 | go: { 295 | // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) 296 | // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported 297 | // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). 298 | // This changes the SP, thus we have to update the SP used by the imported function. 299 | 300 | // func wasmExit(code int32) 301 | "runtime.wasmExit": (sp) => { 302 | sp >>>= 0; 303 | const code = this.mem.getInt32(sp + 8, true); 304 | this.exited = true; 305 | delete this._inst; 306 | delete this._values; 307 | delete this._goRefCounts; 308 | delete this._ids; 309 | delete this._idPool; 310 | this.exit(code); 311 | }, 312 | 313 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) 314 | "runtime.wasmWrite": (sp) => { 315 | sp >>>= 0; 316 | const fd = getInt64(sp + 8); 317 | const p = getInt64(sp + 16); 318 | const n = this.mem.getInt32(sp + 24, true); 319 | global.fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); 320 | }, 321 | 322 | // func resetMemoryDataView() 323 | "runtime.resetMemoryDataView": (sp) => { 324 | sp >>>= 0; 325 | this.mem = new DataView(this._inst.exports.mem.buffer); 326 | }, 327 | 328 | // func nanotime1() int64 329 | "runtime.nanotime1": (sp) => { 330 | sp >>>= 0; 331 | setInt64(sp + 8, (timeOrigin + global.performance.now()) * 1000000); 332 | }, 333 | 334 | // func walltime1() (sec int64, nsec int32) 335 | "runtime.walltime1": (sp) => { 336 | sp >>>= 0; 337 | const msec = (new Date).getTime(); 338 | setInt64(sp + 8, msec / 1000); 339 | this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); 340 | }, 341 | 342 | // func scheduleTimeoutEvent(delay int64) int32 343 | "runtime.scheduleTimeoutEvent": (sp) => { 344 | sp >>>= 0; 345 | const id = this._nextCallbackTimeoutID; 346 | this._nextCallbackTimeoutID++; 347 | this._scheduledTimeouts.set(id, setTimeout( 348 | () => { 349 | this._resume(); 350 | while (this._scheduledTimeouts.has(id)) { 351 | // for some reason Go failed to register the timeout event, log and try again 352 | // (temporary workaround for https://github.com/golang/go/issues/28975) 353 | console.warn("scheduleTimeoutEvent: missed timeout event"); 354 | this._resume(); 355 | } 356 | }, 357 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early 358 | )); 359 | this.mem.setInt32(sp + 16, id, true); 360 | }, 361 | 362 | // func clearTimeoutEvent(id int32) 363 | "runtime.clearTimeoutEvent": (sp) => { 364 | sp >>>= 0; 365 | const id = this.mem.getInt32(sp + 8, true); 366 | clearTimeout(this._scheduledTimeouts.get(id)); 367 | this._scheduledTimeouts.delete(id); 368 | }, 369 | 370 | // func getRandomData(r []byte) 371 | "runtime.getRandomData": (sp) => { 372 | sp >>>= 0; 373 | global.crypto.getRandomValues(loadSlice(sp + 8)); 374 | }, 375 | 376 | // func finalizeRef(v ref) 377 | "syscall/js.finalizeRef": (sp) => { 378 | sp >>>= 0; 379 | const id = this.mem.getUint32(sp + 8, true); 380 | this._goRefCounts[id]--; 381 | if (this._goRefCounts[id] === 0) { 382 | const v = this._values[id]; 383 | this._values[id] = null; 384 | this._ids.delete(v); 385 | this._idPool.push(id); 386 | } 387 | }, 388 | 389 | // func stringVal(value string) ref 390 | "syscall/js.stringVal": (sp) => { 391 | sp >>>= 0; 392 | storeValue(sp + 24, loadString(sp + 8)); 393 | }, 394 | 395 | // func valueGet(v ref, p string) ref 396 | "syscall/js.valueGet": (sp) => { 397 | sp >>>= 0; 398 | const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); 399 | sp = this._inst.exports.getsp() >>> 0; // see comment above 400 | storeValue(sp + 32, result); 401 | }, 402 | 403 | // func valueSet(v ref, p string, x ref) 404 | "syscall/js.valueSet": (sp) => { 405 | sp >>>= 0; 406 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); 407 | }, 408 | 409 | // func valueDelete(v ref, p string) 410 | "syscall/js.valueDelete": (sp) => { 411 | sp >>>= 0; 412 | Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); 413 | }, 414 | 415 | // func valueIndex(v ref, i int) ref 416 | "syscall/js.valueIndex": (sp) => { 417 | sp >>>= 0; 418 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); 419 | }, 420 | 421 | // valueSetIndex(v ref, i int, x ref) 422 | "syscall/js.valueSetIndex": (sp) => { 423 | sp >>>= 0; 424 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); 425 | }, 426 | 427 | // func valueCall(v ref, m string, args []ref) (ref, bool) 428 | "syscall/js.valueCall": (sp) => { 429 | sp >>>= 0; 430 | try { 431 | const v = loadValue(sp + 8); 432 | const m = Reflect.get(v, loadString(sp + 16)); 433 | const args = loadSliceOfValues(sp + 32); 434 | const result = Reflect.apply(m, v, args); 435 | sp = this._inst.exports.getsp() >>> 0; // see comment above 436 | storeValue(sp + 56, result); 437 | this.mem.setUint8(sp + 64, 1); 438 | } catch (err) { 439 | storeValue(sp + 56, err); 440 | this.mem.setUint8(sp + 64, 0); 441 | } 442 | }, 443 | 444 | // func valueInvoke(v ref, args []ref) (ref, bool) 445 | "syscall/js.valueInvoke": (sp) => { 446 | sp >>>= 0; 447 | try { 448 | const v = loadValue(sp + 8); 449 | const args = loadSliceOfValues(sp + 16); 450 | const result = Reflect.apply(v, undefined, args); 451 | sp = this._inst.exports.getsp() >>> 0; // see comment above 452 | storeValue(sp + 40, result); 453 | this.mem.setUint8(sp + 48, 1); 454 | } catch (err) { 455 | storeValue(sp + 40, err); 456 | this.mem.setUint8(sp + 48, 0); 457 | } 458 | }, 459 | 460 | // func valueNew(v ref, args []ref) (ref, bool) 461 | "syscall/js.valueNew": (sp) => { 462 | sp >>>= 0; 463 | try { 464 | const v = loadValue(sp + 8); 465 | const args = loadSliceOfValues(sp + 16); 466 | const result = Reflect.construct(v, args); 467 | sp = this._inst.exports.getsp() >>> 0; // see comment above 468 | storeValue(sp + 40, result); 469 | this.mem.setUint8(sp + 48, 1); 470 | } catch (err) { 471 | storeValue(sp + 40, err); 472 | this.mem.setUint8(sp + 48, 0); 473 | } 474 | }, 475 | 476 | // func valueLength(v ref) int 477 | "syscall/js.valueLength": (sp) => { 478 | sp >>>= 0; 479 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); 480 | }, 481 | 482 | // valuePrepareString(v ref) (ref, int) 483 | "syscall/js.valuePrepareString": (sp) => { 484 | sp >>>= 0; 485 | const str = encoder.encode(String(loadValue(sp + 8))); 486 | storeValue(sp + 16, str); 487 | setInt64(sp + 24, str.length); 488 | }, 489 | 490 | // valueLoadString(v ref, b []byte) 491 | "syscall/js.valueLoadString": (sp) => { 492 | sp >>>= 0; 493 | const str = loadValue(sp + 8); 494 | loadSlice(sp + 16).set(str); 495 | }, 496 | 497 | // func valueInstanceOf(v ref, t ref) bool 498 | "syscall/js.valueInstanceOf": (sp) => { 499 | sp >>>= 0; 500 | this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); 501 | }, 502 | 503 | // func copyBytesToGo(dst []byte, src ref) (int, bool) 504 | "syscall/js.copyBytesToGo": (sp) => { 505 | sp >>>= 0; 506 | const dst = loadSlice(sp + 8); 507 | const src = loadValue(sp + 32); 508 | if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { 509 | this.mem.setUint8(sp + 48, 0); 510 | return; 511 | } 512 | const toCopy = src.subarray(0, dst.length); 513 | dst.set(toCopy); 514 | setInt64(sp + 40, toCopy.length); 515 | this.mem.setUint8(sp + 48, 1); 516 | }, 517 | 518 | // func copyBytesToJS(dst ref, src []byte) (int, bool) 519 | "syscall/js.copyBytesToJS": (sp) => { 520 | sp >>>= 0; 521 | const dst = loadValue(sp + 8); 522 | const src = loadSlice(sp + 16); 523 | if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { 524 | this.mem.setUint8(sp + 48, 0); 525 | return; 526 | } 527 | const toCopy = src.subarray(0, dst.length); 528 | dst.set(toCopy); 529 | setInt64(sp + 40, toCopy.length); 530 | this.mem.setUint8(sp + 48, 1); 531 | }, 532 | 533 | "debug": (value) => { 534 | console.log(value); 535 | }, 536 | } 537 | }; 538 | } 539 | 540 | async run(instance) { 541 | if (!IsWechat) { 542 | if (!(instance instanceof WebAssembly.Instance)) { 543 | throw new Error("Go.run: WebAssembly.Instance expected"); 544 | } 545 | } 546 | this._inst = instance; 547 | this.mem = new DataView(this._inst.exports.mem.buffer); 548 | this._values = [ // JS values that Go currently has references to, indexed by reference id 549 | NaN, 550 | 0, 551 | null, 552 | true, 553 | false, 554 | global, 555 | this, 556 | ]; 557 | this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id 558 | this._ids = new Map([ // mapping from JS values to reference ids 559 | [0, 1], 560 | [null, 2], 561 | [true, 3], 562 | [false, 4], 563 | [global, 5], 564 | [this, 6], 565 | ]); 566 | this._idPool = []; // unused ids that have been garbage collected 567 | this.exited = false; // whether the Go program has exited 568 | 569 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. 570 | let offset = 4096; 571 | 572 | const strPtr = (str) => { 573 | const ptr = offset; 574 | const bytes = encoder.encode(str + "\0"); 575 | new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); 576 | offset += bytes.length; 577 | if (offset % 8 !== 0) { 578 | offset += 8 - (offset % 8); 579 | } 580 | return ptr; 581 | }; 582 | 583 | const argc = this.argv.length; 584 | 585 | const argvPtrs = []; 586 | this.argv.forEach((arg) => { 587 | argvPtrs.push(strPtr(arg)); 588 | }); 589 | argvPtrs.push(0); 590 | 591 | const keys = Object.keys(this.env).sort(); 592 | keys.forEach((key) => { 593 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); 594 | }); 595 | argvPtrs.push(0); 596 | 597 | const argv = offset; 598 | argvPtrs.forEach((ptr) => { 599 | this.mem.setUint32(offset, ptr, true); 600 | this.mem.setUint32(offset + 4, 0, true); 601 | offset += 8; 602 | }); 603 | 604 | this._inst.exports.run(argc, argv); 605 | if (this.exited) { 606 | this._resolveExitPromise(); 607 | } 608 | await this._exitPromise; 609 | } 610 | 611 | _resume() { 612 | if (this.exited) { 613 | throw new Error("Go program has already exited"); 614 | } 615 | this._inst.exports.resume(); 616 | if (this.exited) { 617 | this._resolveExitPromise(); 618 | } 619 | } 620 | 621 | _makeFuncWrapper(id) { 622 | const go = this; 623 | return function () { 624 | const event = { id: id, this: this, args: arguments }; 625 | go._pendingEvent = event; 626 | go._resume(); 627 | return event.result; 628 | }; 629 | } 630 | } 631 | 632 | if ( 633 | typeof module !== "undefined" && 634 | global.require && 635 | global.require.main === module && 636 | global.process && 637 | global.process.versions && 638 | !global.process.versions.electron 639 | ) { 640 | if (process.argv.length < 3) { 641 | console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); 642 | process.exit(1); 643 | } 644 | 645 | const go = new Go(); 646 | go.argv = process.argv.slice(2); 647 | go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); 648 | go.exit = process.exit; 649 | WebAssembly.instantiate(global.fs.readFileSync(process.argv[2]), go.importObject).then((result) => { 650 | process.on("exit", (code) => { // Node.js exits if no event handler is pending 651 | if (code === 0 && !go.exited) { 652 | // deadlock, make Go print error and stack traces 653 | go._pendingEvent = { id: 0 }; 654 | go._resume(); 655 | } 656 | }); 657 | return go.run(result.instance); 658 | }).catch((err) => { 659 | console.error(err); 660 | process.exit(1); 661 | }); 662 | } 663 | })(); 664 | -------------------------------------------------------------------------------- /go_dev/assets/vue.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue.js v2.5.16 3 | * (c) 2014-2018 Evan You 4 | * Released under the MIT License. 5 | */ 6 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0