Bug 1862332 [wpt PR 42877] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / gfx / harfbuzz / src / hb-wasm-shape.cc
bloba70b7666465244f60c330f5335a8876389896853
1 /*
2 * Copyright © 2011 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Google Author(s): Behdad Esfahbod
27 #undef HB_DEBUG_WASM
28 #define HB_DEBUG_WASM 1
30 #include "hb-shaper-impl.hh"
32 #ifdef HAVE_WASM
34 /* Compile wasm-micro-runtime with:
36 * $ cmake -DWAMR_BUILD_MULTI_MODULE=1 -DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_JIT=1
37 * $ make
39 * If you manage to build a wasm shared module successfully and want to use it,
40 * do the following:
42 * - Add -DWAMR_BUILD_MULTI_MODULE=1 to your cmake build for wasm-micro-runtime,
44 * - Remove the #define HB_WASM_NO_MODULES line below,
46 * - Install your shared module with name ending in .wasm in
47 * $(prefix)/$(libdir)/harfbuzz/wasm/
49 * - Build your font's wasm code importing the shared modules with the desired
50 * name. This can be done eg.: __attribute__((import_module("graphite2")))
51 * before each symbol in the shared-module's headers.
53 * - Try shaping your font and hope for the best...
55 * I haven't been able to get this to work since emcc's support for shared libraries
56 * requires support from the host that seems to be missing from wasm-micro-runtime?
59 #include "hb-wasm-api.hh"
60 #include "hb-wasm-api-list.hh"
62 #ifndef HB_WASM_NO_MODULES
63 #define HB_WASM_NO_MODULES
64 #endif
67 #ifndef HB_WASM_NO_MODULES
68 static bool HB_UNUSED
69 _hb_wasm_module_reader (const char *module_name,
70 uint8_t **p_buffer, uint32_t *p_size)
72 char path[sizeof (HB_WASM_MODULE_DIR) + 64] = HB_WASM_MODULE_DIR "/";
73 strncat (path, module_name, sizeof (path) - sizeof (HB_WASM_MODULE_DIR) - 16);
74 strncat (path, ".wasm", 6);
76 auto *blob = hb_blob_create_from_file (path);
78 unsigned length;
79 auto *data = hb_blob_get_data (blob, &length);
81 *p_buffer = (uint8_t *) hb_malloc (length);
83 if (length && !p_buffer)
84 return false;
86 memcpy (*p_buffer, data, length);
87 *p_size = length;
89 hb_blob_destroy (blob);
91 return true;
94 static void HB_UNUSED
95 _hb_wasm_module_destroyer (uint8_t *buffer, uint32_t size)
97 hb_free (buffer);
99 #endif
102 * shaper face data
105 #define HB_WASM_TAG_WASM HB_TAG('W','a','s','m')
107 struct hb_wasm_shape_plan_t {
108 wasm_module_inst_t module_inst;
109 wasm_exec_env_t exec_env;
110 ptr_d(void, wasm_shape_plan);
113 struct hb_wasm_face_data_t {
114 hb_blob_t *wasm_blob;
115 wasm_module_t wasm_module;
116 mutable hb_atomic_ptr_t<hb_wasm_shape_plan_t> plan;
119 static bool
120 _hb_wasm_init ()
122 /* XXX
124 * Umm. Make this threadsafe. How?!
125 * It's clunky that we can't allocate a static mutex.
126 * So we have to first allocate one on the heap atomically...
128 * Do we also need to lock around module creation?
130 * Also, wasm-micro-runtime uses a singleton instance. So if
131 * another library or client uses it, all bets are off. :-(
132 * If nothing else, around HB_REF2OBJ().
135 static bool initialized;
136 if (initialized)
137 return true;
139 RuntimeInitArgs init_args;
140 hb_memset (&init_args, 0, sizeof (RuntimeInitArgs));
142 init_args.mem_alloc_type = Alloc_With_Allocator;
143 init_args.mem_alloc_option.allocator.malloc_func = (void *) hb_malloc;
144 init_args.mem_alloc_option.allocator.realloc_func = (void *) hb_realloc;
145 init_args.mem_alloc_option.allocator.free_func = (void *) hb_free;
147 // Native symbols need below registration phase
148 init_args.n_native_symbols = ARRAY_LENGTH (_hb_wasm_native_symbols);
149 init_args.native_module_name = "env";
150 init_args.native_symbols = _hb_wasm_native_symbols;
152 if (unlikely (!wasm_runtime_full_init (&init_args)))
154 DEBUG_MSG (WASM, nullptr, "Init runtime environment failed.");
155 return false;
158 #ifndef HB_WASM_NO_MODULES
159 wasm_runtime_set_module_reader (_hb_wasm_module_reader,
160 _hb_wasm_module_destroyer);
161 #endif
163 initialized = true;
164 return true;
167 hb_wasm_face_data_t *
168 _hb_wasm_shaper_face_data_create (hb_face_t *face)
170 char error[128];
171 hb_wasm_face_data_t *data = nullptr;
172 hb_blob_t *wasm_blob = nullptr;
173 wasm_module_t wasm_module = nullptr;
175 wasm_blob = hb_face_reference_table (face, HB_WASM_TAG_WASM);
176 unsigned length = hb_blob_get_length (wasm_blob);
177 if (!length)
178 goto fail;
180 if (!_hb_wasm_init ())
181 goto fail;
183 wasm_module = wasm_runtime_load ((uint8_t *) hb_blob_get_data_writable (wasm_blob, nullptr),
184 length, error, sizeof (error));
185 if (unlikely (!wasm_module))
187 DEBUG_MSG (WASM, nullptr, "Load wasm module failed: %s", error);
188 goto fail;
191 data = (hb_wasm_face_data_t *) hb_calloc (1, sizeof (hb_wasm_face_data_t));
192 if (unlikely (!data))
193 goto fail;
195 data->wasm_blob = wasm_blob;
196 data->wasm_module = wasm_module;
198 return data;
200 fail:
201 if (wasm_module)
202 wasm_runtime_unload (wasm_module);
203 hb_blob_destroy (wasm_blob);
204 hb_free (data);
205 return nullptr;
208 static hb_wasm_shape_plan_t *
209 acquire_shape_plan (hb_face_t *face,
210 const hb_wasm_face_data_t *face_data)
212 char error[128];
214 /* Fetch cached one if available. */
215 hb_wasm_shape_plan_t *plan = face_data->plan.get_acquire ();
216 if (likely (plan && face_data->plan.cmpexch (plan, nullptr)))
217 return plan;
219 plan = (hb_wasm_shape_plan_t *) hb_calloc (1, sizeof (hb_wasm_shape_plan_t));
221 wasm_module_inst_t module_inst = nullptr;
222 wasm_exec_env_t exec_env = nullptr;
223 wasm_function_inst_t func = nullptr;
225 constexpr uint32_t stack_size = 32 * 1024, heap_size = 2 * 1024 * 1024;
227 module_inst = plan->module_inst = wasm_runtime_instantiate (face_data->wasm_module,
228 stack_size, heap_size,
229 error, sizeof (error));
230 if (unlikely (!module_inst))
232 DEBUG_MSG (WASM, face_data, "Create wasm module instance failed: %s", error);
233 goto fail;
236 exec_env = plan->exec_env = wasm_runtime_create_exec_env (module_inst,
237 stack_size);
238 if (unlikely (!exec_env)) {
239 DEBUG_MSG (WASM, face_data, "Create wasm execution environment failed.");
240 goto fail;
243 func = wasm_runtime_lookup_function (module_inst, "shape_plan_create", nullptr);
244 if (func)
246 wasm_val_t results[1];
247 wasm_val_t arguments[1];
249 HB_OBJ2REF (face);
250 if (unlikely (!faceref))
252 DEBUG_MSG (WASM, face_data, "Failed to register face object.");
253 goto fail;
256 results[0].kind = WASM_I32;
257 arguments[0].kind = WASM_I32;
258 arguments[0].of.i32 = faceref;
259 bool ret = wasm_runtime_call_wasm_a (exec_env, func,
260 ARRAY_LENGTH (results), results,
261 ARRAY_LENGTH (arguments), arguments);
263 if (unlikely (!ret))
265 DEBUG_MSG (WASM, module_inst, "Calling shape_plan_create() failed: %s",
266 wasm_runtime_get_exception (module_inst));
267 goto fail;
269 plan->wasm_shape_planptr = results[0].of.i32;
272 return plan;
274 fail:
276 if (exec_env)
277 wasm_runtime_destroy_exec_env (exec_env);
278 if (module_inst)
279 wasm_runtime_deinstantiate (module_inst);
280 hb_free (plan);
281 return nullptr;
284 static void
285 release_shape_plan (const hb_wasm_face_data_t *face_data,
286 hb_wasm_shape_plan_t *plan,
287 bool cache = false)
289 if (cache && face_data->plan.cmpexch (nullptr, plan))
290 return;
292 auto *module_inst = plan->module_inst;
293 auto *exec_env = plan->exec_env;
295 /* Is there even any point to having a shape_plan_destroy function
296 * and calling it? */
297 if (plan->wasm_shape_planptr)
300 auto *func = wasm_runtime_lookup_function (module_inst, "shape_plan_destroy", nullptr);
301 if (func)
303 wasm_val_t arguments[1];
305 arguments[0].kind = WASM_I32;
306 arguments[0].of.i32 = plan->wasm_shape_planptr;
307 bool ret = wasm_runtime_call_wasm_a (exec_env, func,
308 0, nullptr,
309 ARRAY_LENGTH (arguments), arguments);
311 if (unlikely (!ret))
313 DEBUG_MSG (WASM, module_inst, "Calling shape_plan_destroy() failed: %s",
314 wasm_runtime_get_exception (module_inst));
319 wasm_runtime_destroy_exec_env (exec_env);
320 wasm_runtime_deinstantiate (module_inst);
321 hb_free (plan);
324 void
325 _hb_wasm_shaper_face_data_destroy (hb_wasm_face_data_t *data)
327 if (data->plan.get_relaxed ())
328 release_shape_plan (data, data->plan);
329 wasm_runtime_unload (data->wasm_module);
330 hb_blob_destroy (data->wasm_blob);
331 hb_free (data);
336 * shaper font data
339 struct hb_wasm_font_data_t {};
341 hb_wasm_font_data_t *
342 _hb_wasm_shaper_font_data_create (hb_font_t *font HB_UNUSED)
344 return (hb_wasm_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
347 void
348 _hb_wasm_shaper_font_data_destroy (hb_wasm_font_data_t *data HB_UNUSED)
354 * shaper
357 hb_bool_t
358 _hb_wasm_shape (hb_shape_plan_t *shape_plan,
359 hb_font_t *font,
360 hb_buffer_t *buffer,
361 const hb_feature_t *features,
362 unsigned int num_features)
364 if (unlikely (buffer->in_error ()))
365 return false;
367 bool ret = true;
368 hb_face_t *face = font->face;
369 const hb_wasm_face_data_t *face_data = face->data.wasm;
371 bool retried = false;
372 if (0)
374 retry:
375 DEBUG_MSG (WASM, font, "Retrying...");
378 wasm_function_inst_t func = nullptr;
380 hb_wasm_shape_plan_t *plan = acquire_shape_plan (face, face_data);
381 if (unlikely (!plan))
383 DEBUG_MSG (WASM, face_data, "Acquiring shape-plan failed.");
384 return false;
387 auto *module_inst = plan->module_inst;
388 auto *exec_env = plan->exec_env;
390 HB_OBJ2REF (font);
391 HB_OBJ2REF (buffer);
392 if (unlikely (!fontref || !bufferref))
394 DEBUG_MSG (WASM, module_inst, "Failed to register objects.");
395 goto fail;
398 func = wasm_runtime_lookup_function (module_inst, "shape", nullptr);
399 if (unlikely (!func))
401 DEBUG_MSG (WASM, module_inst, "Shape function not found.");
402 goto fail;
405 wasm_val_t results[1];
406 wasm_val_t arguments[5];
408 results[0].kind = WASM_I32;
409 arguments[0].kind = WASM_I32;
410 arguments[0].of.i32 = plan->wasm_shape_planptr;
411 arguments[1].kind = WASM_I32;
412 arguments[1].of.i32 = fontref;
413 arguments[2].kind = WASM_I32;
414 arguments[2].of.i32 = bufferref;
415 arguments[3].kind = WASM_I32;
416 arguments[3].of.i32 = num_features ? wasm_runtime_module_dup_data (module_inst,
417 (const char *) features,
418 num_features * sizeof (features[0])) : 0;
419 arguments[4].kind = WASM_I32;
420 arguments[4].of.i32 = num_features;
422 ret = wasm_runtime_call_wasm_a (exec_env, func,
423 ARRAY_LENGTH (results), results,
424 ARRAY_LENGTH (arguments), arguments);
426 if (num_features)
427 wasm_runtime_module_free (module_inst, arguments[2].of.i32);
429 if (unlikely (!ret || !results[0].of.i32))
431 DEBUG_MSG (WASM, module_inst, "Calling shape() failed: %s",
432 wasm_runtime_get_exception (module_inst));
433 if (!buffer->ensure_unicode ())
435 DEBUG_MSG (WASM, font, "Shape failed but buffer is not in Unicode; failing...");
436 goto fail;
438 if (retried)
440 DEBUG_MSG (WASM, font, "Giving up...");
441 goto fail;
443 buffer->successful = true;
444 retried = true;
445 release_shape_plan (face_data, plan);
446 plan = nullptr;
447 goto retry;
450 /* TODO Regularize clusters according to direction & cluster level,
451 * such that client doesn't crash with unmet expectations. */
453 if (!results[0].of.i32)
455 fail:
456 ret = false;
459 release_shape_plan (face_data, plan, ret);
461 if (ret)
463 buffer->clear_glyph_flags ();
464 buffer->unsafe_to_break ();
467 return ret;
470 #endif