CGAL/cgal

Uncertain_conversion_exception when using Emscripten to compile CGAL to Wasm

Closed this issue · 5 comments

Issue Details

I'm compiling CGAL to WebAssembly using Emscripten. Quite some things seem to work; however, sometimes I get the Uncertain_conversion_exception at runtime causing the JavaScript/Wasm program to crash, while the local C++ version runs fine.

Source Code

Here is a minimal program that crashes.

#include <CGAL/approximated_offset_2.h>
#include <CGAL/Gps_circle_segment_traits_2.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>

using Exact = CGAL::Exact_predicates_exact_constructions_kernel;
template <class K> using Point = CGAL::Point_2<K>;
template <class K> using Polygon = CGAL::Polygon_2<K>;

int main(int argc, char* argv[]) {
    std::vector<Point<Exact>> points({{200, -400}, {300, -500}});
    Polygon<Exact> polygon(points.begin(), points.end());
    auto dilation = CGAL::approximated_offset_2(polygon, 50.0, 0.0000001);
    return 0;
}

I compile it using CMake / Emscripten using the following CMake file (note the compile flags).

CMakeLists.txt
cmake_minimum_required(VERSION 3.15)

project(wasm-test
    LANGUAGES CXX)

if(EMSCRIPTEN)
    add_compile_definitions(
            USE_BOOST_HEADERS=1
            CGAL_ALWAYS_ROUND_TO_NEAREST
            CGAL_DISABLE_GMP=ON
            NO_DISABLE_EXCEPTION_CATCHING
    )
    include_directories(${CMAKE_SOURCE_DIR}/lib)
else()
    find_package(CGAL REQUIRED)
    include_directories(${CGAL_INCLUDE_DIR})
    link_libraries(${CGAL_LIBRARIES})
    find_package(GMP REQUIRED)
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(wasm-test
    src/test.cpp
)

if(EMSCRIPTEN)
    set_target_properties(wasm-test PROPERTIES
            LINK_FLAGS "-sNO_DISABLE_EXCEPTION_CATCHING -g"
    )
endif()

target_link_libraries(wasm-test)
The directory in which I work has the following structure:
- lib
  - CGAL (this is a symlink to /usr/include/CGAL where my CGAL headers are installed)
- src
  - test.cpp
- CMakeLists.txt

Steps to reproduce

  1. Install dependencies and set up directory structure above, move to root.
  2. mkdir wasm && cd wasm
  3. emcmake cmake ..
  4. emmake make
  5. node wasm-test.js
  6. Get the stack trace listed below
Stack trace
/home/steven/Documents/wasm-test/wasm/wasm-test.js:1128
      exceptionLast = new CppException(ptr);
                      ^

