├── .appveyor.yml ├── .github └── workflows │ ├── clippy.yml │ └── rust.yml ├── .gitignore ├── Cargo.toml ├── README.md ├── examples └── webgl │ ├── gl-matrix-min.js │ ├── gl-matrix.js │ └── webgl.js ├── src ├── jslib │ ├── context.rs │ ├── eventloop.rs │ ├── globals.rs │ ├── jsclass.rs │ ├── jsfn.rs │ ├── mod.rs │ ├── threadbound.rs │ ├── upcast.rs │ └── upcast_test.rs ├── lib.rs ├── main.rs └── tests │ └── mod.rs └── test.js /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | 4 | environment: 5 | PYTHON3: "C:\\Python37-x64\\python.exe" 6 | # The appveyor image we use has a pretty huge set of things installed... we make the 7 | # initial PATH something sane so we know what to expect 8 | PATH: "C:\\windows\\system32;\ 9 | C:\\windows;\ 10 | C:\\windows\\System32\\Wbem;\ 11 | C:\\windows\\System32\\WindowsPowerShell\\v1.0;\ 12 | C:\\ProgramData\\chocolatey\\bin;\ 13 | C:\\Python27;\ 14 | C:\\Tools\\PsTools;\ 15 | C:\\Tools\\NuGet3;\ 16 | C:\\Program Files\\Microsoft\\Web Platform Installer\\;\ 17 | C:\\Program Files\\7-Zip;\ 18 | C:\\Program Files\\Mercurial;\ 19 | C:\\Program Files (x86)\\Subversion\\bin;\ 20 | C:\\Program Files (x86)\\CMake\\bin;\ 21 | C:\\Program Files (x86)\\Windows Kits\\10\\Windows Performance Toolkit\\;\ 22 | C:\\Program Files (x86)\\MSBuild\\14.0\\Bin;\ 23 | C:\\Program Files\\Amazon\\AWSCLI\\;\ 24 | C:\\Program Files\\Microsoft Windows Performance Toolkit\\;\ 25 | C:\\Program Files\\LLVM\\bin;\ 26 | C:\\Program Files\\Git LFS;\ 27 | C:\\Program Files\\Git\\cmd;\ 28 | C:\\Program Files\\Git\\usr\\bin;\ 29 | C:\\Program Files\\AppVeyor\\BuildAgent;" 30 | CC: "clang-cl.exe" 31 | CXX: "clang-cl.exe" 32 | LINKER: "lld-link.exe" 33 | 34 | VCVARSALL_PATH: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build 35 | 36 | matrix: 37 | - TARGET: x86_64-pc-windows-msvc 38 | - TARGET: i686-pc-windows-msvc 39 | CROSS_COMPILE: 1 40 | install: 41 | - set BUILD_ENV=msvc 42 | - ps: Start-FileDownload "http://servo-rust.s3.amazonaws.com/build/MozillaBuildSetup-2.2.0.exe" 43 | - ps: .\MozillaBuildSetup-2.2.0.exe /S | Out-Null 44 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 45 | - rustup-init.exe -y --default-host=x86_64-pc-windows-msvc 46 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 47 | - if "%CROSS_COMPILE%" == "1" rustup target add %TARGET% 48 | - set MOZTOOLS_PATH=C:\mozilla-build\msys\bin;C:\mozilla-build\mozmake;C:\mozilla-build\yasm 49 | - set NATIVE_WIN32_PYTHON=C:/Python27/python.exe 50 | - set AUTOCONF=C:/mozilla-build/msys/local/bin/autoconf-2.13 51 | - rustc -vV 52 | - cargo -vV 53 | 54 | shallow_clone: true 55 | 56 | build_script: 57 | - echo PATH %PATH% 58 | - echo MOZTOOLS_PATH %MOZTOOLS_PATH% 59 | - cd %APPVEYOR_BUILD_FOLDER% 60 | - cargo build --verbose --verbose --target %TARGET% 61 | 62 | test_script: 63 | - cargo test --verbose --verbose --target %TARGET% 64 | 65 | branches: 66 | only: 67 | - master 68 | - /^v\d+\.\d+\.\d+.*$/ 69 | 70 | cache: 71 | - target 72 | - C:\Users\appveyor\.cargo\registry 73 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | name: Clippy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Install stable rust 13 | uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | - name: Prepare 17 | run: | 18 | sudo apt update 19 | sudo apt install llvm -y 20 | wget -q https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py -O bootstrap.py 21 | python bootstrap.py --application-choice=browser --no-interactive|| exit 0 22 | - name: Build 23 | run: | 24 | export SHELL=/bin/bash 25 | cargo build --verbose 26 | - name: Clippy check 27 | uses: actions-rs/clippy-check@annotations-fallback 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | - name: Cache cargo registry 31 | uses: actions/cache@v1 32 | with: 33 | path: ~/.cargo/registry 34 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 35 | - name: Cache cargo index 36 | uses: actions/cache@v1 37 | with: 38 | path: ~/.cargo/git 39 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | # 14:05 friday 8 | - cron: '5 14 * * 5' 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v1 17 | - name: Install stable rust 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: stable 21 | - name: Prepare 22 | run: | 23 | sudo apt update 24 | sudo apt install llvm -y 25 | wget -q https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py -O bootstrap.py 26 | python bootstrap.py --application-choice=browser --no-interactive|| exit 0 27 | - name: Build 28 | run: | 29 | export SHELL=/bin/bash 30 | cargo build --verbose 31 | - name: Test 32 | run: | 33 | cargo test --verbose 34 | - name: Format 35 | run: | 36 | cargo fmt -- --check 37 | - name: Cache cargo registry 38 | uses: actions/cache@v1 39 | with: 40 | path: ~/.cargo/registry 41 | key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} 42 | - name: Cache cargo index 43 | uses: actions/cache@v1 44 | with: 45 | path: ~/.cargo/git 46 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .tags 4 | mach -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rjs" 3 | version = "0.1.0" 4 | edition = "2018" 5 | authors = ["Matt Peterson "] 6 | 7 | [features] 8 | debugmozjs = ['mozjs/debugmozjs'] 9 | 10 | [dependencies] 11 | mozjs = {git = "https://github.com/servo/rust-mozjs"} 12 | tokio-core = "0.1.17" 13 | tokio-timer = "0.2.13" 14 | futures = "0.1.29" 15 | # concat_bytes = "0.1.0" 16 | lazy_static = "1.4.0" 17 | slab = "0.4.2" 18 | downcast = "0.10.0" 19 | glutin = "0.16.0" 20 | gl = "0.14.0" 21 | libc = "0.2.67" 22 | 23 | [package.metadata.android] 24 | label = "Using assets android-rs-glue example" 25 | assets = "assets" 26 | android_version = 18 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rjs 2 | Native Javascript environment using Rust and Mozilla's SpiderMonkey JS engine. 3 | 4 | ## Why? 5 | This project would like to be: 6 | - A way to use Javascript to make Android games, using a thin WebGL/GLES binding. 7 | - Javascript compatible with Node.js. 8 | - An easy to use binding for Rust projects to embed a Javascript engine. 9 | - A superhero. 10 | 11 | This is going to take a lot of work, and there are things for everyone to do, so 12 | peek into the Issues and look for a "help wanted" issue to get started! 13 | 14 | ## Why not? 15 | - Why not use straight Rust? 16 | Javascript with hot module reloading has a faster edit-compile-run cycle. 17 | 18 | - Why not V8? 19 | It's time for SpiderMonkey to find more uses outside of Firefox. 20 | 21 | ## Building from source 22 | 23 | ### Setting up dependencies 24 | 25 | The single biggest dependency in this project is `rust-mozjs`, which requires 26 | that you have the following installed: 27 | * `cmake` 28 | * `make` 29 | * Python 2.7.x, which needs to be accessible as the `python` executable in your 30 | current `PATH`. 31 | 32 | #### Arch Linux 33 | 34 | ```bash 35 | # In the folder you want to clone into... 36 | pacman -S base-devel python2 cmake 37 | ``` 38 | 39 | #### Windows 40 | 41 | TODO 42 | 43 | ### Building 44 | 45 | * After dependencies have been set up, you should only need [`cargo`](http://doc.crates.io/guide.html#working-on-an-existing-cargo-project) to build this project. 46 | -------------------------------------------------------------------------------- /examples/webgl/gl-matrix-min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gl-matrix - High performance matrix and vector operations 3 | * @author Brandon Jones 4 | * @author Colin MacKenzie IV 5 | * @version 2.4.0 6 | */ 7 | 8 | /* Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. */ 27 | 28 | !function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}(this,function(){return function(t){function n(a){if(r[a])return r[a].exports;var e=r[a]={i:a,l:!1,exports:{}};return t[a].call(e.exports,e,e.exports,n),e.l=!0,e.exports}var r={};return n.m=t,n.c=r,n.d=function(t,r,a){n.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:a})},n.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(r,"a",r),r},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=4)}([function(t,n,r){"use strict";function a(t){n.ARRAY_TYPE=i=t}function e(t){return t*s}function u(t,n){return Math.abs(t-n)<=o*Math.max(1,Math.abs(t),Math.abs(n))}Object.defineProperty(n,"__esModule",{value:!0}),n.setMatrixArrayType=a,n.toRadian=e,n.equals=u;var o=n.EPSILON=1e-6,i=n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,s=(n.RANDOM=Math.random,Math.PI/180)},function(t,n,r){"use strict";function a(){var t=new g.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function e(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t}function u(t){var n=new g.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n}function o(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t}function i(t,n,r,a,e,u,o,i,s){var c=new g.ARRAY_TYPE(9);return c[0]=t,c[1]=n,c[2]=r,c[3]=a,c[4]=e,c[5]=u,c[6]=o,c[7]=i,c[8]=s,c}function s(t,n,r,a,e,u,o,i,s,c){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t}function c(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function f(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t}function M(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=f*o-i*c,h=-f*u+i*s,l=c*u-o*s,v=r*M+a*h+e*l;return v?(v=1/v,t[0]=M*v,t[1]=(-f*a+e*c)*v,t[2]=(i*a-e*o)*v,t[3]=h*v,t[4]=(f*r-e*s)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-c*r+a*s)*v,t[8]=(o*r-a*u)*v,t):null}function h(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8];return t[0]=o*f-i*c,t[1]=e*c-a*f,t[2]=a*i-e*o,t[3]=i*s-u*f,t[4]=r*f-e*s,t[5]=e*u-r*i,t[6]=u*c-o*s,t[7]=a*s-r*c,t[8]=r*o-a*u,t}function l(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8];return n*(c*u-o*s)+r*(-c*e+o*i)+a*(s*e-u*i)}function v(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1],v=r[2],d=r[3],b=r[4],m=r[5],p=r[6],P=r[7],E=r[8];return t[0]=h*a+l*o+v*c,t[1]=h*e+l*i+v*f,t[2]=h*u+l*s+v*M,t[3]=d*a+b*o+m*c,t[4]=d*e+b*i+m*f,t[5]=d*u+b*s+m*M,t[6]=p*a+P*o+E*c,t[7]=p*e+P*i+E*f,t[8]=p*u+P*s+E*M,t}function d(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=s,t[6]=h*a+l*o+c,t[7]=h*e+l*i+f,t[8]=h*u+l*s+M,t}function b(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=Math.sin(r),l=Math.cos(r);return t[0]=l*a+h*o,t[1]=l*e+h*i,t[2]=l*u+h*s,t[3]=l*o-h*a,t[4]=l*i-h*e,t[5]=l*s-h*u,t[6]=c,t[7]=f,t[8]=M,t}function m(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t}function p(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t}function P(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function E(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t}function O(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t}function x(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[3]=f-m,t[6]=h+b,t[1]=f+m,t[4]=1-c-v,t[7]=l-d,t[2]=h-b,t[5]=l+d,t[8]=1-c-M,t}function A(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,E=r*c-u*o,O=a*s-e*i,x=a*c-u*i,A=e*c-u*s,q=f*d-M*v,y=f*b-h*v,w=f*m-l*v,R=M*b-h*d,L=M*m-l*d,S=h*m-l*b,_=p*S-P*L+E*R+O*w-x*y+A*q;return _?(_=1/_,t[0]=(i*S-s*L+c*R)*_,t[1]=(s*w-o*S-c*y)*_,t[2]=(o*L-i*w+c*q)*_,t[3]=(e*L-a*S-u*R)*_,t[4]=(r*S-e*w+u*y)*_,t[5]=(a*w-r*L-u*q)*_,t[6]=(d*A-b*x+m*O)*_,t[7]=(b*E-v*A-m*P)*_,t[8]=(v*x-d*E+m*p)*_,t):null}function q(t,n,r){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t}function y(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"}function w(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))}function R(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t}function L(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t}function S(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t}function _(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t}function I(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]}function N(t,n){var r=t[0],a=t[1],e=t[2],u=t[3],o=t[4],i=t[5],s=t[6],c=t[7],f=t[8],M=n[0],h=n[1],l=n[2],v=n[3],d=n[4],b=n[5],m=n[6],p=n[7],P=n[8];return Math.abs(r-M)<=g.EPSILON*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(a-h)<=g.EPSILON*Math.max(1,Math.abs(a),Math.abs(h))&&Math.abs(e-l)<=g.EPSILON*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(u-v)<=g.EPSILON*Math.max(1,Math.abs(u),Math.abs(v))&&Math.abs(o-d)<=g.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(i-b)<=g.EPSILON*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(s-m)<=g.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=g.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-P)<=g.EPSILON*Math.max(1,Math.abs(f),Math.abs(P))}Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=a,n.fromMat4=e,n.clone=u,n.copy=o,n.fromValues=i,n.set=s,n.identity=c,n.transpose=f,n.invert=M,n.adjoint=h,n.determinant=l,n.multiply=v,n.translate=d,n.rotate=b,n.scale=m,n.fromTranslation=p,n.fromRotation=P,n.fromScaling=E,n.fromMat2d=O,n.fromQuat=x,n.normalFromMat4=A,n.projection=q,n.str=y,n.frob=w,n.add=R,n.subtract=L,n.multiplyScalar=S,n.multiplyScalarAndAdd=_,n.exactEquals=I,n.equals=N;var Y=r(0),g=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(Y);n.mul=v,n.sub=L},function(t,n,r){"use strict";function a(){var t=new Z.ARRAY_TYPE(3);return t[0]=0,t[1]=0,t[2]=0,t}function e(t){var n=new Z.ARRAY_TYPE(3);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n}function u(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)}function o(t,n,r){var a=new Z.ARRAY_TYPE(3);return a[0]=t,a[1]=n,a[2]=r,a}function i(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t}function s(t,n,r,a){return t[0]=n,t[1]=r,t[2]=a,t}function c(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t}function f(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t}function M(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t}function h(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t}function l(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t}function v(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t}function d(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t}function b(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t}function m(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t}function p(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t}function P(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t}function E(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)}function O(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e}function x(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a}function A(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t}function q(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t}function y(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t}function w(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function R(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2];return t[0]=e*s-u*i,t[1]=u*o-a*s,t[2]=a*i-e*o,t}function L(t,n,r,a){var e=n[0],u=n[1],o=n[2];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t}function S(t,n,r,a,e,u){var o=u*u,i=o*(2*u-3)+1,s=o*(u-2)+u,c=o*(u-1),f=o*(3-2*u);return t[0]=n[0]*i+r[0]*s+a[0]*c+e[0]*f,t[1]=n[1]*i+r[1]*s+a[1]*c+e[1]*f,t[2]=n[2]*i+r[2]*s+a[2]*c+e[2]*f,t}function _(t,n,r,a,e,u){var o=1-u,i=o*o,s=u*u,c=i*o,f=3*u*i,M=3*s*o,h=s*u;return t[0]=n[0]*c+r[0]*f+a[0]*M+e[0]*h,t[1]=n[1]*c+r[1]*f+a[1]*M+e[1]*h,t[2]=n[2]*c+r[2]*f+a[2]*M+e[2]*h,t}function I(t,n){n=n||1;var r=2*Z.RANDOM()*Math.PI,a=2*Z.RANDOM()-1,e=Math.sqrt(1-a*a)*n;return t[0]=Math.cos(r)*e,t[1]=Math.sin(r)*e,t[2]=a*n,t}function N(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[3]*a+r[7]*e+r[11]*u+r[15];return o=o||1,t[0]=(r[0]*a+r[4]*e+r[8]*u+r[12])/o,t[1]=(r[1]*a+r[5]*e+r[9]*u+r[13])/o,t[2]=(r[2]*a+r[6]*e+r[10]*u+r[14])/o,t}function Y(t,n,r){var a=n[0],e=n[1],u=n[2];return t[0]=a*r[0]+e*r[3]+u*r[6],t[1]=a*r[1]+e*r[4]+u*r[7],t[2]=a*r[2]+e*r[5]+u*r[8],t}function g(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t}function T(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0],u[1]=e[1]*Math.cos(a)-e[2]*Math.sin(a),u[2]=e[1]*Math.sin(a)+e[2]*Math.cos(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t}function j(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[2]*Math.sin(a)+e[0]*Math.cos(a),u[1]=e[1],u[2]=e[2]*Math.cos(a)-e[0]*Math.sin(a),t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t}function D(t,n,r,a){var e=[],u=[];return e[0]=n[0]-r[0],e[1]=n[1]-r[1],e[2]=n[2]-r[2],u[0]=e[0]*Math.cos(a)-e[1]*Math.sin(a),u[1]=e[0]*Math.sin(a)+e[1]*Math.cos(a),u[2]=e[2],t[0]=u[0]+r[0],t[1]=u[1]+r[1],t[2]=u[2]+r[2],t}function V(t,n){var r=o(t[0],t[1],t[2]),a=o(n[0],n[1],n[2]);y(r,r),y(a,a);var e=w(r,a);return e>1?0:e<-1?Math.PI:Math.acos(e)}function z(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"}function F(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]}function Q(t,n){var r=t[0],a=t[1],e=t[2],u=n[0],o=n[1],i=n[2];return Math.abs(r-u)<=Z.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(a-o)<=Z.EPSILON*Math.max(1,Math.abs(a),Math.abs(o))&&Math.abs(e-i)<=Z.EPSILON*Math.max(1,Math.abs(e),Math.abs(i))}Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=a,n.clone=e,n.length=u,n.fromValues=o,n.copy=i,n.set=s,n.add=c,n.subtract=f,n.multiply=M,n.divide=h,n.ceil=l,n.floor=v,n.min=d,n.max=b,n.round=m,n.scale=p,n.scaleAndAdd=P,n.distance=E,n.squaredDistance=O,n.squaredLength=x,n.negate=A,n.inverse=q,n.normalize=y,n.dot=w,n.cross=R,n.lerp=L,n.hermite=S,n.bezier=_,n.random=I,n.transformMat4=N,n.transformMat3=Y,n.transformQuat=g,n.rotateX=T,n.rotateY=j,n.rotateZ=D,n.angle=V,n.str=z,n.exactEquals=F,n.equals=Q;var X=r(0),Z=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(X);n.sub=f,n.mul=M,n.div=h,n.dist=E,n.sqrDist=O,n.len=u,n.sqrLen=x,n.forEach=function(){var t=a();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=3),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o),t}function w(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]}function R(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t}function L(t,n){return n=n||1,t[0]=T.RANDOM(),t[1]=T.RANDOM(),t[2]=T.RANDOM(),t[3]=T.RANDOM(),y(t,t),m(t,t,n),t}function S(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t}function _(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t[3]=n[3],t}function I(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"}function N(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]}function Y(t,n){var r=t[0],a=t[1],e=t[2],u=t[3],o=n[0],i=n[1],s=n[2],c=n[3];return Math.abs(r-o)<=T.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(a-i)<=T.EPSILON*Math.max(1,Math.abs(a),Math.abs(i))&&Math.abs(e-s)<=T.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=T.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))}Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=a,n.clone=e,n.fromValues=u,n.copy=o,n.set=i,n.add=s,n.subtract=c,n.multiply=f,n.divide=M,n.ceil=h,n.floor=l,n.min=v,n.max=d,n.round=b,n.scale=m,n.scaleAndAdd=p,n.distance=P,n.squaredDistance=E,n.length=O,n.squaredLength=x,n.negate=A,n.inverse=q,n.normalize=y,n.dot=w,n.lerp=R,n.random=L,n.transformMat4=S,n.transformQuat=_,n.str=I,n.exactEquals=N,n.equals=Y;var g=r(0),T=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(g);n.sub=c,n.mul=f,n.div=M,n.dist=P,n.sqrDist=E,n.len=O,n.sqrLen=x,n.forEach=function(){var t=a();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=4),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i0?(a=2*Math.sqrt(r+1),t[3]=.25*a,t[0]=(n[6]-n[9])/a,t[1]=(n[8]-n[2])/a,t[2]=(n[1]-n[4])/a):n[0]>n[5]&n[0]>n[10]?(a=2*Math.sqrt(1+n[0]-n[5]-n[10]),t[3]=(n[6]-n[9])/a,t[0]=.25*a,t[1]=(n[1]+n[4])/a,t[2]=(n[8]+n[2])/a):n[5]>n[10]?(a=2*Math.sqrt(1+n[5]-n[0]-n[10]),t[3]=(n[8]-n[2])/a,t[0]=(n[1]+n[4])/a,t[1]=.25*a,t[2]=(n[6]+n[9])/a):(a=2*Math.sqrt(1+n[10]-n[0]-n[5]),t[3]=(n[1]-n[4])/a,t[0]=(n[8]+n[2])/a,t[1]=(n[6]+n[9])/a,t[2]=.25*a),t}function _(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=e+e,c=u+u,f=o+o,M=e*s,h=e*c,l=e*f,v=u*c,d=u*f,b=o*f,m=i*s,p=i*c,P=i*f,E=a[0],O=a[1],x=a[2];return t[0]=(1-(v+b))*E,t[1]=(h+P)*E,t[2]=(l-p)*E,t[3]=0,t[4]=(h-P)*O,t[5]=(1-(M+b))*O,t[6]=(d+m)*O,t[7]=0,t[8]=(l+p)*x,t[9]=(d-m)*x,t[10]=(1-(M+v))*x,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function I(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=u+u,f=o+o,M=i+i,h=u*c,l=u*f,v=u*M,d=o*f,b=o*M,m=i*M,p=s*c,P=s*f,E=s*M,O=a[0],x=a[1],A=a[2],q=e[0],y=e[1],w=e[2];return t[0]=(1-(d+m))*O,t[1]=(l+E)*O,t[2]=(v-P)*O,t[3]=0,t[4]=(l-E)*x,t[5]=(1-(h+m))*x,t[6]=(b+p)*x,t[7]=0,t[8]=(v+P)*A,t[9]=(b-p)*A,t[10]=(1-(h+d))*A,t[11]=0,t[12]=r[0]+q-(t[0]*q+t[4]*y+t[8]*w),t[13]=r[1]+y-(t[1]*q+t[5]*y+t[9]*w),t[14]=r[2]+w-(t[2]*q+t[6]*y+t[10]*w),t[15]=1,t}function N(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[1]=f+m,t[2]=h-b,t[3]=0,t[4]=f-m,t[5]=1-c-v,t[6]=l+d,t[7]=0,t[8]=h+b,t[9]=l-d,t[10]=1-c-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function Y(t,n,r,a,e,u,o){var i=1/(r-n),s=1/(e-a),c=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*s,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*s,t[10]=(o+u)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*c,t[15]=0,t}function g(t,n,r,a,e){var u=1/Math.tan(n/2),o=1/(a-e);return t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=(e+a)*o,t[11]=-1,t[12]=0,t[13]=0,t[14]=2*e*a*o,t[15]=0,t}function T(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),s=2/(o+i),c=2/(e+u);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(o-i)*s*.5,t[9]=(e-u)*c*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t}function j(t,n,r,a,e,u,o){var i=1/(n-r),s=1/(a-e),c=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*s,t[14]=(o+u)*c,t[15]=1,t}function D(t,n,r,a){var e=void 0,u=void 0,o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=n[0],d=n[1],b=n[2],m=a[0],p=a[1],P=a[2],E=r[0],O=r[1],x=r[2];return Math.abs(v-E)0&&(l=1/Math.sqrt(l),f*=l,M*=l,h*=l);var v=s*h-c*M,d=c*f-i*h,b=i*M-s*f;return t[0]=v,t[1]=d,t[2]=b,t[3]=0,t[4]=M*b-h*d,t[5]=h*v-f*b,t[6]=f*d-M*v,t[7]=0,t[8]=f,t[9]=M,t[10]=h,t[11]=0,t[12]=e,t[13]=u,t[14]=o,t[15]=1,t}function z(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"}function F(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))}function Q(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t[9]=n[9]+r[9],t[10]=n[10]+r[10],t[11]=n[11]+r[11],t[12]=n[12]+r[12],t[13]=n[13]+r[13],t[14]=n[14]+r[14],t[15]=n[15]+r[15],t}function X(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t[9]=n[9]-r[9],t[10]=n[10]-r[10],t[11]=n[11]-r[11],t[12]=n[12]-r[12],t[13]=n[13]-r[13],t[14]=n[14]-r[14],t[15]=n[15]-r[15],t}function Z(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t[9]=n[9]*r,t[10]=n[10]*r,t[11]=n[11]*r,t[12]=n[12]*r,t[13]=n[13]*r,t[14]=n[14]*r,t[15]=n[15]*r,t}function k(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t[9]=n[9]+r[9]*a,t[10]=n[10]+r[10]*a,t[11]=n[11]+r[11]*a,t[12]=n[12]+r[12]*a,t[13]=n[13]+r[13]*a,t[14]=n[14]+r[14]*a,t[15]=n[15]+r[15]*a,t}function U(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]}function W(t,n){var r=t[0],a=t[1],e=t[2],u=t[3],o=t[4],i=t[5],s=t[6],c=t[7],f=t[8],M=t[9],h=t[10],l=t[11],v=t[12],d=t[13],b=t[14],m=t[15],p=n[0],P=n[1],E=n[2],O=n[3],x=n[4],A=n[5],q=n[6],y=n[7],w=n[8],R=n[9],L=n[10],S=n[11],_=n[12],I=n[13],N=n[14],Y=n[15];return Math.abs(r-p)<=C.EPSILON*Math.max(1,Math.abs(r),Math.abs(p))&&Math.abs(a-P)<=C.EPSILON*Math.max(1,Math.abs(a),Math.abs(P))&&Math.abs(e-E)<=C.EPSILON*Math.max(1,Math.abs(e),Math.abs(E))&&Math.abs(u-O)<=C.EPSILON*Math.max(1,Math.abs(u),Math.abs(O))&&Math.abs(o-x)<=C.EPSILON*Math.max(1,Math.abs(o),Math.abs(x))&&Math.abs(i-A)<=C.EPSILON*Math.max(1,Math.abs(i),Math.abs(A))&&Math.abs(s-q)<=C.EPSILON*Math.max(1,Math.abs(s),Math.abs(q))&&Math.abs(c-y)<=C.EPSILON*Math.max(1,Math.abs(c),Math.abs(y))&&Math.abs(f-w)<=C.EPSILON*Math.max(1,Math.abs(f),Math.abs(w))&&Math.abs(M-R)<=C.EPSILON*Math.max(1,Math.abs(M),Math.abs(R))&&Math.abs(h-L)<=C.EPSILON*Math.max(1,Math.abs(h),Math.abs(L))&&Math.abs(l-S)<=C.EPSILON*Math.max(1,Math.abs(l),Math.abs(S))&&Math.abs(v-_)<=C.EPSILON*Math.max(1,Math.abs(v),Math.abs(_))&&Math.abs(d-I)<=C.EPSILON*Math.max(1,Math.abs(d),Math.abs(I))&&Math.abs(b-N)<=C.EPSILON*Math.max(1,Math.abs(b),Math.abs(N))&&Math.abs(m-Y)<=C.EPSILON*Math.max(1,Math.abs(m),Math.abs(Y))}Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=a,n.clone=e,n.copy=u,n.fromValues=o,n.set=i,n.identity=s,n.transpose=c,n.invert=f,n.adjoint=M,n.determinant=h,n.multiply=l,n.translate=v,n.scale=d,n.rotate=b,n.rotateX=m,n.rotateY=p,n.rotateZ=P,n.fromTranslation=E,n.fromScaling=O,n.fromRotation=x,n.fromXRotation=A,n.fromYRotation=q,n.fromZRotation=y,n.fromRotationTranslation=w,n.getTranslation=R,n.getScaling=L,n.getRotation=S,n.fromRotationTranslationScale=_,n.fromRotationTranslationScaleOrigin=I,n.fromQuat=N,n.frustum=Y,n.perspective=g,n.perspectiveFromFieldOfView=T,n.ortho=j,n.lookAt=D,n.targetTo=V,n.str=z,n.frob=F,n.add=Q,n.subtract=X,n.multiplyScalar=Z,n.multiplyScalarAndAdd=k,n.exactEquals=U,n.equals=W;var B=r(0),C=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(B);n.mul=l,n.sub=X},function(t,n,r){"use strict";function a(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function e(){var t=new E.ARRAY_TYPE(4);return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t}function u(t){return t[0]=0,t[1]=0,t[2]=0,t[3]=1,t}function o(t,n,r){r*=.5;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t}function i(t,n){var r=2*Math.acos(n[3]),a=Math.sin(r/2);return 0!=a?(t[0]=n[0]/a,t[1]=n[1]/a,t[2]=n[2]/a):(t[0]=1,t[1]=0,t[2]=0),r}function s(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,t}function c(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+o*i,t[1]=e*s+u*i,t[2]=u*s-e*i,t[3]=o*s-a*i,t}function f(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s-u*i,t[1]=e*s+o*i,t[2]=u*s+a*i,t[3]=o*s-e*i,t}function M(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+e*i,t[1]=e*s-a*i,t[2]=u*s+o*i,t[3]=o*s-u*i,t}function h(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t}function l(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=r[0],c=r[1],f=r[2],M=r[3],h=void 0,l=void 0,v=void 0,d=void 0,b=void 0;return l=e*s+u*c+o*f+i*M,l<0&&(l=-l,s=-s,c=-c,f=-f,M=-M),1-l>1e-6?(h=Math.acos(l),v=Math.sin(h),d=Math.sin((1-a)*h)/v,b=Math.sin(a*h)/v):(d=1-a,b=a),t[0]=d*e+b*s,t[1]=d*u+b*c,t[2]=d*o+b*f,t[3]=d*i+b*M,t}function v(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t}function d(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t}function b(t,n){var r=n[0]+n[4]+n[8],a=void 0;if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;a=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*a,a=.5/a,t[3]=(n[3*u+o]-n[3*o+u])*a,t[u]=(n[3*u+e]+n[3*e+u])*a,t[o]=(n[3*o+e]+n[3*e+o])*a}return t}function m(t,n,r,a){var e=.5*Math.PI/180;n*=e,r*=e,a*=e;var u=Math.sin(n),o=Math.cos(n),i=Math.sin(r),s=Math.cos(r),c=Math.sin(a),f=Math.cos(a);return t[0]=u*s*f-o*i*c,t[1]=o*i*f+u*s*c,t[2]=o*s*c-u*i*f,t[3]=o*s*f+u*i*c,t}function p(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"}Object.defineProperty(n,"__esModule",{value:!0}),n.setAxes=n.sqlerp=n.rotationTo=n.equals=n.exactEquals=n.normalize=n.sqrLen=n.squaredLength=n.len=n.length=n.lerp=n.dot=n.scale=n.mul=n.add=n.set=n.copy=n.fromValues=n.clone=void 0,n.create=e,n.identity=u,n.setAxisAngle=o,n.getAxisAngle=i,n.multiply=s,n.rotateX=c,n.rotateY=f,n.rotateZ=M,n.calculateW=h,n.slerp=l,n.invert=v,n.conjugate=d,n.fromMat3=b,n.fromEuler=m,n.str=p;var P=r(0),E=a(P),O=r(1),x=a(O),A=r(2),q=a(A),y=r(3),w=a(y),R=(n.clone=w.clone,n.fromValues=w.fromValues,n.copy=w.copy,n.set=w.set,n.add=w.add,n.mul=s,n.scale=w.scale,n.dot=w.dot,n.lerp=w.lerp,n.length=w.length),L=(n.len=R,n.squaredLength=w.squaredLength),S=(n.sqrLen=L,n.normalize=w.normalize);n.exactEquals=w.exactEquals,n.equals=w.equals,n.rotationTo=function(){var t=q.create(),n=q.fromValues(1,0,0),r=q.fromValues(0,1,0);return function(a,e,u){var i=q.dot(e,u);return i<-.999999?(q.cross(t,n,e),q.len(t)<1e-6&&q.cross(t,r,e),q.normalize(t,t),o(a,t,Math.PI),a):i>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(q.cross(t,e,u),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+i,S(a,a))}}(),n.sqlerp=function(){var t=e(),n=e();return function(r,a,e,u,o,i){return l(t,a,o,i),l(n,e,u,i),l(r,t,n,2*i*(1-i)),r}}(),n.setAxes=function(){var t=x.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],S(n,b(n,t))}}()},function(t,n,r){"use strict";function a(){var t=new V.ARRAY_TYPE(2);return t[0]=0,t[1]=0,t}function e(t){var n=new V.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n}function u(t,n){var r=new V.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r}function o(t,n){return t[0]=n[0],t[1]=n[1],t}function i(t,n,r){return t[0]=n,t[1]=r,t}function s(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t}function c(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t}function f(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t}function M(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t}function h(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t}function l(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t}function v(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t}function d(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t}function b(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t}function m(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t}function p(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t}function P(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)}function E(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a}function O(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)}function x(t){var n=t[0],r=t[1];return n*n+r*r}function A(t,n){return t[0]=-n[0],t[1]=-n[1],t}function q(t,n){return t[0]=1/n[0],t[1]=1/n[1],t}function y(t,n){var r=n[0],a=n[1],e=r*r+a*a;return e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e),t}function w(t,n){return t[0]*n[0]+t[1]*n[1]}function R(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t}function L(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t}function S(t,n){n=n||1;var r=2*V.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t}function _(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t}function I(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t}function N(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t}function Y(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t}function g(t){return"vec2("+t[0]+", "+t[1]+")"}function T(t,n){return t[0]===n[0]&&t[1]===n[1]}function j(t,n){var r=t[0],a=t[1],e=n[0],u=n[1];return Math.abs(r-e)<=V.EPSILON*Math.max(1,Math.abs(r),Math.abs(e))&&Math.abs(a-u)<=V.EPSILON*Math.max(1,Math.abs(a),Math.abs(u))}Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.sqrDist=n.dist=n.div=n.mul=n.sub=n.len=void 0,n.create=a,n.clone=e,n.fromValues=u,n.copy=o,n.set=i,n.add=s,n.subtract=c,n.multiply=f,n.divide=M,n.ceil=h,n.floor=l,n.min=v,n.max=d,n.round=b,n.scale=m,n.scaleAndAdd=p,n.distance=P,n.squaredDistance=E,n.length=O,n.squaredLength=x,n.negate=A,n.inverse=q,n.normalize=y,n.dot=w,n.cross=R,n.lerp=L,n.random=S,n.transformMat2=_,n.transformMat2d=I,n.transformMat3=N,n.transformMat4=Y,n.str=g,n.exactEquals=T,n.equals=j;var D=r(0),V=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(D);n.len=O,n.sub=c,n.mul=f,n.div=M,n.dist=P,n.sqrDist=E,n.sqrLen=x,n.forEach=function(){var t=a();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=2),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i "" + x).join(', ') + ")"); 54 | puts("" + e); 55 | throw e; 56 | } 57 | let err = target.getError(); 58 | puts("gl."+name+"(" + Array.prototype.slice.call(arguments).map(x => "" + x).join(', ') + "): " + ret); 59 | if (err != 0) { 60 | puts("GL error: " + err.toString(16)); 61 | } 62 | return ret; 63 | }; 64 | return val; 65 | } 66 | puts("Missing property: " + name); 67 | return function(){}; 68 | }, 69 | }); 70 | } catch (e) { 71 | } 72 | if (!gl) { 73 | alert("Could not initialise WebGL, sorry :-("); 74 | } 75 | } 76 | function getShader(gl, type, source) { 77 | /*var shaderScript = document.getElementById(id); 78 | if (!shaderScript) { 79 | return null; 80 | } 81 | var str = ""; 82 | var k = shaderScript.firstChild; 83 | while (k) { 84 | if (k.nodeType == 3) { 85 | str += k.textContent; 86 | } 87 | k = k.nextSibling; 88 | }*/ 89 | var shader; 90 | if (type == "fragment") { 91 | shader = gl.createShader(gl.FRAGMENT_SHADER); 92 | } else if (type == "vertex") { 93 | shader = gl.createShader(gl.VERTEX_SHADER); 94 | } else { 95 | return null; 96 | } 97 | gl.shaderSource(shader, source); 98 | gl.compileShader(shader); 99 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 100 | alert(gl.getShaderInfoLog(shader)); 101 | return null; 102 | } 103 | return shader; 104 | } 105 | var shaderProgram; 106 | function initShaders() { 107 | var fragmentShader = getShader(gl, "fragment", fragment); 108 | var vertexShader = getShader(gl, "vertex", vertex); 109 | shaderProgram = gl.createProgram(); 110 | gl.attachShader(shaderProgram, vertexShader); 111 | gl.attachShader(shaderProgram, fragmentShader); 112 | gl.linkProgram(shaderProgram); 113 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 114 | alert("Could not initialise shaders"); 115 | } 116 | gl.useProgram(shaderProgram); 117 | shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition"); 118 | gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute); 119 | shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix"); 120 | shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix"); 121 | } 122 | var mvMatrix = mat4.create(); 123 | var pMatrix = mat4.create(); 124 | function setMatrixUniforms() { 125 | gl.uniformMatrix4fv(shaderProgram.pMatrixUniform, false, pMatrix); 126 | gl.uniformMatrix4fv(shaderProgram.mvMatrixUniform, false, mvMatrix); 127 | } 128 | var triangleVertexPositionBuffer; 129 | var squareVertexPositionBuffer; 130 | function initBuffers() { 131 | triangleVertexPositionBuffer = gl.createBuffer(); 132 | gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); 133 | var vertices = [ 134 | 0.0, 1.0, 0.0, 135 | -1.0, -1.0, 0.0, 136 | 1.0, -1.0, 0.0 137 | ]; 138 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 139 | triangleVertexPositionBuffer.itemSize = 3; 140 | triangleVertexPositionBuffer.numItems = 3; 141 | squareVertexPositionBuffer = gl.createBuffer(); 142 | gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); 143 | vertices = [ 144 | 1.0, 1.0, 0.0, 145 | -1.0, 1.0, 0.0, 146 | 1.0, -1.0, 0.0, 147 | -1.0, -1.0, 0.0 148 | ]; 149 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 150 | squareVertexPositionBuffer.itemSize = 3; 151 | squareVertexPositionBuffer.numItems = 4; 152 | } 153 | function drawScene() { 154 | gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); 155 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 156 | mat4.perspective(pMatrix, 45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0); 157 | mat4.identity(mvMatrix); 158 | mat4.translate(mvMatrix, mvMatrix, [-1.5, 0.0, -7.0]); 159 | gl.bindBuffer(gl.ARRAY_BUFFER, triangleVertexPositionBuffer); 160 | gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, triangleVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); 161 | setMatrixUniforms(); 162 | gl.drawArrays(gl.TRIANGLES, 0, triangleVertexPositionBuffer.numItems); 163 | mat4.translate(mvMatrix, mvMatrix, [3.0, 0.0, 0.0]); 164 | gl.bindBuffer(gl.ARRAY_BUFFER, squareVertexPositionBuffer); 165 | gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, squareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0); 166 | setMatrixUniforms(); 167 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, squareVertexPositionBuffer.numItems); 168 | } 169 | function webGLStart() { 170 | //var canvas = document.getElementById("lesson01-canvas"); 171 | let canvas = new Window(); 172 | canvas.onevent = function(event) { 173 | //puts("event: " + JSON.stringify(event)); 174 | }; 175 | initGL(canvas); 176 | initShaders(); 177 | initBuffers(); 178 | gl.clearColor(0.0, 0.1, 0.0, 1.0); 179 | gl.enable(gl.DEPTH_TEST); 180 | drawScene(); 181 | } 182 | 183 | 184 | 185 | webGLStart(); 186 | -------------------------------------------------------------------------------- /src/jslib/context.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::not_unsafe_ptr_arg_deref)] 2 | use crate::jslib::eventloop; 3 | use mozjs::jsapi::GetCurrentRealmOrNull; 4 | use mozjs::jsapi::{GetRealmPrivate, HandleObject, JSContext, JSObject, SetRealmPrivate}; 5 | 6 | use std::any::TypeId; 7 | use std::collections::HashMap; 8 | use std::os::raw::c_void; 9 | use std::ptr; 10 | use std::sync::RwLock; 11 | 12 | pub struct RJSContext { 13 | pub cx: *mut JSContext, 14 | pub global: HandleObject, 15 | cls_protos: RwLock>, 16 | } 17 | 18 | #[derive(Copy, Clone)] 19 | pub struct ClassInfo { 20 | pub constr: *mut JSObject, 21 | pub prototype: *mut JSObject, 22 | } 23 | 24 | impl RJSContext { 25 | pub fn new(cx: *mut JSContext, global: HandleObject) -> RJSContext { 26 | RJSContext { 27 | cx, 28 | global, 29 | cls_protos: RwLock::new(HashMap::new()), 30 | } 31 | } 32 | 33 | pub fn get_classinfo_for(&self) -> Option { 34 | self.cls_protos 35 | .read() 36 | .unwrap() 37 | .get(&TypeId::of::()) 38 | .copied() 39 | } 40 | pub fn set_classinfo_for(&self, ci: ClassInfo) { 41 | self.cls_protos 42 | .write() 43 | .unwrap() 44 | .insert(TypeId::of::(), ci); 45 | } 46 | } 47 | 48 | pub type RJSHandle = eventloop::Handle; 49 | pub type RJSRemote = eventloop::Remote; 50 | 51 | pub type RuntimePrivate = eventloop::WeakHandle; 52 | 53 | pub fn store_private(cx: *mut JSContext, handle: &RJSHandle) { 54 | let compartment = unsafe { GetCurrentRealmOrNull(cx) }; 55 | let private = Box::new(handle.downgrade()); 56 | unsafe { 57 | SetRealmPrivate(compartment, Box::into_raw(private) as *mut c_void); 58 | } 59 | } 60 | 61 | pub fn clear_private(cx: *mut JSContext) { 62 | let compartment = unsafe { GetCurrentRealmOrNull(cx) }; 63 | let private = unsafe { GetRealmPrivate(compartment) as *mut RuntimePrivate }; 64 | if !private.is_null() { 65 | unsafe { 66 | let _ = Box::from_raw(private); 67 | SetRealmPrivate(compartment, ptr::null_mut()); 68 | } 69 | } 70 | } 71 | 72 | pub fn get_handle(cx: *mut JSContext) -> Option { 73 | let compartment = unsafe { GetCurrentRealmOrNull(cx) }; 74 | let private = unsafe { GetRealmPrivate(compartment) as *const RuntimePrivate }; 75 | if private.is_null() { 76 | None 77 | } else { 78 | unsafe { (*private).upgrade() } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/jslib/eventloop.rs: -------------------------------------------------------------------------------- 1 | extern crate futures; 2 | extern crate tokio_core; 3 | 4 | use futures::future; 5 | use futures::sync::mpsc; 6 | use futures::sync::oneshot; 7 | use futures::Future; 8 | use futures::Stream; 9 | use tokio_core::reactor as tokio; 10 | 11 | use downcast::Any; 12 | use mozjs::jsapi::{ 13 | GCReason, Heap, JSGCInvocationKind, JSTracer, JS_AddExtraGCRootsTracer, 14 | JS_RemoveExtraGCRootsTracer, NonIncrementalGC, 15 | }; 16 | use mozjs::rust::{GCMethods, Runtime, Trace}; 17 | use slab::Slab; 18 | use std::cell::RefCell; 19 | use std::clone::Clone; 20 | 21 | use std::marker::PhantomData; 22 | use std::rc; 23 | use std::rc::Rc; 24 | use std::sync::{Arc, Weak}; 25 | 26 | use std::os::raw::c_void; 27 | 28 | //type EventLoopFn = for<'t> Fn(&'t T, Handle); 29 | type Message = (Remote, Box) -> ()>); 30 | 31 | type RefSlab = RefCell>; 32 | type RefSlabEl = RefCell>>; 33 | 34 | trait Traceable: Any { 35 | fn get_trace(&self) -> &dyn Trace; 36 | } 37 | 38 | unsafe impl Trace for dyn Traceable { 39 | unsafe fn trace(&self, trc: *mut JSTracer) { 40 | self.get_trace().trace(trc) 41 | } 42 | } 43 | 44 | impl Traceable for T { 45 | fn get_trace(&self) -> &dyn Trace { 46 | self 47 | } 48 | } 49 | downcast!(dyn Traceable); 50 | 51 | unsafe extern "C" fn ref_slab_tracer(trc: *mut JSTracer, data: *mut c_void) { 52 | if data.is_null() { 53 | return; 54 | } 55 | 56 | let slab = data as *mut rc::Weak; 57 | let slab = (*slab).upgrade(); 58 | 59 | if let Some(ref slab) = slab { 60 | slab.borrow().iter().for_each(|item| { 61 | let (_, item) = item; 62 | let optitem = item.borrow(); 63 | if let Some(ref i) = *optitem { 64 | i.trace(trc); 65 | } 66 | }) 67 | } 68 | } 69 | 70 | pub fn run(rt: &Runtime, t: T, first: F) 71 | where 72 | T: Sized, 73 | F: FnOnce(Handle) -> (), 74 | { 75 | let mut core = tokio::Core::new().unwrap(); 76 | 77 | let (tx, rx) = mpsc::unbounded::>(); 78 | let tx = Arc::new(tx); 79 | 80 | let slab: Rc = Rc::new(RefCell::new(Slab::new())); 81 | 82 | let core_handle = core.handle(); 83 | 84 | let data = rc::Rc::new(t); 85 | 86 | let remote = Remote(tx); 87 | let handle = Handle { 88 | remote, 89 | thandle: core_handle.clone(), 90 | data: Rc::clone(&data), 91 | slab: Rc::downgrade(&slab), 92 | }; 93 | 94 | let extradata: *mut rc::Weak = Box::into_raw(Box::new(rc::Weak::clone(&handle.slab))); 95 | unsafe { JS_AddExtraGCRootsTracer(rt.cx(), Some(ref_slab_tracer), extradata as *mut _) }; 96 | 97 | let _: Result<(), ()> = core.run(future::lazy(|| { 98 | first(handle); 99 | 100 | rx.for_each(|tuple| -> Result<(), ()> { 101 | let (remote, f) = tuple; 102 | let handle = Handle { 103 | remote, 104 | thandle: core_handle.clone(), 105 | data: Rc::clone(&data), 106 | slab: Rc::downgrade(&slab), 107 | }; 108 | unsafe { 109 | NonIncrementalGC(rt.cx(), JSGCInvocationKind::GC_SHRINK, GCReason::NO_REASON) 110 | }; 111 | f(handle); 112 | unsafe { 113 | NonIncrementalGC(rt.cx(), JSGCInvocationKind::GC_SHRINK, GCReason::NO_REASON) 114 | }; 115 | Ok(()) 116 | }) 117 | })); 118 | 119 | unsafe { 120 | JS_RemoveExtraGCRootsTracer(rt.cx(), Some(ref_slab_tracer), extradata as *mut _); 121 | Box::from_raw(extradata); 122 | } 123 | } 124 | 125 | pub struct Handle { 126 | remote: Remote, 127 | thandle: tokio::Handle, 128 | data: rc::Rc, 129 | slab: rc::Weak>>, 130 | } 131 | 132 | impl Clone for Handle { 133 | fn clone(&self) -> Self { 134 | Handle { 135 | remote: self.remote.clone(), 136 | thandle: self.thandle.clone(), 137 | data: Rc::clone(&self.data), 138 | slab: rc::Weak::clone(&self.slab), 139 | } 140 | } 141 | } 142 | 143 | impl Handle { 144 | pub fn core_handle(&self) -> &tokio::Handle { 145 | &self.thandle 146 | } 147 | pub fn remote(&self) -> &Remote { 148 | &self.remote 149 | } 150 | pub fn get(&self) -> &T { 151 | &self.data 152 | } 153 | 154 | pub fn downgrade(&self) -> WeakHandle { 155 | WeakHandle { 156 | remote: Arc::downgrade(&self.remote.0), 157 | thandle: self.thandle.clone(), 158 | data: rc::Rc::downgrade(&self.data), 159 | slab: rc::Weak::clone(&self.slab), 160 | } 161 | } 162 | 163 | pub fn store_new(&self, v: V) -> RemoteRef 164 | where 165 | Heap: Default + Trace, 166 | { 167 | let slab = self.slab.upgrade().unwrap(); 168 | let mut slab = slab.borrow_mut(); 169 | 170 | let valbox = Box::new(Heap::default()); 171 | valbox.set(v); 172 | 173 | let key = slab.insert(RefCell::new(Some(valbox))); 174 | 175 | let (tx, rx) = oneshot::channel::<()>(); 176 | let weakslab = rc::Weak::clone(&self.slab); 177 | self.thandle.spawn(rx.then(move |_| { 178 | let slab = weakslab.upgrade().unwrap(); 179 | let mut slab = slab.borrow_mut(); 180 | let r = slab.remove(key); 181 | let o = r.into_inner(); 182 | if let Some(p) = o { 183 | let _: Box = unsafe { p.downcast_unchecked::() }; 184 | } 185 | 186 | Ok(()) 187 | })); 188 | 189 | RemoteRef { 190 | tx: Arc::new(tx), 191 | key, 192 | marker: PhantomData, 193 | } 194 | } 195 | pub fn retrieve(&self, rref: &RemoteRef) -> Option { 196 | let slab = self.slab.upgrade().unwrap(); 197 | let slab = slab.borrow(); 198 | let r = unsafe { slab.get_unchecked(rref.key) }; 199 | let o = r.replace(None); 200 | o.map(|p| { 201 | let b: Box = unsafe { p.downcast_unchecked::() }; 202 | *b 203 | }) 204 | } 205 | 206 | pub fn retrieve_copy(&self, rref: &RemoteRef) -> Option { 207 | let slab = self.slab.upgrade().unwrap(); 208 | let slab = slab.borrow(); 209 | let r = unsafe { slab.get_unchecked(rref.key) }; 210 | let o = &*r.borrow(); 211 | match *o { 212 | None => None, 213 | Some(ref p) => { 214 | let v: &V = unsafe { p.downcast_ref_unchecked::() }; 215 | Some(*v) 216 | } 217 | } 218 | } 219 | } 220 | 221 | #[derive(Clone)] 222 | pub struct WeakHandle { 223 | remote: Weak>>, 224 | thandle: tokio::Handle, 225 | data: rc::Weak, 226 | slab: rc::Weak>>, 227 | } 228 | 229 | impl WeakHandle { 230 | pub fn upgrade(&self) -> Option> { 231 | let remote = self.remote.upgrade(); 232 | let data = self.data.upgrade(); 233 | remote.and_then(|remote| { 234 | data.map(|data| Handle { 235 | remote: Remote(remote), 236 | thandle: self.thandle.clone(), 237 | data, 238 | slab: rc::Weak::clone(&self.slab), 239 | }) 240 | }) 241 | } 242 | } 243 | 244 | #[derive(Clone)] 245 | pub struct RemoteRef { 246 | tx: Arc>, 247 | key: usize, 248 | marker: PhantomData<*const V>, 249 | } 250 | 251 | unsafe impl Send for RemoteRef {} 252 | 253 | pub struct Remote(Arc>>); 254 | impl Clone for Remote { 255 | fn clone(&self) -> Self { 256 | Remote(Arc::clone(&self.0)) 257 | } 258 | } 259 | 260 | impl Remote { 261 | pub fn spawn(&self, f: F) 262 | where 263 | F: FnOnce(Handle) + Send + 'static, 264 | { 265 | let me: Remote = (*self).clone(); 266 | let myfunc: Box) -> () + 'static> = Box::new(f); 267 | //let myfunc: Box> = Box::new( |a, b| f(a, b) ); 268 | let fb = (me, myfunc); 269 | (*self.0).unbounded_send(fb).unwrap() 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /src/jslib/globals.rs: -------------------------------------------------------------------------------- 1 | // const METHODS: &'static [JSFunctionSpec] = &[ 2 | // JSFunctionSpec { 3 | // name: b"addEventListener\0" as *const u8 as *const libc::c_char, 4 | // call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() }, 5 | // nargs: 2, 6 | // flags: JSPROP_ENUMERATE as u16, 7 | // selfHostedName: 0 as *const libc::c_char 8 | // }, 9 | // JSFunctionSpec { 10 | // name: b"removeEventListener\0" as *const u8 as *const libc::c_char, 11 | // call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() }, 12 | // nargs: 2, 13 | // flags: JSPROP_ENUMERATE as u16, 14 | // selfHostedName: 0 as *const libc::c_char 15 | // }, 16 | // JSFunctionSpec { 17 | // name: b"dispatchEvent\0" as *const u8 as *const libc::c_char, 18 | // call: JSNativeWrapper { op: Some(generic_method), info: ptr::null() }, 19 | // nargs: 1, 20 | // flags: JSPROP_ENUMERATE as u16, 21 | // selfHostedName: 0 as *const libc::c_char 22 | // }, 23 | // JSFunctionSpec { 24 | // name: ptr::null(), 25 | // call: JSNativeWrapper { op: None, info: ptr::null() }, 26 | // nargs: 0, 27 | // flags: 0, 28 | // selfHostedName: ptr::null() 29 | // } 30 | // ]; 31 | 32 | // static CLASS: JSClass = JSClass { 33 | // name: b"EventTargetPrototype\0" as *const u8 as *const libc::c_char, 34 | // flags: 0, 35 | // cOps: 0 as *const _, 36 | // reserved: [0 as *mut _; 3] 37 | // }; 38 | 39 | // unsafe extern "C" fn generic_method(_: *mut JSContext, _: u32, _: *mut Value) -> bool { 40 | // true 41 | // } 42 | 43 | // fn defineAllGlobals() { 44 | 45 | // } 46 | -------------------------------------------------------------------------------- /src/jslib/jsclass.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::not_unsafe_ptr_arg_deref)] 2 | use crate::jslib::context; 3 | use crate::jslib::context::{ClassInfo, RJSContext}; 4 | use crate::jslib::jsfn::RJSFn; 5 | use libc::c_uint; 6 | use mozjs::jsapi::{ 7 | CallArgs, Handle, HandleObject, JSClass, JSContext, JSFunctionSpec, JSNativeWrapper, JSObject, 8 | JSPropertySpec, JS_GetConstructor, JS_GetInstancePrivate, JS_InitClass, JS_SetPrivate, 9 | JSCLASS_RESERVED_SLOTS_SHIFT, 10 | }; 11 | use mozjs::JSCLASS_RESERVED_SLOTS_MASK; 12 | use std::ptr; 13 | 14 | pub const JSCLASS_HAS_PRIVATE: c_uint = 1; 15 | 16 | pub const fn jsclass_has_reserved_slots(n: c_uint) -> c_uint { 17 | (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT 18 | } 19 | 20 | pub const fn null_wrapper() -> JSNativeWrapper { 21 | JSNativeWrapper::ZERO 22 | } 23 | 24 | pub const fn null_property() -> JSPropertySpec { 25 | JSPropertySpec::ZERO 26 | } 27 | 28 | pub const fn null_function() -> JSFunctionSpec { 29 | JSFunctionSpec::ZERO 30 | } 31 | 32 | pub trait GetJSClassInfo 33 | where 34 | Self: Sized + 'static, 35 | { 36 | fn class_info(rcx: &RJSContext) -> Option; 37 | } 38 | 39 | impl GetJSClassInfo for () { 40 | fn class_info(_rcx: &RJSContext) -> Option { 41 | Some(ClassInfo { 42 | constr: ptr::null_mut(), 43 | prototype: ptr::null_mut(), 44 | }) 45 | } 46 | } 47 | 48 | pub trait JSClassInitializer { 49 | type Private; 50 | #[allow(clippy::missing_safety_doc)] 51 | unsafe fn init_class(rcx: &RJSContext, obj: HandleObject) -> context::ClassInfo 52 | where 53 | Self: Sized + 'static, 54 | { 55 | let cls = Self::class(); 56 | let parent_info = Self::parent_info(rcx).unwrap(); 57 | let constr = Self::constr(); 58 | let (constrfn, constrnargs) = constr 59 | .map(|c| (Some(c.func()), c.nargs())) 60 | .unwrap_or((None, 0)); 61 | let props = Self::properties(); 62 | let fns = Self::functions(); 63 | let static_props = Self::static_properties(); 64 | let static_fns = Self::static_functions(); 65 | 66 | rooted!(in(rcx.cx) let parent_proto = parent_info.prototype); 67 | 68 | rooted!(in(rcx.cx) let proto = JS_InitClass( 69 | rcx.cx, 70 | obj, 71 | parent_proto.handle().into(), 72 | cls, 73 | constrfn, 74 | constrnargs, 75 | props, 76 | fns, 77 | static_props, 78 | static_fns, 79 | )); 80 | 81 | let constr = JS_GetConstructor(rcx.cx, proto.handle().into()); 82 | 83 | let classinfo = context::ClassInfo { 84 | constr, 85 | prototype: proto.get(), 86 | }; 87 | 88 | rcx.set_classinfo_for::(classinfo); 89 | 90 | classinfo 91 | } 92 | fn class() -> *const JSClass; 93 | 94 | // Get the ClassInfo for this class, if it has been defined already. 95 | fn class_info(rcx: &RJSContext) -> Option; 96 | 97 | // This unfortunately has to be provided by the macro in order for 98 | // init_class to be able to obtain the parent prototype. 99 | fn parent_info(rcx: &RJSContext) -> Option; 100 | fn functions() -> *const JSFunctionSpec; 101 | fn properties() -> *const JSPropertySpec; 102 | fn static_functions() -> *const JSFunctionSpec { 103 | ptr::null() 104 | } 105 | fn static_properties() -> *const JSPropertySpec { 106 | ptr::null() 107 | } 108 | fn constr() -> Option> { 109 | None 110 | } 111 | 112 | fn get_private<'a>( 113 | cx: *mut JSContext, 114 | obj: HandleObject, 115 | args: Option, 116 | ) -> Option<&'a Self::Private> { 117 | unsafe { 118 | let ptr = JS_GetInstancePrivate( 119 | cx, 120 | obj, 121 | Self::class(), 122 | args.map_or(ptr::null_mut(), |mut args| &mut args), 123 | ) as *const Self::Private; 124 | if ptr.is_null() { 125 | None 126 | } else { 127 | Some(&*ptr) 128 | } 129 | } 130 | } 131 | 132 | fn jsnew_with_private(rcx: &RJSContext, private: *mut Self::Private) -> *mut JSObject 133 | where 134 | Self: Sized + 'static, 135 | { 136 | let info = rcx 137 | .get_classinfo_for::() 138 | .unwrap_or_else(|| panic!("{} must be defined in this compartment!", "?")); 139 | 140 | let jsobj = unsafe { 141 | ::mozjs::jsapi::JS_NewObjectWithGivenProto( 142 | rcx.cx, 143 | Self::class(), 144 | Handle::from_marked_location(&info.prototype), 145 | ) 146 | }; 147 | 148 | unsafe { 149 | JS_SetPrivate(jsobj, private as *mut ::std::os::raw::c_void); 150 | } 151 | 152 | jsobj 153 | } 154 | } 155 | 156 | #[macro_export] 157 | macro_rules! compute_once { 158 | ($type:ty = $static:expr ; $body:tt) => { 159 | unsafe { 160 | static mut VAL: $type = $static; 161 | static ONCE: Once = Once::new(); 162 | 163 | ONCE.call_once(|| { 164 | VAL = $body; 165 | }); 166 | 167 | VAL 168 | } 169 | }; 170 | } 171 | 172 | #[macro_export] 173 | macro_rules! c_str { 174 | ($str:expr) => { 175 | concat!($str, "\0").as_ptr() as *const ::std::os::raw::c_char 176 | }; 177 | } 178 | 179 | #[macro_export] 180 | macro_rules! js_class { 181 | ($name:ident extends $parent:ty [$flags:expr] $($body:tt)*) => { 182 | //trace_macros!{true} 183 | __jsclass_parse!{$name [$parent] [$flags] [()] [] [] [] [] $($body)*} 184 | }; 185 | } 186 | 187 | #[macro_export] 188 | macro_rules! __jsclass_parsed { 189 | ($name:ident [$parent:ty] [$flags:expr] [$private:ty] [$($constr:tt)*] [$($fns:tt)*] 190 | [$($ops:tt)*] [$($props:tt)*]) => { 191 | 192 | $( __jsclass_toplevel!{_constr $constr} )* 193 | //$( __jsclass_toplevel!{_fn $fns} )* 194 | $( __jsclass_toplevel!{_op $ops} )* 195 | $( __jsclass_toplevel!{_prop $props} )* 196 | 197 | 198 | 199 | impl JSClassInitializer for $name { 200 | type Private = $private; 201 | 202 | fn class() -> *const JSClass { 203 | compute_once!{ 204 | *const JSClass = ptr::null(); 205 | { 206 | Box::into_raw(Box::new( JSClass { 207 | name: c_str!(stringify!($name)), 208 | flags: $flags, 209 | cOps: __jsclass_ops!([] $($ops)*), 210 | spec: ptr::null(), 211 | ext: ptr::null(), 212 | oOps: ptr::null(), 213 | })) 214 | } 215 | } 216 | } 217 | 218 | fn class_info(rcx: &RJSContext) -> Option<$crate::jslib::context::ClassInfo> { 219 | rcx.get_classinfo_for::() 220 | } 221 | 222 | fn parent_info(rcx: &RJSContext) -> Option<$crate::jslib::context::ClassInfo> { 223 | use $crate::jslib::jsclass::GetJSClassInfo; 224 | //rcx.get_classinfo_for::<$parent>() 225 | <$parent>::class_info(rcx) 226 | } 227 | 228 | fn constr() -> Option> { 229 | 230 | $( 231 | __jsclass_constrspec!{$constr} 232 | )* 233 | 234 | #[allow(unreachable_code)] 235 | None 236 | } 237 | 238 | fn functions() -> *const JSFunctionSpec { 239 | compute_once!{ 240 | *const JSFunctionSpec = ptr::null(); 241 | { 242 | let mut fspecs: Vec = vec![]; 243 | 244 | $( 245 | __jsclass_functionspec!{fspecs $fns} 246 | )* 247 | fspecs.push(null_function()); 248 | 249 | let fboxptr = Box::into_raw(fspecs.into_boxed_slice()); 250 | &(*fboxptr)[0] 251 | } 252 | } 253 | } 254 | 255 | fn properties() -> *const JSPropertySpec { 256 | compute_once!{ 257 | *const JSPropertySpec = ptr::null(); 258 | { 259 | let mut pspecs: Vec = vec![]; 260 | 261 | $( 262 | __jsclass_propertyspec!{pspecs $props} 263 | )* 264 | pspecs.push(null_property()); 265 | 266 | let pboxptr = Box::into_raw(pspecs.into_boxed_slice()); 267 | &(*pboxptr)[0] 268 | } 269 | } 270 | } 271 | } 272 | 273 | 274 | 275 | } 276 | } // macro_rules! js_class 277 | 278 | #[macro_export] 279 | macro_rules! nothing { 280 | ($($any:tt)*) => {}; 281 | } 282 | 283 | #[macro_export] 284 | macro_rules! __jsclass_parse { 285 | ($cname:ident $parent:tt $flags:tt $private:tt $constr:tt $fns:tt $ops:tt $props:tt ) => { 286 | __jsclass_parsed!{$cname $parent $flags $private $constr $fns $ops $props} 287 | }; 288 | ($cname:ident $parent:tt $flags:tt [$private:ty] $constr:tt $fns:tt $ops:tt $props:tt 289 | private: $ty:ty, $($rest:tt)*) => { 290 | __jsclass_parse!{$cname $parent $flags [$ty] $constr $fns $ops $props 291 | $($rest)*} 292 | }; 293 | ($cname:ident $parent:tt $flags:tt $private:tt $constr:tt [$($fns:tt)*] $ops:tt $props:tt 294 | fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*} $($rest:tt)*) => { 295 | __jsclass_parse!{$cname $parent $flags $private $constr [$($fns)* 296 | [fn $name $args -> JSRet<$ret> { $($body)* }] 297 | ] $ops $props 298 | $($rest)*} 299 | }; 300 | ($cname:ident $parent:tt $flags:tt $private:tt [$($constr:tt)*] $fns:tt $ops:tt $props:tt 301 | @constructor fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*} $($rest:tt)*) => { 302 | __jsclass_parse!{$cname $parent $flags $private [$($constr)* 303 | [fn $name $args -> JSRet<$ret> { $($body)* }] 304 | ] $fns $ops $props 305 | $($rest)*} 306 | }; 307 | ($cname:ident $parent:tt $flags:tt $private:tt $constr:tt $fns:tt [$($ops:tt)*] $props:tt 308 | @op($op:ident) fn $name:ident $args:tt -> $ret:ty {$($body:tt)*} $($rest:tt)*) => { 309 | __jsclass_parse!{$cname $parent $flags $private $constr $fns [$($ops)* 310 | [$op fn $name $args -> $ret { $($body)* }] 311 | ] $props 312 | $($rest)*} 313 | }; 314 | ($cname:ident $parent:tt $flags:tt $private:tt $constr:tt $fns:tt $ops:tt [$($props:tt)*] 315 | @prop $name:ident $body:tt $($rest:tt)*) => { 316 | __jsclass_parse!{$cname $parent $flags $private $constr $fns $ops [$($props)* 317 | [$name $body] 318 | ] 319 | $($rest)*} 320 | }; 321 | } 322 | 323 | #[macro_export] 324 | macro_rules! __jsclass_ops { 325 | ([] $($body:tt)*) => { 326 | __jsclass_ops!{[{None}, {None}, {None}, {None}, {None}, {None}, {None}, {None}, {None}, 327 | {None}, {None}, {None}] $($body)* } 328 | }; 329 | 330 | ([$ap:tt, $ca:tt, $co:tt, $dp:tt, $e:tt, $f:tt, $gp:tt, $hi:tt, $mr:tt, $r:tt, $sp:tt, 331 | $t:tt] ) => { 332 | &JSClassOps { 333 | addProperty: $ap, 334 | delProperty: $dp, 335 | enumerate: $e, 336 | newEnumerate: None, 337 | resolve: $r, 338 | mayResolve: $mr, 339 | finalize: $f, 340 | call: $ca, 341 | hasInstance: $hi, 342 | construct: $co, 343 | trace: $t, 344 | //getProperty: $gp, 345 | //setProperty: $sp, 346 | } 347 | }; 348 | ( 349 | [$ap:tt, $ca:tt, $co:tt, $dp:tt, $e:tt, $f:tt, $gp:tt, $hi:tt, $mr:tt, $r:tt, $sp:tt, 350 | $t:tt] 351 | [_op [addProperty fn $fname:ident $args:tt -> $ret:ty { $($fbody:tt)* }]] 352 | $($body:tt)* 353 | ) => { 354 | __jsclass_ops!{ 355 | [{Some($fname)}, $ca, $co, $dp, $e, $f, $gp, $hi, $mr, $r, $sp, $t] 356 | $($body)* 357 | } 358 | }; 359 | ( 360 | [$ap:tt, $ca:tt, $co:tt, $dp:tt, $e:tt, $f:tt, $gp:tt, $hi:tt, $mr:tt, $r:tt, $sp:tt, 361 | $t:tt] 362 | [finalize fn $fname:ident $args:tt -> $ret:ty { $($fbody:tt)* }] 363 | $($body:tt)* 364 | ) => { 365 | __jsclass_ops!{ 366 | [$ap, $ca, $co, $dp, $e, {Some($fname)}, $gp, $hi, $mr, $r, $sp, $t] 367 | $($body)* 368 | } 369 | }; 370 | ( 371 | [$ap:tt, $ca:tt, $co:tt, $dp:tt, $e:tt, $f:tt, $gp:tt, $hi:tt, $mr:tt, $r:tt, $sp:tt, 372 | $t:tt] 373 | [call fn $fname:ident $args:tt -> $ret:ty { $($fbody:tt)* }] 374 | $($body:tt)* 375 | ) => { 376 | __jsclass_ops!{ 377 | [$ap, {Some($fname)}, $co, $dp, $e, $f, $gp, $hi, $mr, $r, $sp, $t] 378 | $($body)* 379 | } 380 | }; 381 | ( 382 | [$ap:tt, $ca:tt, $co:tt, $dp:tt, $e:tt, $f:tt, $gp:tt, $hi:tt, $mr:tt, $r:tt, $sp:tt, 383 | $t:tt] 384 | [$oname:ident fn $fname:ident $args:tt -> $ret:ty { $($fbody:tt)* }] 385 | $($body:tt)* 386 | ) => { 387 | compile_error!("Bad op name" + stringify!($oname)) 388 | }; 389 | ( 390 | $ops:tt 391 | [$cname:ident $cbody:tt] 392 | $($body:tt)* 393 | ) => { 394 | __jsclass_ops!{$ops $($body)* } 395 | }; 396 | } 397 | 398 | #[macro_export] 399 | macro_rules! __jsclass_constrspec { 400 | ([fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*}]) => { 401 | return Some(Box::new($name {})); 402 | }; 403 | } 404 | 405 | #[macro_export] 406 | macro_rules! __jsclass_functionspec { 407 | ($vec:ident [fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*}]) => { 408 | { 409 | 410 | __jsclass_toplevel!{_fn [fn $name $args -> JSRet<$ret> { $($body)*}]} 411 | 412 | $vec.push( 413 | JSFunctionSpec { 414 | //name: b"log\0" as *const u8 as *const c_char, 415 | //name: CString::new(stringify!($name)).unwrap().into_raw(), 416 | name: mozjs::jsapi::JSPropertySpec_Name { string_:concat!(stringify!($name), "\0").as_ptr() as *const ::std::os::raw::c_char}, 417 | selfHostedName: ptr::null(), 418 | flags: JSPROP_ENUMERATE as u16, 419 | nargs: $name{}.nargs() as u16, 420 | call: JSNativeWrapper { 421 | op: Some($name{}.func()), 422 | info: ptr::null(), 423 | }, 424 | } 425 | ); 426 | } 427 | }; 428 | } 429 | 430 | #[macro_export] 431 | macro_rules! __jsclass_toplevel { 432 | (_fn [ fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*} ]) => { 433 | js_fn!{fn $name $args -> JSRet<$ret> { $($body)* } } 434 | }; 435 | (_constr [ fn $name:ident $args:tt -> JSRet<$ret:ty> {$($body:tt)*} ]) => { 436 | js_fn!{fn $name $args -> JSRet<$ret> { $($body)* } } 437 | }; 438 | (_op [$oname:ident fn $name:ident $args:tt -> $ret:ty {$($body:tt)*} ]) => { 439 | #[allow(non_snake_case)] 440 | unsafe extern "C" fn $name $args -> $ret { $($body)* } 441 | }; 442 | (_prop [ $name:ident {} ]) => {}; 443 | (_prop [ $name:ident { get fn $fname:ident $args:tt -> JSRet<$ret:ty> 444 | {$($body:tt)*} $($rest:tt)* } ] ) => { 445 | js_fn!{fn $fname $args -> JSRet<$ret> { $($body)* } } 446 | __jsclass_toplevel!{_prop [ $name { $($rest)* } ]} 447 | }; 448 | (_prop [ $name:ident { set fn $fname:ident $args:tt -> JSRet<$ret:ty> 449 | {$($body:tt)*} $($rest:tt)* } ] ) => { 450 | js_fn!{fn $fname $args -> JSRet<$ret> { $($body)* } } 451 | __jsclass_toplevel!{_prop [ $name { $($rest)* } ]} 452 | }; 453 | } 454 | 455 | #[macro_export] 456 | macro_rules! __jsclass_propertyspec { 457 | ($vec:ident [$name:ident {$($rest:tt)*}]) => { 458 | __jsclass_propertyspec!{{$vec, null_wrapper(), null_wrapper()} @prop $name { $($rest)* }} 459 | }; 460 | ({$vec:ident, $getter:expr, $setter:expr} 461 | @prop $name:ident {}) => { 462 | $vec.push( 463 | JSPropertySpec { 464 | //name: b"window\0" as *const u8 as *const c_char, 465 | //name: CString::new(stringify!($name)).unwrap().into_raw(), 466 | name: mozjs::jsapi::JSPropertySpec_Name { string_:concat!(stringify!($name), "\0").as_ptr() 467 | as *const ::std::os::raw::c_char}, 468 | flags: (JSPROP_ENUMERATE) as u8, 469 | u: mozjs::jsapi::JSPropertySpec_AccessorsOrValue { 470 | accessors: mozjs::jsapi::JSPropertySpec_AccessorsOrValue_Accessors { 471 | getter: mozjs::jsapi::JSPropertySpec_Accessor { 472 | //native: JSNativeWrapper { op: Some(h), info: ptr::null() }, 473 | native: $getter, 474 | }, 475 | setter: mozjs::jsapi::JSPropertySpec_Accessor { 476 | //native: JSNativeWrapper { op: None, info: ptr::null() }, 477 | native: $setter, 478 | } 479 | } 480 | } 481 | //getter: $getter, 482 | //setter: $setter, 483 | }, 484 | ); 485 | }; 486 | 487 | ({$vec:ident, $getter:expr, $setter:expr} 488 | @prop $name:ident { get fn $fname:ident $args:tt -> JSRet<$ret:ty> 489 | {$($body:tt)*} $($rest:tt)* } ) => { 490 | __jsclass_propertyspec!{{$vec, JSNativeWrapper { 491 | op: Some($fname{}.func()), info: ptr::null() 492 | }, $setter} 493 | @prop $name { $($rest)* }} 494 | }; 495 | ({$vec:ident, $getter:expr, $setter:expr} 496 | @prop $name:ident { set fn $fname:ident $args:tt -> JSRet<$ret:ty> 497 | {$($body:tt)*} $($rest:tt)* } ) => { 498 | __jsclass_propertyspec!{{$vec, $getter, JSNativeWrapper { 499 | op: Some($fname{}.func()), info: ptr::null() 500 | }} 501 | @prop $name { $($rest)* }} 502 | }; 503 | } 504 | -------------------------------------------------------------------------------- /src/jslib/jsfn.rs: -------------------------------------------------------------------------------- 1 | use mozjs::jsapi::HandleObject; 2 | use mozjs::jsapi::JSContext; 3 | use mozjs::jsapi::JSFunction; 4 | use mozjs::jsapi::JS_DefineFunction; 5 | use mozjs::jsapi::Value; 6 | 7 | use std::ffi::CStr; 8 | 9 | pub type JSRet = Result>; 10 | 11 | pub type RJSNativeRaw = unsafe extern "C" fn(*mut JSContext, u32, *mut Value) -> bool; 12 | 13 | pub trait RJSFn { 14 | fn func(&self) -> RJSNativeRaw; 15 | fn name(&self) -> &'static CStr; 16 | fn nargs(&self) -> u32; 17 | #[allow(clippy::missing_safety_doc)] 18 | unsafe fn define_on( 19 | &self, 20 | cx: *mut JSContext, 21 | this: HandleObject, 22 | flags: u32, 23 | ) -> *mut JSFunction { 24 | JS_DefineFunction( 25 | cx, 26 | this, 27 | self.name().as_ptr(), 28 | Some(self.func()), 29 | self.nargs(), 30 | flags, 31 | ) 32 | } 33 | } 34 | 35 | #[macro_export] 36 | macro_rules! js_fn_raw { 37 | (fn $name:ident($($param:ident : $type:ty),*) -> JSRet<$ret:ty> $body:tt) => ( 38 | #[allow(non_snake_case)] 39 | unsafe extern "C" fn $name (cx: *mut JSContext, argc: u32, vp: *mut Value) -> bool { 40 | let args = CallArgs::from_vp(vp, argc); 41 | let handle = context::get_handle(cx).unwrap(); 42 | let rcx = handle.get(); 43 | assert!(rcx.cx == cx); 44 | 45 | fn rustImpl($($param : $type),*) -> JSRet<$ret> $body 46 | 47 | let result = rustImpl(rcx, &handle, args); 48 | match result { 49 | Ok(v) => { 50 | v.to_jsval(cx, mozjs::rust::MutableHandle::from_raw(args.rval())); 51 | true 52 | }, 53 | Err(Some(s)) => { 54 | let cstr = CString::new(s).unwrap(); 55 | JS_ReportErrorUTF8(cx, cstr.as_ptr() as *const libc::c_char); 56 | false 57 | }, 58 | Err(None) => { 59 | false 60 | }, 61 | } 62 | 63 | } 64 | ) 65 | } 66 | 67 | #[macro_export] 68 | macro_rules! js_fn { 69 | (fn $name:ident ($($args:tt)*) -> JSRet<$ret:ty> $body:tt) => { 70 | #[allow(non_camel_case_types)] 71 | pub struct $name; 72 | 73 | impl $name { 74 | 75 | js_fn_raw!{fn rawfunc (_rcx: &RJSContext, 76 | _handle: &RJSHandle, 77 | args: CallArgs) -> JSRet<$ret> { 78 | js_unpack_args!({stringify!($name), _rcx, _handle, args} ($($args)*)); 79 | 80 | $body 81 | 82 | }} 83 | } 84 | 85 | impl RJSFn for $name { 86 | 87 | fn func(&self) -> jslib::jsfn::RJSNativeRaw { 88 | $name::rawfunc 89 | } 90 | 91 | fn name(&self) -> &'static ::std::ffi::CStr { 92 | unsafe { 93 | ::std::ffi::CStr::from_bytes_with_nul_unchecked( 94 | concat!(stringify!($name), "\0").as_bytes()) 95 | } 96 | } 97 | 98 | fn nargs(&self) -> u32 { 99 | _js_unpack_args_count!($($args)*,) 100 | } 101 | 102 | } 103 | 104 | 105 | } 106 | 107 | } 108 | 109 | #[macro_export] 110 | macro_rules! js_callback { 111 | ($rcx:ident, move |$($param:ident : $type:ty),*| $body:tt) => ( 112 | (move |tx: Arc>| { 113 | move |$($param : $type),*| { 114 | let _ac = JSAutoRealm::new($rcx.cx, $rcx.global.get()); 115 | 116 | let ret = (|$($param : $type),*| $body) ($($param),*); 117 | 118 | drop(tx); // this drops the handle that keeps the main thread alive 119 | 120 | ret 121 | } 122 | })($rcx.tx.upgrade().unwrap()) // this passes a handle to keep the main thread alive 123 | ) 124 | } 125 | 126 | #[macro_export] 127 | macro_rules! js_unpack_args { 128 | ({$fn:expr, $rcx:expr, $remote:expr, $callargs:expr} (, $($args:tt)*)) => { 129 | js_unpack_args!({$fn, $rcx, $remote, $callargs} ($($args)*)); 130 | }; 131 | ({$fn:expr, $rcx:expr, $remote:expr, $callargs:expr} ($($args:tt)*)) => { 132 | #[allow(unused_imports)] 133 | use mozjs::conversions::FromJSValConvertible; 134 | 135 | if $callargs.argc_ != _js_unpack_args_count!($($args)*,) { 136 | return Err(Some(format!("{}() requires exactly {} argument", $fn, 137 | _js_unpack_args_count!($($args)*,)).into())); 138 | } 139 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, 0) $($args)*,); 140 | }; 141 | } 142 | 143 | #[macro_export] 144 | macro_rules! _js_unpack_args_count { 145 | () => { 146 | 0 147 | }; 148 | ($name:ident: @$special:ident, $($args:tt)*) => { 149 | _js_unpack_args_count!($($args)*) 150 | }; 151 | ($name:ident: @$special:ident $type:ty, $($args:tt)*) => { 152 | _js_unpack_args_count!($($args)*) 153 | }; 154 | ($name:ident: @$special:ident $type:ty {$opt:expr}, $($args:tt)*) => { 155 | _js_unpack_args_count!($($args)*) 156 | }; 157 | ($name:ident: &RJSContext, $($args:tt)*) => { 158 | _js_unpack_args_count!($($args)*) 159 | }; 160 | ($name:ident: &RJSHandle, $($args:tt)*) => { 161 | _js_unpack_args_count!($($args)*) 162 | }; 163 | ($name:ident: CallArgs, $($args:tt)*) => { 164 | _js_unpack_args_count!($($args)*) 165 | }; 166 | ($name:ident: $ty:ty, $($args:tt)*) => { 167 | 1 + _js_unpack_args_count!($($args)*) 168 | }; 169 | ($name:ident: $ty:ty {$opt:expr}, $($args:tt)*) => { 170 | 1 + _js_unpack_args_count!($($args)*) 171 | }; 172 | ($(,)+ $($rest:tt)*) => { 173 | _js_unpack_args_count!($($rest)*) 174 | }; 175 | } 176 | 177 | #[macro_export] 178 | macro_rules! _js_unpack_args_unwrap_args { 179 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 180 | $(,)*) => { 181 | }; 182 | // special: @this 183 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 184 | $name:ident : @this $type:ty, $($args:tt)*) => { 185 | let $name = unsafe { 186 | <$type as FromJSValConvertible>::from_jsval($rcx.cx, mozjs::rust::Handle::from_raw($callargs.thisv()), ()) 187 | .to_result()? }; 188 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n) $($args)*); 189 | }; 190 | // special: @this with options 191 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 192 | $name:ident : @this $type:ty {$opt:expr}, $($args:tt)*) => { 193 | let $name = unsafe { 194 | <$type as FromJSValConvertible>::from_jsval($rcx.cx, $callargs.thisv(), $opt) 195 | .to_result()? }; 196 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n) $($args)*); 197 | }; 198 | // RJSContext 199 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 200 | $name:ident : &RJSContext, $($args:tt)*) => { 201 | let $name: &RJSContext = $rcx; 202 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n) $($args)*); 203 | }; 204 | // RJSHandle 205 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 206 | $name:ident : &RJSHandle, $($args:tt)*) => { 207 | let $name: &RJSHandle = $remote; 208 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n) $($args)*); 209 | }; 210 | // CallArgs 211 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 212 | $name:ident : CallArgs, $($args:tt)*) => { 213 | let $name = $callargs; 214 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n) $($args)*); 215 | }; 216 | // options 217 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 218 | $name:ident : $type:ty {$opt:expr}, $($args:tt)*) => { 219 | let $name = unsafe { 220 | <$type as FromJSValConvertible>::from_jsval($rcx.cx, mozjs::rust::Handle::from_raw($callargs.get($n)), $opt) 221 | .to_result()? }; 222 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n+1) $($args)*); 223 | }; 224 | // no options 225 | (($rcx:expr, $remote:expr, $callargs:expr, $n:expr) 226 | $name:ident : $type:ty, $($args:tt)*) => { 227 | let $name = unsafe { 228 | <$type as FromJSValConvertible>::from_jsval($rcx.cx, mozjs::rust::Handle::from_raw($callargs.get($n)), ()) 229 | .to_result()? }; 230 | _js_unpack_args_unwrap_args!(($rcx, $remote, $callargs, $n+1) $($args)*); 231 | }; 232 | } 233 | -------------------------------------------------------------------------------- /src/jslib/mod.rs: -------------------------------------------------------------------------------- 1 | mod globals; 2 | #[macro_use] 3 | pub mod jsclass; 4 | #[macro_use] 5 | pub mod jsfn; 6 | pub mod context; 7 | pub mod eventloop; 8 | #[macro_use] 9 | pub mod upcast; 10 | #[cfg(test)] 11 | mod upcast_test; 12 | -------------------------------------------------------------------------------- /src/jslib/threadbound.rs: -------------------------------------------------------------------------------- 1 | // This is an attempt at making a value which wraps a non-Sendable value 2 | // and only allows access to the value on the same thread it came from. 3 | 4 | use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; 5 | 6 | static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT; 7 | thread_local!(static THREAD_ID: usize = NEXT_ID.fetch_add(1, Ordering::Relaxed)); 8 | 9 | pub struct ThreadBound { 10 | thread_id: usize, 11 | t: T, 12 | } 13 | 14 | impl Send for ThreadBound; 15 | 16 | impl ThreadBound { 17 | fn new(t: T) -> ThreadBound { 18 | ThreadBound { 19 | thread_id: THREAD_ID.with(|i| *i), 20 | t: t, 21 | } 22 | } 23 | } 24 | 25 | // This can't work because drop might be called from another thread 26 | -------------------------------------------------------------------------------- /src/jslib/upcast.rs: -------------------------------------------------------------------------------- 1 | // this is an attempt to make a way to allow an Any-style trait object be casted to a number of 2 | // trait types 3 | 4 | use std::any::TypeId; 5 | 6 | pub fn try_cast(c: &dyn Castable) -> Option<&T> { 7 | let mut v: Option<&T> = None; 8 | 9 | unsafe { c.unsafe_try_cast(TypeId::of::(), &mut v as *mut Option<&T> as *mut _) } 10 | 11 | v 12 | } 13 | 14 | #[allow(clippy::missing_safety_doc)] 15 | pub trait Castable { 16 | unsafe fn unsafe_try_cast(&self, to: TypeId, out: *mut Option<()>); 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! impl_castable { 21 | ($t:ty : $($tr:ty),*) => { 22 | impl Castable for $t { 23 | unsafe fn unsafe_try_cast(&self, to: ::std::any::TypeId, out: *mut Option<()>) { 24 | if to == ::std::any::TypeId::of::<$t>() { 25 | *(out as *mut Option<&$t>) = Some(self as &$t); 26 | return; 27 | } 28 | $( 29 | if to == ::std::any::TypeId::of::<$tr>() { 30 | *(out as *mut Option<&$tr>) = Some(self as &$tr); 31 | return; 32 | } 33 | )* 34 | } 35 | } 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /src/jslib/upcast_test.rs: -------------------------------------------------------------------------------- 1 | use super::upcast::*; 2 | 3 | struct S; 4 | 5 | impl S { 6 | fn s(&self) -> u32 { 7 | 3 8 | } 9 | } 10 | 11 | trait A { 12 | fn a(&self) -> u32; 13 | } 14 | impl A for S { 15 | fn a(&self) -> u32 { 16 | 1 17 | } 18 | } 19 | 20 | trait B { 21 | fn b(&self) -> u32; 22 | } 23 | impl B for S { 24 | fn b(&self) -> u32 { 25 | 2 26 | } 27 | } 28 | 29 | impl_castable!(S: dyn A, dyn B); 30 | 31 | #[test] 32 | fn test_castable() { 33 | let s = S {}; 34 | 35 | let c: &dyn Castable = &s; 36 | 37 | assert_eq!(Some(1), try_cast::(c).map(|a: &dyn A| a.a())); 38 | assert_eq!(Some(2), try_cast::(c).map(|b: &dyn B| b.b())); 39 | assert_eq!(Some(3), try_cast::(c).map(|s: &S| s.s())); 40 | assert_eq!(None, try_cast::>(c)); 41 | assert_eq!(None, try_cast::<()>(c)); 42 | } 43 | 44 | #[test] 45 | fn test_castable_box() { 46 | let c: Box = Box::new(S); 47 | 48 | assert_eq!(Some(1), try_cast::(&*c).map(|a: &dyn A| a.a())); 49 | assert_eq!(Some(2), try_cast::(&*c).map(|b: &dyn B| b.b())); 50 | assert_eq!(Some(3), try_cast::(&*c).map(|s: &S| s.s())); 51 | assert_eq!(None, try_cast::>(&*c)); 52 | assert_eq!(None, try_cast::<()>(&*c)); 53 | } 54 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate downcast; 3 | extern crate futures; 4 | extern crate lazy_static; 5 | extern crate libc; 6 | #[macro_use] 7 | extern crate mozjs; 8 | extern crate slab; 9 | extern crate tokio_core; 10 | 11 | #[cfg(test)] 12 | mod tests; 13 | 14 | #[macro_use] 15 | pub mod jslib; 16 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "100"] 2 | 3 | extern crate futures; 4 | extern crate gl; 5 | extern crate glutin; 6 | extern crate libc; 7 | #[macro_use] 8 | extern crate mozjs; 9 | extern crate tokio_core; 10 | 11 | use futures::future::{loop_fn, Future, Loop}; 12 | use futures::sync::mpsc; 13 | use futures::Stream; 14 | use glutin::GlContext; 15 | use mozjs::conversions::{ 16 | ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible, 17 | }; 18 | use mozjs::jsapi; 19 | use mozjs::jsapi::StackFormat; 20 | use mozjs::jsapi::{ 21 | CallArgs, HandleValue, HandleValueArray, JSAutoRealm, JSContext, JSObject, 22 | JS_CallFunctionValue, JS_NewArrayObject1, JS_NewGlobalObject, JS_NewObjectForConstructor, 23 | JS_ReportErrorUTF8, JS_SetElement, OnNewGlobalHookOption, Value, 24 | }; 25 | use mozjs::jsapi::{ 26 | Handle, HandleObject, JSClass, JSClassOps, JSFunctionSpec, JSNativeWrapper, JSPropertySpec, 27 | JS_EnumerateStandardClasses, JS_GetPrivate, JS_GetProperty, JS_NewPlainObject, JS_SetPrivate, 28 | JS_SetProperty, JSPROP_ENUMERATE, 29 | }; 30 | use mozjs::jsval; 31 | use mozjs::jsval::NullValue; 32 | use mozjs::jsval::{JSVal, ObjectValue, UndefinedValue}; 33 | use mozjs::rust::{JSEngine, RealmOptions, Runtime, SIMPLE_GLOBAL_CLASS}; 34 | #[macro_use] 35 | extern crate downcast; 36 | extern crate lazy_static; 37 | #[macro_use] 38 | extern crate slab; 39 | 40 | #[cfg(test)] 41 | mod tests; 42 | 43 | #[macro_use] 44 | pub mod jslib; 45 | use core::ptr; 46 | use gl::types::*; 47 | use jslib::context; 48 | use jslib::context::{RJSContext, RJSHandle}; 49 | use jslib::eventloop; 50 | use jslib::jsclass::{ 51 | null_function, null_property, null_wrapper, JSClassInitializer, JSCLASS_HAS_PRIVATE, 52 | }; 53 | use jslib::jsfn::{JSRet, RJSFn}; 54 | 55 | use std::env; 56 | use std::ffi::CString; 57 | use std::fmt::Debug; 58 | use std::fs; 59 | use std::fs::File; 60 | use std::io::Read; 61 | use std::marker::PhantomData; 62 | use std::path::Path; 63 | use std::sync::atomic::{AtomicUsize, Ordering}; 64 | use std::sync::Arc; 65 | use std::sync::Once; 66 | use std::thread; 67 | use std::time::{Duration, Instant}; 68 | use tokio_core::reactor::{Core, Timeout}; 69 | 70 | macro_rules! setprop { 71 | (in($cx:expr, $tval:expr) ($obj:expr) . $prop:ident = $val:expr) => { 72 | unsafe { 73 | $val.to_jsval($cx, $tval.handle_mut()); 74 | JS_SetProperty($cx, $obj, c_str!(stringify!($prop)), $tval.handle().into()); 75 | } 76 | }; 77 | } 78 | 79 | macro_rules! gl_set_props { 80 | ([$cx:expr, $obj:expr, $temp:expr] $($prop:ident)*) => { 81 | 82 | $( 83 | setprop!(in($cx, $temp) ($obj).$prop = gl::$prop); 84 | )* 85 | }; 86 | } 87 | 88 | fn main() { 89 | let filename = env::args() 90 | .nth(1) 91 | .expect("Expected a filename as the first argument"); 92 | 93 | let mut file = File::open(&filename).expect("File is missing"); 94 | let mut contents = String::new(); 95 | file.read_to_string(&mut contents) 96 | .expect("Cannot read file"); 97 | let engine = JSEngine::init().unwrap(); 98 | let rt = Runtime::new(engine.handle()); 99 | #[cfg(debugmozjs)] 100 | unsafe { 101 | jsapi::JS_SetGCZeal(rt.rt(), 2, 1) 102 | }; 103 | 104 | let cx = rt.cx(); 105 | 106 | rooted!(in(cx) let global_root = 107 | unsafe { JS_NewGlobalObject(cx, &SIMPLE_GLOBAL_CLASS, ptr::null_mut(), 108 | OnNewGlobalHookOption::FireOnNewGlobalHook, 109 | &*RealmOptions::default()) } 110 | ); 111 | let global = global_root.handle(); 112 | let rcx = RJSContext::new(cx, global.into()); 113 | 114 | eventloop::run(&rt, rcx, |handle| { 115 | let rcx = handle.get(); 116 | let _ac = JSAutoRealm::new(cx, global.get()); 117 | 118 | context::store_private(cx, &handle); 119 | 120 | let _ = unsafe { JS_EnumerateStandardClasses(cx, global.into()) }; 121 | 122 | let wininfo; 123 | 124 | unsafe { 125 | let _ = puts.define_on(cx, global.into(), 0); 126 | let _ = setTimeout.define_on(cx, global.into(), 0); 127 | let _ = getFileSync.define_on(cx, global.into(), 0); 128 | let _ = readDir.define_on(cx, global.into(), 0); 129 | 130 | Test::init_class(rcx, global.into()); 131 | wininfo = Window::init_class(rcx, global.into()); 132 | WebGLShader::init_class(rcx, global.into()); 133 | WebGLProgram::init_class(rcx, global.into()); 134 | WebGLBuffer::init_class(rcx, global.into()); 135 | WebGLUniformLocation::init_class(rcx, global.into()); 136 | } 137 | 138 | rooted!(in(rcx.cx) let winproto = wininfo.prototype); 139 | let winproto = winproto.handle(); 140 | rooted!(in(rcx.cx) let mut temp = NullValue()); 141 | // TODO: Add all constants, organize them in a nice way 142 | gl_set_props!([rcx.cx, winproto.into(), temp] 143 | FRAGMENT_SHADER VERTEX_SHADER 144 | COMPILE_STATUS LINK_STATUS 145 | ARRAY_BUFFER 146 | STATIC_DRAW 147 | COLOR_BUFFER_BIT DEPTH_BUFFER_BIT 148 | POINTS TRIANGLES TRIANGLE_STRIP 149 | FLOAT 150 | DEPTH_TEST 151 | ); 152 | 153 | rooted!(in(cx) let mut rval = UndefinedValue()); 154 | let res = rt.evaluate_script(global, &contents, &filename, 1, rval.handle_mut()); 155 | if res.is_err() { 156 | unsafe { 157 | report_pending_exception(cx); 158 | } 159 | } 160 | 161 | let str = unsafe { String::from_jsval(cx, rval.handle(), ()) } 162 | .to_result() 163 | .unwrap(); 164 | 165 | println!("script result: {}", str); 166 | }); 167 | 168 | let _ac = JSAutoRealm::new(cx, global.get()); 169 | context::clear_private(cx); 170 | } 171 | 172 | trait ToResult { 173 | fn to_result(self) -> Result>; 174 | } 175 | 176 | impl ToResult for Result, ()> { 177 | fn to_result(self) -> Result> { 178 | match self { 179 | Ok(ConversionResult::Success(v)) => Result::Ok(v), 180 | Ok(ConversionResult::Failure(reason)) => Result::Err(Some(reason.into_owned())), 181 | Err(()) => Result::Err(None), 182 | } 183 | } 184 | } 185 | 186 | js_fn! {fn puts(arg: String) -> JSRet<()> { 187 | println!("puts: {}", arg); 188 | Ok(()) 189 | }} 190 | 191 | js_fn! {fn setTimeout(rcx: &RJSContext, 192 | handle: &RJSHandle, 193 | callback: JSVal, 194 | timeout: u64 {ConversionBehavior::Default} 195 | ) -> JSRet<()> { 196 | rooted!(in(rcx.cx) let callback = callback); 197 | let handle2: RJSHandle = handle.clone(); 198 | //let remote = handle.remote().clone(); 199 | 200 | let timeout = Timeout::new(Duration::from_millis(timeout), handle.core_handle()).unwrap(); 201 | 202 | let callback_ref = handle.store_new(callback.get()); 203 | 204 | handle.core_handle().spawn( 205 | timeout.map_err(|_|()).and_then(move|_| { 206 | let rcx = handle2.get(); 207 | let _ac = JSAutoRealm::new(rcx.cx, rcx.global.get()); 208 | 209 | rooted!(in(rcx.cx) let this_val = rcx.global.get()); 210 | rooted!(in(rcx.cx) let mut rval = UndefinedValue()); 211 | 212 | rooted!(in(rcx.cx) let callback = handle2.retrieve(&callback_ref).unwrap()); 213 | 214 | unsafe { 215 | let ok = JS_CallFunctionValue( 216 | rcx.cx, 217 | this_val.handle().into(), 218 | callback.handle().into(), 219 | &jsapi::HandleValueArray { 220 | elements_: ptr::null_mut(), 221 | length_: 0, 222 | }, 223 | rval.handle_mut().into()); 224 | 225 | if !ok { 226 | println!("error!"); 227 | report_pending_exception(rcx.cx); 228 | } 229 | } 230 | 231 | Ok(()) 232 | }) 233 | ); 234 | 235 | Ok(()) 236 | }} 237 | 238 | js_fn! {fn getFileSync(path: String) -> JSRet> { 239 | if let Ok(mut file) = File::open(path) { 240 | let mut contents = String::new(); 241 | match file.read_to_string(&mut contents) { 242 | Ok(_) => Ok(Some(contents)), 243 | Err(e) => Err(Some(format!("Error reading contents: {}", e))), 244 | } 245 | } else { 246 | Ok(None) 247 | } 248 | }} 249 | 250 | js_fn! {fn readDir(rcx: &RJSContext, path: String) -> JSRet { 251 | unsafe { 252 | rooted!(in(rcx.cx) let arr = JS_NewArrayObject1(rcx.cx, 0)); 253 | rooted!(in(rcx.cx) let mut temp = UndefinedValue()); 254 | 255 | for (i, entry) in fs::read_dir(Path::new(&path)).unwrap().enumerate() { 256 | let entry = entry.unwrap(); 257 | let path = entry.path(); 258 | 259 | path.to_str().unwrap().to_jsval(rcx.cx, temp.handle_mut()); 260 | JS_SetElement(rcx.cx, arr.handle().into(), i as u32, temp.handle().into()); 261 | } 262 | 263 | Ok(ObjectValue(*arr)) 264 | } 265 | }} 266 | 267 | unsafe fn report_pending_exception(cx: *mut JSContext) { 268 | rooted!(in(cx) let mut ex = UndefinedValue()); 269 | if !jsapi::JS_GetPendingException(cx, ex.handle_mut().into()) { 270 | return; 271 | } 272 | jsapi::JS_ClearPendingException(cx); 273 | 274 | let exhandle = Handle::from_marked_location(&ex.get().to_object()); 275 | 276 | let report = jsapi::JS_ErrorFromException(cx, exhandle); 277 | if report.is_null() { 278 | return; 279 | } 280 | let report = &*report; 281 | 282 | let filename = { 283 | let filename = report._base.filename as *const u8; 284 | if filename.is_null() { 285 | "".to_string() 286 | } else { 287 | let length = (0..).find(|i| *filename.offset(*i) == 0).unwrap(); 288 | let filename = ::std::slice::from_raw_parts(filename, length as usize); 289 | String::from_utf8_lossy(filename).into_owned() 290 | } 291 | }; 292 | 293 | let message = { 294 | let message = report._base.message_.data_ as *const u8; 295 | let length = (0..).find(|i| *message.offset(*i) == 0).unwrap(); 296 | let message = ::std::slice::from_raw_parts(message, length as usize); 297 | String::from_utf8_lossy(message) 298 | }; 299 | 300 | /*let line = { 301 | let line = report.linebuf_; 302 | let length = report.linebufLength_; 303 | let line = ::std::slice::from_raw_parts(line, length as usize); 304 | String::from_utf16_lossy(line) 305 | };*/ 306 | 307 | //let ex = String::from_jsval(cx, ex.handle(), ()).to_result().unwrap(); 308 | println!( 309 | "Exception at {}:{}:{}: {}", 310 | filename, report._base.lineno, report._base.column, message 311 | ); 312 | //println!("{:?}", report); 313 | capture_stack!(in(cx) let stack); 314 | let str_stack = stack 315 | .unwrap() 316 | .as_string(None, StackFormat::SpiderMonkey) 317 | .unwrap(); 318 | println!("{}", str_stack); 319 | } 320 | 321 | struct Test {} 322 | 323 | js_class! { Test extends () 324 | [JSCLASS_HAS_PRIVATE] 325 | 326 | @constructor 327 | fn Test_constructor(rcx: &RJSContext, args: CallArgs) -> JSRet<*mut JSObject> { 328 | let obj = unsafe { JS_NewObjectForConstructor(rcx.cx, Test::class(), &args) }; 329 | 330 | Ok(obj) 331 | } 332 | 333 | fn test_puts(arg: String) -> JSRet<()> { 334 | println!("{}", arg); 335 | Ok(()) 336 | } 337 | 338 | @prop test_prop { 339 | get fn Test_get_test_prop() -> JSRet { 340 | Ok(String::from("Test prop")) 341 | } 342 | } 343 | 344 | } 345 | 346 | struct Window { 347 | thread: thread::JoinHandle<()>, 348 | send_msg: mpsc::UnboundedSender, 349 | } 350 | 351 | impl Window { 352 | fn do_on_thread(&self, f: F) 353 | where 354 | F: for<'r> FnOnce(&'r glutin::GlWindow) + Send + 'static, 355 | { 356 | let _ = self.send_msg.unbounded_send(WindowMsg::Do(Box::new(f))); 357 | } 358 | 359 | fn sync_do_on_thread(&self, f: F) -> R 360 | where 361 | F: for<'r> FnOnce(&'r glutin::GlWindow) -> R + Send, 362 | R: Debug + Send + 'static, 363 | { 364 | // using transmute to force adding 'static, since this function is 365 | // enforcing a shorter lifetime 366 | let fbox: Box FnOnce(&'r glutin::GlWindow) -> R + Send> = Box::new(f); 367 | let fbox: Box FnOnce(&'r glutin::GlWindow) -> R + Send + 'static> = 368 | unsafe { ::std::mem::transmute(fbox) }; 369 | 370 | let (send, recv) = futures::sync::oneshot::channel(); 371 | 372 | self.do_on_thread(move |win: &glutin::GlWindow| { 373 | let out = fbox(win); 374 | send.send(out).unwrap(); 375 | }); 376 | 377 | recv.wait().unwrap() 378 | } 379 | } 380 | 381 | fn glutin_event_to_js(cx: *mut JSContext, obj: HandleObject, event: glutin::Event) { 382 | use glutin::Event::*; 383 | rooted!(in(cx) let mut val = NullValue()); 384 | 385 | let set_mouse_scroll_delta = |delta: glutin::MouseScrollDelta| { 386 | use glutin::MouseScrollDelta::*; 387 | rooted!(in(cx) let mut val = NullValue()); 388 | match delta { 389 | LineDelta(x, y) => { 390 | setprop!(in(cx, val) (obj).deltakind = "line"); 391 | setprop!(in(cx, val) (obj).x = x); 392 | setprop!(in(cx, val) (obj).y = y); 393 | } 394 | PixelDelta(x, y) => { 395 | setprop!(in(cx, val) (obj).deltakind = "pixel"); 396 | setprop!(in(cx, val) (obj).x = x); 397 | setprop!(in(cx, val) (obj).y = y); 398 | } 399 | } 400 | }; 401 | 402 | match event { 403 | DeviceEvent { .. } => { 404 | setprop!(in(cx, val) (obj).kind = "device"); 405 | } 406 | WindowEvent { event, .. } => { 407 | use glutin::WindowEvent::*; 408 | setprop!(in(cx, val) (obj).kind = "window"); 409 | match event { 410 | Resized(w, h) => { 411 | setprop!(in(cx, val) (obj).type = "resized"); 412 | setprop!(in(cx, val) (obj).width = w); 413 | setprop!(in(cx, val) (obj).height = h); 414 | } 415 | Moved(x, y) => { 416 | setprop!(in(cx, val) (obj).type = "moved"); 417 | setprop!(in(cx, val) (obj).x = x); 418 | setprop!(in(cx, val) (obj).y = y); 419 | } 420 | CloseRequested => { 421 | setprop!(in(cx, val) (obj).type = "closed"); 422 | } 423 | ReceivedCharacter(c) => { 424 | setprop!(in(cx, val) (obj).type = "char"); 425 | setprop!(in(cx, val) (obj).char = c.encode_utf8(&mut [0; 4])); 426 | } 427 | Focused(focused) => { 428 | setprop!(in(cx, val) (obj).type = "focused"); 429 | setprop!(in(cx, val) (obj).focused = focused); 430 | } 431 | KeyboardInput { input, .. } => { 432 | setprop!(in(cx, val) (obj).type = "key"); 433 | setprop!(in(cx, val) (obj).scancode = input.scancode); 434 | } 435 | CursorMoved { position, .. } => { 436 | setprop!(in(cx, val) (obj).type = "cursormoved"); 437 | setprop!(in(cx, val) (obj).x = position.0); 438 | setprop!(in(cx, val) (obj).y = position.1); 439 | } 440 | CursorEntered { .. } => { 441 | setprop!(in(cx, val) (obj).type = "cursorentered"); 442 | } 443 | CursorLeft { .. } => { 444 | setprop!(in(cx, val) (obj).type = "cursorleft"); 445 | } 446 | MouseWheel { delta, .. } => { 447 | setprop!(in(cx, val) (obj).type = "wheel"); 448 | set_mouse_scroll_delta(delta); 449 | } 450 | MouseInput { state, button, .. } => { 451 | use glutin::ElementState::*; 452 | use glutin::MouseButton::*; 453 | setprop!(in(cx, val) (obj).type = "mouse"); 454 | setprop!(in(cx, val) (obj).pressed = state == Pressed); 455 | setprop!(in(cx, val) (obj).button = match button { 456 | Left => 0, 457 | Right => 1, 458 | Middle => 2, 459 | Other(n) => n, 460 | }); 461 | } 462 | Refresh => { 463 | setprop!(in(cx, val) (obj).type = "refresh"); 464 | } 465 | Touch(touch) => { 466 | use glutin::TouchPhase::*; 467 | setprop!(in(cx, val) (obj).type = "touch"); 468 | setprop!(in(cx, val) (obj).phase = match touch.phase { 469 | Started => "started", 470 | Moved => "moved", 471 | Ended => "ended", 472 | Cancelled => "cancelled", 473 | }); 474 | setprop!(in(cx, val) (obj).pressed = touch.phase == Started || 475 | touch.phase == Moved); 476 | setprop!(in(cx, val) (obj).x = touch.location.0); 477 | setprop!(in(cx, val) (obj).y = touch.location.1); 478 | setprop!(in(cx, val) (obj).id = touch.id); 479 | } 480 | _ => (), 481 | } 482 | } 483 | Suspended { .. } => { 484 | setprop!(in(cx, val) (obj).kind = "suspended"); 485 | } 486 | Awakened => { 487 | setprop!(in(cx, val) (obj).kind = "awakened"); 488 | } 489 | } 490 | } 491 | 492 | pub struct Object { 493 | obj: *mut JSObject, 494 | marker: PhantomData, 495 | } 496 | 497 | impl Object { 498 | fn jsobj(&self) -> *mut JSObject { 499 | self.obj 500 | } 501 | 502 | fn private(&self) -> &T::Private { 503 | // private has already been verified by from_jsval 504 | unsafe { &*(JS_GetPrivate(self.obj) as *const _) } 505 | } 506 | } 507 | 508 | impl FromJSValConvertible for Object { 509 | type Config = (); 510 | 511 | unsafe fn from_jsval( 512 | cx: *mut JSContext, 513 | value: mozjs::rust::HandleValue, 514 | _: (), 515 | ) -> Result>, ()> { 516 | use std::borrow::Cow; 517 | 518 | if !value.is_object() { 519 | return Ok(ConversionResult::Failure(Cow::Borrowed( 520 | "value is not an object", 521 | ))); 522 | } 523 | 524 | let obj = value.to_object(); 525 | 526 | if !jsapi::JS_InstanceOf( 527 | cx, 528 | Handle::from_marked_location(&obj), 529 | T::class(), 530 | ptr::null_mut(), 531 | ) { 532 | return Ok(ConversionResult::Failure(Cow::Borrowed( 533 | "value is not instanceof the class", 534 | ))); 535 | } 536 | 537 | let private = JS_GetPrivate(obj); 538 | if private.is_null() { 539 | return Ok(ConversionResult::Failure(Cow::Borrowed( 540 | "value has no private", 541 | ))); 542 | } 543 | 544 | Ok(ConversionResult::Success(Object { 545 | obj, 546 | marker: PhantomData, 547 | })) 548 | } 549 | } 550 | 551 | pub struct WebGLShader { 552 | id: Arc, 553 | } 554 | 555 | js_class! { WebGLShader extends () 556 | [JSCLASS_HAS_PRIVATE] 557 | private: WebGLShader, 558 | 559 | @constructor 560 | fn WebGLShader_constr() -> JSRet<*mut JSObject> { 561 | Ok(ptr::null_mut()) 562 | } 563 | 564 | fn toString(this: @this Object) -> JSRet { 565 | Ok(format!("{{WebGLShader {}}}", this.private().id.load(Ordering::Relaxed))) 566 | } 567 | 568 | @op(finalize) 569 | fn WebGLShader_finalize(_free: *mut jsapi::JSFreeOp, this: *mut JSObject) -> () { 570 | let private = JS_GetPrivate(this); 571 | if private.is_null() { 572 | return 573 | } 574 | 575 | JS_SetPrivate(this, ptr::null_mut() as *mut _); 576 | let _ = Box::from_raw(private as *mut WebGLShader); 577 | } 578 | } 579 | 580 | pub struct WebGLProgram { 581 | id: Arc, 582 | } 583 | 584 | js_class! { WebGLProgram extends () 585 | [JSCLASS_HAS_PRIVATE] 586 | private: WebGLProgram, 587 | 588 | @constructor 589 | fn WebGLProgram_constr() -> JSRet<*mut JSObject> { 590 | Ok(ptr::null_mut()) 591 | } 592 | 593 | fn toString(this: @this Object) -> JSRet { 594 | Ok(format!("{{WebGLProgram {}}}", this.private().id.load(Ordering::Relaxed))) 595 | } 596 | 597 | @op(finalize) 598 | fn WebGLProgram_finalize(_free: *mut jsapi::JSFreeOp, this: *mut JSObject) -> () { 599 | let private = JS_GetPrivate(this); 600 | if private.is_null() { 601 | return 602 | } 603 | 604 | JS_SetPrivate(this, ptr::null_mut() as *mut _); 605 | let _ = Box::from_raw(private as *mut WebGLProgram); 606 | } 607 | } 608 | 609 | pub struct WebGLBuffer { 610 | id: Arc, 611 | } 612 | 613 | js_class! { WebGLBuffer extends () 614 | [JSCLASS_HAS_PRIVATE] 615 | private: WebGLBuffer, 616 | 617 | @constructor 618 | fn WebGLBuffer_constr() -> JSRet<*mut JSObject> { 619 | Ok(ptr::null_mut()) 620 | } 621 | 622 | fn toString(this: @this Object) -> JSRet { 623 | Ok(format!("{{WebGLBuffer {}}}", this.private().id.load(Ordering::Relaxed))) 624 | } 625 | 626 | @op(finalize) 627 | fn WebGLBuffer_finalize(_free: *mut jsapi::JSFreeOp, this: *mut JSObject) -> () { 628 | let private = JS_GetPrivate(this); 629 | if private.is_null() { 630 | return 631 | } 632 | 633 | JS_SetPrivate(this, ptr::null_mut() as *mut _); 634 | let _ = Box::from_raw(private as *mut WebGLBuffer); 635 | } 636 | } 637 | 638 | pub struct WebGLUniformLocation { 639 | loc: Arc, 640 | } 641 | 642 | js_class! { WebGLUniformLocation extends () 643 | [JSCLASS_HAS_PRIVATE] 644 | private: WebGLUniformLocation, 645 | 646 | @constructor 647 | fn WebGLUniformLocation_constr() -> JSRet<*mut JSObject> { 648 | Ok(ptr::null_mut()) 649 | } 650 | 651 | fn toString(this: @this Object) -> JSRet { 652 | Ok(format!("{{WebGLUniformLocation {}}}", this.private().loc.load(Ordering::Relaxed))) 653 | } 654 | 655 | @op(finalize) 656 | fn WebGLUniformLocation_finalize(_free: *mut jsapi::JSFreeOp, this: *mut JSObject) -> () { 657 | let private = JS_GetPrivate(this); 658 | if private.is_null() { 659 | return 660 | } 661 | 662 | JS_SetPrivate(this, ptr::null_mut() as *mut _); 663 | let _ = Box::from_raw(private as *mut WebGLUniformLocation); 664 | } 665 | } 666 | 667 | js_class! { Window extends () 668 | [JSCLASS_HAS_PRIVATE] 669 | private: Window, 670 | 671 | @constructor 672 | fn Window_constr(handle: &RJSHandle, args: CallArgs) -> JSRet<*mut JSObject> { 673 | let rcx = handle.get(); 674 | let jswin = unsafe { JS_NewObjectForConstructor(rcx.cx, Window::class(), &args) }; 675 | 676 | let handle: RJSHandle = handle.clone(); 677 | 678 | let (send_events, recv_events) = mpsc::unbounded(); 679 | let (send_msg, recv_msg) = mpsc::unbounded(); 680 | 681 | let thread = thread::spawn(move || { 682 | window_thread(recv_msg, send_events); 683 | }); 684 | 685 | { 686 | let handle2 = handle.clone(); 687 | let jswin = handle2.store_new(jswin); 688 | handle.core_handle().spawn( 689 | recv_events.for_each(move |event| -> Result<(), ()> { 690 | let rcx = handle2.get(); 691 | rooted!(in(rcx.cx) let jswin = handle2.retrieve_copy(&jswin).unwrap()); 692 | let _ac = JSAutoRealm::new(rcx.cx, jswin.get()); 693 | rooted!(in(rcx.cx) let obj = unsafe { JS_NewPlainObject(rcx.cx) }); 694 | 695 | //println!("event received: {:?}", &event); 696 | match event { 697 | WindowEvent::Glutin(ge) => glutin_event_to_js(rcx.cx, obj.handle().into(), ge), 698 | WindowEvent::Closed => { println!("WindowEvent closed"); }, 699 | } 700 | 701 | rooted!(in(rcx.cx) let mut onevent = NullValue()); 702 | let success = unsafe { 703 | JS_GetProperty(rcx.cx, 704 | jswin.handle().into(), 705 | c_str!("onevent"), 706 | onevent.handle_mut().into()) 707 | }; 708 | if !success || onevent.is_null_or_undefined() { 709 | println!("success: {:?} onevent: {:?}", success, onevent.is_null()); 710 | return Ok(()); 711 | } 712 | 713 | rooted!(in(rcx.cx) let mut ret = NullValue()); 714 | let args = &[ObjectValue(obj.get())]; 715 | let args = unsafe { HandleValueArray::from_rooted_slice(args) }; 716 | if ! unsafe { 717 | jsapi::Call(rcx.cx, 718 | HandleValue::null(), 719 | onevent.handle().into(), 720 | &args, 721 | ret.handle_mut().into()) 722 | } { 723 | // ignore? 724 | unsafe { report_pending_exception(rcx.cx); } 725 | } 726 | 727 | Ok(()) 728 | }) 729 | ); 730 | } 731 | 732 | 733 | let window = Box::new(Window { 734 | thread: thread, 735 | send_msg: send_msg, 736 | }); 737 | unsafe { 738 | JS_SetPrivate(jswin, Box::into_raw(window) as *mut _); 739 | } 740 | println!("window constructed"); 741 | 742 | Ok(jswin) 743 | } 744 | 745 | fn getContext(this: @this Object, str: String) -> JSRet<*mut JSObject> { 746 | println!("getContext: {}", str); 747 | Ok(this.jsobj()) 748 | } 749 | 750 | fn ping(this: @this Object) -> JSRet<()> { 751 | println!("ping"); 752 | 753 | if let Err(e) = this.private().send_msg.unbounded_send(WindowMsg::Ping) { 754 | println!("ping failed: {}", e); 755 | } 756 | 757 | Ok(()) 758 | } 759 | 760 | fn close(this: @this Object) -> JSRet<()> { 761 | if let Err(e) = this.private().send_msg.unbounded_send(WindowMsg::Close) { 762 | println!("close failed: {}", e); 763 | } 764 | 765 | Ok(()) 766 | } 767 | 768 | fn getError(this: @this Object) 769 | -> JSRet { 770 | Ok(this.private().sync_do_on_thread( 771 | move |_: &glutin::GlWindow| { 772 | unsafe { gl::GetError() } 773 | })) 774 | } 775 | 776 | fn clearColor(this: @this Object, r: f32, g: f32, b: f32, a: f32) 777 | -> JSRet<()> { 778 | this.private().do_on_thread( 779 | move |_: &glutin::GlWindow| { 780 | unsafe { gl::ClearColor(r, g, b, a) }; 781 | }); 782 | 783 | Ok(()) 784 | } 785 | 786 | fn clear(this: @this Object, mask: u32 {ConversionBehavior::Default}) 787 | -> JSRet<()> { 788 | this.private().do_on_thread( 789 | move |_: &glutin::GlWindow| { 790 | unsafe { gl::Clear(mask) }; 791 | }); 792 | 793 | Ok(()) 794 | } 795 | 796 | fn createShader(this: @this Object, rcx: &RJSContext, 797 | shadertype: GLenum {ConversionBehavior::Default}) 798 | -> JSRet<*mut JSObject> { 799 | let shader_priv = Box::new(WebGLShader { 800 | id: Arc::new(AtomicUsize::new(0)), 801 | }); 802 | 803 | let idref = Arc::clone(&shader_priv.id); 804 | 805 | let shader = WebGLShader::jsnew_with_private( 806 | rcx, Box::into_raw(shader_priv) as *mut WebGLShader); 807 | rooted!(in(rcx.cx) let shader = shader); 808 | 809 | this.private().do_on_thread( 810 | move |_: &glutin::GlWindow| { 811 | let id = unsafe { gl::CreateShader(shadertype) }; 812 | idref.store(id as usize, Ordering::Relaxed); 813 | }); 814 | 815 | Ok(shader.get()) 816 | } 817 | 818 | fn shaderSource(this: @this Object, shader: Object, source: String) 819 | -> JSRet<()> { 820 | let idref = Arc::clone(&shader.private().id); 821 | 822 | this.private().do_on_thread( 823 | move |_: &glutin::GlWindow| { 824 | let id = idref.load(Ordering::Relaxed); 825 | unsafe { gl::ShaderSource(id as u32, 826 | 1, 827 | &(source.as_ptr() as *const _), 828 | &(source.len() as _)) }; 829 | }); 830 | 831 | Ok(()) 832 | } 833 | 834 | fn compileShader(this: @this Object, shader: Object) 835 | -> JSRet<()> { 836 | let idref = Arc::clone(&shader.private().id); 837 | 838 | this.private().do_on_thread( 839 | move |_: &glutin::GlWindow| { 840 | let id = idref.load(Ordering::Relaxed); 841 | unsafe { gl::CompileShader(id as u32) }; 842 | }); 843 | 844 | Ok(()) 845 | } 846 | 847 | fn getShaderInfoLog(this: @this Object, shader: Object) 848 | -> JSRet { 849 | let idref = Arc::clone(&shader.private().id); 850 | 851 | Ok(this.private().sync_do_on_thread( 852 | move |_: &glutin::GlWindow| { 853 | let id = idref.load(Ordering::Relaxed); 854 | let mut len = 0; 855 | unsafe { gl::GetShaderiv(id as u32, gl::INFO_LOG_LENGTH, &mut len) }; 856 | 857 | let mut v = vec![0; len as usize]; 858 | let mut outlen = len; 859 | 860 | unsafe { gl::GetShaderInfoLog(id as u32, 861 | len as i32, 862 | &mut outlen, 863 | v.as_mut_ptr() as *mut _) }; 864 | 865 | String::from_utf8(v) 866 | }).unwrap()) 867 | } 868 | 869 | fn getShaderParameter(this: @this Object, shader: Object, 870 | param: GLenum {ConversionBehavior::Default}) 871 | -> JSRet { 872 | let idref = Arc::clone(&shader.private().id); 873 | 874 | let out = this.private().sync_do_on_thread( 875 | move |_: &glutin::GlWindow| { 876 | let id = idref.load(Ordering::Relaxed); 877 | let mut out = -1; 878 | unsafe { gl::GetShaderiv(id as u32, param, &mut out) }; 879 | out 880 | }); 881 | 882 | match param { 883 | gl::COMPILE_STATUS => Ok(jsval::BooleanValue(out > 0)), 884 | gl::DELETE_STATUS => Ok(jsval::BooleanValue(out > 0)), 885 | _ => Ok(jsval::Int32Value(out)), 886 | } 887 | } 888 | 889 | fn createProgram(this: @this Object, rcx: &RJSContext) 890 | -> JSRet<*mut JSObject> { 891 | let program_priv = Box::new(WebGLProgram { 892 | id: Arc::new(AtomicUsize::new(0)), 893 | }); 894 | 895 | let idref = Arc::clone(&program_priv.id); 896 | 897 | let program = WebGLProgram::jsnew_with_private( 898 | rcx, Box::into_raw(program_priv) as *mut WebGLProgram); 899 | rooted!(in(rcx.cx) let program = program); 900 | 901 | this.private().do_on_thread( 902 | move |_: &glutin::GlWindow| { 903 | let id = unsafe { gl::CreateProgram() }; 904 | idref.store(id as usize, Ordering::Relaxed); 905 | }); 906 | 907 | Ok(program.get()) 908 | } 909 | 910 | fn attachShader(this: @this Object, program: Object, 911 | shader: Object) 912 | -> JSRet<()> { 913 | let progidref = Arc::clone(&program.private().id); 914 | let shaderidref = Arc::clone(&shader.private().id); 915 | 916 | this.private().do_on_thread( 917 | move |_: &glutin::GlWindow| { 918 | let prog = progidref.load(Ordering::Relaxed); 919 | let shader = shaderidref.load(Ordering::Relaxed); 920 | unsafe { gl::AttachShader(prog as u32, shader as u32) }; 921 | }); 922 | 923 | Ok(()) 924 | } 925 | 926 | fn linkProgram(this: @this Object, program: Object) 927 | -> JSRet<()> { 928 | let progidref = Arc::clone(&program.private().id); 929 | 930 | this.private().do_on_thread( 931 | move |_: &glutin::GlWindow| { 932 | let prog = progidref.load(Ordering::Relaxed); 933 | unsafe { gl::LinkProgram(prog as u32) }; 934 | }); 935 | 936 | Ok(()) 937 | } 938 | 939 | fn getProgramParameter(this: @this Object, program: Object, 940 | param: GLenum {ConversionBehavior::Default}) 941 | -> JSRet { 942 | let idref = Arc::clone(&program.private().id); 943 | 944 | let out = this.private().sync_do_on_thread( 945 | move |_: &glutin::GlWindow| { 946 | let id = idref.load(Ordering::Relaxed); 947 | let mut out = -1; 948 | unsafe { gl::GetProgramiv(id as u32, param, &mut out) }; 949 | out 950 | }); 951 | 952 | match param { 953 | gl::LINK_STATUS => Ok(jsval::BooleanValue(out > 0)), 954 | gl::DELETE_STATUS => Ok(jsval::BooleanValue(out > 0)), 955 | gl::VALIDATE_STATUS => Ok(jsval::BooleanValue(out > 0)), 956 | _ => Ok(jsval::Int32Value(out)), 957 | } 958 | } 959 | 960 | fn useProgram(this: @this Object, program: Object) 961 | -> JSRet<()> { 962 | let progidref = Arc::clone(&program.private().id); 963 | 964 | this.private().do_on_thread( 965 | move |_: &glutin::GlWindow| { 966 | let prog = progidref.load(Ordering::Relaxed); 967 | unsafe { gl::UseProgram(prog as u32) }; 968 | }); 969 | 970 | Ok(()) 971 | } 972 | 973 | fn getAttribLocation(this: @this Object, program: Object, name: String) 974 | -> JSRet { 975 | let idref = Arc::clone(&program.private().id); 976 | 977 | let name = CString::new(name).unwrap(); 978 | 979 | Ok(this.private().sync_do_on_thread( 980 | move |_: &glutin::GlWindow| { 981 | let id = idref.load(Ordering::Relaxed); 982 | unsafe { gl::GetAttribLocation(id as u32, name.as_ptr()) } 983 | })) 984 | } 985 | 986 | fn enableVertexAttribArray(this: @this Object, index: u32 {ConversionBehavior::Default}) 987 | -> JSRet<()> { 988 | this.private().do_on_thread( 989 | move |_: &glutin::GlWindow| { 990 | unsafe { gl::EnableVertexAttribArray(index) }; 991 | }); 992 | 993 | Ok(()) 994 | } 995 | 996 | fn disableVertexAttribArray(this: @this Object, 997 | index: u32 {ConversionBehavior::Default}) 998 | -> JSRet<()> { 999 | this.private().do_on_thread( 1000 | move |_: &glutin::GlWindow| { 1001 | unsafe { gl::DisableVertexAttribArray(index) }; 1002 | }); 1003 | 1004 | Ok(()) 1005 | } 1006 | 1007 | fn vertexAttribPointer(this: @this Object, 1008 | index: GLuint {ConversionBehavior::Default}, 1009 | size: GLint {ConversionBehavior::Default}, 1010 | type_: GLenum {ConversionBehavior::Default}, 1011 | normalized: bool, 1012 | stride: GLsizei {ConversionBehavior::Default}, 1013 | offset: u32 {ConversionBehavior::Default}) 1014 | -> JSRet<()>{ 1015 | // TODO: Validate these values 1016 | this.private().do_on_thread( 1017 | move |_: &glutin::GlWindow| { 1018 | unsafe { gl::VertexAttribPointer( 1019 | index, 1020 | size, 1021 | type_, 1022 | normalized as u8, 1023 | stride, 1024 | offset as *const _) }; 1025 | }); 1026 | 1027 | Ok(()) 1028 | } 1029 | 1030 | fn getUniformLocation(this: @this Object, rcx: &RJSContext, 1031 | program: Object, name: String) 1032 | -> JSRet<*mut JSObject> { 1033 | let ul_priv = Box::new(WebGLUniformLocation { 1034 | loc: Arc::new(AtomicUsize::new(0)), 1035 | }); 1036 | 1037 | let idref = Arc::clone(&program.private().id); 1038 | let locref = Arc::clone(&ul_priv.loc); 1039 | 1040 | let name = CString::new(name).unwrap(); 1041 | 1042 | let ul = WebGLUniformLocation::jsnew_with_private( 1043 | rcx, Box::into_raw(ul_priv) as *mut WebGLUniformLocation); 1044 | rooted!(in(rcx.cx) let ul = ul); 1045 | 1046 | this.private().do_on_thread( 1047 | move |_: &glutin::GlWindow| { 1048 | let id = idref.load(Ordering::Relaxed); 1049 | 1050 | let loc = unsafe { gl::GetUniformLocation(id as u32, name.as_ptr()) }; 1051 | locref.store(loc as usize, Ordering::Relaxed); 1052 | }); 1053 | 1054 | Ok(ul.get()) 1055 | } 1056 | 1057 | fn createBuffer(this: @this Object, rcx: &RJSContext) 1058 | -> JSRet<*mut JSObject> { 1059 | let buffer_priv = Box::new(WebGLBuffer { 1060 | id: Arc::new(AtomicUsize::new(0)), 1061 | }); 1062 | 1063 | let idref = Arc::clone(&buffer_priv.id); 1064 | 1065 | let buffer = WebGLBuffer::jsnew_with_private( 1066 | rcx, Box::into_raw(buffer_priv) as *mut WebGLBuffer); 1067 | rooted!(in(rcx.cx) let buffer = buffer); 1068 | 1069 | this.private().do_on_thread( 1070 | move |_: &glutin::GlWindow| { 1071 | let mut buffer = 0; 1072 | unsafe { gl::CreateBuffers(1, &mut buffer) }; 1073 | idref.store(buffer as usize, Ordering::Relaxed); 1074 | }); 1075 | 1076 | Ok(buffer.get()) 1077 | } 1078 | 1079 | fn bindBuffer(this: @this Object, target: GLenum {ConversionBehavior::Default}, 1080 | buffer: Object) 1081 | -> JSRet<()> { 1082 | let idref = Arc::clone(&buffer.private().id); 1083 | 1084 | this.private().do_on_thread( 1085 | move |_: &glutin::GlWindow| { 1086 | let id = idref.load(Ordering::Relaxed); 1087 | unsafe { gl::BindBuffer(target, id as u32) } 1088 | }); 1089 | 1090 | Ok(()) 1091 | } 1092 | 1093 | fn bufferData(rcx: &RJSContext, 1094 | this: @this Object, 1095 | target: GLenum {ConversionBehavior::Default}, 1096 | data: JSVal, 1097 | usage: GLenum {ConversionBehavior::Default}) 1098 | -> JSRet<()> { 1099 | if !data.is_object() { 1100 | return Err(Some("data is not an object. size?".to_owned())); 1101 | } 1102 | 1103 | rooted!(in(rcx.cx) let obj = data.to_object()); 1104 | 1105 | typedarray!(in(rcx.cx) let buf: ArrayBuffer = obj.get()); 1106 | typedarray!(in(rcx.cx) let view: ArrayBufferView = obj.get()); 1107 | 1108 | // This construct looks ugly, but it should avoid having to copy the buffer data 1109 | // TODO: Should we always do this? For small amounts of data it might be faster 1110 | // to copy and let the thread continue. 1111 | 1112 | let do_it = |slice: &[u8]| { 1113 | this.private().sync_do_on_thread( 1114 | move |_: &glutin::GlWindow| { 1115 | unsafe { gl::BufferData(target, 1116 | slice.len() as isize, 1117 | slice.as_ptr() as *const _, 1118 | usage) } 1119 | }); 1120 | }; 1121 | 1122 | if let Ok(buf) = buf { 1123 | do_it(unsafe { buf.as_slice() }) 1124 | } else if let Ok(view) = view { 1125 | do_it(unsafe { view.as_slice() }) 1126 | } else { 1127 | panic!("Not ArrayBuffer or ArrayBufferView"); 1128 | }; 1129 | 1130 | 1131 | Ok(()) 1132 | } 1133 | 1134 | fn uniformMatrix4fv(rcx: &RJSContext, 1135 | this: @this Object, 1136 | location: Object, 1137 | transpose: bool, 1138 | value: *mut JSObject) 1139 | -> JSRet<()> { 1140 | typedarray!(in(rcx.cx) let view: Float32Array = value); 1141 | 1142 | // Since this should always be given 16 element arrays, just always copy them 1143 | 1144 | let data = if let Ok(view) = view { 1145 | unsafe { view.as_slice().to_vec() } 1146 | } else { 1147 | rooted!(in(rcx.cx) let value = ObjectValue(value)); 1148 | unsafe { Vec::::from_jsval(rcx.cx, value.handle(), ()).to_result()? } 1149 | }; 1150 | 1151 | let locidref = Arc::clone(&location.private().loc); 1152 | 1153 | this.private().do_on_thread( 1154 | move |_: &glutin::GlWindow| { 1155 | let loc = locidref.load(Ordering::Relaxed); 1156 | unsafe { gl::UniformMatrix4fv(loc as i32, 1157 | 1, 1158 | transpose as GLboolean, 1159 | data.as_ptr()) } 1160 | }); 1161 | 1162 | Ok(()) 1163 | } 1164 | 1165 | fn enable(this: @this Object, cap: GLenum {ConversionBehavior::Default}) 1166 | -> JSRet<()> { 1167 | this.private().do_on_thread( 1168 | move |_: &glutin::GlWindow| { 1169 | unsafe { gl::Enable(cap) }; 1170 | }); 1171 | 1172 | Ok(()) 1173 | } 1174 | 1175 | fn disable(this: @this Object, cap: GLenum {ConversionBehavior::Default}) 1176 | -> JSRet<()> { 1177 | this.private().do_on_thread( 1178 | move |_: &glutin::GlWindow| { 1179 | unsafe { gl::Disable(cap) }; 1180 | }); 1181 | 1182 | Ok(()) 1183 | } 1184 | 1185 | fn viewport(this: @this Object, 1186 | x: GLint {ConversionBehavior::Default}, 1187 | y: GLint {ConversionBehavior::Default}, 1188 | width: GLsizei {ConversionBehavior::Default}, 1189 | height: GLsizei {ConversionBehavior::Default}) 1190 | -> JSRet<()> { 1191 | this.private().do_on_thread( 1192 | move |_: &glutin::GlWindow| { 1193 | unsafe { gl::Viewport(x, y, width, height) }; 1194 | }); 1195 | 1196 | Ok(()) 1197 | } 1198 | 1199 | fn drawArrays(this: @this Object, 1200 | mode: GLenum {ConversionBehavior::Default}, 1201 | first: GLint {ConversionBehavior::Default}, 1202 | count: GLsizei {ConversionBehavior::Default}) 1203 | -> JSRet<()> { 1204 | this.private().do_on_thread( 1205 | move |_: &glutin::GlWindow| { 1206 | unsafe { gl::DrawArrays(mode, first, count) }; 1207 | }); 1208 | 1209 | Ok(()) 1210 | } 1211 | 1212 | @op(finalize) 1213 | fn Window_finalize(_free: *mut jsapi::JSFreeOp, this: *mut JSObject) -> () { 1214 | let private = JS_GetPrivate(this); 1215 | if private.is_null() { 1216 | return 1217 | } 1218 | 1219 | JS_SetPrivate(this, ptr::null_mut() as *mut _); 1220 | let win = Box::from_raw(private as *mut Window); 1221 | let _ = win.send_msg.unbounded_send(WindowMsg::Close); 1222 | win.thread.join().unwrap(); 1223 | println!("window dropped"); 1224 | } 1225 | } 1226 | 1227 | enum WindowMsg { 1228 | Do(Box), 1229 | Ping, 1230 | Close, 1231 | } 1232 | 1233 | #[derive(Debug)] 1234 | enum WindowEvent { 1235 | Glutin(glutin::Event), 1236 | Closed, 1237 | } 1238 | 1239 | use std::cell::RefCell; 1240 | use std::rc::Rc; 1241 | 1242 | fn window_thread( 1243 | recv_msg: mpsc::UnboundedReceiver, 1244 | send_events: mpsc::UnboundedSender, 1245 | ) { 1246 | let mut core = Core::new().unwrap(); 1247 | 1248 | let mut events_loop = glutin::EventsLoop::new(); 1249 | let window = glutin::WindowBuilder::new() 1250 | .with_title("RJS Window") 1251 | .with_dimensions(1024, 768); 1252 | let context = glutin::ContextBuilder::new() 1253 | .with_gl_profile(glutin::GlProfile::Core) 1254 | .with_vsync(true); 1255 | let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); 1256 | 1257 | unsafe { 1258 | gl_window.make_current().unwrap(); 1259 | gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); 1260 | gl::ClearColor(0.0, 0.0, 0.0, 1.0); 1261 | 1262 | // OpenGL and GLES 2.0 treat VAO 0 specially 1263 | let mut vao = 0; 1264 | gl::CreateVertexArrays(1, &mut vao); 1265 | gl::BindVertexArray(vao); 1266 | } 1267 | 1268 | let (stop_send, stop_recv) = futures::sync::oneshot::channel(); 1269 | let stop_recv = stop_recv.map_err(|_| ()); 1270 | 1271 | struct WindowStuff { 1272 | gl_window: glutin::GlWindow, 1273 | stop: Option>, 1274 | } 1275 | 1276 | impl WindowStuff { 1277 | fn stop(&mut self) { 1278 | let stop = self.stop.take(); 1279 | stop.map(|stop| stop.send(())); 1280 | } 1281 | } 1282 | 1283 | let stuff = Rc::new(RefCell::new(WindowStuff { 1284 | gl_window, 1285 | stop: Some(stop_send), 1286 | })); 1287 | 1288 | let recv_msgs = { 1289 | let stuff = Rc::clone(&stuff); 1290 | recv_msg 1291 | .for_each(move |msg| -> Result<(), ()> { 1292 | let mut stuff = stuff.borrow_mut(); 1293 | match msg { 1294 | WindowMsg::Do(func) => { 1295 | //println!("message Do"); 1296 | func(&stuff.gl_window); 1297 | } 1298 | WindowMsg::Ping => { 1299 | println!("pong"); 1300 | } 1301 | WindowMsg::Close => { 1302 | println!("close"); 1303 | stuff.stop(); 1304 | stuff.gl_window.hide(); 1305 | } 1306 | } 1307 | 1308 | Ok(()) 1309 | }) 1310 | .then(|_| -> Result<(), ()> { Ok(()) }) 1311 | }; 1312 | 1313 | // Interval doesn't work great here because it doesn't know how long things will take 1314 | // and can get out of hand. 1315 | 1316 | let handle = &core.handle(); 1317 | 1318 | let handle_window_events = loop_fn((), move |()| { 1319 | let mut stuff = stuff.borrow_mut(); 1320 | 1321 | //unsafe { 1322 | // gl::Clear(gl::COLOR_BUFFER_BIT); 1323 | //} 1324 | let now = Instant::now(); 1325 | stuff.gl_window.swap_buffers().unwrap(); 1326 | let swap_time = now.elapsed(); 1327 | let swap_ms = swap_time.subsec_nanos() as f32 / 1_000_000.0; 1328 | if swap_ms > 1.0 { 1329 | println!("swap took: {}ms", swap_ms); 1330 | } 1331 | thread::sleep(Duration::from_secs(1)); 1332 | 1333 | events_loop.poll_events(|event| { 1334 | match event { 1335 | glutin::Event::WindowEvent { ref event, .. } => match *event { 1336 | glutin::WindowEvent::CloseRequested => { 1337 | stuff.stop(); 1338 | stuff.gl_window.hide(); 1339 | let _ = send_events.unbounded_send(WindowEvent::Closed); 1340 | } 1341 | glutin::WindowEvent::Resized(w, h) => stuff.gl_window.resize(w, h), 1342 | _ => (), 1343 | }, 1344 | glutin::Event::Awakened => { 1345 | return; 1346 | } 1347 | _ => (), 1348 | }; 1349 | let _ = send_events.unbounded_send(WindowEvent::Glutin(event)); 1350 | }); 1351 | 1352 | Timeout::new(Duration::from_millis(16), handle) 1353 | .unwrap() 1354 | .map(|_| Loop::Continue(())) 1355 | }) 1356 | .map_err(|_| ()); 1357 | 1358 | let streams = handle_window_events 1359 | .select(recv_msgs) 1360 | .then(|_| -> Result<(), ()> { Ok(()) }); 1361 | let _ = core.run(stop_recv.select(streams)).map_err(|_| "Oh crap!"); 1362 | println!("window_thread exiting"); 1363 | } 1364 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn it_works() {} 3 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | puts("asdf"); 2 | setTimeout(function() { setTimeout(function() { puts("2!"); }, 100); puts("delayed!"); }, 1000); 3 | setTimeout(function() { puts("3"); }, 100); 4 | setTimeout(function() { puts("3"); }, 1100); 5 | setTimeout(function() { puts("3"); }, 1100); 6 | setTimeout(function() { puts("3"); throw new Error(); }, 1100); 7 | setTimeout(function() { puts("4"); }, 1000); 8 | 9 | puts(getFileSync("test.js")); 10 | puts(readDir(".")); 11 | 12 | try { 13 | puts(1, 2); 14 | } catch (e) { 15 | puts('Caught error: '+e); 16 | } 17 | 18 | var t = new Test(); 19 | puts("Test: " + Object.keys(Test.prototype) + ";"); 20 | t.test_puts(t.test_prop); 21 | 22 | puts("Globals: " + Object.keys(this)); 23 | 24 | let window; 25 | 26 | function changeClear() { 27 | if (!window) return; 28 | 29 | window.clearColor(1, 0, 0, 1); 30 | window.clear(); 31 | } 32 | 33 | function ping() { 34 | if (window) window.ping(); 35 | else return puts("puts: window == null"); 36 | 37 | setTimeout(ping, 500); 38 | } 39 | 40 | setTimeout(function() { 41 | window = new Window(); 42 | window.onevent = function(event) { 43 | puts("Event! " + JSON.stringify(event)); 44 | }; 45 | 46 | ping(); 47 | setTimeout(changeClear, 1000); 48 | 49 | setTimeout(function() { 50 | puts("Wait over... closing..."); 51 | window.close(); 52 | 53 | setTimeout(function() { 54 | puts("Second wait..."); 55 | window = null; 56 | }, 10000); 57 | }, 10000); 58 | }, 1000); 59 | 60 | 61 | 1 + 2 + Object 62 | 63 | --------------------------------------------------------------------------------