diff --git a/.gitignore b/.gitignore index 20adb7c8a..59fda8d2c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,8 @@ gradle-app.setting # Package Files # *.jar +*.dll +*.so *.war *.nar *.ear @@ -64,3 +66,13 @@ _build # Node **/node_modules + +# ------------------------------------------------------------------------------ +# Maven + +.classpath +.project +.settings +pom.xml.releaseBackup +release.properties +target diff --git a/LICENSE b/LICENSE index e23ece2c8..d64569567 100644 --- a/LICENSE +++ b/LICENSE @@ -1,277 +1,202 @@ -Eclipse Public License - v 2.0 - - THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE - PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION - OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. - -1. DEFINITIONS - -"Contribution" means: - - a) in the case of the initial Contributor, the initial content - Distributed under this Agreement, and - - b) in the case of each subsequent Contributor: - i) changes to the Program, and - ii) additions to the Program; - where such changes and/or additions to the Program originate from - and are Distributed by that particular Contributor. A Contribution - "originates" from a Contributor if it was added to the Program by - such Contributor itself or anyone acting on such Contributor's behalf. - Contributions do not include changes or additions to the Program that - are not Modified Works. - -"Contributor" means any person or entity that Distributes the Program. - -"Licensed Patents" mean patent claims licensable by a Contributor which -are necessarily infringed by the use or sale of its Contribution alone -or when combined with the Program. - -"Program" means the Contributions Distributed in accordance with this -Agreement. - -"Recipient" means anyone who receives the Program under this Agreement -or any Secondary License (as applicable), including Contributors. - -"Derivative Works" shall mean any work, whether in Source Code or other -form, that is based on (or derived from) the Program and for which the -editorial revisions, annotations, elaborations, or other modifications -represent, as a whole, an original work of authorship. - -"Modified Works" shall mean any work in Source Code or other form that -results from an addition to, deletion from, or modification of the -contents of the Program, including, for purposes of clarity any new file -in Source Code form that contains any contents of the Program. Modified -Works shall not include works that contain only declarations, -interfaces, types, classes, structures, or files of the Program solely -in each case in order to link to, bind by name, or subclass the Program -or Modified Works thereof. - -"Distribute" means the acts of a) distributing or b) making available -in any manner that enables the transfer of a copy. - -"Source Code" means the form of a Program preferred for making -modifications, including but not limited to software source code, -documentation source, and configuration files. - -"Secondary License" means either the GNU General Public License, -Version 2.0, or any later versions of that license, including any -exceptions or additional permissions as identified by the initial -Contributor. - -2. GRANT OF RIGHTS - - a) Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free copyright - license to reproduce, prepare Derivative Works of, publicly display, - publicly perform, Distribute and sublicense the Contribution of such - Contributor, if any, and such Derivative Works. - - b) Subject to the terms of this Agreement, each Contributor hereby - grants Recipient a non-exclusive, worldwide, royalty-free patent - license under Licensed Patents to make, use, sell, offer to sell, - import and otherwise transfer the Contribution of such Contributor, - if any, in Source Code or other form. This patent license shall - apply to the combination of the Contribution and the Program if, at - the time the Contribution is added by the Contributor, such addition - of the Contribution causes such combination to be covered by the - Licensed Patents. The patent license shall not apply to any other - combinations which include the Contribution. No hardware per se is - licensed hereunder. - - c) Recipient understands that although each Contributor grants the - licenses to its Contributions set forth herein, no assurances are - provided by any Contributor that the Program does not infringe the - patent or other intellectual property rights of any other entity. - Each Contributor disclaims any liability to Recipient for claims - brought by any other entity based on infringement of intellectual - property rights or otherwise. As a condition to exercising the - rights and licenses granted hereunder, each Recipient hereby - assumes sole responsibility to secure any other intellectual - property rights needed, if any. For example, if a third party - patent license is required to allow Recipient to Distribute the - Program, it is Recipient's responsibility to acquire that license - before distributing the Program. - - d) Each Contributor represents that to its knowledge it has - sufficient copyright rights in its Contribution, if any, to grant - the copyright license set forth in this Agreement. - - e) Notwithstanding the terms of any Secondary License, no - Contributor makes additional grants to any Recipient (other than - those set forth in this Agreement) as a result of such Recipient's - receipt of the Program under the terms of a Secondary License - (if permitted under the terms of Section 3). - -3. REQUIREMENTS - -3.1 If a Contributor Distributes the Program in any form, then: - - a) the Program must also be made available as Source Code, in - accordance with section 3.2, and the Contributor must accompany - the Program with a statement that the Source Code for the Program - is available under this Agreement, and informs Recipients how to - obtain it in a reasonable manner on or through a medium customarily - used for software exchange; and - - b) the Contributor may Distribute the Program under a license - different than this Agreement, provided that such license: - i) effectively disclaims on behalf of all other Contributors all - warranties and conditions, express and implied, including - warranties or conditions of title and non-infringement, and - implied warranties or conditions of merchantability and fitness - for a particular purpose; - - ii) effectively excludes on behalf of all other Contributors all - liability for damages, including direct, indirect, special, - incidental and consequential damages, such as lost profits; - - iii) does not attempt to limit or alter the recipients' rights - in the Source Code under section 3.2; and - - iv) requires any subsequent distribution of the Program by any - party to be under a license that satisfies the requirements - of this section 3. - -3.2 When the Program is Distributed as Source Code: - - a) it must be made available under this Agreement, or if the - Program (i) is combined with other material in a separate file or - files made available under a Secondary License, and (ii) the initial - Contributor attached to the Source Code the notice described in - Exhibit A of this Agreement, then the Program may be made available - under the terms of such Secondary Licenses, and - - b) a copy of this Agreement must be included with each copy of - the Program. - -3.3 Contributors may not remove or alter any copyright, patent, -trademark, attribution notices, disclaimers of warranty, or limitations -of liability ("notices") contained within the Program from any copy of -the Program which they Distribute, provided that Contributors may add -their own appropriate notices. - -4. COMMERCIAL DISTRIBUTION - -Commercial distributors of software may accept certain responsibilities -with respect to end users, business partners and the like. While this -license is intended to facilitate the commercial use of the Program, -the Contributor who includes the Program in a commercial product -offering should do so in a manner which does not create potential -liability for other Contributors. Therefore, if a Contributor includes -the Program in a commercial product offering, such Contributor -("Commercial Contributor") hereby agrees to defend and indemnify every -other Contributor ("Indemnified Contributor") against any losses, -damages and costs (collectively "Losses") arising from claims, lawsuits -and other legal actions brought by a third party against the Indemnified -Contributor to the extent caused by the acts or omissions of such -Commercial Contributor in connection with its distribution of the Program -in a commercial product offering. The obligations in this section do not -apply to any claims or Losses relating to any actual or alleged -intellectual property infringement. In order to qualify, an Indemnified -Contributor must: a) promptly notify the Commercial Contributor in -writing of such claim, and b) allow the Commercial Contributor to control, -and cooperate with the Commercial Contributor in, the defense and any -related settlement negotiations. The Indemnified Contributor may -participate in any such claim at its own expense. - -For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those performance -claims and warranties, and if a court requires any other Contributor to -pay any damages as a result, the Commercial Contributor must pay -those damages. - -5. NO WARRANTY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT -PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" -BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR -IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF -TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR -PURPOSE. Each Recipient is solely responsible for determining the -appropriateness of using and distributing the Program and assumes all -risks associated with its exercise of rights under this Agreement, -including but not limited to the risks and costs of program errors, -compliance with applicable laws, damage to or loss of data, programs -or equipment, and unavailability or interruption of operations. - -6. DISCLAIMER OF LIABILITY - -EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT -PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS -SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST -PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE -EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - -7. GENERAL - -If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further -action by the parties hereto, such provision shall be reformed to the -minimum extent necessary to make such provision valid and enforceable. - -If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other software -or hardware) infringes such Recipient's patent(s), then such Recipient's -rights granted under Section 2(b) shall terminate as of the date such -litigation is filed. - -All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of -time after becoming aware of such noncompliance. If all Recipient's -rights under this Agreement terminate, Recipient agrees to cease use -and distribution of the Program as soon as reasonably practicable. -However, Recipient's obligations under this Agreement and any licenses -granted by Recipient relating to the Program shall continue and survive. - -Everyone is permitted to copy and distribute copies of this Agreement, -but in order to avoid inconsistency the Agreement is copyrighted and -may only be modified in the following manner. The Agreement Steward -reserves the right to publish new versions (including revisions) of -this Agreement from time to time. No one other than the Agreement -Steward has the right to modify this Agreement. The Eclipse Foundation -is the initial Agreement Steward. The Eclipse Foundation may assign the -responsibility to serve as the Agreement Steward to a suitable separate -entity. Each new version of the Agreement will be given a distinguishing -version number. The Program (including Contributions) may always be -Distributed subject to the version of the Agreement under which it was -received. In addition, after a new version of the Agreement is published, -Contributor may elect to Distribute the Program (including its -Contributions) under the new version. - -Except as expressly stated in Sections 2(a) and 2(b) above, Recipient -receives no rights or licenses to the intellectual property of any -Contributor under this Agreement, whether expressly, by implication, -estoppel or otherwise. All rights in the Program not expressly granted -under this Agreement are reserved. Nothing in this Agreement is intended -to be enforceable by any entity that is not a Contributor or Recipient. -No third-party beneficiary rights are created under this Agreement. - -Exhibit A - Form of Secondary Licenses Notice - -"This Source Code may also be made available under the following -Secondary Licenses when the conditions for such availability set forth -in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), -version(s), and exceptions or additional permissions here}." - - Simply including a copy of this Agreement, including this Exhibit A - is not sufficient to license the Source Code under Secondary Licenses. - - If it is not possible or desirable to put the notice in a particular - file, then You may include the notice in a location (such as a LICENSE - file in a relevant directory) where a recipient would be likely to - look for such a notice. - - You may add additional accurate notices of copyright ownership. \ No newline at end of file + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.rst b/README.rst new file mode 100644 index 000000000..118241cd9 --- /dev/null +++ b/README.rst @@ -0,0 +1,80 @@ +Javet +===== + +Javet is Java + V8 (JAVa + V + EighT). It is yet another way of embedding V8 in Java. It was inspired by J2V8. I'll try to keep up with latest V8 in a slow pace. If you like my work, please **Star** this project. And, you may visit http://caoccao.blogspot.com/. + +Latest version is **0.7.0**. + +Major Features +============== + +* It supports both Windows and Linux with V8 v8.9.255. +* It exposes the majority of V8 API in JVM. +* It allows injecting JS functions into V8. +* It supports ``BigInt <-> Long``, ``Date <-> ZonedDateTime``. +* It supports Javet engine pool like DB connection pool with a huge performance improvement. + +Quick Start +=========== + +Maven +----- + +.. code-block:: xml + + + com.caoccao.javet + javet + 0.7.0 + + +Gradle Kotlin +------------- + +.. code-block:: kotlin + + dependencies { + implementation("com.caoccao.javet:javet:0.7.0") + } + +Gradle Groovy +------------- + +.. code-block:: groovy + + compile group: 'com.caoccao.javet', name: 'javet', version: '0.7.0' + +Hello Javet +=========== + +.. code-block:: java + + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime()) { + v8Runtime.lock(); + System.out.println(v8Runtime.getExecutor("'Hello Javet'").executeString()); + } + +Documents +========= + +* `Build `_ +* `Development `_ +* `Tutorial `_ +* `FAQ `_ +* `TODO List `_ + +Motivation +========== + +I used to take a try of J2V8 and find it's quite compelling. However, J2V8 is slowly dying, with serious memory leak issues, V8 version issue, etc. + +Sometimes starting from scratch implies lower cost than upgrading an existing solution. I think it might be true here in this project. I've learned quite a lot by manually fixing the Windows and Linux build system of J2V8. + +Also, I had got many ideas on how the API will look like. At the end of 2020, I thought I would be able to write a new one from scratch and leave J2V8 behind. Indeed, I made it few months later. + +Please refer to `History with J2V8 `_ for detail. + +License +======= + +Javet follows `APACHE LICENSE, VERSION 2.0 `_. diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 000000000..07b320aae --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,65 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +plugins { + java + `java-library` +} + +repositories { + mavenCentral() + jcenter() +} + +group = "com.caoccao.javet" +version = "0.7.0" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + runtimeOnly(fileTree("libs")) +} + +afterEvaluate { + tasks.withType(JavaCompile::class) { + options.compilerArgs.add("-Xlint:unchecked") + options.compilerArgs.add("-Xlint:deprecation") + } +} + +task("buildJNIHeaders") { + mkdir("$buildDir/generated/tmp/jni") + project.exec { + workingDir("$projectDir") + commandLine( + "javac", + "-h", + "cpp/jni", + "-d", + "$buildDir/generated/tmp/jni", + "src/main/java/com/caoccao/javet/interop/V8Native.java" + ) + } +} + +tasks.test { + useJUnitPlatform() +} diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 000000000..46c81563b --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.10) + +if(!$ENV{JAVA_HOME}) + message(FATAL_ERROR "JAVA_HOME is not found. Please make sure you have JDK 8 or 11 installed properly.") +endif() + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "limited configs" FORCE) + +# CMP0091 must be set before first project(). +# @see: https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html +if (POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) +endif() + +project(Javet) +aux_source_directory("jni" sourceFiles) +add_library(v8_monolith STATIC IMPORTED) +add_library(Javet SHARED ${sourceFiles} "jni/javet.rc") +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(includeDirs + $ENV{JAVA_HOME}/include + ${V8_DIR}/include + ${V8_DIR}/include/libplatform) + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_definitions(-D_ITERATOR_DEBUG_LEVEL=0) + list(APPEND includeDirs $ENV{JAVA_HOME}/include/win32) + set_property(TARGET Javet APPEND_STRING PROPERTY LINK_FLAGS_RELEASE "") + set_property(TARGET Javet PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_target_properties(v8_monolith PROPERTIES IMPORTED_LOCATION ${V8_DIR}/out.gn/x64.release/obj/v8_monolith.lib) + set_target_properties(Javet PROPERTIES OUTPUT_NAME "libjavet-windows-x86_64.v.${JAVET_VERSION}") + target_link_libraries(Javet v8_monolith DbgHelp Winmm) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") + list(APPEND includeDirs $ENV{JAVA_HOME}/include/linux) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m64 ") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m64 ") + set_target_properties(v8_monolith PROPERTIES IMPORTED_LOCATION ${V8_DIR}/out.gn/x64.release/obj/libv8_monolith.a) + set_target_properties(Javet PROPERTIES OUTPUT_NAME "javet-linux-x86_64.v.${JAVET_VERSION}") + target_link_libraries(Javet v8_monolith debug "-lrt" optimized "-lrt" "${libgcc}") +else() + message(FATAL_ERROR "OS that is not either Windows or Linux hasn't been supported yet.") +endif() + +include_directories(${includeDirs}) diff --git a/cpp/build.cmd b/cpp/build.cmd new file mode 100644 index 000000000..29e78cc21 --- /dev/null +++ b/cpp/build.cmd @@ -0,0 +1,17 @@ +@echo off +REM Usage sample: build -DV8_DIR=C:\v8 +SET JAVET_VERSION=0.7.0 +rd /s/q build +mkdir build +cd build +mkdir ..\..\src\main\resources +cmake ..\ -G "Visual Studio 16 2019" -A x64 -DJAVET_VERSION=%JAVET_VERSION% %* ^ + && cmake --build . -- /p:CharacterSet=Unicode /p:Configuration=Release /p:Platform=x64 +IF %ERRORLEVEL% EQU 0 ( +del /q ..\..\src\main\resources\*.dll +copy /y Release\*.dll ..\..\src\main\resources +echo Build Completed +) ELSE ( +echo Build Failed +) +cd ..\ diff --git a/cpp/build.sh b/cpp/build.sh new file mode 100755 index 000000000..56d3fb458 --- /dev/null +++ b/cpp/build.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# Usage sample: build -DV8_DIR=~/v8 +JAVET_VERSION=0.7.0 +rm -rf build +mkdir build +cd build +mkdir -p ../../src/main/resources +cmake ../ -DJAVET_VERSION=${JAVET_VERSION} "$@" \ + && make -j4 \ + && execstack -c libjavet-linux-x86_64.v.${JAVET_VERSION}.so \ + && strip --strip-unneeded -R .note -R .comment libjavet-linux-x86_64.v.${JAVET_VERSION}.so +if [ $? -eq 0 ]; then + rm -f ../../src/main/resources/*.so + cp -f *.so ../../src/main/resources + echo Build Completed +else + echo Build Failed +fi +cd ../ + diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.cpp b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp new file mode 100644 index 000000000..cd5291d0f --- /dev/null +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp @@ -0,0 +1,715 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "com_caoccao_javet_interop_V8Native.h" +#include "javet_callbacks.h" +#include "javet_constants.h" +#include "javet_converter.h" +#include "javet_enums.h" +#include "javet_exceptions.h" +#include "javet_globals.h" +#include "javet_v8_runtime.h" + + /* + * Development Guide: + * 1. Namespace is not recommended in this project. + * 2. Methods are expected to be sorted alphabatically except JNI_OnLoad. + */ + +#define IS_JAVA_INTEGER(jniEnv, obj) jniEnv->IsInstanceOf(obj, Javet::Main::jclassV8ValueInteger) +#define IS_JAVA_STRING(jniEnv, obj) jniEnv->IsInstanceOf(obj, Javet::Main::jclassV8ValueString) +#define TO_JAVA_INTEGER(jniEnv, obj) jniEnv->CallIntMethod(obj, Javet::Main::jmethodIDV8ValueIntegerToPrimitive) +#define TO_JAVA_STRING(jniEnv, obj) (jstring)jniEnv->CallObjectMethod(obj, Javet::Main::jmethodIDV8ValueStringToPrimitive) +#define IS_V8_ARRAY(type) (type == Javet::Enums::V8ValueReferenceType::Array) +#define IS_V8_ARGUMENTS(type) (type == Javet::Enums::V8ValueReferenceType::Arguments) +#define IS_V8_FUNCTION(type) (type == Javet::Enums::V8ValueReferenceType::Function) +#define IS_V8_MAP(type) (type == Javet::Enums::V8ValueReferenceType::Map) +#define IS_V8_OBJECT(type) (type == Javet::Enums::V8ValueReferenceType::Object) +#define IS_V8_SET(type) (type == Javet::Enums::V8ValueReferenceType::Set) + +#define FETCH_JNI_ENV \ + JNIEnv* jniEnv; \ + Javet::GlobalJavaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_8); \ + Javet::GlobalJavaVM->AttachCurrentThread((void**)&jniEnv, nullptr); \ + +#define RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle) \ + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); \ + v8::Isolate::Scope v8IsolateScope(v8Runtime->v8Isolate); \ + v8::HandleScope v8HandleScope(v8Runtime->v8Isolate); \ + auto v8Context = v8::Local::New(v8Runtime->v8Isolate, v8Runtime->v8Context); \ + v8::Context::Scope v8ContextScope(v8Context); \ + +#define RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle) \ + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); \ + auto v8PersistentObjectPointer = reinterpret_cast*>(v8ValueHandle); \ + v8::Isolate::Scope v8IsolateScope(v8Runtime->v8Isolate); \ + v8::HandleScope v8HandleScope(v8Runtime->v8Isolate); \ + auto v8Context = v8::Local::New(v8Runtime->v8Isolate, v8Runtime->v8Context); \ + v8::Context::Scope v8ContextScope(v8Context); \ + auto v8LocalObject = v8PersistentObjectPointer->Get(v8Runtime->v8Isolate); + +#define SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8Value) \ + try { \ + return Javet::Converter::ToExternalV8Value(jniEnv, v8Context, v8Value); \ + } \ + catch (const std::exception& e) { \ + Javet::Exceptions::ThrowJavetConverterException(jniEnv, e.what()); \ + } + +namespace Javet { + namespace Main { + static jclass jclassV8ValueInteger; + static jmethodID jmethodIDV8ValueIntegerToPrimitive; + + static jclass jclassV8ValueString; + static jmethodID jmethodIDV8ValueStringToPrimitive; + + /* + These Java classes and methods need to be initialized within this file + because the memory address probed changes in another file, + or runtime memory corruption will take place. + */ + void Initialize(JNIEnv* jniEnv) { + jclassV8ValueInteger = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueInteger")); + jmethodIDV8ValueIntegerToPrimitive = jniEnv->GetMethodID(jclassV8ValueInteger, "toPrimitive", "()I"); + + jclassV8ValueString = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueString")); + jmethodIDV8ValueStringToPrimitive = jniEnv->GetMethodID(jclassV8ValueString, "toPrimitive", "()Ljava/lang/String;"); + } + + /* + This callback function has to stay within the same file + so that the memory address doesn't get messed up. + */ + void FunctionCallback(const v8::FunctionCallbackInfo& args) { + FETCH_JNI_ENV; + auto v8LocalContextHandle = args.Data().As(); + auto umContext = reinterpret_cast(v8LocalContextHandle->Int64Value()); + Javet::Callback::V8CallbackContextReference v8CallbackContextReference(jniEnv, umContext); + v8CallbackContextReference.Invoke(args); + } + + void GlobalAccessorGetterCallback( + v8::Local propertyName, + const v8::PropertyCallbackInfo& args) { + args.GetReturnValue().Set(args.GetIsolate()->GetCurrentContext()->Global()); + } + + void CloseWeakObjectReference(const v8::WeakCallbackInfo& data) { + FETCH_JNI_ENV; + auto v8ValueReference = data.GetParameter(); + v8ValueReference->Close(jniEnv); + delete v8ValueReference; + } + } +} + +JNIEXPORT jint JNICALL JNI_OnLoad +(JavaVM* javeVM, void*) { + JNIEnv* jniEnv; + if (javeVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_8) != JNI_OK) { + return ERROR_JNI_ON_LOAD; + } + if (jniEnv == nullptr) { + return ERROR_JNI_ON_LOAD; + } + Javet::GlobalJavaVM = javeVM; + v8::V8::InitializeICU(); + Javet::GlobalV8Platform = v8::platform::NewDefaultPlatform(); + v8::V8::InitializePlatform(Javet::GlobalV8Platform.get()); + v8::V8::Initialize(); + Javet::Callback::Initialize(jniEnv); + Javet::Converter::Initialize(jniEnv); + Javet::Exceptions::Initialize(jniEnv); + Javet::Main::Initialize(jniEnv); + return JNI_VERSION_1_8; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_add +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject value) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (IS_V8_SET(v8ValueType)) { + auto v8ValueValue = Javet::Converter::ToV8Value(jniEnv, v8Context, value); + auto unusedSet = v8LocalObject.As()->Add(v8Context, v8ValueValue); + } +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_call +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject mReceiver, jboolean mReturnResult, jobjectArray mValues) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsFunction()) { + v8::TryCatch v8TryCatch(v8Runtime->v8Isolate); + v8::MaybeLocal maybeLocalValueResult; + auto umReceiver = Javet::Converter::ToV8Value(jniEnv, v8Context, mReceiver); + uint32_t valueCount = mValues == nullptr ? 0 : jniEnv->GetArrayLength(mValues); + if (valueCount > 0) { + auto umValuesPointer = Javet::Converter::ToV8Values(jniEnv, v8Context, mValues); + maybeLocalValueResult = v8LocalObject.As()->Call(v8Context, umReceiver, valueCount, umValuesPointer.get()); + } + else { + maybeLocalValueResult = v8LocalObject.As()->Call(v8Context, umReceiver, 0, nullptr); + } + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetExecutionException(jniEnv, v8Context, v8TryCatch); + } + else if (mReturnResult) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, maybeLocalValueResult.ToLocalChecked()); + } + } + return nullptr; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_clearWeak +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject() && !v8PersistentObjectPointer->IsEmpty() && v8PersistentObjectPointer->IsWeak()) { + auto v8ValueReference = v8PersistentObjectPointer->ClearWeak(); + v8ValueReference->Clear(jniEnv); + delete v8ValueReference; + } +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_cloneV8Value +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + auto clonedV8LocalObject = v8::Local::New(v8Context->GetIsolate(), v8LocalObject); + return Javet::Converter::ToExternalV8Value(jniEnv, v8Context, clonedV8LocalObject); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_closeV8Runtime +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + if (v8Runtime->v8Locker != nullptr) { + Javet::Exceptions::ThrowJavetV8RuntimeLockConflictException(jniEnv, "Cannot close V8 runtime because the native lock is not released"); + } + else { + v8Runtime->v8Context.Reset(); + v8Runtime->v8GlobalObject.Reset(); + // Isolate must be the last one to be disposed. + if (v8Runtime->v8Isolate != nullptr) { + v8Runtime->v8Isolate->Dispose(); + v8Runtime->v8Isolate = nullptr; + } + } +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_compileOnly +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jstring mScript, + jstring mResourceName, jint mResourceLineOffset, jint mResourceColumnOffset, jint mScriptId, jboolean mIsWASM, jboolean mIsModule) { + RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle); + v8::TryCatch v8TryCatch(v8Runtime->v8Isolate); + auto umScript = Javet::Converter::ToV8String(jniEnv, v8Context, mScript); + v8::ScriptOrigin* scriptOriginPointer = Javet::Converter::ToV8ScriptOringinPointer( + jniEnv, v8Context, mResourceName, mResourceLineOffset, mResourceColumnOffset, mScriptId, mIsWASM, mIsModule); + auto maybeLocalCompiledScript = v8::Script::Compile(v8Context, umScript, scriptOriginPointer); + if (scriptOriginPointer != nullptr) { + delete scriptOriginPointer; + } + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetCompilationException(jniEnv, v8Context, v8TryCatch); + } +} + +/* +Creating multiple isolates allows running JavaScript code in multiple threads, truly parallel. +*/ +JNIEXPORT jlong JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Runtime +(JNIEnv* jniEnv, jclass callerClass, jstring mGlobalName) { + auto v8Runtime = new Javet::V8Runtime(); + v8Runtime->mException = nullptr; + jlong v8RuntimeHandle = reinterpret_cast(v8Runtime); + Java_com_caoccao_javet_interop_V8Native_resetV8Isolate(jniEnv, callerClass, v8RuntimeHandle, mGlobalName); + return v8RuntimeHandle; +} + +/* +It only supports Object, Array, Function, Map, Set for now. +Error, Promise, RegExp, Proxy, Symbol, etc. are not supported. +*/ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Value +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jint v8ValueType, jobject mContext) { + RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle); + v8::Local v8ValueValue; + if (IS_V8_OBJECT(v8ValueType)) { + v8ValueValue = v8::Object::New(v8Context->GetIsolate()); + } + else if (IS_V8_ARRAY(v8ValueType)) { + v8ValueValue = v8::Array::New(v8Context->GetIsolate()); + } + else if (IS_V8_FUNCTION(v8ValueType)) { + jobject umContext = jniEnv->NewGlobalRef(mContext); + Javet::Callback::V8CallbackContextReference v8CallbackContextReference(jniEnv, umContext); + v8CallbackContextReference.SetHandle(); + auto v8LocalContextHandle = v8::BigInt::New(v8Context->GetIsolate(), reinterpret_cast(umContext)); + v8ValueValue = v8::Function::New(v8Context, Javet::Main::FunctionCallback, v8LocalContextHandle).ToLocalChecked(); + } + else if (IS_V8_MAP(v8ValueType)) { + v8ValueValue = v8::Map::New(v8Context->GetIsolate()); + } + else if (IS_V8_SET(v8ValueType)) { + v8ValueValue = v8::Set::New(v8Context->GetIsolate()); + } + if (!v8ValueValue.IsEmpty()) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8ValueValue); + } + return nullptr; +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_delete +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, key); + if (IS_V8_ARRAY(v8ValueType)) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject.As()->Delete(v8Context, integerKey).FromMaybe(false); + } + else { + return v8LocalObject.As()->Delete(v8Context, v8ValueKey).FromMaybe(false); + } + } + else if (IS_V8_MAP(v8ValueType)) { + return v8LocalObject.As()->Delete(v8Context, v8ValueKey).FromMaybe(false); + } + else if (IS_V8_SET(v8ValueType)) { + return v8LocalObject.As()->Delete(v8Context, v8ValueKey).FromMaybe(false); + } + else if (v8LocalObject->IsObject()) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject->Delete(v8Context, integerKey).FromMaybe(false); + } + else { + return v8LocalObject->Delete(v8Context, v8ValueKey).FromMaybe(false); + } + } + return false; +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_execute +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jstring mScript, jboolean mReturnResult, + jstring mResourceName, jint mResourceLineOffset, jint mResourceColumnOffset, jint mScriptId, jboolean mIsWASM, jboolean mIsModule) { + RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle); + v8::TryCatch v8TryCatch(v8Runtime->v8Isolate); + auto umScript = Javet::Converter::ToV8String(jniEnv, v8Context, mScript); + v8::ScriptOrigin* scriptOriginPointer = Javet::Converter::ToV8ScriptOringinPointer( + jniEnv, v8Context, mResourceName, mResourceLineOffset, mResourceColumnOffset, mScriptId, mIsWASM, mIsModule); + auto maybeLocalCompiledScript = v8::Script::Compile(v8Context, umScript, scriptOriginPointer); + if (scriptOriginPointer != nullptr) { + delete scriptOriginPointer; + } + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetCompilationException(jniEnv, v8Context, v8TryCatch); + } + else if (!maybeLocalCompiledScript.IsEmpty()) { + v8::Local compliedScript = maybeLocalCompiledScript.ToLocalChecked(); + v8::MaybeLocal maybeLocalValueResult = compliedScript->Run(v8Context); + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetExecutionException(jniEnv, v8Context, v8TryCatch); + } + else if (mReturnResult) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, maybeLocalValueResult.ToLocalChecked()); + } + } + return nullptr; +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_get +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, key); + v8::Local v8ValueValue; + if (IS_V8_ARRAY(v8ValueType) || IS_V8_ARGUMENTS(v8ValueType)) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + if (integerKey >= 0) { + v8ValueValue = v8LocalObject.As()->Get(v8Context, integerKey).ToLocalChecked(); + } + } + else if (!v8ValueKey.IsEmpty()) { + v8ValueValue = v8LocalObject.As()->Get(v8Context, v8ValueKey).ToLocalChecked(); + } + } + else if (!v8ValueKey.IsEmpty()) { + if (IS_V8_MAP(v8ValueType)) { + v8ValueValue = v8LocalObject.As()->Get(v8Context, v8ValueKey).ToLocalChecked(); + } + else if (IS_V8_SET(v8ValueType)) { + v8ValueValue = v8LocalObject.As()->Get(v8Context, v8ValueKey).ToLocalChecked(); + } + else if (v8LocalObject->IsObject()) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + v8ValueValue = v8LocalObject->Get(v8Context, integerKey).ToLocalChecked(); + } + else { + v8ValueValue = v8LocalObject->Get(v8Context, v8ValueKey).ToLocalChecked(); + } + } + } + if (!v8ValueValue.IsEmpty()) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8ValueValue); + } + return Javet::Converter::ToExternalV8ValueUndefined(jniEnv); +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getGlobalObject +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle); + return Javet::Converter::ToExternalV8ValueGlobalObject(jniEnv, v8Runtime->v8GlobalObject); +} + +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getLength +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (IS_V8_ARRAY(v8ValueType)) { + return v8LocalObject.As()->Length(); + } + return 0; +} + +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getSize +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (IS_V8_MAP(v8ValueType)) { + return (jint)v8LocalObject.As()->Size(); + } + if (IS_V8_SET(v8ValueType)) { + return (jint)v8LocalObject.As()->Size(); + } + return 0; +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getOwnPropertyNames +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8LocalObject->GetOwnPropertyNames(v8Context).ToLocalChecked()); + return Javet::Converter::ToExternalV8ValueUndefined(jniEnv); +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getPropertyNames +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8LocalObject->GetPropertyNames(v8Context).ToLocalChecked()); + return Javet::Converter::ToExternalV8ValueUndefined(jniEnv); +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getProperty +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject()) { + v8::Local v8ValueValue; + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + v8ValueValue = v8LocalObject->Get(v8Context, integerKey).ToLocalChecked(); + } + else { + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, key); + if (!v8ValueKey.IsEmpty()) { + v8ValueValue = v8LocalObject->Get(v8Context, v8ValueKey).ToLocalChecked(); + } + } + if (!v8ValueValue.IsEmpty()) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8ValueValue); + } + } + return Javet::Converter::ToExternalV8ValueUndefined(jniEnv); +} + +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_getVersion +(JNIEnv* jniEnv, jclass callerClass) { + const char* utfString = v8::V8::GetVersion(); + return jniEnv->NewStringUTF(utfString); +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_has +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject value) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, value); + if (!v8ValueKey.IsEmpty()) { + if (IS_V8_MAP(v8ValueType)) { + return v8LocalObject.As()->Has(v8Context, v8ValueKey).FromMaybe(false); + } + else if (IS_V8_SET(v8ValueType)) { + return v8LocalObject.As()->Has(v8Context, v8ValueKey).FromMaybe(false); + } + else if (v8LocalObject->IsObject()) { + if (IS_JAVA_INTEGER(jniEnv, value)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, value); + return v8LocalObject->Has(v8Context, integerKey).FromMaybe(false); + } + else { + return v8LocalObject->Has(v8Context, v8ValueKey).FromMaybe(false); + } + } + } + return false; +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_hasOwnProperty +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject()) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject->HasOwnProperty(v8Context, integerKey).FromMaybe(false); + } + else if (IS_JAVA_STRING(jniEnv, key)) { + jstring stringKey = TO_JAVA_STRING(jniEnv, key); + auto v8ValueKey = Javet::Converter::ToV8String(jniEnv, v8Context, stringKey); + return v8LocalObject->HasOwnProperty(v8Context, v8ValueKey).FromMaybe(false); + } + } + return false; +} + +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_invoke +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jstring mFunctionName, jboolean mReturnResult, jobjectArray mValues) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject()) { + auto v8Function = v8LocalObject->Get(v8Context, Javet::Converter::ToV8String(jniEnv, v8Context, mFunctionName)).ToLocalChecked(); + if (v8Function->IsFunction()) { + v8::TryCatch v8TryCatch(v8Runtime->v8Isolate); + v8::MaybeLocal maybeLocalValueResult; + uint32_t valueCount = mValues == nullptr ? 0 : jniEnv->GetArrayLength(mValues); + if (valueCount > 0) { + auto umValuesPointer = Javet::Converter::ToV8Values(jniEnv, v8Context, mValues); + maybeLocalValueResult = v8Function.As()->Call(v8Context, v8LocalObject, valueCount, umValuesPointer.get()); + } + else { + maybeLocalValueResult = v8Function.As()->Call(v8Context, v8LocalObject, 0, nullptr); + } + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetExecutionException(jniEnv, v8Context, v8TryCatch); + } + else if (mReturnResult) { + SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, maybeLocalValueResult.ToLocalChecked()); + } + } + } + return nullptr; +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isWeak +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject() && !v8PersistentObjectPointer->IsEmpty()) { + return (jboolean)v8PersistentObjectPointer->IsWeak(); + } + return false; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_lockV8Runtime +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + if (v8Runtime->v8Locker != nullptr) { + Javet::Exceptions::ThrowJavetV8RuntimeLockConflictException(jniEnv, "Cannot acquire V8 native lock because it has not been released yet"); + } + else { + v8Runtime->v8Locker = new v8::Locker(v8Runtime->v8Isolate); + } +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_removeJNIGlobalRef +(JNIEnv* jniEnv, jclass callerClass, jlong handle) { + jniEnv->DeleteGlobalRef((jobject)handle); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_removeReferenceHandle +(JNIEnv* jniEnv, jclass callerClass, jlong referenceHandle) { + auto v8PersistentObjectPointer = reinterpret_cast*>(referenceHandle); + v8PersistentObjectPointer->Reset(); + delete v8PersistentObjectPointer; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_requestGarbageCollectionForTesting +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jboolean fullGC) { + RUNTIME_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle); + v8Runtime->v8Isolate->RequestGarbageCollectionForTesting(((bool)fullGC) + ? v8::Isolate::GarbageCollectionType::kFullGarbageCollection + : v8::Isolate::GarbageCollectionType::kMinorGarbageCollection); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_resetV8Context +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jstring mGlobalName) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + v8::Locker v8Locker(v8Runtime->v8Isolate); + v8::Isolate::Scope v8IsolateScope(v8Runtime->v8Isolate); + // Create a stack-allocated handle scope. + v8::HandleScope v8HandleScope(v8Runtime->v8Isolate); + auto v8IsolateHandle = v8::ObjectTemplate::New(v8Runtime->v8Isolate); + auto v8Context = v8::Context::New(v8Runtime->v8Isolate, nullptr, v8IsolateHandle); + // Redirects global calls to a given global name. E.g. parseInt() -> window.parseInt(). + if (mGlobalName != nullptr) { + auto umGlobalName = Javet::Converter::ToV8String(jniEnv, v8Context, mGlobalName); + v8IsolateHandle->SetAccessor(umGlobalName, Javet::Main::GlobalAccessorGetterCallback); + } + v8Runtime->v8Context.Reset(v8Runtime->v8Isolate, v8Context); + v8Runtime->v8GlobalObject.Reset( + v8Runtime->v8Isolate, + v8Context->Global()->GetPrototype()->ToObject(v8Context).ToLocalChecked()); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_resetV8Isolate +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jstring mGlobalName) { + Java_com_caoccao_javet_interop_V8Native_closeV8Runtime(jniEnv, callerClass, v8RuntimeHandle); + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + v8::Isolate::CreateParams create_params; + create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); + v8Runtime->v8Isolate = v8::Isolate::New(create_params); + v8::Locker v8Locker(v8Runtime->v8Isolate); + v8::Isolate::Scope v8IsolateScope(v8Runtime->v8Isolate); + // Create a stack-allocated handle scope. + v8::HandleScope v8HandleScope(v8Runtime->v8Isolate); + auto v8IsolateHandle = v8::ObjectTemplate::New(v8Runtime->v8Isolate); + auto v8Context = v8::Context::New(v8Runtime->v8Isolate, nullptr, v8IsolateHandle); + // Redirects global calls to a given global name. E.g. parseInt() -> window.parseInt(). + if (mGlobalName != nullptr) { + auto umGlobalName = Javet::Converter::ToV8String(jniEnv, v8Context, mGlobalName); + v8IsolateHandle->SetAccessor(umGlobalName, Javet::Main::GlobalAccessorGetterCallback); + } + v8Runtime->v8Context.Reset(v8Runtime->v8Isolate, v8Context); + v8Runtime->v8GlobalObject.Reset( + v8Runtime->v8Isolate, + v8Context->Global()->GetPrototype()->ToObject(v8Context).ToLocalChecked()); +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_set +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key, jobject value) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, key); + auto v8ValueValue = Javet::Converter::ToV8Value(jniEnv, v8Context, value); + if (IS_V8_ARRAY(v8ValueType)) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject.As()->Set(v8Context, integerKey, v8ValueValue).FromMaybe(false); + } + else if (!v8ValueKey.IsEmpty()) { + return v8LocalObject.As()->Set(v8Context, v8ValueKey, v8ValueValue).FromMaybe(false); + } + } + else if (!v8ValueKey.IsEmpty()) { + if (IS_V8_MAP(v8ValueType)) { + auto unusedSet = v8LocalObject.As()->Set(v8Context, v8ValueKey, v8ValueValue); + return true; + } + else if (v8LocalObject->IsObject()) { + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject->Set(v8Context, integerKey, v8ValueValue).FromMaybe(false); + } + else { + return v8LocalObject->Set(v8Context, v8ValueKey, v8ValueValue).FromMaybe(false); + } + } + } + return false; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setFlags +(JNIEnv* jniEnv, jclass, jstring flags) { + if (flags) { + char const* str = jniEnv->GetStringUTFChars(flags, nullptr); + v8::V8::SetFlagsFromString(str, jniEnv->GetStringUTFLength(flags)); + jniEnv->ReleaseStringUTFChars(flags, str); + v8::V8::Initialize(); + } +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_setProperty +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject key, jobject value) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject()) { + auto v8ValueValue = Javet::Converter::ToV8Value(jniEnv, v8Context, value); + if (IS_JAVA_INTEGER(jniEnv, key)) { + jint integerKey = TO_JAVA_INTEGER(jniEnv, key); + return v8LocalObject->Set(v8Context, integerKey, v8ValueValue).FromMaybe(false); + } + else { + auto v8ValueKey = Javet::Converter::ToV8Value(jniEnv, v8Context, key); + if (!v8ValueKey.IsEmpty()) { + return v8LocalObject->Set(v8Context, v8ValueKey, v8ValueValue).FromMaybe(false); + } + } + } + return false; +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setWeak +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobject objectReference) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->IsObject() && !v8PersistentObjectPointer->IsEmpty() && !v8PersistentObjectPointer->IsWeak()) { + auto v8ValueReference = new Javet::Callback::V8ValueReference; + v8ValueReference->v8Isolate = v8Context->GetIsolate(); + v8ValueReference->objectReference = jniEnv->NewGlobalRef(objectReference); + v8ValueReference->v8PersistentObjectPointer = v8PersistentObjectPointer; + v8PersistentObjectPointer->SetWeak(v8ValueReference, Javet::Main::CloseWeakObjectReference, v8::WeakCallbackType::kParameter); + } +} + +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_toProtoString +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + v8::Local v8String; + if (v8LocalObject->IsObject()) { + v8String = v8LocalObject->ObjectProtoToString(v8Context).ToLocalChecked(); + } + v8::String::Value stringValue(v8Context->GetIsolate(), v8String); + return jniEnv->NewString(*stringValue, stringValue.length()); +} + +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_toString +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + v8::Local v8String; + if (IS_V8_ARRAY(v8ValueType)) { + v8String = v8LocalObject.As()->ToString(v8Context).ToLocalChecked(); + } + else if (IS_V8_MAP(v8ValueType)) { + v8String = v8LocalObject.As()->ToString(v8Context).ToLocalChecked(); + } + else if (IS_V8_SET(v8ValueType)) { + v8String = v8LocalObject.As()->ToString(v8Context).ToLocalChecked(); + } + else if (v8LocalObject->IsObject()) { + v8String = v8LocalObject->ToString(v8Context).ToLocalChecked(); + } + v8::String::Value stringValue(v8Context->GetIsolate(), v8String); + return jniEnv->NewString(*stringValue, stringValue.length()); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_unlockV8Runtime +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + if (v8Runtime->v8Locker == nullptr) { + Javet::Exceptions::ThrowJavetV8RuntimeLockConflictException(jniEnv, "Cannot release V8 native lock because it has not been acquired yet"); + } + else { + delete v8Runtime->v8Locker; + v8Runtime->v8Locker = nullptr; + } +} + diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.h b/cpp/jni/com_caoccao_javet_interop_V8Native.h new file mode 100644 index 000000000..8a79de8e4 --- /dev/null +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.h @@ -0,0 +1,293 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class com_caoccao_javet_interop_V8Native */ + +#ifndef _Included_com_caoccao_javet_interop_V8Native +#define _Included_com_caoccao_javet_interop_V8Native +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: add + * Signature: (JJILjava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_add + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: call + * Signature: (JJILjava/lang/Object;Z[Ljava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_call + (JNIEnv *, jclass, jlong, jlong, jint, jobject, jboolean, jobjectArray); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: clearWeak + * Signature: (JJI)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_clearWeak + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: cloneV8Value + * Signature: (JJI)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_cloneV8Value + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: closeV8Runtime + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_closeV8Runtime + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: compileOnly + * Signature: (JLjava/lang/String;Ljava/lang/String;IIIZZ)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_compileOnly + (JNIEnv *, jclass, jlong, jstring, jstring, jint, jint, jint, jboolean, jboolean); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: createV8Runtime + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Runtime + (JNIEnv *, jclass, jstring); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: createV8Value + * Signature: (JILjava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Value + (JNIEnv *, jclass, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: delete + * Signature: (JJILjava/lang/Object;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_delete + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: execute + * Signature: (JLjava/lang/String;ZLjava/lang/String;IIIZZ)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_execute + (JNIEnv *, jclass, jlong, jstring, jboolean, jstring, jint, jint, jint, jboolean, jboolean); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: get + * Signature: (JJILjava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_get + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getGlobalObject + * Signature: (J)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getGlobalObject + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getLength + * Signature: (JJI)I + */ +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getLength + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getSize + * Signature: (JJI)I + */ +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getSize + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getOwnPropertyNames + * Signature: (JJI)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getOwnPropertyNames + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getPropertyNames + * Signature: (JJI)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getPropertyNames + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getProperty + * Signature: (JJILjava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getProperty + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getVersion + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_getVersion + (JNIEnv *, jclass); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: has + * Signature: (JJILjava/lang/Object;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_has + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: hasOwnProperty + * Signature: (JJILjava/lang/Object;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_hasOwnProperty + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: invoke + * Signature: (JJILjava/lang/String;Z[Ljava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_invoke + (JNIEnv *, jclass, jlong, jlong, jint, jstring, jboolean, jobjectArray); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: isWeak + * Signature: (JJI)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isWeak + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: lockV8Runtime + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_lockV8Runtime + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: removeJNIGlobalRef + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_removeJNIGlobalRef + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: removeReferenceHandle + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_removeReferenceHandle + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: requestGarbageCollectionForTesting + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_requestGarbageCollectionForTesting + (JNIEnv *, jclass, jlong, jboolean); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: resetV8Context + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_resetV8Context + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: resetV8Isolate + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_resetV8Isolate + (JNIEnv *, jclass, jlong, jstring); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: set + * Signature: (JJILjava/lang/Object;Ljava/lang/Object;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_set + (JNIEnv *, jclass, jlong, jlong, jint, jobject, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: setFlags + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setFlags + (JNIEnv *, jclass, jstring); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: setProperty + * Signature: (JJILjava/lang/Object;Ljava/lang/Object;)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_setProperty + (JNIEnv *, jclass, jlong, jlong, jint, jobject, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: setWeak + * Signature: (JJILjava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setWeak + (JNIEnv *, jclass, jlong, jlong, jint, jobject); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: toProtoString + * Signature: (JJI)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_toProtoString + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: toString + * Signature: (JJI)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_caoccao_javet_interop_V8Native_toString + (JNIEnv *, jclass, jlong, jlong, jint); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: unlockV8Runtime + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_unlockV8Runtime + (JNIEnv *, jclass, jlong); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/cpp/jni/javet.rc b/cpp/jni/javet.rc new file mode 100644 index 000000000..527095e95 --- /dev/null +++ b/cpp/jni/javet.rc @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Chinese (Simplified, PRC) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +#pragma code_page(936) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Chinese (Simplified, PRC) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,7,0,0 + PRODUCTVERSION 0,7,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "CompanyName", "caoccao.com" + VALUE "FileDescription", "caoccao.com" + VALUE "FileVersion", "0.7.0.0" + VALUE "InternalName", "javet-windows-x86_64.v.0.7.0.dll" + VALUE "LegalCopyright", "Copyright (C) 2021" + VALUE "OriginalFilename", "javet-windows-x86_64.v.0.7.0.dll" + VALUE "ProductName", "Javet Windows" + VALUE "ProductVersion", "0.7.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/cpp/jni/javet_callbacks.cpp b/cpp/jni/javet_callbacks.cpp new file mode 100644 index 000000000..3f57ae72a --- /dev/null +++ b/cpp/jni/javet_callbacks.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "javet_callbacks.h" +#include "javet_constants.h" +#include "javet_converter.h" +#include "javet_globals.h" + +namespace Javet { + namespace Callback { + void V8ValueReference::Clear(JNIEnv* jniEnv) { + if (v8PersistentObjectPointer != nullptr) { + jniEnv->DeleteGlobalRef(objectReference); + } + } + + void V8ValueReference::Close(JNIEnv* jniEnv) { + if (v8PersistentObjectPointer != nullptr) { + v8PersistentObjectPointer->Reset(); + v8PersistentObjectPointer = nullptr; + jniEnv->CallVoidMethod(reinterpret_cast(objectReference), jmethodIDIV8ValueReferenceClose, true); + jniEnv->DeleteGlobalRef(objectReference); + } + } + + V8CallbackContextReference::V8CallbackContextReference(JNIEnv* jniEnv, jobject callbackContext) { + this->jniEnv = jniEnv; + this->callbackContext = callbackContext; + } + + jobject V8CallbackContextReference::GetCallbackOwnerFunction() { + return jniEnv->CallObjectMethod(callbackContext, jmethodIDV8CallbackContextGetCallbackOwnerFunction); + } + + void V8CallbackContextReference::Invoke(const v8::FunctionCallbackInfo& args) { + v8::Isolate* v8Isolate = args.GetIsolate(); + v8::Isolate::Scope v8IsolateScope(v8Isolate); + v8::HandleScope v8HandleScope(v8Isolate); + auto v8Context = v8Isolate->GetCurrentContext(); + jobject callbackOwnerFunction = GetCallbackOwnerFunction(); + jboolean isReturnResult = IsReturnResult(); + jboolean isThisObjectRequired = IsThisObjectRequired(); + jobject externalArgs = Javet::Converter::ToExternalV8ValueArray(jniEnv, v8Context, args); + jobject thisObject = isThisObjectRequired ? Javet::Converter::ToExternalV8Value(jniEnv, v8Context, args.This()) : nullptr; + jobject mResult = jniEnv->CallObjectMethod(callbackOwnerFunction, jmethodIDIV8ValueFunctionReceiveCallback, thisObject, externalArgs); + if (jniEnv->ExceptionCheck()) { + jthrowable externalException = jniEnv->ExceptionOccurred(); + jstring externalErrorMessage = (jstring)jniEnv->CallObjectMethod(externalException, jmethodIDThrowableGetMessage); + jniEnv->ExceptionClear(); + v8::Local v8ErrorMessage; + if (externalErrorMessage == nullptr) { + v8ErrorMessage = v8::String::NewFromUtf8(v8Isolate, "Uncaught JavaError: unknown").ToLocalChecked(); + } + else { + v8ErrorMessage = Javet::Converter::ToV8String(jniEnv, v8Context, externalErrorMessage); + jniEnv->DeleteLocalRef(externalErrorMessage); + } + v8Isolate->ThrowException(v8::Exception::Error(v8ErrorMessage)); + jniEnv->DeleteLocalRef(externalException); + } + else if (isReturnResult) { + if (mResult == nullptr) { + args.GetReturnValue().SetUndefined(); + } + else { + args.GetReturnValue().Set(Javet::Converter::ToV8Value(jniEnv, v8Context, mResult)); + } + } + if (thisObject != nullptr) { + jniEnv->DeleteLocalRef(thisObject); + } + if (externalArgs != nullptr) { + jniEnv->DeleteLocalRef(externalArgs); + } + if (mResult != nullptr) { + jniEnv->CallStaticVoidMethod(jclassJavetResourceUtils, jmethodIDJavetResourceUtilsSafeClose, mResult); + jniEnv->DeleteLocalRef(mResult); + } + } + + jboolean V8CallbackContextReference::IsReturnResult() { + return jniEnv->CallBooleanMethod(callbackContext, jmethodIDV8CallbackContextIsReturnResult); + } + + jboolean V8CallbackContextReference::IsThisObjectRequired() { + return jniEnv->CallBooleanMethod(callbackContext, jmethodIDV8CallbackContextIsThisObjectRequired); + } + + void V8CallbackContextReference::SetHandle() { + jniEnv->CallVoidMethod(callbackContext, jmethodIDV8CallbackContextSetHandle, reinterpret_cast(callbackContext)); + } + + void Initialize(JNIEnv* jniEnv) { + jclassIV8ValueFunction = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass( + "com/caoccao/javet/values/reference/IV8ValueFunction")); + jmethodIDIV8ValueFunctionReceiveCallback = jniEnv->GetMethodID( + jclassIV8ValueFunction, + "receiveCallback", + "(Lcom/caoccao/javet/values/V8Value;Lcom/caoccao/javet/values/reference/V8ValueArray;)Lcom/caoccao/javet/values/V8Value;"); + + jclassIV8ValueReference = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass( + "com/caoccao/javet/values/reference/IV8ValueReference")); + jmethodIDIV8ValueReferenceClose = jniEnv->GetMethodID(jclassIV8ValueReference, "close", "(Z)V"); + + jclassJavetResourceUtils = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/utils/JavetResourceUtils")); + jmethodIDJavetResourceUtilsSafeClose = jniEnv->GetStaticMethodID(jclassJavetResourceUtils, "safeClose", "(Ljava/lang/Object;)V"); + + jclassThrowable = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("java/lang/Throwable")); + jmethodIDThrowableGetMessage = jniEnv->GetMethodID(jclassThrowable, "getMessage", "()Ljava/lang/String;"); + + jclassV8CallbackContext = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/utils/V8CallbackContext")); + jmethodIDV8CallbackContextGetCallbackOwnerFunction = jniEnv->GetMethodID( + jclassV8CallbackContext, + "getCallbackOwnerFunction", + "()Lcom/caoccao/javet/values/reference/IV8ValueFunction;"); + jmethodIDV8CallbackContextIsReturnResult = jniEnv->GetMethodID(jclassV8CallbackContext, "isReturnResult", "()Z"); + jmethodIDV8CallbackContextIsThisObjectRequired = jniEnv->GetMethodID(jclassV8CallbackContext, "isThisObjectRequired", "()Z"); + jmethodIDV8CallbackContextSetHandle = jniEnv->GetMethodID(jclassV8CallbackContext, "setHandle", "(J)V"); + } + } +} + diff --git a/cpp/jni/javet_callbacks.h b/cpp/jni/javet_callbacks.h new file mode 100644 index 000000000..fddcee692 --- /dev/null +++ b/cpp/jni/javet_callbacks.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include "javet_v8_runtime.h" + +namespace Javet { + namespace Callback { + + class V8ValueReference { + public: + v8::Isolate* v8Isolate; + jobject objectReference; + v8::Persistent* v8PersistentObjectPointer; + void Clear(JNIEnv* jniEnv); + void Close(JNIEnv* jniEnv); + }; + + class V8CallbackContextReference { + public: + jobject callbackContext; + JNIEnv* jniEnv; + V8CallbackContextReference(JNIEnv* jniEnv, jobject callbackContext); + jobject GetCallbackOwnerFunction(); + void Invoke(const v8::FunctionCallbackInfo& args); + jboolean IsReturnResult(); + jboolean IsThisObjectRequired(); + void SetHandle(); + }; + + static jclass jclassIV8ValueFunction; + static jmethodID jmethodIDIV8ValueFunctionReceiveCallback; + + static jclass jclassIV8ValueReference; + static jmethodID jmethodIDIV8ValueReferenceClose; + + static jclass jclassJavetResourceUtils; + static jmethodID jmethodIDJavetResourceUtilsSafeClose; + + static jclass jclassThrowable; + static jmethodID jmethodIDThrowableGetMessage; + + static jclass jclassV8CallbackContext; + static jmethodID jmethodIDV8CallbackContextGetCallbackOwnerFunction; + static jmethodID jmethodIDV8CallbackContextIsReturnResult; + static jmethodID jmethodIDV8CallbackContextIsThisObjectRequired; + static jmethodID jmethodIDV8CallbackContextSetHandle; + + void Initialize(JNIEnv* jniEnv); + } +} diff --git a/cpp/jni/javet_constants.h b/cpp/jni/javet_constants.h new file mode 100644 index 000000000..a8d962a4c --- /dev/null +++ b/cpp/jni/javet_constants.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define ERROR_JNI_ON_LOAD -1; diff --git a/cpp/jni/javet_converter.cpp b/cpp/jni/javet_converter.cpp new file mode 100644 index 000000000..0f2795551 --- /dev/null +++ b/cpp/jni/javet_converter.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "javet_converter.h" + + // Primitive +#define IS_JAVA_BOOLEAN(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueBoolean) +#define IS_JAVA_DOUBLE(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueDouble) +#define IS_JAVA_INTEGER(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueInteger) +#define IS_JAVA_LONG(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueLong) +#define IS_JAVA_NULL(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueNull) +#define IS_JAVA_STRING(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueString) +#define IS_JAVA_UNDEFINED(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueUndefined) +#define IS_JAVA_ZONED_DATE_TIME(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueZonedDateTime) + +// Reference +#define IS_JAVA_ARGUMENTS(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueArguments) +#define IS_JAVA_ARRAY(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueArray) +#define IS_JAVA_FUNCTION(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueFunction) +#define IS_JAVA_ERROR(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueError) +#define IS_JAVA_GLOBAL_OBJECT(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueGlobalObject) +#define IS_JAVA_MAP(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueMap) +#define IS_JAVA_OBJECT(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueObject) +#define IS_JAVA_PROMISE(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValuePromise) +#define IS_JAVA_PROXY(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueProxy) +#define IS_JAVA_REFERENCE(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueReference) +#define IS_JAVA_REG_EXP(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueRegExp) +#define IS_JAVA_SET(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueSet) +#define IS_JAVA_SYMBOL(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueSymbol) + +namespace Javet { + namespace Converter { + void Initialize(JNIEnv* jniEnv) { + /* + @see https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html + @see https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html + */ + + // Primitive + + jclassV8ValueBoolean = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueBoolean")); + jmethodIDV8ValueBooleanConstructor = jniEnv->GetMethodID(jclassV8ValueBoolean, "", "(Z)V"); + jmethodIDV8ValueBooleanToPrimitive = jniEnv->GetMethodID(jclassV8ValueBoolean, "toPrimitive", "()Z"); + + jclassV8ValueDouble = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueDouble")); + jmethodIDV8ValueDoubleConstructor = jniEnv->GetMethodID(jclassV8ValueDouble, "", "(D)V"); + jmethodIDV8ValueDoubleToPrimitive = jniEnv->GetMethodID(jclassV8ValueDouble, "toPrimitive", "()D"); + + jclassV8ValueInteger = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueInteger")); + jmethodIDV8ValueIntegerConstructor = jniEnv->GetMethodID(jclassV8ValueInteger, "", "(I)V"); + jmethodIDV8ValueIntegerToPrimitive = jniEnv->GetMethodID(jclassV8ValueInteger, "toPrimitive", "()I"); + + jclassV8ValueLong = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueLong")); + jmethodIDV8ValueLongConstructorFromLong = jniEnv->GetMethodID(jclassV8ValueLong, "", "(J)V"); + jmethodIDV8ValueLongConstructorFromString = jniEnv->GetMethodID(jclassV8ValueLong, "", "(Ljava/lang/String;)V"); + jmethodIDV8ValueLongToPrimitive = jniEnv->GetMethodID(jclassV8ValueLong, "toPrimitive", "()J"); + + jclassV8ValueNull = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueNull")); + jmethodIDV8ValueNullConstructor = jniEnv->GetMethodID(jclassV8ValueNull, "", "()V"); + + jclassV8ValueString = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueString")); + jmethodIDV8ValueStringConstructor = jniEnv->GetMethodID(jclassV8ValueString, "", "(Ljava/lang/String;)V"); + jmethodIDV8ValueStringToPrimitive = jniEnv->GetMethodID(jclassV8ValueString, "toPrimitive", "()Ljava/lang/String;"); + + jclassV8ValueUndefined = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueUndefined")); + jmethodIDV8ValueUndefinedConstructor = jniEnv->GetMethodID(jclassV8ValueUndefined, "", "()V"); + + jclassV8ValueUnknown = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueUnknown")); + jmethodIDV8ValueUnknownConstructor = jniEnv->GetMethodID(jclassV8ValueUnknown, "", "(Ljava/lang/String;)V"); + + jclassV8ValueZonedDateTime = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/primitive/V8ValueZonedDateTime")); + jmethodIDV8ValueZonedDateTimeConstructor = jniEnv->GetMethodID(jclassV8ValueZonedDateTime, "", "(J)V"); + jmethodIDV8ValueZonedDateTimeToPrimitive = jniEnv->GetMethodID(jclassV8ValueZonedDateTime, "toPrimitive", "()J"); + + // Reference + + jclassV8ValueArguments = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueArguments")); + jmethodIDV8ValueArgumentsConstructor = jniEnv->GetMethodID(jclassV8ValueArguments, "", "(J)V"); + jmethodIDV8ValueArgumentsGetHandle = jniEnv->GetMethodID(jclassV8ValueArguments, "getHandle", "()J"); + + jclassV8ValueArray = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueArray")); + jmethodIDV8ValueArrayConstructor = jniEnv->GetMethodID(jclassV8ValueArray, "", "(J)V"); + jmethodIDV8ValueArrayGetHandle = jniEnv->GetMethodID(jclassV8ValueArray, "getHandle", "()J"); + + jclassV8ValueFunction = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueFunction")); + jmethodIDV8ValueFunctionConstructor = jniEnv->GetMethodID(jclassV8ValueFunction, "", "(J)V"); + jmethodIDV8ValueFunctionGetHandle = jniEnv->GetMethodID(jclassV8ValueFunction, "getHandle", "()J"); + + jclassV8ValueError = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueError")); + jmethodIDV8ValueErrorConstructor = jniEnv->GetMethodID(jclassV8ValueError, "", "(J)V"); + jmethodIDV8ValueErrorGetHandle = jniEnv->GetMethodID(jclassV8ValueError, "getHandle", "()J"); + + jclassV8ValueGlobalObject = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueGlobalObject")); + jmethodIDV8ValueGlobalObjectConstructor = jniEnv->GetMethodID(jclassV8ValueGlobalObject, "", "(J)V"); + jmethodIDV8ValueGlobalObjectGetHandle = jniEnv->GetMethodID(jclassV8ValueGlobalObject, "getHandle", "()J"); + + jclassV8ValueMap = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueMap")); + jmethodIDV8ValueMapConstructor = jniEnv->GetMethodID(jclassV8ValueMap, "", "(J)V"); + jmethodIDV8ValueMapGetHandle = jniEnv->GetMethodID(jclassV8ValueMap, "getHandle", "()J"); + + jclassV8ValueObject = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueObject")); + jmethodIDV8ValueObjectConstructor = jniEnv->GetMethodID(jclassV8ValueObject, "", "(J)V"); + jmethodIDV8ValueObjectGetHandle = jniEnv->GetMethodID(jclassV8ValueObject, "getHandle", "()J"); + + jclassV8ValuePromise = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValuePromise")); + jmethodIDV8ValuePromiseConstructor = jniEnv->GetMethodID(jclassV8ValuePromise, "", "(J)V"); + jmethodIDV8ValuePromiseGetHandle = jniEnv->GetMethodID(jclassV8ValuePromise, "getHandle", "()J"); + + jclassV8ValueProxy = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueProxy")); + jmethodIDV8ValueProxyConstructor = jniEnv->GetMethodID(jclassV8ValueProxy, "", "(J)V"); + jmethodIDV8ValueProxyGetHandle = jniEnv->GetMethodID(jclassV8ValueProxy, "getHandle", "()J"); + + jclassV8ValueReference = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueReference")); + + jclassV8ValueRegExp = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueRegExp")); + jmethodIDV8ValueRegExpConstructor = jniEnv->GetMethodID(jclassV8ValueRegExp, "", "(J)V"); + jmethodIDV8ValueRegExpGetHandle = jniEnv->GetMethodID(jclassV8ValueRegExp, "getHandle", "()J"); + + jclassV8ValueSet = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueSet")); + jmethodIDV8ValueSetConstructor = jniEnv->GetMethodID(jclassV8ValueSet, "", "(J)V"); + jmethodIDV8ValueSetGetHandle = jniEnv->GetMethodID(jclassV8ValueSet, "getHandle", "()J"); + + jclassV8ValueSymbol = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueSymbol")); + jmethodIDV8ValueSymbolConstructor = jniEnv->GetMethodID(jclassV8ValueSymbol, "", "(J)V"); + jmethodIDV8ValueSymbolGetHandle = jniEnv->GetMethodID(jclassV8ValueSymbol, "getHandle", "()J"); + } + + jobject ToExternalV8ValueArray(JNIEnv* jniEnv, v8::Local& v8Context, const v8::FunctionCallbackInfo& args) { + int argLength = args.Length(); + if (argLength > 0) { + auto v8Array = v8::Array::New(v8Context->GetIsolate(), argLength); + for (int i = 0; i < argLength; ++i) { + auto maybeResult = v8Array->Set(v8Context, i, args[i]); + maybeResult.Check(); + } + return ToExternalV8Value(jniEnv, v8Context, v8Array); + } + return nullptr; + } + + jobject ToExternalV8Value(JNIEnv* jniEnv, v8::Local& v8Context, v8::Local v8Value) { + if (v8Value->IsUndefined()) { + return ToExternalV8ValueUndefined(jniEnv); + } + if (v8Value->IsNull()) { + return ToExternalV8ValueNull(jniEnv); + } + // Reference types + // Note: Reference types must be checked before primitive types are checked. + if (v8Value->IsArray()) { + return jniEnv->NewObject(jclassV8ValueArray, jmethodIDV8ValueArrayConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsTypedArray()) { + // TODO + if (v8Value->IsBigInt64Array()) { + } + if (v8Value->IsBigUint64Array()) { + } + if (v8Value->IsFloat32Array()) { + } + if (v8Value->IsFloat64Array()) { + } + if (v8Value->IsInt16Array()) { + } + if (v8Value->IsInt32Array()) { + } + if (v8Value->IsInt8Array()) { + } + if (v8Value->IsUint16Array()) { + } + if (v8Value->IsUint32Array()) { + } + if (v8Value->IsUint8Array()) { + } + if (v8Value->IsUint8ClampedArray()) { + } + } + if (v8Value->IsArrayBuffer()) { + // TODO + } + if (v8Value->IsArrayBufferView()) { + // TODO + } + if (v8Value->IsMap()) { + return jniEnv->NewObject(jclassV8ValueMap, jmethodIDV8ValueMapConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsMapIterator()) { + // It defaults to V8ValueObject. + } + if (v8Value->IsSet()) { + return jniEnv->NewObject(jclassV8ValueSet, jmethodIDV8ValueSetConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsSetIterator()) { + // It defaults to V8ValueObject. + } + if (v8Value->IsWeakMap()) { + // TODO + } + if (v8Value->IsWeakSet()) { + // TODO + } + if (v8Value->IsArgumentsObject()) { + return jniEnv->NewObject(jclassV8ValueArguments, jmethodIDV8ValueArgumentsConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsPromise()) { + return jniEnv->NewObject(jclassV8ValuePromise, jmethodIDV8ValuePromiseConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsRegExp()) { + return jniEnv->NewObject(jclassV8ValueRegExp, jmethodIDV8ValueRegExpConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsGeneratorObject()) { + // TODO + } + if (v8Value->IsAsyncFunction()) { + // TODO + } + if (v8Value->IsGeneratorFunction()) { + // TODO + } + if (v8Value->IsProxy()) { + // Proxy is also a function. So, it needs to be tested before IsFunction(). + return jniEnv->NewObject(jclassV8ValueProxy, jmethodIDV8ValueProxyConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsFunction()) { + return jniEnv->NewObject(jclassV8ValueFunction, jmethodIDV8ValueFunctionConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsNativeError()) { + return jniEnv->NewObject(jclassV8ValueError, jmethodIDV8ValueErrorConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + if (v8Value->IsSymbol() || v8Value->IsSymbolObject()) { + return jniEnv->NewObject(jclassV8ValueSymbol, jmethodIDV8ValueSymbolConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + // Primitive types + if (v8Value->IsBoolean() || v8Value->IsBooleanObject()) { + return jniEnv->NewObject(jclassV8ValueBoolean, jmethodIDV8ValueBooleanConstructor, v8Value->IsTrue()); + } + if (v8Value->IsInt32()) { + return jniEnv->NewObject(jclassV8ValueInteger, jmethodIDV8ValueIntegerConstructor, v8Value->Int32Value(v8Context).FromMaybe(0)); + } + if (v8Value->IsBigInt() || v8Value->IsBigIntObject()) { +#ifdef JAVET_CONVERTER_BIGINT_STANDARD + // This is the standard way of getting int64. + return jniEnv->NewObject(jclassV8ValueLong, jmethodIDV8ValueLongConstructorFromLong, + v8Value->ToBigInt(v8Context).ToLocalChecked()->Int64Value()); +#else + // There is another way of getting int64. This branch is disabled by default. + v8::String::Value stringValue(v8Context->GetIsolate(), v8Value); + jstring mStringValue = jniEnv->NewString(*stringValue, stringValue.length()); + jobject mV8Value = jniEnv->NewObject(jclassV8ValueLong, jmethodIDV8ValueLongConstructorFromString, mStringValue); + jniEnv->DeleteLocalRef(mStringValue); + return mV8Value; +#endif // JAVET_CONVERTER_BIGINT_STANDARD + } + if (v8Value->IsDate()) { + auto v8Date = v8Value->ToObject(v8Context).ToLocalChecked().As(); + return jniEnv->NewObject(jclassV8ValueZonedDateTime, jmethodIDV8ValueZonedDateTimeConstructor, static_cast(v8Date->ValueOf())); + } + if (v8Value->IsNumber() || v8Value->IsNumberObject()) { + return jniEnv->NewObject(jclassV8ValueDouble, jmethodIDV8ValueDoubleConstructor, v8Value->NumberValue(v8Context).FromMaybe(0)); + } + if (v8Value->IsString() || v8Value->IsStringObject()) { + return ToExternalV8ValuePrimitive(jniEnv, jclassV8ValueString, jmethodIDV8ValueStringConstructor, v8Context, v8Value); + } + if (v8Value->IsName()) { + // It defaults to V8ValueObject. + } + // Object needs to be the last one. + if (v8Value->IsObject()) { + return jniEnv->NewObject(jclassV8ValueObject, jmethodIDV8ValueObjectConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); + } + // Something is wrong. It defaults to toString(). + return ToExternalV8ValuePrimitive(jniEnv, jclassV8ValueUnknown, jmethodIDV8ValueUnknownConstructor, v8Context, v8Value); + } + + inline jobject ToExternalV8ValueNull(JNIEnv* jniEnv) { + return jniEnv->NewObject(jclassV8ValueNull, jmethodIDV8ValueNullConstructor); + } + + jobject ToExternalV8ValueGlobalObject(JNIEnv* jniEnv, v8::Persistent& v8PersistentObject) { + return jniEnv->NewObject(jclassV8ValueGlobalObject, jmethodIDV8ValueGlobalObjectConstructor, reinterpret_cast(&v8PersistentObject)); + } + + inline jobject ToExternalV8ValuePrimitive( + JNIEnv* jniEnv, jclass jclassV8ValuePrimitive, jmethodID jmethodIDV8ValuePrimitiveConstructor, + v8::Local& v8Context, v8::Local v8Value) { + v8::String::Value stringValue(v8Context->GetIsolate(), v8Value->ToString(v8Context).ToLocalChecked()); + jstring mStringValue = jniEnv->NewString(*stringValue, stringValue.length()); + jobject mV8ValuePrimitive = jniEnv->NewObject(jclassV8ValuePrimitive, jmethodIDV8ValuePrimitiveConstructor, mStringValue); + jniEnv->DeleteLocalRef(mStringValue); + return mV8ValuePrimitive; + } + + jobject ToExternalV8ValueUndefined(JNIEnv* jniEnv) { + return jniEnv->NewObject(jclassV8ValueUndefined, jmethodIDV8ValueUndefinedConstructor); + } + + inline v8::Local ToV8Boolean(v8::Local& v8Context, jboolean& managedBoolean) { + return v8::Boolean::New(v8Context->GetIsolate(), managedBoolean); + } + + inline v8::Local ToV8Date(v8::Local& v8Context, jlong& managedLong) { + return v8::Date::New(v8Context, (double)managedLong).ToLocalChecked(); + } + + inline v8::Local ToV8Double(v8::Local& v8Context, jdouble& managedDouble) { + return v8::Number::New(v8Context->GetIsolate(), managedDouble); + } + + inline v8::Local ToV8Integer(v8::Local& v8Context, jint& managedInteger) { + return v8::Integer::New(v8Context->GetIsolate(), managedInteger); + } + + inline v8::Local ToV8Long(v8::Local& v8Context, jlong& managedLong) { + return v8::BigInt::New(v8Context->GetIsolate(), managedLong); + } + + inline v8::Local ToV8Null(v8::Local& v8Context) { + return v8::Null(v8Context->GetIsolate()); + } + + inline v8::Local ToV8Undefined(v8::Local& v8Context) { + return v8::Undefined(v8Context->GetIsolate()); + } + + inline jlong ToV8PersistentObjectReference(v8::Local& v8Context, v8::Local v8Value) { + v8::Persistent* v8PersistentObjectPointer = new v8::Persistent( + v8Context->GetIsolate(), + v8Value->ToObject(v8Context).ToLocalChecked()); + return reinterpret_cast(v8PersistentObjectPointer); + } + + v8::ScriptOrigin* ToV8ScriptOringinPointer(JNIEnv* jniEnv, v8::Local& v8Context, + jstring& mResourceName, jint& mResourceLineOffset, jint& mResourceColumnOffset, jint& mScriptId, jboolean& mIsWASM, jboolean& mIsModule) { + if (mResourceName == nullptr) { + return nullptr; + } + return new v8::ScriptOrigin( + ToV8String(jniEnv, v8Context, mResourceName), + ToV8Integer(v8Context, mResourceLineOffset), + ToV8Integer(v8Context, mResourceColumnOffset), + v8::Local(), + ToV8Integer(v8Context, mScriptId), + v8::Local(), + v8::Local(), + ToV8Boolean(v8Context, mIsWASM), + ToV8Boolean(v8Context, mIsModule), + v8::Local()); + } + + v8::Local ToV8String(JNIEnv* jniEnv, v8::Local& v8Context, jstring& managedString) { + const uint16_t* unmanagedString = jniEnv->GetStringChars(managedString, nullptr); + int length = jniEnv->GetStringLength(managedString); + auto twoByteString = v8::String::NewFromTwoByte( + v8Context->GetIsolate(), unmanagedString, v8::NewStringType::kNormal, length); + if (twoByteString.IsEmpty()) { + return v8::Local(); + } + auto localV8String = twoByteString.ToLocalChecked(); + jniEnv->ReleaseStringChars(managedString, unmanagedString); + return localV8String; + } + + v8::Local ToV8Value(JNIEnv* jniEnv, v8::Local& v8Context, jobject& obj) { + if (obj == nullptr || IS_JAVA_NULL(jniEnv, obj)) { + return ToV8Null(v8Context); + } + else if (IS_JAVA_INTEGER(jniEnv, obj)) { + jint integerObject = jniEnv->CallIntMethod(obj, jmethodIDV8ValueIntegerToPrimitive); + return ToV8Integer(v8Context, integerObject); + } + else if (IS_JAVA_STRING(jniEnv, obj)) { + jstring stringObject = (jstring)jniEnv->CallObjectMethod(obj, jmethodIDV8ValueStringToPrimitive); + return ToV8String(jniEnv, v8Context, stringObject); + } + else if (IS_JAVA_BOOLEAN(jniEnv, obj)) { + jboolean booleanObject = jniEnv->CallBooleanMethod(obj, jmethodIDV8ValueBooleanToPrimitive); + return ToV8Boolean(v8Context, booleanObject); + } + else if (IS_JAVA_DOUBLE(jniEnv, obj)) { + jdouble doubleObject = jniEnv->CallDoubleMethod(obj, jmethodIDV8ValueDoubleToPrimitive); + return ToV8Double(v8Context, doubleObject); + } + else if (IS_JAVA_LONG(jniEnv, obj)) { + jlong longObject = jniEnv->CallLongMethod(obj, jmethodIDV8ValueLongToPrimitive); + return ToV8Long(v8Context, longObject); + } + else if (IS_JAVA_ZONED_DATE_TIME(jniEnv, obj)) { + jlong longObject = (jlong)jniEnv->CallObjectMethod(obj, jmethodIDV8ValueZonedDateTimeToPrimitive); + return ToV8Date(v8Context, longObject); + } + else if (IS_JAVA_REFERENCE(jniEnv, obj)) { + if (IS_JAVA_ARGUMENTS(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueArgumentsGetHandle))); + } + else if (IS_JAVA_ARRAY(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueArrayGetHandle))); + } + else if (IS_JAVA_ERROR(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueErrorGetHandle))); + } + else if (IS_JAVA_GLOBAL_OBJECT(jniEnv, obj)) { + // Global object is a tricky one. + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueGlobalObjectGetHandle))); + } + else if (IS_JAVA_MAP(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueMapGetHandle))); + } + else if (IS_JAVA_OBJECT(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueObjectGetHandle))); + } + else if (IS_JAVA_PROMISE(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValuePromiseGetHandle))); + } + else if (IS_JAVA_PROXY(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueProxyGetHandle))); + } + else if (IS_JAVA_REG_EXP(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueRegExpGetHandle))); + } + else if (IS_JAVA_SET(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueSetGetHandle))); + } + else if (IS_JAVA_SYMBOL(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueSymbolGetHandle))); + } + } + return ToV8Undefined(v8Context); + } + + std::unique_ptr[]> ToV8Values(JNIEnv* jniEnv, v8::Local& v8Context, jobjectArray& mValues) { + std::unique_ptr[]> umValuesPointer; + uint32_t valueCount = mValues == nullptr ? 0 : jniEnv->GetArrayLength(mValues); + if (valueCount > 0) { + umValuesPointer.reset(new v8::Local[valueCount]); + for (uint32_t i = 0; i < valueCount; ++i) { + jobject obj = jniEnv->GetObjectArrayElement(mValues, i); + umValuesPointer.get()[i] = ToV8Value(jniEnv, v8Context, obj); + } + } + return umValuesPointer; + } + } +} diff --git a/cpp/jni/javet_converter.h b/cpp/jni/javet_converter.h new file mode 100644 index 000000000..9bbe848b9 --- /dev/null +++ b/cpp/jni/javet_converter.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#pragma once + +#include +#include + +#define JAVET_CONVERTER_BIGINT_STANDARD + +namespace Javet { + namespace Converter { + // Primitive + + static jclass jclassV8ValueBoolean; + static jmethodID jmethodIDV8ValueBooleanConstructor; + static jmethodID jmethodIDV8ValueBooleanToPrimitive; + + static jclass jclassV8ValueDouble; + static jmethodID jmethodIDV8ValueDoubleConstructor; + static jmethodID jmethodIDV8ValueDoubleToPrimitive; + + static jclass jclassV8ValueInteger; + static jmethodID jmethodIDV8ValueIntegerConstructor; + static jmethodID jmethodIDV8ValueIntegerToPrimitive; + + static jclass jclassV8ValueLong; + static jmethodID jmethodIDV8ValueLongConstructorFromLong; + static jmethodID jmethodIDV8ValueLongConstructorFromString; + static jmethodID jmethodIDV8ValueLongToPrimitive; + + static jclass jclassV8ValueNull; + static jmethodID jmethodIDV8ValueNullConstructor; + + static jclass jclassV8ValueString; + static jmethodID jmethodIDV8ValueStringConstructor; + static jmethodID jmethodIDV8ValueStringToPrimitive; + + static jclass jclassV8ValueUndefined; + static jmethodID jmethodIDV8ValueUndefinedConstructor; + + static jclass jclassV8ValueUnknown; + static jmethodID jmethodIDV8ValueUnknownConstructor; + + static jclass jclassV8ValueZonedDateTime; + static jmethodID jmethodIDV8ValueZonedDateTimeConstructor; + static jmethodID jmethodIDV8ValueZonedDateTimeToPrimitive; + + // Reference + + static jclass jclassV8ValueArguments; + static jmethodID jmethodIDV8ValueArgumentsConstructor; + static jmethodID jmethodIDV8ValueArgumentsGetHandle; + + static jclass jclassV8ValueArray; + static jmethodID jmethodIDV8ValueArrayConstructor; + static jmethodID jmethodIDV8ValueArrayGetHandle; + + static jclass jclassV8ValueFunction; + static jmethodID jmethodIDV8ValueFunctionConstructor; + static jmethodID jmethodIDV8ValueFunctionGetHandle; + + static jclass jclassV8ValueError; + static jmethodID jmethodIDV8ValueErrorConstructor; + static jmethodID jmethodIDV8ValueErrorGetHandle; + + static jclass jclassV8ValueGlobalObject; + static jmethodID jmethodIDV8ValueGlobalObjectConstructor; + static jmethodID jmethodIDV8ValueGlobalObjectGetHandle; + + static jclass jclassV8ValueMap; + static jmethodID jmethodIDV8ValueMapConstructor; + static jmethodID jmethodIDV8ValueMapGetHandle; + + static jclass jclassV8ValueObject; + static jmethodID jmethodIDV8ValueObjectConstructor; + static jmethodID jmethodIDV8ValueObjectGetHandle; + + static jclass jclassV8ValuePromise; + static jmethodID jmethodIDV8ValuePromiseConstructor; + static jmethodID jmethodIDV8ValuePromiseGetHandle; + + static jclass jclassV8ValueProxy; + static jmethodID jmethodIDV8ValueProxyConstructor; + static jmethodID jmethodIDV8ValueProxyGetHandle; + + static jclass jclassV8ValueReference; + + static jclass jclassV8ValueRegExp; + static jmethodID jmethodIDV8ValueRegExpConstructor; + static jmethodID jmethodIDV8ValueRegExpGetHandle; + + static jclass jclassV8ValueSet; + static jmethodID jmethodIDV8ValueSetConstructor; + static jmethodID jmethodIDV8ValueSetGetHandle; + + static jclass jclassV8ValueSymbol; + static jmethodID jmethodIDV8ValueSymbolConstructor; + static jmethodID jmethodIDV8ValueSymbolGetHandle; + + void Initialize(JNIEnv* jniEnv); + + jobject ToExternalV8Value(JNIEnv* jniEnv, v8::Local& v8Context, v8::Local v8Value); + + jobject ToExternalV8ValueArray(JNIEnv* jniEnv, v8::Local& v8Context, const v8::FunctionCallbackInfo& args); + + inline jobject ToExternalV8ValueNull(JNIEnv* jniEnv); + + jobject ToExternalV8ValueGlobalObject(JNIEnv* jniEnv, v8::Persistent& v8PersistentObject); + + inline jobject ToExternalV8ValuePrimitive( + JNIEnv* jniEnv, jclass jclassV8ValuePrimitive, jmethodID jmethodIDV8ValuePrimitiveConstructor, + v8::Local& v8Context, v8::Local v8Value); + + jobject ToExternalV8ValueUndefined(JNIEnv* jniEnv); + + inline v8::Local ToV8Boolean(v8::Local& v8Context, jboolean& managedBoolean); + + inline v8::Local ToV8Date(v8::Local& v8Context, jlong& managedLong); + + inline v8::Local ToV8Double(v8::Local& v8Context, jdouble& managedDouble); + + inline v8::Local ToV8Integer(v8::Local& v8Context, jint& managedInteger); + + inline v8::Local ToV8Long(v8::Local& v8Context, jlong& managedLong); + + inline v8::Local ToV8Null(v8::Local& v8Context); + + inline v8::Local ToV8Undefined(v8::Local& v8Context); + + inline jlong ToV8PersistentObjectReference(v8::Local& v8Context, v8::Local v8Value); + + v8::ScriptOrigin* ToV8ScriptOringinPointer(JNIEnv* jniEnv, v8::Local& v8Context, + jstring& mResourceName, jint& mResourceLineOffset, jint& mResourceColumnOffset, jint& mScriptId, jboolean& mIsWASM, jboolean& mIsModule); + + v8::Local ToV8String(JNIEnv* jniEnv, v8::Local& v8Context, jstring& managedString); + + v8::Local ToV8Value(JNIEnv* jniEnv, v8::Local& v8Context, jobject& obj); + + std::unique_ptr[]> ToV8Values(JNIEnv* jniEnv, v8::Local& v8Context, jobjectArray& mValues); + } +} diff --git a/cpp/jni/javet_enums.h b/cpp/jni/javet_enums.h new file mode 100644 index 000000000..001853c44 --- /dev/null +++ b/cpp/jni/javet_enums.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace Javet { + namespace Enums { + enum V8ValueReferenceType { + Object = 1, + Error = 2, + RegExp = 3, + Promise = 4, + Proxy = 5, + Symbol = 6, + Arguments = 7, + Map = 8, + Set = 9, + Array = 10, + Function = 11, + }; + } +} \ No newline at end of file diff --git a/cpp/jni/javet_exceptions.cpp b/cpp/jni/javet_exceptions.cpp new file mode 100644 index 000000000..70b64947f --- /dev/null +++ b/cpp/jni/javet_exceptions.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "javet_converter.h" +#include "javet_exceptions.h" + +namespace Javet { + namespace Exceptions { + void Initialize(JNIEnv* jniEnv) { + /* + @see https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html + @see https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html + */ + + jclassJavetCompilationException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetCompilationException")); + jmethodIDJavetCompilationExceptionConstructor = jniEnv->GetMethodID(jclassJavetCompilationException, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIII)V"); + jclassJavetConverterException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetConverterException")); + jclassJavetExecutionException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetExecutionException")); + jmethodIDJavetExecutionExceptionConstructor = jniEnv->GetMethodID(jclassJavetExecutionException, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIII)V"); + jclassJavetUnknownCompilationException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetUnknownCompilationException")); + jmethodIDJavetUnknownCompilationExceptionConstructor = jniEnv->GetMethodID(jclassJavetUnknownCompilationException, "", "(Ljava/lang/String;)V"); + jclassJavetUnknownExecutionException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetUnknownExecutionException")); + jmethodIDJavetUnknownExecutionExceptionConstructor = jniEnv->GetMethodID(jclassJavetUnknownExecutionException, "", "(Ljava/lang/String;)V"); + jclassJavetV8RuntimeLockConflictException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException")); + } + + void ThrowJavetCompilationException(JNIEnv* jniEnv, const v8::Local& v8Context, const v8::TryCatch& v8TryCatch) { + auto isolate = v8Context->GetIsolate(); + v8::String::Value exceptionMessage(isolate, v8TryCatch.Exception()); + jstring jStringExceptionMessage = jniEnv->NewString(*exceptionMessage, exceptionMessage.length()); + auto compileErrorMessage = v8TryCatch.Message(); + if (compileErrorMessage.IsEmpty()) { + jthrowable javetUnknownCompilationException = (jthrowable)jniEnv->NewObject( + jclassJavetUnknownCompilationException, + jmethodIDJavetUnknownCompilationExceptionConstructor, + jStringExceptionMessage); + jniEnv->Throw(javetUnknownCompilationException); + } + else { + v8::String::Utf8Value scriptResourceName(isolate, compileErrorMessage->GetScriptResourceName()); + jstring jStringScriptResourceName = jniEnv->NewStringUTF(*scriptResourceName); + v8::String::Value sourceLine(isolate, compileErrorMessage->GetSourceLine(v8Context).ToLocalChecked()); + jstring jStringSourceLine = jniEnv->NewString(*sourceLine, sourceLine.length()); + jthrowable javetConverterException = (jthrowable)jniEnv->NewObject( + jclassJavetCompilationException, + jmethodIDJavetCompilationExceptionConstructor, + jStringExceptionMessage, + jStringScriptResourceName, + jStringSourceLine, + compileErrorMessage->GetLineNumber(v8Context).FromMaybe(0), + compileErrorMessage->GetStartColumn(), + compileErrorMessage->GetEndColumn(), + compileErrorMessage->GetStartPosition(), + compileErrorMessage->GetEndPosition() ); + jniEnv->Throw(javetConverterException); + jniEnv->DeleteLocalRef(jStringSourceLine); + jniEnv->DeleteLocalRef(jStringScriptResourceName); + } + jniEnv->DeleteLocalRef(jStringExceptionMessage); + } + + void ThrowJavetConverterException(JNIEnv* jniEnv, const char* message) { + jniEnv->ThrowNew(jclassJavetConverterException, message); + } + + void ThrowJavetExecutionException(JNIEnv* jniEnv, const v8::Local& v8Context, const v8::TryCatch& v8TryCatch) { + auto isolate = v8Context->GetIsolate(); + v8::String::Value exceptionMessage(isolate, v8TryCatch.Exception()); + jstring jStringExceptionMessage = jniEnv->NewString(*exceptionMessage, exceptionMessage.length()); + auto compileErrorMessage = v8TryCatch.Message(); + if (compileErrorMessage.IsEmpty()) { + jthrowable javetUnknownExecutionException = (jthrowable)jniEnv->NewObject( + jclassJavetUnknownExecutionException, + jmethodIDJavetUnknownExecutionExceptionConstructor, + jStringExceptionMessage); + jniEnv->Throw(javetUnknownExecutionException); + } + else { + v8::String::Utf8Value scriptResourceName(isolate, compileErrorMessage->GetScriptResourceName()); + jstring jStringScriptResourceName = jniEnv->NewStringUTF(*scriptResourceName); + v8::String::Value sourceLine(isolate, compileErrorMessage->GetSourceLine(v8Context).ToLocalChecked()); + jstring jStringSourceLine = jniEnv->NewString(*sourceLine, sourceLine.length()); + jthrowable javetConverterException = (jthrowable)jniEnv->NewObject( + jclassJavetExecutionException, + jmethodIDJavetExecutionExceptionConstructor, + jStringExceptionMessage, + jStringScriptResourceName, + jStringSourceLine, + compileErrorMessage->GetLineNumber(v8Context).FromMaybe(0), + compileErrorMessage->GetStartColumn(), + compileErrorMessage->GetEndColumn(), + compileErrorMessage->GetStartPosition(), + compileErrorMessage->GetEndPosition() ); + jniEnv->Throw(javetConverterException); + jniEnv->DeleteLocalRef(jStringSourceLine); + jniEnv->DeleteLocalRef(jStringScriptResourceName); + } + jniEnv->DeleteLocalRef(jStringExceptionMessage); + } + + void ThrowJavetV8RuntimeLockConflictException(JNIEnv* jniEnv, const char* message) { + jniEnv->ThrowNew(jclassJavetV8RuntimeLockConflictException, message); + } + } +} diff --git a/cpp/jni/javet_exceptions.h b/cpp/jni/javet_exceptions.h new file mode 100644 index 000000000..f1cab06cd --- /dev/null +++ b/cpp/jni/javet_exceptions.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace Javet { + namespace Exceptions { + static jclass jclassJavetCompilationException; + static jmethodID jmethodIDJavetCompilationExceptionConstructor; + static jclass jclassJavetConverterException; + static jclass jclassJavetExecutionException; + static jmethodID jmethodIDJavetExecutionExceptionConstructor; + static jclass jclassJavetUnknownCompilationException; + static jmethodID jmethodIDJavetUnknownCompilationExceptionConstructor; + static jclass jclassJavetUnknownExecutionException; + static jmethodID jmethodIDJavetUnknownExecutionExceptionConstructor; + static jclass jclassJavetV8RuntimeLockConflictException; + + void Initialize(JNIEnv* jniEnv); + + void ThrowJavetCompilationException(JNIEnv* jniEnv, const v8::Local& v8Context, const v8::TryCatch& v8TryCatch); + void ThrowJavetConverterException(JNIEnv* jniEnv, const char* message); + void ThrowJavetExecutionException(JNIEnv* jniEnv, const v8::Local& v8Context, const v8::TryCatch& v8TryCatch); + void ThrowJavetV8RuntimeLockConflictException(JNIEnv* jniEnv, const char* message); + } +} diff --git a/cpp/jni/javet_globals.h b/cpp/jni/javet_globals.h new file mode 100644 index 000000000..8b0864ffb --- /dev/null +++ b/cpp/jni/javet_globals.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +namespace Javet { + static std::unique_ptr GlobalV8Platform = nullptr; + static JavaVM* GlobalJavaVM = nullptr; +} + diff --git a/cpp/jni/javet_v8_runtime.h b/cpp/jni/javet_v8_runtime.h new file mode 100644 index 000000000..e08dc5193 --- /dev/null +++ b/cpp/jni/javet_v8_runtime.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace Javet { + + class V8Runtime { + public: + v8::Isolate* v8Isolate; + v8::Persistent v8Context; + v8::Persistent v8GlobalObject; + v8::Locker* v8Locker; + jthrowable mException; + v8_inspector::V8Inspector* v8Inspector; + }; + +} + diff --git a/cpp/jni/resource.h b/cpp/jni/resource.h new file mode 100644 index 000000000..cf6e7f457 --- /dev/null +++ b/cpp/jni/resource.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by javet.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/docs/build.rst b/docs/build.rst new file mode 100644 index 000000000..bd3e3f92f --- /dev/null +++ b/docs/build.rst @@ -0,0 +1,92 @@ +===== +Build +===== + +Build Environment +================= + +Linux Environment +----------------- + +* CMake 3.10+ +* Ubuntu 18.04 +* JDK 8 + +Windows Environment +------------------- + +* Latest Windows 10 +* Visual Studio 2019 Community +* CMake 3.16+ (comes with Visual Studio) +* Latest Windows 10 SDK with WinDbg +* JDK 8 + +Download Pre-built V8 +===================== + +I have prepared pre-built Linux and Windows version of V8 v8.9.255. Please download the headers and binary from this `drive `_ and unzip them locally. + +Build Javet JNI Library +======================= + +Once V8 is ready, please navigate to ``./cpp``, make sure CMake is accessible and execute corresponding build script. + +* Linux - ``sh build.sh -DV8_DIR=/where_the_v8_directory_is``. +* Windows - ``build.cmd -DV8_DIR=\where_the_v8_directory_is``. + +Note: The V8 directory needs to be absolute path. + +After a while, ``libjavet-linux-x86_64.v.*.*.*.so`` or ``libjavet-windows-x86_64.v.*.*.*.dll`` will be placed in folder ``src/main/resources``. + +Build Javet Jar +=============== + +Once both ``libjavet-linux-x86_64.v.*.*.*.so`` and ``libjavet-windows-x86_64.v.*.*.*.dll`` are built, please put them altogether under ``src/main/resources`` then kick off ``gradle build test``. + +After a while, ``javet-*.*.*.jar`` will be placed in folder ``build/libs``. + +Note: This jar file supports both Linux and Windows. + +Upload Javet to Maven Central (Optional) +---------------------------------------- + +Package Jar files in Maven. + +.. code-block:: sh + + # mvn package + mvn clean + mvn release:prepare + mvn release:perform + +Jar files are built under ``./target``. + +Build V8 (Optional) +=================== + +Please follow the `official guide `_ to build V8 ``8.9.255. If you face any issues, you may contact `@sjtucaocao `_. + +Some Tips on Building V8 +------------------------ + +* Linux requires Python 2.7, CMake 3.10+. Ubuntu 18.04 is the recommended Linux distribution. +* Windows requires Windows 10, Python 2.7, Visual Studio 2019 Community, CMake (comes with Visual Studio), Windows 10 SDK with WinDbg. + +Also, please make sure ``args.gn`` file looks like the following. + +.. code-block:: ini + + is_debug = false + target_cpu = "x64" + v8_monolithic = true + v8_use_external_startup_data = false + is_component_build = false + v8_enable_i18n_support= false + v8_enable_pointer_compression = false + v8_static_library = true + symbol_level = 0 + use_custom_libcxx = false + +``v8_monolith`` is the build target. + +[`Home <../README.rst>`_] diff --git a/docs/development.rst b/docs/development.rst new file mode 100644 index 000000000..fb1a5970e --- /dev/null +++ b/docs/development.rst @@ -0,0 +1,33 @@ +=========== +Development +=========== + +Development Tools +================= + +JDK +--- + +Javet development requires JDK 8 to be installed, though Javet supports JDK 8+. JDK 6 support has been dropped because a few JDK 8 features are heavily used in Javet. + +IDE +--- + +I personally recommend IntelliJ IDEA. + +Gradle +------ + +For now, Gradle v6.7 + Kotlin DSL constructs the build system. + +Maven +----- + +Maven v3.6.3+ is the one. + +NodeJS +------ + +NodeJS 14.5+ is required if you want to go over the tutorial because a few examples use some node modules. After installing NodeJS, please visit ``scripts/node`` directory and run ``npm install``. + +[`Home <../README.rst>`_] diff --git a/README.md b/docs/faq/history_with_j2v8.rst similarity index 58% rename from README.md rename to docs/faq/history_with_j2v8.rst index 98b012131..4663e4b91 100644 --- a/README.md +++ b/docs/faq/history_with_j2v8.rst @@ -1,63 +1,46 @@ -# Javet +History with J2V8 +================= -Javet is Java + V8 (JAVa + V + EighT). It is yet another way of embedding V8 in Java. It was inspired by J2V8. - -## Why Javet? - -### J2V8 Issues +J2V8 Issues +=========== J2V8 is an excellent project on embedding V8 in Java. However, J2V8 community hasn't been active since 2017. The last Windows version 4.6.0 was released on 2016 and the last Linux version 4.8.0 was released on 2017. The V8 in Windows v4.6.0 doesn't even fully support ES6. -The latest community activities were around Android versions. The NodeJS API was dropped. The Windows build has been seriously broken for years. The Linux build is nearly broken. +The latest community activities were around Android versions. The NodeJS API was dropped. The Windows build has been seriously broken for years. The Linux build is nearly broken. There are serious memory leak issues which are hard to be fixed under the current architecture. Its API has stopped evolving for years. Many new features I expect just don't get any chances to be implemented. Obviously, if the build system was broken and couldn't be easily fixed, almost no one would like to contribute. That includes me. When I was trying to fix the build system for Windows and Linux, I had to admit that's so, so, so, challenging. Why is that? I think it's not merely J2V8 community's problem because in the meanwhile V8, NodeJS and ECMAScript move forward rapidly causing many unexpected challenges. Someone or some team needs to look after J2V8 from time to time. Unfortunately, reality is cruel. -### Well, Why not Let Me Start from Scratch? - -Sometimes starting from scratch implies lower cost than upgrading an existing solution. I think it might be true here in this project. I've learned quite a lot by manually fixing the Windows and Linux build system. - -Also, I've got many ideas on how the API will look like. I think I would be able to write a new one from scratch and leave J2V8 behind. - -## Javet Project Status +J2V8 Latest Version +=================== -This is kind of a personal project, yet still on paper. - -### TODO List - -* To start from scratch so that there is no legal issues to J2V8. -* To support Windows and Linux. Supporting MacOS calls for your help. -* To implement unified `V8Object` covering primitive types. -* To implement V8 runtime pool like DB connection pool. The performance test shows there is a huge gap (millions vs. hundreds) between pooled and non-pooled V8 runtime. -* To support more types that are not supported by ECMAScript, e.g. Long, BigDecimal, etc. -* To revive NodeJS. -* To implement runtime debugging capability. - -### What Can I Use? - -For now, please try the latest [J2V8 v6.2.0](https://github.com/caoccao/Javet/releases/tag/0.6.2.0) unofficially released by me. I'll try to keep up with latest V8 in a slow pace. If you like my work, please **Star** this project. And, you may send messages to [@sjtucaocao](https://twitter.com/sjtucaocao). +I managed to unofficially built the latest `J2V8 v6.2.0 `_. * V8 is upgraded to v8.3.110.9 which was released in May, 2020. * Windows and Linux are supported. * NodeJS is dropped temporarily. -I've tested the performance between `j2v8_win32_x86_64-4.6.0.jar` and `j2v8_win32_x86_64-6.2.0.jar` on a Windows machine with CPU i7 10700K. The test code is just `1+1 -> 2`. Here are the comparisons. +I've tested the performance between ``j2v8_win32_x86_64-4.6.0.jar`` and ``j2v8_win32_x86_64-6.2.0.jar`` on a Windows machine with CPU i7 10700K. The test code is just ``1+1 -> 2``. Here are the comparisons. -| Case | 4.6.0 (TPS) | 6.2.0 (TPS) | -|--------------------------------|--------------|--------------| -| Single Session with 1 Thread | 1,003,009 | 1,338,688 | -| Ad-hoc Session with 1 Thread | 35 | 299 | -| 4 Sessions with 4 threads | 2,274,019 | 4,571,428 | +=============================== ============== ============= + Case 4.6.0 (TPS) 6.2.0 (TPS) +=============================== ============== ============= + Single Session with 1 Thread 1,003,009 1,338,688 + Ad-hoc Session with 1 Thread 35 299 + 4 Sessions with 4 threads 2,274,019 4,571,428 +=============================== ============== ============= -With this kind of performance improvement, what are the reasons of sticking to `v4.6.0`? +With this kind of performance improvement, what are the reasons of sticking to ``v4.6.0``? -### Why Windows and Linux only? +Why Windows and Linux only? +=========================== * I don't own a decent Mac device. To be more precisely, I have Mac Mini and MacBook Air, but they are too old (building V8 would take many hours). And I have no plan on buying a new one in the near future. Call for donation? Don't be joking. So there's no MacOS release. * I don't intend to support Android for now. -### Why not Automate the J2V8 Build System? +Why not Automate the J2V8 Build System? +======================================= TL;DR: It's too hard. @@ -65,15 +48,18 @@ TL;DR: It's too hard. * NodeJS was removed early. I haven't got time reviving it in J2V8. * J2V8 build system is too old. * Gradle v2.14.1 is far from the lowest supported gradle version in my latest IntelliJ IDEA. And I don't have interest in installing a legacy Eclipse to play with that version of gradle. Hey, why not upgrade gradle to satisfy IDEA? I tried, but all was bad luck. You may take a try, then understand what I have suffered from. - * CMake is old and seriously broken on Windows. Nowadays, V8 only supports VS 2017 or 2019, but `CMakeLists.txt` is still at the VS 2015 age. No surprise, it doesn't work at all. + * CMake is old and seriously broken on Windows. Nowadays, V8 only supports VS 2017 or 2019, but ``CMakeLists.txt`` is still at the VS 2015 age. No surprise, it doesn't work at all. * Docker build is deeply broken as well. The dependent docker image was gone. There are many errors in many steps. Sitting there, watching the docker build breaks made me full of frustration because I thought it would take me a few months fixing the problems, but I don't have a few months. No one pays me to do that. * Python2 scripts form the outer layer of the build system, also hide the actual building logic from someone who tries to fix the build system. I don't want to spend my precious time fixing Python2 scripts, because I've been on Python3 for many years. I wish my hair could be as much as Guido van Rossum's. Obviously, I am not, so no more Python2. - * Maven is old but the least problematic. At least it allows me to package the jar files with my hack to the `pom.xml`. + * Maven is old but the least problematic. At least it allows me to package the jar files with my hack to the ``pom.xml``. -With these uncertainties, to me, automating the build system is something with ROI closing to 0. Supposing I achieved it in a particular version of V8, let's say v8.3.110.9, it would for sure break in v8.9.213. Yes, I've confirmed that. +With these uncertainties, to me, automating the build system is something with ROI closing to 0. Supposing I achieved it in a particular version of V8, let's say v8.3.110.9, it would for sure break in v8.9.x. Later, I managed to built v8.9.255 which has been embeded into Javet. -### Why not Deploy J2V8 to Maven Repository? +Why not Deploy J2V8 to Maven Repository? +======================================== * I don't have the permission to its official repository. * There has been no Windows / Linux releases deployed since 2016 / 2017. I really don't know who to contact with. * You may easily integrate the jar files in your local maven repository. + +[`Home <../../README.rst>`_] [`FAQ `_] diff --git a/docs/faq/index.rst b/docs/faq/index.rst new file mode 100644 index 000000000..d010528a6 --- /dev/null +++ b/docs/faq/index.rst @@ -0,0 +1,7 @@ +=== +FAQ +=== + +* `History with J2V8 `_ + +[`Home <../../README.rst>`_] diff --git a/docs/todo_list.rst b/docs/todo_list.rst new file mode 100644 index 000000000..906c14cb0 --- /dev/null +++ b/docs/todo_list.rst @@ -0,0 +1,10 @@ +========= +TODO List +========= + +* To support more types that are not supported by ECMAScript, e.g. BigDecimal, etc. +* To revive NodeJS. +* To implement runtime debugging capability. + + +[`Home <../README.rst>`_] diff --git a/docs/tutorial/hello_javet.rst b/docs/tutorial/hello_javet.rst new file mode 100644 index 000000000..71dc4e04b --- /dev/null +++ b/docs/tutorial/hello_javet.rst @@ -0,0 +1,90 @@ +=========== +Hello Javet +=========== + +Reference Javet +=============== + +Maven +----- + +.. code-block:: xml + + + com.caoccao.javet + javet + 0.7.0 + + +Gradle Kotlin +------------- + +.. code-block:: kotlin + + dependencies { + implementation("com.caoccao.javet:javet:0.7.0") + } + +Gradle Groovy +------------- + +.. code-block:: groovy + + compile group: 'com.caoccao.javet', name: 'javet', version: '0.7.0' + +Print **Hello Javet** +===================== + +.. code-block:: java + + // Step 1: Create a V8 runtime from V8 host in try resource. + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime()) { + // Step 2: Request a lock. + v8Runtime.lock(); + // Step 3: Execute a string as JavaScript code and print the result to console. + System.out.println(v8Runtime.getExecutor("'Hello Javet'").executeString()); // Hello Javet + // Step 4: Resource including the lock is recycled automatically at the end of the try resource block. + } + +Print **1 + 1** +=============== + +.. code-block:: java + + // Step 1: Create a V8 runtime from V8 host in try resource. + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime()) { + // Step 2: Request a lock. + v8Runtime.lock(); + // Step 3: Execute a string as JavaScript code and print the result to console. + System.out.println("1 + 1 = " + v8Runtime.getExecutor("1 + 1").executeInteger()); // 2 + // Step 4: Resource including the lock is recycled automatically at the end of the try resource block. + } + +Play with Pool and Console +========================== + +.. code-block:: java + + // Create a Javet engine pool. + try (IJavetEnginePool javetEnginePool = new JavetEnginePool()) { + // Get a Javet engine from the pool. + try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { + // Get a V8 runtime from the engine. + // lock() is not necessary because the Javet engine handles that. + V8Runtime v8Runtime = javetEngine.getV8Runtime(); + // Create a Javet console interceptor. + JavetConsoleInterceptor javetConsoleInterceptor = new JavetConsoleInterceptor(v8Runtime); + // Register the Javet console to V8 global object. + javetConsoleInterceptor.register(v8Runtime.getGlobalObject()); + // V8 console log is redirected to JVM console log. + v8Runtime.getExecutor("console.log('Hello Javet from Pool');").executeVoid(); + // Unregister the Javet console to V8 global object. + javetConsoleInterceptor.unregister(v8Runtime.getGlobalObject()); + // unlock() is not necessary because the Javet engine handles that. + // close() is not necessary because the Javet pool handles that. + } + } + +Please refer to `source code <../../src/test/java/com/caoccao/javet/tutorial/HelloJavet.java>`_ for more detail. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst new file mode 100644 index 000000000..eb8575b86 --- /dev/null +++ b/docs/tutorial/index.rst @@ -0,0 +1,13 @@ +======== +Tutorial +======== + +* `Hello Javet `_ +* `Know the Lock `_ +* `Memory Management `_ +* `Manipulate V8 Function `_ +* `Polyfill `_ + +Complete tutorial is available at `here <../../src/test/java/com/caoccao/javet/tutorial>`_. + +[`Home <../../README.rst>`_] diff --git a/docs/tutorial/know_the_lock.rst b/docs/tutorial/know_the_lock.rst new file mode 100644 index 000000000..a9b3b099c --- /dev/null +++ b/docs/tutorial/know_the_lock.rst @@ -0,0 +1,21 @@ +============= +Know the Lock +============= + +What does Lock Mean in Javet? +============================= + +V8 runtime runs in an isolated and single-threaded environment so that there is no race condition issue. So playing V8 runtime in JVM among multiple threads is prohibited unless the V8 runtime is always protected by **Lock**. + +Javet exposed ``lock()`` and ``unlock()`` in ``V8Runtime``. Any attempt of accessing V8 runtime before ``lock()`` or after ``unlock()`` will result in a checked exception reporting lock conflict or V8 runtime closed. + +Best Practices +============== + +* Always call ``lock()`` before any access to V8 runtime as well as V8 value objects. Especially: + * V8 value reference objects are just Java wrappers of certain JNI native resource, so keeping the lock in effect makes sure V8 threading model is well maintained. + * In function callback scenario, the callback needs to access V8 runtime and V8 value objects, so the presence of lock is required. +* Always keep the lock range as minimal as possible to allow best performance. +* ``unlock()`` is can be skipped if ``close()`` is called. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/manipulate_v8_function.rst b/docs/tutorial/manipulate_v8_function.rst new file mode 100644 index 000000000..248d26325 --- /dev/null +++ b/docs/tutorial/manipulate_v8_function.rst @@ -0,0 +1,71 @@ +====================== +Manipulate V8 Function +====================== + +Lifecycle +========= + +Know the Implication +-------------------- + +Lifecycle of a function is recommended to be managed by V8. This is a bit different from the common usage of other V8 value objects. + +Why? Because in order to keep track of the callback capability, Javet needs to persist few tiny objects in JVM as well as in V8. Those persisted objects get released immediately when ``close()`` is explicitly called and ``isWeak()`` is ``false``. However, once a function is set to a certain object, it is typically no longer needed. If closing that function explicitly really recycles it, the following callback will cause memory corruption. + +The solution is to set the function to weak by ``setWeak()`` so that the lifecycle management is handed over to V8. V8 decides when to recycle the function and notifies Javet to recycle those persisted objects. + +Option 1: The Common Way +------------------------ + +.. code-block:: java + + // Create a function and wrap it with try resource. + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + // Do whatever you want to do with this function + } + // Outside the code block, this function is no longer valid. Calling this function in V8 will result in memory corruption. + +Option 2: The Recommended Way +----------------------------- + +.. code-block:: java + + V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext); + // Set this function to the certain V8 value objects. + v8ValueFunction.setWeak(); + // Once this function is set to weak, its lifecycle is automatically managed by Javet + V8. + // There is no need to call close() any more. + +Automatic Type Conversion +========================= + +Javet is capable of automatically converting its internal ``V8Value`` to other types and that capability can be manipulated by ``JavetConverterUtils`` which also supports custom type conversion. So, the following 4 functions are all the same and valid. + +.. code-block:: java + + // Option 1 + public String echo(String str) { + return str; + } + + // Option 2 + public String echo(V8Value arg) { + return arg == null ? null : arg.toString(); + } + + // Option 3 + public V8Value echo(String str) { + return new V8ValueString(str); + } + + // Option 4 + public V8Value echo(V8Value arg) throws JavetException { + return arg.toClone(); + } + + // All 4 functions above can be handled in Javet as the following function + echo("123"); + +Note: Primitive types must be in their object form in the method signature. E.g. ``boolean`` must be set to ``Boolean``, ``int`` must be set to ``Integer``, etc. Why? Because the converted value could be ``null`` which would cause JDK to complain with an exception. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/memory_management.rst b/docs/tutorial/memory_management.rst new file mode 100644 index 000000000..554b79a93 --- /dev/null +++ b/docs/tutorial/memory_management.rst @@ -0,0 +1,62 @@ +================= +Memory Management +================= + +3 Challenges +============ + +JVM GC +------ + +JVM is known to have a GC that manages memory automatically. However, that doesn't cover the objects in JNI native implementation. Once ``NewGlobalRef(javaObject)`` is called, that ``javaObject`` lives forever in JVM until ``DeleteGlobalRef(javaObject)`` is called. + +C++ Runtime +----------- + +Smart pointers in C++ cannot easily work across JNI to JVM, in other words, raw pointers are directly referenced in JVM as ``long``. C++ runtime has no idea when to free the memory of those raw pointers unless JVM tells C++ runtime to release via JNI. + +V8 GC +----- + +V8 generally categorizes objects in memory to 3 types. + +1. ``v8::Local`` - It lives within the local scope of a C++ function call. +2. ``v8::Persistent`` - Its lifecycle is managed by V8 GC. +3. ``v8::External`` - V8 GC treats it as root object so that it lives as long as the V8 isolate lives. + +Solution: Weak Reference +======================== + +Javet directly borrows the way V8 manages objects in JVM. The rule is simple in the following 2 patterns. + +Manually Manage V8 Objects +-------------------------- + +.. code-block:: java + + // Create an object and wrap it with try resource. + try (V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject()) { + // Do whatever you want to do with this object + // v8ValueObject.close() is called automatically at the end of the block. + } + // Outside the code block, this object is no longer valid. + +Automatically Manage V8 Objects +------------------------------- + +.. code-block:: java + + // Create an object. + V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject(); + // Do whatever you want to do with this object + v8ValueObject.setWeak(); + // Do whatever you want to do with this object + /* + v8ValueObject.close() is called automatically via V8 GC callback. + So, there is no need to close the V8 object explicitly. + This is quite useful when the lifecycle is not determined, E.g. V8 function. + */ + +Note: V8 does not recycle objects that are referenced by other objects. Please make sure the object chain is broken so that GC can work as expected. ``com.caoccao.javet.interception.logging.JavetStandardConsoleInterceptor`` is a good sample showing how to deal with that. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/polyfill.rst b/docs/tutorial/polyfill.rst new file mode 100644 index 000000000..8ee325473 --- /dev/null +++ b/docs/tutorial/polyfill.rst @@ -0,0 +1,36 @@ +======== +Polyfill +======== + +Yes, you can polyfill Javet with NodeJS modules. + +decimal.js +========== + +.. code-block:: java + + public void loadJS() throws JavetException { + File decimalJSFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/node_modules/decimal.js/decimal.js"); + if (decimalJSFile.exists() && decimalJSFile.canRead()) { + logger.logInfo("Loading {0}.", decimalJSFile.getAbsolutePath()); + v8Runtime = V8Host.getInstance().createV8Runtime(); + v8Runtime.lock(); + v8Runtime.getExecutor(decimalJSFile).executeVoid(); + } else { + logger.logError("{0} is not found.", decimalJSFile.getAbsolutePath()); + logger.logError("Please make sure NodeJS is installed, then visit script/node directory and run npm install."); + } + } + + public void test() throws JavetException { + logger.logInfo("1.23 + 2.34 = {0}", v8Runtime.getExecutor( + "const a = new Decimal(1.23);" + + "const b = new Decimal(2.34);" + + "a.add(b).toString();").executeString()); + } + +Please refer to `source code <../../src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java>`_ for more detail. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..e708b1c02 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..be52383ef --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..4f906e0c8 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..0dad72c2d --- /dev/null +++ b/pom.xml @@ -0,0 +1,191 @@ + + 4.0.0 + + com.caoccao.javet + javet + 0.7.0 + javet + Javet is Java + V8 (JAVa + V + EighT). It is a way of embedding V8 in Java. + https://github.com/caoccao/Javet + + + + APACHE LICENSE, VERSION 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Sam Cao + sjtucaocao@gmail.com + caoccao.com + https://www.caoccao.com + + + + + scm:git:git://github.com/caoccao/Javet.git + scm:git:git@github.com:caoccao/caoccao.git + https://github.com/caoccao/Javet + javet-0.7.0 + + + + 1.8 + 1.8 + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + org.junit.jupiter + junit-jupiter-api + 5.6.0 + test + + + + org.junit.jupiter + junit-jupiter-engine + 5.6.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 2.22.2 + + + maven-deploy-plugin + 3.0.0-M1 + + + default-deploy + deploy + + deploy + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.0.0-M1 + + true + false + forked-path + + -Dgpg.passphrase=${gpg.passphrase} -Dmaven.test.skipTests=true -Dmaven.test.skip=true + + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.11.2 + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + UTF-8 + + + + attach-javadoc + + jar + + + + + + + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + \ No newline at end of file diff --git a/scripts/node/package.json b/scripts/node/package.json new file mode 100644 index 000000000..c6b6e6651 --- /dev/null +++ b/scripts/node/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "decimal.js": "^10.2.1" + } +} diff --git a/scripts/node/test-es5/test-es5-multiline-string-literals.js b/scripts/node/test-es5/test-es5-multiline-string-literals.js new file mode 100644 index 000000000..dfda0b904 --- /dev/null +++ b/scripts/node/test-es5/test-es5-multiline-string-literals.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() { + const x = 123; + const result = `aaa${x}bbb`; + console.log(result); + return result; +} + +f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-array-find-index.js b/scripts/node/test-es6/test-es6-array-find-index.js new file mode 100644 index 000000000..2585afe8d --- /dev/null +++ b/scripts/node/test-es6/test-es6-array-find-index.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function x(value, index, array) { + return value > 2; +} + +function f() { + const result = [1,2,3].findIndex(x); + console.log(result); + return result; +} + +f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-array-find.js b/scripts/node/test-es6/test-es6-array-find.js new file mode 100644 index 000000000..6c25187bf --- /dev/null +++ b/scripts/node/test-es6/test-es6-array-find.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function x(value, index, array) { + return value > 2; +} + +function f() { + const result = [1,2,3].find(x); + console.log(result); + return result; +} + +f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-arrow-function.js b/scripts/node/test-es6/test-es6-arrow-function.js new file mode 100644 index 000000000..2160c9f4b --- /dev/null +++ b/scripts/node/test-es6/test-es6-arrow-function.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +const f = () => { + const x = 1; + let y = 2; + const result = x + y; + console.log(result); + return result; +} + +f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-class.js b/scripts/node/test-es6/test-es6-class.js new file mode 100644 index 000000000..5a21e1ea9 --- /dev/null +++ b/scripts/node/test-es6/test-es6-class.js @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +class A { + constructor() { + this.x = 1; + this.y = 2; + } + + f() { + const result = this.x + this.y; + console.log(result); + return result; + } +} + +const a = new A(); +a.f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-default-parameter-values.js b/scripts/node/test-es6/test-es6-default-parameter-values.js new file mode 100644 index 000000000..84a1189e9 --- /dev/null +++ b/scripts/node/test-es6/test-es6-default-parameter-values.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f(x = 1, y = 2) +{ + const result = x + y; + console.log(result); + return result; +} + +f(); diff --git a/scripts/node/test-es6/test-es6-function-rest-parameter.js b/scripts/node/test-es6/test-es6-function-rest-parameter.js new file mode 100644 index 000000000..5e5c3fbad --- /dev/null +++ b/scripts/node/test-es6/test-es6-function-rest-parameter.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f(...args) { + let result = 0; + for (let arg of args) result += arg; + console.log(result); + return result; +} + +f(1,2,3); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-is-finite.js b/scripts/node/test-es6/test-es6-is-finite.js new file mode 100644 index 000000000..481250066 --- /dev/null +++ b/scripts/node/test-es6/test-es6-is-finite.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() +{ + const result = [isFinite(10/1), isFinite(10/0)]; + console.log(result); + return result; +} + +f(); diff --git a/scripts/node/test-es6/test-es6-is-nan.js b/scripts/node/test-es6/test-es6-is-nan.js new file mode 100644 index 000000000..2fe4ee8b7 --- /dev/null +++ b/scripts/node/test-es6/test-es6-is-nan.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() +{ + const result = [isNaN(NaN), isNaN("123")]; + console.log(result); + return result; +} + +f(); diff --git a/scripts/node/test-es6/test-es6-let-const.js b/scripts/node/test-es6/test-es6-let-const.js new file mode 100644 index 000000000..ad9070783 --- /dev/null +++ b/scripts/node/test-es6/test-es6-let-const.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() { + const x = 1; + let y = 2; + const result = x + y; + console.log(result); + return result; +} + +f(); \ No newline at end of file diff --git a/scripts/node/test-es6/test-es6-number-is-integer.js b/scripts/node/test-es6/test-es6-number-is-integer.js new file mode 100644 index 000000000..b83f06e12 --- /dev/null +++ b/scripts/node/test-es6/test-es6-number-is-integer.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() +{ + const result = [Number.isInteger(1), Number.isInteger(1.5)]; + console.log(result); + return result; +} + +f(); diff --git a/scripts/node/test-es6/test-es6-number-is-safe-integer.js b/scripts/node/test-es6/test-es6-number-is-safe-integer.js new file mode 100644 index 000000000..02c4f6f7d --- /dev/null +++ b/scripts/node/test-es6/test-es6-number-is-safe-integer.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() +{ + const result = [Number.isSafeInteger(1), Number.isSafeInteger(12345678901234567890)]; + console.log(result); + return result; +} + +f(); diff --git a/scripts/node/test-es6/test-es6-symbol.js b/scripts/node/test-es6/test-es6-symbol.js new file mode 100644 index 000000000..f36a64ac8 --- /dev/null +++ b/scripts/node/test-es6/test-es6-symbol.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021 caoccao.com Sam Cao + * All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; + +function f() +{ + const result = Symbol("id") == Symbol("id"); + console.log(result); + return result; +} + +f(); diff --git a/scripts/python/change_javet_version.py b/scripts/python/change_javet_version.py new file mode 100644 index 000000000..a672f5288 --- /dev/null +++ b/scripts/python/change_javet_version.py @@ -0,0 +1,96 @@ +''' + Copyright (c) 2021 caoccao.com Sam Cao + All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +''' + +import coloredlogs +import logging +import pathlib +import re +import sys + +coloredlogs.install(level=logging.DEBUG, fmt='%(asctime)-15s %(name)s %(levelname)s: %(message)s') + +class ChangeJavetVersion(object): + + def __init__(self, version) -> None: + self._root_path = (pathlib.Path(__file__) / '../../../').resolve().absolute() + self._version = version + + def update(self): + self._update( + 'README.rst', '\n', + re.compile(r'\*(?P\d+\.\d+\.\d+)\*'), + re.compile(r'^ (?P\d+\.\d+\.\d+)$'), + re.compile(r'javet:(?P\d+\.\d+\.\d+)"'), + re.compile(r'version: \'(?P\d+\.\d+\.\d+)\'')) + self._update( + 'build.gradle.kts', '\n', + re.compile(r'^version = "(?P\d+\.\d+\.\d+)"$')) + self._update( + 'docs/tutorial/hello_javet.rst', '\n', + re.compile(r'^ (?P\d+\.\d+\.\d+)$'), + re.compile(r'javet:(?P\d+\.\d+\.\d+)"'), + re.compile(r'version: \'(?P\d+\.\d+\.\d+)\'')) + self._update( + 'pom.xml', '\n', + re.compile(r'^ (?P\d+\.\d+\.\d+)$')) + self._update( + 'cpp/build.cmd', '\n', + re.compile(r'JAVET_VERSION=(?P\d+\.\d+\.\d+)$')) + self._update( + 'cpp/build.sh', '\n', + re.compile(r'JAVET_VERSION=(?P\d+\.\d+\.\d+)$')) + self._update( + 'src/main/java/com/caoccao/javet/interop/JavetLibLoader.java', '\n', + re.compile(r'LIB_VERSION = "(?P\d+\.\d+\.\d+)";$')) + self._update( + 'cpp/jni/javet.rc', '\n', + re.compile(r'"(?P\d+\.\d+\.\d+)'), + re.compile(r'v\.(?P\d+\.\d+\.\d+)'), + re.compile(r'(?P\d+,\d+,\d+)')) + + def _update(self, relative_file_path: str, line_separator: str, *patterns: list): + file_path = (self._root_path / relative_file_path).resolve().absolute() + logging.info('Updating %s.', str(file_path)) + lines, line_number = [], 1 + for line in file_path.read_text('utf-8').split(line_separator): + for pattern in patterns: + match_object = pattern.search(line) + if match_object is not None: + version = self._version + if ',' in match_object.group('version'): + version = version.replace('.', ',') + logging.info( + ' %d: %s -> %s', + line_number, + match_object.group('version'), + version) + line = '{prefix}{version}{suffix}'.format( + prefix=line[:match_object.start('version')], + version=version, + suffix=line[match_object.end('version'):]) + break + lines.append(line) + line_number += 1 + file_path.write_text(line_separator.join(lines), 'utf-8') + +def main(): + change_javet_version = ChangeJavetVersion('0.7.0') + change_javet_version.update() + return 0 + +if __name__ == '__main__': + sys.exit(int(main() or 0)) diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 000000000..ba720289c --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,18 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +rootProject.name = "javet" diff --git a/src/main/java/com/caoccao/javet/exceptions/BaseJavetScriptingException.java b/src/main/java/com/caoccao/javet/exceptions/BaseJavetScriptingException.java new file mode 100644 index 000000000..3d04b58d2 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/BaseJavetScriptingException.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public abstract class BaseJavetScriptingException extends JavetException { + protected JavetScriptingError error; + + public BaseJavetScriptingException( + String message, String resourceName, String sourceLine, + int lineNumber, int startColumn, int endColumn, int startPosition, int endPosition) { + super(message); + error = new JavetScriptingError(message, resourceName, sourceLine, + lineNumber, startColumn, endColumn, startPosition, endPosition); + } + + public JavetScriptingError getError() { + return error; + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetCompilationException.java b/src/main/java/com/caoccao/javet/exceptions/JavetCompilationException.java new file mode 100644 index 000000000..81c625f4a --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetCompilationException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetCompilationException extends BaseJavetScriptingException { + public JavetCompilationException( + String message, String resourceName, String sourceLine, + int lineNumber, int startColumn, int endColumn, int startPosition, int endPosition) { + super(message, resourceName, sourceLine, lineNumber, startColumn, endColumn, startPosition, endPosition); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetConverterException.java b/src/main/java/com/caoccao/javet/exceptions/JavetConverterException.java new file mode 100644 index 000000000..cb37a0a90 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetConverterException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetConverterException extends JavetException { + public JavetConverterException(String message) { + super(MessageFormat.format("Failed to convert values with error message {0}", message)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetException.java b/src/main/java/com/caoccao/javet/exceptions/JavetException.java new file mode 100644 index 000000000..85330832d --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetException extends Exception { + public JavetException() { + super(); + } + + public JavetException(String message) { + super(message); + } + + public JavetException(String message, Throwable cause) { + super(message, cause); + } + + public JavetException(Throwable cause) { + super(cause); + } + + public JavetException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetExecutionException.java b/src/main/java/com/caoccao/javet/exceptions/JavetExecutionException.java new file mode 100644 index 000000000..bdf5d17fb --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetExecutionException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetExecutionException extends BaseJavetScriptingException { + public JavetExecutionException( + String message, String resourceName, String sourceLine, + int lineNumber, int startColumn, int endColumn, int startPosition, int endPosition) { + super(message, resourceName, sourceLine, lineNumber, startColumn, endColumn, startPosition, endPosition); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetIOException.java b/src/main/java/com/caoccao/javet/exceptions/JavetIOException.java new file mode 100644 index 000000000..c8aa2403f --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetIOException.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.exceptions; + +import java.nio.file.Path; +import java.text.MessageFormat; + +public class JavetIOException extends JavetException { + private JavetIOException(String message) { + super(message); + } + + private JavetIOException(String message, Throwable cause) { + super(message, cause); + } + + public static JavetIOException failedToReadPath(Path path, Throwable cause) { + return new JavetIOException( + MessageFormat.format("Failed to read {0}", path.toFile().getAbsolutePath()), cause); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetNotSupportedException.java b/src/main/java/com/caoccao/javet/exceptions/JavetNotSupportedException.java new file mode 100644 index 000000000..09d4c2b1b --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetNotSupportedException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetNotSupportedException extends JavetException { + public JavetNotSupportedException(String feature) { + super(MessageFormat.format("{0} is not supported", feature)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetOSNotSupportedException.java b/src/main/java/com/caoccao/javet/exceptions/JavetOSNotSupportedException.java new file mode 100644 index 000000000..3257bdb2b --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetOSNotSupportedException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetOSNotSupportedException extends JavetException { + public JavetOSNotSupportedException(String osName) { + super(MessageFormat.format("OS [{0}] is not supported.", osName)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java b/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java new file mode 100644 index 000000000..bcacc9b5e --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetScriptingError.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public final class JavetScriptingError { + private String message; + private String resourceName; + private String sourceLine; + private int lineNumber; + private int startColumn; + private int endColumn; + private int startPosition; + private int endPosition; + + public JavetScriptingError( + String message, String resourceName, String sourceLine, + int lineNumber, int startColumn, int endColumn, int startPosition, int endPosition) { + this.message = message; + this.resourceName = resourceName; + this.sourceLine = sourceLine; + this.lineNumber = lineNumber; + this.startColumn = startColumn; + this.endColumn = endColumn; + this.startPosition = startPosition; + this.endPosition = endPosition; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getSourceLine() { + return sourceLine; + } + + public void setSourceLine(String sourceLine) { + this.sourceLine = sourceLine; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public int getStartColumn() { + return startColumn; + } + + public void setStartColumn(int startColumn) { + this.startColumn = startColumn; + } + + public int getEndColumn() { + return endColumn; + } + + public void setEndColumn(int endColumn) { + this.endColumn = endColumn; + } + + public int getStartPosition() { + return startPosition; + } + + public void setStartPosition(int startPosition) { + this.startPosition = startPosition; + } + + public int getEndPosition() { + return endPosition; + } + + public void setEndPosition(int endPosition) { + this.endPosition = endPosition; + } + + @Override + public String toString() { + return MessageFormat.format( + "Error: {0}\nResource: {1}\nSource Code: {2}\nLine Number: {3}\nColumn: {4}, {5}\nPosition: {6}, {7}", + message, resourceName, sourceLine, Integer.toString(lineNumber), + Integer.toString(startColumn), Integer.toString(endColumn), + Integer.toString(startPosition), Integer.toString(endPosition)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetUnknownCompilationException.java b/src/main/java/com/caoccao/javet/exceptions/JavetUnknownCompilationException.java new file mode 100644 index 000000000..841c306bf --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetUnknownCompilationException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetUnknownCompilationException extends JavetException { + public JavetUnknownCompilationException(String message) { + super(MessageFormat.format("Unknown compilation error {0}", message)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetUnknownExecutionException.java b/src/main/java/com/caoccao/javet/exceptions/JavetUnknownExecutionException.java new file mode 100644 index 000000000..9f6ccdac6 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetUnknownExecutionException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetUnknownExecutionException extends JavetException { + public JavetUnknownExecutionException(String message) { + super(MessageFormat.format("Unknown execution error {0}", message)); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackAlreadyRegisteredException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackAlreadyRegisteredException.java new file mode 100644 index 000000000..62c989635 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackAlreadyRegisteredException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8CallbackAlreadyRegisteredException extends JavetException { + public JavetV8CallbackAlreadyRegisteredException() { + super("V8 callback is already registered"); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackNotRegisteredException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackNotRegisteredException.java new file mode 100644 index 000000000..3d2349adf --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackNotRegisteredException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8CallbackNotRegisteredException extends JavetException { + public JavetV8CallbackNotRegisteredException() { + super("V8 callback is not registered"); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackSignatureMismatchException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackSignatureMismatchException.java new file mode 100644 index 000000000..d5881dc83 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8CallbackSignatureMismatchException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetV8CallbackSignatureMismatchException extends JavetException { + protected JavetV8CallbackSignatureMismatchException(String format, Object... objects) { + super(MessageFormat.format("V8 callback signature mismatches: " + format, objects)); + } + + protected JavetV8CallbackSignatureMismatchException(String message, Throwable cause) { + super(message, cause); + } + + public static JavetV8CallbackSignatureMismatchException parameterSizeMismatch( + int expectedSize, int actualSize) { + return new JavetV8CallbackSignatureMismatchException( + "expected parameter size is {0}, actual parameter size is {1}", + expectedSize, actualSize); + } + + public static JavetV8CallbackSignatureMismatchException parameterTypeMismatch( + Class expectedType, Class actualType) { + return new JavetV8CallbackSignatureMismatchException( + "expected parameter type is {0}, actual parameter type is {1}", + expectedType.getName(), actualType.getName()); + } + + public static JavetV8CallbackSignatureMismatchException unknown( + Throwable cause) { + return new JavetV8CallbackSignatureMismatchException(cause.getMessage(), cause); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyClosedException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyClosedException.java new file mode 100644 index 000000000..7f5369854 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyClosedException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8RuntimeAlreadyClosedException extends JavetException { + public JavetV8RuntimeAlreadyClosedException() { + super("V8 runtime is already closed"); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyRegisteredException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyRegisteredException.java new file mode 100644 index 000000000..e59b7baa6 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeAlreadyRegisteredException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8RuntimeAlreadyRegisteredException extends JavetException { + public JavetV8RuntimeAlreadyRegisteredException() { + super("V8 runtime is already registered"); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLeakException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLeakException.java new file mode 100644 index 000000000..a033654c6 --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLeakException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetV8RuntimeLeakException extends JavetException { + public JavetV8RuntimeLeakException(int count) { + super(MessageFormat.format("{0} V8 runtime(s) leaked", Integer.toString(count))); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java new file mode 100644 index 000000000..ac5ba768b --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +import java.text.MessageFormat; + +public class JavetV8RuntimeLockConflictException extends JavetException { + public JavetV8RuntimeLockConflictException(String message) { + super(message); + } + + public JavetV8RuntimeLockConflictException() { + this("V8 runtime lock conflict is detected"); + } + + public JavetV8RuntimeLockConflictException(long lockedThreadId, long currentThreadId) { + this(MessageFormat.format( + "V8 runtime lock conflict is detected with locked thread ID {0} and current thread ID {1}", + Long.toString(lockedThreadId), Long.toString(currentThreadId))); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeNotRegisteredException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeNotRegisteredException.java new file mode 100644 index 000000000..5e1ddd2af --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeNotRegisteredException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8RuntimeNotRegisteredException extends JavetException { + public JavetV8RuntimeNotRegisteredException() { + super("V8 runtime is not registered"); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8ValueAlreadyClosedException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8ValueAlreadyClosedException.java new file mode 100644 index 000000000..4f6eba68b --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8ValueAlreadyClosedException.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.exceptions; + +public class JavetV8ValueAlreadyClosedException extends JavetException { + public JavetV8ValueAlreadyClosedException() { + super("V8 value is already closed"); + } +} diff --git a/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java new file mode 100644 index 000000000..fd52f446f --- /dev/null +++ b/src/main/java/com/caoccao/javet/interception/BaseJavetInterceptor.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interception; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetInterceptor; +import com.caoccao.javet.interop.IV8CallbackReceiver; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.reference.IV8ValueObject; + +public abstract class BaseJavetInterceptor implements IJavetInterceptor, IV8CallbackReceiver { + protected V8Runtime v8Runtime; + + public BaseJavetInterceptor(V8Runtime v8Runtime) { + this.v8Runtime = v8Runtime; + } + + @Override + public abstract boolean register(IV8ValueObject iV8ValueObject) throws JavetException; + + @Override + public abstract boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException; + + @Override + public V8Runtime getV8Runtime() { + return v8Runtime; + } +} diff --git a/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java new file mode 100644 index 000000000..c4ae9ff1f --- /dev/null +++ b/src/main/java/com/caoccao/javet/interception/logging/BaseJavetConsoleInterceptor.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interception.logging; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetV8CallbackSignatureMismatchException; +import com.caoccao.javet.interception.BaseJavetInterceptor; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.V8CallbackContext; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.IV8ValueObject; +import com.caoccao.javet.values.reference.V8ValueFunction; +import com.caoccao.javet.values.reference.V8ValueObject; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public abstract class BaseJavetConsoleInterceptor extends BaseJavetInterceptor { + protected static final String JS_FUNCTION_DEBUG = "debug"; + protected static final String JS_FUNCTION_ERROR = "error"; + protected static final String JS_FUNCTION_INFO = "info"; + protected static final String JS_FUNCTION_LOG = "log"; + protected static final String JS_FUNCTION_TRACE = "trace"; + protected static final String JS_FUNCTION_WARN = "warn"; + protected static final String JAVA_CONSOLE_DEBUG = "consoleDebug"; + protected static final String JAVA_CONSOLE_ERROR = "consoleError"; + protected static final String JAVA_CONSOLE_INFO = "consoleInfo"; + protected static final String JAVA_CONSOLE_LOG = "consoleLog"; + protected static final String JAVA_CONSOLE_TRACE = "consoleTrace"; + protected static final String JAVA_CONSOLE_WARN = "consoleWarn"; + protected static final String EMPTY = ""; + protected static final String SPACE = " "; + protected static final String PROPERTY_CONSOLE = "console"; + + public BaseJavetConsoleInterceptor(V8Runtime v8Runtime) { + super(v8Runtime); + } + + protected String concat(V8Value... v8Values) { + if (v8Values == null || v8Values.length == 0) { + return EMPTY; + } + return String.join( + SPACE, + Arrays.stream(v8Values).map(v8Value -> v8Value.toString()).collect(Collectors.toList())); + } + + public abstract void consoleDebug(V8Value... v8Values); + + public abstract void consoleError(V8Value... v8Values); + + public abstract void consoleInfo(V8Value... v8Values); + + public abstract void consoleLog(V8Value... v8Values); + + public abstract void consoleTrace(V8Value... v8Values); + + public abstract void consoleWarn(V8Value... v8Values); + + @Override + public boolean register(IV8ValueObject iV8ValueObject) throws JavetException { + try (V8ValueObject console = v8Runtime.createV8ValueObject()) { + iV8ValueObject.set(PROPERTY_CONSOLE, console); + register(console, JS_FUNCTION_DEBUG, JAVA_CONSOLE_DEBUG); + register(console, JS_FUNCTION_ERROR, JAVA_CONSOLE_ERROR); + register(console, JS_FUNCTION_INFO, JAVA_CONSOLE_INFO); + register(console, JS_FUNCTION_LOG, JAVA_CONSOLE_LOG); + register(console, JS_FUNCTION_TRACE, JAVA_CONSOLE_TRACE); + register(console, JS_FUNCTION_WARN, JAVA_CONSOLE_WARN); + return true; + } catch (NoSuchMethodException e) { + throw JavetV8CallbackSignatureMismatchException.unknown(e); + } + } + + protected void register(IV8ValueObject iV8ValueObject, String jsFunctionName, String javaFunctionName) + throws JavetException, NoSuchMethodException { + V8CallbackContext callbackContext = new V8CallbackContext( + this, getClass().getMethod(javaFunctionName, V8Value[].class)); + V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(callbackContext); + iV8ValueObject.set(jsFunctionName, v8ValueFunction); + v8ValueFunction.setWeak(); + } + + @Override + public boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException { + return iV8ValueObject.delete(PROPERTY_CONSOLE); + } +} diff --git a/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java new file mode 100644 index 000000000..db58c72d1 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interception/logging/JavetStandardConsoleInterceptor.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interception.logging; + +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.V8Value; + +import java.io.PrintStream; + +public class JavetStandardConsoleInterceptor extends BaseJavetConsoleInterceptor { + protected PrintStream debug; + protected PrintStream error; + protected PrintStream info; + protected PrintStream log; + protected PrintStream trace; + protected PrintStream warn; + + public JavetStandardConsoleInterceptor(V8Runtime v8Runtime) { + super(v8Runtime); + debug = info = log = trace = warn = System.out; + error = System.err; + } + + public PrintStream getDebug() { + return debug; + } + + public void setDebug(PrintStream debug) { + this.debug = debug; + } + + public PrintStream getError() { + return error; + } + + public void setError(PrintStream error) { + this.error = error; + } + + public PrintStream getInfo() { + return info; + } + + public void setInfo(PrintStream info) { + this.info = info; + } + + public PrintStream getLog() { + return log; + } + + public void setLog(PrintStream log) { + this.log = log; + } + + public PrintStream getTrace() { + return trace; + } + + public void setTrace(PrintStream trace) { + this.trace = trace; + } + + public PrintStream getWarn() { + return warn; + } + + public void setWarn(PrintStream warn) { + this.warn = warn; + } + + @Override + public void consoleDebug(V8Value... v8Values) { + debug.println(concat(v8Values)); + } + + @Override + public void consoleError(V8Value... v8Values) { + error.println(concat(v8Values)); + } + + @Override + public void consoleInfo(V8Value... v8Values) { + info.println(concat(v8Values)); + } + + @Override + public void consoleLog(V8Value... v8Values) { + log.println(concat(v8Values)); + } + + @Override + public void consoleTrace(V8Value... v8Values) { + trace.println(concat(v8Values)); + } + + @Override + public void consoleWarn(V8Value... v8Values) { + warn.println(concat(v8Values)); + } +} diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java b/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java new file mode 100644 index 000000000..26c25082f --- /dev/null +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetClosable.java @@ -0,0 +1,24 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interfaces; + +import com.caoccao.javet.exceptions.JavetException; + +public interface IJavetClosable extends AutoCloseable { + void close() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java b/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java new file mode 100644 index 000000000..90a3ece54 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetInterceptor.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interfaces; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.reference.IV8ValueObject; + +public interface IJavetInterceptor { + boolean register(IV8ValueObject iV8ValueObject) throws JavetException; + + boolean unregister(IV8ValueObject iV8ValueObject) throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java b/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java new file mode 100644 index 000000000..1e55d6053 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetLogger.java @@ -0,0 +1,35 @@ +package com.caoccao.javet.interfaces; + +import java.text.MessageFormat; + +public interface IJavetLogger { + void debug(String message); + + void error(String message); + + void error(Throwable cause, String message); + + void info(String message); + + default void logDebug(String format, Object... objects) { + debug(MessageFormat.format(format, objects)); + } + + default void logError(Throwable cause, String format, Object... objects) { + error(cause, MessageFormat.format(format, objects)); + } + + default void logError(String format, Object... objects) { + error(MessageFormat.format(format, objects)); + } + + default void logInfo(String format, Object... objects) { + info(MessageFormat.format(format, objects)); + } + + default void logWarn(String format, Object... objects) { + warn(MessageFormat.format(format, objects)); + } + + void warn(String message); +} diff --git a/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java b/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java new file mode 100644 index 000000000..8a5bac2f8 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interfaces/IJavetResettable.java @@ -0,0 +1,26 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interfaces; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetV8RuntimeAlreadyClosedException; +import com.caoccao.javet.exceptions.JavetV8RuntimeLockConflictException; + +public interface IJavetResettable { + void reset() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/interop/IV8CallbackReceiver.java b/src/main/java/com/caoccao/javet/interop/IV8CallbackReceiver.java new file mode 100644 index 000000000..609bcdecb --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/IV8CallbackReceiver.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop; + +public interface IV8CallbackReceiver { + V8Runtime getV8Runtime(); +} diff --git a/src/main/java/com/caoccao/javet/interop/IV8Cloneable.java b/src/main/java/com/caoccao/javet/interop/IV8Cloneable.java new file mode 100644 index 000000000..42e442563 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/IV8Cloneable.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; + +@SuppressWarnings("unchecked") +public interface IV8Cloneable { + T toClone() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/interop/IV8Creatable.java b/src/main/java/com/caoccao/javet/interop/IV8Creatable.java new file mode 100644 index 000000000..a0813981d --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/IV8Creatable.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.V8CallbackContext; +import com.caoccao.javet.values.reference.*; + +import java.lang.reflect.Method; + +public interface IV8Creatable { + V8ValueArray createV8ValueArray() throws JavetException; + + V8ValueFunction createV8ValueFunction(V8CallbackContext v8CallbackContext) throws JavetException; + + V8ValueMap createV8ValueMap() throws JavetException; + + V8ValueObject createV8ValueObject() throws JavetException; + + V8ValueSet createV8ValueSet() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/interop/IV8Executable.java b/src/main/java/com/caoccao/javet/interop/IV8Executable.java new file mode 100644 index 000000000..22c59abe5 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/IV8Executable.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.executors.IV8Executor; +import com.caoccao.javet.values.V8Value; + +import java.io.File; +import java.nio.file.Path; + +@SuppressWarnings("unchecked") +public interface IV8Executable { + void compileOnly(String scriptString, V8ScriptOrigin v8ScriptOrigin) throws JavetException; + + T execute( + String scriptString, V8ScriptOrigin v8ScriptOrigin, boolean resultRequired) throws JavetException; + + default IV8Executor getExecutor(File scriptFile) { + return getExecutor(scriptFile.toPath()); + } + + IV8Executor getExecutor(Path scriptPath); + + IV8Executor getExecutor(String scriptString); +} diff --git a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java new file mode 100644 index 000000000..d1b1b84eb --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java @@ -0,0 +1,124 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.exceptions.JavetOSNotSupportedException; +import com.caoccao.javet.utils.JavetDefaultLogger; +import com.caoccao.javet.utils.JavetOSUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.nio.file.Paths; +import java.text.MessageFormat; + +final class JavetLibLoader { + private static final String CHMOD = "chmod"; + private static final String XRR = "755"; + private static final String LIB_VERSION = "0.7.0"; + private static final String LIB_FILE_NAME_FORMAT = "libjavet-{0}-x86_64.v.{1}.{2}"; + private static final String RESOURCE_NAME_FORMAT = "/{0}"; + private static final String LIB_FILE_EXTENSION_LINUX = "so"; + private static final String LIB_FILE_EXTENSION_WINDOWS = "dll"; + private static final String OS_LINUX = "linux"; + private static final String OS_WINDOWS = "windows"; + private static int BUFFER_LENGTH = 4096; + + private static Object lockObject = new Object(); + private static boolean javetLibLoaded = false; + + private JavetLibLoader() { + } + + static boolean load() throws JavetOSNotSupportedException, JavetIOException { + if (!javetLibLoaded) { + synchronized (lockObject) { + if (!javetLibLoaded) { + internalLoad(); + javetLibLoaded = true; + } + } + } + return javetLibLoaded; + } + + private static boolean deployLibFile(File libFile) { + boolean isDeployed = false; + boolean isLibFileLocked = false; + if (libFile.exists()) { + try { + libFile.delete(); + } catch (Exception e) { + isLibFileLocked = true; + } + } + if (!isLibFileLocked) { + byte[] buffer = new byte[BUFFER_LENGTH]; + String resourceName = MessageFormat.format(RESOURCE_NAME_FORMAT, libFile.getName()); + try (InputStream inputStream = JavetLibLoader.class.getResourceAsStream(resourceName); + FileOutputStream outputStream = new FileOutputStream(libFile.getAbsolutePath())) { + while (true) { + int length = inputStream.read(buffer); + if (length == -1) { + break; + } + outputStream.write(buffer, 0, length); + } + isDeployed = true; + } catch (Exception e) { + // Lib file is locked. + } + if (isDeployed && JavetOSUtils.IS_LINUX) { + try { + Runtime.getRuntime().exec(new String[]{CHMOD, XRR, libFile.getAbsolutePath()}).waitFor(); + } catch (Throwable e) { + } + } + } + return isDeployed; + } + + private static File getLibFile(String rootDirectory) throws JavetOSNotSupportedException { + if (JavetOSUtils.IS_WINDOWS) { + return new File( + rootDirectory, + MessageFormat.format( + LIB_FILE_NAME_FORMAT, + OS_WINDOWS, LIB_VERSION, LIB_FILE_EXTENSION_WINDOWS)); + } else if (JavetOSUtils.IS_LINUX) { + return new File( + rootDirectory, + MessageFormat.format( + LIB_FILE_NAME_FORMAT, + OS_LINUX, LIB_VERSION, LIB_FILE_EXTENSION_LINUX)); + } else { + throw new JavetOSNotSupportedException(JavetOSUtils.OS_NAME); + } + } + + private static void internalLoad() throws JavetOSNotSupportedException, JavetIOException { + File tempDirectoryLibFile = getLibFile(JavetOSUtils.TEMP_DIRECTORY); + try { + deployLibFile(tempDirectoryLibFile); + System.load(tempDirectoryLibFile.getAbsolutePath()); + } catch (Throwable t) { + throw JavetIOException.failedToReadPath(tempDirectoryLibFile.toPath(), t); + } + } +} diff --git a/src/main/java/com/caoccao/javet/interop/V8Flags.java b/src/main/java/com/caoccao/javet/interop/V8Flags.java new file mode 100644 index 000000000..cae51d063 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/V8Flags.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop; + +public final class V8Flags { + private boolean allowNativesSyntax; + private boolean exposeGC; + private boolean sealed; + private boolean trackRetainingPath; + private boolean useStrict; + + V8Flags() { + allowNativesSyntax = false; + exposeGC = false; + sealed = false; + trackRetainingPath = false; + useStrict = true; + } + + public boolean isTrackRetainingPath() { + return trackRetainingPath; + } + + public void setTrackRetainingPath(boolean trackRetainingPath) { + if (!sealed) { + this.trackRetainingPath = trackRetainingPath; + } + } + + public boolean isSealed() { + return sealed; + } + + public void seal() { + if (!sealed) { + sealed = true; + } + } + + public boolean isExposeGC() { + return exposeGC; + } + + public void setExposeGC(boolean exposeGC) { + if (!sealed) { + this.exposeGC = exposeGC; + } + } + + public boolean isUseStrict() { + return useStrict; + } + + public void setUseStrict(boolean useStrict) { + if (!sealed) { + this.useStrict = useStrict; + } + } + + public boolean isAllowNativesSyntax() { + return allowNativesSyntax; + } + + public void setAllowNativesSyntax(boolean allowNativesSyntax) { + if (!sealed) { + this.allowNativesSyntax = allowNativesSyntax; + } + } +} diff --git a/src/main/java/com/caoccao/javet/interop/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java new file mode 100644 index 000000000..6a4d86d2b --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -0,0 +1,158 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.exceptions.JavetOSNotSupportedException; +import com.caoccao.javet.exceptions.JavetV8RuntimeLeakException; +import com.caoccao.javet.interfaces.IJavetLogger; +import com.caoccao.javet.utils.JavetDefaultLogger; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public final class V8Host implements AutoCloseable { + private static final long INVALID_HANDLE = 0L; + private static final String FLAG_ALLOW_NATIVES_SYNTAX = "--allow-natives-syntax"; + private static final String FLAG_EXPOSE_GC = "--expose_gc"; + private static final String FLAG_TRACK_RETAINING_PATH = "--track-retaining-path"; + private static final String FLAG_USE_STRICT = "--use_strict"; + private static final String SPACE = " "; + private static V8Host instance = new V8Host(); + + private boolean closed; + private boolean libLoaded; + private boolean isolateCreated; + private ConcurrentHashMap v8RuntimeMap; + private JavetException lastException; + private V8Flags flags; + private IJavetLogger logger; + + private V8Host() { + closed = true; + libLoaded = false; + lastException = null; + flags = new V8Flags(); + logger = new JavetDefaultLogger(getClass().getName()); + v8RuntimeMap = new ConcurrentHashMap<>(); + try { + libLoaded = JavetLibLoader.load(); + closed = false; + } catch (JavetOSNotSupportedException | JavetIOException e) { + logger.logError(e, "Failed to load Javet lib with error {0}.", e.getMessage()); + lastException = e; + } + isolateCreated = false; + } + + public static V8Host getInstance() { + return instance; + } + + public V8Runtime createV8Runtime() { + return createV8Runtime(null); + } + + public V8Flags getFlags() { + return flags; + } + + public V8Runtime createV8Runtime(String globalName) { + return createV8Runtime(false, globalName); + } + + public V8Runtime createV8Runtime(boolean pooled, String globalName) { + if (closed) { + return null; + } + final long handle = V8Native.createV8Runtime(globalName); + isolateCreated = true; + flags.seal(); + V8Runtime v8Runtime = new V8Runtime(this, handle, pooled, globalName); + v8RuntimeMap.put(handle, v8Runtime); + return v8Runtime; + } + + public void closeV8Runtime(V8Runtime v8Runtime) { + if (closed) { + return; + } + if (v8Runtime != null) { + final long handle = v8Runtime.getHandle(); + if (handle > INVALID_HANDLE && v8RuntimeMap.containsKey(handle)) { + V8Native.closeV8Runtime(v8Runtime.getHandle()); + v8RuntimeMap.remove(handle); + } + } + } + + public int getV8RuntimeCount() { + return v8RuntimeMap.size(); + } + + public boolean isLibLoaded() { + return libLoaded; + } + + public JavetException getLastException() { + return lastException; + } + + public boolean isIsolateCreated() { + return isolateCreated; + } + + public boolean isClosed() { + return closed; + } + + public boolean setFlags() { + if (!closed && libLoaded && !isolateCreated) { + List flags = new ArrayList<>(); + if (this.flags.isAllowNativesSyntax()) { + flags.add(FLAG_ALLOW_NATIVES_SYNTAX); + } + if (this.flags.isExposeGC()) { + flags.add(FLAG_EXPOSE_GC); + } + if (this.flags.isUseStrict()) { + flags.add(FLAG_USE_STRICT); + } + if (this.flags.isTrackRetainingPath()) { + flags.add(FLAG_TRACK_RETAINING_PATH); + } + V8Native.setFlags(String.join(SPACE, flags)); + return true; + } + return false; + } + + @Override + public void close() throws JavetException { + if (closed) { + return; + } + closed = true; + final int v8RuntimeCount = getV8RuntimeCount(); + if (v8RuntimeCount != 0) { + throw new JavetV8RuntimeLeakException(v8RuntimeCount); + } + } +} diff --git a/src/main/java/com/caoccao/javet/interop/V8Native.java b/src/main/java/com/caoccao/javet/interop/V8Native.java new file mode 100644 index 000000000..ccc179f19 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/V8Native.java @@ -0,0 +1,122 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +/** + * The type V8 native is the pure interface that defines the JNI C++ implementation. + *

+ * Guidelines: + * 1. Please keep V8Native as small, simple as possible so that the C++ implementation is minimized. + * 2. Please make sure V8Native doesn't not reference any other types so that JNI code generation is quick and clean. + * 3. Please keep the methods in ascending order so that the generated .h file keeps the same order. + * 4. Please keep all methods be static. + * 5. Please don't not inject any other non-native code. + */ +final class V8Native { + private V8Native() { + } + + native static void add(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object value); + + native static Object call( + long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, + Object receiver, boolean returnResult, Object[] values); + + native static void clearWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static Object cloneV8Value( + long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static void closeV8Runtime(long v8RuntimeHandle); + + native static void compileOnly( + long v8RuntimeHandle, String script, + String resourceName, int resourceLineOffset, int resourceColumnOffset, + int scriptId, boolean isWASM, boolean isModule); + + native static long createV8Runtime(String globalName); + + native static Object createV8Value(long v8RuntimeHandle, int v8ValueType, Object context); + + native static boolean delete(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + + native static Object execute( + long v8RuntimeHandle, String script, boolean returnResult, + String resourceName, int resourceLineOffset, int resourceColumnOffset, + int scriptId, boolean isWASM, boolean isModule); + + native static Object get(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + + native static Object getGlobalObject(long v8RuntimeHandle); + + native static int getLength(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static int getSize(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static Object getOwnPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static Object getPropertyNames(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static Object getProperty(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + + native static String getVersion(); + + native static boolean has(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object value); + + native static boolean hasOwnProperty(long v8RuntimeHandle, long v8ValueHandle, int type, Object key); + + native static Object invoke( + long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, + String functionName, boolean returnResult, Object[] values); + + native static boolean isWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static void lockV8Runtime(long v8RuntimeHandle); + + native static void removeJNIGlobalRef(long handle); + + native static void removeReferenceHandle(long referenceHandle); + + native static void requestGarbageCollectionForTesting(long v8RuntimeHandle, boolean fullGC); + + native static void resetV8Context(long v8RuntimeHandle, String globalName); + + native static void resetV8Isolate(long v8RuntimeHandle, String globalName); + + native static boolean set(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key, Object value); + + /** + * Sets flags. + *

+ * Famous flags: + * --use_strict type: bool default: false + * + * @param flags the flags + */ + native static void setFlags(String flags); + + native static boolean setProperty(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key, Object value); + + native static void setWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object objectReference); + + native static String toProtoString(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static String toString(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + + native static void unlockV8Runtime(long v8RuntimeHandle); +} diff --git a/src/main/java/com/caoccao/javet/interop/V8Runtime.java b/src/main/java/com/caoccao/javet/interop/V8Runtime.java new file mode 100644 index 000000000..5e256e60e --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/V8Runtime.java @@ -0,0 +1,444 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetV8RuntimeAlreadyClosedException; +import com.caoccao.javet.exceptions.JavetV8RuntimeAlreadyRegisteredException; +import com.caoccao.javet.exceptions.JavetV8RuntimeLockConflictException; +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.interfaces.IJavetLogger; +import com.caoccao.javet.interop.executors.IV8Executor; +import com.caoccao.javet.interop.executors.V8PathExecutor; +import com.caoccao.javet.interop.executors.V8StringExecutor; +import com.caoccao.javet.utils.JavetDefaultLogger; +import com.caoccao.javet.utils.V8CallbackContext; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.reference.*; + +import java.nio.file.Path; +import java.util.Map; +import java.util.TreeMap; + +@SuppressWarnings("unchecked") +public final class V8Runtime implements + IJavetClosable, IV8Executable, IV8Creatable { + private static final long INVALID_THREAD_ID = -1L; + private static final long INVALID_HANDLE = 0L; + + private String globalName; + private long handle; + private IJavetLogger logger; + private boolean pooled; + private Map referenceMap; + private long threadId; + private V8Host v8Host; + + V8Runtime(V8Host v8Host, long handle, boolean pooled, String globalName) { + assert handle != 0; + this.globalName = globalName; + this.handle = handle; + logger = new JavetDefaultLogger(getClass().getName()); + this.pooled = pooled; + referenceMap = new TreeMap<>(); + threadId = INVALID_THREAD_ID; + this.v8Host = v8Host; + } + + public void add(IV8ValueSet iV8ValueKeySet, V8Value value) throws JavetException { + checkLock(); + decorateV8Value(value); + V8Native.add(handle, iV8ValueKeySet.getHandle(), iV8ValueKeySet.getType(), value); + } + + public void addReference(IV8ValueReference iV8ValueReference) throws + JavetV8RuntimeAlreadyClosedException, JavetV8RuntimeLockConflictException { + checkLock(); + referenceMap.put(iV8ValueReference.getHandle(), iV8ValueReference); + } + + public T call( + IV8ValueObject iV8ValueObject, IV8ValueObject receiver, boolean returnResult, V8Value... v8Values) + throws JavetException { + checkLock(); + decorateV8Values(v8Values); + return decorateV8Value((T) V8Native.call( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), receiver, returnResult, v8Values)); + } + + public void checkLock() throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + if (handle == INVALID_HANDLE) { + throw new JavetV8RuntimeAlreadyClosedException(); + } + final long currentThreadId = Thread.currentThread().getId(); + if (threadId != currentThreadId) { + throw new JavetV8RuntimeLockConflictException(threadId, currentThreadId); + } + } + + public void clearWeak(IV8ValueReference iV8ValueReference) throws JavetException { + checkLock(); + V8Native.clearWeak(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType()); + } + + public T cloneV8Value(IV8ValueReference iV8ValueReference) throws JavetException { + checkLock(); + return decorateV8Value((T) V8Native.cloneV8Value( + handle, iV8ValueReference.getHandle(), iV8ValueReference.getType())); + } + + @Override + public void close() throws JavetException { + if (pooled) { + close(false); + } else { + close(true); + } + } + + public void close(boolean forceClose) throws JavetException { + if (handle != INVALID_HANDLE && forceClose) { + removeReferences(); + if (isLocked()) { + try { + unlock(); + } catch (JavetV8RuntimeLockConflictException e) { + } + } + v8Host.closeV8Runtime(this); + handle = INVALID_HANDLE; + } + } + + @Override + public void compileOnly(String scriptString, V8ScriptOrigin v8ScriptOrigin) throws JavetException { + checkLock(); + V8Native.compileOnly( + handle, scriptString, v8ScriptOrigin.getResourceName(), + v8ScriptOrigin.getResourceLineOffset(), v8ScriptOrigin.getResourceColumnOffset(), + v8ScriptOrigin.getScriptId(), v8ScriptOrigin.isWasm(), v8ScriptOrigin.isModule()); + } + + @Override + public V8ValueArray createV8ValueArray() throws JavetException { + checkLock(); + return decorateV8Value((V8ValueArray) V8Native.createV8Value(handle, V8ValueReferenceType.Array, null)); + } + + @Override + public V8ValueFunction createV8ValueFunction(V8CallbackContext v8CallbackContext) throws JavetException { + checkLock(); + V8ValueFunction v8ValueFunction = decorateV8Value( + (V8ValueFunction) V8Native.createV8Value(handle, V8ValueReferenceType.Function, v8CallbackContext)); + v8ValueFunction.setV8CallbackContext(v8CallbackContext); + return v8ValueFunction; + } + + @Override + public V8ValueMap createV8ValueMap() throws JavetException { + checkLock(); + return decorateV8Value((V8ValueMap) V8Native.createV8Value(handle, V8ValueReferenceType.Map, null)); + } + + @Override + public V8ValueObject createV8ValueObject() throws JavetException { + checkLock(); + return decorateV8Value((V8ValueObject) V8Native.createV8Value(handle, V8ValueReferenceType.Object, null)); + } + + @Override + public V8ValueSet createV8ValueSet() throws JavetException { + checkLock(); + return decorateV8Value((V8ValueSet) V8Native.createV8Value(handle, V8ValueReferenceType.Set, null)); + } + + public T decorateV8Value(T v8Value) throws JavetException { + if (v8Value != null) { + if (v8Value.getV8Runtime() == null) { + v8Value.setV8Runtime(this); + } else if (v8Value.getV8Runtime() != this) { + throw new JavetV8RuntimeAlreadyRegisteredException(); + } + } + return v8Value; + } + + public int decorateV8Values(T... v8Values) throws JavetException { + if (v8Values != null && v8Values.length > 0) { + for (T v8Value : v8Values) { + decorateV8Value(v8Value); + } + return v8Values.length; + } + return 0; + } + + public boolean delete(IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { + checkLock(); + decorateV8Value(key); + return V8Native.delete(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key); + } + + @Override + public T execute( + String scriptString, V8ScriptOrigin v8ScriptOrigin, boolean resultRequired) throws JavetException { + checkLock(); + return decorateV8Value((T) V8Native.execute( + handle, scriptString, resultRequired, v8ScriptOrigin.getResourceName(), + v8ScriptOrigin.getResourceLineOffset(), v8ScriptOrigin.getResourceColumnOffset(), + v8ScriptOrigin.getScriptId(), v8ScriptOrigin.isWasm(), v8ScriptOrigin.isModule())); + } + + public T get( + IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { + checkLock(); + return decorateV8Value((T) V8Native.get( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key)); + } + + @Override + public IV8Executor getExecutor(Path scriptPath) { + return new V8PathExecutor(this, scriptPath); + } + + @Override + public IV8Executor getExecutor(String scriptString) { + return new V8StringExecutor(this, scriptString); + } + + public String getGlobalName() { + return globalName; + } + + public IJavetLogger getLogger() { + return logger; + } + + public V8ValueGlobalObject getGlobalObject() throws JavetException { + return decorateV8Value((V8ValueGlobalObject) V8Native.getGlobalObject(handle)); + } + + public long getHandle() { + return handle; + } + + public int getLength(IV8ValueArray iV8ValueArray) throws JavetException { + checkLock(); + return V8Native.getLength(handle, iV8ValueArray.getHandle(), iV8ValueArray.getType()); + } + + public IV8ValueArray getOwnPropertyNames( + IV8ValueObject iV8ValueObject) throws JavetException { + checkLock(); + return decorateV8Value((V8ValueArray) V8Native.getOwnPropertyNames( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType())); + } + + public T getProperty( + IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { + checkLock(); + decorateV8Value(key); + return decorateV8Value((T) V8Native.getProperty( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key)); + } + + public IV8ValueArray getPropertyNames( + IV8ValueObject iV8ValueObject) throws JavetException { + checkLock(); + return decorateV8Value((V8ValueArray) V8Native.getPropertyNames( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType())); + } + + public int getReferenceCount() + throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + checkLock(); + return referenceMap.size(); + } + + public int getSize(IV8ValueKeyContainer iV8ValueKeyContainer) throws JavetException { + checkLock(); + return V8Native.getSize(handle, iV8ValueKeyContainer.getHandle(), iV8ValueKeyContainer.getType()); + } + + public boolean hasOwnProperty(IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { + checkLock(); + decorateV8Value(key); + return V8Native.hasOwnProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key); + } + + public boolean has(IV8ValueKeyContainer iV8ValueKeyContainer, V8Value value) throws JavetException { + checkLock(); + decorateV8Value(value); + return V8Native.has(handle, iV8ValueKeyContainer.getHandle(), iV8ValueKeyContainer.getType(), value); + } + + public T invoke( + IV8ValueObject iV8ValueObject, String functionName, boolean returnResult, V8Value... v8Values) + throws JavetException { + checkLock(); + decorateV8Values(v8Values); + return decorateV8Value((T) V8Native.invoke( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), functionName, returnResult, v8Values)); + } + + public boolean isLocked() { + return threadId != INVALID_THREAD_ID; + } + + public boolean isPooled() { + return pooled; + } + + public boolean isWeak(IV8ValueReference iV8ValueReference) throws JavetException { + checkLock(); + return V8Native.isWeak(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType()); + } + + public void lock() throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + if (!isLocked()) { + if (handle == INVALID_HANDLE) { + throw new JavetV8RuntimeAlreadyClosedException(); + } + V8Native.lockV8Runtime(handle); + threadId = Thread.currentThread().getId(); + } else { + checkLock(); + } + } + + public void removeJNIGlobalRef(long handle) { + if (handle != INVALID_HANDLE) { + V8Native.removeJNIGlobalRef(handle); + } + } + + public void removeReference(IV8ValueReference iV8ValueReference) + throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + checkLock(); + final long referenceHandle = iV8ValueReference.getHandle(); + if (referenceMap.containsKey(referenceHandle)) { + V8Native.removeReferenceHandle(referenceHandle); + referenceMap.remove(referenceHandle); + } + } + + private void removeReferences() throws JavetException { + if (!referenceMap.isEmpty()) { + if (!isLocked()) { + lock(); + } + int weakReferenceCount = 0; + for (IV8ValueReference iV8ValueReference : referenceMap.values()) { + if (iV8ValueReference.isWeak()) { + ++weakReferenceCount; + } + removeReference(iV8ValueReference); + } + logger.logWarn("{0}/{1} V8 object(s) not recycled.", weakReferenceCount, referenceMap.size()); + referenceMap.clear(); + } + } + + /** + * Requests GC for testing. + * Note: --expose_gc must be set. + * + * @param fullGC true = Full GC, false = Minor GC + */ + public void requestGarbageCollectionForTesting(boolean fullGC) { + V8Native.requestGarbageCollectionForTesting(handle, fullGC); + } + + /** + * Reset V8 context. + * This is a light-weight and recommended reset. + * + * @throws JavetException the javet exception + */ + public void resetContext() throws JavetException { + removeReferences(); + if (isLocked()) { + try { + unlock(); + } catch (JavetV8RuntimeLockConflictException e) { + } + } + V8Native.resetV8Context(handle, globalName); + } + + /** + * Reset V8 isolate. + * This is a heavy reset. Please avoid using it in performance sensitive scenario. + * + * @throws JavetException the javet exception + */ + public void resetIsolate() throws JavetException { + removeReferences(); + if (isLocked()) { + try { + unlock(); + } catch (JavetV8RuntimeLockConflictException e) { + } + } + V8Native.resetV8Isolate(handle, globalName); + } + + public boolean set(IV8ValueObject iV8ValueObject, V8Value key, V8Value value) throws JavetException { + checkLock(); + decorateV8Values(key, value); + return V8Native.set(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key, value); + } + + public void setGlobalName(String globalName) { + this.globalName = globalName; + } + + public void setLogger(IJavetLogger logger) { + this.logger = logger; + } + + public boolean setProperty(IV8ValueObject iV8ValueObject, V8Value key, V8Value value) throws JavetException { + checkLock(); + decorateV8Values(key, value); + return V8Native.setProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key, value); + } + + public void setWeak(IV8ValueReference iV8ValueReference) throws JavetException { + checkLock(); + V8Native.setWeak(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType(), iV8ValueReference); + } + + public String toProtoString(IV8ValueReference iV8ValueReference) + throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + checkLock(); + return V8Native.toProtoString(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType()); + } + + public String toString(IV8ValueReference iV8ValueReference) + throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + checkLock(); + return V8Native.toString(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType()); + } + + public void unlock() throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { + checkLock(); + threadId = INVALID_THREAD_ID; + V8Native.unlockV8Runtime(handle); + } +} diff --git a/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java b/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java new file mode 100644 index 000000000..5f3a3b5e3 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/V8ScriptOrigin.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +public final class V8ScriptOrigin { + + private String resourceName; + private int resourceLineOffset; + private int resourceColumnOffset; + private int scriptId; + private boolean wasm; + private boolean module; + + public V8ScriptOrigin( + String resourceName, + int resourceLineOffset, + int resourceColumnOffset, + int scriptId, + boolean wasm, + boolean module) { + this.resourceName = resourceName; + this.resourceLineOffset = resourceLineOffset; + this.resourceColumnOffset = resourceColumnOffset; + this.scriptId = scriptId; + this.wasm = wasm; + this.module = module; + } + + public V8ScriptOrigin() { + this(null); + } + + public V8ScriptOrigin( + String resourceName) { + this(resourceName, 0, 0); + } + + public V8ScriptOrigin( + String resourceName, + int resourceLineOffset, + int resourceColumnOffset) { + this(resourceName, resourceLineOffset, resourceColumnOffset, + 0, false, false); + } + + public V8ScriptOrigin( + String resourceName, + int scriptId) { + this(resourceName, 0, 0, + scriptId, false, false); + } + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public int getResourceLineOffset() { + return resourceLineOffset; + } + + public void setResourceLineOffset(int resourceLineOffset) { + this.resourceLineOffset = resourceLineOffset; + } + + public int getResourceColumnOffset() { + return resourceColumnOffset; + } + + public void setResourceColumnOffset(int resourceColumnOffset) { + this.resourceColumnOffset = resourceColumnOffset; + } + + public int getScriptId() { + return scriptId; + } + + public void setScriptId(int scriptId) { + this.scriptId = scriptId; + } + + public boolean isWasm() { + return wasm; + } + + public void setWasm(boolean wasm) { + this.wasm = wasm; + } + + public boolean isModule() { + return module; + } + + public void setModule(boolean module) { + this.module = module; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java new file mode 100644 index 000000000..d3b0386d3 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.interop.V8Runtime; + +public interface IJavetEngine extends IJavetClosable { + V8Runtime getV8Runtime() throws JavetException; + + boolean isActive(); +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java new file mode 100644 index 000000000..34cba8859 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.interfaces.IJavetClosable; + +public interface IJavetEnginePool extends IJavetClosable { + int getActiveEngineCount(); + + JavetEngineConfig getConfig(); + + IJavetEngine getEngine(); + + int getIdleEngineCount(); + + void releaseEngine(IJavetEngine engine); +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java new file mode 100644 index 000000000..2011bfc77 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.JavetDateTimeUtils; + +import java.time.ZonedDateTime; + +class JavetEngine implements IJavetEngine { + protected boolean active; + protected IJavetEnginePool iJavetEnginePool; + protected JavetEngineUsage usage; + protected V8Runtime v8Runtime; + + public JavetEngine(IJavetEnginePool iJavetEnginePool) { + this.iJavetEnginePool = iJavetEnginePool; + usage = new JavetEngineUsage(); + setActive(false); + } + + @Override + public void close() throws JavetException { + close(false); + } + + protected void close(boolean forceClose) throws JavetException { + if (v8Runtime.isLocked()) { + v8Runtime.unlock(); + } + setActive(false); + if (forceClose) { + v8Runtime.close(true); + } else { + iJavetEnginePool.releaseEngine(this); + } + } + + protected JavetEngineUsage getUsage() { + return usage; + } + + @Override + public V8Runtime getV8Runtime() throws JavetException { + setActive(true); + if (!v8Runtime.isLocked()) { + v8Runtime.lock(); + } + return v8Runtime; + } + + protected void setV8Runtime(V8Runtime v8Runtime) { + this.v8Runtime = v8Runtime; + } + + /** + * Gets utc now. It's designed for mocking the time in test scenario. + * + * @return the utc now + */ + protected ZonedDateTime getUTCNow() { + return JavetDateTimeUtils.getUTCNow(); + } + + protected void resetContext() throws JavetException { + v8Runtime.resetContext(); + usage.reset(); + if (active) { + v8Runtime.lock(); + } + } + + protected void resetIsolate() throws JavetException { + v8Runtime.resetIsolate(); + usage.reset(); + if (active) { + v8Runtime.lock(); + } + } + + protected void touchLastActiveZonedDateTime() { + usage.setLastActiveZonedDatetime(getUTCNow()); + } + + @Override + public boolean isActive() { + return active; + } + + protected void setActive(boolean active) { + this.active = active; + touchLastActiveZonedDateTime(); + } + +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java new file mode 100644 index 000000000..8f4f2d63e --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.interfaces.IJavetLogger; +import com.caoccao.javet.utils.JavetDefaultLogger; +import com.caoccao.javet.utils.JavetOSUtils; + +import java.util.Objects; + +public final class JavetEngineConfig { + public static final int DEFAULT_MAX_ENGINE_USED_COUNT = 100; + public static final int DEFAULT_POOL_MIN_SIZE = 1; + public static final int DEFAULT_POOL_IDLE_TIMEOUT_SECONDS = 60; + public static final int DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS = 1000; + public static final int DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS = 3600; + public static final String DEFAULT_GLOBAL_NAME = "window"; + public static IJavetLogger DEFAULT_JAVET_LOGGER = new JavetDefaultLogger(JavetEnginePool.class.getName()); + private IJavetLogger javetLogger; + private String globalName; + private int maxEngineUsedCount; + private int poolMaxSize; + private int poolMinSize; + private int poolIdleTimeoutSeconds; + private int poolDaemonCheckIntervalMillis; + private int resetEngineTimeoutSeconds; + + public JavetEngineConfig() { + reset(); + } + + public void reset() { + javetLogger = DEFAULT_JAVET_LOGGER; + globalName = DEFAULT_GLOBAL_NAME; + maxEngineUsedCount = DEFAULT_MAX_ENGINE_USED_COUNT; + final int cpuCount = JavetOSUtils.getCPUCount(); + poolMinSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount >> 1); + poolMaxSize = Math.max(DEFAULT_POOL_MIN_SIZE, cpuCount); + poolIdleTimeoutSeconds = DEFAULT_POOL_IDLE_TIMEOUT_SECONDS; + poolDaemonCheckIntervalMillis = DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS; + resetEngineTimeoutSeconds = DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS; + } + + public String getGlobalName() { + return globalName; + } + + public void setGlobalName(String globalName) { + this.globalName = globalName; + } + + public int getResetEngineTimeoutSeconds() { + return resetEngineTimeoutSeconds; + } + + public void setResetEngineTimeoutSeconds(int resetEngineTimeoutSeconds) { + this.resetEngineTimeoutSeconds = resetEngineTimeoutSeconds; + } + + public int getMaxEngineUsedCount() { + return maxEngineUsedCount; + } + + public void setMaxEngineUsedCount(int maxEngineUsedCount) { + this.maxEngineUsedCount = maxEngineUsedCount; + } + + public IJavetLogger getJavetLogger() { + return javetLogger; + } + + public void setJavetLogger(IJavetLogger javetLogger) { + Objects.requireNonNull(javetLogger); + this.javetLogger = javetLogger; + } + + public int getPoolMaxSize() { + return poolMaxSize; + } + + public void setPoolMaxSize(int poolMaxSize) { + this.poolMaxSize = poolMaxSize; + } + + public int getPoolMinSize() { + return poolMinSize; + } + + public void setPoolMinSize(int poolMinSize) { + this.poolMinSize = poolMinSize; + } + + public int getPoolIdleTimeoutSeconds() { + return poolIdleTimeoutSeconds; + } + + public void setPoolIdleTimeoutSeconds(int poolIdleTimeoutSeconds) { + this.poolIdleTimeoutSeconds = poolIdleTimeoutSeconds; + } + + public int getPoolDaemonCheckIntervalMillis() { + return poolDaemonCheckIntervalMillis; + } + + public void setPoolDaemonCheckIntervalMillis(int poolDaemonCheckIntervalMillis) { + this.poolDaemonCheckIntervalMillis = poolDaemonCheckIntervalMillis; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java new file mode 100644 index 000000000..1fdd175c7 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.utils.JavetDateTimeUtils; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.LinkedList; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +public class JavetEnginePool implements IJavetEnginePool, Runnable { + protected JavetEngineConfig config; + protected LinkedList activeEngineList; + protected Thread daemonThread; + protected LinkedList idleEngineList; + protected Object externalLock; + protected Object internalLock; + protected boolean active; + protected boolean quitting; + + public JavetEnginePool() { + this(new JavetEngineConfig()); + } + + public JavetEnginePool(JavetEngineConfig config) { + Objects.requireNonNull(config); + this.config = config; + activeEngineList = new LinkedList<>(); + idleEngineList = new LinkedList<>(); + externalLock = new Object(); + internalLock = new Object(); + active = false; + quitting = false; + startDaemon(); + } + + protected JavetEngine createEngine() { + JavetEngine engine = new JavetEngine(this); + engine.setV8Runtime(V8Host.getInstance().createV8Runtime(true, config.getGlobalName())); + return engine; + } + + @Override + public void close() throws JavetException { + stopDaemon(); + } + + @Override + public int getActiveEngineCount() { + synchronized (internalLock) { + return activeEngineList.size(); + } + } + + @Override + public JavetEngineConfig getConfig() { + return config; + } + + @Override + public IJavetEngine getEngine() { + config.getJavetLogger().debug("JavetEnginePool.getEngine() begins."); + JavetEngine engine = null; + while (!quitting) { + synchronized (internalLock) { + if (idleEngineList.isEmpty()) { + if (getActiveEngineCount() < config.getPoolMaxSize()) { + engine = createEngine(); + } + } else { + engine = idleEngineList.pop(); + } + if (engine != null) { + engine.setActive(true); + activeEngineList.push(engine); + break; + } + } + try { + TimeUnit.MILLISECONDS.sleep(config.getPoolDaemonCheckIntervalMillis()); + } catch (InterruptedException e) { + config.getJavetLogger().logError(e, "Failed to sleep a while to wait for an idle engine."); + } + } + JavetEngineUsage usage = engine.getUsage(); + usage.increaseUsedCount(); + config.getJavetLogger().debug("JavetEnginePool.getEngine() ends."); + return engine; + } + + @Override + public int getIdleEngineCount() { + synchronized (internalLock) { + return idleEngineList.size(); + } + } + + protected ZonedDateTime getUTCNow() { + return JavetDateTimeUtils.getUTCNow(); + } + + public boolean isActive() { + return active; + } + + @Override + public void releaseEngine(IJavetEngine engine) { + config.getJavetLogger().debug("JavetEnginePool.releaseEngine() begins."); + synchronized (externalLock) { + externalLock.notify(); + } + config.getJavetLogger().debug("JavetEnginePool.releaseEngine() ends."); + } + + @Override + public void run() { + config.getJavetLogger().debug("JavetEnginePool.run() begins."); + while (!quitting) { + synchronized (internalLock) { + if (!activeEngineList.isEmpty()) { + final int activeEngineCount = getActiveEngineCount(); + for (int i = 0; i < activeEngineCount; ++i) { + JavetEngine engine = activeEngineList.pop(); + if (engine.isActive()) { + activeEngineList.push(engine); + } else { + JavetEngineUsage usage = engine.getUsage(); + ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() + .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); + if (usage.getEngineUsedCount() >= config.getMaxEngineUsedCount() || + resetEngineZonedDateTime.isBefore(getUTCNow())) { + try { + config.getJavetLogger().debug("JavetEnginePool reset engine begins."); + engine.resetIsolate(); + config.getJavetLogger().debug("JavetEnginePool reset engine ends."); + } catch (Exception e) { + config.getJavetLogger().logError(e, "Failed to reset idle engine."); + } + } + idleEngineList.push(engine); + } + } + } + if (!idleEngineList.isEmpty()) { + final int idleEngineCount = getIdleEngineCount(); + for (int i = 0; i < idleEngineCount; ++i) { + if (getIdleEngineCount() <= config.getPoolMinSize()) { + break; + } + JavetEngine engine = idleEngineList.pop(); + JavetEngineUsage usage = engine.getUsage(); + ZonedDateTime expirationZonedDateTime = usage.getLastActiveZonedDatetime() + .plus(config.getPoolIdleTimeoutSeconds(), ChronoUnit.SECONDS); + if (expirationZonedDateTime.isBefore(getUTCNow())) { + try { + engine.close(true); + } catch (Throwable t) { + config.getJavetLogger().logError(t, "Failed to release idle engine."); + } + } else { + idleEngineList.push(engine); + } + } + } + } + synchronized (externalLock) { + try { + externalLock.wait(config.getPoolDaemonCheckIntervalMillis()); + } catch (InterruptedException e) { + config.getJavetLogger().logError(e, + "Failed to sleep a while to wait for next round in Javet engine pool daemon."); + } + } + } + config.getJavetLogger().debug("JavetEnginePool daemon is quitting."); + synchronized (internalLock) { + if (!idleEngineList.isEmpty()) { + final int idleEngineCount = getIdleEngineCount(); + for (int i = 0; i < idleEngineCount; ++i) { + JavetEngine engine = idleEngineList.pop(); + try { + engine.close(true); + } catch (Throwable t) { + config.getJavetLogger().logError(t, "Failed to release idle engine."); + } + } + } + while (!activeEngineList.isEmpty()) { + final int activeEngineCount = getActiveEngineCount(); + for (int i = 0; i < activeEngineCount; ++i) { + JavetEngine engine = activeEngineList.pop(); + try { + engine.close(true); + } catch (Throwable t) { + config.getJavetLogger().logError(t, "Failed to release active engine."); + } + } + } + } + config.getJavetLogger().debug("JavetEnginePool.run() ends."); + } + + protected void startDaemon() { + config.getJavetLogger().debug("JavetEnginePool.startDaemon() begins."); + activeEngineList.clear(); + idleEngineList.clear(); + quitting = false; + daemonThread = new Thread(this); + daemonThread.start(); + active = true; + config.getJavetLogger().debug("JavetEnginePool.startDaemon() ends."); + } + + protected void stopDaemon() { + config.getJavetLogger().debug("JavetEnginePool.stopDaemon() begins."); + quitting = true; + try { + if (daemonThread != null) { + daemonThread.join(); + } + } catch (Exception e) { + config.getJavetLogger().logError(e, e.getMessage()); + } finally { + daemonThread = null; + } + active = false; + quitting = false; + config.getJavetLogger().debug("JavetEnginePool.stopDaemon() ends."); + } +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java new file mode 100644 index 000000000..fc21b23f2 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineUsage.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import java.time.ZonedDateTime; + +public class JavetEngineUsage { + protected int engineUsedCount; + protected ZonedDateTime lastActiveZonedDatetime; + + public JavetEngineUsage() { + reset(); + } + + public ZonedDateTime getLastActiveZonedDatetime() { + return lastActiveZonedDatetime; + } + + public void setLastActiveZonedDatetime(ZonedDateTime lastActiveZonedDatetime) { + this.lastActiveZonedDatetime = lastActiveZonedDatetime; + } + + public int getEngineUsedCount() { + return engineUsedCount; + } + + public void increaseUsedCount() { + ++engineUsedCount; + } + + protected void reset() { + engineUsedCount = 0; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/executors/BaseV8Executor.java b/src/main/java/com/caoccao/javet/interop/executors/BaseV8Executor.java new file mode 100644 index 000000000..d0836c452 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/executors/BaseV8Executor.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.executors; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.V8ScriptOrigin; +import com.caoccao.javet.values.V8Value; + +import java.util.Objects; + +public abstract class BaseV8Executor implements IV8Executor { + protected V8Runtime v8Runtime; + protected V8ScriptOrigin v8ScriptOrigin; + + public BaseV8Executor(V8Runtime v8Runtime) { + Objects.requireNonNull(v8Runtime); + this.v8Runtime = v8Runtime; + this.v8ScriptOrigin = new V8ScriptOrigin(); + } + + @Override + public abstract void compileOnly() throws JavetException; + + @Override + public abstract T execute(boolean resultRequired) throws JavetException; + + @Override + public abstract String getScriptString() throws JavetIOException ; + + @Override + public V8Runtime getV8Runtime() { + return v8Runtime; + } + + @Override + public V8ScriptOrigin getV8ScriptOrigin() { + return v8ScriptOrigin; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java b/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java new file mode 100644 index 000000000..6161e04dc --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/executors/IV8Executor.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.executors; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.V8ScriptOrigin; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValuePrimitive; + +import java.time.ZonedDateTime; + +@SuppressWarnings("unchecked") +public interface IV8Executor { + void compileOnly() throws JavetException; + + default T execute() throws JavetException { + return execute(true); + } + + T execute(boolean resultRequired) throws JavetException; + + default Boolean executeBoolean() throws JavetException { + return executeObject(); + } + + default Double executeDouble() throws JavetException { + return executeObject(); + } + + default Integer executeInteger() throws JavetException { + return executeObject(); + } + + default Long executeLong() throws JavetException { + return executeObject(); + } + + default > R executeObject() throws JavetException { + try (V8Value v8Value = execute()) { + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + } + return null; + } + + default String executeString() + throws JavetException { + return executeObject(); + } + + default void executeVoid() throws JavetException { + execute(false); + } + + default ZonedDateTime executeZonedDateTime() throws JavetException { + return executeObject(); + } + + String getScriptString() throws JavetIOException; + + V8Runtime getV8Runtime(); + + V8ScriptOrigin getV8ScriptOrigin(); +} diff --git a/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java b/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java new file mode 100644 index 000000000..7ab18b21b --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/executors/V8PathExecutor.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.executors; + +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.interop.V8Runtime; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +public class V8PathExecutor extends V8StringExecutor { + protected Path scriptPath; + + public V8PathExecutor(V8Runtime v8Runtime, Path scriptPath) { + super(v8Runtime); + this.scriptPath = scriptPath; + } + + @Override + public String getScriptString() throws JavetIOException { + if (scriptString == null) { + try { + scriptString = new String(Files.readAllBytes(scriptPath), StandardCharsets.UTF_8); + v8ScriptOrigin.setResourceName(scriptPath.toFile().getAbsolutePath()); + } catch (IOException e) { + throw JavetIOException.failedToReadPath(scriptPath, e); + } + } + return scriptString; + } + + public Path getScriptPath() { + return scriptPath; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java b/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java new file mode 100644 index 000000000..91dc2ea4c --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/executors/V8StringExecutor.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.executors; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.V8Value; + +public class V8StringExecutor extends BaseV8Executor { + protected String scriptString; + + public V8StringExecutor(V8Runtime v8Runtime) { + this(v8Runtime, null); + } + + public V8StringExecutor(V8Runtime v8Runtime, String scriptString) { + super(v8Runtime); + this.scriptString = scriptString; + } + + @Override + public String getScriptString() throws JavetIOException { + return scriptString; + } + + @Override + public void compileOnly() throws JavetException { + v8Runtime.compileOnly(getScriptString(), v8ScriptOrigin); + } + + @Override + public T execute(boolean resultRequired) throws JavetException { + return v8Runtime.execute(getScriptString(), v8ScriptOrigin, resultRequired); + } +} diff --git a/src/main/java/com/caoccao/javet/utils/JavetConverterUtils.java b/src/main/java/com/caoccao/javet/utils/JavetConverterUtils.java new file mode 100644 index 000000000..431bd7783 --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/JavetConverterUtils.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.*; + +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unchecked") +public final class JavetConverterUtils { + public static final IJavetConverter DEFAULT_BOOLEAN_CONVERTER = new IJavetConverter() { + @Override + public Boolean toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueBoolean) { + return ((V8ValueBoolean) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Boolean obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueBoolean(obj)); + } + }; + + public static final IJavetConverter DEFAULT_DOUBLE_CONVERTER = new IJavetConverter() { + @Override + public Double toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueDouble) { + return ((V8ValueDouble) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Double obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueDouble(obj)); + } + }; + + public static final IJavetConverter DEFAULT_INTEGER_CONVERTER = new IJavetConverter() { + @Override + public Integer toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueInteger) { + return ((V8ValueInteger) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Integer obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueInteger(obj)); + } + }; + + public static final IJavetConverter DEFAULT_LONG_CONVERTER = new IJavetConverter() { + @Override + public Long toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueLong) { + return ((V8ValueLong) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Long obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueLong(obj)); + } + }; + + public static final IJavetConverter DEFAULT_NULL_CONVERTER = new IJavetConverter() { + @Override + public Object toObject(V8Value v8Value) throws JavetException { + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Object obj) throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueNull()); + } + }; + + public static final IJavetConverter DEFAULT_STRING_CONVERTER = new IJavetConverter() { + @Override + public String toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueString) { + return ((V8ValueString) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, String obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueString(obj)); + } + }; + + public static final IJavetConverter DEFAULT_UNDEFINED_CONVERTER = new IJavetConverter() { + @Override + public Object toObject(V8Value v8Value) throws JavetException { + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, Object obj) throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueUndefined()); + } + }; + + public static final IJavetConverter DEFAULT_ZONED_DATE_TIME_CONVERTER = new IJavetConverter() { + @Override + public ZonedDateTime toObject(V8Value v8Value) throws JavetException { + if (v8Value != null && v8Value instanceof V8ValueZonedDateTime) { + return ((V8ValueZonedDateTime) v8Value).getValue(); + } + return null; + } + + @Override + public V8Value toV8Value(V8Runtime v8Runtime, ZonedDateTime obj) throws JavetException { + return v8Runtime.decorateV8Value(obj == null ? new V8ValueNull() : new V8ValueZonedDateTime(obj)); + } + }; + + protected IJavetConverter booleanConverter; + protected IJavetConverter doubleConverter; + protected IJavetConverter integerConverter; + protected IJavetConverter longConverter; + protected IJavetConverter nullConverter; + protected IJavetConverter stringConverter; + protected IJavetConverter undefinedConverter; + protected IJavetConverter zonedDateTimeConverter; + protected List> customConverters; + + public JavetConverterUtils() { + customConverters = new ArrayList<>(); + reset(); + } + + public IJavetConverter getNullConverter() { + return nullConverter; + } + + public void setNullConverter(IJavetConverter nullConverter) { + this.nullConverter = nullConverter; + } + + public IJavetConverter getUndefinedConverter() { + return undefinedConverter; + } + + public void setUndefinedConverter(IJavetConverter undefinedConverter) { + this.undefinedConverter = undefinedConverter; + } + + public IJavetConverter getBooleanConverter() { + return booleanConverter; + } + + public void setBooleanConverter(IJavetConverter booleanConverter) { + this.booleanConverter = booleanConverter; + } + + public IJavetConverter getDoubleConverter() { + return doubleConverter; + } + + public void setDoubleConverter(IJavetConverter doubleConverter) { + this.doubleConverter = doubleConverter; + } + + public IJavetConverter getIntegerConverter() { + return integerConverter; + } + + public void setIntegerConverter(IJavetConverter integerConverter) { + this.integerConverter = integerConverter; + } + + public IJavetConverter getLongConverter() { + return longConverter; + } + + public void setLongConverter(IJavetConverter longConverter) { + this.longConverter = longConverter; + } + + public IJavetConverter getStringConverter() { + return stringConverter; + } + + public void setStringConverter(IJavetConverter stringConverter) { + this.stringConverter = stringConverter; + } + + public IJavetConverter getZonedDateTimeConverter() { + return zonedDateTimeConverter; + } + + public void setZonedDateTimeConverter(IJavetConverter zonedDateTimeConverter) { + this.zonedDateTimeConverter = zonedDateTimeConverter; + } + + public boolean registerCustomConverter(IJavetConverter converter) { + if (customConverters.contains(converter)) { + return false; + } + return customConverters.add(converter); + } + + public boolean unregisterCustomConverter(IJavetConverter converter) { + return customConverters.remove(converter); + } + + public void reset() { + resetBooleanConverter(); + resetDoubleConverter(); + resetIntegerConverter(); + resetLongConverter(); + resetNullConverter(); + resetStringConverter(); + resetUndefinedConverter(); + resetZonedDateTimeConverter(); + resetCustomConverters(); + } + + public void resetBooleanConverter() { + booleanConverter = DEFAULT_BOOLEAN_CONVERTER; + } + + public void resetDoubleConverter() { + doubleConverter = DEFAULT_DOUBLE_CONVERTER; + } + + public void resetIntegerConverter() { + integerConverter = DEFAULT_INTEGER_CONVERTER; + } + + public void resetLongConverter() { + longConverter = DEFAULT_LONG_CONVERTER; + } + + public void resetNullConverter() { + nullConverter = DEFAULT_NULL_CONVERTER; + } + + public void resetStringConverter() { + stringConverter = DEFAULT_STRING_CONVERTER; + } + + public void resetUndefinedConverter() { + undefinedConverter = DEFAULT_UNDEFINED_CONVERTER; + } + + public void resetZonedDateTimeConverter() { + zonedDateTimeConverter = DEFAULT_ZONED_DATE_TIME_CONVERTER; + } + + public void resetCustomConverters() { + customConverters.clear(); + } + + public Object toObject(V8Value v8Value) throws JavetException { + if (v8Value == null || v8Value instanceof V8ValueNull) { + return nullConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueBoolean) { + return booleanConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueDouble) { + return doubleConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueInteger) { + return integerConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueLong) { + return longConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueString) { + return stringConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueUndefined) { + return undefinedConverter.toObject(v8Value); + } else if (v8Value instanceof V8ValueZonedDateTime) { + return zonedDateTimeConverter.toObject(v8Value); + } + if (!customConverters.isEmpty()) { + for (IJavetConverter converter : customConverters) { + Object convertedObject = converter.toObject(v8Value); + if (convertedObject != null) { + return convertedObject; + } + } + } + return v8Value; + } + + public V8Value toV8Value(V8Runtime v8Runtime, Object obj) throws JavetException { + if (obj == null) { + return nullConverter.toV8Value(v8Runtime, obj); + } else if (obj instanceof V8Value) { + return v8Runtime.decorateV8Value((V8Value) obj); + } else if (obj instanceof Boolean) { + return booleanConverter.toV8Value(v8Runtime, (Boolean) obj); + } else if (obj instanceof Double) { + return doubleConverter.toV8Value(v8Runtime, (Double) obj); + } else if (obj instanceof Integer) { + return integerConverter.toV8Value(v8Runtime, (Integer) obj); + } else if (obj instanceof Long) { + return longConverter.toV8Value(v8Runtime, (Long) obj); + } else if (obj instanceof String) { + return stringConverter.toV8Value(v8Runtime, (String) obj); + } else if (obj instanceof ZonedDateTime) { + return zonedDateTimeConverter.toV8Value(v8Runtime, (ZonedDateTime) obj); + } + if (!customConverters.isEmpty()) { + for (IJavetConverter converter : customConverters) { + V8Value convertedObject = converter.toV8Value(v8Runtime, obj); + if (convertedObject != null) { + return convertedObject; + } + } + } + return undefinedConverter.toV8Value(v8Runtime, obj); + } + + public interface IJavetConverter { + T toObject(V8Value v8Value) throws JavetException; + + V8Value toV8Value(V8Runtime v8Runtime, T obj) throws JavetException; + } +} diff --git a/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java b/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java new file mode 100644 index 000000000..f28c61bd0 --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/JavetDateTimeUtils.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +/** + * The type Date time utils. + */ +public final class JavetDateTimeUtils { + + public static final ZoneId ZONE_ID_UTC = ZoneId.of("UTC"); + + private JavetDateTimeUtils() { + } + + /** + * From JS timestamp to zoned date time. + *

+ * Note: the ZoneId needs to be system default because that's what V8 sees. + * + * @param jsTimestamp the JS timestamp + * @return the zoned date time + */ + public static ZonedDateTime toZonedDateTime(long jsTimestamp) { + return toZonedDateTime(jsTimestamp, ZoneId.systemDefault()); + } + + /** + * From JS timestamp to zoned date time. + * + * @param jsTimestamp the JS timestamp + * @param zoneId the zone id + * @return the zoned date time + */ + public static ZonedDateTime toZonedDateTime(long jsTimestamp, ZoneId zoneId) { + return ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsTimestamp), zoneId); + } + + public static ZonedDateTime getUTCNow() { + return ZonedDateTime.now(ZONE_ID_UTC); + } +} diff --git a/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java new file mode 100644 index 000000000..e29b9f4ea --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java @@ -0,0 +1,50 @@ +package com.caoccao.javet.utils; + +import com.caoccao.javet.interfaces.IJavetLogger; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JavetDefaultLogger implements IJavetLogger { + protected Logger logger; + protected String name; + + public JavetDefaultLogger(String name) { + logger = Logger.getLogger(name); + this.name = name; + } + + public String getName() { + return name; + } + + public Logger getLogger() { + return logger; + } + + @Override + public void debug(String message) { + logger.log(Level.FINE, message); + } + + @Override + public void error(String message) { + logger.log(Level.SEVERE, message); + } + + @Override + public void error(Throwable cause, String message) { + logger.severe(message); + logger.severe(cause.getMessage()); + } + + @Override + public void info(String message) { + logger.info(message); + } + + @Override + public void warn(String message) { + logger.warning(message); + } +} diff --git a/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java b/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java new file mode 100644 index 000000000..1d0b0c24e --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/JavetOSUtils.java @@ -0,0 +1,33 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.utils; + +public final class JavetOSUtils { + public static final String OS_NAME = System.getProperty("os.name"); + public static final boolean IS_LINUX = OS_NAME.startsWith("Linux"); + public static final boolean IS_WINDOWS = OS_NAME.startsWith("Windows"); + public static final String LINE_SEPARATOR = System.getProperty("line.separator"); + public static final String TEMP_DIRECTORY = System.getProperty("java.io.tmpdir"); + public static final String WORKING_DIRECTORY = System.getProperty("user.dir"); + + private JavetOSUtils() {} + + public static int getCPUCount() { + return Runtime.getRuntime().availableProcessors(); + } +} diff --git a/src/main/java/com/caoccao/javet/utils/JavetResourceUtils.java b/src/main/java/com/caoccao/javet/utils/JavetResourceUtils.java new file mode 100644 index 000000000..5f0322477 --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/JavetResourceUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.values.V8Value; + +import java.util.List; + +public final class JavetResourceUtils { + private JavetResourceUtils() { + } + + public static void safeClose(List objects) throws JavetException { + if (objects != null && !objects.isEmpty()) { + for (Object obj : objects) { + safeClose(obj); + } + } + } + + public static void safeClose(Object... objects) throws JavetException { + if (objects != null && objects.length > 0) { + for (Object obj : objects) { + safeClose(obj); + } + } + } + + public static void safeClose(Object obj) throws JavetException { + if (obj != null && obj instanceof IJavetClosable) { + ((IJavetClosable) obj).close(); + } + } +} diff --git a/src/main/java/com/caoccao/javet/utils/V8CallbackContext.java b/src/main/java/com/caoccao/javet/utils/V8CallbackContext.java new file mode 100644 index 000000000..1726fd7d2 --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/V8CallbackContext.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import com.caoccao.javet.exceptions.JavetV8CallbackAlreadyRegisteredException; +import com.caoccao.javet.interop.IV8CallbackReceiver; +import com.caoccao.javet.values.reference.IV8ValueFunction; + +import java.lang.reflect.Method; +import java.util.Objects; + +public final class V8CallbackContext { + protected static final String ERROR_V8_CALLBACK_CONTEXT_HANDLE_IS_INVALID = + "V8 callback context handle is invalid"; + protected Method callbackMethod; + protected IV8ValueFunction callbackOwnerFunction; + protected IV8CallbackReceiver callbackReceiver; + protected JavetConverterUtils converter; + protected long handle; + protected boolean returnResult; + protected boolean thisObjectRequired; + + public V8CallbackContext( + IV8CallbackReceiver callbackReceiver, + Method callbackMethod) { + this(callbackReceiver, callbackMethod, false); + } + + public V8CallbackContext( + IV8CallbackReceiver callbackReceiver, + Method callbackMethod, + boolean thisObjectRequired) { + Objects.requireNonNull(callbackReceiver); + Objects.requireNonNull(callbackMethod); + callbackOwnerFunction = null; + this.callbackMethod = callbackMethod; + this.callbackReceiver = callbackReceiver; + converter = new JavetConverterUtils(); + handle = 0L; + this.returnResult = !callbackMethod.getReturnType().equals(Void.TYPE); + this.thisObjectRequired = thisObjectRequired; + } + + public boolean isThisObjectRequired() { + return thisObjectRequired; + } + + public IV8ValueFunction getCallbackOwnerFunction() { + return callbackOwnerFunction; + } + + public void setCallbackOwnerFunction(IV8ValueFunction callbackOwnerFunction) + throws JavetV8CallbackAlreadyRegisteredException { + Objects.requireNonNull(callbackOwnerFunction); + if (this.callbackOwnerFunction == null) { + this.callbackOwnerFunction = callbackOwnerFunction; + } else if (this.callbackOwnerFunction != callbackOwnerFunction) { + throw new JavetV8CallbackAlreadyRegisteredException(); + } + } + + public IV8CallbackReceiver getCallbackReceiver() { + return callbackReceiver; + } + + public Method getCallbackMethod() { + return callbackMethod; + } + + public JavetConverterUtils getConverter() { + return converter; + } + + public long getHandle() { + return handle; + } + + public void setHandle(long handle) { + assert handle > 0L : ERROR_V8_CALLBACK_CONTEXT_HANDLE_IS_INVALID; + this.handle = handle; + } + + public boolean isReturnResult() { + return returnResult; + } +} diff --git a/src/main/java/com/caoccao/javet/utils/V8CallbackReceiver.java b/src/main/java/com/caoccao/javet/utils/V8CallbackReceiver.java new file mode 100644 index 000000000..06a551add --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/V8CallbackReceiver.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.IV8CallbackReceiver; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueArray; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * The type V8 callback receiver. + *

+ * It is supposed to provide a common ground for customized V8 callback receiver. + */ +public class V8CallbackReceiver implements IV8CallbackReceiver { + /** + * The V8 runtime. + */ + protected V8Runtime v8Runtime; + + /** + * Instantiates a new V8 callback receiver. + * + * @param v8Runtime the V8 runtime + */ + public V8CallbackReceiver(V8Runtime v8Runtime) { + Objects.requireNonNull(v8Runtime); + this.v8Runtime = v8Runtime; + } + + /** + * Gets method that does not take any arguments by method name. + * + * @param methodName the method name + * @return the method + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethod(String methodName) throws NoSuchMethodException { + return getMethod(methodName, false, 0); + } + + /** + * Gets method. + * + * @param methodName the method name + * @param thisObjectRequired the this object required + * @return the method + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethod(String methodName, boolean thisObjectRequired) throws NoSuchMethodException { + return getMethod(methodName, thisObjectRequired, 0); + } + + /** + * Gets method that takes given number of arguments by method name. + * + * @param methodName the method name + * @param thisObjectRequired the this object required + * @param argCount the arg count + * @return the method + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethod(String methodName, boolean thisObjectRequired, int argCount) + throws NoSuchMethodException { + if (argCount < 0) { + if (thisObjectRequired) { + return getClass().getMethod(methodName, V8Value.class, V8Value[].class); + } else { + return getClass().getMethod(methodName, V8Value[].class); + } + } else if (argCount == 0) { + if (thisObjectRequired) { + return getClass().getMethod(methodName, V8Value.class); + } else { + return getClass().getMethod(methodName); + } + } else { + Class[] classes = new Class[thisObjectRequired ? argCount + 1 : argCount]; + Arrays.fill(classes, V8Value.class); + return getClass().getMethod(methodName, classes); + } + } + + /** + * Gets method that is customized to given argument types by method name. + * + * @param methodName the method name + * @param parameterTypes the parameter types + * @return the method + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethod(String methodName, Class... parameterTypes) throws NoSuchMethodException { + return getClass().getMethod(methodName, parameterTypes); + } + + /** + * Gets method that takes an arbitrary number of arguments by method name. + * + * @param methodName the method name + * @return the method varargs + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethodVarargs(String methodName) throws NoSuchMethodException { + return getMethod(methodName, false, -1); + } + + /** + * Gets method that takes an arbitrary number of arguments by method name. + * + * @param methodName the method name + * @param thisObjectRequired the this object required + * @return the method varargs + * @throws NoSuchMethodException the no such method exception + */ + public final Method getMethodVarargs(String methodName, boolean thisObjectRequired) throws NoSuchMethodException { + return getMethod(methodName, thisObjectRequired, -1); + } + + @Override + public V8Runtime getV8Runtime() { + return v8Runtime; + } + + /** + * Echo the given V8 value. + * + * @param arg the arg + * @return the V8 value + * @throws JavetException the javet exception + */ + public V8Value echo(V8Value arg) throws JavetException { + return arg.toClone(); + } + + /** + * Echo the given V8 value array. + *

+ * Note: Lifecycle of the input and return arrays is managed by the caller. + * + * @param args the args + * @return the V8 value array + * @throws JavetException the javet exception + */ + public V8ValueArray echo(V8Value... args) throws JavetException { + V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); + for (V8Value arg : args) { + try (V8Value clonedArg = arg.toClone()) { + v8ValueArray.push(clonedArg); + } + } + return v8ValueArray; + } + + /** + * Echo string from input string. + * + * @param str the str + * @return the string + */ + public String echoString(String str) { + return str; + } + + /** + * Echo string from input V8 value. + * + * @param arg the arg + * @return the string + */ + public String echoString(V8Value arg) { + return arg == null ? null : arg.toString(); + } + + /** + * Echo string from the given V8 value array. + *

+ * Note: Lifecycle of the input and return arrays is managed by the caller. + * + * @param args the args + * @return the string + */ + public String echoString(V8Value... args) { + List stringList = new ArrayList<>(args.length); + for (V8Value arg : args) { + stringList.add(arg == null ? null : arg.toString()); + } + return String.join(JavetOSUtils.LINE_SEPARATOR, stringList); + } +} diff --git a/src/main/java/com/caoccao/javet/utils/V8ValueIteratorUtils.java b/src/main/java/com/caoccao/javet/utils/V8ValueIteratorUtils.java new file mode 100644 index 000000000..3ac21936a --- /dev/null +++ b/src/main/java/com/caoccao/javet/utils/V8ValueIteratorUtils.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueObject; +import com.caoccao.javet.values.virtual.V8VirtualList; + +@SuppressWarnings("unchecked") +public final class V8ValueIteratorUtils { + public static final String FUNCTION_NEXT = "next"; + public static final String PROPERTY_DONE = "done"; + public static final String PROPERTY_VALUE = "value"; + + private V8ValueIteratorUtils() { + } + + public static V8VirtualList convertIteratorToIntegerList(V8ValueObject iterator) throws JavetException { + V8VirtualList resultList = new V8VirtualList<>(); + while (true) { + try (V8ValueObject next = iterator.invoke(FUNCTION_NEXT)) { + if (next.getBoolean(PROPERTY_DONE)) { + break; + } + resultList.add(next.getInteger(PROPERTY_VALUE)); + } + } + return resultList; + } + + public static V8VirtualList convertIteratorToV8ValueList(V8ValueObject iterator) throws JavetException { + V8VirtualList resultList = new V8VirtualList<>(); + while (true) { + try (V8ValueObject next = iterator.invoke(FUNCTION_NEXT)) { + if (next.getBoolean(PROPERTY_DONE)) { + break; + } + resultList.add(next.get(PROPERTY_VALUE)); + } + } + return resultList; + } +} diff --git a/src/main/java/com/caoccao/javet/values/IV8Value.java b/src/main/java/com/caoccao/javet/values/IV8Value.java new file mode 100644 index 000000000..6a098a721 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/IV8Value.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.interop.IV8Cloneable; +import com.caoccao.javet.interop.V8Runtime; + +public interface IV8Value extends IJavetClosable, IV8Cloneable { + V8Runtime getV8Runtime(); + + T toClone() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/values/V8Value.java b/src/main/java/com/caoccao/javet/values/V8Value.java new file mode 100644 index 000000000..3a872e399 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/V8Value.java @@ -0,0 +1,65 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values; + +import com.caoccao.javet.exceptions.*; +import com.caoccao.javet.interop.V8Runtime; + +@SuppressWarnings("unchecked") +public abstract class V8Value implements IV8Value { + protected V8Runtime v8Runtime; + + public V8Value() { + v8Runtime = null; + } + + protected abstract void addReference() throws JavetException; + + protected void checkV8Runtime() throws + JavetV8RuntimeNotRegisteredException, JavetV8RuntimeLockConflictException, + JavetV8RuntimeAlreadyClosedException, JavetV8ValueAlreadyClosedException { + if (v8Runtime == null) { + throw new JavetV8RuntimeNotRegisteredException(); + } + this.v8Runtime.checkLock(); + } + + @Override + public void close() throws JavetException { + // V8 lock free + removeReference(); + v8Runtime = null; + } + + @Override + public abstract T toClone() throws JavetException; + + public V8Runtime getV8Runtime() { + return v8Runtime; + } + + public void setV8Runtime(V8Runtime v8Runtime) throws JavetException { + if (this.v8Runtime != null) { + throw new JavetV8RuntimeAlreadyRegisteredException(); + } + this.v8Runtime = v8Runtime; + this.v8Runtime.checkLock(); + } + + protected abstract void removeReference() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/values/V8ValueReferenceType.java b/src/main/java/com/caoccao/javet/values/V8ValueReferenceType.java new file mode 100644 index 000000000..124a210fd --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/V8ValueReferenceType.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values; + +public final class V8ValueReferenceType { + public static final int Object = 1; + public static final int Error = 2; + public static final int RegExp = 3; + public static final int Promise = 4; + public static final int Proxy = 5; + public static final int Symbol = 6; + public static final int Arguments = 7; + public static final int Map = 8; + public static final int Set = 9; + public static final int Array = 10; + public static final int Function = 11; +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueBoolean.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueBoolean.java new file mode 100644 index 000000000..8a4f6cf0a --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueBoolean.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public class V8ValueBoolean extends V8ValuePrimitive { + public V8ValueBoolean() { + this(false); + } + + public V8ValueBoolean(boolean value) { + super(value); + } + + @Override + public V8ValueBoolean toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueBoolean(value)); + } + + public boolean toPrimitive() { + return value.booleanValue(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java new file mode 100644 index 000000000..c52c0d339 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public class V8ValueDouble extends V8ValuePrimitive { + public V8ValueDouble() { + this(0D); + } + + public V8ValueDouble(double value) { + super(value); + } + + @Override + public V8ValueDouble toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueDouble(value)); + } + + public double toPrimitive() { + return value.doubleValue(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueInteger.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueInteger.java new file mode 100644 index 000000000..2a6315f82 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueInteger.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public final class V8ValueInteger extends V8ValuePrimitive { + public V8ValueInteger() { + this(Integer.valueOf(0)); + } + + public V8ValueInteger(int value) { + super(value); + } + + @Override + public V8ValueInteger toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueInteger(value)); + } + + public int toPrimitive() { + return value.intValue(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueLong.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueLong.java new file mode 100644 index 000000000..6d89935fb --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueLong.java @@ -0,0 +1,44 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public final class V8ValueLong extends V8ValuePrimitive { + public V8ValueLong() { + this(Long.valueOf(0)); + } + + public V8ValueLong(long value) { + super(value); + } + + public V8ValueLong(String value) { + super(Long.valueOf(value)); + } + + @Override + public V8ValueLong toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueLong(value)); + } + + public long toPrimitive() { + return value.longValue(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java new file mode 100644 index 000000000..ea085556e --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; + +@SuppressWarnings("unchecked") +public final class V8ValueNull extends V8Value { + + public static final String NULL = "null"; + + public V8ValueNull() { + super(); + } + + @Override + protected void addReference() { + } + + @Override + protected void removeReference() { + } + + @Override + public V8ValueNull toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueNull()); + } + + @Override + public String toString() { + return NULL; + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java new file mode 100644 index 000000000..e608cc39e --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; + +@SuppressWarnings("unchecked") +public abstract class V8ValuePrimitive extends V8Value { + protected T value; + + public V8ValuePrimitive() { + this(null); + } + + public V8ValuePrimitive(T value) { + this.value = value; + } + + @Override + protected void addReference() { + } + + public T getValue() { + return value; + } + + public void setValue(T value) { + this.value = value; + } + + public boolean isEmpty() { + return value == null; + } + + public boolean isPresent() { + return value != null; + } + + @Override + protected void removeReference() { + } + + @Override + public String toString() { + return isEmpty() ? null : value.toString(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueString.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueString.java new file mode 100644 index 000000000..b7d401297 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueString.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public final class V8ValueString extends V8ValuePrimitive { + public V8ValueString() { + this(null); + } + + public V8ValueString(String value) { + super(value); + } + + @Override + public V8ValueString toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueString(value)); + } + + public String toPrimitive() { + return value; + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java new file mode 100644 index 000000000..0137ed695 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; + +@SuppressWarnings("unchecked") +public final class V8ValueUndefined extends V8Value { + + public static final String UNDEFINED = "undefined"; + + public V8ValueUndefined() { + super(); + } + + @Override + protected void addReference() { + } + + @Override + protected void removeReference() { + } + + @Override + public V8ValueUndefined toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueUndefined()); + } + + @Override + public String toString() { + return UNDEFINED; + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java new file mode 100644 index 000000000..0a5b275b3 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java @@ -0,0 +1,36 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public final class V8ValueUnknown extends V8ValuePrimitive { + public V8ValueUnknown() { + this(null); + } + + public V8ValueUnknown(String value) { + super(value); + } + + @Override + public V8ValueUnknown toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueUnknown(value)); + } +} diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueZonedDateTime.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueZonedDateTime.java new file mode 100644 index 000000000..b974b6d9d --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueZonedDateTime.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetDateTimeUtils; + +import java.time.ZonedDateTime; + +@SuppressWarnings("unchecked") +public final class V8ValueZonedDateTime extends V8ValuePrimitive { + public V8ValueZonedDateTime() { + this(null); + } + + public V8ValueZonedDateTime(ZonedDateTime value) { + super(value); + } + + public V8ValueZonedDateTime(long jsTimestamp) { + super(JavetDateTimeUtils.toZonedDateTime(jsTimestamp)); + } + + @Override + public V8ValueZonedDateTime toClone() throws JavetException { + return v8Runtime.decorateV8Value(new V8ValueZonedDateTime(value)); + } + + public long toPrimitive() { + return value.toInstant().toEpochMilli(); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java new file mode 100644 index 000000000..1920deed5 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.*; +import com.caoccao.javet.values.virtual.V8VirtualList; + +@SuppressWarnings("unchecked") +public interface IV8ValueArray extends IV8ValueObject { + V8VirtualList getKeys() throws JavetException; + + int getLength() throws JavetException; + + T pop() throws JavetException; + + default Boolean popBoolean() throws JavetException { + return popObject(); + } + + default Double popDouble() throws JavetException { + return popObject(); + } + + default Integer popInteger() throws JavetException { + return popObject(); + } + + default Long popLong() throws JavetException { + return popObject(); + } + + default V8ValueNull popNull() throws JavetException { + return pop(); + } + + default > R popObject() throws JavetException { + try (V8Value v8Value = pop()) { + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + } + return null; + } + + default String popString() throws JavetException { + return popObject(); + } + + default V8ValueUndefined popUndefined() throws JavetException { + return pop(); + } + + int push(V8Value v8Value) throws JavetException; + + default int push(boolean value) throws JavetException { + return push(new V8ValueBoolean(value)); + } + + default int push(double value) throws JavetException { + return push(new V8ValueDouble(value)); + } + + default int push(int value) throws JavetException { + return push(new V8ValueInteger(value)); + } + + default int push(long value) throws JavetException { + return push(new V8ValueLong(value)); + } + + default int push(String value) throws JavetException { + return push(new V8ValueString(value)); + } + + default int pushNull() throws JavetException { + return push(new V8ValueNull()); + } + + default int pushUndefined() throws JavetException { + return push(new V8ValueUndefined()); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java new file mode 100644 index 000000000..4aa26e899 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValuePrimitive; + +@SuppressWarnings("unchecked") +public interface IV8ValueFunction extends IV8ValueObject { + T call(IV8ValueObject receiver, boolean returnResult, V8Value... v8Values) + throws JavetException; + + default T call(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return call(receiver, true, v8Values); + } + + default Boolean callBoolean(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return callObject(receiver, v8Values); + } + + default Double callDouble(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return callObject(receiver, v8Values); + } + + default Integer callInteger(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return callObject(receiver, v8Values); + } + + default Long callLong(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return callObject(receiver, v8Values); + } + + default > R callObject(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + try (V8Value v8Value = call(receiver, v8Values)) { + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + } + return null; + } + + default String callString(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + return callObject(receiver, v8Values); + } + + default void callVoid(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + call(receiver, false, v8Values); + } + + V8Value receiveCallback(V8Value thisObject, V8ValueArray args) throws Throwable; +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java new file mode 100644 index 000000000..40b5c5113 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.*; + +import java.util.List; + +@SuppressWarnings("unchecked") +public interface IV8ValueKeyContainer extends IV8ValueObject { + List getKeys() throws JavetException; + + int getSize() throws JavetException; + + default boolean has(int value) throws JavetException { + return has(new V8ValueInteger(value)); + } + + default boolean has(long value) throws JavetException { + return has(new V8ValueLong(value)); + } + + default boolean has(String value) throws JavetException { + return has(new V8ValueString(value)); + } + + boolean has(V8Value value) throws JavetException; + + default boolean hasNull() throws JavetException { + return has(new V8ValueNull()); + } + + default boolean hasUndefined() throws JavetException { + return has(new V8ValueUndefined()); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueMap.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueMap.java new file mode 100644 index 000000000..d8d2fe514 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueMap.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; + +import java.util.List; + +@SuppressWarnings("unchecked") +public interface IV8ValueMap extends IV8ValueKeyContainer { + List getEntries() throws JavetException; + + List getValues() throws JavetException; +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java new file mode 100644 index 000000000..f0d03cb1f --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.*; + +import java.time.ZonedDateTime; + +@SuppressWarnings("unchecked") +public interface IV8ValueObject extends IV8ValueReference { + T invoke(String functionName, boolean returnResult, V8Value... v8Values) throws JavetException; + + default T invoke(String functionName, V8Value... v8Values) throws JavetException { + return invoke(functionName, true, v8Values); + } + + default Boolean invokeBoolean(String functionName, V8Value... v8Values) throws JavetException { + return invokeObject(functionName, v8Values); + } + + default Double invokeDouble(String functionName, V8Value... v8Values) throws JavetException { + return invokeObject(functionName, v8Values); + } + + default Integer invokeInteger(String functionName, V8Value... v8Values) throws JavetException { + return invokeObject(functionName, v8Values); + } + + default Long invokeLong(String functionName, V8Value... v8Values) throws JavetException { + return invokeObject(functionName, v8Values); + } + + default > R invokeObject( + String functionName, V8Value... v8Values) throws JavetException { + try (V8Value v8Value = invoke(functionName, v8Values)) { + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + } + return null; + } + + default String invokeString(String functionName, V8Value... v8Values) throws JavetException { + return invokeObject(functionName, v8Values); + } + + default void invokeVoid(String functionName, V8Value... v8Values) throws JavetException { + invoke(functionName, false, v8Values); + } + + default boolean delete(int key) throws JavetException { + return delete(new V8ValueInteger(key)); + } + + default boolean delete(long key) throws JavetException { + return delete(new V8ValueLong(key)); + } + + default boolean delete(String key) throws JavetException { + return delete(new V8ValueString(key)); + } + + boolean delete(V8Value key) throws JavetException; + + default boolean deleteNull() throws JavetException { + return delete(new V8ValueNull()); + } + + default boolean deleteUndefined() throws JavetException { + return delete(new V8ValueUndefined()); + } + + default T get(int key) throws JavetException { + return get(new V8ValueInteger(key)); + } + + default T get(String key) throws JavetException { + return get(new V8ValueString(key)); + } + + T get(V8Value key) throws JavetException; + + default Boolean getBoolean(int key) throws JavetException { + return getObject(key); + } + + default Boolean getBoolean(String key) throws JavetException { + return getObject(key); + } + + default Double getDouble(int key) throws JavetException { + return getObject(key); + } + + default Double getDouble(String key) throws JavetException { + return getObject(key); + } + + default Integer getInteger(int key) throws JavetException { + return getObject(key); + } + + default Integer getInteger(String key) throws JavetException { + return getObject(key); + } + + default Long getLong(int key) throws JavetException { + return getObject(key); + } + + default Long getLong(String key) throws JavetException { + return getObject(key); + } + + default V8ValueNull getNull(int key) throws JavetException { + return get(key); + } + + default V8ValueNull getNull(String key) throws JavetException { + return get(key); + } + + default > R getObject(int key) + throws JavetException { + V8Value v8Value = get(key); + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + return null; + } + + default > R getObject(String key) + throws JavetException { + V8Value v8Value = get(key); + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + return null; + } + + IV8ValueArray getOwnPropertyNames() throws JavetException; + + IV8ValueArray getPropertyNames() throws JavetException; + + default T getProperty(int index) throws JavetException { + return getProperty(new V8ValueInteger(index)); + } + + default T getProperty(String key) throws JavetException { + return getProperty(new V8ValueString(key)); + } + + T getProperty(V8Value key) throws JavetException; + + default Boolean getPropertyBoolean(int index) throws JavetException { + return getPropertyObject(index); + } + + default Boolean getPropertyBoolean(String key) throws JavetException { + return getPropertyObject(key); + } + + default Double getPropertyDouble(int index) throws JavetException { + return getPropertyObject(index); + } + + default Double getPropertyDouble(String key) throws JavetException { + return getPropertyObject(key); + } + + default Integer getPropertyInteger(int index) throws JavetException { + return getPropertyObject(index); + } + + default Integer getPropertyInteger(String key) throws JavetException { + return getPropertyObject(key); + } + + default Long getPropertyLong(int index) throws JavetException { + return getPropertyObject(index); + } + + default Long getPropertyLong(String key) throws JavetException { + return getPropertyObject(key); + } + + default > R getPropertyObject(int index) + throws JavetException { + V8Value v8Value = getProperty(index); + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + return null; + } + + default > R getPropertyObject(String key) + throws JavetException { + V8Value v8Value = getProperty(key); + try { + return ((T) v8Value).getValue(); + } catch (Throwable t) { + } + return null; + } + + default String getPropertyString(int index) throws JavetException { + return getPropertyObject(index); + } + + default String getPropertyString(String key) throws JavetException { + return getPropertyObject(key); + } + + default ZonedDateTime getPropertyZonedDateTime(int index) throws JavetException { + return getPropertyObject(index); + } + + default ZonedDateTime getPropertyZonedDateTime(String key) throws JavetException { + return getPropertyObject(key); + } + + default String getString(int key) throws JavetException { + return getObject(key); + } + + default String getString(String key) throws JavetException { + return getObject(key); + } + + default V8ValueUndefined getUndefined(int key) throws JavetException { + return get(key); + } + + default V8ValueUndefined getUndefined(String key) throws JavetException { + return get(key); + } + + default ZonedDateTime getZonedDateTime(int key) throws JavetException { + return getObject(key); + } + + default ZonedDateTime getZonedDateTime(String key) throws JavetException { + return getObject(key); + } + + default boolean hasOwnProperty(int key) throws JavetException { + return hasOwnProperty(new V8ValueInteger(key)); + } + + default boolean hasOwnProperty(String key) throws JavetException { + return hasOwnProperty(new V8ValueString(key)); + } + + boolean hasOwnProperty(V8Value key) throws JavetException; + + default boolean set(int key, V8Value value) throws JavetException { + return set(new V8ValueInteger(key), value); + } + + default boolean set(String key, V8Value value) throws JavetException { + return set(new V8ValueString(key), value); + } + + boolean set(V8Value key, V8Value value) throws JavetException; + + default boolean setNull(int key) throws JavetException { + return set(new V8ValueInteger(key), new V8ValueNull()); + } + + default boolean setNull(String key) throws JavetException { + return set(new V8ValueString(key), new V8ValueNull()); + } + + default boolean setProperty(int key, V8Value value) throws JavetException { + return setProperty(new V8ValueInteger(key), value); + } + + default boolean setProperty(String key, V8Value value) throws JavetException { + return setProperty(new V8ValueString(key), value); + } + + boolean setProperty(V8Value key, V8Value value) throws JavetException; + + default boolean setPropertyNull(int key) throws JavetException { + return setProperty(new V8ValueInteger(key), new V8ValueNull()); + } + + default boolean setPropertyNull(String key) throws JavetException { + return setProperty(new V8ValueString(key), new V8ValueNull()); + } + + default boolean setPropertyUndefined(int key) throws JavetException { + return setProperty(new V8ValueInteger(key), new V8ValueUndefined()); + } + + default boolean setPropertyUndefined(String key) throws JavetException { + return setProperty(new V8ValueString(key), new V8ValueUndefined()); + } + + default boolean setUndefined(int key) throws JavetException { + return set(new V8ValueInteger(key), new V8ValueUndefined()); + } + + default boolean setUndefined(String key) throws JavetException { + return set(new V8ValueString(key), new V8ValueUndefined()); + } + + String toJsonString(); +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueReference.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueReference.java new file mode 100644 index 000000000..43e0dc65f --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueReference.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.IV8Value; + +public interface IV8ValueReference extends IV8Value { + + void clearWeak() throws JavetException; + + void close(boolean forceClose) throws JavetException; + + long getHandle(); + + int getType(); + + boolean isWeak() throws JavetException; + + boolean isWeak(boolean forceSync) throws JavetException; + + void setWeak() throws JavetException; + + String toProtoString(); +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java new file mode 100644 index 000000000..47043ff42 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValueNull; +import com.caoccao.javet.values.primitive.V8ValueUndefined; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueLong; +import com.caoccao.javet.values.primitive.V8ValueString; + +@SuppressWarnings("unchecked") +public interface IV8ValueSet extends IV8ValueKeyContainer { + + default void add(int value) throws JavetException { + add(new V8ValueInteger(value)); + } + + default void add(long value) throws JavetException { + add(new V8ValueLong(value)); + } + + default void add(String value) throws JavetException { + add(new V8ValueString(value)); + } + + void add(V8Value key) throws JavetException; + + default void addNull() throws JavetException { + add(new V8ValueNull()); + } + + default void addUndefined() throws JavetException { + add(new V8ValueUndefined()); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java new file mode 100644 index 000000000..88436a9bc --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.values.V8ValueReferenceType; + +public class V8ValueArguments extends V8ValueArray { + public V8ValueArguments(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Arguments; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java new file mode 100644 index 000000000..c7a4742b4 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.utils.V8ValueIteratorUtils; +import com.caoccao.javet.values.virtual.V8VirtualList; + +@SuppressWarnings("unchecked") +public class V8ValueArray extends V8ValueObject implements IV8ValueArray { + + public static final String FUNCTION_KEYS = "keys"; + public static final String FUNCTION_POP = "pop"; + public static final String FUNCTION_PUSH = "push"; + + public V8ValueArray(long handle) { + super(handle); + } + + @Override + public T get(int index) throws JavetException { + checkV8Runtime(); + return v8Runtime.get(this, new V8ValueInteger(index)); + } + + @Override + public V8VirtualList getKeys() throws JavetException { + checkV8Runtime(); + try (V8ValueObject arrayIterator = invoke(FUNCTION_KEYS)) { + return V8ValueIteratorUtils.convertIteratorToIntegerList(arrayIterator); + } + } + + @Override + public int getLength() + throws JavetException { + checkV8Runtime(); + return v8Runtime.getLength(this); + } + + @Override + public int getType() { + return V8ValueReferenceType.Array; + } + + @Override + public T pop() throws JavetException { + checkV8Runtime(); + return invoke(FUNCTION_POP); + } + + @Override + public int push(V8Value v8Value) throws JavetException { + checkV8Runtime(); + return invokeInteger(FUNCTION_PUSH, v8Value); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java new file mode 100644 index 000000000..75035f590 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8ValueReferenceType; + +public class V8ValueError extends V8ValueObject { + + public static final String STACK = "stack"; + public static final String MESSAGE = "message"; + + public V8ValueError(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Error; + } + + public String getMessage() throws JavetException { + return getPropertyString(MESSAGE); + } + + public String getStack() throws JavetException { + return getPropertyString(STACK); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java new file mode 100644 index 000000000..72e3173b2 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetV8CallbackAlreadyRegisteredException; +import com.caoccao.javet.exceptions.JavetV8CallbackSignatureMismatchException; +import com.caoccao.javet.interop.IV8CallbackReceiver; +import com.caoccao.javet.utils.JavetConverterUtils; +import com.caoccao.javet.utils.JavetResourceUtils; +import com.caoccao.javet.utils.V8CallbackContext; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.primitive.V8ValueUndefined; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@SuppressWarnings("unchecked") +public class V8ValueFunction extends V8ValueObject implements IV8ValueFunction { + /* + The lifecycle of V8ValueFunction depends on V8CallbackContext which is + managed by JNI native implementation. + */ + protected V8CallbackContext v8CallbackContext; + + public V8ValueFunction(long handle) { + super(handle); + v8CallbackContext = null; + } + + public V8CallbackContext getV8CallbackContext() { + return v8CallbackContext; + } + + public void setV8CallbackContext(V8CallbackContext v8CallbackContext) + throws JavetV8CallbackAlreadyRegisteredException { + Objects.requireNonNull(v8CallbackContext); + if (this.v8CallbackContext == null) { + v8CallbackContext.setCallbackOwnerFunction(this); + this.v8CallbackContext = v8CallbackContext; + } else if (this.v8CallbackContext != v8CallbackContext) { + throw new JavetV8CallbackAlreadyRegisteredException(); + } + } + + @Override + public T call(IV8ValueObject receiver, boolean returnResult, V8Value... v8Values) + throws JavetException { + checkV8Runtime(); + return v8Runtime.call(this, receiver, returnResult, v8Values); + } + + @Override + public void close(boolean forceClose) throws JavetException { + // V8 lock free + if (forceClose || !isWeak()) { + if (v8CallbackContext != null) { + v8Runtime.removeJNIGlobalRef(v8CallbackContext.getHandle()); + v8CallbackContext = null; + } else { + /* + * Function from V8 loses the callback context. + * So there is no need to recycle anything. + */ + } + super.close(forceClose); + } + } + + protected Object convert(JavetConverterUtils converter, Class expectedClass, V8Value v8Value) + throws JavetException { + if (v8Value == null) { + // Skip null + } else if (expectedClass.isAssignableFrom(v8Value.getClass())) { + // Skip assignable + } else { + Object convertedObject = converter.toObject(v8Value); + if (expectedClass.isAssignableFrom(convertedObject.getClass())) { + return convertedObject; + } else { + throw JavetV8CallbackSignatureMismatchException.parameterTypeMismatch( + expectedClass, convertedObject.getClass()); + } + } + return v8Value; + } + + @Override + public int getType() { + return V8ValueReferenceType.Function; + } + + @Override + public V8Value receiveCallback(V8Value thisObject, V8ValueArray args) throws Throwable { + if (v8CallbackContext != null) { + checkV8Runtime(); + List values = new ArrayList<>(); + try { + v8Runtime.decorateV8Values(thisObject, args); + JavetConverterUtils converter = v8CallbackContext.getConverter(); + Method method = v8CallbackContext.getCallbackMethod(); + IV8CallbackReceiver callbackReceiver = v8CallbackContext.getCallbackReceiver(); + Object resultObject = null; + if (v8CallbackContext.isThisObjectRequired()) { + values.add(thisObject); + } + if (args != null) { + final int length = args.getLength(); + for (int i = 0; i < length; ++i) { + values.add(args.get(i)); + } + } + if (values.isEmpty()) { + resultObject = method.invoke(callbackReceiver); + } else { + final int length = values.size(); + List objectValues = new ArrayList<>(); + Class[] parameterTypes = method.getParameterTypes(); + if (method.isVarArgs()) { + for (int i = 0; i < parameterTypes.length; ++i) { + Class parameterClass = parameterTypes[i]; + if (parameterClass.isArray() && i == parameterTypes.length - 1) { + // VarArgs is special. It requires special API to manipulate the array. + Class componentType = parameterClass.getComponentType(); + Object varObject = Array.newInstance(componentType, length - i); + for (int j = i; j < length; ++j) { + Array.set(varObject, j - i, convert( + converter, componentType, (V8Value) values.get(j))); + } + objectValues.add(varObject); + } else { + objectValues.add(convert( + converter, parameterClass, (V8Value) values.get(i))); + } + } + } else { + if (method.getParameterCount() != length) { + throw JavetV8CallbackSignatureMismatchException.parameterSizeMismatch( + length, method.getParameterCount()); + } + for (int i = 0; i < parameterTypes.length; ++i) { + objectValues.add(convert(converter, parameterTypes[i], (V8Value) values.get(i))); + } + } + resultObject = method.invoke(callbackReceiver, objectValues.toArray()); + } + if (v8CallbackContext.isReturnResult()) { + if (resultObject != null) { + if (resultObject instanceof V8Value) { + v8Runtime.decorateV8Value((V8Value) resultObject); + } else { + resultObject = converter.toV8Value(v8Runtime, resultObject); + } + } else { + resultObject = converter.toV8Value(v8Runtime, null); + } + // The lifecycle of the result is handed over to JNI native implementation. + // So, close() or setWeak() must not be called. + return (V8Value) resultObject; + } else { + JavetResourceUtils.safeClose(resultObject); + } + } catch (Throwable t) { + if (t instanceof InvocationTargetException) { + throw t.getCause(); + } else { + throw t; + } + } finally { + if (!v8CallbackContext.isThisObjectRequired()) { + JavetResourceUtils.safeClose(thisObject); + } + JavetResourceUtils.safeClose(args); + JavetResourceUtils.safeClose(values); + } + } + return v8Runtime.decorateV8Value(new V8ValueUndefined()); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java new file mode 100644 index 000000000..fb4969877 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; + +@SuppressWarnings("unchecked") +public final class V8ValueGlobalObject extends V8ValueObject { + public V8ValueGlobalObject(long handle) { + super(handle); + } + + @Override + protected void addReference() { + // Global object lives as long as V8 runtime lives. + } + + @Override + public void clearWeak() throws JavetException { + } + + @Override + public void close(boolean forceClose) throws JavetException { + // Global object lives as long as V8 runtime lives. + } + + @Override + public boolean isWeak() throws JavetException { + return false; + } + + @Override + protected void removeReference() { + // Global object lives as long as V8 runtime lives. + } + + @Override + public void setWeak() throws JavetException { + } + + @Override + public V8ValueGlobalObject toClone() { + return this; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java new file mode 100644 index 000000000..78161c5f1 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.utils.V8ValueIteratorUtils; +import com.caoccao.javet.values.virtual.V8VirtualList; + +import java.util.List; + +@SuppressWarnings("unchecked") +public class V8ValueMap extends V8ValueObject implements IV8ValueMap { + + public static final String FUNCTION_KEYS = "keys"; + public static final String FUNCTION_VALUES = "values"; + public static final String FUNCTION_ENTRIES = "entries"; + + public V8ValueMap(long handle) { + super(handle); + } + + @Override + public V8VirtualList getEntries() throws JavetException { + checkV8Runtime(); + try (V8ValueObject mapIterator = invoke(FUNCTION_ENTRIES)) { + return V8ValueIteratorUtils.convertIteratorToV8ValueList(mapIterator); + } + } + + @Override + public V8VirtualList getKeys() throws JavetException { + checkV8Runtime(); + try (V8ValueObject mapIterator = invoke(FUNCTION_KEYS)) { + return V8ValueIteratorUtils.convertIteratorToV8ValueList(mapIterator); + } + } + + @Override + public int getSize() throws JavetException { + checkV8Runtime(); + return v8Runtime.getSize(this); + } + + @Override + public int getType() { + return V8ValueReferenceType.Map; + } + + @Override + public List getValues() throws JavetException { + checkV8Runtime(); + try (V8ValueObject mapIterator = invoke(FUNCTION_VALUES)) { + return V8ValueIteratorUtils.convertIteratorToV8ValueList(mapIterator); + } + } + + @Override + public boolean has(V8Value value) throws JavetException { + checkV8Runtime(); + return v8Runtime.has(this, value); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java new file mode 100644 index 000000000..c1fbe6fa1 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; + +@SuppressWarnings("unchecked") +public class V8ValueObject extends V8ValueReference implements IV8ValueObject { + + public static final String FUNCTION_STRINGIFY = "stringify"; + public static final String PROPERTY_JSON = "JSON"; + + public V8ValueObject(long handle) { + super(handle); + } + + @Override + public boolean delete(V8Value key) throws JavetException { + checkV8Runtime(); + return v8Runtime.delete(this, key); + } + + @Override + public T get(V8Value key) throws JavetException { + checkV8Runtime(); + return v8Runtime.get(this, key); + } + + @Override + public int getType() { + return V8ValueReferenceType.Object; + } + + @Override + public boolean hasOwnProperty(V8Value key) throws JavetException { + checkV8Runtime(); + return v8Runtime.hasOwnProperty(this, key); + } + + @Override + public IV8ValueArray getOwnPropertyNames() throws JavetException { + checkV8Runtime(); + return v8Runtime.getOwnPropertyNames(this); + } + + @Override + public IV8ValueArray getPropertyNames() throws JavetException { + checkV8Runtime(); + return v8Runtime.getPropertyNames(this); + } + + @Override + public T getProperty(V8Value key) + throws JavetException { + checkV8Runtime(); + return v8Runtime.getProperty(this, key); + } + + @Override + public T invoke(String functionName, boolean returnResult, V8Value... v8Values) + throws JavetException { + checkV8Runtime(); + return v8Runtime.invoke(this, functionName, returnResult, v8Values); + } + + @Override + public boolean set(V8Value key, V8Value value) throws JavetException { + checkV8Runtime(); + return v8Runtime.set(this, key, value); + } + + @Override + public boolean setProperty(V8Value key, V8Value value) throws JavetException { + checkV8Runtime(); + return v8Runtime.setProperty(this, key, value); + } + + @Override + public T toClone() throws JavetException{ + return v8Runtime.cloneV8Value(this); + } + + @Override + public String toJsonString() { + try { + checkV8Runtime(); + try (V8ValueObject jsonObject = v8Runtime.getGlobalObject().get(PROPERTY_JSON)) { + return jsonObject.invokeString(FUNCTION_STRINGIFY, this); + } + } catch (JavetException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java new file mode 100644 index 000000000..99ac751a5 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.values.V8ValueReferenceType; + +public class V8ValuePromise extends V8ValueObject { + + public V8ValuePromise(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Promise; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java new file mode 100644 index 000000000..e6ff75956 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.values.V8ValueReferenceType; + +public class V8ValueProxy extends V8ValueObject { + + public V8ValueProxy(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Proxy; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java new file mode 100644 index 000000000..524a9c3a8 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.*; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.V8Value; + +public abstract class V8ValueReference extends V8Value implements IV8ValueReference { + protected long handle; + protected boolean weak; + + public V8ValueReference(long handle) { + super(); + this.handle = handle; + weak = false; + } + + @Override + protected void addReference() throws JavetException { + checkV8Runtime(); + v8Runtime.addReference(this); + } + + @Override + public void checkV8Runtime() throws + JavetV8RuntimeNotRegisteredException, JavetV8RuntimeLockConflictException, + JavetV8RuntimeAlreadyClosedException, JavetV8ValueAlreadyClosedException { + if (handle == 0L) { + throw new JavetV8ValueAlreadyClosedException(); + } + super.checkV8Runtime(); + } + + @Override + public void clearWeak() throws JavetException { + checkV8Runtime(); + v8Runtime.clearWeak(this); + weak = false; + } + + @Override + public void close() throws JavetException { + close(false); + } + + @Override + public void close(boolean forceClose) throws JavetException { + // V8 lock free + if (handle == 0L) { + throw new JavetV8ValueAlreadyClosedException(); + } + if (forceClose || !isWeak()) { + super.close(); + handle = 0L; + weak = false; + } + } + + @Override + public abstract int getType(); + + @Override + public long getHandle() { + return handle; + } + + @Override + public boolean isWeak() throws JavetException { + // V8 lock free + return weak; + } + + @Override + public boolean isWeak(boolean force) throws JavetException { + if (force) { + checkV8Runtime(); + weak = v8Runtime.isWeak(this); + } + return weak; + } + + @Override + protected void removeReference() throws JavetException { + // V8 lock free + v8Runtime.removeReference(this); + } + + @Override + public void setV8Runtime(V8Runtime v8Runtime) throws JavetException { + super.setV8Runtime(v8Runtime); + addReference(); + } + + @Override + public void setWeak() throws JavetException { + checkV8Runtime(); + v8Runtime.setWeak(this); + weak = true; + } + + @Override + public String toProtoString() { + try { + checkV8Runtime(); + return v8Runtime.toProtoString(this); + } catch (JavetException e) { + return e.getMessage(); + } + } + + @Override + public String toString() { + try { + checkV8Runtime(); + return v8Runtime.toString(this); + } catch (JavetException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java new file mode 100644 index 000000000..18a910a77 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.values.V8ValueReferenceType; + +public class V8ValueRegExp extends V8ValueObject { + + public V8ValueRegExp(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.RegExp; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java new file mode 100644 index 000000000..d438dc42b --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.utils.V8ValueIteratorUtils; +import com.caoccao.javet.values.virtual.V8VirtualList; + +@SuppressWarnings("unchecked") +public class V8ValueSet extends V8ValueObject implements IV8ValueSet { + + public static final String FUNCTION_KEYS = "keys"; + + public V8ValueSet(long handle) { + super(handle); + } + + @Override + public void add(V8Value key) throws JavetException { + checkV8Runtime(); + v8Runtime.add(this, key); + } + + @Override + public int getType() { + return V8ValueReferenceType.Set; + } + + @Override + public V8VirtualList getKeys() throws JavetException { + checkV8Runtime(); + try (V8ValueObject setIterator = invoke(FUNCTION_KEYS)) { + return V8ValueIteratorUtils.convertIteratorToV8ValueList(setIterator); + } + } + + @Override + public int getSize() throws JavetException { + checkV8Runtime(); + return v8Runtime.getSize(this); + } + + @Override + public boolean has(V8Value value) throws JavetException { + checkV8Runtime(); + return v8Runtime.has(this, value); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java new file mode 100644 index 000000000..a46cbcbc0 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8ValueReferenceType; + +import java.text.MessageFormat; + +public class V8ValueSymbol extends V8ValueObject { + + public static final String DESCRIPTION = "description"; + public static final String SYMBOL_0 = "Symbol({0})"; + + public V8ValueSymbol(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Symbol; + } + + public String getDescription() throws JavetException { + return getPropertyString(DESCRIPTION); + } + + @Override + public String toString() { + try { + return MessageFormat.format(SYMBOL_0, getDescription()); + } catch (JavetException e) { + return e.getMessage(); + } + } +} diff --git a/src/main/java/com/caoccao/javet/values/virtual/V8VirtualList.java b/src/main/java/com/caoccao/javet/values/virtual/V8VirtualList.java new file mode 100644 index 000000000..4d87f9ede --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/virtual/V8VirtualList.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.virtual; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetClosable; + +import java.util.ArrayList; +import java.util.Collection; + +@SuppressWarnings("unchecked") +public class V8VirtualList extends ArrayList implements IJavetClosable { + public V8VirtualList(int initialCapacity) { + super(initialCapacity); + } + + public V8VirtualList(Collection c) { + super(c); + } + + public V8VirtualList() { + super(); + } + + @Override + public void close() throws JavetException { + if (!isEmpty()) { + for (Object value : this) { + if (value instanceof IJavetClosable) { + ((IJavetClosable) value).close(); + } + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/BaseTestJavet.java b/src/test/java/com/caoccao/javet/BaseTestJavet.java new file mode 100644 index 000000000..23733da66 --- /dev/null +++ b/src/test/java/com/caoccao/javet/BaseTestJavet.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet; + +import com.caoccao.javet.interfaces.IJavetLogger; +import com.caoccao.javet.interop.V8Flags; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.utils.JavetDefaultLogger; +import org.junit.jupiter.api.BeforeAll; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class BaseTestJavet { + public static final long DEFAULT_INTERVAL_IN_MILLISECONDS = 10; + protected IJavetLogger logger; + + public BaseTestJavet() { + logger = new JavetDefaultLogger(getClass().getName()); + } + + @BeforeAll + public static void beforeAll() { + V8Flags flags = V8Host.getInstance().getFlags(); + if (!flags.isSealed()) { + flags.setAllowNativesSyntax(true); + flags.setExposeGC(true); + flags.setUseStrict(true); + flags.setTrackRetainingPath(true); + } + V8Host.getInstance().setFlags(); + } + + public void runAndWait( + long timeOutInMilliseconds, + long intervalInMilliseconds, + IRunner runner) + throws TimeoutException { + ZonedDateTime startZonedDateTime = ZonedDateTime.now(); + ZonedDateTime endZonedDateTime = startZonedDateTime.plus(timeOutInMilliseconds, ChronoUnit.MILLIS); + while (true) { + if (runner.run()) { + return; + } + if (timeOutInMilliseconds > 0 && endZonedDateTime.isBefore(ZonedDateTime.now())) { + break; + } + try { + TimeUnit.MILLISECONDS.sleep(intervalInMilliseconds); + } catch (InterruptedException e) { + throw new TimeoutException("Failed to sleep"); + } + } + throw new TimeoutException("Runner failed"); + } + + public void runAndWait(long timeOutInMilliseconds, IRunner runner) throws TimeoutException { + runAndWait(timeOutInMilliseconds, DEFAULT_INTERVAL_IN_MILLISECONDS, runner); + } + + public interface IRunner { + boolean run(); + } +} diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java new file mode 100644 index 000000000..e4c20047e --- /dev/null +++ b/src/test/java/com/caoccao/javet/BaseTestJavetRuntime.java @@ -0,0 +1,48 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.interop.V8Runtime; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +import static org.junit.jupiter.api.Assertions.*; + +public abstract class BaseTestJavetRuntime extends BaseTestJavet { + protected V8Runtime v8Runtime; + + @BeforeEach + public void beforeEach() throws JavetException { + v8Runtime = V8Host.getInstance().createV8Runtime(); + assertFalse(v8Runtime.isPooled()); + v8Runtime.lock(); + assertEquals(0, v8Runtime.getReferenceCount(), + "Reference count should be 0 before test case is started."); + } + + @AfterEach + public void afterEach() throws JavetException { + assertEquals(0, v8Runtime.getReferenceCount(), + "Reference count should be 0 after test case is ended."); + v8Runtime.unlock(); + v8Runtime.close(); + assertEquals(0, V8Host.getInstance().getV8RuntimeCount()); + } +} diff --git a/src/test/java/com/caoccao/javet/exceptions/TestJavetCompilationException.java b/src/test/java/com/caoccao/javet/exceptions/TestJavetCompilationException.java new file mode 100644 index 000000000..bcdb95641 --- /dev/null +++ b/src/test/java/com/caoccao/javet/exceptions/TestJavetCompilationException.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.exceptions; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.values.V8Value; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestJavetCompilationException extends BaseTestJavetRuntime { + @Test + public void testUnexpectedIdentifier() { + try { + v8Runtime.getExecutor("const a = 1;\na a a a;").compileOnly(); + fail("Exception should be thrown."); + } catch (JavetCompilationException e) { + assertEquals("SyntaxError: Unexpected identifier", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("SyntaxError: Unexpected identifier", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("a a a a;", javetScriptingError.getSourceLine()); + assertEquals(2, javetScriptingError.getLineNumber()); + assertEquals(2, javetScriptingError.getStartColumn()); + assertEquals(3, javetScriptingError.getEndColumn()); + assertEquals(15, javetScriptingError.getStartPosition()); + assertEquals(16, javetScriptingError.getEndPosition()); + assertEquals( + "Error: SyntaxError: Unexpected identifier\n" + + "Resource: undefined\n" + + "Source Code: a a a a;\n" + + "Line Number: 2\n" + + "Column: 2, 3\n" + + "Position: 15, 16", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetCompilationException should be thrown."); + } + } + + @Test + public void testUnexpectedToken() { + try (V8Value v8Value = v8Runtime.getExecutor("const a = 1;\na ==== 2;").execute()) { + fail("Exception should be thrown."); + } catch (JavetCompilationException e) { + assertEquals("SyntaxError: Unexpected token '='", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("SyntaxError: Unexpected token '='", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("a ==== 2;", javetScriptingError.getSourceLine()); + assertEquals(2, javetScriptingError.getLineNumber()); + assertEquals(5, javetScriptingError.getStartColumn()); + assertEquals(6, javetScriptingError.getEndColumn()); + assertEquals(18, javetScriptingError.getStartPosition()); + assertEquals(19, javetScriptingError.getEndPosition()); + assertEquals( + "Error: SyntaxError: Unexpected token '='\n" + + "Resource: undefined\n" + + "Source Code: a ==== 2;\n" + + "Line Number: 2\n" + + "Column: 5, 6\n" + + "Position: 18, 19", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetCompilationException should be thrown."); + } + } + + @Test + public void testInvalidOrUnexpectedToken() { + try (V8Value v8Value = v8Runtime.getExecutor("1a2b").execute()) { + fail("Exception should be thrown."); + } catch (JavetCompilationException e) { + assertEquals("SyntaxError: Invalid or unexpected token", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("SyntaxError: Invalid or unexpected token", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("1a2b", javetScriptingError.getSourceLine()); + assertEquals(1, javetScriptingError.getLineNumber()); + assertEquals(0, javetScriptingError.getStartColumn()); + assertEquals(1, javetScriptingError.getEndColumn()); + assertEquals(0, javetScriptingError.getStartPosition()); + assertEquals(1, javetScriptingError.getEndPosition()); + assertEquals( + "Error: SyntaxError: Invalid or unexpected token\n" + + "Resource: undefined\n" + + "Source Code: 1a2b\n" + + "Line Number: 1\n" + + "Column: 0, 1\n" + + "Position: 0, 1", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetCompilationException should be thrown."); + } + } +} diff --git a/src/test/java/com/caoccao/javet/exceptions/TestJavetExecutionException.java b/src/test/java/com/caoccao/javet/exceptions/TestJavetExecutionException.java new file mode 100644 index 000000000..3cfae7121 --- /dev/null +++ b/src/test/java/com/caoccao/javet/exceptions/TestJavetExecutionException.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.exceptions; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.values.V8Value; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestJavetExecutionException extends BaseTestJavetRuntime { + @Test + public void testAssignmentToConstantVariable() { + try (V8Value v8Value = v8Runtime.getExecutor("const a = 1; a = 2;").execute()) { + fail("Exception should be thrown."); + } catch (JavetExecutionException e) { + assertEquals("TypeError: Assignment to constant variable.", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("TypeError: Assignment to constant variable.", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("const a = 1; a = 2;", javetScriptingError.getSourceLine()); + assertEquals(1, javetScriptingError.getLineNumber()); + assertEquals(15, javetScriptingError.getStartColumn()); + assertEquals(16, javetScriptingError.getEndColumn()); + assertEquals(15, javetScriptingError.getStartPosition()); + assertEquals(16, javetScriptingError.getEndPosition()); + assertEquals( + "Error: TypeError: Assignment to constant variable.\n" + + "Resource: undefined\n" + + "Source Code: const a = 1; a = 2;\n" + + "Line Number: 1\n" + + "Column: 15, 16\n" + + "Position: 15, 16", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetExecutionException should be thrown."); + } + } + + @Test + public void testNotAFunction() { + try (V8Value v8Value = v8Runtime.getExecutor("const a = 1;\nObject.getPPP(a);").execute()) { + fail("Exception should be thrown."); + } catch (JavetExecutionException e) { + assertEquals("TypeError: Object.getPPP is not a function", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("TypeError: Object.getPPP is not a function", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("Object.getPPP(a);", javetScriptingError.getSourceLine()); + assertEquals(2, javetScriptingError.getLineNumber()); + assertEquals(7, javetScriptingError.getStartColumn()); + assertEquals(8, javetScriptingError.getEndColumn()); + assertEquals(20, javetScriptingError.getStartPosition()); + assertEquals(21, javetScriptingError.getEndPosition()); + assertEquals( + "Error: TypeError: Object.getPPP is not a function\n" + + "Resource: undefined\n" + + "Source Code: Object.getPPP(a);\n" + + "Line Number: 2\n" + + "Column: 7, 8\n" + + "Position: 20, 21", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetExecutionException should be thrown."); + } + } + + @Test + public void testNotDefined() { + try (V8Value v8Value = v8Runtime.getExecutor("Symbol(abc);").execute()) { + fail("Exception should be thrown."); + } catch (JavetExecutionException e) { + assertEquals("ReferenceError: abc is not defined", e.getMessage()); + JavetScriptingError javetScriptingError = e.getError(); + assertEquals("ReferenceError: abc is not defined", javetScriptingError.getMessage()); + assertEquals("undefined", javetScriptingError.getResourceName()); + assertEquals("Symbol(abc);", javetScriptingError.getSourceLine()); + assertEquals(1, javetScriptingError.getLineNumber()); + assertEquals(7, javetScriptingError.getStartColumn()); + assertEquals(8, javetScriptingError.getEndColumn()); + assertEquals(7, javetScriptingError.getStartPosition()); + assertEquals(8, javetScriptingError.getEndPosition()); + assertEquals( + "Error: ReferenceError: abc is not defined\n" + + "Resource: undefined\n" + + "Source Code: Symbol(abc);\n" + + "Line Number: 1\n" + + "Column: 7, 8\n" + + "Position: 7, 8", + javetScriptingError.toString()); + } catch (JavetException e) { + fail("JavetExecutionException should be thrown."); + } + } +} diff --git a/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java b/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java new file mode 100644 index 000000000..0e51257e4 --- /dev/null +++ b/src/test/java/com/caoccao/javet/interception/logging/TestJavetStandardConsoleInterceptor.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interception.logging; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestJavetStandardConsoleInterceptor extends BaseTestJavetRuntime { + protected static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + @Test + public void test() throws IOException, JavetException { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + try (PrintStream printStream = new PrintStream(byteArrayOutputStream)) { + JavetStandardConsoleInterceptor interceptor = + new JavetStandardConsoleInterceptor(v8Runtime); + interceptor.setDebug(printStream); + interceptor.setError(printStream); + interceptor.setInfo(printStream); + interceptor.setLog(printStream); + interceptor.setTrace(printStream); + interceptor.setWarn(printStream); + interceptor.register(v8Runtime.getGlobalObject()); + v8Runtime.getExecutor("console.debug('debug');").executeVoid(); + v8Runtime.getExecutor("console.error('error');").executeVoid(); + v8Runtime.getExecutor("console.info('info');").executeVoid(); + v8Runtime.getExecutor("console.log('log');").executeVoid(); + v8Runtime.getExecutor("console.trace('trace');").executeVoid(); + v8Runtime.getExecutor("console.warn('warn');").executeVoid(); + interceptor.unregister(v8Runtime.getGlobalObject()); + assertEquals( + String.join(LINE_SEPARATOR, "debug", "error", "info", "log", "trace", "warn"), + byteArrayOutputStream.toString(StandardCharsets.UTF_8.name()).trim()); + } + } + v8Runtime.requestGarbageCollectionForTesting(true); + } +} diff --git a/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java new file mode 100644 index 000000000..2df1a76a3 --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/TestJavetLibLoader.java @@ -0,0 +1,32 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.BaseTestJavet; +import com.caoccao.javet.exceptions.JavetIOException; +import com.caoccao.javet.exceptions.JavetOSNotSupportedException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestJavetLibLoader extends BaseTestJavet { + @Test + public void testLoad() throws JavetOSNotSupportedException, JavetIOException { + assertTrue(JavetLibLoader.load()); + } +} diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Host.java b/src/test/java/com/caoccao/javet/interop/TestV8Host.java new file mode 100644 index 000000000..68ad1d287 --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/TestV8Host.java @@ -0,0 +1,45 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.BaseTestJavet; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestV8Host extends BaseTestJavet { + @Test + public void testCreateV8RuntimeWithoutGlobalName() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + assertNotNull(v8Runtime); + assertTrue(v8Host.isIsolateCreated()); + } + } + + @Test + public void testCreateV8RuntimeWithGlobalName() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + assertNotNull(v8Runtime); + assertTrue(v8Host.isIsolateCreated()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Native.java b/src/test/java/com/caoccao/javet/interop/TestV8Native.java new file mode 100644 index 000000000..62bf1fbd0 --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/TestV8Native.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.BaseTestJavet; +import com.caoccao.javet.exceptions.JavetV8RuntimeLockConflictException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8Native extends BaseTestJavet { + @Test + public void testLockAndUnlock() { + final long handle = V8Native.createV8Runtime(null); + try { + final int iterations = 3; + for (int i = 0; i < iterations; ++i) { + assertThrows(JavetV8RuntimeLockConflictException.class, () -> { + V8Native.unlockV8Runtime(handle); + }, "It should not allow unlock before lock."); + V8Native.lockV8Runtime(handle); + assertThrows(JavetV8RuntimeLockConflictException.class, () -> { + V8Native.lockV8Runtime(handle); + }, "It should not allow double lock."); + V8Native.unlockV8Runtime(handle); + } + } finally { + V8Native.closeV8Runtime(handle); + } + } + + @Test + public void testGetVersion() { + String versionString = V8Native.getVersion(); + assertNotNull(versionString); + assertTrue(versionString.startsWith("8.")); + } +} diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java b/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java new file mode 100644 index 000000000..fe5dd493a --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java @@ -0,0 +1,107 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.interop; + +import com.caoccao.javet.BaseTestJavet; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetV8RuntimeLockConflictException; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.primitive.V8ValueUndefined; +import com.caoccao.javet.values.reference.V8ValueObject; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8Runtime extends BaseTestJavet { + + @Test + public void testClose() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + } + } + + @Test + public void testExecute() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + v8Runtime.lock(); + v8Runtime.getExecutor("var a = 1;").executeVoid(); + assertEquals(2, v8Runtime.getExecutor("a + 1").executeInteger()); + } + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + v8Runtime.lock(); + v8Runtime.getExecutor("var a = 1;").executeVoid(); + assertEquals(2, v8Runtime.getExecutor("a + 1").executeInteger()); + try (V8ValueObject window = v8Runtime.createV8ValueObject()) { + v8Runtime.getGlobalObject().set("window", window); + window.set("x", new V8ValueString("1")); + } + assertEquals("1", v8Runtime.getExecutor("window.x;").executeString()); + } + } + + @Test + public void testLockAndUnlock() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + final int iterations = 3; + for (int i = 0; i < iterations; ++i) { + assertThrows(JavetV8RuntimeLockConflictException.class, () -> { + v8Runtime.unlock(); + }, "It should not allow unlock before lock."); + v8Runtime.lock(); + // It should not allow double lock. + v8Runtime.lock(); + v8Runtime.unlock(); + } + } + } + + @Test + public void testResetContext() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + v8Runtime.lock(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + v8Runtime.getGlobalObject().set("a", new V8ValueString("1")); + v8Runtime.unlock(); + v8Runtime.resetContext(); + v8Runtime.lock(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + assertTrue(v8Runtime.getGlobalObject().get("a") instanceof V8ValueUndefined); + v8Runtime.unlock(); + } + } + + @Test + public void testResetIsolate() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime("window")) { + v8Runtime.lock(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + v8Runtime.getGlobalObject().set("a", new V8ValueString("1")); + v8Runtime.unlock(); + v8Runtime.resetIsolate(); + v8Runtime.lock(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + assertTrue(v8Runtime.getGlobalObject().get("a") instanceof V8ValueUndefined); + v8Runtime.unlock(); + } + } +} diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java new file mode 100644 index 000000000..5a7dc8ca3 --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEnginePool.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.interop.engine; + +import com.caoccao.javet.BaseTestJavet; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.executors.IV8Executor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestJavetEnginePool extends BaseTestJavet { + public static final int TEST_POOL_DAEMON_CHECK_INTERVAL_MILLIS = 1; + public static final int TEST_MAX_TIMEOUT = 1000; + private JavetEnginePool javetEnginePool; + private JavetEngineConfig javetEngineConfig ; + + @AfterEach + private void afterEach() throws JavetException { + javetEnginePool.close(); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertFalse(javetEnginePool.isActive()); + } + + @BeforeEach + private void beforeEach() { + javetEnginePool = new JavetEnginePool(); + assertTrue(javetEnginePool.isActive()); + javetEngineConfig = javetEnginePool.getConfig(); + javetEngineConfig.setPoolDaemonCheckIntervalMillis(TEST_POOL_DAEMON_CHECK_INTERVAL_MILLIS); + } + + @Test + public void testSingleThreadedExecution() throws Exception { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + try (IJavetEngine engine = javetEnginePool.getEngine()) { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(1, javetEnginePool.getActiveEngineCount()); + V8Runtime v8Runtime = engine.getV8Runtime(); + assertTrue(v8Runtime.isPooled()); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + v8Runtime.close(); // close() doesn't take effect because the V8 runtime is managed by pool + assertEquals(4, v8Runtime.getExecutor("2 + 2").executeInteger()); + } + runAndWait(TEST_MAX_TIMEOUT, () -> javetEnginePool.getIdleEngineCount() == 1); + assertEquals(1, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + } + + @Test + public void testMultiThreadedExecutionBelowMaxSize() throws Exception { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + final int threadCount = javetEngineConfig.getPoolMaxSize() - javetEngineConfig.getPoolMinSize(); + Thread[] threads = new Thread[threadCount]; + Object lockObject = new Object(); + AtomicInteger runningCount = new AtomicInteger(0); + AtomicInteger failureCount = new AtomicInteger(0); + synchronized (lockObject) { + IntStream.range(0, threadCount).forEach(j -> { + Thread thread = new Thread(() -> { + try (IJavetEngine engine = javetEnginePool.getEngine()) { + runningCount.incrementAndGet(); + V8Runtime v8Runtime = engine.getV8Runtime(); + IV8Executor iV8Executor; + synchronized (lockObject) { + iV8Executor = v8Runtime.getExecutor("1+1"); + } + assertEquals(2, iV8Executor.executeInteger()); + } catch (Exception e) { + failureCount.incrementAndGet(); + logger.logError("Failed to execute. Error: {0}.", e.getMessage()); + } + }); + thread.start(); + threads[j] = thread; + }); + runAndWait(TEST_MAX_TIMEOUT, () -> runningCount.get() == threadCount); + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(threadCount, javetEnginePool.getActiveEngineCount()); + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + logger.logError("Failed to join the worker thread. Error: {0}.", e.getMessage()); + } + } + runAndWait(TEST_MAX_TIMEOUT, () -> threadCount == javetEnginePool.getIdleEngineCount()); + assertEquals(0, failureCount.get()); + assertEquals(threadCount, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + } + + @Test + public void testMultiThreadedExecutionExceedsMaxSize() throws Exception { + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + final int threadCount = javetEngineConfig.getPoolMaxSize() + javetEngineConfig.getPoolMinSize(); + Thread[] threads = new Thread[threadCount]; + Object lockObject = new Object(); + AtomicInteger runningCount = new AtomicInteger(0); + AtomicInteger failureCount = new AtomicInteger(0); + synchronized (lockObject) { + IntStream.range(0, threadCount).forEach(j -> { + Thread thread = new Thread(() -> { + try (IJavetEngine engine = javetEnginePool.getEngine()) { + runningCount.incrementAndGet(); + IV8Executor iV8Executor; + synchronized (lockObject) { + iV8Executor = engine.getV8Runtime().getExecutor("1 + 1"); + } + assertEquals(2, iV8Executor.executeInteger()); + } catch (Exception e) { + failureCount.incrementAndGet(); + logger.logError("Failed to execute. Error: {0}.", e.getMessage()); + } + }); + thread.start(); + threads[j] = thread; + }); + runAndWait(TEST_MAX_TIMEOUT, () -> runningCount.get() == javetEngineConfig.getPoolMaxSize()); + assertEquals(0, javetEnginePool.getIdleEngineCount()); + assertEquals(javetEngineConfig.getPoolMaxSize(), javetEnginePool.getActiveEngineCount()); + } + for (Thread thread : threads) { + try { + thread.join(); + } catch (InterruptedException e) { + logger.logError("Failed to join the worker thread. Error: {0}.", e.getMessage()); + } + } + runAndWait(TEST_MAX_TIMEOUT, () -> javetEngineConfig.getPoolMaxSize() == javetEnginePool.getIdleEngineCount()); + assertEquals(0, failureCount.get()); + assertEquals(javetEngineConfig.getPoolMaxSize(), javetEnginePool.getIdleEngineCount()); + assertEquals(0, javetEnginePool.getActiveEngineCount()); + } +} diff --git a/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java b/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java new file mode 100644 index 000000000..f7e5b067a --- /dev/null +++ b/src/test/java/com/caoccao/javet/mock/MockCallbackReceiver.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.mock; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.V8CallbackReceiver; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.reference.V8ValueArray; +import com.caoccao.javet.values.reference.V8ValueObject; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; + +public class MockCallbackReceiver extends V8CallbackReceiver { + protected boolean called; + + public MockCallbackReceiver(V8Runtime v8Runtime) { + super(v8Runtime); + called = false; + } + + public boolean isCalled() { + return called; + } + + public void setCalled(boolean called) { + this.called = called; + } + + public void blank() { + called = true; + } + + @Override + public V8Value echo(V8Value arg) throws JavetException { + called = true; + return super.echo(arg); + } + + public V8Value echoThis(V8Value thisObject) throws JavetException { + called = true; + return new V8ValueString(((V8ValueObject) thisObject).toJsonString()); + } + + @Override + public String echoString(String str) { + called = true; + return super.echoString(str); + } + + @Override + public String echoString(V8Value arg) { + called = true; + return super.echoString(arg); + } + + @Override + public String echoString(V8Value... args) { + called = true; + return super.echoString(args); + } + + public V8ValueArray echo(V8Value... args) throws JavetException { + called = true; + return super.echo(args); + } + + public V8ValueString echoThis(V8Value thisObject, V8Value arg) throws JavetException { + called = true; + try (V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray()) { + try (V8Value clonedThisObject = thisObject.toClone()) { + v8ValueArray.push(clonedThisObject); + } + try (V8Value clonedArg = arg.toClone()) { + v8ValueArray.push(clonedArg); + } + return new V8ValueString(v8ValueArray.toJsonString()); + } + } + + public V8ValueArray echoThis(V8Value thisObject, V8Value... args) throws JavetException { + called = true; + V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); + try (V8Value clonedThisObject = thisObject.toClone()) { + v8ValueArray.push(clonedThisObject); + } + for (V8Value arg : args) { + try (V8Value clonedArg = arg.toClone()) { + v8ValueArray.push(clonedArg); + } + } + return v8ValueArray; + } + + public String echoThisString(V8Value thisObject, String arg) throws JavetException { + called = true; + try (V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray()) { + try (V8Value clonedThisObject = thisObject.toClone()) { + v8ValueArray.push(clonedThisObject); + } + v8ValueArray.push(arg); + return v8ValueArray.toJsonString(); + } + } + + public void error() throws Exception { + called = true; + throw new Exception("Mock error"); + } + + public String joinWithThis( + V8ValueObject thisObject, + Boolean b, Double d, Integer i, Long l, String s, ZonedDateTime z, V8ValueString v) { + called = true; + List lines = new ArrayList<>(); + lines.add(thisObject.toJsonString()); + lines.add(b.toString()); + lines.add(d.toString()); + lines.add(i.toString()); + lines.add(l.toString()); + lines.add(s); + lines.add(z.withZoneSameInstant(ZoneId.of("UTC")).toString()); + lines.add(v.getValue()); + return String.join(",", lines); + } + + public String joinIntegerArrayWithThis( + V8ValueObject thisObject, + String s, Integer... integers) { + called = true; + List lines = new ArrayList<>(); + lines.add(thisObject.toJsonString()); + lines.add(s); + for (Integer integer : integers) { + lines.add(integer.toString()); + } + return String.join(",", lines); + } + + public String joinWithoutThis( + Boolean b, Double d, Integer i, Long l, String s, ZonedDateTime z, V8ValueString v) { + called = true; + List lines = new ArrayList<>(); + lines.add(b.toString()); + lines.add(d.toString()); + lines.add(i.toString()); + lines.add(l.toString()); + lines.add(s); + lines.add(z.withZoneSameInstant(ZoneId.of("UTC")).toString()); + lines.add(v.getValue()); + return String.join(",", lines); + } +} diff --git a/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java b/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java new file mode 100644 index 000000000..e976de28c --- /dev/null +++ b/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.tutorial; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.interfaces.IJavetLogger; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.JavetDefaultLogger; +import com.caoccao.javet.utils.JavetOSUtils; + +import java.io.File; + +public class DecimalJavet implements IJavetClosable { + private IJavetLogger logger; + private V8Runtime v8Runtime; + + public DecimalJavet() { + logger = new JavetDefaultLogger(getClass().getName()); + v8Runtime = null; + } + + public static void main(String[] args) throws JavetException { + DecimalJavet decimalJavet = new DecimalJavet(); + try { + decimalJavet.loadJS(); + decimalJavet.test(); + } catch (Throwable t) { + decimalJavet.getLogger().error(t, t.getMessage()); + } finally { + decimalJavet.close(); + } + } + + public void loadJS() throws JavetException { + File decimalJSFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/node_modules/decimal.js/decimal.js"); + if (decimalJSFile.exists() && decimalJSFile.canRead()) { + logger.logInfo("Loading {0}.", decimalJSFile.getAbsolutePath()); + v8Runtime = V8Host.getInstance().createV8Runtime(); + v8Runtime.lock(); + v8Runtime.getExecutor(decimalJSFile).executeVoid(); + } else { + logger.logError("{0} is not found.", decimalJSFile.getAbsolutePath()); + logger.logError("Please make sure NodeJS is installed, then visit script/node directory and run npm install."); + } + } + + public void test() throws JavetException { + logger.logInfo("1.23 + 2.34 = {0}", v8Runtime.getExecutor( + "const a = new Decimal(1.23);" + + "const b = new Decimal(2.34);" + + "a.add(b).toString();").executeString()); + } + + public IJavetLogger getLogger() { + return logger; + } + + @Override + public void close() throws JavetException { + if (v8Runtime != null) { + v8Runtime.close(); + } + } +} diff --git a/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java b/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java new file mode 100644 index 000000000..327236315 --- /dev/null +++ b/src/test/java/com/caoccao/javet/tutorial/HelloJavet.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.tutorial; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interception.logging.JavetStandardConsoleInterceptor; +import com.caoccao.javet.interop.V8Host; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.interop.engine.IJavetEngine; +import com.caoccao.javet.interop.engine.IJavetEnginePool; +import com.caoccao.javet.interop.engine.JavetEnginePool; + +public class HelloJavet { + + public static void main(String[] args) throws JavetException { + HelloJavet helloJavet = new HelloJavet(); + helloJavet.printHelloJavet(); + helloJavet.printOnePlusOne(); + helloJavet.playWithPoolAndConsole(); + } + + public void printHelloJavet() throws JavetException { + // Step 1: Create a V8 runtime from V8 host in try resource. + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime()) { + // Step 2: Request a lock. + v8Runtime.lock(); + // Step 3: Execute a string as JavaScript code and print the result to console. + System.out.println(v8Runtime.getExecutor("'Hello Javet'").executeString()); // Hello Javet + // Step 4: Resource including the lock is recycled automatically at the end of the try resource block. + } + } + + public void printOnePlusOne() throws JavetException { + // Step 1: Create a V8 runtime from V8 host in try resource. + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime()) { + // Step 2: Request a lock. + v8Runtime.lock(); + // Step 3: Execute a string as JavaScript code and print the result to console. + System.out.println("1 + 1 = " + v8Runtime.getExecutor("1 + 1").executeInteger()); // 2 + // Step 4: Resource including the lock is recycled automatically at the end of the try resource block. + } + } + + public void playWithPoolAndConsole() throws JavetException { + // Create a Javet engine pool. + try (IJavetEnginePool javetEnginePool = new JavetEnginePool()) { + // Get a Javet engine from the pool. + try (IJavetEngine javetEngine = javetEnginePool.getEngine()) { + // Get a V8 runtime from the engine. + // lock() is not necessary because the Javet engine handles that. + V8Runtime v8Runtime = javetEngine.getV8Runtime(); + // Create a Javet console interceptor. + JavetStandardConsoleInterceptor javetConsoleInterceptor = new JavetStandardConsoleInterceptor(v8Runtime); + // Register the Javet console to V8 global object. + javetConsoleInterceptor.register(v8Runtime.getGlobalObject()); + // V8 console log is redirected to JVM console log. + v8Runtime.getExecutor("console.log('Hello Javet from Pool');").executeVoid(); + // Unregister the Javet console to V8 global object. + javetConsoleInterceptor.unregister(v8Runtime.getGlobalObject()); + // unlock() is not necessary because the Javet engine handles that. + // close() is not necessary because the Javet pool handles that. + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/tutorial/TestES5.java b/src/test/java/com/caoccao/javet/tutorial/TestES5.java new file mode 100644 index 000000000..cb04e3e97 --- /dev/null +++ b/src/test/java/com/caoccao/javet/tutorial/TestES5.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.tutorial; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetOSUtils; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestES5 extends BaseTestJavetRuntime { + + @Test + public void testES5MultipleStringLiterals() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es5/test-es5-multiline-string-literals.js"); + assertEquals("aaa123bbb", v8Runtime.getExecutor(scriptFile).executeString()); + } +} diff --git a/src/test/java/com/caoccao/javet/tutorial/TestES6.java b/src/test/java/com/caoccao/javet/tutorial/TestES6.java new file mode 100644 index 000000000..bd7a5115c --- /dev/null +++ b/src/test/java/com/caoccao/javet/tutorial/TestES6.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.tutorial; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetOSUtils; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.reference.V8ValueArray; +import org.junit.jupiter.api.Test; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestES6 extends BaseTestJavetRuntime { + + @Test + public void testES6ArrayFind() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-array-find.js"); + assertEquals(3, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6ArrayFindIndex() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-array-find-index.js"); + assertEquals(2, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6ArrowFunction() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-arrow-function.js"); + assertEquals(3, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6Class() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-class.js"); + assertEquals(3, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6DefaultParameterValues() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-default-parameter-values.js"); + assertEquals(3, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6FunctionRestParameter() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-function-rest-parameter.js"); + assertEquals(6, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6IsFinite() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-is-finite.js"); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor(scriptFile).execute()) { + assertTrue(v8ValueArray.getBoolean(0)); + assertFalse(v8ValueArray.getBoolean(1)); + } + } + + @Test + public void testES6IsNaN() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-is-nan.js"); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor(scriptFile).execute()) { + assertTrue(v8ValueArray.getBoolean(0)); + assertFalse(v8ValueArray.getBoolean(1)); + } + } + + @Test + public void testES6LetConst() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-let-const.js"); + assertEquals(3, v8Runtime.getExecutor(scriptFile).executeInteger()); + } + + @Test + public void testES6NumberIsInteger() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-number-is-integer.js"); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor(scriptFile).execute()) { + assertTrue(v8ValueArray.getBoolean(0)); + assertFalse(v8ValueArray.getBoolean(1)); + } + } + + @Test + public void testES6NumberIsSafeInteger() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-number-is-safe-integer.js"); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor(scriptFile).execute()) { + assertTrue(v8ValueArray.getBoolean(0)); + assertFalse(v8ValueArray.getBoolean(1)); + } + } + + @Test + public void testES6Symbol() throws JavetException { + File scriptFile = new File( + JavetOSUtils.WORKING_DIRECTORY, + "scripts/node/test-es6/test-es6-symbol.js"); + assertFalse(v8Runtime.getExecutor(scriptFile).executeBoolean()); + } +} diff --git a/src/test/java/com/caoccao/javet/utils/TestJavetDateTimeUtils.java b/src/test/java/com/caoccao/javet/utils/TestJavetDateTimeUtils.java new file mode 100644 index 000000000..1cc3bb5b8 --- /dev/null +++ b/src/test/java/com/caoccao/javet/utils/TestJavetDateTimeUtils.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.utils; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TestJavetDateTimeUtils { + @Test + public void testConversion() { + long jsTimestamp = 1611653084680L; + ZonedDateTime zonedDateTime = JavetDateTimeUtils.toZonedDateTime(jsTimestamp); + if (zonedDateTime.getZone().getId().equals("Asia/Shanghai")) { + assertEquals(2021, zonedDateTime.getYear()); + assertEquals(1, zonedDateTime.getMonthValue()); + assertEquals(26, zonedDateTime.getDayOfMonth()); + assertEquals(17, zonedDateTime.getHour()); + assertEquals(24, zonedDateTime.getMinute()); + assertEquals(44, zonedDateTime.getSecond()); + } else { + zonedDateTime = ZonedDateTime.ofInstant(Instant.ofEpochMilli(jsTimestamp), ZoneId.of("UTC")); + assertEquals(2021, zonedDateTime.getYear()); + assertEquals(1, zonedDateTime.getMonthValue()); + assertEquals(26, zonedDateTime.getDayOfMonth()); + assertEquals(9, zonedDateTime.getHour()); + assertEquals(24, zonedDateTime.getMinute()); + assertEquals(44, zonedDateTime.getSecond()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java new file mode 100644 index 000000000..fc22da7c3 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueBoolean extends BaseTestJavetRuntime { + @Test + public void testBoolean() throws JavetException { + try (V8ValueBoolean v8ValueBoolean = v8Runtime.getExecutor("1 == 1").execute()) { + assertNotNull(v8ValueBoolean); + assertTrue(v8ValueBoolean.isPresent()); + assertTrue(v8ValueBoolean.getValue()); + assertEquals("true", v8ValueBoolean.toString()); + assertEquals(v8Runtime, v8ValueBoolean.getV8Runtime()); + } + try (V8ValueBoolean v8ValueBoolean = v8Runtime.getExecutor("1 != 1").execute()) { + assertNotNull(v8ValueBoolean); + assertTrue(v8ValueBoolean.isPresent()); + assertFalse(v8ValueBoolean.getValue()); + assertEquals("false", v8ValueBoolean.toString()); + assertEquals(v8Runtime, v8ValueBoolean.getV8Runtime()); + } + assertTrue(v8Runtime.getExecutor("true").executeBoolean()); + assertFalse(v8Runtime.getExecutor("false").executeBoolean()); + } + + @Test + public void testBooleanObject() throws JavetException { + assertTrue(v8Runtime.getExecutor("Boolean(true)").executeBoolean()); + assertFalse(v8Runtime.getExecutor("Boolean(false)").executeBoolean()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java new file mode 100644 index 000000000..525e732f1 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java @@ -0,0 +1,46 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueDouble extends BaseTestJavetRuntime { + public static final double DELTA = 0.001; + + @Test + public void testNumber() throws JavetException { + try (V8ValueDouble v8ValueDouble = v8Runtime.getExecutor("1.23").execute()) { + assertNotNull(v8ValueDouble); + assertEquals(1.23, v8ValueDouble.getValue(), DELTA); + assertEquals("1.23", v8ValueDouble.toString()); + assertEquals(v8Runtime, v8ValueDouble.getV8Runtime()); + } + assertEquals(-0.5, v8Runtime.getExecutor("-0.5").executeDouble(), DELTA); + assertEquals(0, v8Runtime.getExecutor("-0.0").executeDouble(), DELTA); + } + + @Test + public void testNumberObject() throws JavetException { + assertEquals(1.23, v8Runtime.getExecutor("Number(1.23)").executeDouble(), DELTA); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java new file mode 100644 index 000000000..801c9df82 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java @@ -0,0 +1,43 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueInteger extends BaseTestJavetRuntime { + @Test + public void testInt32() throws JavetException { + try (V8ValueInteger v8ValueInteger = v8Runtime.getExecutor("1 + 1").execute()) { + assertNotNull(v8ValueInteger); + assertEquals(2, v8ValueInteger.getValue()); + assertEquals("2", v8ValueInteger.toString()); + assertEquals(v8Runtime, v8ValueInteger.getV8Runtime()); + } + assertEquals(-1, v8Runtime.getExecutor("1 - 2").executeInteger()); + } + + @Test + public void testInt32Object() throws JavetException { + assertEquals(123, v8Runtime.getExecutor("Number(123)").executeInteger()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java new file mode 100644 index 000000000..eb1e522c0 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.BaseTestJavetRuntime; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueLong extends BaseTestJavetRuntime { + @Test + public void testBigInt() throws JavetException { + try (V8ValueLong v8ValueLong = v8Runtime.getExecutor("2n ** 62n").execute()) { + assertNotNull(v8ValueLong); + assertEquals(4611686018427387904L, v8ValueLong.getValue()); + assertEquals("4611686018427387904", v8ValueLong.toString()); + assertEquals(-2L, v8Runtime.getExecutor("-2n").executeLong()); + assertEquals(v8Runtime, v8ValueLong.getV8Runtime()); + } + } + + @Test + public void testBigIntObject() throws JavetException { + assertEquals(123L, v8Runtime.getExecutor("BigInt(123n)").executeLong()); + } + + @Test + public void testString() throws JavetException { + assertEquals("4611686018427387904", v8Runtime.getExecutor("(2n ** 62n).toString()").executeString()); + assertEquals("-2", v8Runtime.getExecutor("(-2n).toString()").executeString()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueNull.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueNull.java new file mode 100644 index 000000000..1191dc479 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueNull.java @@ -0,0 +1,35 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.primitive.V8ValueNull; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueNull extends BaseTestJavetRuntime { + @Test + public void testNull() throws JavetException { + V8ValueNull v8ValueNull = v8Runtime.getExecutor("null").execute(); + assertNotNull(v8ValueNull); + assertEquals(v8Runtime, v8ValueNull.getV8Runtime()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java new file mode 100644 index 000000000..e210d004d --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java @@ -0,0 +1,48 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueString extends BaseTestJavetRuntime { + @Test + public void testString() throws JavetException { + try (V8ValueString v8ValueString = v8Runtime.getExecutor("'abc' + 'def'").execute()) { + assertNotNull(v8ValueString); + assertEquals("abcdef", v8ValueString.getValue()); + assertEquals("abcdef", v8ValueString.toString()); + assertEquals(v8Runtime, v8ValueString.getV8Runtime()); + } + assertEquals("中文測試", v8Runtime.getExecutor("'中文測試'").executeString()); + assertEquals("français", v8Runtime.getExecutor("'français'").executeString()); + assertEquals("こにちは", v8Runtime.getExecutor("'こにちは'").executeString()); + } + + @Test + public void testTypeof() throws JavetException { + assertEquals("bigint", v8Runtime.getExecutor("typeof 1n").executeString()); + assertEquals("object", v8Runtime.getExecutor("typeof (new Object(1n))").executeString()); + assertEquals("number", v8Runtime.getExecutor("typeof 1").executeString()); + assertEquals("string", v8Runtime.getExecutor("typeof '1'").executeString()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java new file mode 100644 index 000000000..fb444f1c7 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java @@ -0,0 +1,40 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.primitive.V8ValueUndefined; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueUndefined extends BaseTestJavetRuntime { + @Test + public void testUndefined() throws JavetException { + try (V8ValueUndefined v8ValueUndefined = v8Runtime.getExecutor("undefined").execute()) { + assertNotNull(v8ValueUndefined); + assertEquals(v8Runtime, v8ValueUndefined.getV8Runtime()); + } + try (V8ValueUndefined v8ValueUndefined = v8Runtime.getExecutor("").execute()) { + assertNotNull(v8ValueUndefined); + assertEquals(v8Runtime, v8ValueUndefined.getV8Runtime()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java new file mode 100644 index 000000000..c63f858ea --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021. caoccao.com Sam Cao + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.caoccao.javet.values.primitive; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueZonedDateTime extends BaseTestJavetRuntime { + + public static final int DELTA = 2000; + + @Test + public void testZonedDateTime() throws JavetException { + ZonedDateTime now = ZonedDateTime.now(); + try (V8ValueZonedDateTime v8ValueZonedDateTime = v8Runtime.getExecutor("new Date()").execute()) { + assertNotNull(v8ValueZonedDateTime); + assertEquals(v8Runtime, v8ValueZonedDateTime.getV8Runtime()); + long deltaEpochSecond = v8ValueZonedDateTime.getValue().toEpochSecond() - now.toEpochSecond(); + assertTrue(deltaEpochSecond >= 0 && deltaEpochSecond <= DELTA); + } + ZonedDateTime zonedDateTime = v8Runtime.getExecutor("new Date(1611710223719)").executeZonedDateTime(); + zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("UTC")); + assertEquals(2021, zonedDateTime.getYear()); + assertEquals(1, zonedDateTime.getMonthValue()); + assertEquals(27, zonedDateTime.getDayOfMonth()); + assertEquals(1, zonedDateTime.getHour()); + assertEquals(17, zonedDateTime.getMinute()); + assertEquals(3, zonedDateTime.getSecond()); + assertEquals("2021-01-27T01:17:03.719Z[UTC]", zonedDateTime.toString()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java new file mode 100644 index 000000000..23e79b10d --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArguments.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.BaseTestJavetRuntime; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueArguments extends BaseTestJavetRuntime { + @Test + public void testArguments() throws JavetException { + try (V8ValueArguments v8ValueArguments = v8Runtime.getExecutor( + "const a = function(a, b) { return arguments; }; a(1, '2')").execute()) { + assertNotNull(v8ValueArguments); + assertEquals(1, v8ValueArguments.getInteger(0)); + assertEquals("2", v8ValueArguments.getString(1)); + try (V8ValueArguments clonedV8ValueArguments = v8ValueArguments.toClone()) { + assertEquals(1, v8ValueArguments.getInteger(0)); + assertEquals("2", v8ValueArguments.getString(1)); + assertNotEquals(v8ValueArguments.getHandle(), clonedV8ValueArguments.getHandle()); + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java new file mode 100644 index 000000000..beb854ae7 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.primitive.*; +import com.caoccao.javet.values.virtual.V8VirtualList; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueArray extends BaseTestJavetRuntime { + + @Test + public void testGetAndSet() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = new Array(); a;").execute()) { + v8ValueArray.set(0, new V8ValueString("x")); + v8ValueArray.set(1, new V8ValueString("y")); + v8ValueArray.set(2, new V8ValueString("z")); + v8ValueArray.set("a", new V8ValueInteger(1)); + v8ValueArray.set("b", new V8ValueString("2")); + assertEquals(3, v8ValueArray.getLength()); + assertEquals("x", v8ValueArray.getString(0)); + assertEquals("y", v8ValueArray.getString(1)); + assertEquals("z", v8ValueArray.getString(2)); + assertEquals(1, v8ValueArray.getInteger("a")); + assertEquals("2", v8ValueArray.getString("b")); + assertEquals("x,y,z", v8ValueArray.toString()); + assertEquals("[object Array]", v8ValueArray.toProtoString()); + assertEquals("[\"x\",\"y\",\"z\"]", v8ValueArray.toJsonString()); + try (V8VirtualList keys = v8ValueArray.getKeys()) { + assertEquals(3, keys.size()); + assertEquals(0, keys.get(0)); + assertEquals(1, keys.get(1)); + assertEquals(2, keys.get(2)); + } + } + } + + @Test + public void testGet() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor( + "[1,'2',3n, true, 1.23, [4, 5, null, new Date(1611710223719)]]").execute()) { + assertNotNull(v8ValueArray); + assertEquals(v8Runtime, v8ValueArray.getV8Runtime()); + assertEquals(6, v8ValueArray.getLength()); + assertEquals(1, ((V8ValueInteger) v8ValueArray.get(0)).getValue()); + assertEquals(1, v8ValueArray.getInteger(0)); + assertEquals("2", ((V8ValueString) v8ValueArray.get(1)).getValue()); + assertEquals("2", v8ValueArray.getString(1)); + assertEquals(3L, ((V8ValueLong) v8ValueArray.get(2)).getValue()); + assertEquals(3L, v8ValueArray.getLong(2)); + assertTrue(((V8ValueBoolean) v8ValueArray.get(3)).getValue()); + assertTrue(v8ValueArray.getBoolean(3)); + assertEquals(1.23, ((V8ValueDouble) v8ValueArray.get(4)).getValue(), 0.001); + assertEquals(1.23, v8ValueArray.getDouble(4), 0.001); + assertTrue(v8ValueArray.get(-1) instanceof V8ValueUndefined); + assertTrue(v8ValueArray.get(100) instanceof V8ValueUndefined); + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueArray childV8ValueArray = v8ValueArray.get(5)) { + assertNotNull(childV8ValueArray); + assertEquals(v8Runtime, childV8ValueArray.getV8Runtime()); + assertEquals(4, childV8ValueArray.getLength()); + assertEquals(4, childV8ValueArray.getInteger(0)); + assertEquals(5, childV8ValueArray.getInteger(1)); + assertTrue(childV8ValueArray.get(2) instanceof V8ValueNull); + assertEquals( + "2021-01-27T01:17:03.719Z[UTC]", + childV8ValueArray.getZonedDateTime(3).withZoneSameInstant(ZoneId.of("UTC")).toString()); + assertEquals(2, v8Runtime.getReferenceCount()); + } + assertEquals(1, v8Runtime.getReferenceCount()); + } + } + + @Test + public void testNestedArray() throws JavetException { + try (V8ValueArray outerArray = v8Runtime.getExecutor("[1,2,3]").execute()) { + assertEquals(3, outerArray.getLength()); + try (V8ValueArray innerArray = v8Runtime.createV8ValueArray()) { + assertEquals(1, innerArray.push("a")); + assertEquals(1, innerArray.getLength()); + assertEquals(4, outerArray.push(innerArray)); + } + assertEquals("[1,2,3,[\"a\"]]", outerArray.toJsonString()); + } + } + + @Test + public void testPushPop() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("[]").execute()) { + assertEquals(0, v8ValueArray.getLength()); + assertEquals(1, v8ValueArray.push(true)); + assertEquals(2, v8ValueArray.push(1.23)); + assertEquals(3, v8ValueArray.push(4)); + assertEquals(4, v8ValueArray.push(5L)); + assertEquals(5, v8ValueArray.push("x")); + assertEquals(6, v8ValueArray.pushNull()); + assertEquals(7, v8ValueArray.pushUndefined()); + assertEquals(7, v8ValueArray.getLength()); + assertEquals("true,1.23,4,5,x,,", v8ValueArray.toString()); + assertNotNull(v8ValueArray.popUndefined()); + assertNotNull(v8ValueArray.popNull()); + assertEquals("x", v8ValueArray.popString()); + assertEquals(5L, v8ValueArray.popLong()); + assertEquals(4, v8ValueArray.popInteger()); + assertEquals(1.23, v8ValueArray.popDouble(), 0.001); + assertEquals(true, v8ValueArray.popBoolean()); + assertEquals(0, v8ValueArray.getLength()); + assertEquals("", v8ValueArray.toString()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java new file mode 100644 index 000000000..47c4236f5 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueError.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.BaseTestJavetRuntime; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueError extends BaseTestJavetRuntime { + @Test + public void testError() throws JavetException { + try (V8ValueError v8ValueError = v8Runtime.getExecutor("Error('test')").execute()) { + assertNotNull(v8ValueError); + assertEquals("test", v8ValueError.getMessage()); + assertEquals("Error: test\n at :1:1", v8ValueError.getStack()); + try (IV8ValueArray iV8ValueArray = v8ValueError.getOwnPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(0, iV8ValueArray.getLength()); + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java new file mode 100644 index 000000000..04c3d0f20 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetExecutionException; +import com.caoccao.javet.exceptions.JavetV8ValueAlreadyClosedException; +import com.caoccao.javet.mock.MockCallbackReceiver; +import com.caoccao.javet.utils.V8CallbackContext; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.primitive.V8ValueUndefined; +import org.junit.jupiter.api.Test; + +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueFunction extends BaseTestJavetRuntime { + @Test + public void testArrayPush() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = []; a;").execute()) { + assertNotNull(v8ValueArray); + try (V8ValueFunction v8ValueFunctionPush = v8ValueArray.get("push")) { + assertNotNull(v8ValueFunctionPush); + assertEquals("function push() { [native code] }", v8ValueFunctionPush.toString()); + assertEquals(1, v8ValueFunctionPush.callInteger(v8ValueArray, new V8ValueString("x"))); + } + assertEquals(1, v8ValueArray.getLength()); + assertEquals("x", v8ValueArray.toString()); + } + } + + @Test + public void testCallbackBlankWithoutThis() throws JavetException, NoSuchMethodException { + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("blank")); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext); + assertTrue(v8CallbackContext.getHandle() > 0L); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + globalObject.set("a", a); + a.set("blank", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + v8Runtime.getExecutor("a.blank();").executeVoid(); + assertTrue(mockCallbackReceiver.isCalled()); + v8ValueFunction.setWeak(); + a.delete("blank"); + globalObject.delete("a"); + } + assertEquals(1, v8Runtime.getReferenceCount()); + v8Runtime.requestGarbageCollectionForTesting(true); + assertEquals(0, v8Runtime.getReferenceCount()); + assertThrows(JavetV8ValueAlreadyClosedException.class, () -> v8ValueFunction.close()); + } + + @Test + public void testCallbackBlankWithThis() throws JavetException, NoSuchMethodException { + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", true), true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext); + assertTrue(v8CallbackContext.getHandle() > 0L); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + globalObject.set("a", a); + a.set("x", new V8ValueString("1")); + a.set("echoThis", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + assertEquals("{\"x\":\"1\"}", v8Runtime.getExecutor("a.echoThis();").executeString()); + assertTrue(mockCallbackReceiver.isCalled()); + v8ValueFunction.setWeak(); + a.delete("echoThis"); + globalObject.delete("a"); + } + assertEquals(1, v8Runtime.getReferenceCount()); + v8Runtime.requestGarbageCollectionForTesting(true); + assertEquals(0, v8Runtime.getReferenceCount()); + assertThrows(JavetV8ValueAlreadyClosedException.class, () -> v8ValueFunction.close()); + } + + @Test + public void testCallbackEchoLILOWithoutThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethodVarargs("echo")); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("echo", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("var a = echo(1, '2', 3n); a;").execute()) { + assertEquals(3, v8ValueArray.getLength()); + assertEquals(1, v8ValueArray.getInteger(0)); + assertEquals("2", v8ValueArray.getString(1)); + assertEquals(3L, v8ValueArray.getLong(2)); + } + assertTrue(globalObject.hasOwnProperty("a")); + assertTrue(mockCallbackReceiver.isCalled()); + globalObject.delete("echo"); + } + } + + @Test + public void testCallbackEchoLILOWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethodVarargs("echoThis", true), true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("x", new V8ValueString("1")); + globalObject.set("echoThis", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("var a = echoThis(1, '2', 3n); a;").execute()) { + assertEquals(2, v8Runtime.getReferenceCount()); + assertEquals(4, v8ValueArray.getLength()); + try (V8ValueObject v8ValueObject = v8ValueArray.get(0)) { + assertEquals("1", v8ValueObject.getString("x")); + } + assertEquals(1, v8ValueArray.getInteger(1)); + assertEquals("2", v8ValueArray.getString(2)); + assertEquals(3L, v8ValueArray.getLong(3)); + } + assertTrue(globalObject.hasOwnProperty("a")); + assertTrue(mockCallbackReceiver.isCalled()); + globalObject.delete("echoThis"); + } + } + + @Test + public void testCallbackEchoStringWithoutThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoString", String.class)); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("echoString", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + assertEquals("abc", v8Runtime.getExecutor("const a = echoString('abc'); a;").executeString()); + } + assertTrue(mockCallbackReceiver.isCalled()); + } + + @Test + public void testCallbackEchoStringWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThisString", V8Value.class, String.class), true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + a.set("x", new V8ValueString("1")); + a.set("echoThisString", v8ValueFunction); + globalObject.set("a", a); + } + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueString v8ValueString = v8Runtime.getExecutor( + "const x = a.echoThisString('123'); x;").execute()) { + assertEquals("[{\"x\":\"1\"},\"123\"]", v8ValueString.getValue()); + } + } + assertTrue(mockCallbackReceiver.isCalled()); + } + + @Test + public void testCallbackEchoVIVOWithoutThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("echo", V8Value.class)); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("echo", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueInteger v8ValueInteger = v8Runtime.getExecutor("const a = echo(1); a;").execute()) { + assertEquals(1, v8ValueInteger.getValue()); + } + globalObject.delete("echo"); + } + assertTrue(mockCallbackReceiver.isCalled()); + assertTrue(globalObject.get("a") instanceof V8ValueUndefined); + } + + @Test + public void testCallbackEchoVIVOWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("echoThis", V8Value.class, V8Value.class), true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + a.set("x", new V8ValueString("1")); + a.set("echoThis", v8ValueFunction); + globalObject.set("a", a); + } + assertFalse(mockCallbackReceiver.isCalled()); + try (V8ValueString v8ValueString = v8Runtime.getExecutor("const x = a.echoThis('123'); x;").execute()) { + assertEquals("[{\"x\":\"1\"},\"123\"]", v8ValueString.getValue()); + } + } + assertTrue(mockCallbackReceiver.isCalled()); + } + + @Test + public void testCallbackError() throws JavetException, NoSuchMethodException { + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("error")); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("testError", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + try { + v8Runtime.getExecutor("testError();").executeVoid(); + fail("Failed to report Java error."); + } catch (JavetExecutionException e) { + assertEquals("Error: Mock error", e.getMessage()); + } + assertTrue(mockCallbackReceiver.isCalled()); + try (V8ValueError v8ValueError = v8Runtime.getExecutor( + "let a; try { testError(); } catch (error) { a = error; } a;").execute()) { + assertNotNull(v8ValueError); + assertEquals("Mock error", v8ValueError.getMessage()); + } + globalObject.delete("testError"); + } + mockCallbackReceiver.setCalled(false); + } + + @Test + public void testCallbackJoinWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, + mockCallbackReceiver.getMethod("joinWithThis", + V8ValueObject.class, Boolean.class, Double.class, Integer.class, Long.class, + String.class, ZonedDateTime.class, V8ValueString.class), + true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueObject x = v8Runtime.createV8ValueObject()) { + globalObject.set("x", x); + x.set("p", new V8ValueString("q")); + x.set("joinWithThis", v8ValueFunction); + } + assertFalse(mockCallbackReceiver.isCalled()); + String resultString = v8Runtime.getExecutor( + "const a = x.joinWithThis(true, 1.23, 2, 3n, '4', new Date(1611710223719), '6'); a;").executeString(); + assertEquals("{\"p\":\"q\"},true,1.23,2,3,4,2021-01-27T01:17:03.719Z[UTC],6", resultString); + } + assertTrue(mockCallbackReceiver.isCalled()); + } + + @Test + public void testCallbackJoinIntegerArrayWithThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, + mockCallbackReceiver.getMethod( + "joinIntegerArrayWithThis", V8ValueObject.class, String.class, Integer[].class), + true); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueObject x = v8Runtime.createV8ValueObject()) { + globalObject.set("x", x); + x.set("p", new V8ValueString("q")); + x.set("joinIntegerArrayWithThis", v8ValueFunction); + } + assertFalse(mockCallbackReceiver.isCalled()); + String resultString = v8Runtime.getExecutor( + "const a = x.joinIntegerArrayWithThis('x', 1, 2, 3); a;").executeString(); + assertEquals("{\"p\":\"q\"},x,1,2,3", resultString); + } + assertTrue(mockCallbackReceiver.isCalled()); + } + + @Test + public void testCallbackJoinWithoutThis() throws JavetException, NoSuchMethodException { + assertEquals(0, v8Runtime.getReferenceCount()); + MockCallbackReceiver mockCallbackReceiver = new MockCallbackReceiver(v8Runtime); + V8CallbackContext v8CallbackContext = new V8CallbackContext( + mockCallbackReceiver, mockCallbackReceiver.getMethod("joinWithoutThis", + Boolean.class, Double.class, Integer.class, Long.class, String.class, ZonedDateTime.class, + V8ValueString.class)); + V8ValueObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueFunction v8ValueFunction = v8Runtime.createV8ValueFunction(v8CallbackContext)) { + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.set("joinWithoutThis", v8ValueFunction); + assertFalse(mockCallbackReceiver.isCalled()); + String resultString = v8Runtime.getExecutor( + "const a = joinWithoutThis(true, 1.23, 2, 3n, '4', new Date(1611710223719), '6'); a;").executeString(); + assertEquals("true,1.23,2,3,4,2021-01-27T01:17:03.719Z[UTC],6", resultString); + } + assertTrue(mockCallbackReceiver.isCalled()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueGlobalObject.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueGlobalObject.java new file mode 100644 index 000000000..c617589ee --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueGlobalObject.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueGlobalObject extends BaseTestJavetRuntime { + @Test + public void testGetAndSetProperty() throws JavetException { + assertEquals(0, v8Runtime.getReferenceCount()); + try (V8ValueGlobalObject v8RuntimeGlobalObject = v8Runtime.getGlobalObject()) { + assertEquals(0, v8Runtime.getReferenceCount()); + assertNotNull(v8RuntimeGlobalObject); + v8RuntimeGlobalObject.setProperty("a", new V8ValueInteger(1)); + assertEquals(1, v8RuntimeGlobalObject.getPropertyInteger("a")); + } + assertEquals(0, v8Runtime.getReferenceCount()); + v8Runtime.getExecutor("var b = 3;").executeVoid(); + try (V8ValueGlobalObject v8RuntimeGlobalObject = v8Runtime.getGlobalObject()) { + assertNotNull(v8RuntimeGlobalObject); + assertEquals(3, v8RuntimeGlobalObject.getPropertyInteger("b")); + } + assertEquals(2, v8Runtime.getExecutor("a + 1").executeInteger()); + assertEquals(4, v8Runtime.getExecutor("a + b").executeInteger()); + } + + @Test + public void testInvokeGlobalFunction() throws JavetException { + v8Runtime.getExecutor("function a() { return 1; }").executeVoid(); + try (V8ValueGlobalObject v8RuntimeGlobalObject = v8Runtime.getGlobalObject()) { + assertEquals(1, v8RuntimeGlobalObject.invokeInteger("a")); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java new file mode 100644 index 000000000..31d8f190e --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.virtual.V8VirtualList; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueMap extends BaseTestJavetRuntime { + @Test + public void testGetAndHas() throws JavetException { + try (V8ValueMap v8ValueMap = v8Runtime.getExecutor( + "const a = new Map(); a.set('x', 1); a.set('y', 'b'); a.set(3, 'c'); a;").execute()) { + assertNotNull(v8ValueMap); + assertEquals(3, v8ValueMap.getSize()); + assertEquals(1, v8ValueMap.getInteger("x")); + assertEquals("b", v8ValueMap.getString("y")); + assertEquals("c", v8ValueMap.getString(3)); + assertTrue(v8ValueMap.has("x")); + assertTrue(v8ValueMap.has(3)); + assertFalse(v8ValueMap.has("p")); + assertFalse(v8ValueMap.has(0)); + assertEquals("{}", v8ValueMap.toJsonString()); + assertEquals("[object Map]", v8ValueMap.toString()); + assertEquals("[object Map]", v8ValueMap.toProtoString()); + try (IV8ValueArray iV8ValueArray = v8ValueMap.getOwnPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(0, iV8ValueArray.getLength()); + } + List keys = v8ValueMap.getKeys(); + assertEquals(3, keys.size()); + assertEquals("x", ((V8ValueString) keys.get(0)).getValue()); + assertEquals("y", ((V8ValueString) keys.get(1)).getValue()); + assertEquals(3, ((V8ValueInteger) keys.get(2)).getValue()); + List values = v8ValueMap.getValues(); + assertEquals(3, values.size()); + assertEquals(1, ((V8ValueInteger) values.get(0)).getValue()); + assertEquals("b", ((V8ValueString) values.get(1)).getValue()); + assertEquals("c", ((V8ValueString) values.get(2)).getValue()); + try (V8VirtualList entries = v8ValueMap.getEntries()) { + assertEquals(3, values.size()); + assertEquals(4, v8Runtime.getReferenceCount()); + V8ValueArray entry = (V8ValueArray) entries.get(0); + assertEquals("x", entry.getString(0)); + assertEquals(1, entry.getInteger(1)); + entry = (V8ValueArray) entries.get(1); + assertEquals("y", entry.getString(0)); + assertEquals("b", entry.getString(1)); + entry = (V8ValueArray) entries.get(2); + assertEquals(3, entry.getInteger(0)); + assertEquals("c", entry.getString(1)); + } + assertEquals(1, v8Runtime.getReferenceCount()); + } + } + + @Test + public void testGetSetAndDelete() throws JavetException { + try (V8ValueMap v8ValueMap = v8Runtime.getExecutor("const a = new Map(); a;").execute()) { + v8ValueMap.set("a", new V8ValueInteger(1)); + v8ValueMap.set("b", new V8ValueString("2")); + assertEquals(2, v8ValueMap.getSize()); + assertEquals(1, v8ValueMap.getInteger("a")); + assertEquals("2", v8ValueMap.getString("b")); + v8ValueMap.setNull("c"); + v8ValueMap.setUndefined("d"); + assertEquals(4, v8ValueMap.getSize()); + assertTrue(v8ValueMap.has("a")); + assertFalse(v8ValueMap.has("x")); + assertNotNull(v8ValueMap.getNull("c")); + assertNotNull(v8ValueMap.getUndefined("d")); + assertTrue(v8ValueMap.delete("c")); + assertTrue(v8ValueMap.delete("d")); + assertFalse(v8ValueMap.delete(2)); + assertEquals(2, v8ValueMap.getSize()); + assertTrue(v8ValueMap.delete("a")); + assertTrue(v8ValueMap.delete("b")); + assertEquals(0, v8ValueMap.getSize()); + } + } + + @Test + public void testNestedMap() throws JavetException { + try (V8ValueMap outerObject = v8Runtime.getExecutor("const o = new Map(); o;").execute()) { + assertEquals( + "[]", + v8Runtime.getExecutor( + "JSON.stringify(o, (key, value) => value instanceof Map ? [...value] : value);").executeString()); + try (V8ValueMap innerObject = v8Runtime.createV8ValueMap()) { + innerObject.set("a", new V8ValueString("1")); + outerObject.set("x", innerObject); + } + assertEquals( + "[[\"x\",[[\"a\",\"1\"]]]]", + v8Runtime.getExecutor( + "JSON.stringify(o, (key, value) => value instanceof Map ? [...value] : value);").executeString()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java new file mode 100644 index 000000000..b18bf0de0 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.*; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueObject extends BaseTestJavetRuntime { + @Test + public void testClearWeak() throws JavetException { + V8ValueObject a = v8Runtime.createV8ValueObject(); + V8ValueGlobalObject globalObject = v8Runtime.getGlobalObject(); + globalObject.set("a", a); + a.setWeak(); + assertTrue(a.isWeak()); + assertEquals(1, v8Runtime.getReferenceCount()); + a.close(); + assertEquals(1, v8Runtime.getReferenceCount(), + "Close() should not work because 'a' is weak."); + a.clearWeak(); + assertFalse(a.isWeak()); + assertEquals(1, v8Runtime.getReferenceCount()); + a.close(); + assertEquals(0, v8Runtime.getReferenceCount()); + assertEquals(0L, a.getHandle()); + try (V8ValueObject b = globalObject.get("a")) { + assertTrue(b instanceof V8ValueObject); + } + } + + @Test + public void testGetOwnPropertyNames() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( + "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1}, '中文': '測試'}; x;").execute()) { + try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(7, iV8ValueArray.getLength()); + // Order is preserved since ES2015. + assertEquals("a", iV8ValueArray.getPropertyString(0)); + assertEquals("b", iV8ValueArray.getPropertyString(1)); + assertEquals("c", iV8ValueArray.getPropertyString(2)); + assertEquals("d", iV8ValueArray.getPropertyString(3)); + assertEquals("e", iV8ValueArray.getPropertyString(4)); + assertEquals("g", iV8ValueArray.getPropertyString(5)); + assertEquals("中文", iV8ValueArray.getPropertyString(6)); + } + } + } + + @Test + public void testGetPropertyNames() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( + "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1}, '中文': '測試'}; x;").execute()) { + try (IV8ValueArray iV8ValueArray = v8ValueObject.getPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(7, iV8ValueArray.getLength()); + // Order is preserved since ES2015. + assertEquals("a", iV8ValueArray.getPropertyString(0)); + assertEquals("b", iV8ValueArray.getPropertyString(1)); + assertEquals("c", iV8ValueArray.getPropertyString(2)); + assertEquals("d", iV8ValueArray.getPropertyString(3)); + assertEquals("e", iV8ValueArray.getPropertyString(4)); + assertEquals("g", iV8ValueArray.getPropertyString(5)); + assertEquals("中文", iV8ValueArray.getPropertyString(6)); + } + } + } + + @Test + public void testGetSetDelete() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const a = {}; a;").execute()) { + assertTrue(v8ValueObject.set("a", new V8ValueInteger(1))); + assertTrue(v8ValueObject.set("b", new V8ValueString("2"))); + assertEquals(1, v8ValueObject.getInteger("a")); + assertEquals("2", v8ValueObject.getString("b")); + assertTrue(v8ValueObject.delete("x")); + assertTrue(v8ValueObject.delete("b")); + V8Value v8Value = v8ValueObject.getUndefined("b"); + assertNotNull(v8Value); + } + } + + @Test + public void testGetProperty() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( + "let x = {'a': 1, 'b': '2', 'c': 3n, d: 1, e: null, g: {h: 1, 3: 'x'}, '中文': '測試'};" + + "x['i'] = true;x['j'] = 1.23;x['k'] = new Date(1611710223719);" + + "x;").execute()) { + assertNotNull(v8ValueObject); + assertEquals(v8Runtime, v8ValueObject.getV8Runtime()); + assertEquals(1, ((V8ValueInteger) v8ValueObject.getProperty("a")).getValue()); + assertEquals(1, v8ValueObject.getPropertyInteger("a")); + assertEquals("2", ((V8ValueString) v8ValueObject.getProperty("b")).getValue()); + assertEquals("2", v8ValueObject.getPropertyString("b")); + assertEquals(3L, ((V8ValueLong) v8ValueObject.getProperty("c")).getValue()); + assertEquals(3L, v8ValueObject.getPropertyLong("c")); + assertEquals(1, v8ValueObject.getPropertyInteger("d")); + assertTrue(v8ValueObject.getProperty("e") instanceof V8ValueNull); + assertEquals("測試", v8ValueObject.getPropertyString("中文")); + assertTrue(v8ValueObject.getProperty("$") instanceof V8ValueUndefined); + assertEquals(1, v8Runtime.getReferenceCount()); + try (V8ValueObject childV8ValueObject = v8ValueObject.getProperty("g")) { + assertNotNull(childV8ValueObject); + assertEquals(v8Runtime, childV8ValueObject.getV8Runtime()); + assertEquals(1, childV8ValueObject.getPropertyInteger("h")); + assertEquals("x", childV8ValueObject.getPropertyString(3)); + assertTrue(childV8ValueObject.hasOwnProperty("h")); + assertTrue(childV8ValueObject.hasOwnProperty(3)); + assertFalse(childV8ValueObject.hasOwnProperty("p")); + assertFalse(childV8ValueObject.hasOwnProperty(1)); + assertEquals(2, v8Runtime.getReferenceCount()); + } + assertTrue(v8ValueObject.getPropertyBoolean("i")); + assertEquals(1.23, v8ValueObject.getPropertyDouble("j"), 0.001); + assertEquals( + "2021-01-27T01:17:03.719Z[UTC]", + v8ValueObject.getPropertyZonedDateTime("k").withZoneSameInstant(ZoneId.of("UTC")).toString()); + assertEquals(1, v8Runtime.getReferenceCount()); + } + } + + @Test + public void testInvoke() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = [1, 2, 3]; a;").execute()) { + assertEquals(3, v8ValueArray.getLength()); + v8ValueArray.invoke("push", new V8ValueInteger(4)); + assertEquals(4, v8ValueArray.getLength()); + assertEquals(4, v8ValueArray.getInteger(3)); + assertEquals("1,2,3,4", v8ValueArray.toString()); + } + } + + @Test + public void testNestedObject() throws JavetException { + try (V8ValueObject outerObject = v8Runtime.getExecutor("const o = {}; o;").execute()) { + assertEquals("{}", outerObject.toJsonString()); + try (V8ValueObject innerObject = v8Runtime.createV8ValueObject()) { + innerObject.set("a", new V8ValueString("1")); + outerObject.set("x", innerObject); + } + assertEquals("{\"x\":{\"a\":\"1\"}}", outerObject.toJsonString()); + } + } + + @Test + public void testSetProperty() throws JavetException { + ZonedDateTime now = ZonedDateTime.now(); + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { + assertNotNull(v8ValueObject); + assertEquals(v8Runtime, v8ValueObject.getV8Runtime()); + try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { + assertEquals(0, iV8ValueArray.getLength()); + } + v8ValueObject.setProperty("a", new V8ValueString("1")); + v8ValueObject.setProperty(new V8ValueString("b"), new V8ValueInteger(2)); + v8ValueObject.setProperty(new V8ValueString("c"), new V8ValueLong(3)); + v8ValueObject.setProperty(new V8ValueString("d"), new V8ValueZonedDateTime(now)); + v8ValueObject.setProperty(new V8ValueString("e"), new V8ValueDouble(1.23)); + v8ValueObject.setProperty(new V8ValueString("f"), new V8ValueBoolean(true)); + try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { + assertEquals(6, iV8ValueArray.getLength()); + assertEquals("a", iV8ValueArray.getString(0)); + assertEquals("b", iV8ValueArray.getString(1)); + assertEquals("c", iV8ValueArray.getString(2)); + assertEquals("d", iV8ValueArray.getString(3)); + assertEquals("e", iV8ValueArray.getString(4)); + assertEquals("f", iV8ValueArray.getString(5)); + } + assertEquals("1", v8ValueObject.getPropertyString("a")); + assertEquals(2, v8ValueObject.getPropertyInteger("b")); + assertEquals(3L, v8ValueObject.getPropertyLong("c")); + assertEquals( + now.toInstant().toEpochMilli(), + v8ValueObject.getPropertyZonedDateTime("d").toInstant().toEpochMilli()); + assertEquals(1.23, v8ValueObject.getPropertyDouble("e"), 0.001); + assertTrue(v8ValueObject.getPropertyBoolean("f")); + assertEquals("[object Object]", v8ValueObject.toString()); + assertEquals("[object Object]", v8ValueObject.toProtoString()); + } + } + + @Test + public void testToClone() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { + v8ValueObject.setProperty("a", new V8ValueString("1")); + assertEquals("{\"a\":\"1\"}", v8ValueObject.toJsonString()); + try (V8ValueObject clonedV8ValueObject = v8ValueObject.toClone()) { + assertEquals("{\"a\":\"1\"}", clonedV8ValueObject.toJsonString()); + assertNotEquals(v8ValueObject.getHandle(), clonedV8ValueObject.getHandle()); + assertEquals(v8Runtime, clonedV8ValueObject.getV8Runtime()); + } + } + } + + @Test + public void testToJsonString() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const x = {}; x;").execute()) { + v8ValueObject.setProperty("a", new V8ValueString("1")); + v8ValueObject.setProperty("b", new V8ValueInteger(2)); + v8ValueObject.setProperty("c", new V8ValueDouble(1.23)); + assertEquals("{\"a\":\"1\",\"b\":2,\"c\":1.23}", v8ValueObject.toJsonString()); + } + } + + @Test + public void testSetWeakDirectDescendant() throws JavetException { + V8ValueObject a = v8Runtime.createV8ValueObject(); + V8ValueGlobalObject globalObject = v8Runtime.getGlobalObject(); + globalObject.set("a", a); + a.setWeak(); + assertTrue(a.isWeak()); + assertEquals(1, v8Runtime.getReferenceCount()); + a.close(); + assertEquals(1, v8Runtime.getReferenceCount(), + "Close() should not work because 'a' is weak."); + a.clearWeak(); + assertFalse(a.isWeak()); + assertEquals(1, v8Runtime.getReferenceCount()); + a.setWeak(); + globalObject.delete("a"); + v8Runtime.requestGarbageCollectionForTesting(true); + assertEquals(0, v8Runtime.getReferenceCount()); + assertEquals(0L, a.getHandle()); + assertTrue(globalObject.get("a") instanceof V8ValueUndefined); + } + + @Test + public void testSetWeakIndirectDescendant() throws JavetException { + V8ValueGlobalObject globalObject = v8Runtime.getGlobalObject(); + try (V8ValueObject a = v8Runtime.createV8ValueObject()) { + globalObject.set("a", a); + V8ValueObject b = v8Runtime.createV8ValueObject(); + a.set("b", b); + b.setWeak(); + } + assertEquals(1, v8Runtime.getReferenceCount()); + globalObject.delete("a"); + v8Runtime.requestGarbageCollectionForTesting(true); + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValuePromise.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValuePromise.java new file mode 100644 index 000000000..49e94d88e --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValuePromise.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValuePromise extends BaseTestJavetRuntime { + @Test + public void testPromise() throws JavetException { + try (V8ValuePromise v8ValuePromise = v8Runtime.getExecutor("new Promise(()=>{})").execute()) { + assertNotNull(v8ValuePromise); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java new file mode 100644 index 000000000..eca88a7dc --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueProxy.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.BaseTestJavetRuntime; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueProxy extends BaseTestJavetRuntime { + @Test + public void testProxy() throws JavetException { + try (V8ValueProxy v8ValueProxy = v8Runtime.getExecutor( + "const b = {}; const a = new Proxy(RegExp, b); a;").execute()) { + assertNotNull(v8ValueProxy); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java new file mode 100644 index 000000000..53b1ef2af --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueRegExp.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class TestV8ValueRegExp extends BaseTestJavetRuntime { + @Test + public void testRegExp() throws JavetException { + try (V8ValueRegExp v8ValueRegExp = v8Runtime.getExecutor("/123/g").execute()) { + assertNotNull(v8ValueRegExp); + } + try (V8ValueRegExp v8ValueRegExp = v8Runtime.getExecutor("new RegExp('123')").execute()) { + assertNotNull(v8ValueRegExp); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java new file mode 100644 index 000000000..ff0542375 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.virtual.V8VirtualList; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueSet extends BaseTestJavetRuntime { + @Test + public void testAddHasAndDelete() throws JavetException { + try (V8ValueSet v8ValueSet = v8Runtime.getExecutor("const a = new Set(); a;").execute()) { + assertNotNull(v8ValueSet); + assertEquals(0, v8ValueSet.getSize()); + v8ValueSet.add(1); + assertEquals(1, v8ValueSet.getSize()); + v8ValueSet.add(1); + assertEquals(1, v8ValueSet.getSize()); + v8ValueSet.add("x"); + assertEquals(2, v8ValueSet.getSize()); + v8ValueSet.addNull(); + v8ValueSet.addNull(); + v8ValueSet.addUndefined(); + v8ValueSet.addUndefined(); + assertEquals(4, v8ValueSet.getSize()); + assertTrue(v8ValueSet.has(1)); + assertTrue(v8ValueSet.has("x")); + assertTrue(v8ValueSet.hasNull()); + assertTrue(v8ValueSet.hasUndefined()); + assertTrue(v8ValueSet.deleteNull()); + assertTrue(v8ValueSet.deleteUndefined()); + assertFalse(v8ValueSet.delete(2)); + assertEquals(2, v8ValueSet.getSize()); + assertTrue(v8ValueSet.delete(1)); + assertTrue(v8ValueSet.delete("x")); + assertEquals(0, v8ValueSet.getSize()); + } + } + + @Test + public void testHas() throws JavetException { + try (V8ValueSet v8ValueSet = v8Runtime.getExecutor( + "const a = new Set(); a.add('x', 1); a.add('y', 'b'); a.add(3, 'c'); a;").execute()) { + assertNotNull(v8ValueSet); + assertEquals(3, v8ValueSet.getSize()); + assertTrue(v8ValueSet.has("x")); + assertTrue(v8ValueSet.has("y")); + assertTrue(v8ValueSet.has(3)); + assertFalse(v8ValueSet.has("p")); + assertFalse(v8ValueSet.has(0)); + assertEquals("{}", v8ValueSet.toJsonString()); + assertEquals("[object Set]", v8ValueSet.toString()); + assertEquals("[object Set]", v8ValueSet.toProtoString()); + try (IV8ValueArray iV8ValueArray = v8ValueSet.getOwnPropertyNames()) { + assertNotNull(iV8ValueArray); + assertEquals(0, iV8ValueArray.getLength()); + } + try (V8VirtualList keys = v8ValueSet.getKeys()) { + assertEquals(3, keys.size()); + assertEquals("x", ((V8ValueString) keys.get(0)).getValue()); + assertEquals("y", ((V8ValueString) keys.get(1)).getValue()); + assertEquals(3, ((V8ValueInteger) keys.get(2)).getValue()); + } + } + } + + @Test + public void testNestedSet() throws JavetException { + try (V8ValueSet outerObject = v8Runtime.getExecutor("const o = new Set(); o;").execute()) { + assertEquals( + "[]", + v8Runtime.getExecutor( + "JSON.stringify(o, (key, value) => value instanceof Set ? [...value] : value);").executeString()); + outerObject.add(new V8ValueString("1")); + try (V8ValueSet innerObject = v8Runtime.createV8ValueSet()) { + innerObject.add(new V8ValueString("2")); + outerObject.add(innerObject); + } + assertEquals( + "[\"1\",[\"2\"]]", + v8Runtime.getExecutor( + "JSON.stringify(o, (key, value) => value instanceof Set ? [...value] : value);").executeString()); + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java new file mode 100644 index 000000000..0f02d9c02 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSymbol.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021. caoccao.com Sam Cao + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.caoccao.javet.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetExecutionException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestV8ValueSymbol extends BaseTestJavetRuntime { + @Test + public void testSymbol() throws JavetException { + try (V8ValueSymbol v8ValueSymbol = v8Runtime.getExecutor("Symbol('test')").execute()) { + assertNotNull(v8ValueSymbol); + assertEquals("test", v8ValueSymbol.getDescription()); + assertEquals("Symbol(test)", v8ValueSymbol.toString()); + } + try (V8ValueSymbol v8ValueSymbol = v8Runtime.getExecutor("Symbol(123)").execute()) { + assertNotNull(v8ValueSymbol); + assertEquals("123", v8ValueSymbol.getDescription()); + assertEquals("Symbol(123)", v8ValueSymbol.toString()); + } + } +}