mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 05:31:01 +00:00 
			
		
		
		
	[HTML5] Port JavaScript inline code to libraries.
The API is implemented in javascript, and generates C functions that can be called from godot. This allows much cleaner code replacing all `EM_ASM` calls in our C++ code with plain C function calls. This also gets rid of few hacks and comes with few optimizations (e.g. custom cursor shapes should be much faster now).
This commit is contained in:
		
							parent
							
								
									54cda5c3b8
								
							
						
					
					
						commit
						e2083871eb
					
				
					 33 changed files with 1995 additions and 1461 deletions
				
			
		|  | @ -33,95 +33,30 @@ | |||
| #include "api/javascript_eval.h" | ||||
| #include "emscripten.h" | ||||
| 
 | ||||
| extern "C" EMSCRIPTEN_KEEPALIVE uint8_t *resize_PackedByteArray_and_open_write(PackedByteArray *p_arr, VectorWriteProxy<uint8_t> *r_write, int p_len) { | ||||
| 	p_arr->resize(p_len); | ||||
| 	*r_write = p_arr->write; | ||||
| 	return p_arr->ptrw(); | ||||
| extern "C" { | ||||
| union js_eval_ret { | ||||
| 	uint32_t b; | ||||
| 	double d; | ||||
| 	char *s; | ||||
| }; | ||||
| 
 | ||||
| extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len)); | ||||
| } | ||||
| 
 | ||||
| void *resize_PackedByteArray_and_open_write(void *p_arr, void *r_write, int p_len) { | ||||
| 	PackedByteArray *arr = (PackedByteArray *)p_arr; | ||||
| 	VectorWriteProxy<uint8_t> *write = (VectorWriteProxy<uint8_t> *)r_write; | ||||
| 	arr->resize(p_len); | ||||
| 	*write = arr->write; | ||||
| 	return arr->ptrw(); | ||||
| } | ||||
| 
 | ||||
| Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { | ||||
| 	union { | ||||
| 		bool b; | ||||
| 		double d; | ||||
| 		char *s; | ||||
| 	} js_data; | ||||
| 
 | ||||
| 	union js_eval_ret js_data; | ||||
| 	PackedByteArray arr; | ||||
| 	VectorWriteProxy<uint8_t> arr_write; | ||||
| 
 | ||||
| 	/* clang-format off */ | ||||
| 	Variant::Type return_type = static_cast<Variant::Type>(EM_ASM_INT({ | ||||
| 
 | ||||
| 		const CODE = $0; | ||||
| 		const USE_GLOBAL_EXEC_CONTEXT = $1; | ||||
| 		const PTR = $2; | ||||
| 		const BYTEARRAY_PTR = $3; | ||||
| 		const BYTEARRAY_WRITE_PTR = $4; | ||||
| 		var eval_ret; | ||||
| 		try { | ||||
| 			if (USE_GLOBAL_EXEC_CONTEXT) { | ||||
| 				// indirect eval call grants global execution context
 | ||||
| 				var global_eval = eval; | ||||
| 				eval_ret = global_eval(UTF8ToString(CODE)); | ||||
| 			} else { | ||||
| 				eval_ret = eval(UTF8ToString(CODE)); | ||||
| 			} | ||||
| 		} catch (e) { | ||||
| 			err(e); | ||||
| 			eval_ret = null; | ||||
| 		} | ||||
| 
 | ||||
| 		switch (typeof eval_ret) { | ||||
| 
 | ||||
| 			case 'boolean': | ||||
| 				setValue(PTR, eval_ret, 'i32'); | ||||
| 				return 1; // BOOL
 | ||||
| 
 | ||||
| 			case 'number': | ||||
| 				setValue(PTR, eval_ret, 'double'); | ||||
| 				return 3; // FLOAT
 | ||||
| 
 | ||||
| 			case 'string': | ||||
| 				var array_len = lengthBytesUTF8(eval_ret)+1; | ||||
| 				var array_ptr = _malloc(array_len); | ||||
| 				try { | ||||
| 					if (array_ptr===0) { | ||||
| 						throw new Error('String allocation failed (probably out of memory)'); | ||||
| 					} | ||||
| 					setValue(PTR, array_ptr , '*'); | ||||
| 					stringToUTF8(eval_ret, array_ptr, array_len); | ||||
| 					return 4; // STRING
 | ||||
| 				} catch (e) { | ||||
| 					if (array_ptr!==0) { | ||||
| 						_free(array_ptr) | ||||
| 					} | ||||
| 					err(e); | ||||
| 					// fall through
 | ||||
| 				} | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'object': | ||||
| 				if (eval_ret === null) { | ||||
| 					break; | ||||
| 				} | ||||
| 
 | ||||
| 				if (ArrayBuffer.isView(eval_ret) && !(eval_ret instanceof Uint8Array)) { | ||||
| 					eval_ret = new Uint8Array(eval_ret.buffer); | ||||
| 				} | ||||
| 				else if (eval_ret instanceof ArrayBuffer) { | ||||
| 					eval_ret = new Uint8Array(eval_ret); | ||||
| 				} | ||||
| 				if (eval_ret instanceof Uint8Array) { | ||||
| 					var bytes_ptr = ccall('resize_PackedByteArray_and_open_write', 'number', ['number', 'number' ,'number'], [BYTEARRAY_PTR, BYTEARRAY_WRITE_PTR, eval_ret.length]); | ||||
| 					HEAPU8.set(eval_ret, bytes_ptr); | ||||
| 					return 20; // PACKED_BYTE_ARRAY
 | ||||
| 				} | ||||
| 				break; | ||||
| 		} | ||||
| 		return 0; // NIL
 | ||||
| 
 | ||||
| 	}, p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write)); | ||||
| 	/* clang-format on */ | ||||
| 	Variant::Type return_type = static_cast<Variant::Type>(godot_js_eval(p_code.utf8().get_data(), p_use_global_exec_context, &js_data, &arr, &arr_write, resize_PackedByteArray_and_open_write)); | ||||
| 
 | ||||
| 	switch (return_type) { | ||||
| 		case Variant::BOOL: | ||||
|  | @ -130,9 +65,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { | |||
| 			return js_data.d; | ||||
| 		case Variant::STRING: { | ||||
| 			String str = String::utf8(js_data.s); | ||||
| 			/* clang-format off */ | ||||
| 				EM_ASM_({ _free($0); }, js_data.s); | ||||
| 			/* clang-format on */ | ||||
| 			free(js_data.s); // Must free the string allocated in JS.
 | ||||
| 			return str; | ||||
| 		} | ||||
| 		case Variant::PACKED_BYTE_ARRAY: | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Fabio Alessandrelli
						Fabio Alessandrelli