Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,10 @@ test/lit/lit.site.cfg.py

# files related to clangd cache
.cache/*

# files related to tsc compilation
/src/ts/binaryen-post.js
/src/ts/binaryen-post.d.ts
/src/ts/binaryen_ts.d.ts
/src/ts/binaryen_ts.js
/*.map
39 changes: 39 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,8 @@ if(EMSCRIPTEN)
target_link_libraries(binaryen_wasm "-sEXPORT_ES6")
target_link_libraries(binaryen_wasm "-sEXPORTED_RUNTIME_METHODS=allocateUTF8OnStack,stringToAscii")
target_link_libraries(binaryen_wasm "-sEXPORTED_FUNCTIONS=_malloc,_free")
# useful when working on --post-js file
# target_link_libraries(binaryen_wasm "--minify=0")
target_link_libraries(binaryen_wasm "--post-js=${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js")
target_link_libraries(binaryen_wasm "-msign-ext")
target_link_libraries(binaryen_wasm "-mbulk-memory")
Expand Down Expand Up @@ -503,6 +505,8 @@ if(EMSCRIPTEN)
endif()
target_link_libraries(binaryen_js "-sEXPORTED_RUNTIME_METHODS=allocateUTF8OnStack,stringToAscii")
target_link_libraries(binaryen_js "-sEXPORTED_FUNCTIONS=_malloc,_free")
# useful when working on --post-js file
# target_link_libraries(binaryen_js "--minify=0")
target_link_libraries(binaryen_js "--post-js=${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js")
# js_of_ocaml needs a specified variable with special comment to provide the library to consumers
if(JS_OF_OCAML)
Expand All @@ -521,6 +525,41 @@ if(EMSCRIPTEN)
target_link_libraries(binaryen_js debug "--profiling")
target_link_libraries(binaryen_js debug "-sASSERTIONS")
install(TARGETS binaryen_js DESTINATION ${CMAKE_INSTALL_BINDIR})

# binaryen.ts TypeScript variant (leverages the wasm variant)
# compile the typescript file that wraps the wasm
add_custom_target(binaryen_ts_wrapper
COMMAND tsc --target ES2020 --module ES2022 --declaration --declarationMap
${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen_ts.ts &&
mv ${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen_ts.js ${CMAKE_BINARY_DIR}/bin/binaryen_ts.js &&
mv ${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen_ts.d.ts ${CMAKE_BINARY_DIR}/bin/binaryen_ts.d.ts &&
mv ${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen_ts.d.ts.map ${CMAKE_BINARY_DIR}/bin/binaryen_ts.d.ts.map)
add_custom_target(binaryen_post_ts
COMMAND tsc --target ES2020 --module ES2022
${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen-post.ts)
add_executable(binaryen_wasm_ts
${binaryen_emscripten_SOURCES})
add_dependencies(binaryen_wasm_ts binaryen_ts_wrapper binaryen_post_ts)
target_link_libraries(binaryen_wasm_ts wasm asmjs emscripten-optimizer passes ir cfg support analysis parser wasm)
target_link_libraries(binaryen_wasm_ts "-sFILESYSTEM")
target_link_libraries(binaryen_wasm_ts "-sEXPORT_NAME=Binaryen")
target_link_libraries(binaryen_wasm_ts "-sNODERAWFS=0")
target_link_libraries(binaryen_wasm_ts "-sEXPORT_ES6")
target_link_libraries(binaryen_wasm_ts "-sEXPORTED_RUNTIME_METHODS=allocateUTF8OnStack,stringToAscii")
target_link_libraries(binaryen_wasm_ts "-sEXPORTED_FUNCTIONS=_malloc,_free")
Comment on lines +544 to +549
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to avoid having to repeat all these flags from the vanilla binaryen_wasm build? Do we need another target at all? Can we unconditionally emit the TS wrapper for all binaryen_js and binaryen_wasm builds instead?

Copy link
Contributor Author

@ericvergnaud ericvergnaud Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An option I haven't considered is to apply for JS the same approach than TS i.e. rather than embed the JS code in the generated JS, have it live aside and consume the wasm. That way we would have a single wasm target generating:

  • binaryen_wasm.js (a trimmed down version of the current target)
  • binaryen_wasm_js.js (complements binaryen_wasm.js with the trimmed down stuff above)
  • binaryen_wasm_ts.js
  • binaryen_wasm_ts.d.ts

That's a breaking change in that the JS now requires 2 files instead of 1, but I don't anticipate anyone complaining about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually tried out this approach as an experiment, and technically speaking it would work. Let me know if that's a better idea and I'll evolve this PR accordingly.

That said it not only impacts the binaryen_wasm.js target, but also the binaryen_js.js one, since they share the binaryen.js-post.js post-generation file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kripken, what do you think about this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding adding another file here, that seems possible in general but we do build for node.js using -sSINGLE_FILE. I'm actually not sure why we do that, but if we want to add another file here we should probably look into removing SINGLE_FILE first.

Separately, could another option be to always build for TS and let users use a TS-to-JS transpiler if they don't want TS? How is this kind of thing usually done in the JS/TS ecosystem?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I'm keeping the current JS is backwards compatibility. The build script already creates a JS from TS, but it's a bit different from the current one. If binaryen_wasm only has a few consumers then it's obviously simpler to drop the current one and advertise the breaking changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it's done the usual way I.e. run tsc, which generates the JS file, a d.ts file and a .map file for debugging. Pretty standard.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks.

When you say "a bit different" is that stuff like code size or does it also behave differently? Is it a drop-in replacement?

Copy link
Contributor Author

@ericvergnaud ericvergnaud Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new file behaves differently:

  1. the current file returns a function which must be awaited in order to access the various entry points, whereas the new one directly exposes the entry points (the await is done by the generated file)
  2. a few entry points have been moved or renamed for consistency
  3. xxxID constants have been dropped, purely out of lazyness - I can't think of a scenario where one would need them

I can't change 1, but I can fix 2 and 3 if breaking change 1 is acceptable and that leads do a single source of truth and just 2 targets (like we have today) instead of 3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried turning off SINGLE_FILE. The purpose of this flag seems to be the following:

  • the first generated file is a .wasm file
  • if SINGLE_FILE is off, a .js file is generated, with code to consume the .wasm data, but locating the file is left to the application
  • if SINGLE_FILE is on, the binary .wasm is serialized to base64 and embedded in the .js
    (that doesn't affect the async loading requirement which is the blocking point for typescript, because you can't declare const and enums at runtime)

# useful when working on --post-js file
# target_link_libraries(binaryen_wasm_ts "--minify=0")
target_link_libraries(binaryen_wasm_ts "--post-js=${CMAKE_CURRENT_SOURCE_DIR}/src/ts/binaryen-post.js")
target_link_libraries(binaryen_wasm_ts "-msign-ext")
target_link_libraries(binaryen_wasm_ts "-mbulk-memory")
target_link_libraries(binaryen_wasm_ts optimized "--closure=1")
target_link_libraries(binaryen_wasm_ts optimized "--closure-args=\"--language_in=ECMASCRIPT6 --language_out=ECMASCRIPT6\"")
# TODO: Fix closure warnings! (#5062)
target_link_libraries(binaryen_wasm_ts optimized "-Wno-error=closure")
target_link_libraries(binaryen_wasm_ts optimized "-flto")
target_link_libraries(binaryen_wasm_ts debug "--profiling")
install(TARGETS binaryen_wasm_ts DESTINATION ${CMAKE_INSTALL_BINDIR})

endif()

configure_file(scripts/binaryen-lit.in ${CMAKE_BINARY_DIR}/bin/binaryen-lit @ONLY)
29 changes: 29 additions & 0 deletions src/ts/binaryen-post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
declare var Module: object;
declare var out: (s: string) => void;
declare var stringToAscii: (s: string, ptr: number) => void;
declare var stackSave: () => number;
declare var stackAlloc: (size: number) => number;
declare var stackRestore: (ref: number) => void;
declare var allocateUTF8OnStack: (s: string) => number;
declare var _BinaryenSizeofLiteral: () => number;
declare var _BinaryenSizeofAllocateAndWriteResult: () => number;
declare var UTF8ToString: (ptr: number) => string | null;

type Writer = (s: string) => void;
function swapOut(writer: Writer): Writer {
const saved = out;
out = writer;
return saved;
}

Module['utils'] = {
"swapOut": swapOut,
"stringToAscii": stringToAscii,
"stackSave": stackSave,
"stackAlloc": stackAlloc,
"stackRestore": stackRestore,
"allocateUTF8OnStack": allocateUTF8OnStack,
"_BinaryenSizeofLiteral": _BinaryenSizeofLiteral,
"_BinaryenSizeofAllocateAndWriteResult": _BinaryenSizeofAllocateAndWriteResult,
"UTF8ToString": UTF8ToString
};
Loading