CGAL::Uncertain_conversion_exception: Undecidable conversion of CGAL::Uncertain<T>
    at ___cxa_throw (/home/steven/Documents/wasm-test/wasm/wasm-test.js:1128:23)
    at wasm-test.wasm.CGAL::Uncertain<bool>::make_certain() const (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[1206]:0x2bc29)
    at wasm-test.wasm.CGAL::Uncertain<bool>::operator bool() const (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[1202]:0x2badd)
    at wasm-test.wasm.void CGAL::line_from_pointsC2<CGAL::Interval_nt<false>>(CGAL::Interval_nt<false> const&, CGAL::Interval_nt<false> const&, CGAL::Interval_nt<false> const&, CGAL::Interval_nt<false> const&, CGAL::Interval_nt<false>&, CGAL::Interval_nt<false>&, CGAL::Interval_nt<false>&) (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[2409]:0x4d93d)
    at wasm-test.wasm.CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>>::operator()(CGAL::Return_base_tag, CGAL::Point_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>> const&, CGAL::Point_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>> const&) const (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[2406]:0x4d7c5)
    at wasm-test.wasm.CGAL::Lazy_rep_n<CGAL::Line_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>>, CGAL::Line_2<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>>>, CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>>, CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>>>, CGAL::Cartesian_converter<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>>, CGAL::Simple_cartesian<CGAL::Interval_nt<false>>, CGAL::NT_converter<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>, CGAL::Interval_nt<false>>>, false, CGAL::Return_base_tag, CGAL::Point_2<CGAL::Epeck>, CGAL::Point_2<CGAL::Epeck>>::Lazy_rep_n<CGAL::Return_base_tag const&, CGAL::Point_2<CGAL::Epeck> const&, CGAL::Point_2<CGAL::Epeck> const&>(CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>> const&, CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>>> const&, CGAL::Return_base_tag const&, CGAL::Point_2<CGAL::Epeck> const&, CGAL::Point_2<CGAL::Epeck> const&) (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[2405]:0x4d680)
    at wasm-test.wasm.decltype(auto) CGAL::Lazy_construction<CGAL::Epeck, CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<CGAL::Interval_nt<false>>>, CGAL::CartesianKernelFunctors::Construct_line_2<CGAL::Simple_cartesian<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>>>, CGAL::Default, true>::operator()<CGAL::Return_base_tag, CGAL::Point_2<CGAL::Epeck>, CGAL::Point_2<CGAL::Epeck>>(CGAL::Return_base_tag const&, CGAL::Point_2<CGAL::Epeck> const&, CGAL::Point_2<CGAL::Epeck> const&) const (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[2397]:0x4d348)
    at wasm-test.wasm.CGAL::Line_2<CGAL::Epeck>::Line_2(CGAL::Point_2<CGAL::Epeck> const&, CGAL::Point_2<CGAL::Epeck> const&) (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[2014]:0x44491)
    at wasm-test.wasm.CGAL::_X_monotone_circle_segment_2<CGAL::Epeck, true>::_X_monotone_circle_segment_2(CGAL::Point_2<CGAL::Epeck> const&, CGAL::Point_2<CGAL::Epeck> const&) (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[951]:0x21b31)
    at wasm-test.wasm.std::__2::back_insert_iterator<std::__2::list<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2, std::__2::allocator<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2>>> CGAL::Approx_offset_base_2<CGAL::Epeck, std::__2::vector<CGAL::Point_2<CGAL::Epeck>, std::__2::allocator<CGAL::Point_2<CGAL::Epeck>>>>::_offset_polygon<std::__2::back_insert_iterator<std::__2::list<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2, std::__2::allocator<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2>>>>(CGAL::Polygon_2<CGAL::Epeck, std::__2::vector<CGAL::Point_2<CGAL::Epeck>, std::__2::allocator<CGAL::Point_2<CGAL::Epeck>>>> const&, CGAL::Sign, CGAL::Lazy_exact_nt<boost::multiprecision::number<boost::multiprecision::backends::rational_adaptor<boost::multiprecision::backends::cpp_int_backend<0ul, 0ul, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::__2::allocator<unsigned long long>>>, (boost::multiprecision::expression_template_option)1>> const&, unsigned int, std::__2::back_insert_iterator<std::__2::list<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2, std::__2::allocator<CGAL::Arr_labeled_traits_2<CGAL::Gps_circle_segment_traits_2<CGAL::Epeck, true>>::X_monotone_curve_2>>>) const (wasm://wasm/wasm-test.wasm-0714c812:wasm-function[895]:0x1f3fc) {
  excPtr: 168536
}

Environment
  • Operating system (Windows/Mac/Linux, 32/64 bits): JS/Wasm on WSL2 Ubuntu 25.04 on Windows 11
  • Compiler: Emscripten
  • CGAL version: 6.0.1-1
  • Boost version: 1.83.0.2

Try defining CGAL_ALWAYS_ROUND_TO_NEAREST before including any CGAL header.

As you can see in the CMakeLists.txt I included, I define it there using add_compile_definitions. Defining it in the cpp file using #define CGAL_ALWAYS_ROUND_TO_NEAREST makes no difference and gives a warning that it is already defined.

I missed that there were some collapsed parts...

I can't really reproduce as it requires a 32 bits env (at least when using your cmake script). I'd need to run it in a docker but I don't have time right now.

I figured out the problem. The issue turned out to be not correctly passing the flags to turn off the default disabling of exception catching by Emscripten. My current CMakeLists.txt file with which compilation succeeds is given below.

CMakeLists.txt
cmake_minimum_required(VERSION 3.15)

project(wasm-test
    LANGUAGES CXX)

if(EMSCRIPTEN)
    add_compile_definitions(
            USE_BOOST_HEADERS=1
            CGAL_ALWAYS_ROUND_TO_NEAREST
            CGAL_DISABLE_GMP=ON
    )
    include_directories(${CMAKE_SOURCE_DIR}/lib)
else()
    find_package(CGAL REQUIRED)
    include_directories(${CGAL_INCLUDE_DIR})
    link_libraries(${CGAL_LIBRARIES})
    find_package(GMP REQUIRED)
endif()

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(wasm-test
    src/test.cpp
)

if(EMSCRIPTEN)
    set_target_properties(wasm-test PROPERTIES
            LINK_FLAGS "-g -fwasm-exceptions"
    )
    target_compile_options(wasm-test PRIVATE -fwasm-exceptions)
endif()

target_link_libraries(wasm-test)