1 use std::panic;
2 use webcore::ffi;
3 
4 /// Initializes the library.
5 ///
6 /// Calling this is required for anything to work.
initialize()7 pub fn initialize() {
8     static mut INITIALIZED: bool = false;
9     unsafe {
10         if INITIALIZED {
11             return;
12         }
13 
14         INITIALIZED = true;
15     }
16 
17     js! { @(no_return)
18         Module.STDWEB = {};
19         Module.STDWEB.to_js = function to_js( address ) {
20             var kind = HEAPU8[ address + 12 ];
21             if( kind === 0 ) {
22                 return undefined;
23             } else if( kind === 1 ) {
24                 return null;
25             } else if( kind === 2 ) {
26                 return HEAP32[ address / 4 ];
27             } else if( kind === 3 ) {
28                 return HEAPF64[ address / 8 ];
29             } else if( kind === 4 ) {
30                 var pointer = HEAPU32[ address / 4 ];
31                 var length = HEAPU32[ (address + 4) / 4 ];
32                 return Module.STDWEB.to_js_string( pointer, length );
33             } else if( kind === 5 ) {
34                 return false;
35             } else if( kind === 6 ) {
36                 return true;
37             } else if( kind === 7 ) {
38                 var pointer = HEAPU32[ address / 4 ];
39                 var length = HEAPU32[ (address + 4) / 4 ];
40                 var output = [];
41                 for( var i = 0; i < length; ++i ) {
42                     output.push( Module.STDWEB.to_js( pointer + i * 16 ) );
43                 }
44                 return output;
45             } else if( kind === 8 ) {
46                 var value_array_pointer = HEAPU32[ address / 4 ];
47                 var length = HEAPU32[ (address + 4) / 4 ];
48                 var key_array_pointer = HEAPU32[ (address + 8) / 4 ];
49                 var output = {};
50                 for( var i = 0; i < length; ++i ) {
51                     var key_pointer = HEAPU32[ (key_array_pointer + i * 8) / 4 ];
52                     var key_length = HEAPU32[ (key_array_pointer + 4 + i * 8) / 4 ];
53                     var key = Module.STDWEB.to_js_string( key_pointer, key_length );
54                     var value = Module.STDWEB.to_js( value_array_pointer + i * 16 );
55                     output[ key ] = value;
56                 }
57                 return output;
58             } else if( kind === 9 ) {
59                 return Module.STDWEB.acquire_js_reference( HEAP32[ address / 4 ] );
60             } else if( kind === 10 ) {
61                 var adapter_pointer = HEAPU32[ address / 4 ];
62                 var pointer = HEAPU32[ (address + 4) / 4 ];
63                 var deallocator_pointer = HEAPU32[ (address + 8) / 4 ];
64                 var output = function() {
65                     var args = _malloc( 16 );
66                     Module.STDWEB.from_js( args, arguments );
67                     Runtime.dynCall( "vii", adapter_pointer, [pointer, args] );
68                     var result = Module.STDWEB.tmp;
69                     Module.STDWEB.tmp = null;
70 
71                     return result;
72                 };
73 
74                 output.drop = function() {
75                     output.drop = null;
76                     Runtime.dynCall( "vi", deallocator_pointer, [pointer] );
77                 };
78 
79                 return output;
80             }
81         };
82     };
83 
84     js! { @(no_return)
85         Module.STDWEB.from_js = function from_js( address, value ) {
86             var kind = Object.prototype.toString.call( value );
87             if( kind === "[object String]" ) {
88                 var length = lengthBytesUTF8( value );
89                 var pointer = _malloc( length + 1 );
90                 stringToUTF8( value, pointer, length + 1 );
91                 HEAPU8[ address + 12 ] = 4;
92                 HEAPU32[ address / 4 ] = pointer;
93                 HEAPU32[ (address + 4) / 4 ] = length;
94             } else if( kind === "[object Number]" ) {
95                 if( value === (value|0) ) {
96                     HEAPU8[ address + 12 ] = 2;
97                     HEAP32[ address / 4 ] = value;
98                 } else {
99                     HEAPU8[ address + 12 ] = 3;
100                     HEAPF64[ address / 8 ] = value;
101                 }
102             } else if( value === null ) {
103                 HEAPU8[ address + 12 ] = 1;
104             } else if( value === undefined ) {
105                 HEAPU8[ address + 12 ] = 0;
106             } else if( value === false ) {
107                 HEAPU8[ address + 12 ] = 5;
108             } else if( value === true ) {
109                 HEAPU8[ address + 12 ] = 6;
110             } else if( kind === "[object Array]" || kind === "[object Arguments]" ) {
111                 var length = value.length;
112                 var pointer = _malloc( length * 16 );
113                 HEAPU8[ address + 12 ] = 7;
114                 HEAPU32[ address / 4 ] = pointer;
115                 HEAPU32[ (address + 4) / 4 ] = length;
116                 for( var i = 0; i < length; ++i ) {
117                     Module.STDWEB.from_js( pointer + i * 16, value[ i ] );
118                 }
119             } else if( kind === "[object Object]" ) {
120                 var keys = Object.keys( value );
121                 var length = keys.length;
122                 var key_array_pointer = _malloc( length * 8 );
123                 var value_array_pointer = _malloc( length * 16 );
124                 HEAPU8[ address + 12 ] = 8;
125                 HEAPU32[ address / 4 ] = value_array_pointer;
126                 HEAPU32[ (address + 4) / 4 ] = length;
127                 HEAPU32[ (address + 8) / 4 ] = key_array_pointer;
128                 for( var i = 0; i < length; ++i ) {
129                     var key = keys[ i ];
130                     var key_length = lengthBytesUTF8( key );
131                     var key_pointer = _malloc( key_length + 1 );
132                     stringToUTF8( key, key_pointer, key_length + 1 );
133 
134                     var key_address = key_array_pointer + i * 8;
135                     HEAPU32[ key_address / 4 ] = key_pointer;
136                     HEAPU32[ (key_address + 4) / 4 ] = key_length;
137 
138                     Module.STDWEB.from_js( value_array_pointer + i * 16, value[ key ] );
139                 }
140             } else {
141                 var refid = Module.STDWEB.acquire_rust_reference( value );
142                 HEAPU8[ address + 12 ] = 9;
143                 HEAP32[ address / 4 ] = refid;
144             }
145         };
146     };
147 
148     js! { @(no_return)
149         // This is ported from Rust's stdlib; it's faster than
150         // the string conversion from Emscripten.
151         Module.STDWEB.to_js_string = function to_js_string( index, length ) {
152             index = index|0;
153             length = length|0;
154             var end = (index|0) + (length|0);
155             var output = "";
156             while( index < end ) {
157                 var x = HEAPU8[ index++ ];
158                 if( x < 128 ) {
159                     output += String.fromCharCode( x );
160                     continue;
161                 }
162                 var init = (x & (0x7F >> 2));
163                 var y = 0;
164                 if( index < end ) {
165                     y = HEAPU8[ index++ ];
166                 }
167                 var ch = (init << 6) | (y & 63);
168                 if( x >= 0xE0 ) {
169                     var z = 0;
170                     if( index < end ) {
171                         z = HEAPU8[ index++ ];
172                     }
173                     var y_z = ((y & 63) << 6) | (z & 63);
174                     ch = init << 12 | y_z;
175                     if( x >= 0xF0 ) {
176                         var w = 0;
177                         if( index < end ) {
178                             w = HEAPU8[ index++ ];
179                         }
180                         ch = (init & 7) << 18 | ((y_z << 6) | (w & 63));
181                     }
182                 }
183                 output += String.fromCharCode( ch );
184                 continue;
185             }
186             return output;
187         };
188     };
189 
190     js! { @(no_return)
191         var id_to_ref_map = {};
192         var id_to_refcount_map = {};
193         var ref_to_id_map = new WeakMap();
194         var ref_to_id_symbol_map = {};
195         var last_refid = 1;
196 
197         Module.STDWEB.acquire_rust_reference = function( reference ) {
198             if( reference === undefined || reference === null ) {
199                 return 0;
200             }
201 
202             var refid = ref_to_id_map.get( reference );
203             if( refid === undefined ) {
204                 refid = ref_to_id_symbol_map[ reference ];
205             }
206 
207             if( refid === undefined ) {
208                 refid = last_refid++;
209                 if( typeof reference === "symbol" ) {
210                     ref_to_id_symbol_map[ reference ] = refid;
211                 } else {
212                     ref_to_id_map.set( reference, refid );
213                 }
214                 id_to_ref_map[ refid ] = reference;
215                 id_to_refcount_map[ refid ] = 1;
216             } else {
217                 id_to_refcount_map[ refid ]++;
218             }
219 
220             return refid;
221         };
222 
223         Module.STDWEB.acquire_js_reference = function( refid ) {
224             return id_to_ref_map[ refid ];
225         };
226 
227         Module.STDWEB.increment_refcount = function( refid ) {
228             id_to_refcount_map[ refid ]++;
229         };
230 
231         Module.STDWEB.decrement_refcount = function( refid ) {
232             id_to_refcount_map[ refid ]--;
233             if( id_to_refcount_map[ refid ] === 0 ) {
234                 var reference = id_to_ref_map[ refid ];
235                 delete id_to_ref_map[ refid ];
236                 delete id_to_refcount_map[ refid ];
237                 if( typeof reference === "symbol" ) {
238                     delete ref_to_id_symbol_map[ reference ];
239                 } else {
240                     ref_to_id_map.delete( reference );
241                 }
242             }
243         };
244     }
245 
246     if cfg!( test ) == false {
247         panic::set_hook( Box::new( |info| {
248             em_asm_int!( "console.error( 'Encountered a panic!' );" );
249             if let Some( value ) = info.payload().downcast_ref::< String >() {
250                 em_asm_int!( "\
251                     console.error( 'Panic error message:', Module.STDWEB.to_js_string( $0, $1 ) );\
252                 ", value.as_ptr(), value.len() );
253             }
254             if let Some( location ) = info.location() {
255                 let file = location.file();
256                 em_asm_int!( "\
257                     console.error( 'Panic location:', Module.STDWEB.to_js_string( $0, $1 ) + ':' + $2 );\
258                 ", file.as_ptr(), file.len(), location.line() );
259             }
260         }));
261     }
262 }
263 
264 /// Runs the event loop.
265 ///
266 /// You should call this before returning from `main()`,
267 /// otherwise bad things will happen.
event_loop() -> !268 pub fn event_loop() -> ! {
269     unsafe {
270         ffi::emscripten_set_main_loop( Some( ffi::emscripten_pause_main_loop ), 0, 1 );
271     }
272 
273     unreachable!();
274 }
275