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