├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── external ├── CMakeLists.txt └── gtest │ └── CMakeLists.txt ├── jsi └── jsi │ ├── .clang-format │ ├── CMakeLists.txt │ ├── JSIDynamic.cpp │ ├── JSIDynamic.h │ ├── README.md │ ├── decorator.h │ ├── instrumentation.h │ ├── jsi-inl.h │ ├── jsi.cpp │ ├── jsi.h │ ├── jsilib-posix.cpp │ ├── jsilib-windows.cpp │ ├── jsilib.h │ ├── test │ ├── testlib.cpp │ ├── testlib.h │ └── testlib_ext.cpp │ └── threadsafe.h ├── node-api ├── .clang-format ├── js_native_api.h ├── js_native_api_types.h └── js_runtime_api.h ├── src ├── ApiLoaders │ ├── HermesApi.cpp │ ├── HermesApi.h │ ├── HermesApi.inc │ ├── JSRuntimeApi.cpp │ ├── JSRuntimeApi.h │ ├── JSRuntimeApi.inc │ ├── NodeApi.cpp │ ├── NodeApi.h │ ├── NodeApi.inc │ ├── NodeApi_posix.cpp │ ├── NodeApi_win.cpp │ ├── V8Api.cpp │ ├── V8Api.h │ └── V8Api.inc ├── NodeApiJsiRuntime.cpp └── NodeApiJsiRuntime.h └── tests ├── CMakeLists.txt ├── JsiRuntimeTests.cpp └── NuGet.config /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: AlwaysBreak 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlinesLeft: true 7 | AlignOperands: false 8 | AlignTrailingComments: false 9 | AllowAllParametersOfDeclarationOnNextLine: false 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: Empty 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: true 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BraceWrapping: 21 | AfterClass: false 22 | AfterControlStatement: false 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterObjCDeclaration: false 27 | AfterStruct: false 28 | AfterUnion: false 29 | BeforeCatch: false 30 | BeforeElse: false 31 | IndentBraces: false 32 | BreakBeforeBinaryOperators: None 33 | BreakBeforeBraces: Attach 34 | BreakBeforeTernaryOperators: true 35 | BreakConstructorInitializersBeforeComma: false 36 | BreakAfterJavaFieldAnnotations: false 37 | BreakStringLiterals: false 38 | ColumnLimit: 120 39 | CommentPragmas: '^ IWYU pragma:' 40 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 41 | ConstructorInitializerIndentWidth: 4 42 | ContinuationIndentWidth: 4 43 | Cpp11BracedListStyle: true 44 | DerivePointerAlignment: false 45 | DisableFormat: false 46 | ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, TEST_CLASS, TEST_CLASS_EX ] 47 | IncludeBlocks: Preserve 48 | IncludeCategories: 49 | - Regex: 'pch.h' 50 | Priority: -1 51 | - Regex: '.*\.g\..*' 52 | Priority: 1 53 | - Regex: '^<.*\.h(pp)?>' 54 | Priority: 2 55 | - Regex: '^<.*' 56 | Priority: 3 57 | - Regex: '.*' 58 | Priority: 4 59 | IndentCaseLabels: true 60 | IndentWidth: 2 61 | IndentWrappedFunctionNames: false 62 | KeepEmptyLinesAtTheStartOfBlocks: false 63 | MacroBlockBegin: '' 64 | MacroBlockEnd: '' 65 | MaxEmptyLinesToKeep: 1 66 | NamespaceIndentation: None 67 | ObjCBlockIndentWidth: 2 68 | ObjCSpaceAfterProperty: true 69 | ObjCSpaceBeforeProtocolList: true 70 | PenaltyBreakBeforeFirstCallParameter: 1 71 | PenaltyBreakComment: 300 72 | PenaltyBreakFirstLessLess: 120 73 | PenaltyBreakString: 1000 74 | PenaltyExcessCharacter: 1000000 75 | PenaltyReturnTypeOnItsOwnLine: 200 76 | PointerAlignment: Right 77 | ReflowComments: true 78 | SortIncludes: true 79 | SpaceAfterCStyleCast: false 80 | SpaceBeforeAssignmentOperators: true 81 | SpaceBeforeParens: ControlStatements 82 | SpaceInEmptyParentheses: false 83 | SpacesBeforeTrailingComments: 1 84 | SpacesInAngles: false 85 | SpacesInContainerLiterals: true 86 | SpacesInCStyleCastParentheses: false 87 | SpacesInParentheses: false 88 | SpacesInSquareBrackets: false 89 | Standard: Cpp11 90 | TabWidth: 8 91 | UseTab: Never 92 | ... 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | .vscode/ 353 | build/ 354 | out/ 355 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # set minimum cmake version 5 | cmake_minimum_required(VERSION 3.16 FATAL_ERROR) 6 | cmake_policy(SET CMP0092 NEW) 7 | 8 | # project name and language 9 | project(NodeApiJsi 10 | HOMEPAGE_URL "https://github.com/microsoft/node-api-jsi" 11 | LANGUAGES CXX) 12 | 13 | set(CMAKE_CXX_STANDARD 17) 14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | 17 | add_subdirectory(external) 18 | add_subdirectory(tests) 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSI for Node-API 2 | 3 | This project implements JSI for Node-API. 4 | 5 | [Node-API](https://nodejs.org/api/n-api.html) is an ABI safe API defined by Node.JS project. 6 | 7 | [JSI](https://github.com/facebook/hermes/tree/main/API/jsi/jsi) is a C++ API used 8 | by [React Native](https://github.com/facebook/react-native) to abstract access to 9 | JavaScript engines. 10 | 11 | The JSI implementation on top of Node-API enables use of JS engines 12 | that implement Node-API with React Native. 13 | 14 | ## Contributing 15 | 16 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 17 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 18 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 19 | 20 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 21 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 22 | provided by the bot. You will only need to do this once across all repos using our CLA. 23 | 24 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 25 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 26 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 27 | 28 | ## Trademarks 29 | 30 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 31 | trademarks or logos is subject to and must follow 32 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 33 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 34 | Any use of third-party trademarks or logos are subject to those third-party's policies. 35 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please refer to the product documentation 10 | or use GitHub discussions for this project. 11 | 12 | ## Microsoft Support Policy 13 | 14 | Support for this **JSI for Node-API** is limited to the resources listed above. 15 | -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | add_subdirectory(gtest) 5 | -------------------------------------------------------------------------------- /external/gtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | # the following code to fetch googletest 5 | # is inspired by and adapted after: 6 | # - https://cmake.org/cmake/help/v3.11/module/FetchContent.html 7 | # 8 | # We use version of GTest as in the https://github.com/Microsoft/TestAdapterForGoogleTest 9 | # It allows to run GTests in Visual Studio and ADO. 10 | # Please update the GIT_TAG below after the TAFGT is updated the reference. 11 | include(FetchContent) 12 | 13 | FetchContent_Declare( 14 | googletest 15 | URL https://github.com/google/googletest/archive/06519cedc3159de8b36a504766ad6b7966555f10.zip 16 | DOWNLOAD_EXTRACT_TIMESTAMP true 17 | ) 18 | 19 | FetchContent_GetProperties(googletest) 20 | 21 | if(NOT googletest_POPULATED) 22 | FetchContent_Populate(googletest) 23 | 24 | # Prevent GoogleTest from overriding our compiler/linker options 25 | # when building with Visual Studio 26 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 27 | # Prevent GoogleTest from using PThreads 28 | set(gtest_disable_pthreads ON CACHE BOOL "" FORCE) 29 | 30 | # adds the targets: gtest, gtest_main, gmock, gmock_main 31 | add_subdirectory( 32 | ${googletest_SOURCE_DIR} 33 | ${googletest_BINARY_DIR} 34 | ) 35 | 36 | # Silence std::tr1 warning on MSVC 37 | if(MSVC) 38 | foreach(_tgt gtest gtest_main gmock gmock_main) 39 | target_compile_definitions(${_tgt} 40 | PRIVATE 41 | "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING" 42 | ) 43 | endforeach() 44 | endif() 45 | endif() 46 | -------------------------------------------------------------------------------- /jsi/jsi/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: InheritParentConfig 3 | PointerAlignment: Left 4 | ColumnLimit: 80 5 | ... 6 | -------------------------------------------------------------------------------- /jsi/jsi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | set(CMAKE_CXX_STANDARD 14) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | add_library(jsi 10 | jsi.cpp) 11 | 12 | target_include_directories(jsi PUBLIC ..) 13 | 14 | 15 | set(jsi_compile_flags "") 16 | if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR 17 | "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") 18 | list(APPEND jsi_compile_flags "-Wno-non-virtual-dtor") 19 | elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") 20 | # Turn on Error Handling in MSVC, otherwise objects are not destructed 21 | # when they go out of scope due to exceptions. 22 | list(APPEND jsi_compile_flags "/EHsc") 23 | list(APPEND jsi_compile_flags "/Zi") 24 | list(APPEND jsi_compile_flags "/Qspectre") 25 | list(APPEND jsi_compile_flags "/sdl") 26 | endif() 27 | target_compile_options(jsi PRIVATE ${jsi_compile_flags}) 28 | 29 | install(DIRECTORY "${PROJECT_SOURCE_DIR}/API/jsi/" DESTINATION include 30 | FILES_MATCHING PATTERN "*.h" 31 | PATTERN "test" EXCLUDE) 32 | -------------------------------------------------------------------------------- /jsi/jsi/JSIDynamic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include "JSIDynamic.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | using namespace facebook::jsi; 16 | 17 | namespace facebook { 18 | namespace jsi { 19 | 20 | namespace { 21 | 22 | struct FromDynamic { 23 | FromDynamic(const folly::dynamic* dynArg, Object objArg) 24 | : dyn(dynArg), obj(std::move(objArg)) {} 25 | 26 | const folly::dynamic* dyn; 27 | Object obj; 28 | }; 29 | 30 | // This converts one element. If it's a collection, it gets pushed onto 31 | // the stack for later processing. 32 | Value valueFromDynamicShallow( 33 | Runtime& runtime, 34 | std::vector& stack, 35 | const folly::dynamic& dyn) { 36 | switch (dyn.type()) { 37 | case folly::dynamic::NULLT: 38 | return Value::null(); 39 | case folly::dynamic::ARRAY: { 40 | Object arr = Array(runtime, dyn.size()); 41 | Value ret = Value(runtime, arr); 42 | stack.emplace_back(&dyn, std::move(arr)); 43 | return ret; 44 | } 45 | case folly::dynamic::BOOL: 46 | return Value(dyn.getBool()); 47 | case folly::dynamic::DOUBLE: 48 | return dyn.getDouble(); 49 | case folly::dynamic::INT64: 50 | return Value((double)dyn.getInt()); 51 | case folly::dynamic::OBJECT: { 52 | auto obj = Object(runtime); 53 | Value ret = Value(runtime, obj); 54 | stack.emplace_back(&dyn, std::move(obj)); 55 | return ret; 56 | } 57 | case folly::dynamic::STRING: 58 | return Value(String::createFromUtf8(runtime, dyn.getString())); 59 | } 60 | CHECK(false); 61 | } 62 | 63 | } // namespace 64 | 65 | Value valueFromDynamic(Runtime& runtime, const folly::dynamic& dynInput) { 66 | std::vector stack; 67 | 68 | Value ret = valueFromDynamicShallow(runtime, stack, dynInput); 69 | 70 | while (!stack.empty()) { 71 | auto top = std::move(stack.back()); 72 | stack.pop_back(); 73 | 74 | switch (top.dyn->type()) { 75 | case folly::dynamic::ARRAY: { 76 | Array arr = std::move(top.obj).getArray(runtime); 77 | for (size_t i = 0; i < top.dyn->size(); ++i) { 78 | arr.setValueAtIndex( 79 | runtime, 80 | i, 81 | valueFromDynamicShallow(runtime, stack, (*top.dyn)[i])); 82 | } 83 | break; 84 | } 85 | case folly::dynamic::OBJECT: { 86 | Object obj = std::move(top.obj); 87 | for (const auto& element : top.dyn->items()) { 88 | if (element.first.isNumber() || element.first.isString()) { 89 | obj.setProperty( 90 | runtime, 91 | PropNameID::forUtf8(runtime, element.first.asString()), 92 | valueFromDynamicShallow(runtime, stack, element.second)); 93 | } 94 | } 95 | break; 96 | } 97 | default: 98 | CHECK(false); 99 | } 100 | } 101 | 102 | return ret; 103 | } 104 | 105 | namespace { 106 | 107 | struct FromValue { 108 | FromValue(folly::dynamic* dynArg, Object objArg) 109 | : dyn(dynArg), obj(std::move(objArg)) {} 110 | 111 | folly::dynamic* dyn; 112 | Object obj; 113 | }; 114 | 115 | // This converts one element. If it's a collection, it gets pushed 116 | // onto the stack for later processing. The output is created by 117 | // mutating the output argument, because we need its actual pointer to 118 | // push onto the stack. 119 | void dynamicFromValueShallow( 120 | Runtime& runtime, 121 | std::vector& stack, 122 | const jsi::Value& value, 123 | folly::dynamic& output) { 124 | if (value.isUndefined() || value.isNull()) { 125 | output = nullptr; 126 | } else if (value.isBool()) { 127 | output = value.getBool(); 128 | } else if (value.isNumber()) { 129 | output = value.getNumber(); 130 | } else if (value.isString()) { 131 | output = value.getString(runtime).utf8(runtime); 132 | } else if (value.isObject()) { 133 | Object obj = value.getObject(runtime); 134 | if (obj.isArray(runtime)) { 135 | output = folly::dynamic::array(); 136 | } else if (obj.isFunction(runtime)) { 137 | throw JSError(runtime, "JS Functions are not convertible to dynamic"); 138 | } else { 139 | output = folly::dynamic::object(); 140 | } 141 | stack.emplace_back(&output, std::move(obj)); 142 | #if JSI_VERSION >= 8 143 | } else if (value.isBigInt()) { 144 | throw JSError(runtime, "JS BigInts are not convertible to dynamic"); 145 | #endif 146 | } else if (value.isSymbol()) { 147 | throw JSError(runtime, "JS Symbols are not convertible to dynamic"); 148 | } else { 149 | throw JSError(runtime, "Value is not convertible to dynamic"); 150 | } 151 | } 152 | 153 | } // namespace 154 | 155 | folly::dynamic dynamicFromValue( 156 | Runtime& runtime, 157 | const Value& valueInput, 158 | const std::function& filterObjectKeys) { 159 | std::vector stack; 160 | folly::dynamic ret; 161 | 162 | dynamicFromValueShallow(runtime, stack, valueInput, ret); 163 | 164 | while (!stack.empty()) { 165 | auto top = std::move(stack.back()); 166 | stack.pop_back(); 167 | 168 | if (top.obj.isArray(runtime)) { 169 | // Inserting into a dyn can invalidate references into it, so we 170 | // need to insert new elements up front, then push stuff onto 171 | // the stack. 172 | Array array = top.obj.getArray(runtime); 173 | size_t arraySize = array.size(runtime); 174 | for (size_t i = 0; i < arraySize; ++i) { 175 | top.dyn->push_back(nullptr); 176 | } 177 | for (size_t i = 0; i < arraySize; ++i) { 178 | dynamicFromValueShallow( 179 | runtime, stack, array.getValueAtIndex(runtime, i), top.dyn->at(i)); 180 | } 181 | } else { 182 | Array names = top.obj.getPropertyNames(runtime); 183 | std::vector> props; 184 | for (size_t i = 0; i < names.size(runtime); ++i) { 185 | String name = names.getValueAtIndex(runtime, i).getString(runtime); 186 | Value prop = top.obj.getProperty(runtime, name); 187 | if (prop.isUndefined()) { 188 | continue; 189 | } 190 | auto nameStr = name.utf8(runtime); 191 | if (filterObjectKeys && filterObjectKeys(nameStr)) { 192 | continue; 193 | } 194 | // The JSC conversion uses JSON.stringify, which substitutes 195 | // null for a function, so we do the same here. Just dropping 196 | // the pair might also work, but would require more testing. 197 | if (prop.isObject() && prop.getObject(runtime).isFunction(runtime)) { 198 | prop = Value::null(); 199 | } 200 | props.emplace_back(std::move(nameStr), std::move(prop)); 201 | top.dyn->insert(props.back().first, nullptr); 202 | } 203 | for (const auto& prop : props) { 204 | dynamicFromValueShallow( 205 | runtime, stack, prop.second, (*top.dyn)[prop.first]); 206 | } 207 | } 208 | } 209 | 210 | return ret; 211 | } 212 | 213 | } // namespace jsi 214 | } // namespace facebook 215 | -------------------------------------------------------------------------------- /jsi/jsi/JSIDynamic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | 13 | namespace facebook { 14 | namespace jsi { 15 | 16 | facebook::jsi::Value valueFromDynamic( 17 | facebook::jsi::Runtime& runtime, 18 | const folly::dynamic& dyn); 19 | 20 | folly::dynamic dynamicFromValue( 21 | facebook::jsi::Runtime& runtime, 22 | const facebook::jsi::Value& value, 23 | const std::function& filterObjectKeys = nullptr); 24 | 25 | } // namespace jsi 26 | } // namespace facebook 27 | -------------------------------------------------------------------------------- /jsi/jsi/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Interface 2 | 3 | This folder contains definitions for JSI. 4 | JSI is the public API for Hermes engine. 5 | It is being used by React Native project to work with JS engines. 6 | 7 | ## JSI versions 8 | 9 | JSI has versions associated with the following commit hashes in the 10 | https://github.com/facebook/hermes repo. 11 | 12 | | Version | Commit Hash | Commit Description 13 | |--------:|:-------------------------------------------|------------------------------------------------------ 14 | | 19 | `00a84e7bae9b8569e7b3f4118f4238544c67fe1b` | Add createFromUtf16 JSI method 15 | | 18 | `d0328097291aade7269b2879910e11c48c8fbeb1` | Add default implementation for Object.create(prototype) 16 | | 17 | `1012f891165e7dda3b51939b75bbc52e16e48a75` | Add default implementation for Object.getPrototypeOf and Object.setPrototypeOf 17 | | 16 | `215bbe6ddc75393332b74138205e7cf5ab874e4e` | Add default getStringData/getPropNameIdData implementation 18 | | 15 | `54e428c749358fb4aa25c6d7e2dcc3fc23b47124` | Let Pointer be nothrow-move-constructible 19 | | | `c582a23d62505a00948f5aa49006f60f959f8df4` | Let PointerValue::invalidate() be noexcept 20 | | | `b07ef4fc10dd53ad542f811040c820064c5ceb57` | Let Value be nothrow-move-constructible 21 | | 14 | `c98b00e88bb685e75b769be67919a23a7f03b2e0` | Add utf16 method to JSI 22 | | 13 | `077f2633dea36e1d0d18d6c4f114bf66e3ab74cf` | Add HeapSnapshotOptions for jsi::Instrumentation 23 | | 12 | `de9dfe408bc3f8715aef161c5fcec291fc6dacb2` | Add queueMicrotask method to JSI 24 | | | `a699e87f995bc2f7193990ff36a57ec9cad940e5` | Make queueMicrotask pure virtual 25 | | 11 | `a1c168705f609c8f1ae800c60d88eb199154264b` | Add JSI method for setting external memory size 26 | | 10 | `b81666598672cb5f8b365fe6548d3273f216322e` | Clarify const-ness of JSI references 27 | | 9 | `e6d887ae96bef5c71032f11ed1a9fb9fecec7b46` | Add external ArrayBuffers to JSI 28 | | 8 | `4d64e61a1f9926eca0afd4eb38d17cea30bdc34c` | Add BigInt JSI API support 29 | | | `e8cc57311877464478da5421265bcc898098e136` | Add methods for creating and interacting with BigInt 30 | | 7 | `4efad65b8266e3f26e04e3ca9addf92fc4d6ded8` | Add API for setting/getting native state 31 | | 6 | `bc3cfb87fbfc82732936ec4445c9765bf9a5f08a` | Add BigInt skeleton 32 | | 5 | `2b6d408980d7f23f50602fd88169c8a9881592a6` | Add PropNameID::fromSymbol 33 | | 4 | `a5bee55c8301bb8662e408feee28bbc3e2a1fc81` | Introduce drainMicrotasks to JSI 34 | | 3 | `0c9daa5a5eee7649558a53e3e541b80c89048c42` | Change jsi::Runtime::lockWeakObject to take a mutable ref 35 | | 2 | `e0616e77e1ddc3ea5e2ccbca2e20dd0c4049c637` | Make it possible for a Runtime implementation to provide its own JSON parsing 36 | | 1 | `3ba9615f80913764ecb6456779d502e31dde9e5d` | Fix build break in MSVC (#26462) 37 | | 0 | `f22a18f67da3f03db59c1ec715d6ec3776b03fbf` | Initial commit 38 | 39 | Relationship to React Native versions: 40 | 41 | | Hermes RN Version | JSI version | 42 | |------------------:|------------:| 43 | | main | 19 | 44 | | 0.79.0 | 19 | 45 | | 0.78.0 | 18 | 46 | | 0.77.0 | 15 | 47 | | 0.76.0 | 13 | 48 | | 0.74.3 | 12 | 49 | | 0.74.0 | 11 | 50 | | 0.73.0 | 10 | 51 | | 0.72.0 | 10 | 52 | | 0.71.0 | 9 | 53 | | 0.70.0 | 6 | 54 | | 0.69.0 | 5 | 55 | -------------------------------------------------------------------------------- /jsi/jsi/instrumentation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | namespace facebook { 19 | namespace jsi { 20 | 21 | /// Methods for starting and collecting instrumentation, an \c Instrumentation 22 | /// instance is associated with a particular \c Runtime instance, which it 23 | /// controls the instrumentation of. 24 | /// None of these functions should return newly created jsi values, nor should 25 | /// it modify the values of any jsi values in the heap (although GCs are fine). 26 | class JSI_EXPORT Instrumentation { 27 | public: 28 | #if JSI_VERSION >= 13 29 | /// Additional options controlling what to include when capturing a heap 30 | /// snapshot. 31 | struct HeapSnapshotOptions { 32 | bool captureNumericValue{false}; 33 | }; 34 | #endif 35 | 36 | virtual ~Instrumentation() = default; 37 | 38 | /// Returns GC statistics as a JSON-encoded string, with an object containing 39 | /// "type" and "version" fields outermost. "type" is a string, unique to a 40 | /// particular implementation of \c jsi::Instrumentation, and "version" is a 41 | /// number to indicate any revision to that implementation and its output 42 | /// format. 43 | /// 44 | /// \pre This call can only be made on the instrumentation instance of a 45 | /// runtime initialised to collect GC statistics. 46 | /// 47 | /// \post All cumulative measurements mentioned in the output are accumulated 48 | /// across the entire lifetime of the Runtime. 49 | /// 50 | /// \return the GC statistics collected so far, as a JSON-encoded string. 51 | virtual std::string getRecordedGCStats() = 0; 52 | 53 | /// Request statistics about the current state of the runtime's heap. This 54 | /// function can be called at any time, and should produce information that is 55 | /// correct at the instant it is called (i.e, not stale). 56 | /// 57 | /// \return a map from a string key to a number associated with that 58 | /// statistic. 59 | virtual std::unordered_map getHeapInfo( 60 | bool includeExpensive) = 0; 61 | 62 | /// Perform a full garbage collection. 63 | /// \param cause The cause of this collection, as it should be reported in 64 | /// logs. 65 | virtual void collectGarbage(std::string cause) = 0; 66 | 67 | /// A HeapStatsUpdate is a tuple of the fragment index, the number of objects 68 | /// in that fragment, and the number of bytes used by those objects. 69 | /// A "fragment" is a view of all objects allocated within a time slice. 70 | using HeapStatsUpdate = std::tuple; 71 | 72 | /// Start capturing JS stack-traces for all JS heap allocated objects. These 73 | /// can be accessed via \c ::createSnapshotToFile(). 74 | /// \param fragmentCallback If present, invoke this callback every so often 75 | /// with the most recently seen object ID, and a list of fragments that have 76 | /// been updated. This callback will be invoked on the same thread that the 77 | /// runtime is using. 78 | virtual void startTrackingHeapObjectStackTraces( 79 | std::function stats)> fragmentCallback) = 0; 83 | 84 | /// Stop capture JS stack-traces for JS heap allocated objects. 85 | virtual void stopTrackingHeapObjectStackTraces() = 0; 86 | 87 | /// Start a heap sampling profiler that will sample heap allocations, and the 88 | /// stack trace they were allocated at. Reports a summary of which functions 89 | /// allocated the most. 90 | /// \param samplingInterval The number of bytes allocated to wait between 91 | /// samples. This will be used as the expected value of a poisson 92 | /// distribution. 93 | virtual void startHeapSampling(size_t samplingInterval) = 0; 94 | 95 | /// Turns off the heap sampling profiler previously enabled via 96 | /// \c startHeapSampling. Writes the output of the sampling heap profiler to 97 | /// \p os. The output is a JSON formatted string. 98 | virtual void stopHeapSampling(std::ostream& os) = 0; 99 | 100 | #if JSI_VERSION >= 13 101 | /// Captures the heap to a file 102 | /// 103 | /// \param path to save the heap capture. 104 | /// \param options additional options for what to capture. 105 | virtual void createSnapshotToFile( 106 | const std::string& path, 107 | const HeapSnapshotOptions& options = {false}) = 0; 108 | #else 109 | /// Captures the heap to a file 110 | /// 111 | /// \param path to save the heap capture 112 | virtual void createSnapshotToFile(const std::string& path) = 0; 113 | #endif 114 | 115 | #if JSI_VERSION >= 13 116 | /// Captures the heap to an output stream 117 | /// 118 | /// \param os output stream to write to. 119 | /// \param options additional options for what to capture. 120 | virtual void createSnapshotToStream( 121 | std::ostream& os, 122 | const HeapSnapshotOptions& options = {false}) = 0; 123 | #else 124 | /// Captures the heap to an output stream 125 | /// 126 | /// \param os output stream to write to. 127 | virtual void createSnapshotToStream(std::ostream& os) = 0; 128 | #endif 129 | 130 | /// If the runtime has been created to trace to a temp file, flush 131 | /// any unwritten parts of the trace of bridge traffic to the file, 132 | /// and return the name of the file. Otherwise, return the empty string. 133 | /// Tracing is disabled after this call. 134 | virtual std::string flushAndDisableBridgeTrafficTrace() = 0; 135 | 136 | /// Write basic block profile trace to the given file name. 137 | virtual void writeBasicBlockProfileTraceToFile( 138 | const std::string& fileName) const = 0; 139 | 140 | /// Dump external profiler symbols to the given file name. 141 | virtual void dumpProfilerSymbolsToFile(const std::string& fileName) const = 0; 142 | }; 143 | 144 | } // namespace jsi 145 | } // namespace facebook 146 | -------------------------------------------------------------------------------- /jsi/jsi/jsi-inl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | namespace facebook { 11 | namespace jsi { 12 | namespace detail { 13 | 14 | inline Value toValue(Runtime&, std::nullptr_t) { 15 | return Value::null(); 16 | } 17 | inline Value toValue(Runtime&, bool b) { 18 | return Value(b); 19 | } 20 | inline Value toValue(Runtime&, double d) { 21 | return Value(d); 22 | } 23 | inline Value toValue(Runtime&, float f) { 24 | return Value(static_cast(f)); 25 | } 26 | inline Value toValue(Runtime&, int i) { 27 | return Value(i); 28 | } 29 | inline Value toValue(Runtime& runtime, const char* str) { 30 | return String::createFromAscii(runtime, str); 31 | } 32 | inline Value toValue(Runtime& runtime, const std::string& str) { 33 | return String::createFromUtf8(runtime, str); 34 | } 35 | template 36 | inline Value toValue(Runtime& runtime, const T& other) { 37 | static_assert( 38 | std::is_base_of::value, 39 | "This type cannot be converted to Value"); 40 | return Value(runtime, other); 41 | } 42 | inline Value toValue(Runtime& runtime, const Value& value) { 43 | return Value(runtime, value); 44 | } 45 | inline Value&& toValue(Runtime&, Value&& value) { 46 | return std::move(value); 47 | } 48 | 49 | inline PropNameID toPropNameID(Runtime& runtime, const char* name) { 50 | return PropNameID::forAscii(runtime, name); 51 | } 52 | inline PropNameID toPropNameID(Runtime& runtime, const std::string& name) { 53 | return PropNameID::forUtf8(runtime, name); 54 | } 55 | inline PropNameID&& toPropNameID(Runtime&, PropNameID&& name) { 56 | return std::move(name); 57 | } 58 | 59 | /// Helper to throw while still compiling with exceptions turned off. 60 | template 61 | [[noreturn]] inline void throwOrDie(Args&&... args) { 62 | std::rethrow_exception( 63 | std::make_exception_ptr(E{std::forward(args)...})); 64 | } 65 | 66 | } // namespace detail 67 | 68 | template 69 | inline T Runtime::make(Runtime::PointerValue* pv) { 70 | return T(pv); 71 | } 72 | 73 | #if JSI_VERSION >= 3 74 | inline Runtime::PointerValue* Runtime::getPointerValue(jsi::Pointer& pointer) { 75 | return pointer.ptr_; 76 | } 77 | #endif 78 | 79 | inline const Runtime::PointerValue* Runtime::getPointerValue( 80 | const jsi::Pointer& pointer) { 81 | return pointer.ptr_; 82 | } 83 | 84 | inline const Runtime::PointerValue* Runtime::getPointerValue( 85 | const jsi::Value& value) { 86 | return value.data_.pointer.ptr_; 87 | } 88 | 89 | #if JSI_VERSION >= 17 90 | Value Object::getPrototype(Runtime& runtime) const { 91 | return runtime.getPrototypeOf(*this); 92 | } 93 | #endif 94 | 95 | inline Value Object::getProperty(Runtime& runtime, const char* name) const { 96 | return getProperty(runtime, String::createFromAscii(runtime, name)); 97 | } 98 | 99 | inline Value Object::getProperty(Runtime& runtime, const String& name) const { 100 | return runtime.getProperty(*this, name); 101 | } 102 | 103 | inline Value Object::getProperty(Runtime& runtime, const PropNameID& name) 104 | const { 105 | return runtime.getProperty(*this, name); 106 | } 107 | 108 | inline bool Object::hasProperty(Runtime& runtime, const char* name) const { 109 | return hasProperty(runtime, String::createFromAscii(runtime, name)); 110 | } 111 | 112 | inline bool Object::hasProperty(Runtime& runtime, const String& name) const { 113 | return runtime.hasProperty(*this, name); 114 | } 115 | 116 | inline bool Object::hasProperty(Runtime& runtime, const PropNameID& name) 117 | const { 118 | return runtime.hasProperty(*this, name); 119 | } 120 | 121 | template 122 | void Object::setProperty(Runtime& runtime, const char* name, T&& value) 123 | JSI_CONST_10 { 124 | setProperty( 125 | runtime, String::createFromAscii(runtime, name), std::forward(value)); 126 | } 127 | 128 | template 129 | void Object::setProperty(Runtime& runtime, const String& name, T&& value) 130 | JSI_CONST_10 { 131 | setPropertyValue( 132 | runtime, name, detail::toValue(runtime, std::forward(value))); 133 | } 134 | 135 | template 136 | void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) 137 | JSI_CONST_10 { 138 | setPropertyValue( 139 | runtime, name, detail::toValue(runtime, std::forward(value))); 140 | } 141 | 142 | inline Array Object::getArray(Runtime& runtime) const& { 143 | assert(runtime.isArray(*this)); 144 | (void)runtime; // when assert is disabled we need to mark this as used 145 | return Array(runtime.cloneObject(ptr_)); 146 | } 147 | 148 | inline Array Object::getArray(Runtime& runtime) && { 149 | assert(runtime.isArray(*this)); 150 | (void)runtime; // when assert is disabled we need to mark this as used 151 | Runtime::PointerValue* value = ptr_; 152 | ptr_ = nullptr; 153 | return Array(value); 154 | } 155 | 156 | inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) const& { 157 | assert(runtime.isArrayBuffer(*this)); 158 | (void)runtime; // when assert is disabled we need to mark this as used 159 | return ArrayBuffer(runtime.cloneObject(ptr_)); 160 | } 161 | 162 | inline ArrayBuffer Object::getArrayBuffer(Runtime& runtime) && { 163 | assert(runtime.isArrayBuffer(*this)); 164 | (void)runtime; // when assert is disabled we need to mark this as used 165 | Runtime::PointerValue* value = ptr_; 166 | ptr_ = nullptr; 167 | return ArrayBuffer(value); 168 | } 169 | 170 | inline Function Object::getFunction(Runtime& runtime) const& { 171 | assert(runtime.isFunction(*this)); 172 | return Function(runtime.cloneObject(ptr_)); 173 | } 174 | 175 | inline Function Object::getFunction(Runtime& runtime) && { 176 | assert(runtime.isFunction(*this)); 177 | (void)runtime; // when assert is disabled we need to mark this as used 178 | Runtime::PointerValue* value = ptr_; 179 | ptr_ = nullptr; 180 | return Function(value); 181 | } 182 | 183 | template 184 | inline bool Object::isHostObject(Runtime& runtime) const { 185 | return runtime.isHostObject(*this) && 186 | std::dynamic_pointer_cast(runtime.getHostObject(*this)); 187 | } 188 | 189 | template <> 190 | inline bool Object::isHostObject(Runtime& runtime) const { 191 | return runtime.isHostObject(*this); 192 | } 193 | 194 | template 195 | inline std::shared_ptr Object::getHostObject(Runtime& runtime) const { 196 | assert(isHostObject(runtime)); 197 | return std::static_pointer_cast(runtime.getHostObject(*this)); 198 | } 199 | 200 | template 201 | inline std::shared_ptr Object::asHostObject(Runtime& runtime) const { 202 | if (!isHostObject(runtime)) { 203 | detail::throwOrDie( 204 | "Object is not a HostObject of desired type"); 205 | } 206 | return std::static_pointer_cast(runtime.getHostObject(*this)); 207 | } 208 | 209 | template <> 210 | inline std::shared_ptr Object::getHostObject( 211 | Runtime& runtime) const { 212 | assert(runtime.isHostObject(*this)); 213 | return runtime.getHostObject(*this); 214 | } 215 | 216 | #if JSI_VERSION >= 7 217 | template 218 | inline bool Object::hasNativeState(Runtime& runtime) const { 219 | return runtime.hasNativeState(*this) && 220 | std::dynamic_pointer_cast(runtime.getNativeState(*this)); 221 | } 222 | 223 | template <> 224 | inline bool Object::hasNativeState(Runtime& runtime) const { 225 | return runtime.hasNativeState(*this); 226 | } 227 | 228 | template 229 | inline std::shared_ptr Object::getNativeState(Runtime& runtime) const { 230 | assert(hasNativeState(runtime)); 231 | return std::static_pointer_cast(runtime.getNativeState(*this)); 232 | } 233 | 234 | inline void Object::setNativeState( 235 | Runtime& runtime, 236 | std::shared_ptr state) const { 237 | runtime.setNativeState(*this, state); 238 | } 239 | #endif 240 | 241 | #if JSI_VERSION >= 11 242 | inline void Object::setExternalMemoryPressure(Runtime& runtime, size_t amt) 243 | const { 244 | runtime.setExternalMemoryPressure(*this, amt); 245 | } 246 | #endif 247 | 248 | inline Array Object::getPropertyNames(Runtime& runtime) const { 249 | return runtime.getPropertyNames(*this); 250 | } 251 | 252 | inline Value WeakObject::lock(Runtime& runtime) JSI_CONST_10 { 253 | return runtime.lockWeakObject(*this); 254 | } 255 | 256 | template 257 | void Array::setValueAtIndex(Runtime& runtime, size_t i, T&& value) 258 | JSI_CONST_10 { 259 | setValueAtIndexImpl( 260 | runtime, i, detail::toValue(runtime, std::forward(value))); 261 | } 262 | 263 | inline Value Array::getValueAtIndex(Runtime& runtime, size_t i) const { 264 | return runtime.getValueAtIndex(*this, i); 265 | } 266 | 267 | inline Function Function::createFromHostFunction( 268 | Runtime& runtime, 269 | const jsi::PropNameID& name, 270 | unsigned int paramCount, 271 | jsi::HostFunctionType func) { 272 | return runtime.createFunctionFromHostFunction( 273 | name, paramCount, std::move(func)); 274 | } 275 | 276 | inline Value Function::call(Runtime& runtime, const Value* args, size_t count) 277 | const { 278 | return runtime.call(*this, Value::undefined(), args, count); 279 | } 280 | 281 | inline Value Function::call(Runtime& runtime, std::initializer_list args) 282 | const { 283 | return call(runtime, args.begin(), args.size()); 284 | } 285 | 286 | template 287 | inline Value Function::call(Runtime& runtime, Args&&... args) const { 288 | // A more awesome version of this would be able to create raw values 289 | // which can be used directly without wrapping and unwrapping, but 290 | // this will do for now. 291 | return call(runtime, {detail::toValue(runtime, std::forward(args))...}); 292 | } 293 | 294 | inline Value Function::callWithThis( 295 | Runtime& runtime, 296 | const Object& jsThis, 297 | const Value* args, 298 | size_t count) const { 299 | return runtime.call(*this, Value(runtime, jsThis), args, count); 300 | } 301 | 302 | inline Value Function::callWithThis( 303 | Runtime& runtime, 304 | const Object& jsThis, 305 | std::initializer_list args) const { 306 | return callWithThis(runtime, jsThis, args.begin(), args.size()); 307 | } 308 | 309 | template 310 | inline Value Function::callWithThis( 311 | Runtime& runtime, 312 | const Object& jsThis, 313 | Args&&... args) const { 314 | // A more awesome version of this would be able to create raw values 315 | // which can be used directly without wrapping and unwrapping, but 316 | // this will do for now. 317 | return callWithThis( 318 | runtime, jsThis, {detail::toValue(runtime, std::forward(args))...}); 319 | } 320 | 321 | template 322 | inline Array Array::createWithElements(Runtime& runtime, Args&&... args) { 323 | return createWithElements( 324 | runtime, {detail::toValue(runtime, std::forward(args))...}); 325 | } 326 | 327 | template 328 | inline std::vector PropNameID::names( 329 | Runtime& runtime, 330 | Args&&... args) { 331 | return names({detail::toPropNameID(runtime, std::forward(args))...}); 332 | } 333 | 334 | template 335 | inline std::vector PropNameID::names( 336 | PropNameID (&&propertyNames)[N]) { 337 | std::vector result; 338 | result.reserve(N); 339 | for (auto& name : propertyNames) { 340 | result.push_back(std::move(name)); 341 | } 342 | return result; 343 | } 344 | 345 | inline Value Function::callAsConstructor( 346 | Runtime& runtime, 347 | const Value* args, 348 | size_t count) const { 349 | return runtime.callAsConstructor(*this, args, count); 350 | } 351 | 352 | inline Value Function::callAsConstructor( 353 | Runtime& runtime, 354 | std::initializer_list args) const { 355 | return callAsConstructor(runtime, args.begin(), args.size()); 356 | } 357 | 358 | template 359 | inline Value Function::callAsConstructor(Runtime& runtime, Args&&... args) 360 | const { 361 | return callAsConstructor( 362 | runtime, {detail::toValue(runtime, std::forward(args))...}); 363 | } 364 | 365 | #if JSI_VERSION >= 8 366 | String BigInt::toString(Runtime& runtime, int radix) const { 367 | return runtime.bigintToString(*this, radix); 368 | } 369 | #endif 370 | 371 | } // namespace jsi 372 | } // namespace facebook 373 | -------------------------------------------------------------------------------- /jsi/jsi/jsi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | namespace facebook { 17 | namespace jsi { 18 | 19 | namespace { 20 | 21 | // This is used for generating short exception strings. 22 | std::string kindToString(const Value& v, Runtime* rt = nullptr) { 23 | if (v.isUndefined()) { 24 | return "undefined"; 25 | } else if (v.isNull()) { 26 | return "null"; 27 | } else if (v.isBool()) { 28 | return v.getBool() ? "true" : "false"; 29 | } else if (v.isNumber()) { 30 | return "a number"; 31 | } else if (v.isString()) { 32 | return "a string"; 33 | } else if (v.isSymbol()) { 34 | return "a symbol"; 35 | #if JSI_VERSION >= 6 36 | } else if (v.isBigInt()) { 37 | return "a bigint"; 38 | #endif 39 | } else { 40 | assert(v.isObject() && "Expecting object."); 41 | return rt != nullptr && v.getObject(*rt).isFunction(*rt) ? "a function" 42 | : "an object"; 43 | } 44 | } 45 | 46 | // getPropertyAsFunction() will try to create a JSError. If the 47 | // failure is in building a JSError, this will lead to infinite 48 | // recursion. This function is used in place of getPropertyAsFunction 49 | // when building JSError, to avoid that infinite recursion. 50 | Value callGlobalFunction(Runtime& runtime, const char* name, const Value& arg) { 51 | Value v = runtime.global().getProperty(runtime, name); 52 | if (!v.isObject()) { 53 | throw JSINativeException( 54 | std::string("callGlobalFunction: JS global property '") + name + 55 | "' is " + kindToString(v, &runtime) + ", expected a Function"); 56 | } 57 | Object o = v.getObject(runtime); 58 | if (!o.isFunction(runtime)) { 59 | throw JSINativeException( 60 | std::string("callGlobalFunction: JS global property '") + name + 61 | "' is a non-callable Object, expected a Function"); 62 | } 63 | Function f = std::move(o).getFunction(runtime); 64 | return f.call(runtime, arg); 65 | } 66 | 67 | #if JSI_VERSION >= 14 68 | // Given a sequence of UTF8 encoded bytes, advance the input to past where a 69 | // 32-bit unicode codepoint as been decoded and return the codepoint. If the 70 | // UTF8 encoding is invalid, then return the value with the unicode replacement 71 | // character (U+FFFD). This decoder also relies on zero termination at end of 72 | // the input for bound checks. 73 | // \param input char pointer pointing to the current character 74 | // \return Unicode codepoint 75 | uint32_t decodeUTF8(const char*& input) { 76 | uint32_t ch = (unsigned char)input[0]; 77 | if (ch <= 0x7f) { 78 | input += 1; 79 | return ch; 80 | } 81 | uint32_t ret; 82 | constexpr uint32_t replacementCharacter = 0xFFFD; 83 | if ((ch & 0xE0) == 0xC0) { 84 | uint32_t ch1 = (unsigned char)input[1]; 85 | if ((ch1 & 0xC0) != 0x80) { 86 | input += 1; 87 | return replacementCharacter; 88 | } 89 | ret = ((ch & 0x1F) << 6) | (ch1 & 0x3F); 90 | input += 2; 91 | if (ret <= 0x7F) { 92 | return replacementCharacter; 93 | } 94 | } else if ((ch & 0xF0) == 0xE0) { 95 | uint32_t ch1 = (unsigned char)input[1]; 96 | if ((ch1 & 0x40) != 0 || (ch1 & 0x80) == 0) { 97 | input += 1; 98 | return replacementCharacter; 99 | } 100 | uint32_t ch2 = (unsigned char)input[2]; 101 | if ((ch2 & 0x40) != 0 || (ch2 & 0x80) == 0) { 102 | input += 2; 103 | return replacementCharacter; 104 | } 105 | ret = ((ch & 0x0F) << 12) | ((ch1 & 0x3F) << 6) | (ch2 & 0x3F); 106 | input += 3; 107 | if (ret <= 0x7FF) { 108 | return replacementCharacter; 109 | } 110 | } else if ((ch & 0xF8) == 0xF0) { 111 | uint32_t ch1 = (unsigned char)input[1]; 112 | if ((ch1 & 0x40) != 0 || (ch1 & 0x80) == 0) { 113 | input += 1; 114 | return replacementCharacter; 115 | } 116 | uint32_t ch2 = (unsigned char)input[2]; 117 | if ((ch2 & 0x40) != 0 || (ch2 & 0x80) == 0) { 118 | input += 2; 119 | return replacementCharacter; 120 | } 121 | uint32_t ch3 = (unsigned char)input[3]; 122 | if ((ch3 & 0x40) != 0 || (ch3 & 0x80) == 0) { 123 | input += 3; 124 | return replacementCharacter; 125 | } 126 | ret = ((ch & 0x07) << 18) | ((ch1 & 0x3F) << 12) | ((ch2 & 0x3F) << 6) | 127 | (ch3 & 0x3F); 128 | input += 4; 129 | if (ret <= 0xFFFF) { 130 | return replacementCharacter; 131 | } 132 | if (ret > 0x10FFFF) { 133 | return replacementCharacter; 134 | } 135 | } else { 136 | input += 1; 137 | return replacementCharacter; 138 | } 139 | return ret; 140 | } 141 | 142 | // Given a valid 32-bit unicode codepoint, encode it as UTF-16 into the output. 143 | void encodeUTF16(std::u16string& out, uint32_t cp) { 144 | if (cp < 0x10000) { 145 | out.push_back((uint16_t)cp); 146 | return; 147 | } 148 | cp -= 0x10000; 149 | uint16_t highSurrogate = 0xD800 + ((cp >> 10) & 0x3FF); 150 | out.push_back(highSurrogate); 151 | uint16_t lowSurrogate = 0xDC00 + (cp & 0x3FF); 152 | out.push_back(lowSurrogate); 153 | } 154 | 155 | // Convert the UTF8 encoded string into a UTF16 encoded string. If the 156 | // input is not valid UTF8, the replacement character (U+FFFD) is used to 157 | // represent the invalid sequence. 158 | std::u16string convertUTF8ToUTF16(const std::string& utf8) { 159 | std::u16string ret; 160 | const char* curr = utf8.data(); 161 | const char* end = curr + utf8.length(); 162 | while (curr < end) { 163 | auto cp = decodeUTF8(curr); 164 | encodeUTF16(ret, cp); 165 | } 166 | return ret; 167 | } 168 | #endif 169 | 170 | #if JSI_VERSION >= 19 171 | // Given a unsigned number, which is less than 16, return the hex character. 172 | inline char hexDigit(unsigned x) { 173 | return static_cast(x < 10 ? '0' + x : 'A' + (x - 10)); 174 | } 175 | 176 | // Given a sequence of UTF 16 code units, return true if all code units are 177 | // ASCII characters 178 | bool isAllASCII(const char16_t* utf16, size_t length) { 179 | for (const char16_t* e = utf16 + length; utf16 != e; ++utf16) { 180 | if (*utf16 > 0x7F) 181 | return false; 182 | } 183 | return true; 184 | } 185 | 186 | // Given a sequences of UTF 16 code units, return a string that explicitly 187 | // expresses the code units 188 | std::string getUtf16CodeUnitString(const char16_t* utf16, size_t length) { 189 | // Every character will need 4 hex digits + the character escape "\u". 190 | // Plus 2 character for the opening and closing single quote. 191 | std::string s = std::string(6 * length + 2, 0); 192 | s.front() = '\''; 193 | 194 | for (size_t i = 0; i != length; ++i) { 195 | char16_t ch = utf16[i]; 196 | size_t start = (6 * i) + 1; 197 | 198 | s[start] = '\\'; 199 | s[start + 1] = 'u'; 200 | 201 | s[start + 2] = hexDigit((ch >> 12) & 0x000f); 202 | s[start + 3] = hexDigit((ch >> 8) & 0x000f); 203 | s[start + 4] = hexDigit((ch >> 4) & 0x000f); 204 | s[start + 5] = hexDigit(ch & 0x000f); 205 | } 206 | s.back() = '\''; 207 | return s; 208 | } 209 | #endif 210 | 211 | } // namespace 212 | 213 | Buffer::~Buffer() = default; 214 | 215 | #if JSI_VERSION >= 9 216 | MutableBuffer::~MutableBuffer() = default; 217 | #endif 218 | 219 | PreparedJavaScript::~PreparedJavaScript() = default; 220 | 221 | Value HostObject::get(Runtime&, const PropNameID&) { 222 | return Value(); 223 | } 224 | 225 | void HostObject::set(Runtime& rt, const PropNameID& name, const Value&) { 226 | std::string msg("TypeError: Cannot assign to property '"); 227 | msg += name.utf8(rt); 228 | msg += "' on HostObject with default setter"; 229 | throw JSError(rt, msg); 230 | } 231 | 232 | HostObject::~HostObject() {} 233 | 234 | #if JSI_VERSION >= 7 235 | NativeState::~NativeState() {} 236 | #endif 237 | 238 | Runtime::~Runtime() {} 239 | 240 | Instrumentation& Runtime::instrumentation() { 241 | class NoInstrumentation : public Instrumentation { 242 | std::string getRecordedGCStats() override { 243 | return ""; 244 | } 245 | 246 | std::unordered_map getHeapInfo(bool) override { 247 | return std::unordered_map{}; 248 | } 249 | 250 | void collectGarbage(std::string) override {} 251 | 252 | void startTrackingHeapObjectStackTraces( 253 | std::function)>) override {} 257 | void stopTrackingHeapObjectStackTraces() override {} 258 | 259 | void startHeapSampling(size_t) override {} 260 | void stopHeapSampling(std::ostream&) override {} 261 | 262 | #if JSI_VERSION >= 13 263 | void createSnapshotToFile( 264 | const std::string& /*path*/, 265 | const HeapSnapshotOptions& /*options*/) override 266 | #else 267 | void createSnapshotToFile(const std::string&) override 268 | #endif 269 | { 270 | throw JSINativeException( 271 | "Default instrumentation cannot create a heap snapshot"); 272 | } 273 | 274 | #if JSI_VERSION >= 13 275 | void createSnapshotToStream( 276 | std::ostream& /*os*/, 277 | const HeapSnapshotOptions& /*options*/) override 278 | #else 279 | void createSnapshotToStream(std::ostream&) override 280 | #endif 281 | { 282 | throw JSINativeException( 283 | "Default instrumentation cannot create a heap snapshot"); 284 | } 285 | 286 | std::string flushAndDisableBridgeTrafficTrace() override { 287 | std::abort(); 288 | } 289 | 290 | void writeBasicBlockProfileTraceToFile(const std::string&) const override { 291 | std::abort(); 292 | } 293 | 294 | void dumpProfilerSymbolsToFile(const std::string&) const override { 295 | std::abort(); 296 | } 297 | }; 298 | 299 | static NoInstrumentation sharedInstance; 300 | return sharedInstance; 301 | } 302 | 303 | #if JSI_VERSION >= 2 304 | Value Runtime::createValueFromJsonUtf8(const uint8_t* json, size_t length) { 305 | Function parseJson = global() 306 | .getPropertyAsObject(*this, "JSON") 307 | .getPropertyAsFunction(*this, "parse"); 308 | return parseJson.call(*this, String::createFromUtf8(*this, json, length)); 309 | } 310 | #else 311 | Value Value::createFromJsonUtf8( 312 | Runtime& runtime, 313 | const uint8_t* json, 314 | size_t length) { 315 | Function parseJson = runtime.global() 316 | .getPropertyAsObject(runtime, "JSON") 317 | .getPropertyAsFunction(runtime, "parse"); 318 | return parseJson.call(runtime, String::createFromUtf8(runtime, json, length)); 319 | } 320 | #endif 321 | 322 | #if JSI_VERSION >= 19 323 | String Runtime::createStringFromUtf16(const char16_t* utf16, size_t length) { 324 | if (isAllASCII(utf16, length)) { 325 | std::string buffer(length, '\0'); 326 | for (size_t i = 0; i < length; ++i) { 327 | buffer[i] = static_cast(utf16[i]); 328 | } 329 | return createStringFromAscii(buffer.data(), length); 330 | } 331 | auto s = getUtf16CodeUnitString(utf16, length); 332 | return global() 333 | .getPropertyAsFunction(*this, "eval") 334 | .call(*this, s) 335 | .getString(*this); 336 | } 337 | 338 | PropNameID Runtime::createPropNameIDFromUtf16( 339 | const char16_t* utf16, 340 | size_t length) { 341 | auto jsString = createStringFromUtf16(utf16, length); 342 | return createPropNameIDFromString(jsString); 343 | } 344 | #endif 345 | 346 | #if JSI_VERSION >= 14 347 | std::u16string Runtime::utf16(const PropNameID& sym) { 348 | auto utf8Str = utf8(sym); 349 | return convertUTF8ToUTF16(utf8Str); 350 | } 351 | 352 | std::u16string Runtime::utf16(const String& str) { 353 | auto utf8Str = utf8(str); 354 | return convertUTF8ToUTF16(utf8Str); 355 | } 356 | #endif 357 | 358 | #if JSI_VERSION >= 16 359 | void Runtime::getStringData( 360 | const jsi::String& str, 361 | void* ctx, 362 | void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) { 363 | auto utf16Str = utf16(str); 364 | cb(ctx, false, utf16Str.data(), utf16Str.size()); 365 | } 366 | 367 | void Runtime::getPropNameIdData( 368 | const jsi::PropNameID& sym, 369 | void* ctx, 370 | void (*cb)(void* ctx, bool ascii, const void* data, size_t num)) { 371 | auto utf16Str = utf16(sym); 372 | cb(ctx, false, utf16Str.data(), utf16Str.size()); 373 | } 374 | #endif 375 | 376 | #if JSI_VERSION >= 17 377 | void Runtime::setPrototypeOf(const Object& object, const Value& prototype) { 378 | auto setPrototypeOfFn = global() 379 | .getPropertyAsObject(*this, "Object") 380 | .getPropertyAsFunction(*this, "setPrototypeOf"); 381 | setPrototypeOfFn.call(*this, object, prototype).asObject(*this); 382 | } 383 | 384 | Value Runtime::getPrototypeOf(const Object& object) { 385 | auto setPrototypeOfFn = global() 386 | .getPropertyAsObject(*this, "Object") 387 | .getPropertyAsFunction(*this, "getPrototypeOf"); 388 | return setPrototypeOfFn.call(*this, object); 389 | } 390 | #endif 391 | 392 | #if JSI_VERSION >= 18 393 | Object Runtime::createObjectWithPrototype(const Value& prototype) { 394 | auto createFn = global() 395 | .getPropertyAsObject(*this, "Object") 396 | .getPropertyAsFunction(*this, "create"); 397 | return createFn.call(*this, prototype).asObject(*this); 398 | } 399 | #endif 400 | 401 | Pointer& Pointer::operator=(Pointer&& other) JSI_NOEXCEPT_15 { 402 | if (ptr_) { 403 | ptr_->invalidate(); 404 | } 405 | ptr_ = other.ptr_; 406 | other.ptr_ = nullptr; 407 | return *this; 408 | } 409 | 410 | Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const { 411 | Value v = getProperty(runtime, name); 412 | 413 | if (!v.isObject()) { 414 | throw JSError( 415 | runtime, 416 | std::string("getPropertyAsObject: property '") + name + "' is " + 417 | kindToString(v, &runtime) + ", expected an Object"); 418 | } 419 | 420 | return v.getObject(runtime); 421 | } 422 | 423 | Function Object::getPropertyAsFunction(Runtime& runtime, const char* name) 424 | const { 425 | Object obj = getPropertyAsObject(runtime, name); 426 | if (!obj.isFunction(runtime)) { 427 | throw JSError( 428 | runtime, 429 | std::string("getPropertyAsFunction: property '") + name + "' is " + 430 | kindToString(std::move(obj), &runtime) + ", expected a Function"); 431 | }; 432 | 433 | return std::move(obj).getFunction(runtime); 434 | } 435 | 436 | Array Object::asArray(Runtime& runtime) const& { 437 | if (!isArray(runtime)) { 438 | throw JSError( 439 | runtime, 440 | "Object is " + kindToString(Value(runtime, *this), &runtime) + 441 | ", expected an array"); 442 | } 443 | return getArray(runtime); 444 | } 445 | 446 | Array Object::asArray(Runtime& runtime) && { 447 | if (!isArray(runtime)) { 448 | throw JSError( 449 | runtime, 450 | "Object is " + kindToString(Value(runtime, *this), &runtime) + 451 | ", expected an array"); 452 | } 453 | return std::move(*this).getArray(runtime); 454 | } 455 | 456 | Function Object::asFunction(Runtime& runtime) const& { 457 | if (!isFunction(runtime)) { 458 | throw JSError( 459 | runtime, 460 | "Object is " + kindToString(Value(runtime, *this), &runtime) + 461 | ", expected a function"); 462 | } 463 | return getFunction(runtime); 464 | } 465 | 466 | Function Object::asFunction(Runtime& runtime) && { 467 | if (!isFunction(runtime)) { 468 | throw JSError( 469 | runtime, 470 | "Object is " + kindToString(Value(runtime, *this), &runtime) + 471 | ", expected a function"); 472 | } 473 | return std::move(*this).getFunction(runtime); 474 | } 475 | 476 | Value::Value(Value&& other) JSI_NOEXCEPT_15 : Value(other.kind_) { 477 | if (kind_ == BooleanKind) { 478 | data_.boolean = other.data_.boolean; 479 | } else if (kind_ == NumberKind) { 480 | data_.number = other.data_.number; 481 | } else if (kind_ >= PointerKind) { 482 | new (&data_.pointer) Pointer(std::move(other.data_.pointer)); 483 | } 484 | // when the other's dtor runs, nothing will happen. 485 | other.kind_ = UndefinedKind; 486 | } 487 | 488 | Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) { 489 | // data_ is uninitialized, so use placement new to create non-POD 490 | // types in it. Any other kind of initialization will call a dtor 491 | // first, which is incorrect. 492 | if (kind_ == BooleanKind) { 493 | data_.boolean = other.data_.boolean; 494 | } else if (kind_ == NumberKind) { 495 | data_.number = other.data_.number; 496 | } else if (kind_ == SymbolKind) { 497 | new (&data_.pointer) Pointer(runtime.cloneSymbol(other.data_.pointer.ptr_)); 498 | #if JSI_VERSION >= 6 499 | } else if (kind_ == BigIntKind) { 500 | new (&data_.pointer) Pointer(runtime.cloneBigInt(other.data_.pointer.ptr_)); 501 | #endif 502 | } else if (kind_ == StringKind) { 503 | new (&data_.pointer) Pointer(runtime.cloneString(other.data_.pointer.ptr_)); 504 | } else if (kind_ >= ObjectKind) { 505 | new (&data_.pointer) Pointer(runtime.cloneObject(other.data_.pointer.ptr_)); 506 | } 507 | } 508 | 509 | Value::~Value() { 510 | if (kind_ >= PointerKind) { 511 | data_.pointer.~Pointer(); 512 | } 513 | } 514 | 515 | bool Value::strictEquals(Runtime& runtime, const Value& a, const Value& b) { 516 | if (a.kind_ != b.kind_) { 517 | return false; 518 | } 519 | switch (a.kind_) { 520 | case UndefinedKind: 521 | case NullKind: 522 | return true; 523 | case BooleanKind: 524 | return a.data_.boolean == b.data_.boolean; 525 | case NumberKind: 526 | return a.data_.number == b.data_.number; 527 | case SymbolKind: 528 | return runtime.strictEquals( 529 | static_cast(a.data_.pointer), 530 | static_cast(b.data_.pointer)); 531 | #if JSI_VERSION >= 6 532 | case BigIntKind: 533 | return runtime.strictEquals( 534 | static_cast(a.data_.pointer), 535 | static_cast(b.data_.pointer)); 536 | #endif 537 | case StringKind: 538 | return runtime.strictEquals( 539 | static_cast(a.data_.pointer), 540 | static_cast(b.data_.pointer)); 541 | case ObjectKind: 542 | return runtime.strictEquals( 543 | static_cast(a.data_.pointer), 544 | static_cast(b.data_.pointer)); 545 | } 546 | return false; 547 | } 548 | 549 | bool Value::asBool() const { 550 | if (!isBool()) { 551 | throw JSINativeException( 552 | "Value is " + kindToString(*this) + ", expected a boolean"); 553 | } 554 | 555 | return getBool(); 556 | } 557 | 558 | double Value::asNumber() const { 559 | if (!isNumber()) { 560 | throw JSINativeException( 561 | "Value is " + kindToString(*this) + ", expected a number"); 562 | } 563 | 564 | return getNumber(); 565 | } 566 | 567 | Object Value::asObject(Runtime& rt) const& { 568 | if (!isObject()) { 569 | throw JSError( 570 | rt, "Value is " + kindToString(*this, &rt) + ", expected an Object"); 571 | } 572 | 573 | return getObject(rt); 574 | } 575 | 576 | Object Value::asObject(Runtime& rt) && { 577 | if (!isObject()) { 578 | throw JSError( 579 | rt, "Value is " + kindToString(*this, &rt) + ", expected an Object"); 580 | } 581 | auto ptr = data_.pointer.ptr_; 582 | data_.pointer.ptr_ = nullptr; 583 | return static_cast(ptr); 584 | } 585 | 586 | Symbol Value::asSymbol(Runtime& rt) const& { 587 | if (!isSymbol()) { 588 | throw JSError( 589 | rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol"); 590 | } 591 | 592 | return getSymbol(rt); 593 | } 594 | 595 | Symbol Value::asSymbol(Runtime& rt) && { 596 | if (!isSymbol()) { 597 | throw JSError( 598 | rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol"); 599 | } 600 | 601 | return std::move(*this).getSymbol(rt); 602 | } 603 | 604 | #if JSI_VERSION >= 6 605 | BigInt Value::asBigInt(Runtime& rt) const& { 606 | if (!isBigInt()) { 607 | throw JSError( 608 | rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt"); 609 | } 610 | 611 | return getBigInt(rt); 612 | } 613 | 614 | BigInt Value::asBigInt(Runtime& rt) && { 615 | if (!isBigInt()) { 616 | throw JSError( 617 | rt, "Value is " + kindToString(*this, &rt) + ", expected a BigInt"); 618 | } 619 | 620 | return std::move(*this).getBigInt(rt); 621 | } 622 | #endif 623 | 624 | String Value::asString(Runtime& rt) const& { 625 | if (!isString()) { 626 | throw JSError( 627 | rt, "Value is " + kindToString(*this, &rt) + ", expected a String"); 628 | } 629 | 630 | return getString(rt); 631 | } 632 | 633 | String Value::asString(Runtime& rt) && { 634 | if (!isString()) { 635 | throw JSError( 636 | rt, "Value is " + kindToString(*this, &rt) + ", expected a String"); 637 | } 638 | 639 | return std::move(*this).getString(rt); 640 | } 641 | 642 | String Value::toString(Runtime& runtime) const { 643 | Function toString = runtime.global().getPropertyAsFunction(runtime, "String"); 644 | return toString.call(runtime, *this).getString(runtime); 645 | } 646 | 647 | #if JSI_VERSION >= 8 648 | uint64_t BigInt::asUint64(Runtime& runtime) const { 649 | if (!isUint64(runtime)) { 650 | throw JSError(runtime, "Lossy truncation in BigInt64::asUint64"); 651 | } 652 | return getUint64(runtime); 653 | } 654 | 655 | int64_t BigInt::asInt64(Runtime& runtime) const { 656 | if (!isInt64(runtime)) { 657 | throw JSError(runtime, "Lossy truncation in BigInt64::asInt64"); 658 | } 659 | return getInt64(runtime); 660 | } 661 | #endif 662 | 663 | Array Array::createWithElements( 664 | Runtime& rt, 665 | std::initializer_list elements) { 666 | Array result(rt, elements.size()); 667 | size_t index = 0; 668 | for (const auto& element : elements) { 669 | result.setValueAtIndex(rt, index++, element); 670 | } 671 | return result; 672 | } 673 | 674 | std::vector HostObject::getPropertyNames(Runtime&) { 675 | return {}; 676 | } 677 | 678 | Runtime::ScopeState* Runtime::pushScope() { 679 | return nullptr; 680 | } 681 | 682 | void Runtime::popScope(ScopeState*) {} 683 | 684 | JSError::JSError(Runtime& rt, Value&& value) { 685 | setValue(rt, std::move(value)); 686 | } 687 | 688 | JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) { 689 | try { 690 | setValue( 691 | rt, 692 | callGlobalFunction(rt, "Error", String::createFromUtf8(rt, message_))); 693 | } catch (const JSIException& ex) { 694 | message_ = std::string(ex.what()) + " (while raising " + message_ + ")"; 695 | setValue(rt, String::createFromUtf8(rt, message_)); 696 | } 697 | } 698 | 699 | JSError::JSError(Runtime& rt, std::string msg, std::string stack) 700 | : message_(std::move(msg)), stack_(std::move(stack)) { 701 | try { 702 | Object e(rt); 703 | e.setProperty(rt, "message", String::createFromUtf8(rt, message_)); 704 | e.setProperty(rt, "stack", String::createFromUtf8(rt, stack_)); 705 | setValue(rt, std::move(e)); 706 | } catch (const JSIException& ex) { 707 | setValue(rt, String::createFromUtf8(rt, ex.what())); 708 | } 709 | } 710 | 711 | JSError::JSError(std::string what, Runtime& rt, Value&& value) 712 | : JSIException(std::move(what)) { 713 | setValue(rt, std::move(value)); 714 | } 715 | 716 | JSError::JSError(Value&& value, std::string message, std::string stack) 717 | : JSIException(message + "\n\n" + stack), 718 | value_(std::make_shared(std::move(value))), 719 | message_(std::move(message)), 720 | stack_(std::move(stack)) {} 721 | 722 | void JSError::setValue(Runtime& rt, Value&& value) { 723 | value_ = std::make_shared(std::move(value)); 724 | 725 | if ((message_.empty() || stack_.empty()) && value_->isObject()) { 726 | auto obj = value_->getObject(rt); 727 | 728 | if (message_.empty()) { 729 | try { 730 | Value message = obj.getProperty(rt, "message"); 731 | if (!message.isUndefined() && !message.isString()) { 732 | message = callGlobalFunction(rt, "String", message); 733 | } 734 | if (message.isString()) { 735 | message_ = message.getString(rt).utf8(rt); 736 | } else if (!message.isUndefined()) { 737 | message_ = "String(e.message) is a " + kindToString(message, &rt); 738 | } 739 | } catch (const JSIException& ex) { 740 | message_ = std::string("[Exception while creating message string: ") + 741 | ex.what() + "]"; 742 | } 743 | } 744 | 745 | if (stack_.empty()) { 746 | try { 747 | Value stack = obj.getProperty(rt, "stack"); 748 | if (!stack.isUndefined() && !stack.isString()) { 749 | stack = callGlobalFunction(rt, "String", stack); 750 | } 751 | if (stack.isString()) { 752 | stack_ = stack.getString(rt).utf8(rt); 753 | } else if (!stack.isUndefined()) { 754 | stack_ = "String(e.stack) is a " + kindToString(stack, &rt); 755 | } 756 | } catch (const JSIException& ex) { 757 | message_ = std::string("[Exception while creating stack string: ") + 758 | ex.what() + "]"; 759 | } 760 | } 761 | } 762 | 763 | if (message_.empty()) { 764 | try { 765 | if (value_->isString()) { 766 | message_ = value_->getString(rt).utf8(rt); 767 | } else { 768 | Value message = callGlobalFunction(rt, "String", *value_); 769 | if (message.isString()) { 770 | message_ = message.getString(rt).utf8(rt); 771 | } else { 772 | message_ = "String(e) is a " + kindToString(message, &rt); 773 | } 774 | } 775 | } catch (const JSIException& ex) { 776 | message_ = std::string("[Exception while creating message string: ") + 777 | ex.what() + "]"; 778 | } 779 | } 780 | 781 | if (stack_.empty()) { 782 | stack_ = "no stack"; 783 | } 784 | 785 | if (what_.empty()) { 786 | what_ = message_ + "\n\n" + stack_; 787 | } 788 | } 789 | 790 | JSIException::~JSIException() {} 791 | 792 | JSINativeException::~JSINativeException() {} 793 | 794 | JSError::~JSError() {} 795 | 796 | } // namespace jsi 797 | } // namespace facebook 798 | -------------------------------------------------------------------------------- /jsi/jsi/jsilib-posix.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #ifndef _WINDOWS 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace facebook { 22 | namespace jsi { 23 | 24 | namespace { 25 | 26 | constexpr size_t kErrorBufferSize = 512; 27 | 28 | __attribute__((format(printf, 1, 2))) void throwFormattedError( 29 | const char* fmt, 30 | ...) { 31 | char logBuffer[kErrorBufferSize]; 32 | 33 | va_list va_args; 34 | va_start(va_args, fmt); 35 | int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args); 36 | va_end(va_args); 37 | 38 | if (result < 0) { 39 | throw JSINativeException( 40 | std::string("Failed to format error message: ") + fmt); 41 | } 42 | 43 | throw JSINativeException(logBuffer); 44 | } 45 | 46 | class ScopedFile { 47 | public: 48 | ScopedFile(const std::string& path) 49 | : path_(path), fd_(::open(path.c_str(), O_RDONLY)) { 50 | if (fd_ == -1) { 51 | throwFormattedError( 52 | "Could not open %s: %s", path.c_str(), strerror(errno)); 53 | } 54 | } 55 | 56 | ~ScopedFile() { 57 | ::close(fd_); 58 | } 59 | 60 | size_t size() { 61 | struct stat fileInfo; 62 | if (::fstat(fd_, &fileInfo) == -1) { 63 | throwFormattedError( 64 | "Could not stat %s: %s", path_.c_str(), strerror(errno)); 65 | } 66 | return fileInfo.st_size; 67 | } 68 | 69 | uint8_t* mmap(size_t size) { 70 | void* result = ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd_, 0); 71 | if (result == MAP_FAILED) { 72 | throwFormattedError( 73 | "Could not mmap %s: %s", path_.c_str(), strerror(errno)); 74 | } 75 | return reinterpret_cast(result); 76 | } 77 | 78 | const std::string& path_; 79 | const int fd_; 80 | }; 81 | 82 | } // namespace 83 | 84 | FileBuffer::FileBuffer(const std::string& path) { 85 | ScopedFile file(path); 86 | size_ = file.size(); 87 | data_ = file.mmap(size_); 88 | } 89 | 90 | FileBuffer::~FileBuffer() { 91 | if (::munmap(data_, size_)) { 92 | // terminate the program with pending exception 93 | try { 94 | throwFormattedError( 95 | "Could not unmap memory (%p, %zu bytes): %s", 96 | data_, 97 | size_, 98 | strerror(errno)); 99 | } catch (...) { 100 | std::terminate(); 101 | } 102 | } 103 | } 104 | 105 | } // namespace jsi 106 | } // namespace facebook 107 | 108 | #endif // !defined(_WINDOWS) 109 | -------------------------------------------------------------------------------- /jsi/jsi/jsilib-windows.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #ifdef _WINDOWS 9 | 10 | #include 11 | 12 | namespace facebook { 13 | namespace jsi { 14 | 15 | FileBuffer::FileBuffer(const std::string&) { 16 | // TODO(T41045067) Implement this on Windows 17 | throw new JSINativeException("FileBuffer is not implemented on Windows"); 18 | } 19 | 20 | FileBuffer::~FileBuffer() { 21 | assert(false && "FileBuffer is not implemented on Windows"); 22 | } 23 | 24 | } // namespace jsi 25 | } // namespace facebook 26 | 27 | #endif //_WINDOWS 28 | -------------------------------------------------------------------------------- /jsi/jsi/jsilib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | namespace facebook { 13 | namespace jsi { 14 | 15 | class FileBuffer : public Buffer { 16 | public: 17 | FileBuffer(const std::string& path); 18 | ~FileBuffer() override; 19 | 20 | size_t size() const override { 21 | return size_; 22 | } 23 | 24 | const uint8_t* data() const override { 25 | return data_; 26 | } 27 | 28 | private: 29 | size_t size_; 30 | uint8_t* data_; 31 | }; 32 | 33 | // A trivial implementation of PreparedJavaScript that simply stores the source 34 | // buffer and URL. 35 | class SourceJavaScriptPreparation final : public jsi::PreparedJavaScript, 36 | public jsi::Buffer { 37 | std::shared_ptr buf_; 38 | std::string sourceURL_; 39 | 40 | public: 41 | SourceJavaScriptPreparation( 42 | std::shared_ptr buf, 43 | std::string sourceURL) 44 | : buf_(std::move(buf)), sourceURL_(std::move(sourceURL)) {} 45 | 46 | const std::string& sourceURL() const { 47 | return sourceURL_; 48 | } 49 | 50 | size_t size() const override { 51 | return buf_->size(); 52 | } 53 | const uint8_t* data() const override { 54 | return buf_->data(); 55 | } 56 | }; 57 | 58 | } // namespace jsi 59 | } // namespace facebook 60 | -------------------------------------------------------------------------------- /jsi/jsi/test/testlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | namespace facebook { 18 | namespace jsi { 19 | 20 | class Runtime; 21 | 22 | using RuntimeFactory = std::function()>; 23 | 24 | std::vector runtimeGenerators(); 25 | 26 | class JSITestBase : public ::testing::TestWithParam { 27 | public: 28 | JSITestBase() : factory(GetParam()), runtime(factory()), rt(*runtime) {} 29 | 30 | Value eval(const char* code) { 31 | return rt.global().getPropertyAsFunction(rt, "eval").call(rt, code); 32 | } 33 | 34 | Function function(const std::string& code) { 35 | return eval(("(" + code + ")").c_str()).getObject(rt).getFunction(rt); 36 | } 37 | 38 | bool checkValue(const Value& value, const std::string& jsValue) { 39 | return function("function(value) { return value == " + jsValue + "; }") 40 | .call(rt, std::move(value)) 41 | .getBool(); 42 | } 43 | 44 | RuntimeFactory factory; 45 | std::unique_ptr runtime; 46 | Runtime& rt; 47 | }; 48 | } // namespace jsi 49 | } // namespace facebook 50 | -------------------------------------------------------------------------------- /jsi/jsi/test/testlib_ext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | // These tests are adopted from the Hermes API tests 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace facebook::jsi; 25 | 26 | class JSITestExt : public JSITestBase {}; 27 | 28 | // TODO: figure out how to fix it for V8 29 | #if 0 30 | TEST_P(JSITestExt, StrictHostFunctionBindTest) { 31 | Function coolify = Function::createFromHostFunction( 32 | rt, 33 | PropNameID::forAscii(rt, "coolify"), 34 | 0, 35 | [](Runtime&, const Value& thisVal, const Value* args, size_t count) { 36 | EXPECT_TRUE(thisVal.isUndefined()); 37 | return thisVal.isUndefined(); 38 | }); 39 | rt.global().setProperty(rt, "coolify", coolify); 40 | EXPECT_TRUE(eval("(function() {" 41 | " \"use strict\";" 42 | " return coolify.bind(undefined)();" 43 | "})()") 44 | .getBool()); 45 | } 46 | #endif 47 | 48 | TEST_P(JSITestExt, DescriptionTest) { 49 | // Description is not empty 50 | EXPECT_NE(rt.description().size(), 0); 51 | } 52 | 53 | TEST_P(JSITestExt, ArrayBufferTest) { 54 | eval( 55 | "var buffer = new ArrayBuffer(16);\ 56 | var int32View = new Int32Array(buffer);\ 57 | int32View[0] = 1234;\ 58 | int32View[1] = 5678;"); 59 | 60 | Object object = rt.global().getPropertyAsObject(rt, "buffer"); 61 | EXPECT_TRUE(object.isArrayBuffer(rt)); 62 | 63 | auto arrayBuffer = object.getArrayBuffer(rt); 64 | EXPECT_EQ(arrayBuffer.size(rt), 16); 65 | 66 | int32_t* buffer = reinterpret_cast(arrayBuffer.data(rt)); 67 | EXPECT_EQ(buffer[0], 1234); 68 | EXPECT_EQ(buffer[1], 5678); 69 | } 70 | 71 | #if JSI_VERSION >= 9 72 | #ifndef JSI_V8_IMPL 73 | TEST_P(JSITestExt, ExternalArrayBufferTest) { 74 | struct FixedBuffer : MutableBuffer { 75 | size_t size() const override { 76 | return sizeof(arr); 77 | } 78 | uint8_t* data() override { 79 | return reinterpret_cast(arr.data()); 80 | } 81 | 82 | std::array arr; 83 | }; 84 | 85 | { 86 | auto buf = std::make_shared(); 87 | for (uint32_t i = 0; i < buf->arr.size(); i++) 88 | buf->arr[i] = i; 89 | auto arrayBuffer = ArrayBuffer(rt, buf); 90 | auto square = eval( 91 | R"#( 92 | (function (buf) { 93 | var view = new Uint32Array(buf); 94 | for(var i = 0; i < view.length; i++) view[i] = view[i] * view[i]; 95 | }) 96 | )#"); 97 | square.asObject(rt).asFunction(rt).call(rt, arrayBuffer); 98 | for (uint32_t i = 0; i < 256; i++) 99 | EXPECT_EQ(buf->arr[i], i * i); 100 | } 101 | } 102 | #endif 103 | // This test fails in CI for V8 (x86 Release Win32), so disabling it for now. 104 | // TEST_P(JSITestExt, NoCorruptionOnJSError) { 105 | // // If the test crashes or infinite loops, the likely cause is that 106 | // // Hermes API library is not built with proper compiler flags 107 | // // (-fexception in GCC/CLANG, /EHsc in MSVC) 108 | // try { 109 | // rt.evaluateJavaScript(std::make_unique("foo.bar = 1"), ""); 110 | // FAIL() << "Expected JSIException"; 111 | // } catch (const facebook::jsi::JSIException&) { 112 | // // expected exception, ignore 113 | // } 114 | // try { 115 | // rt.evaluateJavaScript(std::make_unique("foo.baz = 1"), ""); 116 | // FAIL() << "Expected JSIException"; 117 | // } catch (const facebook::jsi::JSIException&) { 118 | // // expected exception, ignore 119 | // } 120 | // rt.evaluateJavaScript(std::make_unique("gc()"), ""); 121 | // } 122 | 123 | #if !defined(JSI_V8_IMPL) 124 | TEST_P(JSITestExt, SpreadHostObjectWithOwnProperties) { 125 | class HostObjectWithPropertyNames : public HostObject { 126 | std::vector getPropertyNames(Runtime& rt) override { 127 | return PropNameID::names(rt, "prop1", "1", "2", "prop2", "3"); 128 | } 129 | Value get(Runtime& runtime, const PropNameID& name) override { 130 | return Value(); 131 | } 132 | }; 133 | 134 | Object ho = Object::createFromHostObject( 135 | rt, std::make_shared()); 136 | rt.global().setProperty(rt, "ho", ho); 137 | 138 | auto res = eval(R"###( 139 | var spreaded = {...ho}; 140 | var props = Object.getOwnPropertyNames(spreaded); 141 | props.toString(); 142 | )###") 143 | .getString(rt) 144 | .utf8(rt); 145 | EXPECT_EQ(res, "1,2,3,prop1,prop2"); 146 | } 147 | 148 | TEST_P(JSITestExt, HostObjectWithOwnProperties) { 149 | class HostObjectWithPropertyNames : public HostObject { 150 | std::vector getPropertyNames(Runtime& rt) override { 151 | return PropNameID::names(rt, "prop1", "1", "2", "prop2", "3"); 152 | } 153 | Value get(Runtime& runtime, const PropNameID& name) override { 154 | if (PropNameID::compare( 155 | runtime, name, PropNameID::forAscii(runtime, "prop1"))) 156 | return 10; 157 | return Value(); 158 | } 159 | }; 160 | 161 | Object ho = Object::createFromHostObject( 162 | rt, std::make_shared()); 163 | rt.global().setProperty(rt, "ho", ho); 164 | 165 | EXPECT_TRUE(eval("\"prop1\" in ho").getBool()); 166 | EXPECT_TRUE(eval("1 in ho").getBool()); 167 | EXPECT_TRUE(eval("2 in ho").getBool()); 168 | EXPECT_TRUE(eval("\"prop2\" in ho").getBool()); 169 | EXPECT_TRUE(eval("3 in ho").getBool()); 170 | // HostObjects say they own any property, even if it's not in their property 171 | // names list. 172 | // This is an explicit design choice, to avoid the runtime and API costs of 173 | // handling checking for property existence. 174 | EXPECT_TRUE(eval("\"foo\" in ho").getBool()); 175 | 176 | EXPECT_TRUE(eval("var properties = Object.getOwnPropertyNames(ho);" 177 | "properties[0] === '1' && " 178 | "properties[1] === '2' && " 179 | "properties[2] === '3' && " 180 | "properties[3] === 'prop1' && " 181 | "properties[4] === 'prop2' && " 182 | "properties.length === 5") 183 | .getBool()); 184 | EXPECT_TRUE(eval("ho[2] === undefined").getBool()); 185 | EXPECT_TRUE(eval("ho.prop2 === undefined").getBool()); 186 | 187 | eval("Object.defineProperty(ho, '0', {value: 'hi there'})"); 188 | eval("Object.defineProperty(ho, '2', {value: 'hi there'})"); 189 | eval("Object.defineProperty(ho, '4', {value: 'hi there'})"); 190 | eval("Object.defineProperty(ho, 'prop2', {value: 'hi there'})"); 191 | 192 | EXPECT_TRUE(eval("var properties = Object.getOwnPropertyNames(ho);" 193 | "properties[0] === '0' && " 194 | "properties[1] === '1' && " 195 | "properties[2] === '2' && " 196 | "properties[3] === '3' && " 197 | "properties[4] === '4' && " 198 | "properties[5] === 'prop2' && " 199 | "properties[6] === 'prop1' && " 200 | "properties.length === 7") 201 | .getBool()); 202 | EXPECT_TRUE(eval("ho[2] === 'hi there'").getBool()); 203 | EXPECT_TRUE(eval("ho.prop2 === 'hi there'").getBool()); 204 | 205 | // hasOwnProperty() always succeeds on HostObject 206 | EXPECT_TRUE( 207 | eval("Object.prototype.hasOwnProperty.call(ho, 'prop1')").getBool()); 208 | EXPECT_TRUE( 209 | eval("Object.prototype.hasOwnProperty.call(ho, 'any-string')").getBool()); 210 | 211 | // getOwnPropertyDescriptor() always succeeds on HostObject 212 | EXPECT_TRUE(eval("var d = Object.getOwnPropertyDescriptor(ho, 'prop1');" 213 | "d != undefined && " 214 | "d.value == 10 && " 215 | "d.enumerable && " 216 | "d.writable ") 217 | .getBool()); 218 | EXPECT_TRUE(eval("var d = Object.getOwnPropertyDescriptor(ho, 'any-string');" 219 | "d != undefined && " 220 | "d.value == undefined && " 221 | "d.enumerable && " 222 | "d.writable") 223 | .getBool()); 224 | } 225 | #endif 226 | 227 | TEST_P(JSITestExt, WeakReferences) { 228 | Object o = eval("({one: 1})").getObject(rt); 229 | WeakObject wo = WeakObject(rt, o); 230 | rt.global().setProperty(rt, "obj", o); 231 | 232 | eval("gc()"); 233 | 234 | Value v = wo.lock(rt); 235 | 236 | // At this point, the object has three strong refs (C++ o, v; JS global.obj). 237 | 238 | EXPECT_TRUE(v.isObject()); 239 | EXPECT_EQ(v.getObject(rt).getProperty(rt, "one").asNumber(), 1); 240 | 241 | // Now start removing references. 242 | 243 | v = nullptr; 244 | 245 | // Two left 246 | 247 | eval("gc()"); 248 | EXPECT_EQ(wo.lock(rt).getObject(rt).getProperty(rt, "one").asNumber(), 1); 249 | 250 | o = Object(rt); 251 | 252 | // Now one, only JS 253 | 254 | eval("gc()"); 255 | EXPECT_EQ(wo.lock(rt).getObject(rt).getProperty(rt, "one").asNumber(), 1); 256 | 257 | eval("obj = null"); 258 | 259 | // Now none. 260 | 261 | eval("gc()"); 262 | EXPECT_TRUE(wo.lock(rt).isUndefined()); 263 | 264 | // test where the last ref is C++ 265 | 266 | o = eval("({two: 2})").getObject(rt); 267 | wo = WeakObject(rt, o); 268 | v = Value(rt, o); 269 | 270 | eval("gc()"); 271 | EXPECT_EQ(wo.lock(rt).getObject(rt).getProperty(rt, "two").asNumber(), 2); 272 | 273 | v = nullptr; 274 | 275 | eval("gc()"); 276 | EXPECT_EQ(wo.lock(rt).getObject(rt).getProperty(rt, "two").asNumber(), 2); 277 | 278 | o = Object(rt); 279 | 280 | eval("gc()"); 281 | EXPECT_TRUE(wo.lock(rt).isUndefined()); 282 | } 283 | 284 | TEST_P(JSITestExt, HostObjectAsParentTest) { 285 | class HostObjectWithProp : public HostObject { 286 | Value get(Runtime& runtime, const PropNameID& name) override { 287 | if (PropNameID::compare( 288 | runtime, name, PropNameID::forAscii(runtime, "prop1"))) 289 | return 10; 290 | return Value(); 291 | } 292 | }; 293 | 294 | Object ho = 295 | Object::createFromHostObject(rt, std::make_shared()); 296 | rt.global().setProperty(rt, "ho", ho); 297 | 298 | EXPECT_TRUE( 299 | eval("var subClass = {__proto__: ho}; subClass.prop1 == 10;").getBool()); 300 | } 301 | 302 | #if JSI_VERSION >= 7 303 | TEST_P(JSITestExt, NativeStateTest) { 304 | class C : public facebook::jsi::NativeState { 305 | public: 306 | int* dtors; 307 | C(int* _dtors) : dtors(_dtors) {} 308 | virtual ~C() override { 309 | ++*dtors; 310 | } 311 | }; 312 | int dtors1 = 0; 313 | int dtors2 = 0; 314 | { 315 | Object obj = eval("({one: 1})").getObject(rt); 316 | ASSERT_FALSE(obj.hasNativeState(rt)); 317 | { 318 | // Set some state. 319 | obj.setNativeState(rt, std::make_shared(&dtors1)); 320 | ASSERT_TRUE(obj.hasNativeState(rt)); 321 | auto ptr = obj.getNativeState(rt); 322 | EXPECT_EQ(ptr->dtors, &dtors1); 323 | } 324 | { 325 | // Overwrite the state. 326 | obj.setNativeState(rt, std::make_shared(&dtors2)); 327 | ASSERT_TRUE(obj.hasNativeState(rt)); 328 | auto ptr = obj.getNativeState(rt); 329 | EXPECT_EQ(ptr->dtors, &dtors2); 330 | } 331 | } // closing scope -> obj unreachable 332 | // should finalize both 333 | eval("gc()"); 334 | EXPECT_EQ(1, dtors1); 335 | EXPECT_EQ(1, dtors2); 336 | 337 | // Trying to set native state on frozen object should throw. 338 | // TODO: Make V8 implementation to throw here. 339 | // { 340 | // Object frozen = eval("Object.freeze({one: 1})").getObject(rt); 341 | // ASSERT_THROW( 342 | // frozen.setNativeState(rt, std::make_shared(&dtors1)), 343 | // JSIException); 344 | // } 345 | // Make sure any NativeState cells are finalized before leaving, since they 346 | // point to local variables. Otherwise ASAN will complain. 347 | eval("gc()"); 348 | } 349 | #endif 350 | 351 | #if JSI_VERSION >= 5 352 | TEST_P(JSITestExt, PropNameIDFromSymbol) { 353 | auto strProp = PropNameID::forAscii(rt, "a"); 354 | auto secretProp = PropNameID::forSymbol( 355 | rt, eval("var secret = Symbol('a'); secret;").getSymbol(rt)); 356 | auto globalProp = 357 | PropNameID::forSymbol(rt, eval("Symbol.for('a');").getSymbol(rt)); 358 | auto x = 359 | eval("({a : 'str', [secret] : 'secret', [Symbol.for('a')] : 'global'});") 360 | .getObject(rt); 361 | 362 | EXPECT_EQ(x.getProperty(rt, strProp).getString(rt).utf8(rt), "str"); 363 | EXPECT_EQ(x.getProperty(rt, secretProp).getString(rt).utf8(rt), "secret"); 364 | EXPECT_EQ(x.getProperty(rt, globalProp).getString(rt).utf8(rt), "global"); 365 | } 366 | #endif 367 | 368 | TEST_P(JSITestExt, HasComputedTest) { 369 | // The only use of JSObject::hasComputed() is in HermesRuntimeImpl, 370 | // so we test its Proxy support here, instead of from JS. 371 | 372 | EXPECT_FALSE(eval("'prop' in new Proxy({}, {})").getBool()); 373 | EXPECT_TRUE(eval("'prop' in new Proxy({prop:1}, {})").getBool()); 374 | EXPECT_FALSE( 375 | eval("'prop' in new Proxy({}, {has() { return false; }})").getBool()); 376 | EXPECT_TRUE( 377 | eval("'prop' in new Proxy({}, {has() { return true; }})").getBool()); 378 | 379 | // While we're here, test that a HostFunction can be used as a proxy 380 | // trap. This could be very powerful in the right hands. 381 | Function returnTrue = Function::createFromHostFunction( 382 | rt, 383 | PropNameID::forAscii(rt, "returnTrue"), 384 | 0, 385 | [](Runtime& rt, const Value&, const Value* args, size_t count) { 386 | EXPECT_EQ(count, 2); 387 | EXPECT_EQ(args[1].toString(rt).utf8(rt), "prop"); 388 | return true; 389 | }); 390 | rt.global().setProperty(rt, "returnTrue", returnTrue); 391 | EXPECT_TRUE(eval("'prop' in new Proxy({}, {has: returnTrue})").getBool()); 392 | } 393 | 394 | TEST_P(JSITestExt, GlobalObjectTest) { 395 | rt.global().setProperty(rt, "a", 5); 396 | eval("f = function(b) { return a + b; }"); 397 | eval("gc()"); 398 | EXPECT_EQ(eval("f(10)").getNumber(), 15); 399 | } 400 | #endif 401 | 402 | #if JSI_VERSION >= 8 403 | TEST_P(JSITestExt, BigIntJSI) { 404 | Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt"); 405 | auto BigInt = [&](const char* v) { return bigintCtor.call(rt, eval(v)); }; 406 | 407 | auto v0 = BigInt("0"); 408 | auto b0 = v0.asBigInt(rt); 409 | EXPECT_EQ(v0.toString(rt).utf8(rt), "0"); 410 | EXPECT_EQ(b0.toString(rt).utf8(rt), "0"); 411 | 412 | auto vffffffffffffffff = BigInt("0xffffffffffffffffn"); 413 | auto bffffffffffffffff = vffffffffffffffff.asBigInt(rt); 414 | EXPECT_EQ(vffffffffffffffff.toString(rt).utf8(rt), "18446744073709551615"); 415 | EXPECT_EQ(bffffffffffffffff.toString(rt, 16).utf8(rt), "ffffffffffffffff"); 416 | EXPECT_EQ(bffffffffffffffff.toString(rt, 36).utf8(rt), "3w5e11264sgsf"); 417 | 418 | auto vNeg1 = BigInt("-1"); 419 | auto bNeg1 = vNeg1.asBigInt(rt); 420 | EXPECT_EQ(vNeg1.toString(rt).utf8(rt), "-1"); 421 | EXPECT_EQ(bNeg1.toString(rt, 16).utf8(rt), "-1"); 422 | EXPECT_EQ(bNeg1.toString(rt, 36).utf8(rt), "-1"); 423 | 424 | EXPECT_TRUE(BigInt::strictEquals(rt, b0, b0)); 425 | EXPECT_TRUE(BigInt::strictEquals(rt, bffffffffffffffff, bffffffffffffffff)); 426 | EXPECT_FALSE(BigInt::strictEquals(rt, bNeg1, bffffffffffffffff)); 427 | } 428 | 429 | TEST_P(JSITestExt, BigIntJSIFromScalar) { 430 | Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt"); 431 | auto BigInt = [&](const char* v) { 432 | return bigintCtor.call(rt, eval(v)).asBigInt(rt); 433 | }; 434 | 435 | EXPECT_TRUE(BigInt::strictEquals(rt, BigInt("0"), BigInt::fromUint64(rt, 0))); 436 | EXPECT_TRUE(BigInt::strictEquals(rt, BigInt("0"), BigInt::fromInt64(rt, 0))); 437 | EXPECT_TRUE(BigInt::strictEquals( 438 | rt, BigInt("0xdeadbeef"), BigInt::fromUint64(rt, 0xdeadbeef))); 439 | EXPECT_TRUE(BigInt::strictEquals( 440 | rt, BigInt("0xc0ffee"), BigInt::fromInt64(rt, 0xc0ffee))); 441 | EXPECT_TRUE(BigInt::strictEquals( 442 | rt, BigInt("0xffffffffffffffffn"), BigInt::fromUint64(rt, ~0ull))); 443 | EXPECT_TRUE( 444 | BigInt::strictEquals(rt, BigInt("-1"), BigInt::fromInt64(rt, ~0ull))); 445 | } 446 | 447 | TEST_P(JSITestExt, BigIntJSIToString) { 448 | auto b = BigInt::fromUint64(rt, 1); 449 | // Test all possible radixes. 450 | for (int radix = 2; radix <= 36; ++radix) { 451 | EXPECT_EQ(b.toString(rt, radix).utf8(rt), "1") << radix; 452 | } 453 | 454 | // Test some invaild radixes. 455 | EXPECT_THROW(b.toString(rt, -1), JSIException); 456 | EXPECT_THROW(b.toString(rt, 0), JSIException); 457 | EXPECT_THROW(b.toString(rt, 1), JSIException); 458 | EXPECT_THROW(b.toString(rt, 37), JSIException); 459 | EXPECT_THROW(b.toString(rt, 100), JSIException); 460 | 461 | Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt"); 462 | auto BigInt = [&](int value) { 463 | return bigintCtor.call(rt, value).asBigInt(rt); 464 | }; 465 | 466 | // Now test that the radix is being passed to the VM. 467 | for (int radix = 2; radix <= 36; ++radix) { 468 | EXPECT_EQ(BigInt(radix + 1).toString(rt, radix).utf8(rt), "11") << radix; 469 | EXPECT_EQ(BigInt(-(radix + 1)).toString(rt, radix).utf8(rt), "-11") 470 | << radix; 471 | } 472 | } 473 | 474 | TEST_P(JSITestExt, BigIntJSITruncation) { 475 | auto lossless = [](uint64_t value) { return std::make_tuple(value, true); }; 476 | auto lossy = [](uint64_t value) { return std::make_tuple(value, false); }; 477 | 478 | auto toInt64 = [this](const BigInt& b) { 479 | return std::make_tuple(b.getInt64(rt), b.isInt64(rt)); 480 | }; 481 | 482 | auto toUint64 = [this](const BigInt& b) { 483 | return std::make_tuple(b.getUint64(rt), b.isUint64(rt)); 484 | }; 485 | 486 | Function bigintCtor = rt.global().getPropertyAsFunction(rt, "BigInt"); 487 | auto BigInt = [&](const char* v) { 488 | return bigintCtor.call(rt, eval(v)).asBigInt(rt); 489 | }; 490 | 491 | // 0n can be truncated losslessly to either int64_t and uint64_t 492 | auto b = BigInt::fromUint64(rt, 0); 493 | EXPECT_EQ(toUint64(b), lossless(0)); 494 | EXPECT_TRUE( 495 | BigInt::strictEquals(rt, BigInt::fromUint64(rt, b.getUint64(rt)), b)); 496 | EXPECT_EQ(toInt64(b), lossless(0)); 497 | EXPECT_TRUE( 498 | BigInt::strictEquals(rt, BigInt::fromInt64(rt, b.getInt64(rt)), b)); 499 | 500 | // Creating BigInt from an ~0ull. This value can't be truncated losslessly to 501 | // int64_t. 502 | b = BigInt::fromUint64(rt, ~0ull); 503 | EXPECT_EQ(toUint64(b), lossless(~0ull)); 504 | EXPECT_TRUE( 505 | BigInt::strictEquals(rt, BigInt::fromUint64(rt, b.getUint64(rt)), b)); 506 | EXPECT_EQ(toInt64(b), lossy(~0ull)); 507 | 508 | // Creating BigInt from an -1ull. This value can't be truncated losslessly to 509 | // int64_t. 510 | b = BigInt::fromInt64(rt, -1ull); 511 | EXPECT_EQ(toUint64(b), lossy(-1ull)); 512 | EXPECT_EQ(toInt64(b), lossless(-1ull)); 513 | EXPECT_TRUE( 514 | BigInt::strictEquals(rt, BigInt::fromInt64(rt, b.getInt64(rt)), b)); 515 | 516 | // 0x10000000000000000n can't be truncated to int64_t nor uint64_t. 517 | b = BigInt("0x10000000000000000n"); 518 | EXPECT_EQ(toUint64(b), lossy(0)); 519 | EXPECT_EQ(toInt64(b), lossy(0)); 520 | 521 | // -0x10000000000000000n can't be truncated to int64_t nor uint64_t. 522 | b = BigInt("-0x10000000000000000n"); 523 | EXPECT_EQ(toUint64(b), lossy(0)); 524 | EXPECT_EQ(toInt64(b), lossy(0)); 525 | 526 | // (1n << 65n) - 1n can't be truncated to int64_t nor uint64_t. 527 | b = BigInt("(1n << 65n) - 1n"); 528 | EXPECT_EQ(toUint64(b), lossy(~0ull)); 529 | EXPECT_EQ(toInt64(b), lossy(~0ull)); 530 | } 531 | #endif 532 | 533 | TEST_P(JSITestExt, NativeExceptionDoesNotUseGlobalError) { 534 | Function alwaysThrows = Function::createFromHostFunction( 535 | rt, 536 | PropNameID::forAscii(rt, "alwaysThrows"), 537 | 0, 538 | [](Runtime&, const Value&, const Value*, size_t) -> Value { 539 | throw std::logic_error( 540 | "Native std::logic_error C++ exception in Host Function"); 541 | }); 542 | rt.global().setProperty(rt, "alwaysThrows", alwaysThrows); 543 | rt.global().setProperty(rt, "Error", 10); 544 | 545 | auto test = eval( 546 | R"#((function(val) { 547 | 'use strict'; 548 | try { 549 | alwaysThrows(val); 550 | } catch(e) { 551 | return 'typeof Error is ' + typeof(Error) + '; ' + e.message; 552 | } 553 | throw new Error('Unreachable statement'); 554 | }))#") 555 | .getObject(rt) 556 | .getFunction(rt); 557 | EXPECT_EQ( 558 | "typeof Error is number; Exception in HostFunction: Native " 559 | "std::logic_error C++ exception in Host Function", 560 | test.call(rt).getString(rt).utf8(rt)); 561 | } 562 | 563 | INSTANTIATE_TEST_SUITE_P( 564 | Runtimes, 565 | JSITestExt, 566 | ::testing::ValuesIn(runtimeGenerators())); 567 | -------------------------------------------------------------------------------- /jsi/jsi/threadsafe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace facebook { 16 | namespace jsi { 17 | 18 | class ThreadSafeRuntime : public Runtime { 19 | public: 20 | virtual void lock() const = 0; 21 | virtual void unlock() const = 0; 22 | virtual Runtime& getUnsafeRuntime() = 0; 23 | }; 24 | 25 | namespace detail { 26 | 27 | template 28 | struct WithLock { 29 | L lock; 30 | WithLock(R& r) : lock(r) {} 31 | void before() { 32 | lock.lock(); 33 | } 34 | void after() { 35 | lock.unlock(); 36 | } 37 | }; 38 | 39 | // The actual implementation of a given ThreadSafeRuntime. It's parameterized 40 | // by: 41 | // 42 | // - R: The actual Runtime type that this wraps 43 | // - L: A lock type that has three members: 44 | // - L(R& r) // ctor 45 | // - void lock() 46 | // - void unlock() 47 | template 48 | class ThreadSafeRuntimeImpl final 49 | : public WithRuntimeDecorator, R, ThreadSafeRuntime> { 50 | public: 51 | template 52 | ThreadSafeRuntimeImpl(Args&&... args) 53 | : WithRuntimeDecorator, R, ThreadSafeRuntime>( 54 | unsafe_, 55 | lock_), 56 | unsafe_(std::forward(args)...), 57 | lock_(unsafe_) {} 58 | 59 | R& getUnsafeRuntime() override { 60 | return WithRuntimeDecorator, R, ThreadSafeRuntime>::plain(); 61 | } 62 | 63 | void lock() const override { 64 | lock_.before(); 65 | } 66 | 67 | void unlock() const override { 68 | lock_.after(); 69 | } 70 | 71 | private: 72 | R unsafe_; 73 | mutable WithLock lock_; 74 | }; 75 | 76 | } // namespace detail 77 | 78 | } // namespace jsi 79 | } // namespace facebook 80 | -------------------------------------------------------------------------------- /node-api/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: Inline 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^' 65 | Priority: 2 66 | - Regex: '^<.*\.h>' 67 | Priority: 1 68 | - Regex: '^<.*' 69 | Priority: 2 70 | - Regex: '.*' 71 | Priority: 3 72 | IncludeIsMainRegex: '([-_](test|unittest))?$' 73 | IndentCaseLabels: true 74 | IndentPPDirectives: None 75 | IndentWidth: 2 76 | IndentWrappedFunctionNames: false 77 | JavaScriptQuotes: Leave 78 | JavaScriptWrapImports: true 79 | KeepEmptyLinesAtTheStartOfBlocks: false 80 | MacroBlockBegin: '' 81 | MacroBlockEnd: '' 82 | MaxEmptyLinesToKeep: 1 83 | NamespaceIndentation: None 84 | ObjCBlockIndentWidth: 2 85 | ObjCSpaceAfterProperty: false 86 | ObjCSpaceBeforeProtocolList: false 87 | PenaltyBreakAssignment: 2 88 | PenaltyBreakBeforeFirstCallParameter: 1 89 | PenaltyBreakComment: 300 90 | PenaltyBreakFirstLessLess: 120 91 | PenaltyBreakString: 1000 92 | PenaltyExcessCharacter: 1000000 93 | PenaltyReturnTypeOnItsOwnLine: 200 94 | PointerAlignment: Left 95 | ReflowComments: true 96 | SortIncludes: true 97 | SortUsingDeclarations: true 98 | SpaceAfterCStyleCast: false 99 | SpaceAfterTemplateKeyword: true 100 | SpaceBeforeAssignmentOperators: true 101 | SpaceBeforeParens: ControlStatements 102 | SpaceInEmptyParentheses: false 103 | SpacesBeforeTrailingComments: 2 104 | SpacesInAngles: false 105 | SpacesInContainerLiterals: true 106 | SpacesInCStyleCastParentheses: false 107 | SpacesInParentheses: false 108 | SpacesInSquareBrackets: false 109 | Standard: Auto 110 | TabWidth: 8 111 | UseTab: Never 112 | -------------------------------------------------------------------------------- /node-api/js_native_api.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_JS_NATIVE_API_H_ 2 | #define SRC_JS_NATIVE_API_H_ 3 | 4 | // This file needs to be compatible with C compilers. 5 | #include // NOLINT(modernize-deprecated-headers) 6 | #include // NOLINT(modernize-deprecated-headers) 7 | 8 | // Use INT_MAX, this should only be consumed by the pre-processor anyway. 9 | #define NAPI_VERSION_EXPERIMENTAL 2147483647 10 | #ifndef NAPI_VERSION 11 | #ifdef NAPI_EXPERIMENTAL 12 | #define NAPI_VERSION NAPI_VERSION_EXPERIMENTAL 13 | #else 14 | // The baseline version for N-API. 15 | // The NAPI_VERSION controls which version will be used by default when 16 | // compiling a native addon. If the addon developer specifically wants to use 17 | // functions available in a new version of N-API that is not yet ported in all 18 | // LTS versions, they can set NAPI_VERSION knowing that they have specifically 19 | // depended on that version. 20 | #define NAPI_VERSION 8 21 | #endif 22 | #endif 23 | 24 | #include "js_native_api_types.h" 25 | 26 | // If you need __declspec(dllimport), either include instead, or 27 | // define NAPI_EXTERN as __declspec(dllimport) on the compiler's command line. 28 | #ifndef NAPI_EXTERN 29 | #ifdef _WIN32 30 | #define NAPI_EXTERN __declspec(dllexport) 31 | #elif defined(__wasm__) 32 | #define NAPI_EXTERN \ 33 | __attribute__((visibility("default"))) \ 34 | __attribute__((__import_module__("napi"))) 35 | #else 36 | #define NAPI_EXTERN __attribute__((visibility("default"))) 37 | #endif 38 | #endif 39 | 40 | #define NAPI_AUTO_LENGTH SIZE_MAX 41 | 42 | #ifdef __cplusplus 43 | #define EXTERN_C_START extern "C" { 44 | #define EXTERN_C_END } 45 | #else 46 | #define EXTERN_C_START 47 | #define EXTERN_C_END 48 | #endif 49 | 50 | EXTERN_C_START 51 | 52 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_last_error_info( 53 | node_api_nogc_env env, const napi_extended_error_info** result); 54 | 55 | // Getters for defined singletons 56 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_undefined(napi_env env, 57 | napi_value* result); 58 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_null(napi_env env, 59 | napi_value* result); 60 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_global(napi_env env, 61 | napi_value* result); 62 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env, 63 | bool value, 64 | napi_value* result); 65 | 66 | // Methods to create Primitive types/Objects 67 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env, 68 | napi_value* result); 69 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env, 70 | napi_value* result); 71 | NAPI_EXTERN napi_status NAPI_CDECL 72 | napi_create_array_with_length(napi_env env, size_t length, napi_value* result); 73 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_double(napi_env env, 74 | double value, 75 | napi_value* result); 76 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_int32(napi_env env, 77 | int32_t value, 78 | napi_value* result); 79 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_uint32(napi_env env, 80 | uint32_t value, 81 | napi_value* result); 82 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_int64(napi_env env, 83 | int64_t value, 84 | napi_value* result); 85 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_latin1( 86 | napi_env env, const char* str, size_t length, napi_value* result); 87 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf8(napi_env env, 88 | const char* str, 89 | size_t length, 90 | napi_value* result); 91 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_string_utf16(napi_env env, 92 | const char16_t* str, 93 | size_t length, 94 | napi_value* result); 95 | #ifdef NAPI_EXPERIMENTAL 96 | #define NODE_API_EXPERIMENTAL_HAS_EXTERNAL_STRINGS 97 | NAPI_EXTERN napi_status NAPI_CDECL 98 | node_api_create_external_string_latin1(napi_env env, 99 | char* str, 100 | size_t length, 101 | node_api_nogc_finalize finalize_callback, 102 | void* finalize_hint, 103 | napi_value* result, 104 | bool* copied); 105 | NAPI_EXTERN napi_status NAPI_CDECL 106 | node_api_create_external_string_utf16(napi_env env, 107 | char16_t* str, 108 | size_t length, 109 | node_api_nogc_finalize finalize_callback, 110 | void* finalize_hint, 111 | napi_value* result, 112 | bool* copied); 113 | #endif // NAPI_EXPERIMENTAL 114 | 115 | #ifdef NAPI_EXPERIMENTAL 116 | #define NODE_API_EXPERIMENTAL_HAS_PROPERTY_KEYS 117 | NAPI_EXTERN napi_status NAPI_CDECL node_api_create_property_key_utf16( 118 | napi_env env, const char16_t* str, size_t length, napi_value* result); 119 | #endif // NAPI_EXPERIMENTAL 120 | 121 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_symbol(napi_env env, 122 | napi_value description, 123 | napi_value* result); 124 | #if NAPI_VERSION >= 9 125 | NAPI_EXTERN napi_status NAPI_CDECL 126 | node_api_symbol_for(napi_env env, 127 | const char* utf8description, 128 | size_t length, 129 | napi_value* result); 130 | #endif // NAPI_VERSION >= 9 131 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_function(napi_env env, 132 | const char* utf8name, 133 | size_t length, 134 | napi_callback cb, 135 | void* data, 136 | napi_value* result); 137 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_error(napi_env env, 138 | napi_value code, 139 | napi_value msg, 140 | napi_value* result); 141 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_type_error(napi_env env, 142 | napi_value code, 143 | napi_value msg, 144 | napi_value* result); 145 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_range_error(napi_env env, 146 | napi_value code, 147 | napi_value msg, 148 | napi_value* result); 149 | #if NAPI_VERSION >= 9 150 | NAPI_EXTERN napi_status NAPI_CDECL node_api_create_syntax_error( 151 | napi_env env, napi_value code, napi_value msg, napi_value* result); 152 | #endif // NAPI_VERSION >= 9 153 | 154 | // Methods to get the native napi_value from Primitive type 155 | NAPI_EXTERN napi_status NAPI_CDECL napi_typeof(napi_env env, 156 | napi_value value, 157 | napi_valuetype* result); 158 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_double(napi_env env, 159 | napi_value value, 160 | double* result); 161 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int32(napi_env env, 162 | napi_value value, 163 | int32_t* result); 164 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_uint32(napi_env env, 165 | napi_value value, 166 | uint32_t* result); 167 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_int64(napi_env env, 168 | napi_value value, 169 | int64_t* result); 170 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bool(napi_env env, 171 | napi_value value, 172 | bool* result); 173 | 174 | // Copies LATIN-1 encoded bytes from a string into a buffer. 175 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_latin1( 176 | napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); 177 | 178 | // Copies UTF-8 encoded bytes from a string into a buffer. 179 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf8( 180 | napi_env env, napi_value value, char* buf, size_t bufsize, size_t* result); 181 | 182 | // Copies UTF-16 encoded bytes from a string into a buffer. 183 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_string_utf16(napi_env env, 184 | napi_value value, 185 | char16_t* buf, 186 | size_t bufsize, 187 | size_t* result); 188 | 189 | // Methods to coerce values 190 | // These APIs may execute user scripts 191 | NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_bool(napi_env env, 192 | napi_value value, 193 | napi_value* result); 194 | NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_number(napi_env env, 195 | napi_value value, 196 | napi_value* result); 197 | NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_object(napi_env env, 198 | napi_value value, 199 | napi_value* result); 200 | NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_string(napi_env env, 201 | napi_value value, 202 | napi_value* result); 203 | 204 | // Methods to work with Objects 205 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_prototype(napi_env env, 206 | napi_value object, 207 | napi_value* result); 208 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_property_names(napi_env env, 209 | napi_value object, 210 | napi_value* result); 211 | NAPI_EXTERN napi_status NAPI_CDECL napi_set_property(napi_env env, 212 | napi_value object, 213 | napi_value key, 214 | napi_value value); 215 | NAPI_EXTERN napi_status NAPI_CDECL napi_has_property(napi_env env, 216 | napi_value object, 217 | napi_value key, 218 | bool* result); 219 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_property(napi_env env, 220 | napi_value object, 221 | napi_value key, 222 | napi_value* result); 223 | NAPI_EXTERN napi_status NAPI_CDECL napi_delete_property(napi_env env, 224 | napi_value object, 225 | napi_value key, 226 | bool* result); 227 | NAPI_EXTERN napi_status NAPI_CDECL napi_has_own_property(napi_env env, 228 | napi_value object, 229 | napi_value key, 230 | bool* result); 231 | NAPI_EXTERN napi_status NAPI_CDECL napi_set_named_property(napi_env env, 232 | napi_value object, 233 | const char* utf8name, 234 | napi_value value); 235 | NAPI_EXTERN napi_status NAPI_CDECL napi_has_named_property(napi_env env, 236 | napi_value object, 237 | const char* utf8name, 238 | bool* result); 239 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_named_property(napi_env env, 240 | napi_value object, 241 | const char* utf8name, 242 | napi_value* result); 243 | NAPI_EXTERN napi_status NAPI_CDECL napi_set_element(napi_env env, 244 | napi_value object, 245 | uint32_t index, 246 | napi_value value); 247 | NAPI_EXTERN napi_status NAPI_CDECL napi_has_element(napi_env env, 248 | napi_value object, 249 | uint32_t index, 250 | bool* result); 251 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_element(napi_env env, 252 | napi_value object, 253 | uint32_t index, 254 | napi_value* result); 255 | NAPI_EXTERN napi_status NAPI_CDECL napi_delete_element(napi_env env, 256 | napi_value object, 257 | uint32_t index, 258 | bool* result); 259 | NAPI_EXTERN napi_status NAPI_CDECL 260 | napi_define_properties(napi_env env, 261 | napi_value object, 262 | size_t property_count, 263 | const napi_property_descriptor* properties); 264 | 265 | // Methods to work with Arrays 266 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_array(napi_env env, 267 | napi_value value, 268 | bool* result); 269 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_array_length(napi_env env, 270 | napi_value value, 271 | uint32_t* result); 272 | 273 | // Methods to compare values 274 | NAPI_EXTERN napi_status NAPI_CDECL napi_strict_equals(napi_env env, 275 | napi_value lhs, 276 | napi_value rhs, 277 | bool* result); 278 | 279 | // Methods to work with Functions 280 | NAPI_EXTERN napi_status NAPI_CDECL napi_call_function(napi_env env, 281 | napi_value recv, 282 | napi_value func, 283 | size_t argc, 284 | const napi_value* argv, 285 | napi_value* result); 286 | NAPI_EXTERN napi_status NAPI_CDECL napi_new_instance(napi_env env, 287 | napi_value constructor, 288 | size_t argc, 289 | const napi_value* argv, 290 | napi_value* result); 291 | NAPI_EXTERN napi_status NAPI_CDECL napi_instanceof(napi_env env, 292 | napi_value object, 293 | napi_value constructor, 294 | bool* result); 295 | 296 | // Methods to work with napi_callbacks 297 | 298 | // Gets all callback info in a single call. (Ugly, but faster.) 299 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_cb_info( 300 | napi_env env, // [in] Node-API environment handle 301 | napi_callback_info cbinfo, // [in] Opaque callback-info handle 302 | size_t* argc, // [in-out] Specifies the size of the provided argv array 303 | // and receives the actual count of args. 304 | napi_value* argv, // [out] Array of values 305 | napi_value* this_arg, // [out] Receives the JS 'this' arg for the call 306 | void** data); // [out] Receives the data pointer for the callback. 307 | 308 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_new_target( 309 | napi_env env, napi_callback_info cbinfo, napi_value* result); 310 | NAPI_EXTERN napi_status NAPI_CDECL 311 | napi_define_class(napi_env env, 312 | const char* utf8name, 313 | size_t length, 314 | napi_callback constructor, 315 | void* data, 316 | size_t property_count, 317 | const napi_property_descriptor* properties, 318 | napi_value* result); 319 | 320 | // Methods to work with external data objects 321 | NAPI_EXTERN napi_status NAPI_CDECL napi_wrap(napi_env env, 322 | napi_value js_object, 323 | void* native_object, 324 | node_api_nogc_finalize finalize_cb, 325 | void* finalize_hint, 326 | napi_ref* result); 327 | NAPI_EXTERN napi_status NAPI_CDECL napi_unwrap(napi_env env, 328 | napi_value js_object, 329 | void** result); 330 | NAPI_EXTERN napi_status NAPI_CDECL napi_remove_wrap(napi_env env, 331 | napi_value js_object, 332 | void** result); 333 | NAPI_EXTERN napi_status NAPI_CDECL 334 | napi_create_external(napi_env env, 335 | void* data, 336 | node_api_nogc_finalize finalize_cb, 337 | void* finalize_hint, 338 | napi_value* result); 339 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_external(napi_env env, 340 | napi_value value, 341 | void** result); 342 | 343 | // Methods to control object lifespan 344 | 345 | // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. 346 | NAPI_EXTERN napi_status NAPI_CDECL 347 | napi_create_reference(napi_env env, 348 | napi_value value, 349 | uint32_t initial_refcount, 350 | napi_ref* result); 351 | 352 | // Deletes a reference. The referenced value is released, and may 353 | // be GC'd unless there are other references to it. 354 | NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference(napi_env env, 355 | napi_ref ref); 356 | 357 | // Increments the reference count, optionally returning the resulting count. 358 | // After this call the reference will be a strong reference because its 359 | // refcount is >0, and the referenced object is effectively "pinned". 360 | // Calling this when the refcount is 0 and the object is unavailable 361 | // results in an error. 362 | NAPI_EXTERN napi_status NAPI_CDECL napi_reference_ref(napi_env env, 363 | napi_ref ref, 364 | uint32_t* result); 365 | 366 | // Decrements the reference count, optionally returning the resulting count. 367 | // If the result is 0 the reference is now weak and the object may be GC'd 368 | // at any time if there are no other references. Calling this when the 369 | // refcount is already 0 results in an error. 370 | NAPI_EXTERN napi_status NAPI_CDECL napi_reference_unref(napi_env env, 371 | napi_ref ref, 372 | uint32_t* result); 373 | 374 | // Attempts to get a referenced value. If the reference is weak, 375 | // the value might no longer be available, in that case the call 376 | // is still successful but the result is NULL. 377 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_reference_value(napi_env env, 378 | napi_ref ref, 379 | napi_value* result); 380 | 381 | NAPI_EXTERN napi_status NAPI_CDECL 382 | napi_open_handle_scope(napi_env env, napi_handle_scope* result); 383 | NAPI_EXTERN napi_status NAPI_CDECL 384 | napi_close_handle_scope(napi_env env, napi_handle_scope scope); 385 | NAPI_EXTERN napi_status NAPI_CDECL napi_open_escapable_handle_scope( 386 | napi_env env, napi_escapable_handle_scope* result); 387 | NAPI_EXTERN napi_status NAPI_CDECL napi_close_escapable_handle_scope( 388 | napi_env env, napi_escapable_handle_scope scope); 389 | 390 | NAPI_EXTERN napi_status NAPI_CDECL 391 | napi_escape_handle(napi_env env, 392 | napi_escapable_handle_scope scope, 393 | napi_value escapee, 394 | napi_value* result); 395 | 396 | // Methods to support error handling 397 | NAPI_EXTERN napi_status NAPI_CDECL napi_throw(napi_env env, napi_value error); 398 | NAPI_EXTERN napi_status NAPI_CDECL napi_throw_error(napi_env env, 399 | const char* code, 400 | const char* msg); 401 | NAPI_EXTERN napi_status NAPI_CDECL napi_throw_type_error(napi_env env, 402 | const char* code, 403 | const char* msg); 404 | NAPI_EXTERN napi_status NAPI_CDECL napi_throw_range_error(napi_env env, 405 | const char* code, 406 | const char* msg); 407 | #if NAPI_VERSION >= 9 408 | NAPI_EXTERN napi_status NAPI_CDECL node_api_throw_syntax_error(napi_env env, 409 | const char* code, 410 | const char* msg); 411 | #endif // NAPI_VERSION >= 9 412 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_error(napi_env env, 413 | napi_value value, 414 | bool* result); 415 | 416 | // Methods to support catching exceptions 417 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_exception_pending(napi_env env, 418 | bool* result); 419 | NAPI_EXTERN napi_status NAPI_CDECL 420 | napi_get_and_clear_last_exception(napi_env env, napi_value* result); 421 | 422 | // Methods to work with array buffers and typed arrays 423 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_arraybuffer(napi_env env, 424 | napi_value value, 425 | bool* result); 426 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_arraybuffer(napi_env env, 427 | size_t byte_length, 428 | void** data, 429 | napi_value* result); 430 | #ifndef NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED 431 | NAPI_EXTERN napi_status NAPI_CDECL 432 | napi_create_external_arraybuffer(napi_env env, 433 | void* external_data, 434 | size_t byte_length, 435 | node_api_nogc_finalize finalize_cb, 436 | void* finalize_hint, 437 | napi_value* result); 438 | #endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED 439 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info( 440 | napi_env env, napi_value arraybuffer, void** data, size_t* byte_length); 441 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_typedarray(napi_env env, 442 | napi_value value, 443 | bool* result); 444 | NAPI_EXTERN napi_status NAPI_CDECL 445 | napi_create_typedarray(napi_env env, 446 | napi_typedarray_type type, 447 | size_t length, 448 | napi_value arraybuffer, 449 | size_t byte_offset, 450 | napi_value* result); 451 | NAPI_EXTERN napi_status NAPI_CDECL 452 | napi_get_typedarray_info(napi_env env, 453 | napi_value typedarray, 454 | napi_typedarray_type* type, 455 | size_t* length, 456 | void** data, 457 | napi_value* arraybuffer, 458 | size_t* byte_offset); 459 | 460 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_dataview(napi_env env, 461 | size_t length, 462 | napi_value arraybuffer, 463 | size_t byte_offset, 464 | napi_value* result); 465 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_dataview(napi_env env, 466 | napi_value value, 467 | bool* result); 468 | NAPI_EXTERN napi_status NAPI_CDECL 469 | napi_get_dataview_info(napi_env env, 470 | napi_value dataview, 471 | size_t* bytelength, 472 | void** data, 473 | napi_value* arraybuffer, 474 | size_t* byte_offset); 475 | 476 | // version management 477 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_version(node_api_nogc_env env, 478 | uint32_t* result); 479 | 480 | // Promises 481 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_promise(napi_env env, 482 | napi_deferred* deferred, 483 | napi_value* promise); 484 | NAPI_EXTERN napi_status NAPI_CDECL napi_resolve_deferred(napi_env env, 485 | napi_deferred deferred, 486 | napi_value resolution); 487 | NAPI_EXTERN napi_status NAPI_CDECL napi_reject_deferred(napi_env env, 488 | napi_deferred deferred, 489 | napi_value rejection); 490 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_promise(napi_env env, 491 | napi_value value, 492 | bool* is_promise); 493 | 494 | // Running a script 495 | NAPI_EXTERN napi_status NAPI_CDECL napi_run_script(napi_env env, 496 | napi_value script, 497 | napi_value* result); 498 | 499 | // Memory management 500 | NAPI_EXTERN napi_status NAPI_CDECL napi_adjust_external_memory( 501 | node_api_nogc_env env, int64_t change_in_bytes, int64_t* adjusted_value); 502 | 503 | #if NAPI_VERSION >= 5 504 | 505 | // Dates 506 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_date(napi_env env, 507 | double time, 508 | napi_value* result); 509 | 510 | NAPI_EXTERN napi_status NAPI_CDECL napi_is_date(napi_env env, 511 | napi_value value, 512 | bool* is_date); 513 | 514 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_date_value(napi_env env, 515 | napi_value value, 516 | double* result); 517 | 518 | // Add finalizer for pointer 519 | NAPI_EXTERN napi_status NAPI_CDECL 520 | napi_add_finalizer(napi_env env, 521 | napi_value js_object, 522 | void* finalize_data, 523 | node_api_nogc_finalize finalize_cb, 524 | void* finalize_hint, 525 | napi_ref* result); 526 | 527 | #endif // NAPI_VERSION >= 5 528 | 529 | #ifdef NAPI_EXPERIMENTAL 530 | #define NODE_API_EXPERIMENTAL_HAS_POST_FINALIZER 531 | 532 | NAPI_EXTERN napi_status NAPI_CDECL 533 | node_api_post_finalizer(node_api_nogc_env env, 534 | napi_finalize finalize_cb, 535 | void* finalize_data, 536 | void* finalize_hint); 537 | 538 | #endif // NAPI_EXPERIMENTAL 539 | 540 | #if NAPI_VERSION >= 6 541 | 542 | // BigInt 543 | NAPI_EXTERN napi_status NAPI_CDECL napi_create_bigint_int64(napi_env env, 544 | int64_t value, 545 | napi_value* result); 546 | NAPI_EXTERN napi_status NAPI_CDECL 547 | napi_create_bigint_uint64(napi_env env, uint64_t value, napi_value* result); 548 | NAPI_EXTERN napi_status NAPI_CDECL 549 | napi_create_bigint_words(napi_env env, 550 | int sign_bit, 551 | size_t word_count, 552 | const uint64_t* words, 553 | napi_value* result); 554 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_int64(napi_env env, 555 | napi_value value, 556 | int64_t* result, 557 | bool* lossless); 558 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_value_bigint_uint64( 559 | napi_env env, napi_value value, uint64_t* result, bool* lossless); 560 | NAPI_EXTERN napi_status NAPI_CDECL 561 | napi_get_value_bigint_words(napi_env env, 562 | napi_value value, 563 | int* sign_bit, 564 | size_t* word_count, 565 | uint64_t* words); 566 | 567 | // Object 568 | NAPI_EXTERN napi_status NAPI_CDECL 569 | napi_get_all_property_names(napi_env env, 570 | napi_value object, 571 | napi_key_collection_mode key_mode, 572 | napi_key_filter key_filter, 573 | napi_key_conversion key_conversion, 574 | napi_value* result); 575 | 576 | // Instance data 577 | NAPI_EXTERN napi_status NAPI_CDECL 578 | napi_set_instance_data(node_api_nogc_env env, 579 | void* data, 580 | napi_finalize finalize_cb, 581 | void* finalize_hint); 582 | 583 | NAPI_EXTERN napi_status NAPI_CDECL napi_get_instance_data(node_api_nogc_env env, 584 | void** data); 585 | #endif // NAPI_VERSION >= 6 586 | 587 | #if NAPI_VERSION >= 7 588 | // ArrayBuffer detaching 589 | NAPI_EXTERN napi_status NAPI_CDECL 590 | napi_detach_arraybuffer(napi_env env, napi_value arraybuffer); 591 | 592 | NAPI_EXTERN napi_status NAPI_CDECL 593 | napi_is_detached_arraybuffer(napi_env env, napi_value value, bool* result); 594 | #endif // NAPI_VERSION >= 7 595 | 596 | #if NAPI_VERSION >= 8 597 | // Type tagging 598 | NAPI_EXTERN napi_status NAPI_CDECL napi_type_tag_object( 599 | napi_env env, napi_value value, const napi_type_tag* type_tag); 600 | 601 | NAPI_EXTERN napi_status NAPI_CDECL 602 | napi_check_object_type_tag(napi_env env, 603 | napi_value value, 604 | const napi_type_tag* type_tag, 605 | bool* result); 606 | NAPI_EXTERN napi_status NAPI_CDECL napi_object_freeze(napi_env env, 607 | napi_value object); 608 | NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env, 609 | napi_value object); 610 | #endif // NAPI_VERSION >= 8 611 | 612 | EXTERN_C_END 613 | 614 | #endif // SRC_JS_NATIVE_API_H_ 615 | -------------------------------------------------------------------------------- /node-api/js_native_api_types.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_JS_NATIVE_API_TYPES_H_ 2 | #define SRC_JS_NATIVE_API_TYPES_H_ 3 | 4 | // This file needs to be compatible with C compilers. 5 | // This is a public include file, and these includes have essentially 6 | // became part of it's API. 7 | #include // NOLINT(modernize-deprecated-headers) 8 | #include // NOLINT(modernize-deprecated-headers) 9 | 10 | // TODO: (vmoroz) NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT is not part of this 11 | // Node-API file. It is here temporary to enable compilation. Remove it after 12 | // updating tests. 13 | #define NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT 14 | 15 | #if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) 16 | typedef uint16_t char16_t; 17 | #endif 18 | 19 | #ifndef NAPI_CDECL 20 | #ifdef _WIN32 21 | #define NAPI_CDECL __cdecl 22 | #else 23 | #define NAPI_CDECL 24 | #endif 25 | #endif 26 | 27 | // JSVM API types are all opaque pointers for ABI stability 28 | // typedef undefined structs instead of void* for compile time type safety 29 | typedef struct napi_env__* napi_env; 30 | 31 | // We need to mark APIs which can be called during garbage collection (GC), 32 | // meaning that they do not affect the state of the JS engine, and can 33 | // therefore be called synchronously from a finalizer that itself runs 34 | // synchronously during GC. Such APIs can receive either a `napi_env` or a 35 | // `node_api_nogc_env` as their first parameter, because we should be able to 36 | // also call them during normal, non-garbage-collecting operations, whereas 37 | // APIs that affect the state of the JS engine can only receive a `napi_env` as 38 | // their first parameter, because we must not call them during GC. In lieu of 39 | // inheritance, we use the properties of the const qualifier to accomplish 40 | // this, because both a const and a non-const value can be passed to an API 41 | // expecting a const value, but only a non-const value can be passed to an API 42 | // expecting a non-const value. 43 | // 44 | // In conjunction with appropriate CFLAGS to warn us if we're passing a const 45 | // (nogc) environment into an API that expects a non-const environment, and the 46 | // definition of nogc finalizer function pointer types below, which receive a 47 | // nogc environment as their first parameter, and can thus only call nogc APIs 48 | // (unless the user explicitly casts the environment), we achieve the ability 49 | // to ensure at compile time that we do not call APIs that affect the state of 50 | // the JS engine from a synchronous (nogc) finalizer. 51 | #if !defined(NAPI_EXPERIMENTAL) || \ 52 | (defined(NAPI_EXPERIMENTAL) && \ 53 | defined(NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT)) 54 | typedef struct napi_env__* node_api_nogc_env; 55 | #else 56 | typedef const struct napi_env__* node_api_nogc_env; 57 | #endif 58 | 59 | typedef struct napi_value__* napi_value; 60 | typedef struct napi_ref__* napi_ref; 61 | typedef struct napi_handle_scope__* napi_handle_scope; 62 | typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope; 63 | typedef struct napi_callback_info__* napi_callback_info; 64 | typedef struct napi_deferred__* napi_deferred; 65 | 66 | typedef enum { 67 | napi_default = 0, 68 | napi_writable = 1 << 0, 69 | napi_enumerable = 1 << 1, 70 | napi_configurable = 1 << 2, 71 | 72 | // Used with napi_define_class to distinguish static properties 73 | // from instance properties. Ignored by napi_define_properties. 74 | napi_static = 1 << 10, 75 | 76 | #if NAPI_VERSION >= 8 77 | // Default for class methods. 78 | napi_default_method = napi_writable | napi_configurable, 79 | 80 | // Default for object properties, like in JS obj[prop]. 81 | napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable, 82 | #endif // NAPI_VERSION >= 8 83 | } napi_property_attributes; 84 | 85 | typedef enum { 86 | // ES6 types (corresponds to typeof) 87 | napi_undefined, 88 | napi_null, 89 | napi_boolean, 90 | napi_number, 91 | napi_string, 92 | napi_symbol, 93 | napi_object, 94 | napi_function, 95 | napi_external, 96 | napi_bigint, 97 | } napi_valuetype; 98 | 99 | typedef enum { 100 | napi_int8_array, 101 | napi_uint8_array, 102 | napi_uint8_clamped_array, 103 | napi_int16_array, 104 | napi_uint16_array, 105 | napi_int32_array, 106 | napi_uint32_array, 107 | napi_float32_array, 108 | napi_float64_array, 109 | napi_bigint64_array, 110 | napi_biguint64_array, 111 | } napi_typedarray_type; 112 | 113 | typedef enum { 114 | napi_ok, 115 | napi_invalid_arg, 116 | napi_object_expected, 117 | napi_string_expected, 118 | napi_name_expected, 119 | napi_function_expected, 120 | napi_number_expected, 121 | napi_boolean_expected, 122 | napi_array_expected, 123 | napi_generic_failure, 124 | napi_pending_exception, 125 | napi_cancelled, 126 | napi_escape_called_twice, 127 | napi_handle_scope_mismatch, 128 | napi_callback_scope_mismatch, 129 | napi_queue_full, 130 | napi_closing, 131 | napi_bigint_expected, 132 | napi_date_expected, 133 | napi_arraybuffer_expected, 134 | napi_detachable_arraybuffer_expected, 135 | napi_would_deadlock, // unused 136 | napi_no_external_buffers_allowed, 137 | napi_cannot_run_js, 138 | } napi_status; 139 | // Note: when adding a new enum value to `napi_status`, please also update 140 | // * `const int last_status` in the definition of `napi_get_last_error_info()' 141 | // in file js_native_api_v8.cc. 142 | // * `const char* error_messages[]` in file js_native_api_v8.cc with a brief 143 | // message explaining the error. 144 | // * the definition of `napi_status` in doc/api/n-api.md to reflect the newly 145 | // added value(s). 146 | 147 | typedef napi_value(NAPI_CDECL* napi_callback)(napi_env env, 148 | napi_callback_info info); 149 | typedef void(NAPI_CDECL* napi_finalize)(napi_env env, 150 | void* finalize_data, 151 | void* finalize_hint); 152 | 153 | #if !defined(NAPI_EXPERIMENTAL) || \ 154 | (defined(NAPI_EXPERIMENTAL) && \ 155 | defined(NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT)) 156 | typedef napi_finalize node_api_nogc_finalize; 157 | #else 158 | typedef void(NAPI_CDECL* node_api_nogc_finalize)(node_api_nogc_env env, 159 | void* finalize_data, 160 | void* finalize_hint); 161 | #endif 162 | 163 | typedef struct { 164 | // One of utf8name or name should be NULL. 165 | const char* utf8name; 166 | napi_value name; 167 | 168 | napi_callback method; 169 | napi_callback getter; 170 | napi_callback setter; 171 | napi_value value; 172 | 173 | napi_property_attributes attributes; 174 | void* data; 175 | } napi_property_descriptor; 176 | 177 | typedef struct { 178 | const char* error_message; 179 | void* engine_reserved; 180 | uint32_t engine_error_code; 181 | napi_status error_code; 182 | } napi_extended_error_info; 183 | 184 | #if NAPI_VERSION >= 6 185 | typedef enum { 186 | napi_key_include_prototypes, 187 | napi_key_own_only 188 | } napi_key_collection_mode; 189 | 190 | typedef enum { 191 | napi_key_all_properties = 0, 192 | napi_key_writable = 1, 193 | napi_key_enumerable = 1 << 1, 194 | napi_key_configurable = 1 << 2, 195 | napi_key_skip_strings = 1 << 3, 196 | napi_key_skip_symbols = 1 << 4 197 | } napi_key_filter; 198 | 199 | typedef enum { 200 | napi_key_keep_numbers, 201 | napi_key_numbers_to_strings 202 | } napi_key_conversion; 203 | #endif // NAPI_VERSION >= 6 204 | 205 | #if NAPI_VERSION >= 8 206 | typedef struct { 207 | uint64_t lower; 208 | uint64_t upper; 209 | } napi_type_tag; 210 | #endif // NAPI_VERSION >= 8 211 | 212 | #endif // SRC_JS_NATIVE_API_TYPES_H_ 213 | -------------------------------------------------------------------------------- /node-api/js_runtime_api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef SRC_JS_RUNTIME_API_H_ 5 | #define SRC_JS_RUNTIME_API_H_ 6 | 7 | #include "js_native_api.h" 8 | 9 | // 10 | // Node-API extensions required for JavaScript engine hosting. 11 | // 12 | // It is a very early version of the APIs which we consider to be experimental. 13 | // These APIs are not stable yet and are subject to change while we continue 14 | // their development. After some time we will stabilize the APIs and make them 15 | // "officially stable". 16 | // 17 | 18 | #define JSR_API NAPI_EXTERN napi_status NAPI_CDECL 19 | 20 | EXTERN_C_START 21 | 22 | typedef struct jsr_runtime_s* jsr_runtime; 23 | typedef struct jsr_config_s* jsr_config; 24 | typedef struct jsr_prepared_script_s* jsr_prepared_script; 25 | typedef struct jsr_napi_env_scope_s* jsr_napi_env_scope; 26 | 27 | typedef void(NAPI_CDECL* jsr_data_delete_cb)(void* data, void* deleter_data); 28 | 29 | //============================================================================= 30 | // jsr_runtime 31 | //============================================================================= 32 | 33 | JSR_API jsr_create_runtime(jsr_config config, jsr_runtime* runtime); 34 | JSR_API jsr_delete_runtime(jsr_runtime runtime); 35 | JSR_API jsr_runtime_get_node_api_env(jsr_runtime runtime, napi_env* env); 36 | 37 | //============================================================================= 38 | // jsr_config 39 | //============================================================================= 40 | 41 | JSR_API jsr_create_config(jsr_config* config); 42 | JSR_API jsr_delete_config(jsr_config config); 43 | 44 | JSR_API jsr_config_enable_inspector(jsr_config config, bool value); 45 | JSR_API jsr_config_set_inspector_runtime_name(jsr_config config, 46 | const char* name); 47 | JSR_API jsr_config_set_inspector_port(jsr_config config, uint16_t port); 48 | JSR_API jsr_config_set_inspector_break_on_start(jsr_config config, bool value); 49 | 50 | JSR_API jsr_config_enable_gc_api(jsr_config config, bool value); 51 | 52 | JSR_API jsr_config_set_explicit_microtasks(jsr_config config, bool value); 53 | 54 | //============================================================================= 55 | // jsr_config task runner 56 | //============================================================================= 57 | 58 | // A callback to run task 59 | typedef void(NAPI_CDECL* jsr_task_run_cb)(void* task_data); 60 | 61 | // A callback to post task to the task runner 62 | typedef void(NAPI_CDECL* jsr_task_runner_post_task_cb)( 63 | void* task_runner_data, 64 | void* task_data, 65 | jsr_task_run_cb task_run_cb, 66 | jsr_data_delete_cb task_data_delete_cb, 67 | void* deleter_data); 68 | 69 | JSR_API jsr_config_set_task_runner( 70 | jsr_config config, 71 | void* task_runner_data, 72 | jsr_task_runner_post_task_cb task_runner_post_task_cb, 73 | jsr_data_delete_cb task_runner_data_delete_cb, 74 | void* deleter_data); 75 | 76 | //============================================================================= 77 | // jsr_config script cache 78 | //============================================================================= 79 | 80 | typedef void(NAPI_CDECL* jsr_script_cache_load_cb)( 81 | void* script_cache_data, 82 | const char* source_url, 83 | uint64_t source_hash, 84 | const char* runtime_name, 85 | uint64_t runtime_version, 86 | const char* cache_tag, 87 | const uint8_t** buffer, 88 | size_t* buffer_size, 89 | jsr_data_delete_cb* buffer_delete_cb, 90 | void** deleter_data); 91 | 92 | typedef void(NAPI_CDECL* jsr_script_cache_store_cb)( 93 | void* script_cache_data, 94 | const char* source_url, 95 | uint64_t source_hash, 96 | const char* runtime_name, 97 | uint64_t runtime_version, 98 | const char* cache_tag, 99 | const uint8_t* buffer, 100 | size_t buffer_size, 101 | jsr_data_delete_cb buffer_delete_cb, 102 | void* deleter_data); 103 | 104 | JSR_API jsr_config_set_script_cache( 105 | jsr_config config, 106 | void* script_cache_data, 107 | jsr_script_cache_load_cb script_cache_load_cb, 108 | jsr_script_cache_store_cb script_cache_store_cb, 109 | jsr_data_delete_cb script_cache_data_delete_cb, 110 | void* deleter_data); 111 | 112 | //============================================================================= 113 | // napi_env scope 114 | //============================================================================= 115 | 116 | // Opens the napi_env scope in the current thread. 117 | // Calling Node-API functions without the opened scope may cause a failure. 118 | // The scope must be closed by the jsr_close_napi_env_scope call. 119 | JSR_API jsr_open_napi_env_scope(napi_env env, jsr_napi_env_scope* scope); 120 | 121 | // Closes the napi_env scope in the current thread. It must match to the 122 | // jsr_open_napi_env_scope call. 123 | JSR_API jsr_close_napi_env_scope(napi_env env, jsr_napi_env_scope scope); 124 | 125 | //============================================================================= 126 | // Additional functions to implement JSI 127 | //============================================================================= 128 | 129 | // To implement JSI description() 130 | JSR_API jsr_get_description(napi_env env, const char** result); 131 | 132 | // To implement JSI queueMicrotask() 133 | JSR_API jsr_queue_microtask(napi_env env, napi_value callback); 134 | 135 | // To implement JSI drainMicrotasks() 136 | JSR_API 137 | jsr_drain_microtasks(napi_env env, int32_t max_count_hint, bool* result); 138 | 139 | // To implement JSI isInspectable() 140 | JSR_API jsr_is_inspectable(napi_env env, bool* result); 141 | 142 | //============================================================================= 143 | // Script preparing and running. 144 | // 145 | // Script is usually converted to byte code, or in other words - prepared - for 146 | // execution. Then, we can run the prepared script. 147 | //============================================================================= 148 | 149 | // Run script with source URL. 150 | JSR_API jsr_run_script(napi_env env, 151 | napi_value source, 152 | const char* source_url, 153 | napi_value* result); 154 | 155 | // Prepare the script for running. 156 | JSR_API jsr_create_prepared_script(napi_env env, 157 | const uint8_t* script_data, 158 | size_t script_length, 159 | jsr_data_delete_cb script_delete_cb, 160 | void* deleter_data, 161 | const char* source_url, 162 | jsr_prepared_script* result); 163 | 164 | // Delete the prepared script. 165 | JSR_API jsr_delete_prepared_script(napi_env env, 166 | jsr_prepared_script prepared_script); 167 | 168 | // Run the prepared script. 169 | JSR_API jsr_prepared_script_run(napi_env env, 170 | jsr_prepared_script prepared_script, 171 | napi_value* result); 172 | 173 | //============================================================================= 174 | // Functions to support unit tests. 175 | //============================================================================= 176 | 177 | // Provides a hint to run garbage collection. 178 | // It is typically used for unit tests. 179 | // It requires enabling GC by calling jsr_config_enable_gc_api. 180 | JSR_API jsr_collect_garbage(napi_env env); 181 | 182 | // Checks if the environment has an unhandled promise rejection. 183 | JSR_API jsr_has_unhandled_promise_rejection(napi_env env, bool* result); 184 | 185 | // Gets and clears the last unhandled promise rejection. 186 | JSR_API jsr_get_and_clear_last_unhandled_promise_rejection(napi_env env, 187 | napi_value* result); 188 | 189 | // Create new napi_env for the runtime. 190 | JSR_API jsr_create_node_api_env(napi_env root_env, 191 | int32_t api_version, 192 | napi_env* env); 193 | 194 | // Run task in the environment context. 195 | JSR_API jsr_run_task(napi_env env, jsr_task_run_cb task_cb, void* data); 196 | 197 | EXTERN_C_END 198 | 199 | #endif // !SRC_JS_RUNTIME_API_H_ 200 | -------------------------------------------------------------------------------- /src/ApiLoaders/HermesApi.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "HermesApi.h" 5 | 6 | namespace Microsoft::NodeApiJsi { 7 | 8 | namespace { 9 | 10 | struct HermesApiNames { 11 | #define HERMES_FUNC(func) static constexpr const char func[] = #func; 12 | #define HERMES_INSPECTOR_FUNC(func) HERMES_FUNC(func); 13 | #include "HermesApi.inc" 14 | }; 15 | 16 | // Load all inspector functions together to ensure their availability from different threads. 17 | void loadInspectorFuncs() { 18 | HermesApi *current = HermesApi::current(); 19 | #define HERMES_INSPECTOR_FUNC(func) \ 20 | decltype(::func) *loaded_##func = reinterpret_cast(current->getFuncPtr(HermesApiNames::func)); \ 21 | size_t offset_##func = offsetof(HermesApi, func); \ 22 | *reinterpret_cast(reinterpret_cast(current) + offset_##func) = loaded_##func; 23 | #include "HermesApi.inc" 24 | } 25 | 26 | } // namespace 27 | 28 | thread_local HermesApi *HermesApi::current_{}; 29 | 30 | HermesApi::HermesApi(IFuncResolver *funcResolver) 31 | : JSRuntimeApi(funcResolver) 32 | #define HERMES_FUNC(func) \ 33 | , func(&ApiFuncResolver::stub) 34 | #define HERMES_INSPECTOR_FUNC(func) \ 35 | , \ 36 | func(&ApiFuncResolver:: \ 37 | preloadStub<&loadInspectorFuncs>) 38 | 39 | #include "HermesApi.inc" 40 | { 41 | } 42 | 43 | HermesApi *HermesApi::fromLib() { 44 | static LibFuncResolver funcResolver("hermes"); 45 | static HermesApi *libHermesApi = new HermesApi(&funcResolver); 46 | return libHermesApi; 47 | } 48 | 49 | } // namespace Microsoft::NodeApiJsi 50 | -------------------------------------------------------------------------------- /src/ApiLoaders/HermesApi.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #ifndef APILOADERS_HERMESAPI_H_ 6 | #define APILOADERS_HERMESAPI_H_ 7 | 8 | #include 9 | #include "JSRuntimeApi.h" 10 | 11 | namespace Microsoft::NodeApiJsi { 12 | 13 | class HermesApi : public JSRuntimeApi { 14 | public: 15 | HermesApi(IFuncResolver *funcResolver); 16 | 17 | static HermesApi *current() noexcept { 18 | return current_; 19 | } 20 | 21 | static void setCurrent(HermesApi *current) noexcept { 22 | JSRuntimeApi::setCurrent(current); 23 | current_ = current; 24 | } 25 | 26 | static HermesApi *fromLib(); 27 | 28 | class Scope : public JSRuntimeApi::Scope { 29 | public: 30 | Scope() : Scope(HermesApi::fromLib()) {} 31 | 32 | Scope(HermesApi *hermesApi) : JSRuntimeApi::Scope(hermesApi), prevHermesApi_(HermesApi::current_) { 33 | HermesApi::current_ = hermesApi; 34 | } 35 | 36 | ~Scope() { 37 | HermesApi::current_ = prevHermesApi_; 38 | } 39 | 40 | private: 41 | HermesApi *prevHermesApi_; 42 | }; 43 | 44 | public: 45 | #define HERMES_FUNC(func) decltype(::func) *const func; 46 | #define HERMES_INSPECTOR_FUNC(func) HERMES_FUNC(func) 47 | #include "HermesApi.inc" 48 | 49 | private: 50 | static thread_local HermesApi *current_; 51 | }; 52 | 53 | } // namespace Microsoft::NodeApiJsi 54 | 55 | #endif // !APILOADERS_HERMESAPI_H_ 56 | -------------------------------------------------------------------------------- /src/ApiLoaders/HermesApi.inc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | // The Hermes functions sorted alphabetically 5 | 6 | #ifndef HERMES_FUNC 7 | #define HERMES_FUNC(func) 8 | #endif 9 | 10 | #ifndef HERMES_INSPECTOR_FUNC 11 | #define HERMES_INSPECTOR_FUNC(func) 12 | #endif 13 | 14 | HERMES_FUNC(hermes_dump_crash_data) 15 | HERMES_FUNC(hermes_sampling_profiler_enable) 16 | HERMES_FUNC(hermes_sampling_profiler_disable) 17 | HERMES_FUNC(hermes_sampling_profiler_add) 18 | HERMES_FUNC(hermes_sampling_profiler_remove) 19 | HERMES_FUNC(hermes_sampling_profiler_dump_to_file) 20 | HERMES_FUNC(hermes_config_enable_default_crash_handler) 21 | HERMES_FUNC(hermes_set_inspector) 22 | 23 | HERMES_INSPECTOR_FUNC(hermes_create_local_connection) 24 | HERMES_INSPECTOR_FUNC(hermes_delete_local_connection) 25 | HERMES_INSPECTOR_FUNC(hermes_local_connection_send_message) 26 | HERMES_INSPECTOR_FUNC(hermes_local_connection_disconnect) 27 | 28 | #undef HERMES_FUNC 29 | #undef HERMES_INSPECTOR_FUNC 30 | -------------------------------------------------------------------------------- /src/ApiLoaders/JSRuntimeApi.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "JSRuntimeApi.h" 5 | 6 | EXTERN_C_START 7 | 8 | // Default JSR function implementations if they are not found in the engine DLL. 9 | extern napi_status NAPI_CDECL default_jsr_open_napi_env_scope(napi_env env, jsr_napi_env_scope *scope); 10 | extern napi_status NAPI_CDECL default_jsr_close_napi_env_scope(napi_env env, jsr_napi_env_scope scope); 11 | extern napi_status NAPI_CDECL default_jsr_get_description(napi_env env, const char **result); 12 | extern napi_status NAPI_CDECL default_jsr_queue_microtask(napi_env env, napi_value callback); 13 | extern napi_status NAPI_CDECL default_jsr_drain_microtasks(napi_env env, int32_t max_count_hint, bool *result); 14 | extern napi_status NAPI_CDECL default_jsr_is_inspectable(napi_env env, bool *result); 15 | 16 | extern napi_status NAPI_CDECL default_jsr_create_prepared_script( 17 | napi_env env, 18 | const uint8_t *script_utf8, 19 | size_t script_length, 20 | jsr_data_delete_cb script_delete_cb, 21 | void *deleter_data, 22 | const char *source_url, 23 | jsr_prepared_script *result); 24 | extern napi_status NAPI_CDECL default_jsr_delete_prepared_script(napi_env env, jsr_prepared_script prepared_script); 25 | extern napi_status NAPI_CDECL 26 | default_jsr_prepared_script_run(napi_env env, jsr_prepared_script prepared_script, napi_value *result); 27 | 28 | EXTERN_C_END 29 | 30 | namespace Microsoft::NodeApiJsi { 31 | 32 | namespace { 33 | 34 | struct JSRuntimeApiNames { 35 | #define JSR_FUNC(func) static constexpr const char func[] = #func; 36 | #define JSR_JSI_FUNC JSR_FUNC 37 | #define JSR_PREPARED_SCRIPT JSR_FUNC 38 | #include "JSRuntimeApi.inc" 39 | }; 40 | 41 | // Prepared script functions either should be all loaded or we use all default functions. 42 | void loadPreparedScriptFuncs() { 43 | JSRuntimeApi *current = JSRuntimeApi::current(); 44 | bool useDefault = false; 45 | #define JSR_PREPARED_SCRIPT(func) \ 46 | decltype(::func) *loaded_##func = \ 47 | reinterpret_cast(current->getFuncPtr(JSRuntimeApiNames::func)); \ 48 | useDefault = useDefault || loaded_##func == nullptr; 49 | #include "JSRuntimeApi.inc" 50 | #define JSR_PREPARED_SCRIPT(func) \ 51 | size_t offset_##func = offsetof(JSRuntimeApi, func); \ 52 | *reinterpret_cast(reinterpret_cast(current) + offset_##func) = \ 53 | useDefault ? &default_##func : loaded_##func; 54 | #include "JSRuntimeApi.inc" 55 | } 56 | 57 | } // namespace 58 | 59 | thread_local JSRuntimeApi *JSRuntimeApi::current_{}; 60 | 61 | JSRuntimeApi::JSRuntimeApi(IFuncResolver *funcResolver) 62 | : NodeApi(funcResolver) 63 | #define JSR_FUNC(func) \ 64 | , \ 65 | func(&ApiFuncResolver:: \ 66 | stub) 67 | #define JSR_JSI_FUNC(func) \ 68 | , \ 69 | func(&ApiFuncResolver:: \ 70 | optionalStub<&default_##func>) 71 | #define JSR_PREPARED_SCRIPT(func) \ 72 | , \ 73 | func(&ApiFuncResolver:: \ 74 | preloadStub<&loadPreparedScriptFuncs>) 75 | #include "JSRuntimeApi.inc" 76 | { 77 | } 78 | 79 | } // namespace Microsoft::NodeApiJsi 80 | -------------------------------------------------------------------------------- /src/ApiLoaders/JSRuntimeApi.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef APILOADERS_JSRUNTIMEAPI_H_ 5 | #define APILOADERS_JSRUNTIMEAPI_H_ 6 | 7 | #include 8 | #include "NodeApi.h" 9 | 10 | namespace Microsoft::NodeApiJsi { 11 | 12 | class JSRuntimeApi : public NodeApi { 13 | public: 14 | JSRuntimeApi(IFuncResolver *funcResolver); 15 | 16 | static JSRuntimeApi *current() { 17 | return current_; 18 | } 19 | 20 | static void setCurrent(JSRuntimeApi *current) { 21 | NodeApi::setCurrent(current); 22 | current_ = current; 23 | } 24 | 25 | class Scope : public NodeApi::Scope { 26 | public: 27 | Scope(JSRuntimeApi *api) : NodeApi::Scope(api), prevJSRuntimeApi_(JSRuntimeApi::current_) { 28 | JSRuntimeApi::current_ = api; 29 | } 30 | 31 | ~Scope() { 32 | JSRuntimeApi::current_ = prevJSRuntimeApi_; 33 | } 34 | 35 | private: 36 | JSRuntimeApi *prevJSRuntimeApi_; 37 | }; 38 | 39 | public: 40 | #define JSR_FUNC(func) decltype(::func) *const func; 41 | #define JSR_JSI_FUNC JSR_FUNC 42 | #define JSR_PREPARED_SCRIPT JSR_FUNC 43 | #include "JSRuntimeApi.inc" 44 | 45 | private: 46 | static thread_local JSRuntimeApi *current_; 47 | }; 48 | 49 | } // namespace Microsoft::NodeApiJsi 50 | 51 | #endif // !APILOADERS_JSRUNTIMEAPI_H_ 52 | -------------------------------------------------------------------------------- /src/ApiLoaders/JSRuntimeApi.inc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef JSR_FUNC 5 | #define JSR_FUNC(func) 6 | #endif 7 | 8 | #ifndef JSR_JSI_FUNC 9 | #define JSR_JSI_FUNC(func) 10 | #endif 11 | 12 | #ifndef JSR_PREPARED_SCRIPT 13 | #define JSR_PREPARED_SCRIPT(func) 14 | #endif 15 | 16 | // The JS runtime functions sorted alphabetically. 17 | JSR_FUNC(jsr_collect_garbage) 18 | JSR_FUNC(jsr_config_enable_gc_api) 19 | JSR_FUNC(jsr_config_enable_inspector) 20 | JSR_FUNC(jsr_config_set_explicit_microtasks) 21 | JSR_FUNC(jsr_config_set_inspector_break_on_start) 22 | JSR_FUNC(jsr_config_set_inspector_port) 23 | JSR_FUNC(jsr_config_set_inspector_runtime_name) 24 | JSR_FUNC(jsr_config_set_script_cache) 25 | JSR_FUNC(jsr_config_set_task_runner) 26 | JSR_FUNC(jsr_create_config) 27 | JSR_FUNC(jsr_create_runtime) 28 | JSR_FUNC(jsr_delete_config) 29 | JSR_FUNC(jsr_delete_runtime) 30 | JSR_FUNC(jsr_get_and_clear_last_unhandled_promise_rejection) 31 | JSR_FUNC(jsr_has_unhandled_promise_rejection) 32 | JSR_FUNC(jsr_run_script) 33 | JSR_FUNC(jsr_runtime_get_node_api_env) 34 | 35 | // The JS runtime functions needed for JSI. 36 | JSR_JSI_FUNC(jsr_close_napi_env_scope) 37 | JSR_JSI_FUNC(jsr_queue_microtask) 38 | JSR_JSI_FUNC(jsr_drain_microtasks) 39 | JSR_JSI_FUNC(jsr_get_description) 40 | JSR_JSI_FUNC(jsr_is_inspectable) 41 | JSR_JSI_FUNC(jsr_open_napi_env_scope) 42 | 43 | // The JS runtime functions needed for prepared script. 44 | JSR_PREPARED_SCRIPT(jsr_create_prepared_script) 45 | JSR_PREPARED_SCRIPT(jsr_delete_prepared_script) 46 | JSR_PREPARED_SCRIPT(jsr_prepared_script_run) 47 | 48 | #undef JSR_FUNC 49 | #undef JSR_JSI_FUNC 50 | #undef JSR_PREPARED_SCRIPT -------------------------------------------------------------------------------- /src/ApiLoaders/NodeApi.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "NodeApi.h" 5 | 6 | namespace Microsoft::NodeApiJsi { 7 | 8 | namespace { 9 | 10 | struct NodeApiNames { 11 | #define NODE_API_FUNC(func) static constexpr const char func[] = #func; 12 | #include "NodeApi.inc" 13 | }; 14 | 15 | } // namespace 16 | 17 | LibFuncResolver::LibFuncResolver(const char *libName) : libHandle_(LibLoader::loadLib(libName)) {} 18 | 19 | FuncPtr LibFuncResolver::getFuncPtr(const char *funcName) { 20 | return LibLoader::getFuncPtr(libHandle_, funcName); 21 | } 22 | 23 | DelayLoadedApi::DelayLoadedApi(IFuncResolver *funcResolver) : funcResolver_(funcResolver) {} 24 | 25 | DelayLoadedApi::~DelayLoadedApi() = default; 26 | 27 | FuncPtr DelayLoadedApi::getFuncPtr(const char *funcName) { 28 | return funcResolver_->getFuncPtr(funcName); 29 | } 30 | 31 | thread_local NodeApi *NodeApi::current_{}; 32 | 33 | NodeApi::NodeApi(IFuncResolver *funcResolver) 34 | : DelayLoadedApi(funcResolver) 35 | #define NODE_API_FUNC(func) \ 36 | , func(&ApiFuncResolver::stub) 37 | #include "NodeApi.inc" 38 | { 39 | } 40 | 41 | } // namespace Microsoft::NodeApiJsi 42 | -------------------------------------------------------------------------------- /src/ApiLoaders/NodeApi.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #ifndef APILOADERS_NODEAPI_H_ 6 | #define APILOADERS_NODEAPI_H_ 7 | 8 | #include 9 | 10 | namespace Microsoft::NodeApiJsi { 11 | 12 | using LibHandle = struct LibHandle_t *; 13 | using FuncPtr = struct FuncPtr_t *; 14 | 15 | class LibLoader { 16 | public: 17 | static LibHandle loadLib(const char *libName); 18 | static FuncPtr getFuncPtr(LibHandle libHandle, const char *funcName); 19 | }; 20 | 21 | template 22 | struct ApiFuncResolver; 23 | 24 | template 25 | struct ApiFuncResolver { 26 | // Stub is a special function that loads the targeting function on the first call, 27 | // replaces function pointer field with the loaded function, and executes the function. 28 | // After this all future calls to the loaded function are going to be directly from 29 | // the field and not use this Stub. 30 | static TResult NAPI_CDECL stub(TArgs... args) { 31 | using TFunc = TResult(NAPI_CDECL *)(TArgs...); 32 | TApi *current = TApi::current(); 33 | TFunc func = reinterpret_cast(current->getFuncPtr(funcName)); 34 | *reinterpret_cast(reinterpret_cast(current) + offset) = func; 35 | return (*func)(args...); 36 | } 37 | 38 | // Optional stub tries to load target function on the first call and set it 39 | // as the function pointer. If loading fails, then it uses provided default 40 | // function implementation. The default function can either do the required 41 | // work or return a failure. 42 | template 43 | static TResult NAPI_CDECL optionalStub(TArgs... args) { 44 | using TFunc = TResult(NAPI_CDECL *)(TArgs...); 45 | TApi *current = TApi::current(); 46 | TFunc func = reinterpret_cast(current->getFuncPtr(funcName)); 47 | if (func == nullptr) { 48 | func = defaultFunc; 49 | } 50 | *reinterpret_cast(reinterpret_cast(current) + offset) = func; 51 | return (*func)(args...); 52 | } 53 | 54 | template 55 | static TResult NAPI_CDECL preloadStub(TArgs... args) { 56 | using TFunc = TResult(NAPI_CDECL *)(TArgs...); 57 | preloadFunc(); 58 | TApi *current = TApi::current(); 59 | TFunc func = *reinterpret_cast(reinterpret_cast(current) + offset); 60 | return (*func)(args...); 61 | } 62 | }; 63 | 64 | struct IFuncResolver { 65 | virtual FuncPtr getFuncPtr(const char *funcName) = 0; 66 | }; 67 | 68 | class LibFuncResolver : public IFuncResolver { 69 | public: 70 | LibFuncResolver(const char *libName); 71 | FuncPtr getFuncPtr(const char *funcName) override; 72 | 73 | private: 74 | LibHandle libHandle_; 75 | }; 76 | 77 | class DelayLoadedApi { 78 | public: 79 | DelayLoadedApi(IFuncResolver *funcResolver); 80 | ~DelayLoadedApi(); 81 | 82 | FuncPtr getFuncPtr(const char *funcName); 83 | 84 | DelayLoadedApi(const DelayLoadedApi &) = delete; 85 | DelayLoadedApi &operator=(const DelayLoadedApi &) = delete; 86 | 87 | private: 88 | IFuncResolver *funcResolver_; 89 | }; 90 | 91 | class NodeApi : public DelayLoadedApi { 92 | public: 93 | NodeApi(IFuncResolver *funcResolver); 94 | 95 | static NodeApi *current() { 96 | return current_; 97 | } 98 | 99 | static void setCurrent(NodeApi *current) { 100 | current_ = current; 101 | } 102 | 103 | class Scope { 104 | public: 105 | Scope(NodeApi *nodeApi) : prevNodeApi_(NodeApi::current_) { 106 | NodeApi::current_ = nodeApi; 107 | } 108 | 109 | ~Scope() { 110 | NodeApi::current_ = prevNodeApi_; 111 | } 112 | 113 | private: 114 | NodeApi *prevNodeApi_; 115 | }; 116 | 117 | public: 118 | #define NODE_API_FUNC(func) decltype(::func) *const func; 119 | #include "NodeApi.inc" 120 | 121 | private: 122 | static thread_local NodeApi *current_; 123 | }; 124 | 125 | } // namespace Microsoft::NodeApiJsi 126 | 127 | #endif // !APILOADERS_NODEAPI_H_ 128 | -------------------------------------------------------------------------------- /src/ApiLoaders/NodeApi.inc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef NODE_API_FUNC 5 | #define NODE_API_FUNC(func) 6 | #endif 7 | 8 | // The Node-API functions sorted alphabetically. 9 | NODE_API_FUNC(napi_add_finalizer) 10 | NODE_API_FUNC(napi_adjust_external_memory) 11 | NODE_API_FUNC(napi_call_function) 12 | NODE_API_FUNC(napi_check_object_type_tag) 13 | NODE_API_FUNC(napi_close_escapable_handle_scope) 14 | NODE_API_FUNC(napi_close_handle_scope) 15 | NODE_API_FUNC(napi_coerce_to_bool) 16 | NODE_API_FUNC(napi_coerce_to_number) 17 | NODE_API_FUNC(napi_coerce_to_object) 18 | NODE_API_FUNC(napi_coerce_to_string) 19 | NODE_API_FUNC(napi_create_array_with_length) 20 | NODE_API_FUNC(napi_create_array) 21 | NODE_API_FUNC(napi_create_arraybuffer) 22 | NODE_API_FUNC(napi_create_bigint_int64) 23 | NODE_API_FUNC(napi_create_bigint_uint64) 24 | NODE_API_FUNC(napi_create_bigint_words) 25 | NODE_API_FUNC(napi_create_dataview) 26 | NODE_API_FUNC(napi_create_date) 27 | NODE_API_FUNC(napi_create_double) 28 | NODE_API_FUNC(napi_create_error) 29 | NODE_API_FUNC(napi_create_external_arraybuffer) 30 | NODE_API_FUNC(napi_create_external) 31 | NODE_API_FUNC(napi_create_function) 32 | NODE_API_FUNC(napi_create_int32) 33 | NODE_API_FUNC(napi_create_int64) 34 | NODE_API_FUNC(napi_create_object) 35 | NODE_API_FUNC(napi_create_promise) 36 | NODE_API_FUNC(napi_create_range_error) 37 | NODE_API_FUNC(napi_create_reference) 38 | NODE_API_FUNC(napi_create_string_latin1) 39 | NODE_API_FUNC(napi_create_string_utf16) 40 | NODE_API_FUNC(napi_create_string_utf8) 41 | NODE_API_FUNC(napi_create_symbol) 42 | NODE_API_FUNC(napi_create_type_error) 43 | NODE_API_FUNC(napi_create_typedarray) 44 | NODE_API_FUNC(napi_create_uint32) 45 | NODE_API_FUNC(napi_define_class) 46 | NODE_API_FUNC(napi_define_properties) 47 | NODE_API_FUNC(napi_delete_element) 48 | NODE_API_FUNC(napi_delete_property) 49 | NODE_API_FUNC(napi_delete_reference) 50 | NODE_API_FUNC(napi_detach_arraybuffer) 51 | NODE_API_FUNC(napi_escape_handle) 52 | NODE_API_FUNC(napi_get_all_property_names) 53 | NODE_API_FUNC(napi_get_and_clear_last_exception) 54 | NODE_API_FUNC(napi_get_array_length) 55 | NODE_API_FUNC(napi_get_arraybuffer_info) 56 | NODE_API_FUNC(napi_get_boolean) 57 | NODE_API_FUNC(napi_get_cb_info) 58 | NODE_API_FUNC(napi_get_dataview_info) 59 | NODE_API_FUNC(napi_get_date_value) 60 | NODE_API_FUNC(napi_get_element) 61 | NODE_API_FUNC(napi_get_global) 62 | NODE_API_FUNC(napi_get_instance_data) 63 | NODE_API_FUNC(napi_get_last_error_info) 64 | NODE_API_FUNC(napi_get_named_property) 65 | NODE_API_FUNC(napi_get_new_target) 66 | NODE_API_FUNC(napi_get_null) 67 | NODE_API_FUNC(napi_get_property_names) 68 | NODE_API_FUNC(napi_get_property) 69 | NODE_API_FUNC(napi_get_prototype) 70 | NODE_API_FUNC(napi_get_reference_value) 71 | NODE_API_FUNC(napi_get_typedarray_info) 72 | NODE_API_FUNC(napi_get_undefined) 73 | NODE_API_FUNC(napi_get_value_bigint_int64) 74 | NODE_API_FUNC(napi_get_value_bigint_uint64) 75 | NODE_API_FUNC(napi_get_value_bigint_words) 76 | NODE_API_FUNC(napi_get_value_bool) 77 | NODE_API_FUNC(napi_get_value_double) 78 | NODE_API_FUNC(napi_get_value_external) 79 | NODE_API_FUNC(napi_get_value_int32) 80 | NODE_API_FUNC(napi_get_value_int64) 81 | NODE_API_FUNC(napi_get_value_string_latin1) 82 | NODE_API_FUNC(napi_get_value_string_utf16) 83 | NODE_API_FUNC(napi_get_value_string_utf8) 84 | NODE_API_FUNC(napi_get_value_uint32) 85 | NODE_API_FUNC(napi_get_version) 86 | NODE_API_FUNC(napi_has_element) 87 | NODE_API_FUNC(napi_has_named_property) 88 | NODE_API_FUNC(napi_has_own_property) 89 | NODE_API_FUNC(napi_has_property) 90 | NODE_API_FUNC(napi_instanceof) 91 | NODE_API_FUNC(napi_is_array) 92 | NODE_API_FUNC(napi_is_arraybuffer) 93 | NODE_API_FUNC(napi_is_dataview) 94 | NODE_API_FUNC(napi_is_date) 95 | NODE_API_FUNC(napi_is_detached_arraybuffer) 96 | NODE_API_FUNC(napi_is_error) 97 | NODE_API_FUNC(napi_is_exception_pending) 98 | NODE_API_FUNC(napi_is_promise) 99 | NODE_API_FUNC(napi_is_typedarray) 100 | NODE_API_FUNC(napi_new_instance) 101 | NODE_API_FUNC(napi_object_freeze) 102 | NODE_API_FUNC(napi_object_seal) 103 | NODE_API_FUNC(napi_open_escapable_handle_scope) 104 | NODE_API_FUNC(napi_open_handle_scope) 105 | NODE_API_FUNC(napi_reference_ref) 106 | NODE_API_FUNC(napi_reference_unref) 107 | NODE_API_FUNC(napi_reject_deferred) 108 | NODE_API_FUNC(napi_remove_wrap) 109 | NODE_API_FUNC(napi_resolve_deferred) 110 | NODE_API_FUNC(napi_run_script) 111 | NODE_API_FUNC(napi_set_element) 112 | NODE_API_FUNC(napi_set_instance_data) 113 | NODE_API_FUNC(napi_set_named_property) 114 | NODE_API_FUNC(napi_set_property) 115 | NODE_API_FUNC(napi_strict_equals) 116 | NODE_API_FUNC(napi_throw_error) 117 | NODE_API_FUNC(napi_throw_range_error) 118 | NODE_API_FUNC(napi_throw_type_error) 119 | NODE_API_FUNC(napi_throw) 120 | NODE_API_FUNC(napi_type_tag_object) 121 | NODE_API_FUNC(napi_typeof) 122 | NODE_API_FUNC(napi_unwrap) 123 | NODE_API_FUNC(napi_wrap) 124 | 125 | #undef NODE_API_FUNC 126 | -------------------------------------------------------------------------------- /src/ApiLoaders/NodeApi_posix.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "NodeApi.h" 5 | 6 | namespace Microsoft::NodeApiJsi { 7 | 8 | LibHandle LibLoader::loadLib(const char *libName) { 9 | // TODO: implement 10 | } 11 | 12 | FuncPtr LibLoader::getFuncPtr(LibHandle libHandle, const char *funcName) { 13 | // TODO: implement 14 | } 15 | 16 | } // namespace Microsoft::NodeApiJsi 17 | -------------------------------------------------------------------------------- /src/ApiLoaders/NodeApi_win.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "NodeApi.h" 5 | #ifndef WIN32_LEAN_AND_MEAN 6 | #define WIN32_LEAN_AND_MEAN 7 | #endif 8 | #ifndef NOMINMAX 9 | #define NOMINMAX 10 | #endif 11 | #include 12 | 13 | namespace Microsoft::NodeApiJsi { 14 | 15 | LibHandle LibLoader::loadLib(const char *libName) { 16 | return reinterpret_cast(LoadLibraryA(libName)); 17 | } 18 | 19 | FuncPtr LibLoader::getFuncPtr(LibHandle libHandle, const char *funcName) { 20 | return reinterpret_cast(GetProcAddress(reinterpret_cast(libHandle), funcName)); 21 | } 22 | 23 | } // namespace Microsoft::NodeApiJsi 24 | -------------------------------------------------------------------------------- /src/ApiLoaders/V8Api.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include "V8Api.h" 5 | 6 | namespace Microsoft::NodeApiJsi { 7 | 8 | namespace { 9 | 10 | struct V8ApiNames { 11 | #define V8_FUNC(func) static constexpr const char func[] = #func; 12 | #include "V8Api.inc" 13 | }; 14 | 15 | } // namespace 16 | 17 | thread_local V8Api *V8Api::current_{}; 18 | 19 | V8Api::V8Api(IFuncResolver *funcResolver) 20 | : JSRuntimeApi(funcResolver) 21 | #define V8_FUNC(func) , func(&ApiFuncResolver::stub) 22 | #include "V8Api.inc" 23 | { 24 | } 25 | 26 | V8Api *V8Api::fromLib() { 27 | static LibFuncResolver funcResolver("v8jsi"); 28 | static V8Api *libV8Api = new V8Api(&funcResolver); 29 | return libV8Api; 30 | } 31 | 32 | } // namespace Microsoft::NodeApiJsi 33 | -------------------------------------------------------------------------------- /src/ApiLoaders/V8Api.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #ifndef APILOADERS_V8API_H_ 6 | #define APILOADERS_V8API_H_ 7 | 8 | #include 9 | #include "JSRuntimeApi.h" 10 | 11 | namespace Microsoft::NodeApiJsi { 12 | 13 | class V8Api : public JSRuntimeApi { 14 | public: 15 | V8Api(IFuncResolver *funcResolver); 16 | 17 | static V8Api *current() noexcept { 18 | return current_; 19 | } 20 | 21 | static void setCurrent(V8Api *current) noexcept { 22 | JSRuntimeApi::setCurrent(current); 23 | current_ = current; 24 | } 25 | 26 | static V8Api *fromLib(); 27 | 28 | class Scope : public JSRuntimeApi::Scope { 29 | public: 30 | Scope() : Scope(V8Api::fromLib()) {} 31 | 32 | Scope(V8Api *v8Api) : JSRuntimeApi::Scope(v8Api), prevV8Api_(V8Api::current_) { 33 | V8Api::current_ = v8Api; 34 | } 35 | 36 | ~Scope() { 37 | V8Api::current_ = prevV8Api_; 38 | } 39 | 40 | private: 41 | V8Api *prevV8Api_; 42 | }; 43 | 44 | public: 45 | #define V8_FUNC(func) decltype(::func) *const func; 46 | #include "V8Api.inc" 47 | 48 | private: 49 | static thread_local V8Api *current_; 50 | }; 51 | 52 | } // namespace Microsoft::NodeApiJsi 53 | 54 | #endif // !APILOADERS_V8API_H_ 55 | -------------------------------------------------------------------------------- /src/ApiLoaders/V8Api.inc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #ifndef V8_FUNC 5 | #define V8_FUNC(func) 6 | #endif 7 | 8 | // The V8 runtime functions sorted alphabetically. 9 | V8_FUNC(v8_config_enable_multithreading) 10 | 11 | #undef V8_FUNC 12 | -------------------------------------------------------------------------------- /src/NodeApiJsiRuntime.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #pragma once 5 | #ifndef NODEAPIJSIRUNTIME_H_ 6 | #define NODEAPIJSIRUNTIME_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include "ApiLoaders/JSRuntimeApi.h" 12 | 13 | namespace Microsoft::NodeApiJsi { 14 | 15 | std::unique_ptr makeNodeApiJsiRuntime( 16 | napi_env env, 17 | JSRuntimeApi *jsrApi, 18 | std::function onDelete) noexcept; 19 | 20 | struct NodeApiEnvScope { 21 | NodeApiEnvScope(napi_env env) : env_(env) { 22 | JSRuntimeApi::current()->jsr_open_napi_env_scope(env, &scope_); 23 | } 24 | 25 | NodeApiEnvScope(const NodeApiEnvScope &) = delete; 26 | NodeApiEnvScope &operator=(const NodeApiEnvScope &) = delete; 27 | 28 | ~NodeApiEnvScope() { 29 | JSRuntimeApi::current()->jsr_close_napi_env_scope(env_, scope_); 30 | } 31 | 32 | private: 33 | napi_env env_{}; 34 | jsr_napi_env_scope scope_{}; 35 | }; 36 | } // namespace Microsoft::NodeApiJsi 37 | 38 | #endif // !NODEAPIJSIRUNTIME_H_ 39 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. 2 | # Licensed under the MIT License. 3 | 4 | add_executable(jsi_tests 5 | "../jsi/jsi/test/testlib.h" 6 | "../jsi/jsi/test/testlib.cpp" 7 | "../jsi/jsi/test/testlib_ext.cpp" 8 | "../jsi/jsi/decorator.h" 9 | "../jsi/jsi/instrumentation.h" 10 | "../jsi/jsi/jsi-inl.h" 11 | "../jsi/jsi/jsi.cpp" 12 | "../jsi/jsi/jsi.h" 13 | "../jsi/jsi/jsilib-posix.cpp" 14 | "../jsi/jsi/jsilib-windows.cpp" 15 | "../jsi/jsi/jsilib.h" 16 | "../jsi/jsi/threadsafe.h" 17 | "../src/ApiLoaders/HermesApi.cpp" 18 | "../src/ApiLoaders/HermesApi.h" 19 | "../src/ApiLoaders/JSRuntimeApi.cpp" 20 | "../src/ApiLoaders/JSRuntimeApi.h" 21 | "../src/ApiLoaders/NodeApi_win.cpp" 22 | "../src/ApiLoaders/NodeApi.cpp" 23 | "../src/ApiLoaders/NodeApi.h" 24 | "../src/ApiLoaders/V8Api.cpp" 25 | "../src/ApiLoaders/V8Api.h" 26 | "../src/NodeApiJsiRuntime.cpp" 27 | "../src/NodeApiJsiRuntime.h" 28 | "JsiRuntimeTests.cpp" 29 | ) 30 | 31 | target_include_directories(jsi_tests PUBLIC ../jsi ../src ../node-api) 32 | target_link_libraries(jsi_tests PUBLIC gtest_main) 33 | 34 | find_program(NUGET_EXE NAMES nuget) 35 | if(NOT NUGET_EXE) 36 | message("NUGET.EXE not found.") 37 | message(FATAL_ERROR "Please install this executable, and run CMake again.") 38 | endif() 39 | 40 | execute_process( 41 | COMMAND ${NUGET_EXE} 42 | install "Microsoft.JavaScript.Hermes" 43 | -Version 0.1.27 44 | -ExcludeVersion 45 | -OutputDirectory ${CMAKE_BINARY_DIR}/packages 46 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 47 | 48 | target_link_libraries(jsi_tests PRIVATE ${CMAKE_BINARY_DIR}/packages/Microsoft.JavaScript.Hermes/build/native/Microsoft.JavaScript.Hermes.targets) 49 | 50 | execute_process( 51 | COMMAND ${NUGET_EXE} 52 | install "ReactNative.V8Jsi.Windows" 53 | -Version 0.75.4 54 | -ExcludeVersion 55 | -OutputDirectory ${CMAKE_BINARY_DIR}/packages 56 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 57 | 58 | target_link_libraries(jsi_tests PRIVATE ${CMAKE_BINARY_DIR}/packages/ReactNative.V8Jsi.Windows/build/native/ReactNative.V8Jsi.Windows.targets) 59 | 60 | enable_testing() 61 | 62 | add_test( 63 | NAME jsi_tests 64 | COMMAND $ 65 | ) -------------------------------------------------------------------------------- /tests/JsiRuntimeTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace Microsoft::NodeApiJsi; 11 | 12 | namespace facebook::jsi { 13 | 14 | std::vector runtimeGenerators() { 15 | return { 16 | RuntimeFactory([]() { 17 | HermesApi *hermesApi = HermesApi::fromLib(); 18 | HermesApi::setCurrent(hermesApi); 19 | 20 | jsr_config config{}; 21 | jsr_runtime runtime{}; 22 | napi_env env{}; 23 | hermesApi->jsr_create_config(&config); 24 | hermesApi->jsr_config_enable_gc_api(config, true); 25 | hermesApi->jsr_create_runtime(config, &runtime); 26 | hermesApi->jsr_delete_config(config); 27 | hermesApi->jsr_runtime_get_node_api_env(runtime, &env); 28 | 29 | NodeApiEnvScope envScope{env}; 30 | 31 | return makeNodeApiJsiRuntime( 32 | env, hermesApi, [runtime]() { HermesApi::current()->jsr_delete_runtime(runtime); }); 33 | }), 34 | RuntimeFactory([]() { 35 | V8Api *v8Api = V8Api::fromLib(); 36 | V8Api::setCurrent(v8Api); 37 | 38 | jsr_config config{}; 39 | jsr_runtime runtime{}; 40 | napi_env env{}; 41 | v8Api->jsr_create_config(&config); 42 | v8Api->jsr_config_enable_gc_api(config, true); 43 | v8Api->jsr_create_runtime(config, &runtime); 44 | v8Api->jsr_delete_config(config); 45 | v8Api->jsr_runtime_get_node_api_env(runtime, &env); 46 | 47 | NodeApiEnvScope envScope{env}; 48 | 49 | return makeNodeApiJsiRuntime(env, v8Api, [runtime]() { V8Api::current()->jsr_delete_runtime(runtime); }); 50 | })}; 51 | } 52 | 53 | } // namespace facebook::jsi 54 | -------------------------------------------------------------------------------- /tests/NuGet.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------