From 182ce69281dbd3d4f5a96026257cce31aac5c05d Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Tue, 12 Dec 2023 15:05:05 +0100 Subject: [PATCH 01/14] Reorganize native realm dependencies into package --- .gitignore | 6 +- CMakeLists.txt | 99 ------------------- package-lock.json | 3 +- package.json | 1 - packages/realm/bindgen/CMakeLists.txt | 11 +-- .../realm/bindgen/src}/.npmignore | 0 .../realm/bindgen/src}/CMakeLists.txt | 0 .../realm/bindgen/src}/android/CMakeLists.txt | 8 +- .../realm/bindgen/src}/android/hack.cpp | 0 .../realm/bindgen/src}/android/hack.hpp | 0 .../io_realm_react_RealmReactModule.cpp | 0 .../android/io_realm_react_RealmReactModule.h | 0 .../realm/bindgen/src}/android/jni_utils.cpp | 0 .../realm/bindgen/src}/android/jni_utils.hpp | 0 .../realm/bindgen/src}/android/platform.cpp | 0 .../realm/bindgen/src}/ios/CMakeLists.txt | 0 .../realm/bindgen/src}/ios/platform.mm | 0 .../realm/bindgen/src}/jsi/CMakeLists.txt | 0 .../realm/bindgen/src}/jsi/jsi_class.hpp | 0 .../realm/bindgen/src}/jsi/jsi_externs.hpp | 0 .../realm/bindgen/src}/jsi/jsi_function.hpp | 0 .../realm/bindgen/src}/jsi/jsi_init.cpp | 0 .../realm/bindgen/src}/jsi/jsi_init.h | 0 .../realm/bindgen/src}/jsi/jsi_init.hpp | 0 .../realm/bindgen/src}/jsi/jsi_object.hpp | 0 .../realm/bindgen/src}/jsi/jsi_protected.hpp | 0 .../bindgen/src}/jsi/jsi_return_value.hpp | 0 .../realm/bindgen/src}/jsi/jsi_string.hpp | 0 .../realm/bindgen/src}/jsi/jsi_types.hpp | 0 .../realm/bindgen/src}/jsi/jsi_value.hpp | 0 .../realm/bindgen/src}/logger.hpp | 0 .../realm/bindgen/src}/node/CMakeLists.txt | 0 .../realm/bindgen/src}/node/node_class.hpp | 0 .../realm/bindgen/src}/node/node_context.hpp | 0 .../realm/bindgen/src}/node/node_dummy.cpp | 0 .../bindgen/src}/node/node_exception.hpp | 0 .../realm/bindgen/src}/node/node_function.hpp | 0 .../realm/bindgen/src}/node/node_init.cpp | 0 .../realm/bindgen/src}/node/node_init.hpp | 0 .../realm/bindgen/src}/node/node_object.hpp | 0 .../bindgen/src}/node/node_protected.hpp | 0 .../bindgen/src}/node/node_return_value.hpp | 0 .../realm/bindgen/src}/node/node_string.hpp | 0 .../realm/bindgen/src}/node/node_types.hpp | 0 .../realm/bindgen/src}/node/node_value.hpp | 0 .../realm/bindgen/src}/node/platform.cpp | 0 .../realm/bindgen/src}/platform.hpp | 0 packages/realm/package.json | 7 +- .../realm/scripts}/build-android.js | 6 +- .../realm/scripts}/build-ios.sh | 6 +- .../realm/scripts}/build-node-pre-gyp.ps1 | 0 .../realm/scripts}/ccache-clang++.sh | 0 .../realm/scripts}/ccache-clang.sh | 0 53 files changed, 22 insertions(+), 125 deletions(-) delete mode 100644 CMakeLists.txt rename {src => packages/realm/bindgen/src}/.npmignore (100%) rename {src => packages/realm/bindgen/src}/CMakeLists.txt (100%) rename {src => packages/realm/bindgen/src}/android/CMakeLists.txt (92%) rename {src => packages/realm/bindgen/src}/android/hack.cpp (100%) rename {src => packages/realm/bindgen/src}/android/hack.hpp (100%) rename {src => packages/realm/bindgen/src}/android/io_realm_react_RealmReactModule.cpp (100%) rename {src => packages/realm/bindgen/src}/android/io_realm_react_RealmReactModule.h (100%) rename {src => packages/realm/bindgen/src}/android/jni_utils.cpp (100%) rename {src => packages/realm/bindgen/src}/android/jni_utils.hpp (100%) rename {src => packages/realm/bindgen/src}/android/platform.cpp (100%) rename {src => packages/realm/bindgen/src}/ios/CMakeLists.txt (100%) rename {src => packages/realm/bindgen/src}/ios/platform.mm (100%) rename {src => packages/realm/bindgen/src}/jsi/CMakeLists.txt (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_class.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_externs.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_function.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_init.cpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_init.h (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_init.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_object.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_protected.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_return_value.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_string.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_types.hpp (100%) rename {src => packages/realm/bindgen/src}/jsi/jsi_value.hpp (100%) rename {src => packages/realm/bindgen/src}/logger.hpp (100%) rename {src => packages/realm/bindgen/src}/node/CMakeLists.txt (100%) rename {src => packages/realm/bindgen/src}/node/node_class.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_context.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_dummy.cpp (100%) rename {src => packages/realm/bindgen/src}/node/node_exception.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_function.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_init.cpp (100%) rename {src => packages/realm/bindgen/src}/node/node_init.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_object.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_protected.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_return_value.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_string.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_types.hpp (100%) rename {src => packages/realm/bindgen/src}/node/node_value.hpp (100%) rename {src => packages/realm/bindgen/src}/node/platform.cpp (100%) rename {src => packages/realm/bindgen/src}/platform.hpp (100%) rename {scripts => packages/realm/scripts}/build-android.js (96%) rename {scripts => packages/realm/scripts}/build-ios.sh (94%) rename {scripts => packages/realm/scripts}/build-node-pre-gyp.ps1 (100%) rename {scripts => packages/realm/scripts}/ccache-clang++.sh (100%) rename {scripts => packages/realm/scripts}/ccache-clang.sh (100%) diff --git a/.gitignore b/.gitignore index 90c4ea8c3b6..5b91ac900c1 100644 --- a/.gitignore +++ b/.gitignore @@ -49,9 +49,9 @@ yarn.lock # Android/IJ /android -/react-native/android/src/main/jni/core -/react-native/android/.cxx/ -/build-android/ +/packages/realm/react-native/android/src/main/jni/core +/packages/realm/react-native/android/.cxx/ +/packages/realm/build-android/ .idea .gradle local.properties diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 263becfa118..00000000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,99 +0,0 @@ -include(CheckCXXCompilerFlag) - -# compiling for Node.js, need to set up toolchains before project() call -if(DEFINED CMAKE_JS_VERSION) - if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - if(NODE_ARCH STREQUAL "ia32") - set(VCPKG_TARGET_TRIPLET "x86-windows-static" CACHE STRING "") - elseif(NODE_ARCH STREQUAL "x64") - set(VCPKG_TARGET_TRIPLET "x64-windows-static" CACHE STRING "") - else() - message(FATAL_ERROR "Unable to deduce vcpkg target triplet") - endif() - elseif(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") - if(NODE_ARCH STREQUAL "arm64") - set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "") - else() - set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE STRING "") - endif() - elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") - if(NODE_ARCH STREQUAL "arm") - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vendor/realm-core/tools/cmake/armv7-linux-gnueabihf.toolchain.cmake") - # due to how multiarch works in debian this is needed to link to the correct system libraries - set(CMAKE_IGNORE_PATH "/usr/lib/x86_64-linux-gnu") - elseif(NODE_ARCH STREQUAL "arm64") - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vendor/realm-core/tools/cmake/aarch64-linux-gnu.toolchain.cmake") - set(CMAKE_IGNORE_PATH "/usr/lib/x86_64-linux-gnu") - elseif(NODE_ARCH STREQUAL "x64") - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/vendor/realm-core/tools/cmake/x86_64-linux-gnu.toolchain.cmake") - endif() - add_link_options(-fuse-ld=gold) - add_link_options(-Wl,-z,noexecstack) - add_link_options(-Wl,--warn-execstack) - endif() -endif() - -cmake_minimum_required(VERSION 3.21.3) -project(RealmJS) - -# Use ccache if available -find_program(CCACHE_PROGRAM ccache) -if(CCACHE_PROGRAM) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") -endif() - -# Copied from Realm Core's CMakeList.txt -function(add_cxx_flag_if_supported flag) - if(flag MATCHES "^-Wno-") - # Compilers ignore unknown -Wno-foo flags, so look for -Wfoo instead. - string(REPLACE "-Wno-" "-W" check_flag ${flag}) - else() - set(check_flag ${flag}) - endif() - - check_cxx_compiler_flag(${check_flag} HAVE${check_flag}) - if(HAVE${check_flag}) - add_compile_options($<$:${flag}>) - endif() -endfunction() - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -set(PACKAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -if(DEFINED CMAKE_JS_VERSION) - include(NodeJSTargets) -endif() - -if (NOT MSVC) - # Ninja buffers output so we need to tell the compiler to use colors even though stdout isn't a tty. - if(CMAKE_GENERATOR STREQUAL "Ninja") - add_cxx_flag_if_supported(-fdiagnostics-color) - endif() -endif() - -option(REALM_JS_BUILD_CORE_FROM_SOURCE "Build Realm Core from source, as opposed to downloading prebuilt binaries" ON) -if(REALM_JS_BUILD_CORE_FROM_SOURCE) - set(REALM_BUILD_LIB_ONLY ON) - set(REALM_ENABLE_SYNC ON) - - if(ANDROID) - set(REALM_ANDROID ON) - endif() - - add_subdirectory(vendor/realm-core EXCLUDE_FROM_ALL) -endif() - -add_subdirectory(src) -add_definitions(-DNAPI_VERSION=5) diff --git a/package-lock.json b/package-lock.json index e229c046ed9..f475457b64e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,6 @@ "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "clang-format": "^1.8.0", - "command-line-args": "^5.2.1", "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-flowtype": "^8.0.3", @@ -28964,6 +28963,7 @@ "@types/path-browserify": "^1.0.0", "chai": "4.3.6", "cmake-js": "6.3.2", + "command-line-args": "^5.2.1", "cross-env": "^7.0.3", "mocha": "^10.1.0", "path-browserify": "^1.0.1", @@ -49123,6 +49123,7 @@ "bson": "^4.7.2", "chai": "4.3.6", "cmake-js": "6.3.2", + "command-line-args": "^5.2.1", "cross-env": "^7.0.3", "debug": "^4.3.4", "mocha": "^10.1.0", diff --git a/package.json b/package.json index 05e80e16042..98fde9045f5 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,6 @@ "@typescript-eslint/eslint-plugin": "^5.60.0", "@typescript-eslint/parser": "^5.60.0", "clang-format": "^1.8.0", - "command-line-args": "^5.2.1", "eslint": "^8.43.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-flowtype": "^8.0.3", diff --git a/packages/realm/bindgen/CMakeLists.txt b/packages/realm/bindgen/CMakeLists.txt index 5df763b6123..4ddb93111fd 100644 --- a/packages/realm/bindgen/CMakeLists.txt +++ b/packages/realm/bindgen/CMakeLists.txt @@ -100,7 +100,7 @@ if(REALM_JS_BUILD_CORE_FROM_SOURCE) add_subdirectory(vendor/realm-core EXCLUDE_FROM_ALL) endif() - + add_library(realm-js OBJECT) target_link_libraries(realm-js Realm::ObjectStore) @@ -119,9 +119,6 @@ endif() target_include_directories(realm-js PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") -# TODO move platform.hpp and delete this line. -target_include_directories(realm-js PRIVATE "${PACKAGE_ROOT_DIR}/src") - set(SDK_DIR ${PACKAGE_ROOT_DIR}/packages/realm) file(GLOB_RECURSE SDK_TS_FILES @@ -178,7 +175,7 @@ if(DEFINED CMAKE_JS_VERSION) SOURCES ${SDK_TS_FILES} ) - target_sources(realm-js PRIVATE node_init.cpp ${CMAKE_JS_SRC} ${PACKAGE_ROOT_DIR}/src/node/platform.cpp) + target_sources(realm-js PRIVATE node_init.cpp ${CMAKE_JS_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/node/platform.cpp) else() include(jsi.cmake) @@ -194,9 +191,9 @@ else() target_sources(realm-js PRIVATE jsi_init.cpp ${CMAKE_JS_SRC}) if(ANDROID) - add_subdirectory(${PACKAGE_ROOT_DIR}/src/android android) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/android android) elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) - add_subdirectory(${PACKAGE_ROOT_DIR}/src/ios ios) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/ios ios) else() # disabling for now to simplify the bootstrapping process. # message(FATAL_ERROR "Only support JSI builds for Android and iOS") diff --git a/src/.npmignore b/packages/realm/bindgen/src/.npmignore similarity index 100% rename from src/.npmignore rename to packages/realm/bindgen/src/.npmignore diff --git a/src/CMakeLists.txt b/packages/realm/bindgen/src/CMakeLists.txt similarity index 100% rename from src/CMakeLists.txt rename to packages/realm/bindgen/src/CMakeLists.txt diff --git a/src/android/CMakeLists.txt b/packages/realm/bindgen/src/android/CMakeLists.txt similarity index 92% rename from src/android/CMakeLists.txt rename to packages/realm/bindgen/src/android/CMakeLists.txt index fb8c42e277a..79a5f4b5066 100644 --- a/src/android/CMakeLists.txt +++ b/packages/realm/bindgen/src/android/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(realm-js-android SHARED platform.cpp jni_utils.cpp io_realm_react_RealmReactModule.cpp - ../../node_modules/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.cpp + ${REACT_NATIVE_ROOT_DIR}/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.cpp ) set_target_properties(realm-js-android PROPERTIES @@ -110,9 +110,9 @@ endif() find_library(FBJNI_LIBRARY fbjni PATHS ${FBJNI_LIB_DIR} NO_CMAKE_FIND_ROOT_PATH) target_include_directories(realm-js-android PUBLIC ${FBJNI_INCLUDE_DIR} - ../../node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ - ../../node_modules/react-native/ReactAndroid/src/main/jni/react/turbomodule/ - ../../node_modules/react-native/ReactCommon/callinvoker/ + ${REACT_NATIVE_ROOT_DIR}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ + ${REACT_NATIVE_ROOT_DIR}/ReactAndroid/src/main/jni/react/turbomodule/ + ${REACT_NATIVE_ROOT_DIR}/react-native/ReactCommon/callinvoker/ ) target_link_libraries(realm-js-android diff --git a/src/android/hack.cpp b/packages/realm/bindgen/src/android/hack.cpp similarity index 100% rename from src/android/hack.cpp rename to packages/realm/bindgen/src/android/hack.cpp diff --git a/src/android/hack.hpp b/packages/realm/bindgen/src/android/hack.hpp similarity index 100% rename from src/android/hack.hpp rename to packages/realm/bindgen/src/android/hack.hpp diff --git a/src/android/io_realm_react_RealmReactModule.cpp b/packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.cpp similarity index 100% rename from src/android/io_realm_react_RealmReactModule.cpp rename to packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.cpp diff --git a/src/android/io_realm_react_RealmReactModule.h b/packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.h similarity index 100% rename from src/android/io_realm_react_RealmReactModule.h rename to packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.h diff --git a/src/android/jni_utils.cpp b/packages/realm/bindgen/src/android/jni_utils.cpp similarity index 100% rename from src/android/jni_utils.cpp rename to packages/realm/bindgen/src/android/jni_utils.cpp diff --git a/src/android/jni_utils.hpp b/packages/realm/bindgen/src/android/jni_utils.hpp similarity index 100% rename from src/android/jni_utils.hpp rename to packages/realm/bindgen/src/android/jni_utils.hpp diff --git a/src/android/platform.cpp b/packages/realm/bindgen/src/android/platform.cpp similarity index 100% rename from src/android/platform.cpp rename to packages/realm/bindgen/src/android/platform.cpp diff --git a/src/ios/CMakeLists.txt b/packages/realm/bindgen/src/ios/CMakeLists.txt similarity index 100% rename from src/ios/CMakeLists.txt rename to packages/realm/bindgen/src/ios/CMakeLists.txt diff --git a/src/ios/platform.mm b/packages/realm/bindgen/src/ios/platform.mm similarity index 100% rename from src/ios/platform.mm rename to packages/realm/bindgen/src/ios/platform.mm diff --git a/src/jsi/CMakeLists.txt b/packages/realm/bindgen/src/jsi/CMakeLists.txt similarity index 100% rename from src/jsi/CMakeLists.txt rename to packages/realm/bindgen/src/jsi/CMakeLists.txt diff --git a/src/jsi/jsi_class.hpp b/packages/realm/bindgen/src/jsi/jsi_class.hpp similarity index 100% rename from src/jsi/jsi_class.hpp rename to packages/realm/bindgen/src/jsi/jsi_class.hpp diff --git a/src/jsi/jsi_externs.hpp b/packages/realm/bindgen/src/jsi/jsi_externs.hpp similarity index 100% rename from src/jsi/jsi_externs.hpp rename to packages/realm/bindgen/src/jsi/jsi_externs.hpp diff --git a/src/jsi/jsi_function.hpp b/packages/realm/bindgen/src/jsi/jsi_function.hpp similarity index 100% rename from src/jsi/jsi_function.hpp rename to packages/realm/bindgen/src/jsi/jsi_function.hpp diff --git a/src/jsi/jsi_init.cpp b/packages/realm/bindgen/src/jsi/jsi_init.cpp similarity index 100% rename from src/jsi/jsi_init.cpp rename to packages/realm/bindgen/src/jsi/jsi_init.cpp diff --git a/src/jsi/jsi_init.h b/packages/realm/bindgen/src/jsi/jsi_init.h similarity index 100% rename from src/jsi/jsi_init.h rename to packages/realm/bindgen/src/jsi/jsi_init.h diff --git a/src/jsi/jsi_init.hpp b/packages/realm/bindgen/src/jsi/jsi_init.hpp similarity index 100% rename from src/jsi/jsi_init.hpp rename to packages/realm/bindgen/src/jsi/jsi_init.hpp diff --git a/src/jsi/jsi_object.hpp b/packages/realm/bindgen/src/jsi/jsi_object.hpp similarity index 100% rename from src/jsi/jsi_object.hpp rename to packages/realm/bindgen/src/jsi/jsi_object.hpp diff --git a/src/jsi/jsi_protected.hpp b/packages/realm/bindgen/src/jsi/jsi_protected.hpp similarity index 100% rename from src/jsi/jsi_protected.hpp rename to packages/realm/bindgen/src/jsi/jsi_protected.hpp diff --git a/src/jsi/jsi_return_value.hpp b/packages/realm/bindgen/src/jsi/jsi_return_value.hpp similarity index 100% rename from src/jsi/jsi_return_value.hpp rename to packages/realm/bindgen/src/jsi/jsi_return_value.hpp diff --git a/src/jsi/jsi_string.hpp b/packages/realm/bindgen/src/jsi/jsi_string.hpp similarity index 100% rename from src/jsi/jsi_string.hpp rename to packages/realm/bindgen/src/jsi/jsi_string.hpp diff --git a/src/jsi/jsi_types.hpp b/packages/realm/bindgen/src/jsi/jsi_types.hpp similarity index 100% rename from src/jsi/jsi_types.hpp rename to packages/realm/bindgen/src/jsi/jsi_types.hpp diff --git a/src/jsi/jsi_value.hpp b/packages/realm/bindgen/src/jsi/jsi_value.hpp similarity index 100% rename from src/jsi/jsi_value.hpp rename to packages/realm/bindgen/src/jsi/jsi_value.hpp diff --git a/src/logger.hpp b/packages/realm/bindgen/src/logger.hpp similarity index 100% rename from src/logger.hpp rename to packages/realm/bindgen/src/logger.hpp diff --git a/src/node/CMakeLists.txt b/packages/realm/bindgen/src/node/CMakeLists.txt similarity index 100% rename from src/node/CMakeLists.txt rename to packages/realm/bindgen/src/node/CMakeLists.txt diff --git a/src/node/node_class.hpp b/packages/realm/bindgen/src/node/node_class.hpp similarity index 100% rename from src/node/node_class.hpp rename to packages/realm/bindgen/src/node/node_class.hpp diff --git a/src/node/node_context.hpp b/packages/realm/bindgen/src/node/node_context.hpp similarity index 100% rename from src/node/node_context.hpp rename to packages/realm/bindgen/src/node/node_context.hpp diff --git a/src/node/node_dummy.cpp b/packages/realm/bindgen/src/node/node_dummy.cpp similarity index 100% rename from src/node/node_dummy.cpp rename to packages/realm/bindgen/src/node/node_dummy.cpp diff --git a/src/node/node_exception.hpp b/packages/realm/bindgen/src/node/node_exception.hpp similarity index 100% rename from src/node/node_exception.hpp rename to packages/realm/bindgen/src/node/node_exception.hpp diff --git a/src/node/node_function.hpp b/packages/realm/bindgen/src/node/node_function.hpp similarity index 100% rename from src/node/node_function.hpp rename to packages/realm/bindgen/src/node/node_function.hpp diff --git a/src/node/node_init.cpp b/packages/realm/bindgen/src/node/node_init.cpp similarity index 100% rename from src/node/node_init.cpp rename to packages/realm/bindgen/src/node/node_init.cpp diff --git a/src/node/node_init.hpp b/packages/realm/bindgen/src/node/node_init.hpp similarity index 100% rename from src/node/node_init.hpp rename to packages/realm/bindgen/src/node/node_init.hpp diff --git a/src/node/node_object.hpp b/packages/realm/bindgen/src/node/node_object.hpp similarity index 100% rename from src/node/node_object.hpp rename to packages/realm/bindgen/src/node/node_object.hpp diff --git a/src/node/node_protected.hpp b/packages/realm/bindgen/src/node/node_protected.hpp similarity index 100% rename from src/node/node_protected.hpp rename to packages/realm/bindgen/src/node/node_protected.hpp diff --git a/src/node/node_return_value.hpp b/packages/realm/bindgen/src/node/node_return_value.hpp similarity index 100% rename from src/node/node_return_value.hpp rename to packages/realm/bindgen/src/node/node_return_value.hpp diff --git a/src/node/node_string.hpp b/packages/realm/bindgen/src/node/node_string.hpp similarity index 100% rename from src/node/node_string.hpp rename to packages/realm/bindgen/src/node/node_string.hpp diff --git a/src/node/node_types.hpp b/packages/realm/bindgen/src/node/node_types.hpp similarity index 100% rename from src/node/node_types.hpp rename to packages/realm/bindgen/src/node/node_types.hpp diff --git a/src/node/node_value.hpp b/packages/realm/bindgen/src/node/node_value.hpp similarity index 100% rename from src/node/node_value.hpp rename to packages/realm/bindgen/src/node/node_value.hpp diff --git a/src/node/platform.cpp b/packages/realm/bindgen/src/node/platform.cpp similarity index 100% rename from src/node/platform.cpp rename to packages/realm/bindgen/src/node/platform.cpp diff --git a/src/platform.hpp b/packages/realm/bindgen/src/platform.hpp similarity index 100% rename from src/platform.hpp rename to packages/realm/bindgen/src/platform.hpp diff --git a/packages/realm/package.json b/packages/realm/package.json index bf469b35933..dffa75aeb3b 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -220,9 +220,9 @@ } }, "build:android": { - "command": "node ../../scripts/build-android.js", + "command": "node ./scripts/build-android.js", "files": [ - "../../src/android/**", + "bindgen/android/**", "bindgen/src/**/*.ts", "bindgen/vendor/realm-core/bindgen/src/**/*.ts", "bindgen/vendor/realm-core/bindgen/src/**/*.h", @@ -230,7 +230,7 @@ ] }, "build:ios": { - "command": "../../scripts/build-ios.sh -c ${CONFIGURATION:=Release} ${PLATFORMS}", + "command": "scripts/build-ios.sh -c ${CONFIGURATION:=Release} ${PLATFORMS}", "files": [ "../../src/ios/**", "bindgen/src/**/*.ts", @@ -302,6 +302,7 @@ "@types/mocha": "^10.0.0", "@types/node": "^18.15.10", "@types/path-browserify": "^1.0.0", + "command-line-args": "^5.2.1", "chai": "4.3.6", "cmake-js": "6.3.2", "cross-env": "^7.0.3", diff --git a/scripts/build-android.js b/packages/realm/scripts/build-android.js similarity index 96% rename from scripts/build-android.js rename to packages/realm/scripts/build-android.js index cae822736b7..91f473c2762 100644 --- a/scripts/build-android.js +++ b/packages/realm/scripts/build-android.js @@ -21,7 +21,7 @@ const fs = require("fs-extra"); const path = require("path"); const exec = require("child_process").execFileSync; -const { version } = require("realm/package.json"); +const { version } = require("../package.json"); const packageRoot = path.resolve(__dirname, ".."); @@ -91,7 +91,7 @@ for (const arch of architectures) { "-DANDROID_NATIVE_API_LEVEL=16", `-DCMAKE_BUILD_TYPE=${buildType}`, "-DANDROID_STL=c++_shared", - path.resolve(packageRoot, "packages/realm/bindgen"), + path.resolve(packageRoot, "./bindgen"), ]; exec(cmakePath, args, { cwd: archBuildDir, stdio: "inherit" }); @@ -105,8 +105,6 @@ generateVersionFile(); function generateVersionFile() { const targetFile = path.resolve( packageRoot, - "packages", - "realm", "react-native", "android", "src", diff --git a/scripts/build-ios.sh b/packages/realm/scripts/build-ios.sh similarity index 94% rename from scripts/build-ios.sh rename to packages/realm/scripts/build-ios.sh index b5a0be129ef..48397f35919 100755 --- a/scripts/build-ios.sh +++ b/packages/realm/scripts/build-ios.sh @@ -6,8 +6,8 @@ set -o pipefail # Start in the root directory of the project. cd "$(dirname "$0")/.." PROJECT_ROOT=$(pwd) -SDK_PATH=$PROJECT_ROOT/packages/realm -BINDGEN_PATH=$PROJECT_ROOT/packages/realm/bindgen +SDK_PATH=$PROJECT_ROOT +BINDGEN_PATH=$PROJECT_ROOT/bindgen SCRIPT=$(basename "${BASH_SOURCE[0]}") function usage { @@ -97,7 +97,7 @@ DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" # Configure CMake project SDKROOT="$DEVELOPER_DIR/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/" cmake "$BINDGEN_PATH" -GXcode \ - -DCMAKE_TOOLCHAIN_FILE="$PROJECT_ROOT/packages/realm/bindgen/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ + -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out/$\$EFFECTIVE_PLATFORM_NAME" \ DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ diff --git a/scripts/build-node-pre-gyp.ps1 b/packages/realm/scripts/build-node-pre-gyp.ps1 similarity index 100% rename from scripts/build-node-pre-gyp.ps1 rename to packages/realm/scripts/build-node-pre-gyp.ps1 diff --git a/scripts/ccache-clang++.sh b/packages/realm/scripts/ccache-clang++.sh similarity index 100% rename from scripts/ccache-clang++.sh rename to packages/realm/scripts/ccache-clang++.sh diff --git a/scripts/ccache-clang.sh b/packages/realm/scripts/ccache-clang.sh similarity index 100% rename from scripts/ccache-clang.sh rename to packages/realm/scripts/ccache-clang.sh From 39a86a8f3e92cdabdfe625766eb65d4e683b8048 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Tue, 12 Dec 2023 15:11:34 +0100 Subject: [PATCH 02/14] Remove unused native modules for node --- .../realm/bindgen/src/node/CMakeLists.txt | 38 - .../realm/bindgen/src/node/node_class.hpp | 1636 ----------------- .../realm/bindgen/src/node/node_context.hpp | 33 - .../realm/bindgen/src/node/node_dummy.cpp | 39 - .../realm/bindgen/src/node/node_exception.hpp | 34 - .../realm/bindgen/src/node/node_function.hpp | 63 - packages/realm/bindgen/src/node/node_init.cpp | 52 - packages/realm/bindgen/src/node/node_init.hpp | 32 - .../realm/bindgen/src/node/node_object.hpp | 245 --- .../realm/bindgen/src/node/node_protected.hpp | 247 --- .../bindgen/src/node/node_return_value.hpp | 130 -- .../realm/bindgen/src/node/node_string.hpp | 81 - .../realm/bindgen/src/node/node_types.hpp | 87 - .../realm/bindgen/src/node/node_value.hpp | 396 ---- 14 files changed, 3113 deletions(-) delete mode 100644 packages/realm/bindgen/src/node/CMakeLists.txt delete mode 100644 packages/realm/bindgen/src/node/node_class.hpp delete mode 100644 packages/realm/bindgen/src/node/node_context.hpp delete mode 100644 packages/realm/bindgen/src/node/node_dummy.cpp delete mode 100644 packages/realm/bindgen/src/node/node_exception.hpp delete mode 100644 packages/realm/bindgen/src/node/node_function.hpp delete mode 100644 packages/realm/bindgen/src/node/node_init.cpp delete mode 100644 packages/realm/bindgen/src/node/node_init.hpp delete mode 100644 packages/realm/bindgen/src/node/node_object.hpp delete mode 100644 packages/realm/bindgen/src/node/node_protected.hpp delete mode 100644 packages/realm/bindgen/src/node/node_return_value.hpp delete mode 100644 packages/realm/bindgen/src/node/node_string.hpp delete mode 100644 packages/realm/bindgen/src/node/node_types.hpp delete mode 100644 packages/realm/bindgen/src/node/node_value.hpp diff --git a/packages/realm/bindgen/src/node/CMakeLists.txt b/packages/realm/bindgen/src/node/CMakeLists.txt deleted file mode 100644 index 2c68f2d7104..00000000000 --- a/packages/realm/bindgen/src/node/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -add_library(realm-js SHARED - node_init.cpp - platform.cpp - ${CMAKE_JS_SRC} -) - -set_target_properties(realm-js PROPERTIES - OUTPUT_NAME "realm" - PREFIX "" - SUFFIX ".node" -) - -if(WIN32) - set_source_files_properties(node_init.cpp PROPERTIES - # see https://docs.microsoft.com/en-us/cpp/build/reference/bigobj-increase-number-of-sections-in-dot-obj-file?view=msvc-160#remarks - COMPILE_FLAGS "/bigobj" - ) - - find_package(OpenSSL REQUIRED) - target_link_libraries(realm-js OpenSSL::SSL) - target_link_options(realm-js PRIVATE "/WHOLEARCHIVE:libssl.lib") -endif() - -target_compile_definitions(realm-js PRIVATE - REALM_HAVE_UV=1 - REALM_PLATFORM_NODE=1 -) - -if(REALM_JS_BUILD_CORE_FROM_SOURCE AND TARGET ObjectStore) - # we need to tell ObjectStore to use the libuv provided by Node.js - target_link_libraries(ObjectStore PUBLIC NodeJS) - target_compile_definitions(ObjectStore PUBLIC - REALM_HAVE_UV=1 - REALM_PLATFORM_NODE=1 - ) -endif() - -target_link_libraries(realm-js realm-js-shared NodeJS) diff --git a/packages/realm/bindgen/src/node/node_class.hpp b/packages/realm/bindgen/src/node/node_class.hpp deleted file mode 100644 index 01c1f31fdc6..00000000000 --- a/packages/realm/bindgen/src/node/node_class.hpp +++ /dev/null @@ -1,1636 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" - -#include "js_class.hpp" -#include "js_util.hpp" - -#include "napi.h" - -#include -#include -#include -#include -#include -#include -#include - -// forward declare the types for gcc to compile correctly -namespace realm { -namespace js { -template -struct RealmObjectClass; - -template -class RealmClass; -} // namespace js -namespace node { -struct Types; -} -} // namespace realm - -namespace realm { -namespace node { - -Napi::FunctionReference ObjectGetOwnPropertyDescriptor; -node::Protected ExternalSymbol; -Napi::FunctionReference ObjectCreate; -Napi::FunctionReference ObjectSetPrototypeOf; -Napi::FunctionReference GlobalProxy; -Napi::FunctionReference FunctionBind; -Napi::FunctionReference RealmClassConstructor; - -static void node_class_init(Napi::Env env) -{ - auto setPrototypeOf = env.Global().Get("Object").As().Get("setPrototypeOf").As(); - ObjectSetPrototypeOf = Napi::Persistent(setPrototypeOf); - ObjectSetPrototypeOf.SuppressDestruct(); - - auto create = env.Global().Get("Object").As().Get("create").As(); - ObjectCreate = Napi::Persistent(create); - ObjectCreate.SuppressDestruct(); - - - auto getOwnPropertyDescriptor = - env.Global().Get("Object").As().Get("getOwnPropertyDescriptor").As(); - ObjectGetOwnPropertyDescriptor = Napi::Persistent(getOwnPropertyDescriptor); - ObjectGetOwnPropertyDescriptor.SuppressDestruct(); - - auto proxy = env.Global().Get("Proxy").As(); - GlobalProxy = Napi::Persistent(proxy); - GlobalProxy.SuppressDestruct(); - - - auto bind = env.Global() - .Get("Function") - .As() - .Get("prototype") - .As() - .Get("bind") - .As(); - FunctionBind = Napi::Persistent(bind); - FunctionBind.SuppressDestruct(); - - Napi::Symbol ext = Napi::Symbol::New(env, "_external"); - ExternalSymbol = node::Protected(env, ext); - ExternalSymbol.SuppressDestruct(); -} - -template -using ClassDefinition = js::ClassDefinition; - -using ConstructorType = js::ConstructorType; -using ArgumentsMethodType = js::ArgumentsMethodType; -using Arguments = js::Arguments; -using PropertyType = js::PropertyType; -using IndexPropertyType = js::IndexPropertyType; -using StringPropertyType = js::StringPropertyType; - -template -class WrappedObject : public Napi::ObjectWrap> { - using Internal = typename ClassType::Internal; - -public: - WrappedObject(const Napi::CallbackInfo& info); - - static Napi::Value create_instance_with_proxy(const Napi::CallbackInfo& info); - - static Napi::Function init(Napi::Env env, const std::string& name, - node::Types::FunctionCallback constructor_callback, - std::function has_native_method_callback, - const std::vector>>& properties, - const IndexPropertyType* indexPropertyHandlers = nullptr); - - static Napi::Object create_instance(Napi::Env env, Internal* internal = nullptr); - - static bool is_instance(Napi::Env env, const Napi::Object& object); - - static WrappedObject* try_unwrap(const Napi::Object& object); - - Internal* get_internal(); - void set_internal(Internal* internal); - static void set_factory_constructor(const Napi::Function& factoryConstructor); - static Napi::Function get_constructor(Napi::Env env); - - Napi::Value method_callback(const Napi::CallbackInfo& info); - Napi::Value getter_callback(const Napi::CallbackInfo& info); - void setter_callback(const Napi::CallbackInfo& info, const Napi::Value& value); - void readonly_setter_callback(const Napi::CallbackInfo& info, const Napi::Value& value); - static void readonly_static_setter_callback(const Napi::CallbackInfo& info, const Napi::Value& value); - -private: - std::unique_ptr m_internal; - static Napi::FunctionReference constructor; - static Napi::FunctionReference factory_constructor; - static IndexPropertyType* m_indexPropertyHandlers; - static std::string m_name; - static std::function m_has_native_methodFunc; - static Napi::Reference> m_nullExternal; - - class ProxyHandler { - public: - static Napi::Value get_instance_proxy_handler(Napi::Env env); - - private: - static Napi::Value bind_native_function(Napi::Env env, const std::string& functionName, - const Napi::Function& function, const Napi::Object& thisObject); - static Napi::ObjectReference m_proxyHandler; - - - static Napi::Value get_proxy_trap(const Napi::CallbackInfo& info); - static Napi::Value set_proxy_trap(const Napi::CallbackInfo& info); - static Napi::Value own_keys_proxy_trap(const Napi::CallbackInfo& info); - static Napi::Value has_proxy_trap(const Napi::CallbackInfo& info); - static Napi::Value get_own_property_descriptor_trap(const Napi::CallbackInfo& info); - static Napi::Value get_prototype_of_proxy_trap(const Napi::CallbackInfo& info); - static Napi::Value set_prototype_of_proxy_trap(const Napi::CallbackInfo& info); - }; -}; - -template -Napi::FunctionReference WrappedObject::constructor; - -template -Napi::FunctionReference WrappedObject::factory_constructor; - -template -IndexPropertyType* WrappedObject::m_indexPropertyHandlers; - -template -std::string WrappedObject::m_name; - -template -std::function WrappedObject::m_has_native_methodFunc; - -template -Napi::Reference> WrappedObject::m_nullExternal; - - -struct SchemaObjectType { - Napi::FunctionReference constructor; -}; - -template -class ObjectWrap { - using Internal = typename ClassType::Internal; - using ParentClassType = typename ClassType::Parent; - -public: - static Napi::Function create_constructor(Napi::Env env); - - static Napi::Object create_instance(Napi::Env env, Internal* = nullptr); - static Napi::Object create_instance_by_schema(Napi::Env env, Napi::Function& constructor, - const realm::ObjectSchema& schema, Internal* internal = nullptr); - static Napi::Object create_instance_by_schema(Napi::Env env, const realm::ObjectSchema& schema, - Internal* internal = nullptr) - { - Napi::Function constructor; - return create_instance_by_schema(env, constructor, schema, internal); - } - static void internal_finalizer(Napi::Env, typename ClassType::Internal* internal); - - static void on_context_destroy(Napi::Env env, std::string realmPath); - static bool is_instance(Napi::Env env, const Napi::Object& object); - - static Internal* get_internal(Napi::Env env, const Napi::Object& object); - static void set_internal(Napi::Env env, Napi::Object& object, Internal* data); - - static Napi::Value constructor_callback(const Napi::CallbackInfo& info); - static bool has_native_method(const std::string& name); - -private: - static auto& get_class(); - static auto& get_nativeMethods(); - static auto& get_schemaObjectTypes(); - - // Gives access to ObjectWrap init_class private static member. See - // https://stackoverflow.com/a/40937193 - template - friend struct ObjectWrapAccessor; - - static Napi::Function init_class(Napi::Env env); - - static Napi::ClassPropertyDescriptor> - setup_method(Napi::Env env, const std::string& name, node::Types::FunctionCallback callback); - static Napi::ClassPropertyDescriptor> - setup_static_method(Napi::Env env, const std::string& name, node::Types::FunctionCallback callback); - static Napi::ClassPropertyDescriptor> - setup_property(Napi::Env env, const std::string& name, const PropertyType&); - static Napi::ClassPropertyDescriptor> - setup_static_property(Napi::Env env, const std::string& name, const PropertyType&); - - static Napi::Value property_getter(const Napi::CallbackInfo& info); - static void property_setter(const Napi::CallbackInfo& info); - static std::vector - create_napi_property_descriptors(Napi::Env env, const Napi::Object& constructorPrototype, - const realm::ObjectSchema& schema, bool redefine = true); -}; - -template <> -class ObjectWrap { -public: - using Internal = void; - - static Napi::Function create_constructor(Napi::Env env) - { - return Napi::Function(); - } - - static Napi::Function init_class(Napi::Env env) - { - return Napi::Function(); - } - - static bool has_native_method(const std::string& name) - { - return false; - } -}; - -// Gives access to ObjectWrap init_class private static member. See https://stackoverflow.com/a/40937193 -template -struct ObjectWrapAccessor { - inline static Napi::Function init_class(Napi::Env env) - { - return ObjectWrap::init_class(env); - } -}; - -static inline std::vector get_arguments(const Napi::CallbackInfo& info) -{ - size_t count = info.Length(); - std::vector arguments; - arguments.reserve(count); - - for (u_int i = 0; i < count; i++) { - arguments.push_back(info[i]); - } - - return arguments; -} - -static inline std::vector napi_get_arguments(const Napi::CallbackInfo& info) -{ - size_t count = info.Length(); - std::vector arguments; - arguments.reserve(count); - - for (u_int i = 0; i < count; i++) { - arguments.push_back(info[i]); - } - - return arguments; -} - -template -inline auto& ObjectWrap::get_class() -{ - static ClassType s_class; - return s_class; -} - -template -inline auto& ObjectWrap::get_nativeMethods() -{ - static std::unordered_set s_nativeMethods; - return s_nativeMethods; -} - - -template -inline auto& ObjectWrap::get_schemaObjectTypes() -{ - static std::unordered_map*> s_schemaObjectTypes; - return s_schemaObjectTypes; -} - -// A cache for property names. The pair is property name and a node::String* to the same string representation. -// The cache is persisted throughout the process life time to preseve property names between constructor cache -// invalidations (on_destory_context is called) Since RealmObjectClass instances may be used after context is -// destroyed, their property names should be valid -static std::unordered_map propertyNamesCache; - -static node::String* get_cached_property_name(const std::string& name) -{ - if (propertyNamesCache.count(name)) { - node::String* cachedName = propertyNamesCache.at(name); - return cachedName; - } - - node::String* result = new node::String(name); - propertyNamesCache.emplace(name, result); - return result; -} - - -inline static void copy_object(Napi::Env env, const Napi::Value& source, const Napi::Error& target) -{ - Napi::HandleScope scope(env); - - if (source.IsEmpty() || source.IsNull() || source.IsUndefined()) { - return; - } - - Napi::Function objectFunc = env.Global().Get("Object").As(); - Napi::Function assignFunc = objectFunc.Get("assign").As(); - assignFunc.Call({target.Value(), source}); -} - -template -WrappedObject::WrappedObject(const Napi::CallbackInfo& info) - : Napi::ObjectWrap>(info) -{ - Napi::Env env = info.Env(); - - // skip the constructor_callback if create_instance is creating a JS instance only - if (info.Length() == 1 && info[0].IsExternal()) { - Napi::External external = info[0].As>(); - if (!external.Data()) { - return; - } - - Internal* internal = external.Data(); - set_internal(internal); - return; - } - - try { - node::Types::FunctionCallback constructor_callback = (node::Types::FunctionCallback)info.Data(); - constructor_callback(info); - } - catch (const node::Exception& e) { - Napi::Error error = Napi::Error::New(info.Env(), e.what()); - copy_object(env, e.m_value, error); - throw error; - } - catch (const std::exception& e) { - throw Napi::Error::New(env, e.what()); - } -} - -template -Napi::Function -WrappedObject::init(Napi::Env env, const std::string& name, - node::Types::FunctionCallback constructor_callback, - std::function has_native_method_callback, - const std::vector>>& properties, - const IndexPropertyType* indexPropertyHandlers) -{ - - m_name = name; - m_has_native_methodFunc = has_native_method_callback; - WrappedObject::m_nullExternal = Napi::Persistent(Napi::External::New(env, nullptr)); - WrappedObject::m_nullExternal.SuppressDestruct(); - - Napi::Function ctor = Napi::ObjectWrap>::DefineClass(env, name.c_str(), properties, - (void*)constructor_callback); - - constructor = Napi::Persistent(ctor); - constructor.SuppressDestruct(); - - m_indexPropertyHandlers = const_cast(indexPropertyHandlers); - return ctor; -} - -// This creates the required JS instance with a Proxy parent to support get and set handlers and returns a proxy -// created on the JS instance to support property enumeration handler the returned JS Proxy has only ownKeys trap -// setup so that all other member accesses skip the Proxy and go directly to the JS instance -template -Napi::Value WrappedObject::create_instance_with_proxy(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - - if (constructor.IsEmpty()) { - std::string typeName(typeid(ClassType).name()); - std::string errorMessage = - util::format("create_instance_with_proxy: Class %s not initialized. Call init() first", typeName); - throw Napi::Error::New(env, errorMessage); - } - - if (!info.IsConstructCall()) { - throw Napi::Error::New(env, "This function should be called as a constructor"); - } - - Napi::EscapableHandleScope scope(env); - - try { - - auto arguments = napi_get_arguments(info); - Napi::Object instance = constructor.New(arguments); - - // using DefineProperty to make it non enumerable and non configurable and non writable - instance.DefineProperty(Napi::PropertyDescriptor::Value("_instance", instance, napi_default)); - - info.This().As().DefineProperty( - Napi::PropertyDescriptor::Value("isRealmCtor", Napi::Boolean::New(env, true), napi_configurable)); - - ObjectSetPrototypeOf.Call({info.This(), instance}); - Napi::Object instanceProxy = GlobalProxy.New({info.This(), ProxyHandler::get_instance_proxy_handler(env)}); - - instance.DefineProperty(Napi::PropertyDescriptor::Value("_instanceProxy", instanceProxy, napi_default)); - return scope.Escape(instanceProxy); - } - catch (const Napi::Error& e) { - throw e; - } - catch (const std::exception& e) { - throw Napi::Error::New(info.Env(), e.what()); - } -} - -template -Napi::Object WrappedObject::create_instance(Napi::Env env, Internal* internal) -{ - if (constructor.IsEmpty() || factory_constructor.IsEmpty()) { - std::string typeName(typeid(ClassType).name()); - std::string errorMessage = - util::format("create_instance: Class %s not initialized. Call init() first", typeName); - throw Napi::Error::New(env, errorMessage); - } - Napi::EscapableHandleScope scope(env); - - // creating a JS instance only. pass null as first and single argument - // Internal intern = &std::shared_ptr(internal); - Napi::External external; - if (internal) { - external = Napi::External::New(env, internal); - } - else { - external = WrappedObject::m_nullExternal.Value(); - } - - Napi::Object instance = factory_constructor.New({external}); - return scope.Escape(instance).As(); -} - -template -WrappedObject* WrappedObject::try_unwrap(const Napi::Object& object) -{ - Napi::Env env = object.Env(); - - WrappedObject* unwrapped; - napi_status status = napi_unwrap(env, object, reinterpret_cast(&unwrapped)); - if ((status) != napi_ok) { - Napi::Object instance = object.Get("_instance").As(); - if (instance.IsUndefined() || instance.IsNull()) { - throw Napi::Error::New(env, "Invalid object. No _instance member"); - } - - status = napi_unwrap(env, instance, reinterpret_cast(&unwrapped)); - } - - NAPI_THROW_IF_FAILED(env, status, nullptr); - return unwrapped; -} - -template -inline typename ClassType::Internal* WrappedObject::get_internal() -{ - return m_internal.get(); -} - -template -inline void WrappedObject::set_internal(Internal* internal) -{ - m_internal = std::unique_ptr(internal); -} - -template -void WrappedObject::set_factory_constructor(const Napi::Function& factoryConstructor) -{ - factory_constructor = Napi::Persistent(factoryConstructor); - factory_constructor.SuppressDestruct(); -} - -template -Napi::Function WrappedObject::get_constructor(Napi::Env env) -{ - if (!constructor.IsEmpty()) { - return constructor.Value(); - } - - return Napi::Function(env, env.Null()); -} - -template -inline bool WrappedObject::is_instance(Napi::Env env, const Napi::Object& object) -{ - if (constructor.IsEmpty()) { - std::string typeName(typeid(ClassType).name()); - std::string errorMessage = util::format("is_instance: Class %s not initialized. Call init() first", typeName); - throw Napi::Error::New(env, errorMessage); - } - - Napi::HandleScope scope(env); - - // Check the object is instance of the constructor. This will be true when the object have it's prototype set with - // setPrototypeOf. This is true for objects configured in the schema with a function type. - Napi::Function ctor = constructor.Value(); - bool isInstanceOf = object.InstanceOf(ctor); - if (isInstanceOf) { - return true; - } - - // Object store needs is_instance to return true when called with RealmObject instance even if the prototype was - // changed with setPrototypeOf - Napi::Object instance = object.Get("_instance").As(); - if (!instance.IsUndefined()) { - isInstanceOf = instance.InstanceOf(ctor); - } - - // handle RealmObjects with user defined ctors without extending RealmObject - // In this case we just check for existing internal value to identify RealmObject instances - if (!isInstanceOf) { - bool isRealmObjectClass = std::is_same>::value; - if (isRealmObjectClass) { - Napi::External external = - object.Get(ExternalSymbol).As>(); - if (!external.IsUndefined()) { - return true; - } - } - } - - return isInstanceOf; -} - - -static inline Napi::Value method_callback(const Napi::CallbackInfo& info) -{ - node::Types::FunctionCallback method = (node::Types::FunctionCallback)info.Data(); - return method(info); -} - -template -Napi::Value WrappedObject::method_callback(const Napi::CallbackInfo& info) -{ - node::Types::FunctionCallback method = (node::Types::FunctionCallback)info.Data(); - return method(info); -} - -template -Napi::Value WrappedObject::getter_callback(const Napi::CallbackInfo& info) -{ - PropertyType* propertyType = (PropertyType*)info.Data(); - return propertyType->getter(info); -} - -template -void WrappedObject::setter_callback(const Napi::CallbackInfo& info, const Napi::Value& value) -{ - PropertyType* propertyType = (PropertyType*)info.Data(); - propertyType->setter(info, value); -} - -template -void WrappedObject::readonly_setter_callback(const Napi::CallbackInfo& info, const Napi::Value& value) -{ - Napi::Error error = Napi::Error::New(info.Env(), "Cannot assign to read only property"); - error.Set("readOnly", true); - throw error; -} - -template -void WrappedObject::readonly_static_setter_callback(const Napi::CallbackInfo& info, - const Napi::Value& value) -{ - Napi::Error error = Napi::Error::New(info.Env(), "Cannot assign to read only static property"); - error.Set("readOnly", true); - throw error; -} - -template -Napi::ObjectReference WrappedObject::ProxyHandler::m_proxyHandler; - -template -Napi::Value WrappedObject::ProxyHandler::get_instance_proxy_handler(Napi::Env env) -{ - if (!m_proxyHandler.IsEmpty()) { - return m_proxyHandler.Value(); - } - - Napi::Object proxyObject = Napi::Object::New(env); - Napi::PropertyDescriptor instanceGetTrapFunc = - Napi::PropertyDescriptor::Function("get", &WrappedObject::ProxyHandler::get_proxy_trap); - Napi::PropertyDescriptor instanceSetTrapFunc = - Napi::PropertyDescriptor::Function("set", &WrappedObject::ProxyHandler::set_proxy_trap); - Napi::PropertyDescriptor ownKeysTrapFunc = - Napi::PropertyDescriptor::Function("ownKeys", &WrappedObject::ProxyHandler::own_keys_proxy_trap); - Napi::PropertyDescriptor hasTrapFunc = - Napi::PropertyDescriptor::Function("has", &WrappedObject::ProxyHandler::has_proxy_trap); - Napi::PropertyDescriptor getOwnPropertyDescriptorTrapFunc = Napi::PropertyDescriptor::Function( - "getOwnPropertyDescriptor", &WrappedObject::ProxyHandler::get_own_property_descriptor_trap); - Napi::PropertyDescriptor getPrototypeOfFunc = Napi::PropertyDescriptor::Function( - "getPrototypeOf", &WrappedObject::ProxyHandler::get_prototype_of_proxy_trap); - Napi::PropertyDescriptor setPrototypeOfFunc = Napi::PropertyDescriptor::Function( - "setPrototypeOf", &WrappedObject::ProxyHandler::set_prototype_of_proxy_trap); - - proxyObject.DefineProperties({instanceGetTrapFunc, instanceSetTrapFunc, ownKeysTrapFunc, hasTrapFunc, - getOwnPropertyDescriptorTrapFunc, getPrototypeOfFunc, setPrototypeOfFunc}); - - m_proxyHandler = Napi::Persistent(proxyObject); - m_proxyHandler.SuppressDestruct(); - return proxyObject; -} - -static Napi::Value bind_function(Napi::Env env, const std::string& functionName, const Napi::Function& function, - const Napi::Object& thisObject) -{ - Napi::Function boundFunc = FunctionBind.Call(function, {thisObject}).As(); - return boundFunc; -} - - -template -Napi::Value WrappedObject::ProxyHandler::bind_native_function(Napi::Env env, - const std::string& functionName, - const Napi::Function& function, - const Napi::Object& thisObject) -{ - Napi::EscapableHandleScope scope(env); - - // do not bind the non native functions. These are attached from extensions.js and should be called on the - // instanceProxy. - if (!m_has_native_methodFunc(functionName)) { - // return undefined to indicate this is not a native function - return scope.Escape(env.Undefined()); - } - - Napi::Value result = bind_function(env, functionName, function, thisObject); - return scope.Escape(result); -} - -static inline Napi::Object get_prototype(Napi::Env env, const Napi::Object& object) -{ - napi_value napi_proto; - napi_status status = napi_get_prototype(env, object, &napi_proto); - if (status != napi_ok) { - throw Napi::Error::New(env, "Invalid object. Couldn't get prototype of object"); - } - Napi::Object prototypeObject = Napi::Object(env, napi_proto); - return prototypeObject; -} - -template -Napi::Value WrappedObject::ProxyHandler::get_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object target = info[0].As(); - Napi::Value property = info[1]; - -#if DEBUG - std::string _debugproperty = property.IsString() ? (std::string)property.As() : ""; - const char* _debugPropertyName = _debugproperty.c_str(); - (void)_debugPropertyName; // disable unused variable warning -#endif - - Napi::Object instance = target.Get("_instance").As(); - if (instance.IsUndefined() || instance.IsNull()) { - throw Napi::Error::New(env, "Invalid object. No _instance member"); - } - - // skip Symbols - if (!property.IsString()) { - Napi::Value propertyValue = instance.Get(property); - return scope.Escape(propertyValue); - } - - Napi::String propertyName = property.As(); - std::string propertyText = propertyName; - - if (propertyText == "_instance") { - return scope.Escape(instance); - } - - // Order of execution - // 1.check for number and call get index handlers - // 2.check if its a native function - // 3.get any other property name from the instance - - - // 1.Check property is number and call index handler - char firstChar = *propertyText.c_str(); - - // myobject[""] and negative indexes return undefined in JavaScript - if (propertyText.length() == 0 || firstChar == '-') { - return scope.Escape(env.Undefined()); - } - - bool isNumber = isdigit(firstChar) || firstChar == '+'; - if (isNumber) { - int32_t index = 0; - try { - index = std::stoi(propertyText); - } - catch (const std::exception& e) { - throw Napi::Error::New(env, "Invalid number " + propertyText); - } - - WrappedObject* wrappedObject = WrappedObject::Unwrap(instance); - Napi::Value result = wrappedObject->m_indexPropertyHandlers->getter(info, instance, index); - return scope.Escape(result); - } - - - // 2. Check if its a native function - if (m_has_native_methodFunc(propertyText)) { - // TODO: cache this function in the wrappedObject of this instance - Napi::Value propertyValue = instance.Get(property); - Napi::Value result = bind_function(env, propertyText, propertyValue.As(), instance); - return scope.Escape(result); - } - - // return all other properties from the instance - Napi::Value propertyValue = instance.Get(property); - return scope.Escape(propertyValue); -} - -template -Napi::Value WrappedObject::ProxyHandler::set_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object target = info[0].As(); - Napi::Value property = info[1]; - Napi::Value value = info[2]; - -#if DEBUG - std::string _debugproperty = property.IsString() ? (std::string)property.As() : ""; - const char* _debugPropertyName = _debugproperty.c_str(); - (void)_debugPropertyName; // disable unused variable warning -#endif - - - Napi::Object instance = target.Get("_instance").As(); - if (instance.IsUndefined() || instance.IsNull()) { - throw Napi::Error::New(env, "Invalid object. No _instance member"); - } - - // skip Symbols - if (!property.IsString()) { - instance.Set(property, value); - return scope.Escape(value); - } - - Napi::String propertyName = property.As(); - std::string propertyText = propertyName; - - // Order of execution - // 1.check for number and call set index handlers - // 2.get any other property name from the instance - - // do not assign empty property name. myarray[''] = 42; is valid in JS - if (propertyText.length() == 0) { - throw Napi::Error::New(env, "Invalid number ''"); - } - - // 1.Check property is number and call set index handler - char firstChar = *propertyText.c_str(); - bool isNumber = isdigit(firstChar) || firstChar == '+' || firstChar == '-'; - if (isNumber) { - try { - realm::js::validated_positive_index(propertyText); - } - catch (const std::out_of_range& e) { - throw Napi::Error::New(env, e.what()); - } - - int32_t index = 0; - try { - index = std::stoi(propertyText); - } - catch (const std::exception& e) { - throw Napi::Error::New(env, "Invalid number " + propertyText); - } - - WrappedObject* wrappedObject = WrappedObject::Unwrap(instance); - if (wrappedObject->m_indexPropertyHandlers->setter == nullptr) { - std::string message = std::string("Cannot assign to read only index ") + util::to_string(index); - throw Napi::Error::New(env, message); - } - - Napi::Value result = wrappedObject->m_indexPropertyHandlers->setter(info, instance, index, value); - return scope.Escape(result); - } - - // call Set on the instance for non indexed properties - try { - instance.Set(property, value); - return scope.Escape(Napi::Boolean::New(env, true)); - } - catch (const Napi::Error& e) { - Napi::Boolean readOnly = e.Get("readOnly").As(); - if (!readOnly.IsUndefined() && readOnly) { - std::string message = "Cannot assign to read only property '" + propertyText + "'"; - throw Napi::Error::New(env, message); - } - - throw e; - } -} - -template -Napi::Value WrappedObject::ProxyHandler::own_keys_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object target = info[0].As(); - - Napi::Object instance = target.Get("_instance").As(); - WrappedObject* wrappedObject = WrappedObject::Unwrap(instance); - - if (wrappedObject->m_indexPropertyHandlers != nullptr) { - uint32_t length = instance.Get("length").As(); - Napi::Array array = Napi::Array::New(env, length); - for (uint32_t i = 0; i < length; i++) { - array.Set(i, Napi::Number::New(env, i).ToString()); - } - return scope.Escape(array); - } - - return scope.Escape(Napi::Array::New(env, 0)); -} - -template -Napi::Value WrappedObject::ProxyHandler::has_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object target = info[0].As(); - Napi::Value propertyArg = info[1]; - - - // skip symbols - if (!propertyArg.IsString()) { - bool hasProperty = target.Has(propertyArg); - return scope.Escape(Napi::Boolean::From(env, hasProperty)); - } - - Napi::String property = propertyArg.As(); - std::string propertyText = property; - - if (propertyText.length() == 0) { - return scope.Escape(Napi::Boolean::From(env, false)); - } - - Napi::Object instance = target.Get("_instance").As(); - if (instance.IsUndefined() || instance.IsNull()) { - bool hasProperty = target.Has(propertyArg); - return scope.Escape(Napi::Boolean::From(env, hasProperty)); - } - - if (target.Has(property)) { - return scope.Escape(Napi::Boolean::From(env, true)); - } - - // Property should be a number from here on - char firstChar = *propertyText.c_str(); - bool isNumber = isdigit(firstChar) || firstChar == '+'; - - // return false for negative indexes and non numbers - if (!isNumber || firstChar == '-') { - return scope.Escape(Napi::Boolean::From(env, false)); - } - - int32_t index = 0; - try { - index = std::stoi(propertyText); - } - catch (const std::exception& e) { - // not a number. return false; - return scope.Escape(Napi::Boolean::From(env, false)); - } - - int32_t length = instance.Get("length").As(); - bool hasIndex = index >= 0 && index < length; - return scope.Escape(Napi::Boolean::From(env, hasIndex)); -} - -template -Napi::Value WrappedObject::ProxyHandler::get_own_property_descriptor_trap(const Napi::CallbackInfo& info) -{ - // This exists only for ownKeysTrap to work properly with Object.keys(). It does not check if the property is from - // the named handler or it is an existing property on the instance. This implementation can be extended to return - // the true property descriptor if the property is an existing one - - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object descriptor = Napi::Object::New(env); - descriptor.Set("enumerable", Napi::Boolean::New(env, true)); - descriptor.Set("configurable", Napi::Boolean::New(env, true)); - - return scope.Escape(descriptor); -} - -template -Napi::Value WrappedObject::ProxyHandler::get_prototype_of_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - - Napi::Object target = info[0].As(); - Napi::Object proto = get_prototype(env, target); - return scope.Escape(proto); -} - -template -Napi::Value WrappedObject::ProxyHandler::set_prototype_of_proxy_trap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - throw Napi::Error::New(env, "Setting the prototype on this type of object is not supported"); -} - -static Napi::Value property_getter_callback(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - try { - PropertyType* propertyType = (PropertyType*)info.Data(); - Napi::Value result = propertyType->getter(info); - return result; - } - catch (const Napi::Error& e) { - e.ThrowAsJavaScriptException(); - return env.Undefined(); - } -} - -template -Napi::Value ObjectWrap::property_getter(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - auto& s_class = get_class(); - try { - auto propertyName = (node::String*)info.Data(); - return s_class.string_accessor.getter(info, info.This().As(), propertyName->ToString(env)); - } - catch (const Napi::Error& e) { - e.ThrowAsJavaScriptException(); - return env.Undefined(); - } -} - -template -void ObjectWrap::property_setter(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - auto& s_class = get_class(); - try { - auto propertyName = (node::String*)info.Data(); - auto value = info[0]; - s_class.string_accessor.setter(info, info.This().As(), propertyName->ToString(env), value); - } - catch (const Napi::Error& e) { - e.ThrowAsJavaScriptException(); - } -} - -template -Napi::Function ObjectWrap::create_constructor(Napi::Env env) -{ - auto& s_class = get_class(); - Napi::Function ctor = init_class(env); - - // If the class has no index accessor we can create an instance of the class itself and can skip proxy objects - bool has_index_accessor = (s_class.index_accessor.getter || s_class.index_accessor.setter); - bool isRealmObjectClass = std::is_same>::value; - - if (!has_index_accessor || isRealmObjectClass) { - WrappedObject::set_factory_constructor(ctor); - return ctor; - } - - // since NAPI ctors can't change the returned type we need to return a factory func which will be called when 'new - // ctor()' is called from JS. This will create a JS Proxy and return it to the caller. The proxy is needed to - // support named and index handlers - Napi::Function factory = - Napi::Function::New(env, WrappedObject::create_instance_with_proxy, s_class.name); - auto ctorPrototypeProperty = ctor.Get("prototype"); - - // the factory function should have the same prototype property as the constructor.prototype so `instanceOf` works - factory.Set("prototype", ctorPrototypeProperty); - ObjectSetPrototypeOf.Call({factory, ctor}); - - WrappedObject::set_factory_constructor(factory); - - return factory; -} - -template -Napi::Function ObjectWrap::init_class(Napi::Env env) -{ - auto& s_class = get_class(); - // check if the constructor is already created. It means this class and it's parent are already initialized. - Napi::Function ctor = WrappedObject::get_constructor(env); - if (!ctor.IsNull()) { - return ctor; - } - - bool isRealmObjectClass = std::is_same>::value; - - std::vector>> properties; - - if (!isRealmObjectClass) { - // setup properties and accessors on the class - for (auto& pair : s_class.static_properties) { - Napi::ClassPropertyDescriptor> statc_property = - setup_static_property(env, pair.first, pair.second); - properties.push_back(statc_property); - } - - for (auto& pair : s_class.static_methods) { - Napi::ClassPropertyDescriptor> staticMethod = - setup_static_method(env, pair.first, pair.second); - properties.push_back(staticMethod); - } - - for (auto& pair : s_class.methods) { - Napi::ClassPropertyDescriptor> method = - setup_method(env, pair.first, pair.second); - properties.push_back(method); - } - - for (auto& pair : s_class.properties) { - Napi::ClassPropertyDescriptor> property = - setup_property(env, pair.first, pair.second); - properties.push_back(property); - } - } - - bool has_index_accessor = (s_class.index_accessor.getter || s_class.index_accessor.setter); - const IndexPropertyType* index_accessor = has_index_accessor ? &s_class.index_accessor : nullptr; - - ctor = WrappedObject::init(env, s_class.name, &ObjectWrap::constructor_callback, - &ObjectWrap::has_native_method, properties, index_accessor); - - auto ctorPrototypeProperty = ctor.Get("prototype"); - if (ctorPrototypeProperty.IsUndefined()) { - throw std::runtime_error("undefined 'prototype' on constructor"); - } - - Napi::Function parentCtor = ObjectWrapAccessor::init_class(env); - if (!parentCtor.IsEmpty()) { - - auto parentCtorPrototypeProperty = parentCtor.Get("prototype"); - if (parentCtorPrototypeProperty.IsUndefined()) { - throw std::runtime_error("undefined 'prototype' on parent constructor"); - } - - ObjectSetPrototypeOf.Call({ctorPrototypeProperty, parentCtorPrototypeProperty}); - ObjectSetPrototypeOf.Call({ctor, parentCtor}); - } - - // use PropertyDescriptors instead of ClassPropertyDescriptors, since ClassPropertyDescriptors requires the JS - // instance members callbacks to be instance members of the WrappedObject class - if (isRealmObjectClass) { - std::vector properties; - auto ctorPrototype = ctor.Get("prototype").As(); - for (auto& pair : s_class.methods) { - auto descriptor = Napi::PropertyDescriptor::Function( - env, ctorPrototype, Napi::String::New(env, pair.first) /*name*/, &method_callback, - napi_default | realm::js::PropertyAttributes::DontEnum, (void*)pair.second /*callback*/); - properties.push_back(descriptor); - } - - for (auto& pair : s_class.properties) { - napi_property_attributes napi_attributes = - napi_default | (realm::js::PropertyAttributes::DontEnum | realm::js::PropertyAttributes::DontDelete); - auto descriptor = Napi::PropertyDescriptor::Accessor( - Napi::String::New(env, pair.first) /*name*/, napi_attributes, (void*)&pair.second /*callback*/); - properties.push_back(descriptor); - } - - ctorPrototype.DefineProperties(properties); - } - - bool isRealmClass = std::is_same>::value; - if (isRealmClass) { - RealmClassConstructor = Napi::Persistent(ctor); - RealmClassConstructor.SuppressDestruct(); - } - - - return ctor; -} - -template -Napi::Object ObjectWrap::create_instance(Napi::Env env, Internal* internal) -{ - Napi::EscapableHandleScope scope(env); - - - bool isRealmObjectClass = std::is_same>::value; - - if (isRealmObjectClass && !internal) { - throw Napi::Error::New(env, "RealmObjectClass requires an internal realm object when creating instances"); - } - - Napi::Object factory = WrappedObject::create_instance(env, internal); - return scope.Escape(factory).As(); -} - -inline static void schema_object_type_constructor(const Napi::CallbackInfo& info) {} - -template -void ObjectWrap::internal_finalizer(Napi::Env, typename ClassType::Internal* internal) -{ - if (internal) { - delete internal; - internal = nullptr; - } -} - -static inline void remove_schema_object(std::unordered_map* schemaObjects, - const std::string& schemaName) -{ - bool schemaExists = schemaObjects->count(schemaName); - if (!schemaExists) { - return; - } - - SchemaObjectType* schemaObjectType = schemaObjects->at(schemaName); - schemaObjects->erase(schemaName); - schemaObjectType->constructor.Reset(); - delete schemaObjectType; -} - -template -inline std::vector -ObjectWrap::create_napi_property_descriptors(Napi::Env env, const Napi::Object& constructorPrototype, - const realm::ObjectSchema& schema, bool redefine) -{ - std::vector properties; - - for (auto& property : schema.persisted_properties) { - std::string propName = property.public_name.empty() ? property.name : property.public_name; - if (redefine || !constructorPrototype.HasOwnProperty(propName)) { - node::String* name = get_cached_property_name(propName); - auto descriptor = Napi::PropertyDescriptor::Accessor( - name->ToString(env), napi_enumerable, (void*)name); - properties.push_back(descriptor); - } - } - - for (auto& property : schema.computed_properties) { - std::string propName = property.public_name.empty() ? property.name : property.public_name; - if (redefine || !constructorPrototype.HasOwnProperty(propName)) { - node::String* name = get_cached_property_name(propName); - auto descriptor = Napi::PropertyDescriptor::Accessor( - name->ToString(env), napi_enumerable, (void*)name); - properties.push_back(descriptor); - } - } - - return properties; -} - -template -Napi::Object ObjectWrap::create_instance_by_schema(Napi::Env env, Napi::Function& constructor, - const realm::ObjectSchema& schema, Internal* internal) -{ - Napi::EscapableHandleScope scope(env); - auto& s_schemaObjectTypes = get_schemaObjectTypes(); - auto& s_class = get_class(); - - bool isRealmObjectClass = std::is_same>::value; - if (!isRealmObjectClass) { - throw Napi::Error::New(env, "Creating instances by schema is supported for RealmObjectClass only"); - } - - if (isRealmObjectClass && !internal) { - throw Napi::Error::New( - env, "RealmObjectClass requires an internal realm object when creating instances by schema"); - } - - Napi::Object instance; - auto config = internal->realm()->config(); - std::string path = config.path; - auto version = internal->realm()->schema_version(); - std::string schemaName = schema.name + ":" + std::to_string(version); - - std::unordered_map* schemaObjects = nullptr; - if (!s_schemaObjectTypes.count(path)) { - // std::map> - schemaObjects = new std::unordered_map(); - s_schemaObjectTypes.emplace(path, schemaObjects); - } - else { - schemaObjects = s_schemaObjectTypes.at(path); - } - - Napi::Function schemaObjectConstructor; - Napi::Symbol externalSymbol = ExternalSymbol; - // if we are creating a RealmObject from schema with no user defined constructor - if (constructor.IsEmpty()) { - // 1.Check by name if the constructor is already created for this RealmObject - if (!schemaObjects->count(schemaName)) { - - // 2.Create the constructor - - // get or create the RealmObjectClass constructor - - // create the RealmObject function by name - schemaObjectConstructor = Napi::Function::New(env, schema_object_type_constructor, schema.name); - - Napi::Function realmObjectClassConstructor = ObjectWrap::create_constructor(env); - auto parentCtorPrototypeProperty = realmObjectClassConstructor.Get("prototype"); - auto childPrototypeProperty = schemaObjectConstructor.Get("prototype").As(); - ObjectSetPrototypeOf.Call({childPrototypeProperty, parentCtorPrototypeProperty}); - ObjectSetPrototypeOf.Call({schemaObjectConstructor, realmObjectClassConstructor}); - - // get all properties from the schema - std::vector properties = - create_napi_property_descriptors(env, childPrototypeProperty, schema, true /*redefine*/); - - // define the properties on the prototype of the schema object constructor - childPrototypeProperty.DefineProperties(properties); - - SchemaObjectType* schemaObjectType = new SchemaObjectType(); - schemaObjects->emplace(schemaName, schemaObjectType); - schemaObjectType->constructor = Napi::Persistent(schemaObjectConstructor); - schemaObjectType->constructor.SuppressDestruct(); - } - else { - // hot path. The constructor for this schema object is already cached. use it and return a new instance - SchemaObjectType* schemaObjectType = schemaObjects->at(schemaName); - schemaObjectConstructor = schemaObjectType->constructor.Value(); - } - - Napi::External externalValue = Napi::External::New(env, internal, internal_finalizer); - Napi::Object constructorPrototype = schemaObjectConstructor.Get("prototype").As(); - instance = ObjectCreate.Call({constructorPrototype}).As(); - instance.Set(externalSymbol, externalValue); - } - else { - Napi::Object constructorPrototype = constructor.Get("prototype").As(); - // creating a RealmObject with user defined constructor - - bool schemaExists = schemaObjects->count(schemaName); - SchemaObjectType* schemaObjectType; - if (schemaExists) { - schemaObjectType = schemaObjects->at(schemaName); - schemaObjectConstructor = schemaObjectType->constructor.Value(); - - // check if constructors have changed for the same schema object and name - if (!schemaObjectConstructor.StrictEquals(constructor)) { - schemaExists = false; - remove_schema_object(schemaObjects, schemaName); - } - } - - // hot path. The constructor for this schema object is already cached. use it and return a new instance - if (schemaExists) { - schemaObjectType = schemaObjects->at(schemaName); - schemaObjectConstructor = schemaObjectType->constructor.Value(); - instance = ObjectCreate.Call({constructorPrototype}).As(); - - Napi::External externalValue = Napi::External::New(env, internal, internal_finalizer); - instance.Set(externalSymbol, externalValue); - - return scope.Escape(instance).As(); - } - - schemaObjectConstructor = constructor; - - // get all properties from the schema - std::vector properties = - create_napi_property_descriptors(env, constructorPrototype, schema, false /*redefine*/); - - // define the properties on the prototype of the schema object constructor - if (properties.size() > 0) { - constructorPrototype.DefineProperties(properties); - } - - instance = ObjectCreate.Call({constructorPrototype}).As(); - - - Napi::External externalValue = Napi::External::New(env, internal, internal_finalizer); - instance.Set(externalSymbol, externalValue); - - schemaObjectType = new SchemaObjectType(); - schemaObjects->emplace(schemaName, schemaObjectType); - schemaObjectType->constructor = Napi::Persistent(schemaObjectConstructor); - schemaObjectType->constructor.SuppressDestruct(); - } - - return scope.Escape(instance).As(); -} - -template -inline void ObjectWrap::on_context_destroy(Napi::Env env, std::string realmPath) -{ - std::unordered_map* schemaObjects = nullptr; - auto& s_schemaObjectTypes = get_schemaObjectTypes(); - if (!s_schemaObjectTypes.count(realmPath)) { - return; - } - - schemaObjects = s_schemaObjectTypes.at(realmPath); - for (auto it = schemaObjects->begin(); it != schemaObjects->end(); ++it) { - it->second->constructor.Reset(); - SchemaObjectType* schemaObjecttype = it->second; - delete schemaObjecttype; - } - s_schemaObjectTypes.erase(realmPath); - - delete schemaObjects; -} - -template -inline bool ObjectWrap::is_instance(Napi::Env env, const Napi::Object& object) -{ - return WrappedObject::is_instance(env, object); -} - -template -typename ClassType::Internal* ObjectWrap::get_internal(Napi::Env env, const Napi::Object& object) -{ - bool isRealmObjectClass = std::is_same>::value; - if (isRealmObjectClass) { - Napi::External external = - object.Get(ExternalSymbol).As>(); - if (external.IsUndefined()) { - return nullptr; - } - - return external.Data(); - } - - WrappedObject* wrappedObject = WrappedObject::try_unwrap(object); - return wrappedObject->get_internal(); -} - -template -void ObjectWrap::set_internal(Napi::Env env, Napi::Object& object, typename ClassType::Internal* internal) -{ - bool isRealmObjectClass = std::is_same>::value; - if (isRealmObjectClass) { - Napi::Object& obj = const_cast(object); - obj.Set(ExternalSymbol, Napi::External::New(env, internal)); - return; - } - - WrappedObject* wrappedObject = WrappedObject::try_unwrap(object); - wrappedObject->set_internal(internal); -} - -template -Napi::Value ObjectWrap::constructor_callback(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - Napi::EscapableHandleScope scope(env); - auto& s_class = get_class(); - - if (reinterpret_cast(s_class.constructor) != nullptr) { - auto arguments = get_arguments(info); - node::Arguments args{env, arguments.size(), arguments.data()}; - s_class.constructor(env, info.This().As(), args); - return scope.Escape(env.Null()); // return a value to comply with Napi::FunctionCallback - } - else { - throw Napi::Error::New(env, "Illegal constructor"); - } -} - -template -bool ObjectWrap::has_native_method(const std::string& name) -{ - auto& s_nativeMethods = get_nativeMethods(); - if (s_nativeMethods.find(name) != s_nativeMethods.end()) { - return true; - } - - return ObjectWrap::has_native_method(name); -} - -template -Napi::ClassPropertyDescriptor> -ObjectWrap::setup_method(Napi::Env env, const std::string& name, node::Types::FunctionCallback callback) -{ - auto& s_nativeMethods = get_nativeMethods(); - auto methodCallback = - (typename WrappedObject::InstanceMethodCallback)(&WrappedObject::method_callback); - s_nativeMethods.insert(name); - return WrappedObject::InstanceMethod( - name.c_str(), methodCallback, napi_default | realm::js::PropertyAttributes::DontEnum, (void*)callback); -} - -template -Napi::ClassPropertyDescriptor> -ObjectWrap::setup_static_method(Napi::Env env, const std::string& name, - node::Types::FunctionCallback callback) -{ - return Napi::ObjectWrap>::StaticMethod( - name.c_str(), callback, napi_static | realm::js::PropertyAttributes::DontEnum); -} - -template -Napi::ClassPropertyDescriptor> -ObjectWrap::setup_property(Napi::Env env, const std::string& name, const PropertyType& property) -{ - napi_property_attributes napi_attributes = - napi_default | (realm::js::PropertyAttributes::DontEnum | realm::js::PropertyAttributes::DontDelete); - - auto getterCallback = - (typename WrappedObject::InstanceGetterCallback)(&WrappedObject::getter_callback); - auto setterCallback = (typename WrappedObject::InstanceSetterCallback)( - &WrappedObject::readonly_setter_callback); - if (property.setter) { - setterCallback = - (typename WrappedObject::InstanceSetterCallback)(&WrappedObject::setter_callback); - } - - return WrappedObject::InstanceAccessor(name.c_str(), getterCallback, setterCallback, napi_attributes, - (void*)&property); -} - -template -Napi::ClassPropertyDescriptor> -ObjectWrap::setup_static_property(Napi::Env env, const std::string& name, const PropertyType& property) -{ - napi_property_attributes napi_attributes = - napi_static | (realm::js::PropertyAttributes::DontEnum | realm::js::PropertyAttributes::DontDelete); - - auto getterCallback = (typename WrappedObject::StaticGetterCallback)(property.getter); - typename WrappedObject::StaticSetterCallback setterCallback = - &WrappedObject::readonly_static_setter_callback; - if (property.setter) { - setterCallback = (typename WrappedObject::StaticSetterCallback)(property.setter); - } - - return WrappedObject::StaticAccessor(name.c_str(), getterCallback, setterCallback, napi_attributes, - nullptr); -} - -} // namespace node - -namespace js { - -template -class ObjectWrap : public node::ObjectWrap {}; - - -#define HANDLE_WRAP_EXCEPTION \ - catch (const Napi::Error& e) \ - { \ - throw; \ - } \ - catch (const node::Exception& e) \ - { \ - Napi::Error error = Napi::Error::New(info.Env(), e.what()); \ - copy_object(env, e.m_value, error); \ - throw error; \ - } \ - catch (const std::exception& e) \ - { \ - throw Napi::Error::New(info.Env(), e.what()); \ - } - - -template -Napi::Value wrap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - auto arguments = node::get_arguments(info); - node::Arguments args{info.Env(), arguments.size(), arguments.data()}; - node::ReturnValue result(env); - - Napi::Object instanceProxy = info.This().As().Get("_instanceProxy").As(); - if (instanceProxy.IsUndefined()) { - instanceProxy = info.This().As(); - } - - try { - F(env, instanceProxy, args, result); - return result.ToValue(); - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info) -{ - Napi::Env env = info.Env(); - node::ReturnValue result(env); - try { - F(env, info.This().As(), result); - return result.ToValue(); - } - HANDLE_WRAP_EXCEPTION -} - -template -void wrap(const Napi::CallbackInfo& info, const Napi::Value& value) -{ - Napi::Env env = info.Env(); - - try { - F(env, info.This().As(), value); - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info, const Napi::Object& instance, uint32_t index) -{ - Napi::Env env = info.Env(); - node::ReturnValue result(env); - - try { - try { - F(env, instance, index, result); - return result.ToValue(); - } - catch (const std::out_of_range& e) { - // Out-of-bounds index getters should just return undefined in JS. - result.set_undefined(); - return result.ToValue(); - } - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info, const Napi::Object& instance, uint32_t index, - const Napi::Value& value) -{ - Napi::Env env = info.Env(); - - try { - bool success = F(env, instance, index, value); - - // Indicate that the property was intercepted. - return Napi::Value::From(env, success); - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info, const Napi::Object& instance, const Napi::String& property) -{ - Napi::Env env = info.Env(); - node::ReturnValue result(env); - - try { - F(env, instance, property, result); - return result.ToValue(); - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info, const Napi::Object& instance, const Napi::String& property, - const Napi::Value& value) -{ - Napi::Env env = info.Env(); - try { - bool success = F(env, instance, property, value); - return Napi::Value::From(env, success); - } - HANDLE_WRAP_EXCEPTION -} - -template -Napi::Value wrap(const Napi::CallbackInfo& info, const Napi::Object& instance) -{ - Napi::Env env = info.Env(); - - try { - auto names = F(env, instance); - - int count = (int)names.size(); - Napi::Array array = Napi::Array::New(env, count); - for (int i = 0; i < count; i++) { - array.Set(i, names[i]); - } - - return array; - } - HANDLE_WRAP_EXCEPTION -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_context.hpp b/packages/realm/bindgen/src/node/node_context.hpp deleted file mode 100644 index 2c1beb1523c..00000000000 --- a/packages/realm/bindgen/src/node/node_context.hpp +++ /dev/null @@ -1,33 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" -#include "napi.h" - -namespace realm { -namespace js { - -template <> -inline Napi::Env node::Context::get_global_context(Napi::Env env) -{ - return env; -} -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_dummy.cpp b/packages/realm/bindgen/src/node/node_dummy.cpp deleted file mode 100644 index c1e0503fa3e..00000000000 --- a/packages/realm/bindgen/src/node/node_dummy.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -// NOTE: This dummy file exists only to make Xcode build the Realm Node dynamic library. -#include "node.h" - -extern "C" void node_module_register(void* mod) {} - -namespace node { -namespace Buffer { -bool HasInstance(v8::Local val) -{ - return false; -} -char* Data(v8::Local val) -{ - return nullptr; -} -size_t Length(v8::Local val) -{ - return 0; -} -} // namespace Buffer -} // namespace node diff --git a/packages/realm/bindgen/src/node/node_exception.hpp b/packages/realm/bindgen/src/node/node_exception.hpp deleted file mode 100644 index 6c6a44c0663..00000000000 --- a/packages/realm/bindgen/src/node/node_exception.hpp +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" -#include "napi.h" - -namespace realm { -namespace js { - -template <> -inline Napi::Value node::Exception::value(Napi::Env env, const std::string& message) -{ - return Napi::String::New(env, message); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_function.hpp b/packages/realm/bindgen/src/node/node_function.hpp deleted file mode 100644 index d1f7e919cd5..00000000000 --- a/packages/realm/bindgen/src/node/node_function.hpp +++ /dev/null @@ -1,63 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" -#include "napi.h" - -namespace realm { -namespace js { - -template <> -inline Napi::Value node::Function::call(Napi::Env env, const Napi::Function& function, - const Napi::Object& this_object, size_t argc, const Napi::Value arguments[]) -{ - auto recv = this_object.IsEmpty() ? env.Global() : this_object; - - std::vector args(const_cast(arguments), - const_cast(arguments) + argc); - auto result = function.Call(recv, args); - return result; -} - -template <> -inline Napi::Value node::Function::callback(Napi::Env env, const Napi::Function& function, - const Napi::Object& this_object, size_t argc, - const Napi::Value arguments[]) -{ - auto recv = this_object.IsEmpty() ? env.Global() : this_object; - - std::vector args(const_cast(arguments), - const_cast(arguments) + argc); - auto result = function.MakeCallback(recv, args); - return result; -} - -template <> -inline Napi::Object node::Function::construct(Napi::Env env, const Napi::Function& function, size_t argc, - const Napi::Value arguments[]) -{ - std::vector args(const_cast(arguments), - const_cast(arguments) + argc); - auto result = function.New(args); - return result; -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_init.cpp b/packages/realm/bindgen/src/node/node_init.cpp deleted file mode 100644 index 8ba62e0e99d..00000000000 --- a/packages/realm/bindgen/src/node/node_init.cpp +++ /dev/null @@ -1,52 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - - -#include "node_init.hpp" -#include "napi.h" - -#if !REALM_ENABLE_SYNC -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "crypt32"); -#endif - -#include "js_realm.hpp" - -namespace realm { -namespace node { - -static void napi_init(Napi::Env env, Napi::Object exports) -{ - node_class_init(env); - - Napi::Function realm_constructor = js::RealmClass::create_constructor(env); - - std::string name = realm_constructor.Get("name").As(); - exports.Set(Napi::String::New(env, name), realm_constructor); -} - -} // namespace node -} // namespace realm - -static Napi::Object NAPI_Init(Napi::Env env, Napi::Object exports) -{ - realm::node::napi_init(env, exports); - return exports; -} - -NODE_API_MODULE(realm, NAPI_Init) diff --git a/packages/realm/bindgen/src/node/node_init.hpp b/packages/realm/bindgen/src/node/node_init.hpp deleted file mode 100644 index fb83f954167..00000000000 --- a/packages/realm/bindgen/src/node/node_init.hpp +++ /dev/null @@ -1,32 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_string.hpp" -#include "node_protected.hpp" -#include "node_function.hpp" -#include "node_value.hpp" -#include "node_context.hpp" -#include "node_object.hpp" -#include "node_exception.hpp" -#include "node_return_value.hpp" -#include "node_class.hpp" - -// FIXME: js_object_accessor.hpp includes js_list.hpp which includes js_object_accessor.hpp. -#include "js_object_accessor.hpp" diff --git a/packages/realm/bindgen/src/node/node_object.hpp b/packages/realm/bindgen/src/node/node_object.hpp deleted file mode 100644 index adca80842ef..00000000000 --- a/packages/realm/bindgen/src/node/node_object.hpp +++ /dev/null @@ -1,245 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" -#include "napi.h" - -namespace realm { -namespace js { - -inline napi_property_attributes operator|(napi_property_attributes a, PropertyAttributes b) -{ - int flag = napi_default; - - if ((b & DontEnum) != DontEnum) { - flag |= napi_enumerable; - } - - if ((b & DontDelete) != DontDelete) { - flag |= napi_configurable; - } - - if ((b & ReadOnly) != ReadOnly) { - flag |= napi_writable; - } - - napi_property_attributes napi_flag = static_cast(a | flag); - return napi_flag; -} - -template <> -inline Napi::Value node::Object::get_property(Napi::Env env, const Napi::Object& object, StringData key) -{ - try { - return object.Get(key); - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline Napi::Value node::Object::get_property(Napi::Env env, const Napi::Object& object, const node::String& key) -{ - try { - return object.Get(key); - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline Napi::Value node::Object::get_property(Napi::Env env, const Napi::Object& object, uint32_t index) -{ - try { - return object.Get(index); - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline void node::Object::set_property(Napi::Env env, Napi::Object& object, const node::String& key, - const Napi::Value& value, PropertyAttributes attributes) -{ - try { - Napi::Object obj = object; - if (attributes) { - napi_property_attributes napi_attributes = napi_default | attributes; - std::string name = key; - auto propDescriptor = Napi::PropertyDescriptor::Value(name, value, napi_attributes); - obj.DefineProperty(propDescriptor); - } - else { - obj.Set(key, value); - } - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline void node::Object::set_property(Napi::Env env, Napi::Object& object, uint32_t index, const Napi::Value& value) -{ - try { - Napi::Object obj = object; - obj.Set(index, value); - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline std::vector node::Object::get_property_names(Napi::Env env, const Napi::Object& object) -{ - try { - auto propertyNames = object.GetPropertyNames(); - - uint32_t count = propertyNames.Length(); - std::vector names; - names.reserve(count); - - for (uint32_t i = 0; i < count; i++) { - names.push_back(node::Value::to_string(env, propertyNames[i])); - } - - return names; - } - catch (const Napi::Error& e) { - throw node::Exception(env, e.Message()); - } -} - -template <> -inline Napi::Value node::Object::get_prototype(Napi::Env env, const Napi::Object& object) -{ - napi_value result; - napi_status status = napi_get_prototype(env, object, &result); - if (status != napi_ok) { - throw node::Exception(env, "Failed to get object's prototype"); - } - return Napi::Object(env, result); -} - -template <> -inline void node::Object::set_prototype(Napi::Env env, const Napi::Object& object, const Napi::Value& prototype) -{ - auto setPrototypeOfFunc = - env.Global().Get("Object").As().Get("setPrototypeOf").As(); - if (setPrototypeOfFunc.IsEmpty() || setPrototypeOfFunc.IsUndefined()) { - throw std::runtime_error("no 'setPrototypeOf'"); - } - - setPrototypeOfFunc.Call({object, prototype}); -} - -template <> -inline Napi::Object node::Object::create_empty(Napi::Env env) -{ - return Napi::Object::New(env); -} - -template <> -inline Napi::Object node::Object::create_array(Napi::Env env, uint32_t length, const Napi::Value values[]) -{ - Napi::Array array = Napi::Array::New(env, length); - for (uint32_t i = 0; i < length; i++) { - set_property(env, array, i, values[i]); - } - return array; -} - -template <> -inline Napi::Object node::Object::create_date(Napi::Env env, double time) -{ - Napi::Function date_constructor = env.Global().Get("Date").As(); - Napi::Number value = Napi::Number::New(env, time); - return date_constructor.New({value}); -} - -template <> -template -inline Napi::Object node::Object::create_instance(Napi::Env env, typename ClassType::Internal* internal) -{ - return node::ObjectWrap::create_instance(env, internal); -} - -template <> -template -inline Napi::Object node::Object::create_instance_by_schema(Napi::Env env, Napi::Function& constructor, - const realm::ObjectSchema& schema, - typename ClassType::Internal* internal) -{ - return node::ObjectWrap::create_instance_by_schema(env, constructor, schema, internal); -} - -template <> -template -inline Napi::Object node::Object::create_instance_by_schema(Napi::Env env, const realm::ObjectSchema& schema, - typename ClassType::Internal* internal) -{ - return node::ObjectWrap::create_instance_by_schema(env, schema, internal); -} - -template -inline void on_context_destroy(Napi::Env env, std::string realmPath) -{ - node::ObjectWrap::on_context_destroy(env, realmPath); -} - -template <> -template -inline bool node::Object::is_instance(Napi::Env env, const Napi::Object& object) -{ - return node::ObjectWrap::is_instance(env, object); -} - -template <> -template -inline typename ClassType::Internal* node::Object::get_internal(Napi::Env env, const Napi::Object& object) -{ - return node::ObjectWrap::get_internal(env, object); -} - -template <> -template -inline void node::Object::set_internal(Napi::Env env, Napi::Object& object, typename ClassType::Internal* internal) -{ - return node::ObjectWrap::set_internal(env, object, internal); -} - -template <> -inline void node::Object::set_global(Napi::Env env, const node::String& key, const Napi::Value& value) -{ - Object::set_property(env, env.Global(), key, value); -} - -template <> -inline Napi::Value node::Object::get_global(Napi::Env env, const node::String& key) -{ - return Object::get_property(env, env.Global(), key); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_protected.hpp b/packages/realm/bindgen/src/node/node_protected.hpp deleted file mode 100644 index 6f51be3dfaa..00000000000 --- a/packages/realm/bindgen/src/node/node_protected.hpp +++ /dev/null @@ -1,247 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" - -namespace realm { -namespace node { - -template -class Protected { -protected: - Napi::Env m_env; - napi_ref m_ref; - bool m_isValue; - bool m_suppressDestruct = false; - -public: - Protected() - : m_env(nullptr) - , m_ref(nullptr) - , m_isValue(false) - { - } - - Protected(Napi::Env env, MemberType value) - : m_env(env) - , m_isValue(false) - { - napi_status status = napi_create_reference(env, value, 1, &m_ref); - if (status == napi_object_expected || status == napi_invalid_arg) { - m_isValue = true; - Napi::Object object = Napi::Object::New(m_env); - object.Set("value", value); - status = napi_create_reference(env, object, 1, &m_ref); - } - - if (status != napi_ok) { - throw std::runtime_error(util::format("Can't create protected reference: napi_status %1", status)); - } - } - - Protected(const Protected& other) - : m_env(other.m_env) - , m_ref(other.m_ref) - , m_isValue(other.m_isValue) - { - uint32_t result; - napi_status status = napi_reference_ref(m_env, m_ref, &result); - if (status != napi_ok) { - throw std::runtime_error( - util::format("Can't increase protected reference count: napi_status %1", status)); - } - } - - Protected(Protected&& other) - : Protected() - { - swap(*this, other); - } - - friend void swap(Protected& first, Protected& second) - { - std::swap(first.m_env, second.m_env); - std::swap(first.m_ref, second.m_ref); - std::swap(first.m_isValue, second.m_isValue); - std::swap(first.m_suppressDestruct, second.m_suppressDestruct); - } - - // uses the copy and swap idiom - Protected& operator=(Protected other) - { - swap(*this, other); - return *this; - } - - ~Protected() - { - if (m_ref == nullptr || m_suppressDestruct) { - return; - } - - try { - uint32_t result; - napi_status status = napi_reference_unref(m_env, m_ref, &result); - REALM_ASSERT((status == napi_ok) && "~Protected: Can't decrease protected reference count"); - - if (result == 0) { - napi_status status = napi_delete_reference(m_env, m_ref); - REALM_ASSERT((status == napi_ok) && "~Protected: Can't unallocate protected reference"); - } - - m_ref = nullptr; - } - catch (...) { - } - } - - operator MemberType() const - { - napi_value value; - napi_status status = napi_get_reference_value(m_env, m_ref, &value); - if (status != napi_ok) { - throw std::runtime_error(util::format("Can't get protected reference: napi_status %1", status)); - } - - if (value == nullptr) { - throw std::runtime_error(util::format("Can not use unallocated protected reference")); - } - - if (m_isValue) { - Napi::Object object = Napi::Object(m_env, value); - return object.Get("value").As(); - } - - MemberType member = MemberType(m_env, value); - return member; - } - - explicit operator bool() const - { - napi_value value; - napi_status status = napi_get_reference_value(m_env, m_ref, &value); - if (status != napi_ok) { - throw std::runtime_error(util::format("Can't get protected reference: napi_status %1", status)); - } - - if (value == nullptr) { - throw std::runtime_error(util::format("Can not use unallocated protected reference")); - } - - return value == nullptr; - } - - bool operator==(const MemberType& other) const - { - MemberType memberType = *this; - - return memberType == other; - } - - bool operator!=(const MemberType& other) const - { - MemberType memberType = *this; - return memberType != other; - } - - bool operator==(const Protected& other) const - { - MemberType thisValue = *this; - MemberType otherValue = *other; - return thisValue == otherValue; - } - - bool operator!=(const Protected& other) const - { - MemberType thisValue = *this; - MemberType otherValue = *other; - return thisValue != otherValue; - } - - void SuppressDestruct() - { - m_suppressDestruct = true; - } - - struct Comparator { - bool operator()(const Protected& a, const Protected& b) const - { - MemberType aValue = a; - MemberType bValue = b; - return aValue == bValue; - } - }; -}; - -} // namespace node - -namespace js { - -template <> -class Protected { - node::Types::GlobalContext m_ctx; - -public: - Protected(node::Types::GlobalContext ctx) - : m_ctx(ctx) - { - } - - operator Napi::Env() const - { - return m_ctx; - } - - bool operator==(const Protected& other) const - { - // GlobalContext is always equal since its Napi::Env - return true; - } -}; - -template <> -class Protected : public node::Protected { -public: - Protected(Napi::Env env, Napi::Value value) - : node::Protected(env, value) - { - } -}; - -template <> -class Protected : public node::Protected { -public: - Protected(Napi::Env env, Napi::Object object) - : node::Protected(env, object) - { - } -}; - -template <> -class Protected : public node::Protected { -public: - Protected(Napi::Env env, Napi::Function function) - : node::Protected(env, function) - { - } -}; - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_return_value.hpp b/packages/realm/bindgen/src/node/node_return_value.hpp deleted file mode 100644 index 884a24a23c5..00000000000 --- a/packages/realm/bindgen/src/node/node_return_value.hpp +++ /dev/null @@ -1,130 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include - -#include "node_types.hpp" -#include "napi.h" - -namespace realm { -namespace js { - -template <> -class ReturnValue { - Napi::Env m_env; - Napi::Value m_value; - -public: - ReturnValue(Napi::Env env) - : m_env(env) - , m_value(Napi::Value(env, env.Undefined())) - { - } - ReturnValue(Napi::Env env, Napi::Value value) - : m_env(env) - , m_value(value) - { - } - - Napi::Value ToValue() const - { - // guard check. env.Empty() values cause node to fail in obscure places, so return undefined instead - if (m_value.IsEmpty()) { - return Napi::Value(m_env, m_env.Undefined()); - } - - return m_value; - } - - void set(const Napi::Value& value) - { - m_value = Napi::Value(m_env, value); - } - - void set(const std::string& string) - { - m_value = Napi::Value::From(m_env, string); - } - - void set(const char* str) - { - if (!str) { - m_value = Napi::Value(m_env, m_env.Null()); - } - else { - m_value = Napi::Value::From(m_env, str); - } - } - - void set(bool boolean) - { - m_value = Napi::Value::From(m_env, boolean); - } - - void set(double number) - { - m_value = Napi::Value::From(m_env, number); - } - - void set(int32_t number) - { - m_value = Napi::Value::From(m_env, number); - } - - void set(uint32_t number) - { - m_value = Napi::Value::From(m_env, number); - } - - void set(realm::Mixed mixed) - { - m_value = Value::from_mixed(m_env, nullptr, mixed); - } - - void set_null() - { - m_value = Napi::Value(m_env, m_env.Null()); - } - - - void set_undefined() - { - m_value = Napi::Value(m_env, m_env.Undefined()); - } - - template - void set(std::optional value) - { - if (value) { - set(*value); - } - else { - set_undefined(); - } - } - - operator Napi::Value() const - { - return ToValue(); - } -}; - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_string.hpp b/packages/realm/bindgen/src/node/node_string.hpp deleted file mode 100644 index 6d74ad05748..00000000000 --- a/packages/realm/bindgen/src/node/node_string.hpp +++ /dev/null @@ -1,81 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" - -#include "napi.h" - -namespace realm { -namespace js { - -template <> -class String { - using StringType = String; - - std::string m_str; - -public: - static bson::Bson to_bson(StringType stringified_ejson) - { - return bson::parse(std::string(stringified_ejson)); - } - - static std::string from_bson(const bson::Bson& bson) - { - return bson.to_string(); - } - - String(const char* s) - : m_str(s) - { - } - String(const std::string& s) - : m_str(s) - { - } - String(const Napi::String& s); - String(Napi::String&& s) - : String(s) - { - } - - operator std::string() const& - { - return m_str; - } - - operator std::string() && - { - return std::move(m_str); - } - - Napi::String ToString(Napi::Env env) - { - return Napi::String::New(env, m_str); - } -}; - -inline String::String(const Napi::String& s) -{ - m_str = s.Utf8Value(); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_types.hpp b/packages/realm/bindgen/src/node/node_types.hpp deleted file mode 100644 index 572f82c6a03..00000000000 --- a/packages/realm/bindgen/src/node/node_types.hpp +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include - -#include "napi.h" - -#define HANDLESCOPE(env) Napi::HandleScope handle_scope(env); - -#include "js_types.hpp" - -namespace realm { -namespace node { - -extern Napi::FunctionReference RealmClassConstructor; - -struct Types { - using Context = Napi::Env; - using GlobalContext = Napi::Env; - using Value = Napi::Value; - using Object = Napi::Object; - using String = Napi::String; - using Function = Napi::Function; - - typedef Napi::Value (*NapiFunctionCallback)(const Napi::CallbackInfo& info); - - typedef Napi::Value (*NapiIndexGetterCallback)(const Napi::CallbackInfo& info, const Napi::Object& instance, - uint32_t index); - typedef Napi::Value (*NapiIndexSetterCallback)(const Napi::CallbackInfo& info, const Napi::Object& instance, - uint32_t index, const Napi::Value& value); - typedef Napi::Value (*NapiPropertyGetterCallback)(const Napi::CallbackInfo& info); - typedef void (*NapiPropertySetterCallback)(const Napi::CallbackInfo& info, const Napi::Value& value); - - typedef Napi::Value (*NapiStringPropertyGetterCallback)(const Napi::CallbackInfo& info, - const Napi::Object& instance, - const Napi::String& property); - typedef Napi::Value (*NapiStringPropertySetterCallback)(const Napi::CallbackInfo& info, - const Napi::Object& instance, - const Napi::String& property, const Napi::Value& value); - typedef Napi::Value (*NapiStringPropertyEnumeratorCallback)(const Napi::CallbackInfo& info, - const Napi::Object& instance); - - using ConstructorCallback = NapiFunctionCallback; - using FunctionCallback = NapiFunctionCallback; - using PropertyGetterCallback = NapiPropertyGetterCallback; - using PropertySetterCallback = NapiPropertySetterCallback; - using IndexPropertyGetterCallback = NapiIndexGetterCallback; - using IndexPropertySetterCallback = NapiIndexSetterCallback; - - using StringPropertyGetterCallback = NapiStringPropertyGetterCallback; - using StringPropertySetterCallback = NapiStringPropertySetterCallback; - using StringPropertyEnumeratorCallback = NapiStringPropertyEnumeratorCallback; -}; - -template -class ObjectWrap; - -using String = js::String; -using Context = js::Context; -using Value = js::Value; -using Function = js::Function; -using Object = js::Object; -using Exception = js::Exception; -using ReturnValue = js::ReturnValue; - -} // namespace node -} // namespace realm diff --git a/packages/realm/bindgen/src/node/node_value.hpp b/packages/realm/bindgen/src/node/node_value.hpp deleted file mode 100644 index 51edfaceab8..00000000000 --- a/packages/realm/bindgen/src/node/node_value.hpp +++ /dev/null @@ -1,396 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "node_types.hpp" -#include "napi.h" -#include "realm/binary_data.hpp" - -namespace realm { -namespace js { - -template <> -inline const char* node::Value::typeof(Napi::Env env, const Napi::Value& value) -{ - if (value.IsNull()) { - return "null"; - } - if (value.IsNumber()) { - return "number"; - } - if (value.IsString()) { - return "string"; - } - if (value.IsBoolean()) { - return "boolean"; - } - if (value.IsUndefined()) { - return "undefined"; - } - if (value.IsObject()) { - return "object"; - } - return "unknown"; -} - -template <> -inline bool node::Value::is_array(Napi::Env env, const Napi::Value& value) -{ - return value.IsArray(); -} - -template <> -inline bool node::Value::is_array_buffer(Napi::Env env, const Napi::Value& value) -{ - return value.IsArrayBuffer(); -} - -template <> -inline bool node::Value::is_array_buffer_view(Napi::Env env, const Napi::Value& value) -{ - return value.IsTypedArray() || value.IsDataView(); -} - -template <> -inline bool node::Value::is_date(Napi::Env env, const Napi::Value& value) -{ - if (value.IsEmpty()) { - return false; - } - - bool is_date; - napi_status status = napi_is_date(env, value, &is_date); - NAPI_THROW_IF_FAILED(env, status, false); - return is_date; -} - -template <> -inline bool node::Value::is_boolean(Napi::Env env, const Napi::Value& value) -{ - return value.IsBoolean(); -} - -template <> -inline bool node::Value::is_constructor(Napi::Env env, const Napi::Value& value) -{ - return value.IsFunction(); -} - - -template <> -inline bool node::Value::is_error(Napi::Env env, const Napi::Value& value) -{ - return value.IsObject() && value.As().InstanceOf(env.Global().Get("Error").As()); -} - -template <> -inline bool node::Value::is_function(Napi::Env env, const Napi::Value& value) -{ - return value.IsFunction(); -} - -template <> -inline bool node::Value::is_null(Napi::Env env, const Napi::Value& value) -{ - return value.IsNull(); -} - -template <> -inline bool node::Value::is_number(Napi::Env env, const Napi::Value& value) -{ - return value.IsNumber(); -} - -inline bool is_bson_type(Napi::Env env, const Napi::Value& value, std::string type) -{ - if (value.IsNull() || value.IsUndefined() || !value.IsObject()) { - return false; - } - - Napi::Object object = value.As(); - Napi::Value bsonType = object.Get("_bsontype"); - if (bsonType.IsUndefined()) { - return false; - } - - return bsonType.StrictEquals(Napi::String::New(env, type)); -} - -template <> -inline bool node::Value::is_decimal128(Napi::Env env, const Napi::Value& value) -{ - return is_bson_type(env, value, "Decimal128"); -} - -template <> -inline bool node::Value::is_object_id(Napi::Env env, const Napi::Value& value) -{ - return is_bson_type(env, value, "ObjectID"); -} - -template <> -inline bool node::Value::is_uuid(Napi::Env env, const Napi::Value& value) -{ - return is_bson_type(env, value, "UUID"); -} - -template <> -inline bool node::Value::is_object(Napi::Env env, const Napi::Value& value) -{ - return value.IsObject(); -} - -template <> -inline bool node::Value::is_string(Napi::Env env, const Napi::Value& value) -{ - return value.IsString(); -} - -template <> -inline bool node::Value::is_undefined(Napi::Env env, const Napi::Value& value) -{ - return value.IsUndefined(); -} - -template <> -inline bool node::Value::is_binary(Napi::Env env, const Napi::Value& value) -{ - return Value::is_array_buffer(env, value) || Value::is_array_buffer_view(env, value); -} - -template <> -inline bool node::Value::is_valid(const Napi::Value& value) -{ - return !value.IsEmpty(); -} - -template <> -inline Napi::Value node::Value::from_boolean(Napi::Env env, bool boolean) -{ - return Napi::Boolean::New(env, boolean); -} - - -template <> -inline Napi::Value node::Value::from_null(Napi::Env env) -{ - return Napi::Value(env, env.Null()); -} - -template <> -inline Napi::Value node::Value::from_number(Napi::Env env, double number) -{ - return Napi::Number::New(env, number); -} - -template <> -inline Napi::Value node::Value::from_nonnull_string(Napi::Env env, const node::String& string) -{ - return Napi::String::New(env, string); -} - -template <> -inline Napi::Value node::Value::from_nonnull_binary(Napi::Env env, BinaryData data) -{ - Napi::EscapableHandleScope scope(env); - - Napi::ArrayBuffer buffer = Napi::ArrayBuffer::New(env, data.size()); - - if (data.size()) { - memcpy(buffer.Data(), data.data(), data.size()); - } - - return scope.Escape(buffer); -} - -template <> -inline Napi::Value node::Value::from_undefined(Napi::Env env) -{ - return Napi::Value(env, env.Undefined()); -} - -template <> -inline bool node::Value::to_boolean(Napi::Env env, const Napi::Value& value) -{ - return value.ToBoolean(); -} - -template <> -inline node::String node::Value::to_string(Napi::Env env, const Napi::Value& value) -{ - return value.ToString(); -} - -template <> -inline double node::Value::to_number(Napi::Env env, const Napi::Value& value) -{ - double number = value.ToNumber(); - if (std::isnan(number)) { - throw std::invalid_argument( - util::format("Value '%1' not convertible to a number.", (std::string)to_string(env, value))); - } - - return number; -} - -template <> -inline OwnedBinaryData node::Value::to_binary_impl(Napi::Env env, const Napi::Value& value) -{ - if (value.IsDataView()) { - auto buf = value.As(); - return OwnedBinaryData(reinterpret_cast(buf.Data()), buf.ByteLength()); - } - else if (value.IsTypedArray()) { - auto view = value.As(); - auto buf = view.ArrayBuffer(); - return OwnedBinaryData(reinterpret_cast(buf.Data()) + view.ByteOffset(), view.ByteLength()); - } - else if (value.IsArrayBuffer()) { - auto buf = value.As(); - return OwnedBinaryData(reinterpret_cast(buf.Data()), buf.ByteLength()); - } - - throw std::runtime_error("Can only convert Buffer, ArrayBuffer, and ArrayBufferView objects to binary"); -} - - -template <> -inline Napi::Object node::Value::to_object(Napi::Env env, const Napi::Value& value) -{ - return value.ToObject(); -} - -template <> -inline Napi::Object node::Value::to_array(Napi::Env env, const Napi::Value& value) -{ - return to_object(env, value); -} - -template <> -inline Napi::Function node::Value::to_function(Napi::Env env, const Napi::Value& value) -{ - return value.IsFunction() ? value.As() : Napi::Function(); -} - -template <> -inline Napi::Function node::Value::to_constructor(Napi::Env env, const Napi::Value& value) -{ - return to_function(env, value); -} - -template <> -inline Napi::Object node::Value::to_date(Napi::Env env, const Napi::Value& value) -{ - if (value.IsString()) { - Napi::Function date_constructor = to_constructor(env, env.Global().Get("Date")); - std::array args{{value}}; - return node::Function::construct(env, date_constructor, args.size(), args.data()); - } - - return to_object(env, value); -} - -template <> -inline Napi::Value node::Value::from_decimal128(Napi::Env env, const Decimal128& number) -{ - Napi::EscapableHandleScope scope(env); - - if (number.is_null()) { - return scope.Escape(Napi::Value(env, env.Null())); - } - - Napi::Function realm_constructor = node::RealmClassConstructor.Value(); - Napi::Object decimal_constructor = realm_constructor.Get("_Decimal128").As(); - Napi::Function fromStringFunc = decimal_constructor.Get("fromString").As(); - Napi::String numberAsString = Napi::String::New(env, number.to_string()); - Napi::Value result = fromStringFunc.Call({numberAsString}); - - return scope.Escape(result); -} - -template <> -inline Decimal128 node::Value::to_decimal128(Napi::Env env, const Napi::Value& value) -{ - Napi::HandleScope scope(env); - - Napi::Object decimal128 = value.As(); - Napi::Function toStringFunc = decimal128.Get("toString").As(); - node::String string = toStringFunc.Call(value, {}).As(); - std::string decimal128AsString = string; - Decimal128 result(decimal128AsString); - return result; -} - -template <> -inline Napi::Value node::Value::from_object_id(Napi::Env env, const ObjectId& objectId) -{ - Napi::EscapableHandleScope scope(env); - - Napi::Function realm_constructor = node::RealmClassConstructor.Value(); - Napi::Function object_id_constructor = realm_constructor.Get("_ObjectId").As(); - napi_value args[] = {Napi::String::New(env, objectId.to_string())}; - Napi::Value result = object_id_constructor.New(1, args); - return scope.Escape(result); -} - -template <> -inline ObjectId node::Value::to_object_id(Napi::Env env, const Napi::Value& value) -{ - Napi::HandleScope scope(env); - - Napi::Object objectId = value.As(); - Napi::Function toHexStringFunc = objectId.Get("toHexString").As(); - node::String string = toHexStringFunc.Call(value, {}).As(); - std::string objectIdAsString = string; - ObjectId result(objectIdAsString.c_str()); - return result; -} - -template <> -Napi::Value node::Value::from_uuid(Napi::Env env, const UUID& uuid) -{ - Napi::EscapableHandleScope scope(env); - - Napi::Function realm_constructor = node::RealmClassConstructor.Value(); - Napi::Function uuid_constructor = realm_constructor.Get("_UUID").As(); - - napi_value args[] = {Napi::Buffer::Copy(env, uuid.to_bytes().data(), UUID::num_bytes)}; - Napi::Value result = uuid_constructor.New(1, args); - - return scope.Escape(result); -} - -template <> -inline UUID node::Value::to_uuid(Napi::Env env, const Napi::Value& value) -{ - Napi::HandleScope scope(env); - - Napi::Object uuid = value.As(); - // TODO: Temp implementation of JS UUID has a buffer on the "id" key. This is corresponding to the official - // ObjectId implementation - BUT could change. - auto buffer = uuid.Get("id").As>(); - UUID::UUIDBytes bytes; - memcpy(&bytes, buffer.Data(), UUID::num_bytes); - - UUID result(bytes); - return result; -} - -} // namespace js -} // namespace realm From fcf69b4396e3ba70033cc88647f6c99da5ac3cfb Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Tue, 12 Dec 2023 15:50:52 +0100 Subject: [PATCH 03/14] Correction for ios build script --- packages/realm/scripts/build-ios.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/realm/scripts/build-ios.sh b/packages/realm/scripts/build-ios.sh index 48397f35919..1984e884671 100755 --- a/packages/realm/scripts/build-ios.sh +++ b/packages/realm/scripts/build-ios.sh @@ -116,7 +116,7 @@ done rm -rf _include mkdir -p _include/realm-js-ios -cp "$PROJECT_ROOT"/src/jsi/jsi_init.h _include/realm-js-ios/ +cp "$BINDGEN_PATH"/src/jsi/jsi_init.h _include/realm-js-ios/ rm -rf ../realm-js-ios.xcframework xcodebuild -create-xcframework \ From 767047b6ded8b669e62e30edab71f7b6c44b6d4c Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Tue, 12 Dec 2023 16:01:51 +0100 Subject: [PATCH 04/14] Remove more files, fix android build, mv cmake dir --- Realm.xcworkspace/contents.xcworkspacedata | 13 -------- .../xcshareddata/Realm.xcscmblueprint | 31 ------------------- compile_commands.json | 1 - packages/realm/bindgen/CMakeLists.txt | 2 +- .../realm/bindgen/cmake}/NodeJSTargets.cmake | 0 .../realm/bindgen/src/android/CMakeLists.txt | 2 +- react-native.config.js | 31 ------------------- 7 files changed, 2 insertions(+), 78 deletions(-) delete mode 100644 Realm.xcworkspace/contents.xcworkspacedata delete mode 100644 Realm.xcworkspace/xcshareddata/Realm.xcscmblueprint delete mode 120000 compile_commands.json rename {cmake => packages/realm/bindgen/cmake}/NodeJSTargets.cmake (100%) delete mode 100644 react-native.config.js diff --git a/Realm.xcworkspace/contents.xcworkspacedata b/Realm.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 58e6bdb83e9..00000000000 --- a/Realm.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - diff --git a/Realm.xcworkspace/xcshareddata/Realm.xcscmblueprint b/Realm.xcworkspace/xcshareddata/Realm.xcscmblueprint deleted file mode 100644 index 30f010e7480..00000000000 --- a/Realm.xcworkspace/xcshareddata/Realm.xcscmblueprint +++ /dev/null @@ -1,31 +0,0 @@ -{ - "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "40F53A12E4AE40C654358321B91166ABD3E910A6", - "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { - - }, - "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { - "40F53A12E4AE40C654358321B91166ABD3E910A6" : 0, - "F6F96CA34C5878B0A9123C7C37855491A5E599DA" : 0, - "8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : 0 - }, - "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "5EE721F9-041C-4877-9E73-A925C9DB080A", - "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { - "40F53A12E4AE40C654358321B91166ABD3E910A6" : "realm-js\/", - "8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" : "realm-js\/src\/object-store\/" - }, - "DVTSourceControlWorkspaceBlueprintNameKey" : "Realm", - "DVTSourceControlWorkspaceBlueprintVersion" : 204, - "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Realm.xcworkspace", - "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ - { - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/realm\/realm-js.git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "40F53A12E4AE40C654358321B91166ABD3E910A6" - }, - { - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/realm\/realm-object-store.git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8F3C415DA79CDA7D23734F285B95F9F9A3C0CB81" - } - ] -} \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json deleted file mode 120000 index 25eb4b2b4b7..00000000000 --- a/compile_commands.json +++ /dev/null @@ -1 +0,0 @@ -build/compile_commands.json \ No newline at end of file diff --git a/packages/realm/bindgen/CMakeLists.txt b/packages/realm/bindgen/CMakeLists.txt index 4ddb93111fd..2e61462bacd 100644 --- a/packages/realm/bindgen/CMakeLists.txt +++ b/packages/realm/bindgen/CMakeLists.txt @@ -84,7 +84,7 @@ if(CMAKE_GENERATOR MATCHES "^Ninja") endif() if(DEFINED CMAKE_JS_VERSION) - list(APPEND CMAKE_MODULE_PATH "${PACKAGE_ROOT_DIR}/cmake") + list(APPEND CMAKE_MODULE_PATH "./cmake") include(NodeJSTargets) endif() diff --git a/cmake/NodeJSTargets.cmake b/packages/realm/bindgen/cmake/NodeJSTargets.cmake similarity index 100% rename from cmake/NodeJSTargets.cmake rename to packages/realm/bindgen/cmake/NodeJSTargets.cmake diff --git a/packages/realm/bindgen/src/android/CMakeLists.txt b/packages/realm/bindgen/src/android/CMakeLists.txt index 79a5f4b5066..59d106ce75e 100644 --- a/packages/realm/bindgen/src/android/CMakeLists.txt +++ b/packages/realm/bindgen/src/android/CMakeLists.txt @@ -112,7 +112,7 @@ target_include_directories(realm-js-android PUBLIC ${FBJNI_INCLUDE_DIR} ${REACT_NATIVE_ROOT_DIR}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ ${REACT_NATIVE_ROOT_DIR}/ReactAndroid/src/main/jni/react/turbomodule/ - ${REACT_NATIVE_ROOT_DIR}/react-native/ReactCommon/callinvoker/ + ${REACT_NATIVE_ROOT_DIR}/ReactCommon/callinvoker/ ) target_link_libraries(realm-js-android diff --git a/react-native.config.js b/react-native.config.js deleted file mode 100644 index 263b3e3f9fb..00000000000 --- a/react-native.config.js +++ /dev/null @@ -1,31 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -/* eslint-env node */ - -module.exports = { - // config for a library is scoped under "dependency" key - dependency: { - platforms: { - android: { - sourceDir: "./react-native/android", - }, - ios: {}, - }, - }, -}; From 2e2b144707bbbae104a1fd94017bd518a3ead5c3 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Tue, 12 Dec 2023 19:32:00 +0100 Subject: [PATCH 05/14] Fix ios integration tests --- integration-tests/environments/react-native/ios/Podfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/integration-tests/environments/react-native/ios/Podfile b/integration-tests/environments/react-native/ios/Podfile index 82d1ef024a6..0fbe616650b 100644 --- a/integration-tests/environments/react-native/ios/Podfile +++ b/integration-tests/environments/react-native/ios/Podfile @@ -73,8 +73,8 @@ target 'RealmReactNativeTests' do # Applying https://github.com/facebook/folly/issues/1470#issuecomment-943123653 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FOLLY_HAVE_CLOCK_GETTIME=1'] end - config.build_settings['CC'] = "../../../../../scripts/ccache-clang.sh" - config.build_settings['CXX'] = "../../../../../scripts/ccache-clang++.sh" + config.build_settings['CC'] = "../../../../../packages/realm/scripts/ccache-clang.sh" + config.build_settings['CXX'] = "../../../../../packages/realm/scripts/ccache-clang++.sh" end end end From 5ad03eb566a04ac4d4c00f3d7356fd803855fb17 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 13 Dec 2023 09:59:04 +0100 Subject: [PATCH 06/14] Remove unused jsi functions --- packages/realm/bindgen/src/jsi/jsi_class.hpp | 783 ------------------ .../realm/bindgen/src/jsi/jsi_function.hpp | 67 -- packages/realm/bindgen/src/jsi/jsi_init.hpp | 30 - packages/realm/bindgen/src/jsi/jsi_object.hpp | 227 ----- .../realm/bindgen/src/jsi/jsi_protected.hpp | 87 -- .../bindgen/src/jsi/jsi_return_value.hpp | 131 --- packages/realm/bindgen/src/jsi/jsi_string.hpp | 93 --- packages/realm/bindgen/src/jsi/jsi_types.hpp | 401 --------- packages/realm/bindgen/src/jsi/jsi_value.hpp | 429 ---------- 9 files changed, 2248 deletions(-) delete mode 100644 packages/realm/bindgen/src/jsi/jsi_class.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_function.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_init.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_object.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_protected.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_return_value.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_string.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_types.hpp delete mode 100644 packages/realm/bindgen/src/jsi/jsi_value.hpp diff --git a/packages/realm/bindgen/src/jsi/jsi_class.hpp b/packages/realm/bindgen/src/jsi/jsi_class.hpp deleted file mode 100644 index da8ce42e45f..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_class.hpp +++ /dev/null @@ -1,783 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once -#include "jsi_types.hpp" -#include "jsi_return_value.hpp" -#include "jsi_string.hpp" -#include "jsi_object.hpp" - -#include "js_class.hpp" -#include "js_util.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace realm::js { - -template -struct RealmObjectClass; -template -class RealmClass; - -namespace fbjsi = facebook::jsi; - -template <> -struct Arguments { - const std::vector valStorage; - const JsiEnv ctx; - const size_t count; - const JsiVal* const value; - - Arguments(JsiEnv env, size_t argc, const JsiVal* argv) - : ctx(env) - , count(argc) - , value(argv) - { - } - - Arguments(JsiEnv env, size_t argc, const fbjsi::Value* argv) - : valStorage([&] { - std::vector out; - out.reserve(argc); - for (size_t i = 0; i < argc; i++) { - out.emplace_back(env, argv[i]); - } - return out; - }()) - , ctx(env) - , count(argc) - , value(valStorage.data()) - { - } - - // If moving or copying were allowed, we would need to update value's pointer - Arguments(Arguments&&) = delete; - - JsiVal operator[](size_t index) const noexcept - { - if (index >= count) { - return ctx.undefined(); - } - return ctx(value[index]); - } - - void validate_maximum(size_t max) const - { - if (max < count) { - throw std::invalid_argument( - util::format("Invalid arguments: at most %1 expected, but %2 supplied.", max, count)); - } - } - - void validate_count(size_t expected) const - { - if (count != expected) { - throw std::invalid_argument( - util::format("Invalid arguments: %1 expected, but %2 supplied.", expected, count)); - } - } - - void validate_between(size_t min, size_t max) const - { - if (count < min || count > max) { - throw std::invalid_argument( - util::format("Invalid arguments: expected between %1 and %2, but %3 supplied.", min, max, count)); - } - } -}; - -namespace realmjsi { // realm::js::realmjsi - -inline std::optional ObjectGetOwnPropertyDescriptor(JsiEnv env, const fbjsi::Object& target, - const std::string& name) -{ - auto obj = js::globalType(env, "Object"); - auto res = obj.getPropertyAsFunction(env, "getOwnPropertyDescriptor").callWithThis(env, obj, target, name); - if (!res.isObject()) - return {}; - return std::move(res).getObject(env); -} - -inline void ObjectSetPrototypeOf(JsiEnv env, const fbjsi::Value& target, const fbjsi::Value& proto) -{ - auto obj = js::globalType(env, "Object"); - obj.getPropertyAsFunction(env, "setPrototypeOf").callWithThis(env, obj, target, proto); -} - -// Cache various objects we fetch from the runtime which are hot paths -// during object creation -static std::optional s_object; -static std::optional s_object_create; - -inline JsiObj ObjectCreate(JsiEnv env, const fbjsi::Object& proto) -{ - if (REALM_UNLIKELY(!s_object)) { - s_object = js::globalType(env, "Object"); - } - if (REALM_UNLIKELY(!s_object_create)) { - s_object_create = (*s_object).getPropertyAsFunction(env, "create"); - } - - return env(s_object_create->call(env, proto)).asObject(); -} - -inline void defineProperty(JsiEnv env, const fbjsi::Object& target, StringData name, const fbjsi::Object& descriptor) -{ - auto objClass = js::globalType(env, "Object"); - objClass.getPropertyAsFunction(env, "defineProperty") - .callWithThis(env, objClass, target, str(env, name), descriptor); -}; - -inline void copyProperty(JsiEnv env, const fbjsi::Object& from, const fbjsi::Object& to, const std::string& name) -{ - auto prop = ObjectGetOwnPropertyDescriptor(env, from, name); - REALM_ASSERT_RELEASE(prop); - defineProperty(env, to, name, *prop); -} - -// The name used for the property on the JS object which stores the reference to the corresponding C++ object. -// We use an empty string as testing showed it was 1% faster with JSC and 4% faster with Hermes than using -// an actual string, and also has the benefit that it is not a valid Realm object key name. -inline constexpr const char g_internal_field[] = ""; - -template -using ClassDefinition = js::ClassDefinition; - -using ConstructorType = js::ConstructorType; -using ArgumentsMethodType = js::ArgumentsMethodType; -using ReturnValue = js::ReturnValue; -using Arguments = js::Arguments; -using PropertyType = js::PropertyType; -using IndexPropertyType = js::IndexPropertyType; -using StringPropertyType = js::StringPropertyType; - -template -class Wrapper : public fbjsi::HostObject { -public: - template >> - Wrapper(Args&&... args) - : obj(std::forward(args)...) - { - } - - T obj; -}; - -template -inline T& unwrap(Wrapper& wrapper) -{ - return wrapper.obj; -} - -template -inline T& unwrap(const std::shared_ptr>& wrapper) -{ - return unwrap(*wrapper); -} - -template -inline T& unwrap(JsiEnv env, const fbjsi::Object& wrapper) -{ - return unwrap(wrapper.getHostObject>(env)); -} - -template -inline T& unwrap(JsiEnv env, const fbjsi::Value& wrapper) -{ - return unwrap(env, wrapper.asObject(env)); -} - -template -inline T& unwrap(const JsiObj& wrapper) -{ - return unwrap(wrapper.env(), wrapper.get()); -} - -template -inline T& unwrap(const JsiVal& wrapper) -{ - return unwrap(wrapper.env(), wrapper.get()); -} - -template -inline T* unwrapUnique(JsiEnv env, const U& arg) -{ - return unwrap>(env, arg).get(); -} - -template -JsiObj wrap(JsiEnv env, T arg) -{ - return env(fbjsi::Object::createFromHostObject(env, std::make_shared>(std::move(arg)))); -} - -template >> -JsiObj wrap(JsiEnv env, Args&&... args) -{ - return env(fbjsi::Object::createFromHostObject(env, std::make_shared>(std::forward(args)...))); -} - -template -JsiObj wrapUnique(JsiEnv env, T* arg) -{ - return wrap(env, std::unique_ptr(arg)); -} - -template -class ObjectWrap { -public: - using Internal = typename T::Internal; - using ParentClassType = typename T::Parent; - - // NOTE: if this is static, it won't support multiple runtimes. - // Also, may need to suppress destruction. - inline static std::optional s_ctor; - - // Cache the JSI String instance representing the field name of the internal - // C++ object for the lifetime of the current env, as this is a hot path - inline static std::optional s_js_internal_field_name; - - // Cache various objects we fetch from the runtime which are hot paths - // during object creation - inline static std::optional s_proto; - inline static std::optional s_wrapper; - - /** - * @brief callback for invalid access to index setters - * Throws an error when a users attemps to write to an index on a type that - * doesn't support it. - * - * @return nothing; always throws - */ - static fbjsi::Value readonly_index_setter_callback(fbjsi::Runtime& env, const fbjsi::Value& thisVal, - const fbjsi::Value* args, size_t count) - { - throw fbjsi::JSError(env, "Cannot assign to index"); - } - - /** - * @brief callback for invalid access to property setters - * Trows an error when a user attempts to write to a read-only property - * - * @param propname name of the property the user is trying to write to - * @return nothin; always throws - */ - static fbjsi::Value readonly_setter_callback(fbjsi::Runtime& env, const fbjsi::Value& thisVal, - const fbjsi::Value* args, size_t count, std::string const& propname) - { - throw fbjsi::JSError(env, util::format("Cannot assign to read only property '%1'", propname)); - } - - static JsiFunc create_constructor(JsiEnv env) - { - if (s_ctor) { - return *s_ctor; - } - - auto& s_type = get_class(); - - auto nativeFunc = !bool(s_type.constructor) - ? fbjsi::Value() - : fbjsi::Function::createFromHostFunction( - env, propName(env, s_type.name), /* paramCount verified by callback */ 0, - [](fbjsi::Runtime& rt, const fbjsi::Value&, const fbjsi::Value* args, - size_t count) -> fbjsi::Value { - REALM_ASSERT_RELEASE(count >= 1); - auto env = JsiEnv(rt); - auto& s_type = get_class(); - auto arguments = Arguments{env, count - 1, args + 1}; - s_type.constructor(env, env(args[0]).asObject(), arguments); - return fbjsi::Value(); - }); - - s_ctor = env(globalType(env, "Function") - .call(env, "nativeFunc", - util::format(R"( - return function %1(...args) { - // Allow explicit construction only for classes with a constructor - if (new.target && !nativeFunc) { - throw TypeError("Illegal constructor"); - } - if (nativeFunc) - nativeFunc(this, ...args); - - if ('_proxyWrapper' in %1) - return %1._proxyWrapper(this); - })", - s_type.name)) - .asObject(env) - .asFunction(env) - .call(env, std::move(nativeFunc)) - .asObject(env) - .asFunction(env)); - - js::Context::register_invalidator([] { - // Ensure all static references tied to the runtime are destructed when the runtime goes away. - // This is to avoid reassignment and destruction throwing because the runtime has disappeared. - s_ctor.reset(); - s_js_internal_field_name.reset(); - s_proto.reset(); - s_wrapper.reset(); - s_object.reset(); - s_object_create.reset(); - }); - - for (auto&& [name, prop] : s_type.static_properties) { - auto desc = fbjsi::Object(env); - if (prop.getter) { - desc.setProperty(env, "get", funcVal(env, "get_" + name, 0, prop.getter)); - } - if (prop.setter) { - desc.setProperty(env, "set", funcVal(env, "set_" + name, 1, prop.setter)); - } - else { - desc.setProperty( - env, "set", - funcVal(env, "set_" + name, 0, - std::bind(ObjectWrap::readonly_setter_callback, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, name))); - } - defineProperty(env, *s_ctor, name, desc); - } - - for (auto&& [name, method] : s_type.static_methods) { - auto desc = fbjsi::Object(env); - desc.setProperty(env, "value", - funcVal(env, name, /* paramCount must be verified by callback */ 0, method)); - defineProperty(env, *s_ctor, name, desc); - } - - auto proto = (*s_ctor)->getPropertyAsObject(env, "prototype"); - - for (auto&& [name, prop] : s_type.properties) { - auto desc = fbjsi::Object(env); - if (prop.getter) { - desc.setProperty(env, "get", funcVal(env, "get_" + name, 0, prop.getter)); - } - if (prop.setter) { - desc.setProperty(env, "set", funcVal(env, "set_" + name, 1, prop.setter)); - } - else { - desc.setProperty( - env, "set", - funcVal(env, "set_" + name, 0, - std::bind(ObjectWrap::readonly_setter_callback, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, name))); - } - defineProperty(env, proto, name, desc); - } - - for (auto&& [name, method] : s_type.methods) { - auto desc = fbjsi::Object(env); - desc.setProperty(env, "value", - funcVal(env, name, /* paramCount must be verified by callback */ 0, method)); - defineProperty(env, proto, name, desc); - } - - if constexpr (!std::is_void_v) { - REALM_ASSERT_RELEASE(ObjectWrap::s_ctor); - JsiFunc parentCtor = *ObjectWrap::s_ctor; - - auto parentProto = parentCtor->getProperty(env, "prototype"); - if (parentProto.isUndefined()) { - throw std::runtime_error("undefined 'prototype' on parent constructor"); - } - - ObjectSetPrototypeOf(env, fbjsi::Value(env, proto), fbjsi::Value(std::move(parentProto))); - ObjectSetPrototypeOf(env, fbjsi::Value(env, s_ctor->get()), fbjsi::Value(std::move(parentCtor.get()))); - } - - if (s_type.index_accessor) { - // Code below assumes getter is present, and it doesn't make sense to have setter without one. - REALM_ASSERT_RELEASE(s_type.index_accessor.getter); - - // XXX Do we want to trap things like ownKeys() and getOwnPropertyDescriptors() to support for...in? - auto [getter, setter] = s_type.index_accessor; - auto desc = fbjsi::Object(env); - - s_wrapper = - globalType(env, "Function") - .call(env, "getter", "setter", R"( - const integerPattern = /^-?\d+$/; - function getIndex(prop) { - if (typeof prop === "string" && integerPattern.test(prop)) { - return parseInt(prop, 10); - } else { - return Number.NaN; - } - } - const handler = { - ownKeys(target) { - const out = Reflect.ownKeys(target) - const end = target.length - for (let i = 0; i < end; i++) { - out.push(String(i)); - } - return out; - }, - getOwnPropertyDescriptor(target, prop) { - const index = getIndex(prop); - if (Number.isNaN(index)) { - return Reflect.getOwnPropertyDescriptor(...arguments); - } else if (index >= 0 && index < target.length) { - return { - configurable: true, - enumerable: true, - }; - } - }, - get(target, prop, receiver) { - const index = getIndex(prop); - if (Number.isNaN(index)) { - return Reflect.get(...arguments); - } else if (index >= 0 && index < target.length) { - return getter(target, index); - } - }, - set(target, prop, value, receiver) { - const index = getIndex(prop); - if (Number.isNaN(index)) { - return Reflect.set(...arguments); - } else if (index < 0) { - // This mimics realm::js::validated_positive_index - throw new Error(`Index ${index} cannot be less than zero.`); - } else { - return setter(target, index, value); - } - } - } - return (obj) => new Proxy(obj, handler); - )") - .asObject(env) - .asFunction(env) - .call(env, funcVal(env, "getter", 0, getter), - funcVal(env, "setter", 1, setter ? setter : ObjectWrap::readonly_index_setter_callback)) - .asObject(env) - .asFunction(env); - - desc.setProperty(env, "value", *s_wrapper); - defineProperty(env, *s_ctor, "_proxyWrapper", desc); - } - - return *s_ctor; - } - - static JsiObj create_instance(JsiEnv env, Internal* ptr = nullptr) - { - if (REALM_UNLIKELY(!s_proto)) { - s_proto = (*s_ctor)->getPropertyAsObject(env, "prototype"); - } - - auto obj = ObjectCreate(env, *s_proto); - - set_internal(env, obj, ptr); - - if (s_wrapper) { - obj = env((*s_wrapper).call(env, std::move(obj.get()))).asObject(); - } - - return obj; - } - - static JsiObj create_instance_by_schema(JsiEnv env, JsiFunc& constructor, const realm::ObjectSchema& schema, - Internal* internal = nullptr) - { - return create_instance_by_schema(env, &constructor, schema, internal); - } - static JsiObj create_instance_by_schema(JsiEnv env, const realm::ObjectSchema& schema, - Internal* internal = nullptr) - { - return create_instance_by_schema(env, nullptr, schema, internal); - } - - static void on_context_destroy(JsiEnv, std::string realmPath) - { - get_schemaObjectTypes().erase(realmPath); - } - - static bool is_instance(JsiEnv env, JsiObj object) - { - return object->instanceOf(env, *s_ctor); - } - - static Internal* get_internal(JsiEnv env, const JsiObj& object) - { - if (REALM_UNLIKELY(!s_js_internal_field_name)) { - s_js_internal_field_name = fbjsi::String::createFromAscii(env, g_internal_field); - } - - auto internal = object->getProperty(env, *s_js_internal_field_name); - if (internal.isUndefined()) { - // In the case of a user opening a Realm with a class-based model, - // the user defined constructor will get called before the "internal" property has been set. - if constexpr (std::is_same_v>) - return nullptr; - throw fbjsi::JSError(env, "no internal field"); - } - // The following check is disabled to support user defined classes that doesn't extend Realm.Object - // if (!JsiObj(object)->instanceOf(env, *s_ctor)) { - // throw fbjsi::JSError(env, "calling method on wrong type of object"); - // } - return unwrapUnique(env, std::move(internal)); - } - static void set_internal(JsiEnv env, const JsiObj& object, Internal* data) - { - auto desc = fbjsi::Object(env); - desc.setProperty(env, "value", wrapUnique(env, data)); - desc.setProperty(env, "configurable", true); - defineProperty(env, object, g_internal_field, desc); - } - -private: - static fbjsi::Value funcVal(JsiEnv env, const std::string& name, size_t args, fbjsi::HostFunctionType&& func) - { - if (!func) - return fbjsi::Value(); - return fbjsi::Value( - fbjsi::Function::createFromHostFunction(env, propName(env, name), uint32_t(args), std::move(func))); - }; - - static void defineSchemaProperties(JsiEnv env, const fbjsi::Object& constructorPrototype, - const realm::ObjectSchema& schema, bool redefine) - { - // Do the same thing for all computed and persisted properties - auto loopBody = [&](const Property& property) { - const auto& name = property.public_name.empty() ? property.name : property.public_name; - // TODO should this use hasOwnProperty? - if (!redefine && constructorPrototype.hasProperty(env, str(env, name))) { - return; - } - - auto desc = fbjsi::Object(env); - desc.setProperty(env, "enumerable", true); - - desc.setProperty(env, "get", - funcVal(env, "get_" + name, 0, - [name = String(name)](fbjsi::Runtime& rt, const fbjsi::Value& thisVal, - const fbjsi::Value* args, size_t count) { - if (count != 0) - throw fbjsi::JSError(rt, "getters take no arguments"); - return get_class().string_accessor.getter(rt, thisVal, name); - })); - desc.setProperty(env, "set", - funcVal(env, "set_" + name, 1, - [name = String(name)](fbjsi::Runtime& rt, const fbjsi::Value& thisVal, - const fbjsi::Value* args, size_t count) { - if (count != 1) - throw fbjsi::JSError(rt, "setters take exactly 1 argument"); - return get_class().string_accessor.setter(rt, thisVal, name, args[0]); - })); - - defineProperty(env, constructorPrototype, name, desc); - }; - - for (auto&& property : schema.persisted_properties) { - loopBody(property); - } - for (auto&& property : schema.computed_properties) { - loopBody(property); - } - } - - static JsiObj create_instance_by_schema(JsiEnv env, JsiFunc* maybeConstructor, const realm::ObjectSchema& schema, - Internal* internal = nullptr) - { - auto& s_schemaObjectTypes = get_schemaObjectTypes(); - auto& s_class = get_class(); - - bool isRealmObjectClass = std::is_same_v>; - if (!isRealmObjectClass) { - throw fbjsi::JSError(env, "Creating instances by schema is supported for RealmObjectClass only"); - } - - if (!internal) { - throw fbjsi::JSError( - env, "RealmObjectClass requires an internal realm object when creating instances by schema"); - } - - REALM_ASSERT_RELEASE(!s_class.index_accessor); // assume we don't need a ProxyWrapper - - auto config = internal->realm()->config(); - std::string path = config.path; - auto version = internal->realm()->schema_version(); - std::string schemaName = schema.name + ":" + std::to_string(version); - - const JsiFunc& realmObjectClassConstructor = *ObjectWrap::s_ctor; - - auto& schemaObjects = s_schemaObjectTypes[path]; - - // jsi::Symbol externalSymbol = ExternalSymbol; - - // if we are creating a RealmObject from schema with no user defined constructor - if (!maybeConstructor) { - // 1.Check by name if the constructor is already created for this RealmObject - if (!schemaObjects.count(schemaName)) { - // 2.Create the constructor - // create an anonymous RealmObject function - auto schemaObjectConstructor = globalType(env, "Function") - .callAsConstructor(env, "return function () {}") - .asObject(env) - .asFunction(env) - .call(env) - .asObject(env) - .asFunction(env); - - - auto schemaProto = schemaObjectConstructor.getProperty(env, "prototype"); - ObjectSetPrototypeOf(env, schemaProto, realmObjectClassConstructor->getProperty(env, "prototype")); - ObjectSetPrototypeOf(env, JsiVal(env(schemaObjectConstructor)), JsiVal(realmObjectClassConstructor)); - - defineSchemaProperties(env, std::move(schemaProto).asObject(env), schema, true); - - schemaObjects.emplace(schemaName, std::move(schemaObjectConstructor)); - } - } - else { - // creating a RealmObject with user defined constructor - auto& constructor = *maybeConstructor; - - bool schemaExists = schemaObjects.count(schemaName); - if (schemaExists) { - // check if constructors have changed for the same schema object and name - if (!fbjsi::Function::strictEquals(env, schemaObjects.at(schemaName), constructor)) { - schemaExists = false; - schemaObjects.erase(schemaName); - } - } - - if (!schemaExists) { - schemaObjects.emplace(schemaName, JsiFunc(constructor).get()); - auto constructorPrototype = constructor->getPropertyAsObject(env, "prototype"); - - // get all properties from the schema - defineSchemaProperties(env, env(constructorPrototype), schema, false); - } - } - - const auto& schemaObjectCtor = schemaObjects.at(schemaName); - auto constructorPrototype = schemaObjectCtor.getPropertyAsObject(env, "prototype"); - auto instance = ObjectCreate(env, constructorPrototype); - set_internal(env, instance, internal); - return instance; - } - - static auto& get_class() - { - // TODO this is silly. These should be static properties. - static T s_class; - return s_class; - } - - inline static auto& get_schemaObjectTypes() - { - // NOTE: this being static prevents using multiple runtimes. - static std::unordered_map> s_schemaObjectTypes; - return s_schemaObjectTypes; - } -}; - -} // namespace realmjsi - -template -class ObjectWrap : public realm::js::realmjsi::ObjectWrap {}; - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const fbjsi::Value* args, size_t count) -{ - auto env = JsiEnv(rt); - auto result = realmjsi::ReturnValue(env); - auto arguments = realmjsi::Arguments{env, count, args}; - - F(env, env(thisVal).asObject(), arguments, result); - return std::move(result).ToValue(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const fbjsi::Value* args, size_t count) -{ - auto env = JsiEnv(rt); - auto result = realmjsi::ReturnValue(env); - auto arguments = realmjsi::Arguments{env, count, args}; - arguments.validate_count(0); - - F(env, env(thisVal).asObject(), result); - return std::move(result).ToValue(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const fbjsi::Value* args, size_t count) -{ - auto env = JsiEnv(rt); - auto arguments = realmjsi::Arguments{env, count, args}; - arguments.validate_count(1); - - F(env, env(thisVal).asObject(), JsiVal(env, args[0])); - - return fbjsi::Value(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value&, const fbjsi::Value* args, size_t count) -{ - REALM_ASSERT_RELEASE(count == 2); - auto env = JsiEnv(rt); - auto out = realmjsi::ReturnValue(env); - F(env, env(args[0]).asObject(), uint32_t(args[1].asNumber()), out); - return std::move(out).ToValue(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value&, const fbjsi::Value* args, size_t count) -{ - REALM_ASSERT_RELEASE(count == 3); - auto env = JsiEnv(rt); - return fbjsi::Value(F(env, env(args[0]).asObject(), uint32_t(args[1].asNumber()), env(args[2]))); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const realmjsi::String& str) -{ - auto env = JsiEnv(rt); - auto result = realmjsi::ReturnValue(env); - F(env, env(thisVal).asObject(), str, result); - return std::move(result).ToValue(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const realmjsi::String& str, - const fbjsi::Value& value) -{ - auto env = JsiEnv(rt); - F(env, env(thisVal).asObject(), str, env(value)); - return fbjsi::Value(); -} - -template -fbjsi::Value wrap(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, const fbjsi::Value* args, size_t count) -{ - // This is only used in the JSC impl. - REALM_UNREACHABLE(); -} - -} // namespace realm::js diff --git a/packages/realm/bindgen/src/jsi/jsi_function.hpp b/packages/realm/bindgen/src/jsi/jsi_function.hpp deleted file mode 100644 index e1ecd4093cb..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_function.hpp +++ /dev/null @@ -1,67 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_types.hpp" -#include "jsi_externs.hpp" - -namespace realm { -namespace js { - -template -inline T flush_and_return(T&& val) -{ - flush_ui_queue(); - return val; -} - -template <> -inline JsiVal realmjsi::Function::call(JsiEnv env, const JsiFunc& function, size_t argc, const JsiVal arguments[]) -{ - return flush_and_return(env(function->call(env, env.args(arguments, argc), argc))); -} - -template <> -inline JsiVal realmjsi::Function::call(JsiEnv env, const JsiFunc& function, const JsiObj& this_object, size_t argc, - const JsiVal arguments[]) -{ - return flush_and_return(env(function->callWithThis(env, this_object, env.args(arguments, argc), argc))); -} - -template <> -inline JsiVal realmjsi::Function::callback(JsiEnv env, const JsiFunc& function, size_t argc, const JsiVal arguments[]) -{ - return flush_and_return(env(function->call(env, env.args(arguments, argc), argc))); -} -template <> -inline JsiVal realmjsi::Function::callback(JsiEnv env, const JsiFunc& function, const JsiObj& this_object, - size_t argc, const JsiVal arguments[]) -{ - return flush_and_return(env(function->callWithThis(env, this_object, env.args(arguments, argc), argc))); -} - -template <> -inline JsiObj realmjsi::Function::construct(JsiEnv env, const JsiFunc& function, size_t argc, - const JsiVal arguments[]) -{ - return flush_and_return(env(function->callAsConstructor(env, env.args(arguments, argc), argc).asObject(env))); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/jsi/jsi_init.hpp b/packages/realm/bindgen/src/jsi/jsi_init.hpp deleted file mode 100644 index 28e787359d9..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_init.hpp +++ /dev/null @@ -1,30 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_string.hpp" -#include "jsi_protected.hpp" -#include "jsi_function.hpp" -#include "jsi_value.hpp" -#include "jsi_object.hpp" -#include "jsi_return_value.hpp" -#include "jsi_class.hpp" - -// FIXME: js_object_accessor.hpp includes js_list.hpp which includes js_object_accessor.hpp. -#include "js_object_accessor.hpp" diff --git a/packages/realm/bindgen/src/jsi/jsi_object.hpp b/packages/realm/bindgen/src/jsi/jsi_object.hpp deleted file mode 100644 index 13476fc3803..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_object.hpp +++ /dev/null @@ -1,227 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_types.hpp" -#include "jsi_string.hpp" - -namespace realm { -namespace js { - -namespace realmjsi { -// forward-declare JSI ObjectWrap from jsi_class.hpp -template -class ObjectWrap; -} // namespace realmjsi - -namespace fbjsi = facebook::jsi; - -#if 0 -inline napi_property_attributes operator|(napi_property_attributes a, PropertyAttributes b) { - int flag = napi_default; - - if ((b & DontEnum) != DontEnum) { - flag |= napi_enumerable; - } - - if ((b & DontDelete) != DontDelete) { - flag |= napi_configurable; - } - - if ((b & ReadOnly) != ReadOnly) { - flag |= napi_writable; - } - - napi_property_attributes napi_flag = static_cast(a | flag); - return napi_flag; -} -#endif - -template <> -inline JsiVal realmjsi::Object::get_property(JsiEnv env, const JsiObj& object, StringData key) -{ - return env(object->getProperty(env, propName(env, key))); -} - -template <> -inline JsiVal realmjsi::Object::get_property(JsiEnv env, const JsiObj& object, const realmjsi::String& key) -{ - return env(object->getProperty(env, propName(env, key))); -} - -template <> -inline JsiVal realmjsi::Object::get_property(JsiEnv env, const JsiObj& object, uint32_t index) -{ - if (object->isArray(env)) - return env(object->asArray(env).getValueAtIndex(env, index)); - return realmjsi::Object::get_property(env, object, std::to_string(index)); -} - -template <> -inline void realmjsi::Object::set_property(JsiEnv env, JsiObj& object, const realmjsi::String& key, - const JsiVal& value, PropertyAttributes attributes) -{ - if (attributes) { - auto desc = fbjsi::Object(env); - desc.setProperty(env, "configurable", !(attributes & DontDelete)); - desc.setProperty(env, "enumerable", !(attributes & DontEnum)); - desc.setProperty(env, "writable", !(attributes & ReadOnly)); - desc.setProperty(env, "value", value); - - auto objClass = env->global().getPropertyAsObject(env, "Object"); - objClass.getPropertyAsFunction(env, "defineProperty") - .callWithThis(env, objClass, object, str(env, key), std::move(desc)); - } - else { - object->setProperty(env, propName(env, key), value); - } -} - -template <> -inline void realmjsi::Object::set_property(JsiEnv env, JsiObj& object, uint32_t index, const JsiVal& value) -{ - if (object->isArray(env)) - return object->asArray(env).setValueAtIndex(env, index, value); - return realmjsi::Object::set_property(env, object, std::to_string(index), value); -} - -template <> -inline std::vector realmjsi::Object::get_property_names(JsiEnv env, const JsiObj& object) -{ - auto namesArray = object->getPropertyNames(env); - - size_t count = namesArray.length(env); - std::vector names; - names.reserve(count); - - for (size_t i = 0; i < count; i++) { - names.push_back(namesArray.getValueAtIndex(env, i).asString(env).utf8(env)); - } - - return names; -} - -template <> -inline JsiVal realmjsi::Object::get_prototype(JsiEnv env, const JsiObj& object) -{ - auto objClass = env->global().getPropertyAsObject(env, "Object"); - return env(objClass.getPropertyAsFunction(env, "getPrototypeOf").callWithThis(env, objClass, object)); -} - -template <> -inline void realmjsi::Object::set_prototype(JsiEnv env, const JsiObj& object, const JsiVal& prototype) -{ - auto objClass = env->global().getPropertyAsObject(env, "Object"); - objClass.getPropertyAsFunction(env, "setPrototypeOf").callWithThis(env, objClass, object, prototype); -} - -template <> -inline JsiObj realmjsi::Object::create_empty(JsiEnv env) -{ - return JsiObj(env); -} - -template <> -inline JsiObj realmjsi::Object::create_array(JsiEnv env, uint32_t length, const JsiVal values[]) -{ - fbjsi::Array array = fbjsi::Array(env, length); - for (uint32_t i = 0; i < length; i++) { - array.setValueAtIndex(env, i, values[i]); - } - return env(std::move(array)); -} - -template <> -inline JsiObj realmjsi::Object::create_date(JsiEnv env, double time) -{ - return env(env->global().getPropertyAsFunction(env, "Date").callAsConstructor(env, time).asObject(env)); -} - -template <> -template -inline JsiObj realmjsi::Object::create_instance(JsiEnv env, typename ClassType::Internal* internal) -{ - return realmjsi::ObjectWrap::create_instance(env, internal); -} - -template <> -template -inline JsiObj realmjsi::Object::create_instance_by_schema(JsiEnv env, JsiFunc& constructor, - const realm::ObjectSchema& schema, - typename ClassType::Internal* internal) -{ - return realmjsi::ObjectWrap::create_instance_by_schema(env, constructor, schema, internal); -} - -template <> -template -inline JsiObj realmjsi::Object::create_instance_by_schema(JsiEnv env, const realm::ObjectSchema& schema, - typename ClassType::Internal* internal) -{ - return realmjsi::ObjectWrap::create_instance_by_schema(env, schema, internal); -} - -template -inline void on_context_destroy(JsiEnv env, std::string realmPath) -{ - realmjsi::ObjectWrap::on_context_destroy(env, realmPath); -} - -template <> -template -inline bool realmjsi::Object::is_instance(JsiEnv env, const JsiObj& object) -{ - return realmjsi::ObjectWrap::is_instance(env, object); -} - -template <> -template -inline typename ClassType::Internal* realmjsi::Object::get_internal(JsiEnv env, const JsiObj& object) -{ - return realmjsi::ObjectWrap::get_internal(env, object); -} - -template <> -template -inline void realmjsi::Object::set_internal(JsiEnv env, JsiObj& object, typename ClassType::Internal* internal) -{ - return realmjsi::ObjectWrap::set_internal(env, object, internal); -} - -template <> -inline void realmjsi::Object::set_global(JsiEnv env, const realmjsi::String& key, const JsiVal& value) -{ - auto global = env.global(); - Object::set_property(env, global, key, value); -} - -template <> -inline JsiVal realmjsi::Object::get_global(JsiEnv env, const realmjsi::String& key) -{ - return Object::get_property(env, env.global(), key); -} - -template <> -inline JsiVal realmjsi::Exception::value(JsiEnv env, const std::string& message) -{ - return str(env, message); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/jsi/jsi_protected.hpp b/packages/realm/bindgen/src/jsi/jsi_protected.hpp deleted file mode 100644 index 255e35ab3f4..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_protected.hpp +++ /dev/null @@ -1,87 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2016 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_types.hpp" - -namespace realm { -namespace js { - -template <> -class Protected : public JsiVal { -public: - Protected(JsiEnv, JsiVal value) - : JsiVal(std::move(value)) - { - } - struct Comparator { - bool operator()(const Protected& a, const Protected& b) const - { - return a == b; - } - }; -}; - -template <> -class Protected : public JsiObj { -public: - Protected(JsiEnv, JsiObj value) - : JsiObj(std::move(value)) - { - } - struct Comparator { - bool operator()(const Protected& a, const Protected& b) const - { - return a == b; - } - }; -}; - -template <> -class Protected : public JsiFunc { -public: - Protected(JsiEnv, JsiFunc value) - : JsiFunc(std::move(value)) - { - } - struct Comparator { - bool operator()(const Protected& a, const Protected& b) const - { - return a == b; - } - }; -}; - -template <> -class Protected : public JsiEnv { -public: - Protected(JsiEnv env) - : JsiEnv(env) - { - } - struct Comparator { - bool operator()(const Protected& a, const Protected& b) const - { - return a == b; - } - }; -}; - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/jsi/jsi_return_value.hpp b/packages/realm/bindgen/src/jsi/jsi_return_value.hpp deleted file mode 100644 index ae99fe8009a..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_return_value.hpp +++ /dev/null @@ -1,131 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include - -#include "jsi_types.hpp" -#include "jsi_string.hpp" - -namespace realm { -namespace js { - -namespace fbjsi = facebook::jsi; - -template <> -class ReturnValue { - JsiEnv m_env; - fbjsi::Value m_value; // defaults to undefined - -public: - ReturnValue(JsiEnv env) - : m_env(env) - { - } - ReturnValue(JsiEnv env, fbjsi::Value&& value) - : m_env(env) - , m_value(std::move(value)) - { - } - ReturnValue(JsiEnv env, const fbjsi::Value& value) - : m_env(env) - , m_value(env, value) - { - } - - fbjsi::Value ToValue() && - { - return std::move(m_value); - } - - void set(JsiVal value) - { - m_value = std::move(value.get()); - } - - void set(const std::string& string) - { - m_value = str(m_env, string).get(); - } - - void set(const char* c_str) - { - if (!c_str) { - set_null(); - } - else { - m_value = str(m_env, c_str).get(); - } - } - - void set(bool boolean) - { - m_value = fbjsi::Value(boolean); - } - - void set(double number) - { - m_value = fbjsi::Value(number); - } - - void set(int32_t number) - { - set(double(number)); - } - - void set(uint32_t number) - { - set(double(number)); - } - - void set(realm::Mixed mixed) - { - m_value = js::Value::from_mixed(m_env, nullptr, mixed).get(); - } - - void set_null() - { - m_value = fbjsi::Value::null(); - } - - - void set_undefined() - { - m_value = fbjsi::Value::undefined(); - } - - template - void set(const std::optional& value) - { - if (value) { - set(std::move(*value)); - } - else { - set_undefined(); - } - } - - operator JsiVal() const - { - return m_env(m_value); - } -}; - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/jsi/jsi_string.hpp b/packages/realm/bindgen/src/jsi/jsi_string.hpp deleted file mode 100644 index fa5763b4385..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_string.hpp +++ /dev/null @@ -1,93 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_types.hpp" - -namespace realm { -namespace js { - -namespace fbjsi = facebook::jsi; - -template <> -class String { - using StringType = String; - - std::string m_str; - -public: - static bson::Bson to_bson(StringType stringified_ejson) - { - return bson::parse(std::string(std::move(stringified_ejson))); - } - - static std::string from_bson(const bson::Bson& bson) - { - return bson.to_string(); - } - - String(StringData s) - : m_str(s) - { - } - String(std::string&& s) - : m_str(std::move(s)) - { - } - String(const std::string& s) - : m_str(s) - { - } - String(const char* s) - : m_str(s) - { - } - - operator StringData() const - { - return m_str; - } - - operator std::string() && - { - return std::move(m_str); - } - operator std::string() const& - { - return m_str; - } - - fbjsi::String ToString(fbjsi::Runtime* env) - { - return fbjsi::String::createFromUtf8(*env, m_str); - } -}; - -inline fbjsi::PropNameID propName(JsiEnv env, StringData name) -{ - return fbjsi::PropNameID::forUtf8(env, reinterpret_cast(name.data()), name.size()); -} - -inline JsiString str(JsiEnv env, StringData name) -{ - return env(fbjsi::String::createFromUtf8(env, reinterpret_cast(name.data()), name.size())); -} - -} // namespace js -} // namespace realm diff --git a/packages/realm/bindgen/src/jsi/jsi_types.hpp b/packages/realm/bindgen/src/jsi/jsi_types.hpp deleted file mode 100644 index 4df20cd4496..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_types.hpp +++ /dev/null @@ -1,401 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include -#include -#include -#include - -#include - -#define HANDLESCOPE(env) ::facebook::jsi::Scope handle_scope(env); - -#include "js_types.hpp" - - -namespace realm { - -namespace fbjsi = facebook::jsi; - -namespace js { -class JsiVal; -class JsiObj; -class JsiString; -class JsiFunc; - -class JsiEnv { -public: - /*implicit*/ JsiEnv(fbjsi::Runtime& rt) - : m_rt(&rt) - { - } - /*implicit*/ operator fbjsi::Runtime&() const - { - return *m_rt; - } - - fbjsi::Runtime* operator->() const - { - return m_rt; - } - fbjsi::Runtime& get() const - { - return *m_rt; - } - - JsiVal operator()(const fbjsi::Value&) const; - JsiVal operator()(fbjsi::Value&&) const; - JsiObj operator()(const fbjsi::Object&) const; - JsiObj operator()(fbjsi::Object&&) const; - JsiString operator()(const fbjsi::String&) const; - JsiString operator()(fbjsi::String&&) const; - JsiFunc operator()(const fbjsi::Function&) const; - JsiFunc operator()(fbjsi::Function&&) const; - - JsiVal null() const; - JsiVal undefined() const; - - JsiObj global() const; - - /** Warning, this can only appear directly as an argument to callFoo(), not assigned to a variable! */ - const fbjsi::Value* args(const JsiVal* argv, size_t argc, std::vector&& buf = {}) const; - - friend bool operator==(const JsiEnv& a, const JsiEnv& b) - { - return a.m_rt == b.m_rt; - } - - template - JsiObj obj(std::pair... pairs); - -private: - fbjsi::Runtime* m_rt; -}; - -template -class JsiWrap { -public: - JsiWrap(JsiEnv env, T&& val) - : m_env(env) - , m_val(std::move(val)) - { - } - - JsiWrap(JsiWrap&& other) = default; - JsiWrap& operator=(JsiWrap&& other) = default; - - JsiWrap(const JsiWrap& other) - : JsiWrap(CRTP(other.env(), other.get())) - { - } - JsiWrap& operator=(const JsiWrap& other) - { - *this = JsiWrap(other); - } - - T* operator->() - { - return &m_val; - } - const T* operator->() const - { - return &m_val; - } - const T* operator*() const - { - return &m_val; - } - - /*implicit*/ operator const T&() const& - { - return m_val; - } - /*implicit*/ operator T&() & - { - return m_val; - } - /*implicit*/ operator T&&() && - { - return std::move(m_val); - } - - const T& get() const& - { - return m_val; - } - T& get() & - { - return m_val; - } - T&& get() && - { - return std::move(m_val); - } - - const JsiEnv& env() const - { - return m_env; - } - - friend bool operator==(const JsiWrap& a, const JsiWrap& b) - { - REALM_ASSERT_RELEASE(&a.env().get() == &b.env().get()); - return T::strictEquals(a.env(), a.get(), b.get()); - } - -protected: - JsiEnv m_env; - T m_val; -}; - -class JsiString : public JsiWrap { -public: - using JsiWrap::JsiWrap; - JsiString(JsiEnv env, const fbjsi::String& val) - : JsiWrap(env, fbjsi::Value(env, val).getString(env)) - { - } -}; - -class JsiFunc : public JsiWrap { -public: - using JsiWrap::JsiWrap; - JsiFunc(JsiEnv env, const fbjsi::Function& val) - : JsiWrap(env, fbjsi::Value(env, val).getObject(env).getFunction(env)) - { - } -}; - -class JsiObj : public JsiWrap { -public: - using JsiWrap::JsiWrap; - JsiObj(JsiEnv env, const fbjsi::Object& val) - : JsiWrap(env, fbjsi::Value(env, val).getObject(env)) - { - } - /*implicit*/ JsiObj(JsiFunc f) - : JsiWrap(f.env(), std::move(f).get()) - { - } - explicit JsiObj(JsiEnv env) - : JsiWrap(env, fbjsi::Object(env)) - { - } - - bool operator==(const JsiObj& other) const - { - return static_cast(*this) == static_cast(other); - } - - bool operator!=(const JsiObj& other) const - { - return !(*this == other); - } -}; - -class JsiVal : public JsiWrap { -public: - using JsiWrap::JsiWrap; - JsiVal(JsiEnv env, const fbjsi::Value& val) - : JsiWrap(env, fbjsi::Value(env, val)) - { - } - /*implicit*/ JsiVal(JsiString val) - : JsiWrap(val.env(), std::move(val).get()) - { - } - /*implicit*/ JsiVal(JsiFunc val) - : JsiWrap(val.env(), std::move(val).get()) - { - } - /*implicit*/ JsiVal(JsiObj val) - : JsiWrap(val.env(), std::move(val).get()) - { - } - - JsiObj asObject() const& - { - return {env(), get().asObject(env())}; - } - JsiObj asObject() && - { - return {env(), std::move(get()).asObject(env())}; - } -}; - -inline JsiVal JsiEnv::operator()(const fbjsi::Value& val) const -{ - return {*this, val}; -} -inline JsiVal JsiEnv::operator()(fbjsi::Value&& val) const -{ - return {*this, std::move(val)}; -} -inline JsiObj JsiEnv::operator()(const fbjsi::Object& val) const -{ - return {*this, val}; -} -inline JsiObj JsiEnv::operator()(fbjsi::Object&& val) const -{ - return {*this, std::move(val)}; -} -inline JsiString JsiEnv::operator()(const fbjsi::String& val) const -{ - return {*this, val}; -} -inline JsiString JsiEnv::operator()(fbjsi::String&& val) const -{ - return {*this, std::move(val)}; -} -inline JsiFunc JsiEnv::operator()(const fbjsi::Function& val) const -{ - return {*this, val}; -} -inline JsiFunc JsiEnv::operator()(fbjsi::Function&& val) const -{ - return {*this, std::move(val)}; -} - -inline JsiVal JsiEnv::null() const -{ - return {*this, fbjsi::Value::null()}; -} -inline JsiVal JsiEnv::undefined() const -{ - return {*this, fbjsi::Value::undefined()}; -} -inline JsiObj JsiEnv::global() const -{ - return {*this, m_rt->global()}; -} -inline const fbjsi::Value* JsiEnv::args(const JsiVal* argv, size_t argc, std::vector&& buf) const -{ - // Special case for 0 or 1 arguments to avoid any copies and allocations. - if (argc == 0) - return nullptr; - if (argc == 1) - return &argv[0].get(); - - buf.reserve(argc); - for (size_t i = 0; i < argc; i++) { - buf.emplace_back(*this, argv[i]); - } - - return buf.data(); -} - -template -JsiObj JsiEnv::obj(std::pair... pairs) -{ - auto obj = fbjsi::Object(*this); - (..., obj.setProperty(*this, pairs.first, std::move(pairs.second))); - return (*this)(std::move(obj)); -} - -namespace realmjsi { -struct Types { - using Context = JsiEnv; - using GlobalContext = JsiEnv; - using Value = JsiVal; - using Object = JsiObj; - using String = JsiString; - using Function = JsiFunc; - - using JsiFunctionCallback = fbjsi::Value (*)(fbjsi::Runtime& rt, const fbjsi::Value& thisVal, - const fbjsi::Value* args, size_t count); - - using JsiIndexGetterCallback = JsiFunctionCallback; - using JsiIndexSetterCallback = JsiFunctionCallback; - using JsiPropertyGetterCallback = JsiFunctionCallback; - using JsiPropertySetterCallback = JsiFunctionCallback; - - using JsiStringPropertyGetterCallback = JsiFunctionCallback; - using JsiStringPropertySetterCallback = JsiFunctionCallback; - using JsiStringPropertyEnumeratorCallback = JsiFunctionCallback; - - using ConstructorCallback = JsiFunctionCallback; - using FunctionCallback = JsiFunctionCallback; - using PropertyGetterCallback = JsiPropertyGetterCallback; - using PropertySetterCallback = JsiPropertySetterCallback; - using IndexPropertyGetterCallback = JsiIndexGetterCallback; - using IndexPropertySetterCallback = JsiIndexSetterCallback; - - using StringPropertyGetterCallback = fbjsi::Value (*)(fbjsi::Runtime&, const fbjsi::Value&, - const js::String&); - using StringPropertySetterCallback = fbjsi::Value (*)(fbjsi::Runtime&, const fbjsi::Value&, - const js::String&, const fbjsi::Value&); - using StringPropertyEnumeratorCallback = JsiStringPropertyEnumeratorCallback; -}; // struct Types - -template -class ObjectWrap; - -using String = js::String; -using Context = js::Context; -using Value = js::Value; -using Function = js::Function; -using Object = js::Object; -using Exception = js::Exception; -using ReturnValue = js::ReturnValue; - -} // namespace realmjsi - - -template <> -inline realmjsi::Types::Context realmjsi::Context::get_global_context(realmjsi::Types::Context env) -{ - return env; -} - -inline fbjsi::Function globalType(fbjsi::Runtime& env, const char* name) -{ - return env.global().getPropertyAsFunction(env, name); -} - -} // namespace js -} // namespace realm - -// A bit of a hack, but important for usability. -namespace facebook { -namespace jsi { -namespace detail { -template <> -inline Value toValue(Runtime&, const realm::js::JsiVal& val) -{ - return realm::js::JsiVal(val).get(); -} -template <> -inline Value toValue(Runtime&, const realm::js::JsiObj& val) -{ - return realm::js::JsiVal(val).get(); -} -template <> -inline Value toValue(Runtime&, const realm::js::JsiFunc& val) -{ - return realm::js::JsiVal(val).get(); -} -template <> -inline Value toValue(Runtime&, const realm::js::JsiString& val) -{ - return realm::js::JsiVal(val).get(); -} -} // namespace detail -} // namespace jsi -} // namespace facebook diff --git a/packages/realm/bindgen/src/jsi/jsi_value.hpp b/packages/realm/bindgen/src/jsi/jsi_value.hpp deleted file mode 100644 index f5f5a77ce6a..00000000000 --- a/packages/realm/bindgen/src/jsi/jsi_value.hpp +++ /dev/null @@ -1,429 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - -#pragma once - -#include "jsi_string.hpp" -#include "jsi_types.hpp" -#include "realm/util/to_string.hpp" - -namespace realm { -namespace js { - -namespace fbjsi = facebook::jsi; - -template <> -inline const char* realmjsi::Value::typeof(JsiEnv env, const JsiVal& value) -{ - if (value->isNull()) { - return "null"; - } - if (value->isNumber()) { - return "number"; - } - if (value->isString()) { - return "string"; - } - if (value->isBool()) { - return "boolean"; - } - if (value->isUndefined()) { - return "undefined"; - } - if (value->isObject()) { - return "object"; - } - return "unknown"; -} - -template <> -inline bool realmjsi::Value::is_array(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && value->getObject(env).isArray(env); -} - -template <> -inline bool realmjsi::Value::is_array_buffer(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && value->getObject(env).isArrayBuffer(env); -} - -template <> -inline bool realmjsi::Value::is_array_buffer_view(JsiEnv env, const JsiVal& value) -{ - return globalType(env, "ArrayBuffer").getPropertyAsFunction(env, "isView").call(env, value).getBool(); -} - -template <> -inline bool realmjsi::Value::is_date(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && - value->getObject(env).instanceOf(env, env->global().getPropertyAsFunction(env, "Date")); -} - -template <> -inline bool realmjsi::Value::is_boolean(JsiEnv env, const JsiVal& value) -{ - return value->isBool(); -} - -template <> -inline bool realmjsi::Value::is_constructor(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && value->getObject(env).isFunction(env); -} - -template <> -inline bool realmjsi::Value::is_error(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && - value->getObject(env).instanceOf(env, env->global().getPropertyAsFunction(env, "Error")); -} - -template <> -inline bool realmjsi::Value::is_function(JsiEnv env, const JsiVal& value) -{ - return value->isObject() && value->getObject(env).isFunction(env); -} - -template <> -inline bool realmjsi::Value::is_null(JsiEnv env, const JsiVal& value) -{ - return value->isNull(); -} - -template <> -inline bool realmjsi::Value::is_number(JsiEnv env, const JsiVal& value) -{ - return value->isNumber(); -} - -inline bool is_bson_type(JsiEnv env, const JsiVal& value, std::string type) -{ - if (value->isNull() || value->isUndefined() || !value->isObject()) { - return false; - } - - auto bsonType = value->getObject(env).getProperty(env, "_bsontype"); - if (bsonType.isUndefined()) { - return false; - } - - return fbjsi::Value::strictEquals(env, bsonType, JsiVal(str(env, type))); -} - -template <> -inline bool realmjsi::Value::is_decimal128(JsiEnv env, const JsiVal& value) -{ - return is_bson_type(env, value, "Decimal128"); -} - -template <> -inline bool realmjsi::Value::is_object_id(JsiEnv env, const JsiVal& value) -{ - return is_bson_type(env, value, "ObjectID"); -} - -template <> -inline bool realmjsi::Value::is_object(JsiEnv env, const JsiVal& value) -{ - return value->isObject(); -} - -template <> -inline bool realmjsi::Value::is_string(JsiEnv env, const JsiVal& value) -{ - return value->isString(); -} - -template <> -inline bool realmjsi::Value::is_undefined(JsiEnv env, const JsiVal& value) -{ - return value->isUndefined(); -} - -template <> -inline bool realmjsi::Value::is_binary(JsiEnv env, const JsiVal& value) -{ - return Value::is_array_buffer(env, value) || Value::is_array_buffer_view(env, value); -} - -template <> -inline bool realmjsi::Value::is_valid(const JsiVal& value) -{ - return (*value) != nullptr; -} - -template <> -inline bool realmjsi::Value::is_uuid(JsiEnv env, const JsiVal& value) -{ - return is_bson_type(env, value, "UUID"); -} - -template <> -inline JsiVal realmjsi::Value::from_boolean(JsiEnv env, bool boolean) -{ - return JsiVal(env, boolean); -} - -template <> -inline JsiVal realmjsi::Value::from_null(JsiEnv env) -{ - return env.null(); -} - -template <> -inline JsiVal realmjsi::Value::from_number(JsiEnv env, double number) -{ - return JsiVal(env, number); -} - -template <> -inline JsiVal realmjsi::Value::from_nonnull_string(JsiEnv env, const realmjsi::String& string) -{ - return str(env, StringData(string)); -} - -template <> -inline JsiVal realmjsi::Value::from_nonnull_binary(JsiEnv env, BinaryData data) -{ - fbjsi::ArrayBuffer buffer = - globalType(env, "ArrayBuffer").callAsConstructor(env, double(data.size())).getObject(env).getArrayBuffer(env); - - if (data.size()) { - memcpy(buffer.data(env), data.data(), data.size()); - } - - return env(std::move(buffer)); -} - -template <> -inline JsiVal realmjsi::Value::from_undefined(JsiEnv env) -{ - return env.undefined(); -} - -template <> -inline JsiVal realmjsi::Value::from_uuid(JsiEnv env, const UUID& uuid) -{ - return env(globalType(env, "Realm") - .getPropertyAsFunction(env, "_UUID") - .callAsConstructor(env, str(env, uuid.to_string()))); -} - -template <> -inline bool realmjsi::Value::to_boolean(JsiEnv env, const JsiVal& value) -{ - if (value->isBool()) { - return value->getBool(); - } - - // boolean conversions as specified by - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean ... - - // trivial conversions to false - if (value->isUndefined() || value->isNull()) { - return false; - } - - if (value->isObject()) { - // not null, as checked above - return true; - } - - if (value->isString()) { - // only the empty string is false - return value->toString(env).utf8(env) != ""; - } - - if (value->isNumber()) { - double const dblval = value->asNumber(); - if (std::isnan(dblval)) { - return false; - } - - std::string const stringval = value->toString(env).utf8(env); - return (stringval != "0" && stringval != "-0"); - } - - throw fbjsi::JSError(env, - util::format("TypeError: cannot convert type %1 to boolean", Value::typeof(env, value))); -} - -template <> -inline realmjsi::String realmjsi::Value::to_string(JsiEnv env, const JsiVal& value) -{ - return value->toString(env).utf8(env); -} - -template <> -inline double realmjsi::Value::to_number(JsiEnv env, const JsiVal& value) -{ - double number = std::nan(""); - if (value->isNumber()) { - number = value->asNumber(); - } - else if (value->isString()) { - std::string string = value->toString(env).utf8(env); - try { - number = std::stod(string); - } - catch (std::invalid_argument) { - // The number will remain nan and we defer to the check below to throw an exception. - } - } - else if (is_date(env, value)) { - fbjsi::Object date = value->getObject(env); - number = date.getPropertyAsFunction(env, "getTime").callWithThis(env, date).getNumber(); - } - if (std::isnan(number)) { - throw std::invalid_argument( - util::format("Value '%1' not convertible to a number.", (std::string)to_string(env, value))); - } - - return number; -} - -template <> -inline OwnedBinaryData realmjsi::Value::to_binary_impl(JsiEnv env, const JsiVal& value) -{ - auto obj = value->asObject(env); - if (obj.isArrayBuffer(env)) { - auto buf = std::move(obj).getArrayBuffer(env); - return OwnedBinaryData(reinterpret_cast(buf.data(env)), buf.length(env)); - } - - if (is_array_buffer_view(env, value)) { - auto buffer = obj.getPropertyAsObject(env, "buffer").getArrayBuffer(env); - size_t byteOffset = static_cast(obj.getProperty(env, "byteOffset").asNumber()); - size_t byteLength = static_cast(obj.getProperty(env, "byteLength").asNumber()); - return OwnedBinaryData(reinterpret_cast(buffer.data(env) + byteOffset), byteLength); - } - - throw std::runtime_error("Can only convert ArrayBuffer and ArrayBufferView objects to binary"); -} - -/** - * @brief convert a JSI value to an object - * Will try to convert a given value to a JavaScript object according to - * https://tc39.es/ecma262/#sec-toobject. Most primitive types will be wrapped - * in their corresponding object types (e.g., string -> String). - * - * @param env JSI runtime environment - * @param value JSI value that will be converted to object - * @return JsiObj - */ -template <> -inline JsiObj realmjsi::Value::to_object(JsiEnv env, JsiVal const& value) -{ - if (value->isObject()) { - return env(value->asObject(env)); - } - - // trivial non-conversions - if (value->isNull() || value->isUndefined()) { - throw fbjsi::JSError(env, util::format("TypeError: cannot convert '%1' to object", - realmjsi::Value::typeof(env, value))); // throw TypeError - } - - // use JavaScript's `Object()` to wrap types in their corresponding object types - auto objectCtor = env->global().getPropertyAsFunction(env, "Object"); - fbjsi::Value wrappedValue = objectCtor.callAsConstructor(env, value); - if (!wrappedValue.isObject()) { - throw fbjsi::JSError( - env, util::format("TypeError: cannot wrap %1 in Object", realmjsi::Value::typeof(env, value))); - } - return env(wrappedValue.asObject(env)); -} - -template <> -inline JsiObj realmjsi::Value::to_array(JsiEnv env, const JsiVal& value) -{ - return to_object(env, value); -} - -template <> -inline JsiFunc realmjsi::Value::to_function(JsiEnv env, const JsiVal& value) -{ - return env(value->asObject(env).asFunction(env)); -} - -template <> -inline JsiFunc realmjsi::Value::to_constructor(JsiEnv env, const JsiVal& value) -{ - return to_function(env, value); -} - -template <> -inline JsiObj realmjsi::Value::to_date(JsiEnv env, const JsiVal& value) -{ - if (value->isString()) { - return env(globalType(env, "Date").callAsConstructor(env, value).asObject(env)); - } - - return to_object(env, value); -} - -template <> -inline JsiVal realmjsi::Value::from_decimal128(JsiEnv env, const Decimal128& number) -{ - if (number.is_null()) { - return env(fbjsi::Value::null()); - } - - return env(globalType(env, "Realm") - .getPropertyAsObject(env, "_Decimal128") - .getPropertyAsFunction(env, "fromString") - .call(env, str(env, number.to_string()))); -} - -template <> -inline Decimal128 realmjsi::Value::to_decimal128(JsiEnv env, const JsiVal& value) -{ - return Decimal128(value->toString(env).utf8(env)); -} - -template <> -inline JsiVal realmjsi::Value::from_object_id(JsiEnv env, const ObjectId& objectId) -{ - return env(globalType(env, "Realm") - .getPropertyAsFunction(env, "_ObjectId") - .callAsConstructor(env, str(env, objectId.to_string()))); -} - -template <> -inline ObjectId realmjsi::Value::to_object_id(JsiEnv env, const JsiVal& value) -{ - auto objectId = value->asObject(env); - return ObjectId(objectId.getPropertyAsFunction(env, "toHexString") - .callWithThis(env, objectId) - .getString(env) - .utf8(env) - .c_str()); -} - -template <> -inline UUID realmjsi::Value::to_uuid(JsiEnv env, const JsiVal& value) -{ - auto uuid = value->asObject(env); - return UUID( - uuid.getPropertyAsFunction(env, "toHexString").callWithThis(env, uuid).getString(env).utf8(env).c_str()); -} - -} // namespace js -} // namespace realm From f6f0c32023e22876e04e5593fb2bfd8e13a80d0e Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 13 Dec 2023 09:59:26 +0100 Subject: [PATCH 07/14] Update android build script to ts --- packages/realm/package.json | 2 +- .../{build-android.js => build-android.ts} | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) rename packages/realm/scripts/{build-android.js => build-android.ts} (89%) diff --git a/packages/realm/package.json b/packages/realm/package.json index dffa75aeb3b..67c8b96d9d6 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -220,7 +220,7 @@ } }, "build:android": { - "command": "node ./scripts/build-android.js", + "command": "tsx ./scripts/build-android.ts", "files": [ "bindgen/android/**", "bindgen/src/**/*.ts", diff --git a/packages/realm/scripts/build-android.js b/packages/realm/scripts/build-android.ts similarity index 89% rename from packages/realm/scripts/build-android.js rename to packages/realm/scripts/build-android.ts index 91f473c2762..d9636b7345d 100644 --- a/packages/realm/scripts/build-android.js +++ b/packages/realm/scripts/build-android.ts @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////// // -// Copyright 2021 Realm Inc. +// Copyright 2023 Realm Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,19 +15,25 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////// +/* eslint-disable no-console */ -const commandLineArgs = require("command-line-args"); -const fs = require("fs-extra"); -const path = require("path"); -const exec = require("child_process").execFileSync; - -const { version } = require("../package.json"); +import commandLineArgs from "command-line-args"; +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync as exec } from "node:child_process"; +import { version } from "../package.json"; const packageRoot = path.resolve(__dirname, ".."); const NDK_VERSION = "23.1.7779620"; const { ANDROID_SDK_ROOT } = process.env; + +if (!ANDROID_SDK_ROOT) { + console.error(`Missing env variable ANDROID_SDK_ROOT`); + process.exit(1); +} + if (!fs.existsSync(ANDROID_SDK_ROOT)) { console.error(`Missing the Android SDK ${ANDROID_SDK_ROOT}`); process.exit(1); @@ -66,11 +72,11 @@ const cmakePath = process.platform === "win32" ? "cmake.exe" : "cmake"; const buildPath = path.resolve(packageRoot, "build-android"); if (options.clean) { if (fs.existsSync(buildPath)) { - fs.removeSync(buildPath); + fs.rmSync(buildPath, { recursive: true, force: true }); } } -fs.ensureDirSync(buildPath, { recursive: true }); +fs.mkdirSync(buildPath, { recursive: true }); for (const arch of architectures) { console.log(`\nBuilding Realm JS Android for ${arch} (${buildType})`); From 6196462d6aa196127a3ae347469603dc1368c743 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 13 Dec 2023 11:14:51 +0100 Subject: [PATCH 08/14] Reorg binding files into their own folder --- packages/realm/bindgen/CMakeLists.txt | 10 +++++++--- packages/realm/{bindgen/src => binding}/CMakeLists.txt | 0 .../{bindgen/src => binding}/android/CMakeLists.txt | 2 -- .../realm/{bindgen/src => binding}/android/hack.cpp | 0 .../realm/{bindgen/src => binding}/android/hack.hpp | 0 .../android/io_realm_react_RealmReactModule.cpp | 0 .../android/io_realm_react_RealmReactModule.h | 0 .../{bindgen/src => binding}/android/jni_utils.cpp | 0 .../{bindgen/src => binding}/android/jni_utils.hpp | 0 .../{bindgen/src => binding}/android/platform.cpp | 0 .../realm/{bindgen/src => binding}/ios/CMakeLists.txt | 0 .../realm/{bindgen/src => binding}/ios/platform.mm | 0 .../realm/{bindgen/src => binding}/jsi/CMakeLists.txt | 0 .../realm/{bindgen/src => binding}/jsi/jsi_externs.hpp | 0 .../realm/{bindgen/src => binding}/jsi/jsi_init.cpp | 0 packages/realm/{bindgen/src => binding}/jsi/jsi_init.h | 0 packages/realm/{bindgen/src => binding}/logger.hpp | 0 .../realm/{bindgen/src => binding}/node/platform.cpp | 0 packages/realm/{bindgen/src => binding}/platform.hpp | 0 packages/realm/scripts/build-ios.sh | 3 ++- 20 files changed, 9 insertions(+), 6 deletions(-) rename packages/realm/{bindgen/src => binding}/CMakeLists.txt (100%) rename packages/realm/{bindgen/src => binding}/android/CMakeLists.txt (99%) rename packages/realm/{bindgen/src => binding}/android/hack.cpp (100%) rename packages/realm/{bindgen/src => binding}/android/hack.hpp (100%) rename packages/realm/{bindgen/src => binding}/android/io_realm_react_RealmReactModule.cpp (100%) rename packages/realm/{bindgen/src => binding}/android/io_realm_react_RealmReactModule.h (100%) rename packages/realm/{bindgen/src => binding}/android/jni_utils.cpp (100%) rename packages/realm/{bindgen/src => binding}/android/jni_utils.hpp (100%) rename packages/realm/{bindgen/src => binding}/android/platform.cpp (100%) rename packages/realm/{bindgen/src => binding}/ios/CMakeLists.txt (100%) rename packages/realm/{bindgen/src => binding}/ios/platform.mm (100%) rename packages/realm/{bindgen/src => binding}/jsi/CMakeLists.txt (100%) rename packages/realm/{bindgen/src => binding}/jsi/jsi_externs.hpp (100%) rename packages/realm/{bindgen/src => binding}/jsi/jsi_init.cpp (100%) rename packages/realm/{bindgen/src => binding}/jsi/jsi_init.h (100%) rename packages/realm/{bindgen/src => binding}/logger.hpp (100%) rename packages/realm/{bindgen/src => binding}/node/platform.cpp (100%) rename packages/realm/{bindgen/src => binding}/platform.hpp (100%) diff --git a/packages/realm/bindgen/CMakeLists.txt b/packages/realm/bindgen/CMakeLists.txt index 2e61462bacd..a5088d879f3 100644 --- a/packages/realm/bindgen/CMakeLists.txt +++ b/packages/realm/bindgen/CMakeLists.txt @@ -120,6 +120,10 @@ endif() target_include_directories(realm-js PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src") set(SDK_DIR ${PACKAGE_ROOT_DIR}/packages/realm) +set(BINDING_DIR ${SDK_DIR}/binding) + +target_include_directories(realm-js PRIVATE "${BINDING_DIR}") + file(GLOB_RECURSE SDK_TS_FILES LIST_DIRECTORIES false @@ -175,7 +179,7 @@ if(DEFINED CMAKE_JS_VERSION) SOURCES ${SDK_TS_FILES} ) - target_sources(realm-js PRIVATE node_init.cpp ${CMAKE_JS_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/node/platform.cpp) + target_sources(realm-js PRIVATE node_init.cpp ${CMAKE_JS_SRC} ${BINDING_DIR}/node/platform.cpp) else() include(jsi.cmake) @@ -191,9 +195,9 @@ else() target_sources(realm-js PRIVATE jsi_init.cpp ${CMAKE_JS_SRC}) if(ANDROID) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/android android) + add_subdirectory(${BINDING_DIR}/android android) elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/ios ios) + add_subdirectory(${BINDING_DIR}/ios ios) else() # disabling for now to simplify the bootstrapping process. # message(FATAL_ERROR "Only support JSI builds for Android and iOS") diff --git a/packages/realm/bindgen/src/CMakeLists.txt b/packages/realm/binding/CMakeLists.txt similarity index 100% rename from packages/realm/bindgen/src/CMakeLists.txt rename to packages/realm/binding/CMakeLists.txt diff --git a/packages/realm/bindgen/src/android/CMakeLists.txt b/packages/realm/binding/android/CMakeLists.txt similarity index 99% rename from packages/realm/bindgen/src/android/CMakeLists.txt rename to packages/realm/binding/android/CMakeLists.txt index 59d106ce75e..bf27935f432 100644 --- a/packages/realm/bindgen/src/android/CMakeLists.txt +++ b/packages/realm/binding/android/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.18.1) -set(REACT_NATIVE_VERSION "0.71.0-rc.5") - set(REACT_NATIVE_ROOT_DIR "${PACKAGE_ROOT_DIR}/node_modules/react-native") set(REACT_NATIVE_AAR_DIR "${CMAKE_BINARY_DIR}/../react-native-aar") # TODO: Could we use CMAKE_BINARY_DIR instead? set(REACT_NATIVE_VERSION "0.71.0") diff --git a/packages/realm/bindgen/src/android/hack.cpp b/packages/realm/binding/android/hack.cpp similarity index 100% rename from packages/realm/bindgen/src/android/hack.cpp rename to packages/realm/binding/android/hack.cpp diff --git a/packages/realm/bindgen/src/android/hack.hpp b/packages/realm/binding/android/hack.hpp similarity index 100% rename from packages/realm/bindgen/src/android/hack.hpp rename to packages/realm/binding/android/hack.hpp diff --git a/packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.cpp b/packages/realm/binding/android/io_realm_react_RealmReactModule.cpp similarity index 100% rename from packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.cpp rename to packages/realm/binding/android/io_realm_react_RealmReactModule.cpp diff --git a/packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.h b/packages/realm/binding/android/io_realm_react_RealmReactModule.h similarity index 100% rename from packages/realm/bindgen/src/android/io_realm_react_RealmReactModule.h rename to packages/realm/binding/android/io_realm_react_RealmReactModule.h diff --git a/packages/realm/bindgen/src/android/jni_utils.cpp b/packages/realm/binding/android/jni_utils.cpp similarity index 100% rename from packages/realm/bindgen/src/android/jni_utils.cpp rename to packages/realm/binding/android/jni_utils.cpp diff --git a/packages/realm/bindgen/src/android/jni_utils.hpp b/packages/realm/binding/android/jni_utils.hpp similarity index 100% rename from packages/realm/bindgen/src/android/jni_utils.hpp rename to packages/realm/binding/android/jni_utils.hpp diff --git a/packages/realm/bindgen/src/android/platform.cpp b/packages/realm/binding/android/platform.cpp similarity index 100% rename from packages/realm/bindgen/src/android/platform.cpp rename to packages/realm/binding/android/platform.cpp diff --git a/packages/realm/bindgen/src/ios/CMakeLists.txt b/packages/realm/binding/ios/CMakeLists.txt similarity index 100% rename from packages/realm/bindgen/src/ios/CMakeLists.txt rename to packages/realm/binding/ios/CMakeLists.txt diff --git a/packages/realm/bindgen/src/ios/platform.mm b/packages/realm/binding/ios/platform.mm similarity index 100% rename from packages/realm/bindgen/src/ios/platform.mm rename to packages/realm/binding/ios/platform.mm diff --git a/packages/realm/bindgen/src/jsi/CMakeLists.txt b/packages/realm/binding/jsi/CMakeLists.txt similarity index 100% rename from packages/realm/bindgen/src/jsi/CMakeLists.txt rename to packages/realm/binding/jsi/CMakeLists.txt diff --git a/packages/realm/bindgen/src/jsi/jsi_externs.hpp b/packages/realm/binding/jsi/jsi_externs.hpp similarity index 100% rename from packages/realm/bindgen/src/jsi/jsi_externs.hpp rename to packages/realm/binding/jsi/jsi_externs.hpp diff --git a/packages/realm/bindgen/src/jsi/jsi_init.cpp b/packages/realm/binding/jsi/jsi_init.cpp similarity index 100% rename from packages/realm/bindgen/src/jsi/jsi_init.cpp rename to packages/realm/binding/jsi/jsi_init.cpp diff --git a/packages/realm/bindgen/src/jsi/jsi_init.h b/packages/realm/binding/jsi/jsi_init.h similarity index 100% rename from packages/realm/bindgen/src/jsi/jsi_init.h rename to packages/realm/binding/jsi/jsi_init.h diff --git a/packages/realm/bindgen/src/logger.hpp b/packages/realm/binding/logger.hpp similarity index 100% rename from packages/realm/bindgen/src/logger.hpp rename to packages/realm/binding/logger.hpp diff --git a/packages/realm/bindgen/src/node/platform.cpp b/packages/realm/binding/node/platform.cpp similarity index 100% rename from packages/realm/bindgen/src/node/platform.cpp rename to packages/realm/binding/node/platform.cpp diff --git a/packages/realm/bindgen/src/platform.hpp b/packages/realm/binding/platform.hpp similarity index 100% rename from packages/realm/bindgen/src/platform.hpp rename to packages/realm/binding/platform.hpp diff --git a/packages/realm/scripts/build-ios.sh b/packages/realm/scripts/build-ios.sh index 1984e884671..21c4447bd0c 100755 --- a/packages/realm/scripts/build-ios.sh +++ b/packages/realm/scripts/build-ios.sh @@ -8,6 +8,7 @@ cd "$(dirname "$0")/.." PROJECT_ROOT=$(pwd) SDK_PATH=$PROJECT_ROOT BINDGEN_PATH=$PROJECT_ROOT/bindgen +BINDING_PATH=$PROJECT_ROOT/binding SCRIPT=$(basename "${BASH_SOURCE[0]}") function usage { @@ -116,7 +117,7 @@ done rm -rf _include mkdir -p _include/realm-js-ios -cp "$BINDGEN_PATH"/src/jsi/jsi_init.h _include/realm-js-ios/ +cp "$BINDING_PATH"/jsi/jsi_init.h _include/realm-js-ios/ rm -rf ../realm-js-ios.xcframework xcodebuild -create-xcframework \ From 0a8a70cb23f57cd899f362c6d690aedadc88eb70 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 13 Dec 2023 11:50:55 +0100 Subject: [PATCH 09/14] Keep ccache in the root scripts --- integration-tests/environments/react-native/ios/Podfile | 4 ++-- packages/realm/scripts/build-ios.sh | 4 ++-- {packages/realm/scripts => scripts}/ccache-clang++.sh | 0 {packages/realm/scripts => scripts}/ccache-clang.sh | 0 4 files changed, 4 insertions(+), 4 deletions(-) rename {packages/realm/scripts => scripts}/ccache-clang++.sh (100%) rename {packages/realm/scripts => scripts}/ccache-clang.sh (100%) diff --git a/integration-tests/environments/react-native/ios/Podfile b/integration-tests/environments/react-native/ios/Podfile index 0fbe616650b..82d1ef024a6 100644 --- a/integration-tests/environments/react-native/ios/Podfile +++ b/integration-tests/environments/react-native/ios/Podfile @@ -73,8 +73,8 @@ target 'RealmReactNativeTests' do # Applying https://github.com/facebook/folly/issues/1470#issuecomment-943123653 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FOLLY_HAVE_CLOCK_GETTIME=1'] end - config.build_settings['CC'] = "../../../../../packages/realm/scripts/ccache-clang.sh" - config.build_settings['CXX'] = "../../../../../packages/realm/scripts/ccache-clang++.sh" + config.build_settings['CC'] = "../../../../../scripts/ccache-clang.sh" + config.build_settings['CXX'] = "../../../../../scripts/ccache-clang++.sh" end end end diff --git a/packages/realm/scripts/build-ios.sh b/packages/realm/scripts/build-ios.sh index 21c4447bd0c..1773ffd30ed 100755 --- a/packages/realm/scripts/build-ios.sh +++ b/packages/realm/scripts/build-ios.sh @@ -105,8 +105,8 @@ DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ -scheme realm-js-ios \ "${DESTINATIONS[@]}" \ -configuration $CONFIGURATION \ - CC="$PROJECT_ROOT/scripts/ccache-clang.sh" \ - CXX="$PROJECT_ROOT/scripts/ccache-clang++.sh" \ + CC="$PROJECT_ROOT/../../scripts/ccache-clang.sh" \ + CXX="$PROJECT_ROOT/../../scripts/ccache-clang++.sh" \ ONLY_ACTIVE_ARCH=NO \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ SUPPORTS_MACCATALYST=YES diff --git a/packages/realm/scripts/ccache-clang++.sh b/scripts/ccache-clang++.sh similarity index 100% rename from packages/realm/scripts/ccache-clang++.sh rename to scripts/ccache-clang++.sh diff --git a/packages/realm/scripts/ccache-clang.sh b/scripts/ccache-clang.sh similarity index 100% rename from packages/realm/scripts/ccache-clang.sh rename to scripts/ccache-clang.sh From 2c67a13285e901ded6ef3184f7bee60f5380ba66 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Sat, 30 Dec 2023 11:09:10 +0100 Subject: [PATCH 10/14] Make builds for iOS work without downloading prebuilds --- .../environments/react-native/ios/Podfile | 2 - .../RealmReactNativeTests.entitlements | 12 ++ packages/realm/RealmJS.podspec | 24 +++ packages/realm/bindgen/jsi.cmake | 13 +- .../realm/scripts/build-ios-on-install.sh | 139 ++++++++++++++++++ packages/realm/scripts/ccache-clang++.sh | 17 +++ packages/realm/scripts/ccache-clang.sh | 17 +++ packages/realm/scripts/cmake-ios.sh | 17 +++ 8 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 integration-tests/environments/react-native/ios/RealmReactNativeTests/RealmReactNativeTests.entitlements create mode 100755 packages/realm/scripts/build-ios-on-install.sh create mode 100755 packages/realm/scripts/ccache-clang++.sh create mode 100755 packages/realm/scripts/ccache-clang.sh create mode 100755 packages/realm/scripts/cmake-ios.sh diff --git a/integration-tests/environments/react-native/ios/Podfile b/integration-tests/environments/react-native/ios/Podfile index 82d1ef024a6..1a7507b1858 100644 --- a/integration-tests/environments/react-native/ios/Podfile +++ b/integration-tests/environments/react-native/ios/Podfile @@ -73,8 +73,6 @@ target 'RealmReactNativeTests' do # Applying https://github.com/facebook/folly/issues/1470#issuecomment-943123653 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FOLLY_HAVE_CLOCK_GETTIME=1'] end - config.build_settings['CC'] = "../../../../../scripts/ccache-clang.sh" - config.build_settings['CXX'] = "../../../../../scripts/ccache-clang++.sh" end end end diff --git a/integration-tests/environments/react-native/ios/RealmReactNativeTests/RealmReactNativeTests.entitlements b/integration-tests/environments/react-native/ios/RealmReactNativeTests/RealmReactNativeTests.entitlements new file mode 100644 index 00000000000..fc07546cfa3 --- /dev/null +++ b/integration-tests/environments/react-native/ios/RealmReactNativeTests/RealmReactNativeTests.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.personal-information.location + + + diff --git a/packages/realm/RealmJS.podspec b/packages/realm/RealmJS.podspec index c12bf2ba160..1ef893f3d84 100644 --- a/packages/realm/RealmJS.podspec +++ b/packages/realm/RealmJS.podspec @@ -42,6 +42,12 @@ Pod::Spec.new do |s| s.source = { :http => 'https://github.com/realm/realm-js/blob/main/CONTRIBUTING.md#how-to-debug-react-native-podspec' } s.source_files = 'react-native/ios/RealmReact/*.mm' + # 'binding/ios/*.mm', + # 'binding/jsi/*.{hpp,cpp,h}', + # 'binding/*.{hpp}', + # 'bindgen/src/**/*.{h}' + # 'bindgen/vendor/**/*.{h,hpp}' + s.public_header_files = 'react-native/ios/RealmReact/*.h' s.frameworks = uses_frameworks ? ['React'] : [] @@ -51,6 +57,7 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { # Setting up clang 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', + #'GCC_CXX_LANGUAGE_STANDARD' => 'c++17', 'CLANG_CXX_LIBRARY' => 'libc++', # Setting the current project version and versioning system to get a symbol for analytics 'CURRENT_PROJECT_VERSION' => s.version, @@ -58,6 +65,7 @@ Pod::Spec.new do |s| # Header search paths are prefixes to the path specified in #include macros 'HEADER_SEARCH_PATHS' => [ '"$(PODS_TARGET_SRCROOT)/react-native/ios/RealmReact/"', + '"$(PODS_TARGET_SRCROOT)/react-native/ios/build/_include"', '"$(PODS_ROOT)/Headers/Public/React-Core/"' #"'#{app_path}/ios/Pods/Headers/Public/React-Core'" # Use this line instead of 👆 while linting ].join(' ') @@ -67,4 +75,20 @@ Pod::Spec.new do |s| s.vendored_frameworks = 'react-native/ios/realm-js-ios.xcframework' s.dependency 'React' + + script_location = "#{__dir__}/scripts/build-ios-on-install.sh" + + CMAKE_PATH = Pod::Executable::which!('cmake') + NODE_PATH = Pod::Executable::which!('node') + + # Post install script + s.script_phase = { + :name => 'Generate Realm xcframework', + :execution_position => :before_compile, + :script => <<-EOS + export CMAKE_PATH=#{CMAKE_PATH} + export NODE_PATH=#{NODE_PATH} + /bin/sh -c \"#{script_location} -c $CONFIGURATION $PLATFORM_NAME\" + EOS + } end diff --git a/packages/realm/bindgen/jsi.cmake b/packages/realm/bindgen/jsi.cmake index e904dfb39dc..5b182823d13 100644 --- a/packages/realm/bindgen/jsi.cmake +++ b/packages/realm/bindgen/jsi.cmake @@ -4,8 +4,19 @@ # This enables building for iOS on the end-users machine, # where "react-native" is installed as a sibling to our package instead of being a dev-dependency of our package. +if(NOT DEFINED NODE_PATH) +set(NODE_PATH $ENV{NODE_PATH}) + if(NOT NODE_PATH) + find_program(NODE_PATH node) + endif() +endif() + +if(NOT DEFINED NODE_PATH) + message(FATAL_ERROR "Node.js not found") +endif() + execute_process( - COMMAND node --print "path.dirname(require.resolve('react-native/package.json'))" + COMMAND ${NODE_PATH} --print "path.dirname(require.resolve('react-native/package.json'))" OUTPUT_VARIABLE REACT_NATIVE_ROOT_DIR OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY diff --git a/packages/realm/scripts/build-ios-on-install.sh b/packages/realm/scripts/build-ios-on-install.sh new file mode 100755 index 00000000000..c2db409ba45 --- /dev/null +++ b/packages/realm/scripts/build-ios-on-install.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +set -e +set -o pipefail + +# Start in the root directory of the project. +cd "$(dirname "$0")/.." + +# Need npx and node +NODE_DIRECTORY=$(dirname "$NODE_PATH") +export PATH="$NODE_DIRECTORY:$PATH" + +# Take homebrew installs as well +CMAKE_DIRECTORY=$(dirname "$CMAKE_PATH") +export PATH="$CMAKE_DIRECTORY:$PATH" + +PROJECT_ROOT=$(pwd) +SDK_PATH=$PROJECT_ROOT +BINDGEN_PATH=$PROJECT_ROOT/bindgen +BINDING_PATH=$PROJECT_ROOT/binding +SCRIPT=$(basename "${BASH_SOURCE[0]}") + +function usage { + echo "Usage: ${SCRIPT} [-c ] []" + echo "" + echo "Arguments:" + echo " -c : build configuration (Debug or Release)" + echo " : platforms to build for (maccatalyst, ios, or iphonesimulator)" + exit 1; +} + +CONFIGURATION=Release +SUPPORT_PLATFORMS=(maccatalyst ios iphonesimulator) + +function is_supported_platform(){ + for platform in "${SUPPORT_PLATFORMS[@]}"; do + [[ "${platform}" == $1 ]] && return 0 + done + return 1 +} + +# Parse the options +while getopts ":c:" opt; do + case "${opt}" in + c) CONFIGURATION=${OPTARG};; + *) usage;; + esac +done + +echo "Configuration: ${CONFIGURATION}" + +shift $((OPTIND-1)) +PLATFORMS=($@) + +if [ -z ${PLATFORMS} ]; then + echo "No platform given. building all platforms..."; + PLATFORMS=(ios maccatalyst iphonesimulator) +else + echo "Building for..."; + for check_platform in "${PLATFORMS[@]}"; do + if ! is_supported_platform $check_platform; then + echo "${check_platform} is not a supported platform" + usage + exit 1 + fi + echo ${check_platform}; + done +fi + +DESTINATIONS=() +LIBRARIES=() +BUILD_LIB_CMDS=() + +for platform in "${PLATFORMS[@]}"; do + case "$platform" in + ios) + DESTINATIONS+=(-destination 'generic/platform=iOS') + LIBRARIES+=(-library ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a -headers ./_include) + BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a ./out/$CONFIGURATION-iphoneos/*.a") + ;; + maccatalyst) + DESTINATIONS+=(-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst') + LIBRARIES+=(-library ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a -headers ./_include) + BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a ./out/$CONFIGURATION-maccatalyst/*.a") + ;; + iphonesimulator) + DESTINATIONS+=(-destination 'generic/platform=iOS Simulator') + LIBRARIES+=(-library ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a -headers ./_include) + BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a ./out/$CONFIGURATION-iphonesimulator/*.a") + ;; + *) + echo "${platform} not supported" + usage + exit 1 + ;; + esac +done + +pushd $SDK_PATH/react-native/ios + +mkdir -p build +pushd build + +# If the developer directory is not set, use the default Xcode path +SELECTED_DEVELOPER_DIR="$(xcode-select -p)" +DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" + +# Configure CMake project +env DEVELOPER_DIR="$DEVELOPER_DIR" SDKROOT="$SDKROOT" $CMAKE_PATH "$BINDGEN_PATH" -GXcode \ + -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out/$\$EFFECTIVE_PLATFORM_NAME" \ + # -DCMAKE_C_COMPILER="$PROJECT_ROOT/scripts/ccache-clang.sh" \ + # -DCMAKE_CXX_COMPILER="$PROJECT_ROOT/scripts/ccache-clang++.sh" + # -DCMAKE_C_COMPILER="/usr/bin/clang" \ + # -DCMAKE_CXX_COMPILER="/usr/bin/clang++"\ + + +DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ + -scheme realm-js-ios \ + "${DESTINATIONS[@]}" \ + -configuration $CONFIGURATION \ + # CC="$PROJECT_ROOT/scripts/ccache-clang.sh" \ + # CXX="$PROJECT_ROOT/scripts/ccache-clang++.sh" \ + ONLY_ACTIVE_ARCH=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ + SUPPORTS_MACCATALYST=YES + +for cmd in "${BUILD_LIB_CMDS[@]}"; do + eval "${cmd}" +done + +rm -rf _include +mkdir -p _include/realm-js-ios +cp "$BINDING_PATH"/jsi/jsi_init.h _include/realm-js-ios/ + +rm -rf ../realm-js-ios.xcframework +xcodebuild -create-xcframework \ + "${LIBRARIES[@]}" \ + -output ../realm-js-ios.xcframework diff --git a/packages/realm/scripts/ccache-clang++.sh b/packages/realm/scripts/ccache-clang++.sh new file mode 100755 index 00000000000..d2050550029 --- /dev/null +++ b/packages/realm/scripts/ccache-clang++.sh @@ -0,0 +1,17 @@ +#!/bin/sh +if command -v ccache &> /dev/null; then +# reference for xcode specific cacche settings: reactnative docs and ccache manpage +# https://reactnative.dev/docs/build-speed#xcode-specific-setup +# https://ccache.dev/manual/4.3.html + export CCACHE_MAXSIZE=10G + export CCACHE_CPP2=true + export CCACHE_DIRECT=true + export CCACHE_DEPEND=true + export CCACHE_HARDLINK=true + export CCACHE_FILECLONE=true + export CCACHE_INODECACHE=true + export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches,modules,ivfsoverlay,pch_defines,system_headers + exec ccache clang++ "$@" +else + exec clang++ "$@" +fi diff --git a/packages/realm/scripts/ccache-clang.sh b/packages/realm/scripts/ccache-clang.sh new file mode 100755 index 00000000000..7fb31f28af9 --- /dev/null +++ b/packages/realm/scripts/ccache-clang.sh @@ -0,0 +1,17 @@ +#!/bin/sh +if command -v ccache &> /dev/null; then +# reference for xcode specific cacche settings: reactnative docs and ccache manpage +# https://reactnative.dev/docs/build-speed#xcode-specific-setup +# https://ccache.dev/manual/4.3.html + export CCACHE_MAXSIZE=10G + export CCACHE_CPP2=true + export CCACHE_DIRECT=true + export CCACHE_DEPEND=true + export CCACHE_HARDLINK=true + export CCACHE_FILECLONE=true + export CCACHE_INODECACHE=true + export CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches,modules,ivfsoverlay,pch_defines,system_headers + exec ccache clang "$@" +else + exec clang "$@" +fi diff --git a/packages/realm/scripts/cmake-ios.sh b/packages/realm/scripts/cmake-ios.sh new file mode 100755 index 00000000000..10b9100534a --- /dev/null +++ b/packages/realm/scripts/cmake-ios.sh @@ -0,0 +1,17 @@ + +# If the developer directory is not set, use the default Xcode path +SELECTED_DEVELOPER_DIR="$(xcode-select -p)" +DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" + +PROJECT_ROOT=$(pwd) +BINDGEN_PATH=$PROJECT_ROOT/bindgen + +pushd $PROJECT_ROOT/react-native/ios + +mkdir -p build +pushd build + +# Configure CMake project +SDKROOT="$DEVELOPER_DIR/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/" cmake "$BINDGEN_PATH" -GXcode \ + -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out/$\$EFFECTIVE_PLATFORM_NAME" \ From ca91ebcb08cbbeea1bfe12b8cb2d8913fc3385f7 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Thu, 4 Jan 2024 17:56:33 +0100 Subject: [PATCH 11/14] Remove xcframework and just use static libraries --- packages/realm/RealmJS.podspec | 19 +++++---- .../realm/scripts/build-ios-on-install.sh | 41 ++++++++----------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/packages/realm/RealmJS.podspec b/packages/realm/RealmJS.podspec index 1ef893f3d84..e2ce9f4a621 100644 --- a/packages/realm/RealmJS.podspec +++ b/packages/realm/RealmJS.podspec @@ -33,20 +33,15 @@ Pod::Spec.new do |s| s.authors = package['author'] s.homepage = package['homepage'] - s.platform = :ios, '9.0' + s.platform = :ios, '13.4' - # The source field is a required field in the podspec, but it is not ment to be used. - # This is because the Podspec is not ment to be published into a CocoaPod repository, instead React Native uses a :path style dependency when adding this to the users projects Podfile. + # The source field is a required field in the podspec, but it is not meant to be used. + # This is because the Podspec is not meant to be published into a CocoaPod repository, instead React Native uses a :path style dependency when adding this to the users projects Podfile. # @see https://guides.cocoapods.org/using/the-podfile.html#using-the-files-from-a-folder-local-to-the-machine # @see https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#platform-ios s.source = { :http => 'https://github.com/realm/realm-js/blob/main/CONTRIBUTING.md#how-to-debug-react-native-podspec' } s.source_files = 'react-native/ios/RealmReact/*.mm' - # 'binding/ios/*.mm', - # 'binding/jsi/*.{hpp,cpp,h}', - # 'binding/*.{hpp}', - # 'bindgen/src/**/*.{h}' - # 'bindgen/vendor/**/*.{h,hpp}' s.public_header_files = 'react-native/ios/RealmReact/*.h' @@ -71,8 +66,14 @@ Pod::Spec.new do |s| ].join(' ') } + lib_location = "react-native/ios/build/libs" + # TODO: Consider providing an option to build with the -dbg binaries instead - s.vendored_frameworks = 'react-native/ios/realm-js-ios.xcframework' + s.vendored_libraries = "#{lib_location}/librealm.a", + "#{lib_location}/librealm-js-ios.a", + "#{lib_location}/librealm-object-store.a", + "#{lib_location}/librealm-parser.a", + "#{lib_location}/librealm-sync.a" s.dependency 'React' diff --git a/packages/realm/scripts/build-ios-on-install.sh b/packages/realm/scripts/build-ios-on-install.sh index c2db409ba45..1303bbebff3 100755 --- a/packages/realm/scripts/build-ios-on-install.sh +++ b/packages/realm/scripts/build-ios-on-install.sh @@ -68,25 +68,18 @@ else fi DESTINATIONS=() -LIBRARIES=() -BUILD_LIB_CMDS=() +# TODO: Can this be infered from xcode env variables? (maccatalyst appears to be a unicorn) for platform in "${PLATFORMS[@]}"; do case "$platform" in ios) DESTINATIONS+=(-destination 'generic/platform=iOS') - LIBRARIES+=(-library ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a ./out/$CONFIGURATION-iphoneos/*.a") ;; maccatalyst) DESTINATIONS+=(-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst') - LIBRARIES+=(-library ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a ./out/$CONFIGURATION-maccatalyst/*.a") ;; iphonesimulator) DESTINATIONS+=(-destination 'generic/platform=iOS Simulator') - LIBRARIES+=(-library ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a ./out/$CONFIGURATION-iphonesimulator/*.a") ;; *) echo "${platform} not supported" @@ -108,32 +101,34 @@ DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" # Configure CMake project env DEVELOPER_DIR="$DEVELOPER_DIR" SDKROOT="$SDKROOT" $CMAKE_PATH "$BINDGEN_PATH" -GXcode \ -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out/$\$EFFECTIVE_PLATFORM_NAME" \ - # -DCMAKE_C_COMPILER="$PROJECT_ROOT/scripts/ccache-clang.sh" \ - # -DCMAKE_CXX_COMPILER="$PROJECT_ROOT/scripts/ccache-clang++.sh" - # -DCMAKE_C_COMPILER="/usr/bin/clang" \ - # -DCMAKE_CXX_COMPILER="/usr/bin/clang++"\ + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out" \ DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ -scheme realm-js-ios \ "${DESTINATIONS[@]}" \ -configuration $CONFIGURATION \ - # CC="$PROJECT_ROOT/scripts/ccache-clang.sh" \ - # CXX="$PROJECT_ROOT/scripts/ccache-clang++.sh" \ ONLY_ACTIVE_ARCH=NO \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ SUPPORTS_MACCATALYST=YES -for cmd in "${BUILD_LIB_CMDS[@]}"; do - eval "${cmd}" -done - +# copy needed headers into project rm -rf _include mkdir -p _include/realm-js-ios cp "$BINDING_PATH"/jsi/jsi_init.h _include/realm-js-ios/ -rm -rf ../realm-js-ios.xcframework -xcodebuild -create-xcframework \ - "${LIBRARIES[@]}" \ - -output ../realm-js-ios.xcframework +# copy built into a unified location +rm -rf libs +mkdir libs +cp out/$CONFIGURATION/*.a libs + +# Rename the *.a files so there are the same regardles if configuration was debug or release + +pushd libs + +find . -type f -name "*-dbg*" | while read -r file; do + # Construct new filename by removing '-dbg' + newfile=$(echo "$file" | sed 's/-dbg//') + # Rename the file + mv "$file" "$newfile" +done From 68a8b0a4d87971ce1cb80e312c2117a672ac6810 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 17 Jan 2024 16:24:14 +0100 Subject: [PATCH 12/14] Refactor ios builds * build from core prebuilds * phase script only called when input and outputs have changes * reinstalling pods will wipe build files, forcing a rebuild * generate input file list so that core can be rebuilt on changes * generate dummy libraries so that libraries can be generated * create build option which forces core to build from source * build from source if prebuild url is not reachable --- .github/workflows/pr-realm-js.yml | 47 +----- .gitignore | 9 +- contrib/debug-rn-examples.md | 9 +- .../environments/react-native/ios/Podfile | 6 + .../project.pbxproj | 12 +- .../environments/react-native/package.json | 24 +--- packages/realm/RealmJS.podspec | 52 +++---- packages/realm/binding/CMakeLists.txt | 3 +- packages/realm/binding/jsi/jsi_init.cpp | 81 ----------- packages/realm/package.json | 62 +++----- .../react-native/ios/RealmReact/RealmReact.mm | 2 +- .../react-native/ios/output-files.xcfilelist | 5 + .../realm/scripts/build-ios-on-install.sh | 134 ------------------ packages/realm/scripts/build-ios.sh | 125 ---------------- packages/realm/scripts/generate-dummy-libs.sh | 14 ++ packages/realm/scripts/generate-input-list.sh | 13 ++ packages/realm/scripts/generate-ios-libs.sh | 69 +++++++++ 17 files changed, 183 insertions(+), 484 deletions(-) delete mode 100644 packages/realm/binding/jsi/jsi_init.cpp create mode 100644 packages/realm/react-native/ios/output-files.xcfilelist delete mode 100755 packages/realm/scripts/build-ios-on-install.sh delete mode 100755 packages/realm/scripts/build-ios.sh create mode 100755 packages/realm/scripts/generate-dummy-libs.sh create mode 100755 packages/realm/scripts/generate-input-list.sh create mode 100755 packages/realm/scripts/generate-ios-libs.sh diff --git a/.github/workflows/pr-realm-js.yml b/.github/workflows/pr-realm-js.yml index bb32d95a1db..54dc08da3e7 100644 --- a/.github/workflows/pr-realm-js.yml +++ b/.github/workflows/pr-realm-js.yml @@ -60,9 +60,9 @@ jobs: - { os: android, runner: ubuntu-latest, arch: x86, artifact-path: packages/realm/react-native/android/src/main/jniLibs } - { os: darwin, runner: macos-latest, arch: x64, artifact-path: packages/realm/prebuilds, test-node: true, test-electron: true } - { os: darwin, runner: macos-latest, arch: arm64, artifact-path: packages/realm/prebuilds, test-node: true, test-electron: true } - - { os: ios, runner: macos-latest-xlarge, arch: simulator, artifact-path: packages/realm/react-native/ios/realm-js-ios.xcframework } - - { os: ios, runner: macos-latest-xlarge, arch: catalyst, artifact-path: packages/realm/react-native/ios/realm-js-ios.xcframework } - - { os: ios, runner: macos-latest-xlarge, arch: ios, artifact-path: packages/realm/react-native/ios/realm-js-ios.xcframework } + - { os: ios, runner: macos-latest-xlarge, arch: simulator, artifact-path: packages/realm/react-native/shared/jsi_init.cpp } + - { os: ios, runner: macos-latest-xlarge, arch: catalyst, artifact-path: packages/realm/react-native/shared/jsi_init.cpp } + - { os: ios, runner: macos-latest-xlarge, arch: ios, artifact-path: packages/realm/react-native/shared/jsi_init.cpp } steps: - name: Checkout code uses: actions/checkout@v3 @@ -177,15 +177,9 @@ jobs: if: ${{ (matrix.variant.os != 'ios') && (matrix.variant.os != 'android') }} run: npm run build:node:prebuild:${{matrix.variant.arch}} --workspace realm - # build the c++ library for IOS - # the Info.plist needs to be regenerated with all libraries in place - - name: Build iOS - if: ${{ (matrix.variant.os == 'ios') }} + - name: Generate JSI run: | - npm run build:ios --workspace realm - rm -vf ${{ matrix.variant.artifact-path }}/Info.plist - env: - PLATFORMS: ${{ matrix.variant.arch }} + npm run bindgen:generate:jsi --workspace realm # build the c++ library for Android - name: Build Android @@ -202,38 +196,9 @@ jobs: README.md ${{ matrix.variant.artifact-path }} - ios-xcframework: - name: Generate Info.plist with all frameworks in place - needs: [build] - if: ${{ success() || failure() }} - runs-on: macos-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - submodules: "recursive" - - - name: Download prebuilds - uses: actions/download-artifact@v3 - with: - name: realm-js-prebuilds - - - name: Regenerate Info.plist - run: scripts/regen-info-plist.sh packages/realm/react-native/ios/realm-js-ios.xcframework - - # Due to a limitation in upload-artifact a redundant file is needed to force - # preserving paths (https://github.com/actions/upload-artifact/issues/174) - - name: Upload prebuild artifact - uses: actions/upload-artifact@v3 - with: - name: realm-js-prebuilds - path: | - README.md - packages/realm/react-native/ios/realm-js-ios.xcframework/Info.plist - integration-tests: name: Test ${{ matrix.variant.environment }} on ${{ matrix.variant.os }} (${{matrix.variant.target}}) - needs: [bundle, build, ios-xcframework] + needs: [bundle, build] if: ${{ success() || failure() }} env: REALM_DISABLE_ANALYTICS: 1 diff --git a/.gitignore b/.gitignore index 5b91ac900c1..2bda06441f2 100644 --- a/.gitignore +++ b/.gitignore @@ -97,14 +97,9 @@ coverage/ **/realm-config #Ignore symlinked directories (otherwise on Windows they are shown as untracked files) -/react-native/android/src/main/jni/vendor/ -/react-native/android/src/main/jni/src/ /build-tmp*/ /cmakebuild/ -/react-native/android/src/main/java/io/realm/react/Version.java -/react-native/android/src/main/jniLibs/ /realm*.tgz -/react-native/ios/realm-js-ios.xcframework/ # Ignore template package-lock.json files /templates/*/package-lock.json @@ -119,7 +114,9 @@ coverage/ /packages/realm/prebuilds/ /packages/realm/react-native/android/src/main/java/io/realm/react/Version.java /packages/realm/react-native/android/src/main/jniLibs/ -/packages/realm/react-native/ios/realm-js-ios.xcframework/ +/packages/realm/react-native/ios/lib/ +/packages/realm/react-native/ios/input-files.xcfilelist +/packages/realm/react-native/shared/ # Wireit repo caches .wireit diff --git a/contrib/debug-rn-examples.md b/contrib/debug-rn-examples.md index 3be0866dede..ffca29c0aa6 100644 --- a/contrib/debug-rn-examples.md +++ b/contrib/debug-rn-examples.md @@ -106,14 +106,9 @@ Since we want to keep the simplicity for our users, changes made in order to deb ## Common Issues -### Android or iOS doesn't find Realm. +### Android doesn't find Realm. -Make sure to build binaries for Android and iOS in packages/realm by running: - -``` -npm run build:ios --workspace=realm -``` -or +Make sure to build binaries for Android in packages/realm by running: ``` npm run build:android --workspace=realm diff --git a/integration-tests/environments/react-native/ios/Podfile b/integration-tests/environments/react-native/ios/Podfile index 1a7507b1858..b24ef6c6198 100644 --- a/integration-tests/environments/react-native/ios/Podfile +++ b/integration-tests/environments/react-native/ios/Podfile @@ -73,6 +73,12 @@ target 'RealmReactNativeTests' do # Applying https://github.com/facebook/folly/issues/1470#issuecomment-943123653 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FOLLY_HAVE_CLOCK_GETTIME=1'] end + + if target.name == "RealmJS" + target.build_configurations.each do |config| + config.build_settings['BUILD_CORE'] = 'true' + end + end end end end diff --git a/integration-tests/environments/react-native/ios/RealmReactNativeTests.xcodeproj/project.pbxproj b/integration-tests/environments/react-native/ios/RealmReactNativeTests.xcodeproj/project.pbxproj index 6d95a0dfd86..17b9aa38035 100644 --- a/integration-tests/environments/react-native/ios/RealmReactNativeTests.xcodeproj/project.pbxproj +++ b/integration-tests/environments/react-native/ios/RealmReactNativeTests.xcodeproj/project.pbxproj @@ -604,7 +604,11 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -674,7 +678,11 @@ "-DFOLLY_MOBILE=1", "-DFOLLY_USE_LIBCPP=1", ); - OTHER_LDFLAGS = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-Wl", + "-ld_classic", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../../../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/integration-tests/environments/react-native/package.json b/integration-tests/environments/react-native/package.json index 749429f9dd0..c68ec1afd6b 100644 --- a/integration-tests/environments/react-native/package.json +++ b/integration-tests/environments/react-native/package.json @@ -31,16 +31,9 @@ }, "pod-install:simulator": { "command": "pod-install || (cd ios && bundle install && bundle exec pod install)", - "dependencies": [ - { - "script": "../../../packages/realm:build:ios:debug:simulator", - "cascade": false - } - ], "clean": "if-file-deleted", "files": [ - "ios/Podfile", - "../../../packages/realm/react-native/ios/realm-js-ios.xcframework" + "ios/Podfile" ], "output": [ "ios/Pods", @@ -57,15 +50,8 @@ }, "pod-install:catalyst": { "command": "pod-install || (cd ios && bundle install && bundle exec pod install)", - "dependencies": [ - { - "script": "../../../packages/realm:build:ios:debug:catalyst", - "cascade": false - } - ], "files": [ - "ios/Podfile", - "../../../packages/realm/react-native/ios/realm-js-ios.xcframework" + "ios/Podfile" ], "output": [ "ios/Pods", @@ -105,6 +91,7 @@ "test:ios": { "command": "npm run common", "dependencies": [ + "../../../packages/realm:bindgen:generate:jsi", "pod-install:simulator", "../../../packages/realm:bundle", "../../../packages/mocha-reporter:bundle", @@ -118,6 +105,7 @@ "test:catalyst": { "command": "npm run common", "dependencies": [ + "../../../packages/realm:bindgen:generate:jsi", "pod-install:catalyst", "../../../packages/realm:bundle", "../../../packages/mocha-reporter:bundle", @@ -144,8 +132,8 @@ "watch:ios": { "command": "npm run common", "dependencies": [ + "../../../packages/realm:bindgen:generate:jsi", "pod-install:simulator", - "../../../packages/realm:build:ios:debug:simulator", "../../../packages/realm:bundle", "../../../packages/mocha-reporter:bundle", "../../../packages/realm-network-transport:bundle" @@ -158,8 +146,8 @@ "watch:catalyst": { "command": "npm run common", "dependencies": [ + "../../../packages/realm:bindgen:generate:jsi", "pod-install:catalyst", - "../../../packages/realm:build:ios:debug:catalyst", "../../../packages/realm:bundle", "../../../packages/mocha-reporter:bundle", "../../../packages/realm-network-transport:bundle" diff --git a/packages/realm/RealmJS.podspec b/packages/realm/RealmJS.podspec index e2ce9f4a621..b34c53fa62d 100644 --- a/packages/realm/RealmJS.podspec +++ b/packages/realm/RealmJS.podspec @@ -33,7 +33,7 @@ Pod::Spec.new do |s| s.authors = package['author'] s.homepage = package['homepage'] - s.platform = :ios, '13.4' + s.platform = :ios, '9.0' # The source field is a required field in the podspec, but it is not meant to be used. # This is because the Podspec is not meant to be published into a CocoaPod repository, instead React Native uses a :path style dependency when adding this to the users projects Podfile. @@ -41,9 +41,12 @@ Pod::Spec.new do |s| # @see https://github.com/react-native-community/cli/blob/master/docs/autolinking.md#platform-ios s.source = { :http => 'https://github.com/realm/realm-js/blob/main/CONTRIBUTING.md#how-to-debug-react-native-podspec' } - s.source_files = 'react-native/ios/RealmReact/*.mm' + s.source_files = 'react-native/ios/RealmReact/*.mm', + 'react-native/shared/*.cpp', + 'binding/ios/platform.mm' - s.public_header_files = 'react-native/ios/RealmReact/*.h' + s.public_header_files = 'react-native/ios/RealmReact/*.h', + 'react-native/ios/build/include/**/*.{h,hpp}' s.frameworks = uses_frameworks ? ['React'] : [] @@ -51,45 +54,46 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { # Setting up clang - 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17', - #'GCC_CXX_LANGUAGE_STANDARD' => 'c++17', + 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20', 'CLANG_CXX_LIBRARY' => 'libc++', # Setting the current project version and versioning system to get a symbol for analytics 'CURRENT_PROJECT_VERSION' => s.version, 'VERSIONING_SYSTEM' => 'apple-generic', + 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) REALM_ENABLE_SYNC=1', + 'GCC_SYMBOLS_PRIVATE_EXTERN' => 'YES', # Header search paths are prefixes to the path specified in #include macros 'HEADER_SEARCH_PATHS' => [ '"$(PODS_TARGET_SRCROOT)/react-native/ios/RealmReact/"', - '"$(PODS_TARGET_SRCROOT)/react-native/ios/build/_include"', - '"$(PODS_ROOT)/Headers/Public/React-Core/"' - #"'#{app_path}/ios/Pods/Headers/Public/React-Core'" # Use this line instead of 👆 while linting + '"$(PODS_TARGET_SRCROOT)/react-native/ios/build/include/"', + '"$(PODS_TARGET_SRCROOT)/binding/"', + '"$(PODS_TARGET_SRCROOT)/bindgen/src/"', + '"$(PODS_TARGET_SRCROOT)/bindgen/vendor/realm-core/bindgen/src/"' ].join(' ') } + # Create placeholders for vendored_libraries, so they are added to the xcode project + s.prepare_command = <<-EOS + source "#{__dir__}/scripts/generate-dummy-libs.sh" + source "#{__dir__}/scripts/generate-input-list.sh" + EOS - lib_location = "react-native/ios/build/libs" - - # TODO: Consider providing an option to build with the -dbg binaries instead - s.vendored_libraries = "#{lib_location}/librealm.a", - "#{lib_location}/librealm-js-ios.a", - "#{lib_location}/librealm-object-store.a", - "#{lib_location}/librealm-parser.a", - "#{lib_location}/librealm-sync.a" + s.vendored_libraries = "react-native/ios/lib/librealm.a", + "react-native/ios/lib/librealm-object-store.a", + "react-native/ios/lib/librealm-parser.a", + "react-native/ios/lib/librealm-sync.a" s.dependency 'React' - script_location = "#{__dir__}/scripts/build-ios-on-install.sh" - CMAKE_PATH = Pod::Executable::which!('cmake') - NODE_PATH = Pod::Executable::which!('node') - # Post install script + #Post install script s.script_phase = { - :name => 'Generate Realm xcframework', + :name => 'Retrieve libraries and headers', :execution_position => :before_compile, + :input_file_lists => ["#{__dir__}/react-native/ios/input-files.xcfilelist"], + :output_file_lists => ["#{__dir__}/react-native/ios/output-files.xcfilelist"], :script => <<-EOS - export CMAKE_PATH=#{CMAKE_PATH} - export NODE_PATH=#{NODE_PATH} - /bin/sh -c \"#{script_location} -c $CONFIGURATION $PLATFORM_NAME\" + CMAKE_PATH="#{CMAKE_PATH}" + source "#{__dir__}/scripts/generate-ios-libs.sh" EOS } end diff --git a/packages/realm/binding/CMakeLists.txt b/packages/realm/binding/CMakeLists.txt index 7bd329353d0..f999716705e 100644 --- a/packages/realm/binding/CMakeLists.txt +++ b/packages/realm/binding/CMakeLists.txt @@ -1,6 +1,5 @@ # temporary cache bust -add_library(realm-js-shared OBJECT - js_realm.cpp) +add_library(realm-js-shared OBJECT) target_link_libraries(realm-js-shared PUBLIC Realm::ObjectStore) target_include_directories(realm-js-shared PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/packages/realm/binding/jsi/jsi_init.cpp b/packages/realm/binding/jsi/jsi_init.cpp deleted file mode 100644 index 73eef2ba55b..00000000000 --- a/packages/realm/binding/jsi/jsi_init.cpp +++ /dev/null @@ -1,81 +0,0 @@ -//////////////////////////////////////////////////////////////////////////// -// -// Copyright 2021 Realm Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//////////////////////////////////////////////////////////////////////////// - - -#include "jsi_init.hpp" - -#if !REALM_ENABLE_SYNC -#pragma comment(lib, "ws2_32.lib") -#pragma comment(lib, "crypt32") -#endif - -#include "js_realm.hpp" -#include "js_notifications.hpp" - -#include -#include - -namespace realmjsi = realm::js::realmjsi; -namespace fbjsi = facebook::jsi; - -namespace realm { -namespace js { - -std::function flush_ui_queue; - -} // namespace js -} // namespace realm - -namespace realm::js::jsi { -extern "C" void realm_jsi_init(fbjsi::Runtime& rt, fbjsi::Object& exports, std::function flush_ui_queue) -{ - // Store the function used to flush React Native microtask queue - js::flush_ui_queue = flush_ui_queue; - - auto env = JsiEnv(rt); - fbjsi::Function realm_constructor = js::RealmClass::create_constructor(env); - auto name = realm_constructor.getProperty(env, "name").asString(env); - exports.setProperty(env, std::move(name), std::move(realm_constructor)); -} - -extern "C" void realm_jsi_invalidate_caches() -{ - // Close all cached Realms - realm::_impl::RealmCoordinator::clear_all_caches(); - // Clear the Object Store App cache, to prevent instances from using a context that was released - realm::app::App::clear_cached_apps(); - // Clear notifications - realm::js::notifications::NotificationBucket::clear(); - realm::js::notifications::NotificationBucket::Token>::clear(); - realm::js::notifications::NotificationBucket::Token>::clear(); - // Ensure all registered invalidators get notified that the runtime is going away. - realm::js::Context::invalidate(); -} - -// Note: This must be called before RJSInvalidateCaches, otherwise the app cache -// will have been cleared and so no sync sessions will be closed -extern "C" void realm_jsi_close_sync_sessions() -{ - // Force all sync sessions to close immediately. This prevents the new JS thread - // from opening a new sync session while the old one is still active when reloading - // in dev mode. - realm::app::App::close_all_sync_sessions(); -} -} // namespace realm::js::jsi - -// TODO hook up as TurboModule diff --git a/packages/realm/package.json b/packages/realm/package.json index 67c8b96d9d6..2b5b10ab576 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -72,6 +72,7 @@ "bundle:coverage": "ENABLE_TEST_COVERAGE_INSTRUMENTATION=true npm run bundle", "bindgen:configure": "wireit", "bindgen:build:node": "wireit", + "bindgen:generate:jsi": "wireit", "bindgen:generate:typescript": "wireit", "bindgen:generate:wrappers": "wireit", "bindgen:generate:spec-schema": "wireit", @@ -82,10 +83,6 @@ "build:node:prebuild:x64": "wireit", "build:node:prebuild:ia32": "wireit", "build:android": "wireit", - "build:ios": "wireit", - "build:ios:debug:simulator": "wireit", - "build:ios:debug:ios": "wireit", - "build:ios:debug:catalyst": "wireit", "install": "prebuild-install --runtime napi || echo 'Failed to download prebuild for Realm'", "docs": "wireit", "postinstall": "node ./scripts/submit-analytics.mjs" @@ -131,6 +128,24 @@ "bindgen:configure" ] }, + "bindgen:generate:jsi": { + "command": "realm-bindgen --template bindgen/src/templates/jsi.ts --spec bindgen/vendor/realm-core/bindgen/spec.yml --spec bindgen/js_spec.yml --opt-in bindgen/js_opt_in_spec.yml --output ./react-native/shared", + "dependencies": [ + "bindgen:generate:spec-schema" + ], + "files": [ + "bindgen/vendor/realm-core/bindgen/spec.yml", + "bindgen/vendor/realm-core/bindgen/src", + "bindgen/js_spec.yml", + "bindgen/js_opt_in_spec.yml", + "bindgen/src", + "!bindgen/src/templates", + "bindgen/src/templates/jsi.ts" + ], + "output": [ + "react-native/shared/jsi_init.cpp" + ] + }, "bindgen:generate:typescript": { "command": "realm-bindgen --template bindgen/src/templates/typescript.ts --spec bindgen/vendor/realm-core/bindgen/spec.yml --spec bindgen/js_spec.yml --opt-in bindgen/js_opt_in_spec.yml --output ./generated/ts", "dependencies": [ @@ -229,45 +244,6 @@ "bindgen/src/**/*.h" ] }, - "build:ios": { - "command": "scripts/build-ios.sh -c ${CONFIGURATION:=Release} ${PLATFORMS}", - "files": [ - "../../src/ios/**", - "bindgen/src/**/*.ts", - "bindgen/vendor/realm-core/bindgen/src/**/*.ts", - "bindgen/vendor/realm-core/bindgen/src/**/*.h", - "bindgen/src/**/*.h" - ], - "env": { - "PLATFORMS": { - "external": true - }, - "CONFIGURATION": { - "external": true - } - } - }, - "build:ios:debug:simulator": { - "command": "npm run build:ios", - "env": { - "PLATFORMS": "simulator", - "CONFIGURATION": "Debug" - } - }, - "build:ios:debug:ios": { - "command": "npm run build:ios", - "env": { - "PLATFORMS": "ios", - "CONFIGURATION": "Debug" - } - }, - "build:ios:debug:catalyst": { - "command": "npm run build:ios", - "env": { - "PLATFORMS": "catalyst", - "CONFIGURATION": "Debug" - } - }, "docs": { "command": "typedoc", "dependencies": [ diff --git a/packages/realm/react-native/ios/RealmReact/RealmReact.mm b/packages/realm/react-native/ios/RealmReact/RealmReact.mm index 55f97afb0cd..fdbc748187f 100644 --- a/packages/realm/react-native/ios/RealmReact/RealmReact.mm +++ b/packages/realm/react-native/ios/RealmReact/RealmReact.mm @@ -18,7 +18,7 @@ #import "RealmReact.h" -#import +#import #import #import diff --git a/packages/realm/react-native/ios/output-files.xcfilelist b/packages/realm/react-native/ios/output-files.xcfilelist new file mode 100644 index 00000000000..a18b3f2d12e --- /dev/null +++ b/packages/realm/react-native/ios/output-files.xcfilelist @@ -0,0 +1,5 @@ + +$(SRCROOT)/react-native/ios/build/lib/librealm.a +$(SRCROOT)/react-native/ios/build/lib/librealm-sync.a +$(SRCROOT)/react-native/ios/build/lib/librealm-parser.a +$(SRCROOT)/react-native/ios/build/lib/librealm-object-store.a diff --git a/packages/realm/scripts/build-ios-on-install.sh b/packages/realm/scripts/build-ios-on-install.sh deleted file mode 100755 index 1303bbebff3..00000000000 --- a/packages/realm/scripts/build-ios-on-install.sh +++ /dev/null @@ -1,134 +0,0 @@ -#!/bin/bash - -set -e -set -o pipefail - -# Start in the root directory of the project. -cd "$(dirname "$0")/.." - -# Need npx and node -NODE_DIRECTORY=$(dirname "$NODE_PATH") -export PATH="$NODE_DIRECTORY:$PATH" - -# Take homebrew installs as well -CMAKE_DIRECTORY=$(dirname "$CMAKE_PATH") -export PATH="$CMAKE_DIRECTORY:$PATH" - -PROJECT_ROOT=$(pwd) -SDK_PATH=$PROJECT_ROOT -BINDGEN_PATH=$PROJECT_ROOT/bindgen -BINDING_PATH=$PROJECT_ROOT/binding -SCRIPT=$(basename "${BASH_SOURCE[0]}") - -function usage { - echo "Usage: ${SCRIPT} [-c ] []" - echo "" - echo "Arguments:" - echo " -c : build configuration (Debug or Release)" - echo " : platforms to build for (maccatalyst, ios, or iphonesimulator)" - exit 1; -} - -CONFIGURATION=Release -SUPPORT_PLATFORMS=(maccatalyst ios iphonesimulator) - -function is_supported_platform(){ - for platform in "${SUPPORT_PLATFORMS[@]}"; do - [[ "${platform}" == $1 ]] && return 0 - done - return 1 -} - -# Parse the options -while getopts ":c:" opt; do - case "${opt}" in - c) CONFIGURATION=${OPTARG};; - *) usage;; - esac -done - -echo "Configuration: ${CONFIGURATION}" - -shift $((OPTIND-1)) -PLATFORMS=($@) - -if [ -z ${PLATFORMS} ]; then - echo "No platform given. building all platforms..."; - PLATFORMS=(ios maccatalyst iphonesimulator) -else - echo "Building for..."; - for check_platform in "${PLATFORMS[@]}"; do - if ! is_supported_platform $check_platform; then - echo "${check_platform} is not a supported platform" - usage - exit 1 - fi - echo ${check_platform}; - done -fi - -DESTINATIONS=() - -# TODO: Can this be infered from xcode env variables? (maccatalyst appears to be a unicorn) -for platform in "${PLATFORMS[@]}"; do - case "$platform" in - ios) - DESTINATIONS+=(-destination 'generic/platform=iOS') - ;; - maccatalyst) - DESTINATIONS+=(-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst') - ;; - iphonesimulator) - DESTINATIONS+=(-destination 'generic/platform=iOS Simulator') - ;; - *) - echo "${platform} not supported" - usage - exit 1 - ;; - esac -done - -pushd $SDK_PATH/react-native/ios - -mkdir -p build -pushd build - -# If the developer directory is not set, use the default Xcode path -SELECTED_DEVELOPER_DIR="$(xcode-select -p)" -DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" - -# Configure CMake project -env DEVELOPER_DIR="$DEVELOPER_DIR" SDKROOT="$SDKROOT" $CMAKE_PATH "$BINDGEN_PATH" -GXcode \ - -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out" \ - - -DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ - -scheme realm-js-ios \ - "${DESTINATIONS[@]}" \ - -configuration $CONFIGURATION \ - ONLY_ACTIVE_ARCH=NO \ - BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SUPPORTS_MACCATALYST=YES - -# copy needed headers into project -rm -rf _include -mkdir -p _include/realm-js-ios -cp "$BINDING_PATH"/jsi/jsi_init.h _include/realm-js-ios/ - -# copy built into a unified location -rm -rf libs -mkdir libs -cp out/$CONFIGURATION/*.a libs - -# Rename the *.a files so there are the same regardles if configuration was debug or release - -pushd libs - -find . -type f -name "*-dbg*" | while read -r file; do - # Construct new filename by removing '-dbg' - newfile=$(echo "$file" | sed 's/-dbg//') - # Rename the file - mv "$file" "$newfile" -done diff --git a/packages/realm/scripts/build-ios.sh b/packages/realm/scripts/build-ios.sh deleted file mode 100755 index 1773ffd30ed..00000000000 --- a/packages/realm/scripts/build-ios.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail - -# Start in the root directory of the project. -cd "$(dirname "$0")/.." -PROJECT_ROOT=$(pwd) -SDK_PATH=$PROJECT_ROOT -BINDGEN_PATH=$PROJECT_ROOT/bindgen -BINDING_PATH=$PROJECT_ROOT/binding -SCRIPT=$(basename "${BASH_SOURCE[0]}") - -function usage { - echo "Usage: ${SCRIPT} [-c ] []" - echo "" - echo "Arguments:" - echo " -c : build configuration (Debug or Release)" - echo " : platforms to build for (catalyst, ios, or simulator)" - exit 1; -} - -CONFIGURATION=Release -SUPPORT_PLATFORMS=(catalyst ios simulator) - -function is_supported_platform(){ - for platform in "${SUPPORT_PLATFORMS[@]}"; do - [[ "${platform}" == $1 ]] && return 0 - done - return 1 -} - -# Parse the options -while getopts ":c:" opt; do - case "${opt}" in - c) CONFIGURATION=${OPTARG};; - *) usage;; - esac -done - -echo "Configuration: ${CONFIGURATION}" - -shift $((OPTIND-1)) -PLATFORMS=($@) - -if [ -z ${PLATFORMS} ]; then - echo "No platform given. building all platforms..."; - PLATFORMS=(ios catalyst simulator) -else - echo "Building for..."; - for check_platform in "${PLATFORMS[@]}"; do - if ! is_supported_platform $check_platform; then - echo "${check_platform} is not a supported platform" - usage - exit 1 - fi - echo ${check_platform}; - done -fi - -DESTINATIONS=() -LIBRARIES=() -BUILD_LIB_CMDS=() - -for platform in "${PLATFORMS[@]}"; do - case "$platform" in - ios) - DESTINATIONS+=(-destination 'generic/platform=iOS') - LIBRARIES+=(-library ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphoneos/librealm-js-ios.a ./out/$CONFIGURATION-iphoneos/*.a") - ;; - catalyst) - DESTINATIONS+=(-destination 'platform=macOS,arch=x86_64,variant=Mac Catalyst') - LIBRARIES+=(-library ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-maccatalyst/librealm-js-ios.a ./out/$CONFIGURATION-maccatalyst/*.a") - ;; - simulator) - DESTINATIONS+=(-destination 'generic/platform=iOS Simulator') - LIBRARIES+=(-library ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a -headers ./_include) - BUILD_LIB_CMDS+=("xcrun libtool -static -D -o ./out/$CONFIGURATION-iphonesimulator/librealm-js-ios.a ./out/$CONFIGURATION-iphonesimulator/*.a") - ;; - *) - echo "${platform} not supported" - usage - exit 1 - ;; - esac -done - -pushd $SDK_PATH/react-native/ios - -mkdir -p build -pushd build - -# If the developer directory is not set, use the default Xcode path -SELECTED_DEVELOPER_DIR="$(xcode-select -p)" -DEVELOPER_DIR="${DEVELOPER_DIR:-${SELECTED_DEVELOPER_DIR}}" - -# Configure CMake project -SDKROOT="$DEVELOPER_DIR/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/" cmake "$BINDGEN_PATH" -GXcode \ - -DCMAKE_TOOLCHAIN_FILE="$BINDGEN_PATH/vendor/realm-core/tools/cmake/xcode.toolchain.cmake" \ - -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="$(pwd)/out/$\$EFFECTIVE_PLATFORM_NAME" \ - -DEVELOPER_DIR="$DEVELOPER_DIR" xcodebuild build \ - -scheme realm-js-ios \ - "${DESTINATIONS[@]}" \ - -configuration $CONFIGURATION \ - CC="$PROJECT_ROOT/../../scripts/ccache-clang.sh" \ - CXX="$PROJECT_ROOT/../../scripts/ccache-clang++.sh" \ - ONLY_ACTIVE_ARCH=NO \ - BUILD_LIBRARY_FOR_DISTRIBUTION=YES \ - SUPPORTS_MACCATALYST=YES - -for cmd in "${BUILD_LIB_CMDS[@]}"; do - eval "${cmd}" -done - -rm -rf _include -mkdir -p _include/realm-js-ios -cp "$BINDING_PATH"/jsi/jsi_init.h _include/realm-js-ios/ - -rm -rf ../realm-js-ios.xcframework -xcodebuild -create-xcframework \ - "${LIBRARIES[@]}" \ - -output ../realm-js-ios.xcframework diff --git a/packages/realm/scripts/generate-dummy-libs.sh b/packages/realm/scripts/generate-dummy-libs.sh new file mode 100755 index 00000000000..68a4b47ad05 --- /dev/null +++ b/packages/realm/scripts/generate-dummy-libs.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +BUILD_DIR=react-native/ios/build +LIB_DIR=react-native/ios/lib + +rm -rf $LIB_DIR +mkdir $LIB_DIR +pushd $LIB_DIR + +touch librealm.a librealm-sync.a librealm-parser.a librealm-object-store.a +popd + +# Wiping the build dir. If this is being called, then this will ensure the phase script is executed again. +rm -rf $BUILD_DIR diff --git a/packages/realm/scripts/generate-input-list.sh b/packages/realm/scripts/generate-input-list.sh new file mode 100755 index 00000000000..d05c2cb1c5a --- /dev/null +++ b/packages/realm/scripts/generate-input-list.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +REALM_CORE_SRC=bindgen/vendor/realm-core/src +INPUT_FILELIST=react-native/ios/input-files.xcfilelist + +# Clear the file list +> $INPUT_FILELIST + +find "$REALM_CORE_SRC" -type f \( -name "*.h" -o -name "*.cpp" -o -name "*.hpp" \) | while read -r file; do + echo "\$(SRCROOT)/$file" >> "$INPUT_FILELIST" +done + +echo "\$(SRCROOT)/bindgen/vendor/realm-core/dependencies.list" >> "$INPUT_FILELIST" diff --git a/packages/realm/scripts/generate-ios-libs.sh b/packages/realm/scripts/generate-ios-libs.sh new file mode 100755 index 00000000000..19b6f14e906 --- /dev/null +++ b/packages/realm/scripts/generate-ios-libs.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -e +set -o pipefail + +# Start in the root directory of the project. +cd "$(dirname "$0")/.." + +# Take homebrew installs as well +CMAKE_DIRECTORY=$(dirname "$CMAKE_PATH") +export PATH="$CMAKE_DIRECTORY:$PATH" + +PROJECT_ROOT=$PODS_TARGET_SRCROOT +BINDGEN_PATH=$PROJECT_ROOT/bindgen +BINDING_PATH=$PROJECT_ROOT/binding + +pushd $PROJECT_ROOT/react-native/ios + +# Should we wipe this directory first? +mkdir -p build +pushd build + +# Get core version +CORE_VERSION=$(grep "^VERSION=" $BINDGEN_PATH/vendor/realm-core/dependencies.list | cut -d '=' -f2) +TAR_FILE_NAME=realm-Release-v$CORE_VERSION-$PLATFORM_NAME-devel.tar.gz + +# There are only Release builds available on the CDN +CDN_URL="https://static.realm.io/downloads/core/v$CORE_VERSION/$PLATFORM_NAME/Release/$TAR_FILE_NAME" + +# Check if URL is valid and reachable +if ! curl --output /dev/null --silent --head --fail "$CDN_URL"; then + echo "URL $CDN_URL is not valid or reachable." + BUILD_CORE = "true" +fi + +if $BUILD_CORE == "true"; then + echo "Building realm-core..." + pushd $PROJECT_ROOT/bindgen/vendor/realm-core + ./tools/build-apple-device.sh -p $PLATFORM_NAME -c $CONFIGURATION + cp -R _CPack_Packages/$PLATFORM_NAME/TGZ/realm-$CONFIGURATION-v$CORE_VERSION-$PLATFORM_NAME/devel/* $PROJECT_ROOT/react-native/ios/build + popd +else + # Download core prebuild and extract + curl -o $TAR_FILE_NAME $CDN_URL + # Check if the download was successful + if [ -f "$TAR_FILE_NAME" ]; then + # Extract the contents + tar -xzf "$TAR_FILE_NAME" + + rm "$TAR_FILE_NAME" + else + echo "Failed to find required core libraries." + fi +fi + +pushd lib + +# Debug builds add '-dbg' to the filename, but xcode has links created, so renaming is necessary +find . -type f -name "*-dbg*" | while read -r file; do + # Construct new filename by removing '-dbg' + newfile=$(echo "$file" | sed 's/-dbg//') + # Rename the file + mv "$file" "$newfile" +done + +# Overwite the linked vendored libraries +cp *.a ../../lib + +popd From 4d6d0b2e2b5420d40cfc271334102359ec3906d9 Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Wed, 17 Jan 2024 16:39:07 +0100 Subject: [PATCH 13/14] Use an BUILD_REALM_CORE env var to build core from source --- integration-tests/environments/react-native/ios/Podfile | 6 ------ packages/realm/RealmJS.podspec | 3 +++ packages/realm/scripts/generate-ios-libs.sh | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/integration-tests/environments/react-native/ios/Podfile b/integration-tests/environments/react-native/ios/Podfile index b24ef6c6198..1a7507b1858 100644 --- a/integration-tests/environments/react-native/ios/Podfile +++ b/integration-tests/environments/react-native/ios/Podfile @@ -73,12 +73,6 @@ target 'RealmReactNativeTests' do # Applying https://github.com/facebook/folly/issues/1470#issuecomment-943123653 config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)', 'FOLLY_HAVE_CLOCK_GETTIME=1'] end - - if target.name == "RealmJS" - target.build_configurations.each do |config| - config.build_settings['BUILD_CORE'] = 'true' - end - end end end end diff --git a/packages/realm/RealmJS.podspec b/packages/realm/RealmJS.podspec index b34c53fa62d..ea5ae4f1c39 100644 --- a/packages/realm/RealmJS.podspec +++ b/packages/realm/RealmJS.podspec @@ -5,6 +5,8 @@ package = JSON.parse(File.read(File.expand_path('package.json', __dir__))) app_path = File.expand_path('../..', __dir__) +build_realm_core = ENV["BUILD_REALM_CORE"] == "1" + # There is no API to detect the use of "use_frameworks!" in the Podfile which depends on this Podspec. # The "React" framework is only available and should be used if the Podfile calls use_frameworks! # Therefore we make an assumption on the location of the Podfile and check if it contains "use_frameworks!" ... @@ -92,6 +94,7 @@ Pod::Spec.new do |s| :input_file_lists => ["#{__dir__}/react-native/ios/input-files.xcfilelist"], :output_file_lists => ["#{__dir__}/react-native/ios/output-files.xcfilelist"], :script => <<-EOS + BUILD_REALM_CORE="#{build_realm_core ? '1' : '0'}" CMAKE_PATH="#{CMAKE_PATH}" source "#{__dir__}/scripts/generate-ios-libs.sh" EOS diff --git a/packages/realm/scripts/generate-ios-libs.sh b/packages/realm/scripts/generate-ios-libs.sh index 19b6f14e906..e339c333920 100755 --- a/packages/realm/scripts/generate-ios-libs.sh +++ b/packages/realm/scripts/generate-ios-libs.sh @@ -30,10 +30,10 @@ CDN_URL="https://static.realm.io/downloads/core/v$CORE_VERSION/$PLATFORM_NAME/Re # Check if URL is valid and reachable if ! curl --output /dev/null --silent --head --fail "$CDN_URL"; then echo "URL $CDN_URL is not valid or reachable." - BUILD_CORE = "true" + BUILD_REALM_CORE = "1" fi -if $BUILD_CORE == "true"; then +if [ "$BUILD_REALM_CORE" == "1" ]; then echo "Building realm-core..." pushd $PROJECT_ROOT/bindgen/vendor/realm-core ./tools/build-apple-device.sh -p $PLATFORM_NAME -c $CONFIGURATION From c4e1a1397fd0b1dfb8c6dc88680f368d3e57129b Mon Sep 17 00:00:00 2001 From: Andrew Meyer Date: Fri, 19 Jan 2024 09:37:19 +0100 Subject: [PATCH 14/14] Prepare for public consumption * update the packed contents of the package * update relevant documentation --- contrib/debugging-react-native.md | 13 ++++++++----- packages/realm/README.md | 2 +- packages/realm/bindgen/vendor/realm-core | 2 +- packages/realm/package.json | 8 +++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/contrib/debugging-react-native.md b/contrib/debugging-react-native.md index a54c8d63049..e925ae78e59 100644 --- a/contrib/debugging-react-native.md +++ b/contrib/debugging-react-native.md @@ -14,9 +14,12 @@ In your realm-js project, run the following commands to prepare for debug mode: ``` # install js dependencies, but skip building the node binaries $ npm install --ignore-scripts +``` + +When running `pod install` for iOS, be sure to set the environment variable `BUILD_REALM_CORE=1`. This will flag core to be built from source with the Debug configuration. -# build the xcframework -$ ./scripts/build-ios.sh -c Debug -s +``` +BUILD_REALM_CORE=1 npx pod-install ``` You are now prepared to either use the [ReactTestApp](#setup-reacttestapp) or [prepare a custom project](#setup-custom-react-native-project) @@ -30,7 +33,7 @@ There is a test application in `tests/ReactTestApp` which will run all the unit $ cd tests $ npm install -# install the test app dependencies and copy realm and realm tests into node_modules using `install-local` +# install the test app dependencies and copy realm and realm tests into node_modules using `install-local` $ cd ReactTestApp $ npm install $ npx install-local @@ -41,8 +44,8 @@ $ open ios/ReactTestApp.xcworkspace ``` You should now be able to move onto -- [Debugging Javascript](#debugging-javascript) -- [Debugging C++](#debugging-c++) +- [Debugging Javascript](#debugging-javascript) +- [Debugging C++](#debugging-c++) ## Setup Custom React Native Project diff --git a/packages/realm/README.md b/packages/realm/README.md index 4edab2f88fc..0e4ff34982e 100644 --- a/packages/realm/README.md +++ b/packages/realm/README.md @@ -138,7 +138,7 @@ rm -rf ios/Pods rm ios/Podfile.lock rm -rf ~/Library/Developer/Xcode/DerivedData ``` -Afterwards, reinstall pods and try again. If this still doesn't work, ensure that `node_modules/realm/react-native/ios/realm-js-ios.xcframework` exists and contains a binary for your architecture. If this is missing, try reinstalling the `realm`` npm package. +Afterwards, reinstall pods and try again. If this still doesn't work, ensure that `node_modules/realm/react-native/ios/lib` directory exists and contains a libraries (`.a` files). If this is missing, try reinstalling the `realm` npm package and as well as cocoapods. #### Android This can occur when installing `realm` and not performing a clean build. The following commands can be used to clear your cache: diff --git a/packages/realm/bindgen/vendor/realm-core b/packages/realm/bindgen/vendor/realm-core index 71f94d75e25..669c9b85333 160000 --- a/packages/realm/bindgen/vendor/realm-core +++ b/packages/realm/bindgen/vendor/realm-core @@ -1 +1 @@ -Subproject commit 71f94d75e25bfc8913fcd93ae8de550b57577a4a +Subproject commit 669c9b85333e9bd7074909c51649875e0aa7376c diff --git a/packages/realm/package.json b/packages/realm/package.json index 2b5b10ab576..9bcd3461d05 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -54,11 +54,13 @@ "index.react-native.js", "types.d.cts", "react-native/android", - "react-native/ios/realm-js-ios.xcframework", - "react-native/ios/realm-js-ios.xcframework/**/*.a", "react-native/ios/RealmReact", - "bindgen/vendor/realm-core/dependencies.list", + "react-native/ios/output-files.xcfilelist", + "react-native/shared", + "binding", + "bindgen", "scripts/submit-analytics.mjs", + "scripts/generate-*.sh", "react-native.config.js", "RealmJS.podspec", "binding.gyp"