diff --git a/README.rst b/README.rst index e0829a5df..4ff53a24b 100644 --- a/README.rst +++ b/README.rst @@ -33,7 +33,7 @@ Maven com.caoccao.javet javet - 0.7.1 + 0.7.2 Gradle Kotlin DSL @@ -41,14 +41,14 @@ Gradle Kotlin DSL .. code-block:: kotlin - implementation("com.caoccao.javet:javet:0.7.1") + implementation("com.caoccao.javet:javet:0.7.2") Gradle Groovy DSL ^^^^^^^^^^^^^^^^^ .. code-block:: groovy - implementation 'com.caoccao.javet:javet:0.7.1' + implementation 'com.caoccao.javet:javet:0.7.2' Hello Javet ----------- @@ -62,25 +62,15 @@ Hello Javet Documents ========= -* `Build `_ -* `Development `_ +* `Development `_ + * `Build `_ + * `Design `_ + * `Performance `_ * `Tutorial `_ -* `Performance `_ * `Release Notes `_ * `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 ======= diff --git a/build.gradle.kts b/build.gradle.kts index a3cd87abb..502f0d83e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,7 +26,7 @@ repositories { } group = "com.caoccao.javet" -version = "0.7.1" +version = "0.7.2" repositories { mavenCentral() diff --git a/cpp/build.cmd b/cpp/build.cmd index 57eb833bb..cb16ea9bc 100644 --- a/cpp/build.cmd +++ b/cpp/build.cmd @@ -1,6 +1,6 @@ @echo off REM Usage sample: build -DV8_DIR=C:\v8 -SET JAVET_VERSION=0.7.1 +SET JAVET_VERSION=0.7.2 rd /s/q build mkdir build cd build diff --git a/cpp/build.sh b/cpp/build.sh index a5a38dbac..827e3913e 100755 --- a/cpp/build.sh +++ b/cpp/build.sh @@ -1,7 +1,7 @@ #!/bin/sh # Usage sample: build -DV8_DIR=~/v8 -JAVET_VERSION=0.7.1 +JAVET_VERSION=0.7.2 rm -rf build mkdir build cd build diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.cpp b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp index f7d22f1f6..21e8652ca 100644 --- a/cpp/jni/com_caoccao_javet_interop_V8Native.cpp +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.cpp @@ -44,6 +44,7 @@ #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_ARRAY_BUFFER(type) (type == Javet::Enums::V8ValueReferenceType::ArrayBuffer) #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) @@ -69,7 +70,18 @@ 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); + auto v8LocalObject = v8PersistentObjectPointer->Get(v8Context->GetIsolate()); + +#define RUNTIME_AND_2_VALUES_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle1, v8ValueHandle2) \ + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); \ + auto v8PersistentObjectPointer1 = reinterpret_cast*>(v8ValueHandle1); \ + auto v8PersistentObjectPointer2 = reinterpret_cast*>(v8ValueHandle2); \ + 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 v8LocalObject1 = v8PersistentObjectPointer1->Get(v8Context->GetIsolate()); \ + auto v8LocalObject2 = v8PersistentObjectPointer2->Get(v8Context->GetIsolate()); #define SAFE_CONVERT_AND_RETURN_JAVE_V8_VALUE(jniEnv, v8Context, v8Value) \ try { \ @@ -182,6 +194,30 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_call return nullptr; } +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_callAsConstructor +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType, jobjectArray mValues) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + if (v8LocalObject->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 = v8LocalObject.As()->CallAsConstructor(v8Context, valueCount, umValuesPointer.get()); + } + else { + maybeLocalValueResult = v8LocalObject.As()->CallAsConstructor(v8Context, 0, nullptr); + } + if (v8TryCatch.HasCaught()) { + Javet::Exceptions::ThrowJavetExecutionException(jniEnv, v8Context, v8TryCatch); + } + else { + 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); @@ -259,6 +295,11 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Value else if (IS_V8_ARRAY(v8ValueType)) { v8ValueValue = v8::Array::New(v8Context->GetIsolate()); } + else if (IS_V8_ARRAY_BUFFER(v8ValueType)) { + if (IS_JAVA_INTEGER(jniEnv, mContext)) { + v8ValueValue = v8::ArrayBuffer::New(v8Context->GetIsolate(), TO_JAVA_INTEGER(jniEnv, mContext)); + } + } else if (IS_V8_FUNCTION(v8ValueType)) { jobject umContext = jniEnv->NewGlobalRef(mContext); Javet::Callback::JavetCallbackContextReference javetCallbackContextReference(jniEnv, umContext); @@ -309,6 +350,12 @@ JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_delete return false; } +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_equals +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle1, jlong v8ValueHandle2) { + RUNTIME_AND_2_VALUES_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle1, v8ValueHandle2); + return v8LocalObject1->Equals(v8Context, v8LocalObject2).FromMaybe(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) { @@ -342,7 +389,7 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_get 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_V8_ARGUMENTS(v8ValueType) || IS_V8_ARRAY(v8ValueType) || v8LocalObject->IsTypedArray()) { if (IS_JAVA_INTEGER(jniEnv, key)) { jint integerKey = TO_JAVA_INTEGER(jniEnv, key); if (integerKey >= 0) { @@ -382,11 +429,20 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getGlobalObjec return Javet::Converter::ToExternalV8ValueGlobalObject(jniEnv, v8Runtime->v8GlobalObject); } +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getIdentityHash +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle, jint v8ValueType) { + RUNTIME_AND_VALUE_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle); + return v8LocalObject->GetIdentityHash(); +} + 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 (jint)v8LocalObject.As()->Length(); + } + if (v8LocalObject->IsTypedArray()) { + return (jint)v8LocalObject.As()->Length(); } return 0; } @@ -513,6 +569,18 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_invoke return nullptr; } +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isDead +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + return v8Runtime->v8Isolate->IsDead(); +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isInUse +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + return v8Runtime->v8Isolate->IsInUse(); +} + 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); @@ -670,6 +738,24 @@ JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setWeak } } +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_sameValue +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle1, jlong v8ValueHandle2) { + RUNTIME_AND_2_VALUES_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle1, v8ValueHandle2); + return v8LocalObject1->SameValue(v8LocalObject2); +} + +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_strictEquals +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle, jlong v8ValueHandle1, jlong v8ValueHandle2) { + RUNTIME_AND_2_VALUES_HANDLES_TO_OBJECTS_WITH_SCOPE(v8RuntimeHandle, v8ValueHandle1, v8ValueHandle2); + return v8LocalObject1->StrictEquals(v8LocalObject2); +} + +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_terminateExecution +(JNIEnv* jniEnv, jclass callerClass, jlong v8RuntimeHandle) { + auto v8Runtime = reinterpret_cast(v8RuntimeHandle); + v8Runtime->v8Isolate->TerminateExecution(); +} + 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); diff --git a/cpp/jni/com_caoccao_javet_interop_V8Native.h b/cpp/jni/com_caoccao_javet_interop_V8Native.h index 8a79de8e4..9313d542f 100644 --- a/cpp/jni/com_caoccao_javet_interop_V8Native.h +++ b/cpp/jni/com_caoccao_javet_interop_V8Native.h @@ -23,6 +23,14 @@ JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_add 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: callAsConstructor + * Signature: (JJI[Ljava/lang/Object;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_callAsConstructor + (JNIEnv *, jclass, jlong, jlong, jint, jobjectArray); + /* * Class: com_caoccao_javet_interop_V8Native * Method: clearWeak @@ -79,6 +87,14 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_createV8Value JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_delete (JNIEnv *, jclass, jlong, jlong, jint, jobject); +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: equals + * Signature: (JJJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_equals + (JNIEnv *, jclass, jlong, jlong, jlong); + /* * Class: com_caoccao_javet_interop_V8Native * Method: execute @@ -103,6 +119,14 @@ JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_get JNIEXPORT jobject JNICALL Java_com_caoccao_javet_interop_V8Native_getGlobalObject (JNIEnv *, jclass, jlong); +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: getIdentityHash + * Signature: (JJI)I + */ +JNIEXPORT jint JNICALL Java_com_caoccao_javet_interop_V8Native_getIdentityHash + (JNIEnv *, jclass, jlong, jlong, jint); + /* * Class: com_caoccao_javet_interop_V8Native * Method: getLength @@ -175,6 +199,22 @@ JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_hasOwnPropert 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: isDead + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isDead + (JNIEnv *, jclass, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: isInUse + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_isInUse + (JNIEnv *, jclass, jlong); + /* * Class: com_caoccao_javet_interop_V8Native * Method: isWeak @@ -263,6 +303,30 @@ JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_setProperty JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_setWeak (JNIEnv *, jclass, jlong, jlong, jint, jobject); +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: sameValue + * Signature: (JJJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_sameValue + (JNIEnv *, jclass, jlong, jlong, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: strictEquals + * Signature: (JJJ)Z + */ +JNIEXPORT jboolean JNICALL Java_com_caoccao_javet_interop_V8Native_strictEquals + (JNIEnv *, jclass, jlong, jlong, jlong); + +/* + * Class: com_caoccao_javet_interop_V8Native + * Method: terminateExecution + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_caoccao_javet_interop_V8Native_terminateExecution + (JNIEnv *, jclass, jlong); + /* * Class: com_caoccao_javet_interop_V8Native * Method: toProtoString diff --git a/cpp/jni/javet.rc b/cpp/jni/javet.rc index 1f70f4d61..a2e96d2e6 100644 --- a/cpp/jni/javet.rc +++ b/cpp/jni/javet.rc @@ -61,8 +61,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,7,1,0 - PRODUCTVERSION 0,7,1,0 + FILEVERSION 0,7,2,0 + PRODUCTVERSION 0,7,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "caoccao.com" VALUE "FileDescription", "caoccao.com" - VALUE "FileVersion", "0.7.1.0" - VALUE "InternalName", "javet-windows-x86_64.v.0.7.1.dll" + VALUE "FileVersion", "0.7.2.0" + VALUE "InternalName", "javet-windows-x86_64.v.0.7.2.dll" VALUE "LegalCopyright", "Copyright (C) 2021" - VALUE "OriginalFilename", "javet-windows-x86_64.v.0.7.1.dll" + VALUE "OriginalFilename", "javet-windows-x86_64.v.0.7.2.dll" VALUE "ProductName", "Javet Windows" - VALUE "ProductVersion", "0.7.1.0" + VALUE "ProductVersion", "0.7.2.0" END END BLOCK "VarFileInfo" diff --git a/cpp/jni/javet_converter.cpp b/cpp/jni/javet_converter.cpp index 0f2795551..e2b961f1f 100644 --- a/cpp/jni/javet_converter.cpp +++ b/cpp/jni/javet_converter.cpp @@ -16,6 +16,7 @@ */ #include "javet_converter.h" +#include "javet_enums.h" // Primitive #define IS_JAVA_BOOLEAN(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueBoolean) @@ -30,10 +31,13 @@ // Reference #define IS_JAVA_ARGUMENTS(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueArguments) #define IS_JAVA_ARRAY(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueArray) +#define IS_JAVA_ARRAY_BUFFER(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueArrayBuffer) +#define IS_JAVA_DATA_VIEW(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueDataView) #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_ITERATOR(jniEnv, obj) jniEnv->IsInstanceOf(obj, jclassV8ValueIterator) #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) @@ -96,6 +100,14 @@ namespace Javet { jmethodIDV8ValueArrayConstructor = jniEnv->GetMethodID(jclassV8ValueArray, "", "(J)V"); jmethodIDV8ValueArrayGetHandle = jniEnv->GetMethodID(jclassV8ValueArray, "getHandle", "()J"); + jclassV8ValueArrayBuffer = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueArrayBuffer")); + jmethodIDV8ValueArrayBufferConstructor = jniEnv->GetMethodID(jclassV8ValueArrayBuffer, "", "(JLjava/nio/ByteBuffer;)V"); + jmethodIDV8ValueArrayBufferGetHandle = jniEnv->GetMethodID(jclassV8ValueArrayBuffer, "getHandle", "()J"); + + jclassV8ValueDataView = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueDataView")); + jmethodIDV8ValueDataViewConstructor = jniEnv->GetMethodID(jclassV8ValueDataView, "", "(J)V"); + jmethodIDV8ValueDataViewGetHandle = jniEnv->GetMethodID(jclassV8ValueDataView, "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"); @@ -112,6 +124,10 @@ namespace Javet { jmethodIDV8ValueMapConstructor = jniEnv->GetMethodID(jclassV8ValueMap, "", "(J)V"); jmethodIDV8ValueMapGetHandle = jniEnv->GetMethodID(jclassV8ValueMap, "getHandle", "()J"); + jclassV8ValueIterator = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueIterator")); + jmethodIDV8ValueIteratorConstructor = jniEnv->GetMethodID(jclassV8ValueIterator, "", "(J)V"); + jmethodIDV8ValueIteratorGetHandle = jniEnv->GetMethodID(jclassV8ValueIterator, "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"); @@ -137,6 +153,10 @@ namespace Javet { jclassV8ValueSymbol = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueSymbol")); jmethodIDV8ValueSymbolConstructor = jniEnv->GetMethodID(jclassV8ValueSymbol, "", "(J)V"); jmethodIDV8ValueSymbolGetHandle = jniEnv->GetMethodID(jclassV8ValueSymbol, "getHandle", "()J"); + + jclassV8ValueTypedArray = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/values/reference/V8ValueTypedArray")); + jmethodIDV8ValueTypedArrayConstructor = jniEnv->GetMethodID(jclassV8ValueTypedArray, "", "(JI)V"); + jmethodIDV8ValueTypedArrayGetHandle = jniEnv->GetMethodID(jclassV8ValueTypedArray, "getHandle", "()J"); } jobject ToExternalV8ValueArray(JNIEnv* jniEnv, v8::Local& v8Context, const v8::FunctionCallbackInfo& args) { @@ -165,47 +185,66 @@ namespace Javet { return jniEnv->NewObject(jclassV8ValueArray, jmethodIDV8ValueArrayConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); } if (v8Value->IsTypedArray()) { - // TODO + int type = Javet::Enums::V8ValueReferenceType::Invalid; if (v8Value->IsBigInt64Array()) { + type = Javet::Enums::V8ValueReferenceType::BigInt64Array; } - if (v8Value->IsBigUint64Array()) { + else if (v8Value->IsBigUint64Array()) { + type = Javet::Enums::V8ValueReferenceType::BigUint64Array; } - if (v8Value->IsFloat32Array()) { + else if (v8Value->IsFloat32Array()) { + type = Javet::Enums::V8ValueReferenceType::Float32Array; } - if (v8Value->IsFloat64Array()) { + else if (v8Value->IsFloat64Array()) { + type = Javet::Enums::V8ValueReferenceType::Float64Array; } - if (v8Value->IsInt16Array()) { + else if (v8Value->IsInt16Array()) { + type = Javet::Enums::V8ValueReferenceType::Int16Array; } - if (v8Value->IsInt32Array()) { + else if (v8Value->IsInt32Array()) { + type = Javet::Enums::V8ValueReferenceType::Int32Array; } - if (v8Value->IsInt8Array()) { + else if (v8Value->IsInt8Array()) { + type = Javet::Enums::V8ValueReferenceType::Int8Array; } - if (v8Value->IsUint16Array()) { + else if (v8Value->IsUint16Array()) { + type = Javet::Enums::V8ValueReferenceType::Uint16Array; } - if (v8Value->IsUint32Array()) { + else if (v8Value->IsUint32Array()) { + type = Javet::Enums::V8ValueReferenceType::Uint32Array; } - if (v8Value->IsUint8Array()) { + else if (v8Value->IsUint8Array()) { + type = Javet::Enums::V8ValueReferenceType::Uint8Array; } - if (v8Value->IsUint8ClampedArray()) { + else if (v8Value->IsUint8ClampedArray()) { + type = Javet::Enums::V8ValueReferenceType::Uint8ClampedArray; } + if (type != Javet::Enums::V8ValueReferenceType::Invalid) { + return jniEnv->NewObject(jclassV8ValueTypedArray, jmethodIDV8ValueTypedArrayConstructor, ToV8PersistentObjectReference(v8Context, v8Value), type); + } + } + if (v8Value->IsDataView()) { + return jniEnv->NewObject(jclassV8ValueDataView, jmethodIDV8ValueDataViewConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); } if (v8Value->IsArrayBuffer()) { - // TODO + auto v8ArrayBuffer = v8Value.As(); + return jniEnv->NewObject(jclassV8ValueArrayBuffer, jmethodIDV8ValueArrayBufferConstructor, ToV8PersistentObjectReference(v8Context, v8Value), + jniEnv->NewDirectByteBuffer(v8ArrayBuffer->GetBackingStore()->Data(), v8ArrayBuffer->ByteLength())); } if (v8Value->IsArrayBufferView()) { - // TODO + /* + ArrayBufferView is a helper type representing any of typed array or DataView. + This block shouldn't be entered. + */ } 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->IsMapIterator() || v8Value->IsSetIterator()) { + return jniEnv->NewObject(jclassV8ValueIterator, jmethodIDV8ValueIteratorConstructor, ToV8PersistentObjectReference(v8Context, v8Value)); } if (v8Value->IsWeakMap()) { // TODO @@ -252,18 +291,8 @@ namespace Javet { 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(); @@ -424,9 +453,9 @@ namespace Javet { return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( jniEnv->CallLongMethod(obj, jmethodIDV8ValueMapGetHandle))); } - else if (IS_JAVA_OBJECT(jniEnv, obj)) { + else if (IS_JAVA_ITERATOR(jniEnv, obj)) { return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( - jniEnv->CallLongMethod(obj, jmethodIDV8ValueObjectGetHandle))); + jniEnv->CallLongMethod(obj, jmethodIDV8ValueIteratorGetHandle))); } else if (IS_JAVA_PROMISE(jniEnv, obj)) { return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( @@ -448,6 +477,10 @@ namespace Javet { return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( jniEnv->CallLongMethod(obj, jmethodIDV8ValueSymbolGetHandle))); } + else if (IS_JAVA_OBJECT(jniEnv, obj)) { + return v8::Local::New(v8Context->GetIsolate(), *reinterpret_cast*>( + jniEnv->CallLongMethod(obj, jmethodIDV8ValueObjectGetHandle))); + } } return ToV8Undefined(v8Context); } diff --git a/cpp/jni/javet_converter.h b/cpp/jni/javet_converter.h index 9bbe848b9..ad32f37e5 100644 --- a/cpp/jni/javet_converter.h +++ b/cpp/jni/javet_converter.h @@ -21,8 +21,6 @@ #include #include -#define JAVET_CONVERTER_BIGINT_STANDARD - namespace Javet { namespace Converter { // Primitive @@ -71,6 +69,14 @@ namespace Javet { static jmethodID jmethodIDV8ValueArrayConstructor; static jmethodID jmethodIDV8ValueArrayGetHandle; + static jclass jclassV8ValueArrayBuffer; + static jmethodID jmethodIDV8ValueArrayBufferConstructor; + static jmethodID jmethodIDV8ValueArrayBufferGetHandle; + + static jclass jclassV8ValueDataView; + static jmethodID jmethodIDV8ValueDataViewConstructor; + static jmethodID jmethodIDV8ValueDataViewGetHandle; + static jclass jclassV8ValueFunction; static jmethodID jmethodIDV8ValueFunctionConstructor; static jmethodID jmethodIDV8ValueFunctionGetHandle; @@ -87,6 +93,10 @@ namespace Javet { static jmethodID jmethodIDV8ValueMapConstructor; static jmethodID jmethodIDV8ValueMapGetHandle; + static jclass jclassV8ValueIterator; + static jmethodID jmethodIDV8ValueIteratorConstructor; + static jmethodID jmethodIDV8ValueIteratorGetHandle; + static jclass jclassV8ValueObject; static jmethodID jmethodIDV8ValueObjectConstructor; static jmethodID jmethodIDV8ValueObjectGetHandle; @@ -113,6 +123,10 @@ namespace Javet { static jmethodID jmethodIDV8ValueSymbolConstructor; static jmethodID jmethodIDV8ValueSymbolGetHandle; + static jclass jclassV8ValueTypedArray; + static jmethodID jmethodIDV8ValueTypedArrayConstructor; + static jmethodID jmethodIDV8ValueTypedArrayGetHandle; + void Initialize(JNIEnv* jniEnv); jobject ToExternalV8Value(JNIEnv* jniEnv, v8::Local& v8Context, v8::Local v8Value); diff --git a/cpp/jni/javet_enums.h b/cpp/jni/javet_enums.h index 001853c44..19380c1d4 100644 --- a/cpp/jni/javet_enums.h +++ b/cpp/jni/javet_enums.h @@ -2,7 +2,7 @@ * Copyright (c) 2021 caoccao.com Sam Cao * All rights reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); + * 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 @@ -18,19 +18,34 @@ #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, - }; - } + namespace Enums { + enum V8ValueReferenceType { + Invalid = 0, + Object = 1, + Error = 2, + RegExp = 3, + Promise = 4, + Proxy = 5, + Symbol = 6, + Arguments = 7, + Map = 8, + Set = 9, + Array = 10, + Function = 11, + Iterator = 12, + DataView = 30, + ArrayBuffer = 31, + Int8Array = 32, // -128 to 127 1 8-bit two's complement signed integer byte int8_t + Uint8Array = 33, // 0 to 255 1 8-bit unsigned integer octet uint8_t + Uint8ClampedArray = 34, // 0 to 255 1 8-bit unsigned integer (clamped) octet uint8_t + Int16Array = 35, // -32768 to 32767 2 16-bit two's complement signed integer short int16_t + Uint16Array = 36, // 0 to 65535 2 16-bit unsigned integer unsigned short uint16_t + Int32Array = 37, // -2147483648 to 2147483647 4 32-bit two's complement signed integer long int32_t + Uint32Array = 38, // 0 to 4294967295 4 32-bit unsigned integer unsigned long uint32_t + Float32Array = 39, // 1.2¡Á10^-38 to 3.4¡Á10^38 4 32-bit IEEE floating point number (7 significant digits e.g., 1.234567) unrestricted float float + Float64Array = 40, // 5.0¡Á10^-324 to 1.8¡Á10^308 8 64-bit IEEE floating point number (16 significant digits e.g., 1.23456789012345) unrestricted double double + BigInt64Array = 41, // -2^63 to 2^63-1 8 64-bit two's complement signed integer bigint int64_t (signed long long) + BigUint64Array = 42, // 0 to 2^64-1 8 64-bit unsigned integer bigint uint64_t (unsigned long long) + }; + } } \ No newline at end of file diff --git a/cpp/jni/javet_exceptions.cpp b/cpp/jni/javet_exceptions.cpp index 70b64947f..0e6750f5c 100644 --- a/cpp/jni/javet_exceptions.cpp +++ b/cpp/jni/javet_exceptions.cpp @@ -31,6 +31,8 @@ namespace Javet { 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"); + jclassJavetTerminatedException = (jclass)jniEnv->NewGlobalRef(jniEnv->FindClass("com/caoccao/javet/exceptions/JavetTerminatedException")); + jmethodIDJavetTerminatedExceptionConstructor = jniEnv->GetMethodID(jclassJavetTerminatedException, "", "(Z)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")); @@ -42,8 +44,8 @@ namespace Javet { 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()) { + auto v8LocalMessage = v8TryCatch.Message(); + if (v8LocalMessage.IsEmpty()) { jthrowable javetUnknownCompilationException = (jthrowable)jniEnv->NewObject( jclassJavetUnknownCompilationException, jmethodIDJavetUnknownCompilationExceptionConstructor, @@ -51,9 +53,9 @@ namespace Javet { jniEnv->Throw(javetUnknownCompilationException); } else { - v8::String::Utf8Value scriptResourceName(isolate, compileErrorMessage->GetScriptResourceName()); + v8::String::Utf8Value scriptResourceName(isolate, v8LocalMessage->GetScriptResourceName()); jstring jStringScriptResourceName = jniEnv->NewStringUTF(*scriptResourceName); - v8::String::Value sourceLine(isolate, compileErrorMessage->GetSourceLine(v8Context).ToLocalChecked()); + v8::String::Value sourceLine(isolate, v8LocalMessage->GetSourceLine(v8Context).ToLocalChecked()); jstring jStringSourceLine = jniEnv->NewString(*sourceLine, sourceLine.length()); jthrowable javetConverterException = (jthrowable)jniEnv->NewObject( jclassJavetCompilationException, @@ -61,11 +63,11 @@ namespace Javet { jStringExceptionMessage, jStringScriptResourceName, jStringSourceLine, - compileErrorMessage->GetLineNumber(v8Context).FromMaybe(0), - compileErrorMessage->GetStartColumn(), - compileErrorMessage->GetEndColumn(), - compileErrorMessage->GetStartPosition(), - compileErrorMessage->GetEndPosition() ); + v8LocalMessage->GetLineNumber(v8Context).FromMaybe(0), + v8LocalMessage->GetStartColumn(), + v8LocalMessage->GetEndColumn(), + v8LocalMessage->GetStartPosition(), + v8LocalMessage->GetEndPosition()); jniEnv->Throw(javetConverterException); jniEnv->DeleteLocalRef(jStringSourceLine); jniEnv->DeleteLocalRef(jStringScriptResourceName); @@ -79,37 +81,46 @@ namespace Javet { 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); + if (v8TryCatch.HasTerminated()) { + jthrowable javetTerminatedException = (jthrowable)jniEnv->NewObject( + jclassJavetTerminatedException, + jmethodIDJavetTerminatedExceptionConstructor, + v8TryCatch.CanContinue()); + jniEnv->Throw(javetTerminatedException); } 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); + v8::String::Value exceptionMessage(isolate, v8TryCatch.Exception()); + jstring jStringExceptionMessage = jniEnv->NewString(*exceptionMessage, exceptionMessage.length()); + auto v8LocalMessage = v8TryCatch.Message(); + if (v8LocalMessage.IsEmpty()) { + jthrowable javetUnknownExecutionException = (jthrowable)jniEnv->NewObject( + jclassJavetUnknownExecutionException, + jmethodIDJavetUnknownExecutionExceptionConstructor, + jStringExceptionMessage); + jniEnv->Throw(javetUnknownExecutionException); + } + else { + v8::String::Utf8Value scriptResourceName(isolate, v8LocalMessage->GetScriptResourceName()); + jstring jStringScriptResourceName = jniEnv->NewStringUTF(*scriptResourceName); + v8::String::Value sourceLine(isolate, v8LocalMessage->GetSourceLine(v8Context).ToLocalChecked()); + jstring jStringSourceLine = jniEnv->NewString(*sourceLine, sourceLine.length()); + jthrowable javetConverterException = (jthrowable)jniEnv->NewObject( + jclassJavetExecutionException, + jmethodIDJavetExecutionExceptionConstructor, + jStringExceptionMessage, + jStringScriptResourceName, + jStringSourceLine, + v8LocalMessage->GetLineNumber(v8Context).FromMaybe(0), + v8LocalMessage->GetStartColumn(), + v8LocalMessage->GetEndColumn(), + v8LocalMessage->GetStartPosition(), + v8LocalMessage->GetEndPosition()); + jniEnv->Throw(javetConverterException); + jniEnv->DeleteLocalRef(jStringSourceLine); + jniEnv->DeleteLocalRef(jStringScriptResourceName); + } + jniEnv->DeleteLocalRef(jStringExceptionMessage); } - jniEnv->DeleteLocalRef(jStringExceptionMessage); } void ThrowJavetV8RuntimeLockConflictException(JNIEnv* jniEnv, const char* message) { diff --git a/cpp/jni/javet_exceptions.h b/cpp/jni/javet_exceptions.h index f1cab06cd..767b8bf52 100644 --- a/cpp/jni/javet_exceptions.h +++ b/cpp/jni/javet_exceptions.h @@ -27,6 +27,8 @@ namespace Javet { static jclass jclassJavetConverterException; static jclass jclassJavetExecutionException; static jmethodID jmethodIDJavetExecutionExceptionConstructor; + static jclass jclassJavetTerminatedException; + static jmethodID jmethodIDJavetTerminatedExceptionConstructor; static jclass jclassJavetUnknownCompilationException; static jmethodID jmethodIDJavetUnknownCompilationExceptionConstructor; static jclass jclassJavetUnknownExecutionException; diff --git a/docs/development/best_practices.rst b/docs/development/best_practices.rst new file mode 100644 index 000000000..b9a57e2ca --- /dev/null +++ b/docs/development/best_practices.rst @@ -0,0 +1,28 @@ +============== +Best Practices +============== + +Thread, Engine and Pool +======================= + +* Always get 1 Javet engine from the pool in 1 thread. +* If multiple context is required in 1 thread, there are 2 options. + * Call ``resetContext()`` between context switch. + * Obtain multiple V8Runtime instances. +* Do not pass Javet engine to other threads. +* Always return Javet engine to pool in the end via try-with-resource or calling ``close()`` explicitly. +* Subclass Javet engine pool and Javet engine to complete your customization. Indeed, they are open to full customization. + +Resource Management +=================== + +* Dangling V8 objects will be forced to be recycled by Javet under the following scenarios and corresponding log will reflect that. Keeping an eye on the log helps address memory leak issues in the early stage. + * Engine is closed. + * Pool is closed. + * Context is reset. + * Isolate is reset. +* Always apply try-with-resource to Javet objects regardless of primitive or reference if they are not returned to Javet. +* Always prohibit calling ``close()`` of Javet objects if they will be returned to Javet. +* If the lifecycle of V8 objects is uncertain, calling ``setWeak()`` is the only way so that calling ``close()`` is no longer required. Be careful, calling ``close()`` after calling ``setWeak()`` may lead to V8 core dump immediately. + +[`Home <../../README.rst>`_] [`Development `_] diff --git a/docs/build.rst b/docs/development/build.rst similarity index 97% rename from docs/build.rst rename to docs/development/build.rst index 8f3d866a1..3d610bb24 100644 --- a/docs/build.rst +++ b/docs/development/build.rst @@ -89,4 +89,4 @@ Also, please make sure ``args.gn`` file looks like the following. ``v8_monolith`` is the build target. -[`Home <../README.rst>`_] +[`Home <../../README.rst>`_] [`Development `_] diff --git a/docs/development/design.rst b/docs/development/design.rst new file mode 100644 index 000000000..4620f25c8 --- /dev/null +++ b/docs/development/design.rst @@ -0,0 +1,58 @@ +============ +Javet Design +============ + +Architecture +============ + +.. image:: ../resources/images/javet_architecture.png?raw=true + :alt: Javet Architecture + +Primitive and Reference Types in Javet +-------------------------------------- + +There is a vague boundary between V8 primitive and reference types. In Javet, the definition of primitive is a mixture of both V8 and Java primitive types as a trade-off in design. + +=========================== ======================= ============================== +Feature Primitive Reference +=========================== ======================= ============================== +Interception No Yes +Memory Copy Copy by Value Copy by Reference +Resource Leak Not Possible Possible +Set to Weak No Yes +=========================== ======================= ============================== + +Reference typed objects keep memory footprint in V8 + JNI + JVM. All resource will be recycled when ``close()`` is called. That is quite an old school way of managing resource. Javet tries to hide that kind of tedious work from Java applications via try-with-resource. + +Please refer to `Best Practices `_ for detail. + +Engine Pool +=========== + +.. image:: ../resources/images/javet_engine_pool.png?raw=true + :alt: Javet Engine Pool + +V8 Isolate and Context in Javet +------------------------------- + +`Getting started with embedding V8 `_ is an excellent article that explains the concepts, design, insights of V8. In summary: + +* An isolate is a VM instance with its own heap. +* A context is an execution environment that allows separate, unrelated, JavaScript applications to run in a single instance of V8. + +In Javet, that model is simplified to 1 engine - 1 runtime - 1 isolate - 1 context. In V8Runtime, ``resetIsolate()`` and ``resetContext()`` are both exposed. It is recommended to always use ``resetContext()`` to get a brand new V8 context for the following reasons. + +* ``resetContext()`` is a much cheaper operation with much better performance. +* ``resetContext()`` is good enough in terms of getting a brand new V8 context. + +Javet Engine Pool +----------------- + +Multiple Javet engines are managed by Javet Engine Pool which works almost the same way as a typical DB connection pool. Javet Engine Pool is thread-safe. However, Javet Engine is **NOT** thread-safe because it is designed to be single-threaded and lock free for the following reasons. + +* V8 isolate and V8 context are single-threaded. Thread context violation results in V8 core dump immediately. +* Javet Engine performs better without locks. Actually, Javet engine only validates current thread ID to minimize the performance overhead. + +Please refer to `Best Practices `_ for detail. + +[`Home <../../README.rst>`_] [`Development `_] diff --git a/docs/development/index.rst b/docs/development/index.rst new file mode 100644 index 000000000..28869a82f --- /dev/null +++ b/docs/development/index.rst @@ -0,0 +1,11 @@ +=========== +Development +=========== + +* `Development Tools `_ +* `Build `_ +* `Design `_ +* `Best Practices `_ +* `Performance `_ + +[`Home <../../README.rst>`_] diff --git a/docs/performance.rst b/docs/development/performance.rst similarity index 73% rename from docs/performance.rst rename to docs/development/performance.rst index bf8624ec3..816904c8f 100644 --- a/docs/performance.rst +++ b/docs/development/performance.rst @@ -11,12 +11,12 @@ Here is the performance test result from i7 10700K + Windows 10. Test case is ju =============================== =============== Case TPS =============================== =============== -Single Context with 1 Thread 759,589 -Ad-hoc Context with 1 Thread 2,921 -Single Context with 8 Threads 4,464,285 -Ad-hoc Context with 8 Threads 15,209 +Single Context with 1 Thread 752,728 +Ad-hoc Context with 1 Thread 2,895 +Single Context with 8 Threads 4,268,943 +Ad-hoc Context with 8 Threads 15,278 =============================== =============== Reference: https://v8.dev/docs/embed#contexts -[`Home <../README.rst>`_] +[`Home <../../README.rst>`_] [`Development `_] diff --git a/docs/development.rst b/docs/development/tools.rst similarity index 65% rename from docs/development.rst rename to docs/development/tools.rst index 49776a008..58f950747 100644 --- a/docs/development.rst +++ b/docs/development/tools.rst @@ -1,38 +1,40 @@ ================= -Javet 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 ---- +IntelliJ +======== I personally recommend IntelliJ IDEA. Gradle ------- +====== For now, Gradle v6.7 + Kotlin DSL constructs the build system. -Maven ------ +Maven (Optional) +================ + +Maven v3.6.3+ is used for packaging bundle to maven central. -Maven v3.6.3+ is the one. +Visual Studio Community 2019 (Optional) +======================================= + +Visual Studio Community 2019 is used for JNI development. Python 3.8+ (Optional) ----------------------- +====================== Python 3.8+ is required if you want to upgrade the version. NodeJS (Optional) ------------------ +================= 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>`_] +[`Home <../../README.rst>`_] [`Development `_] diff --git a/docs/faq/history_with_j2v8.rst b/docs/faq/history_with_j2v8.rst index a7d0e9516..9d6161d03 100644 --- a/docs/faq/history_with_j2v8.rst +++ b/docs/faq/history_with_j2v8.rst @@ -1,3 +1,4 @@ +================= History with J2V8 ================= diff --git a/docs/faq/index.rst b/docs/faq/index.rst index d010528a6..727c78929 100644 --- a/docs/faq/index.rst +++ b/docs/faq/index.rst @@ -3,5 +3,6 @@ FAQ === * `History with J2V8 `_ +* `What is the Motivation? `_ [`Home <../../README.rst>`_] diff --git a/docs/faq/what_is_the_motivation.rst b/docs/faq/what_is_the_motivation.rst new file mode 100644 index 000000000..57490579b --- /dev/null +++ b/docs/faq/what_is_the_motivation.rst @@ -0,0 +1,13 @@ +======================= +What is the 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. + +[`Home <../../README.rst>`_] [`FAQ `_] diff --git a/docs/release_notes.rst b/docs/release_notes.rst index ccd612c25..df8a86609 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -2,6 +2,16 @@ Release Notes ============= +0.7.2 +----- + +* Added ``setFunction(String functionName, String codeString)`` to ``IV8ValueObject`` +* Added ``equals()`` and ``strictEquals()`` and ``sameValue()`` to ``IV8Value`` +* Added ``getIdentityHash()`` to ``IV8ValueReference`` +* Added ``isDead()``, ``isInUse()``, ``callAsConstructor()`` and ``terminateExecution()`` to ``V8Runtime`` +* Added V8 typed array and data view +* Added ``IJavetEngineGuard`` + 0.7.1 ----- diff --git a/docs/resources/images/javet_architecture.drawio b/docs/resources/images/javet_architecture.drawio new file mode 100644 index 000000000..128975c74 --- /dev/null +++ b/docs/resources/images/javet_architecture.drawio @@ -0,0 +1 @@ +7Z1bd5s4EIB/jR83BxBg/Jhr25ymm2160mbfZJCxshjlYDmx++tXwgIDIyfOrrl0tfFDzAAC5huNNGLmeITOF+sPGX6a37CIJCPHitYjdDFyHNtFvvgnJRslsWwliTMaKdlOcEd/kuJAJV3RiCxrB3LGEk6f6sKQpSkJeU2Gs4y91A+bsaR+1SccEyC4C3ECpd9pxOdbaeBZO/lHQuN5cWXbUnsWuDhYCZZzHLGXighdjtB5xhjfflusz0kitVfoZXve1Z695Y1lJOWHnDB5/PjlDza/9n8u3Yg+4IdH9OG3YNvKM05W6oHVzfJNoYGMrdKIyEasETp7mVNO7p5wKPe+COhCNueLRGzZ4uuMpVxBtOXh8CbVfT+TjJN1RaRu+gNhC8KzjThE7UVKfy879XuF+ucV1aOJEmKFPC6b2mlFfFGKeYeS7PcoyT5ASTRJzlnCsvxcNJsRPwyFfMkz9hep7InGk6l1JDW6Sjub+mZFq7av0aoTtKVVB2j1PgB6FQ/H68qrKyllKWloVIlwQuNUbIZCZUTIz6SqqOjYp2rHgkaRvIyWVt3oc6tWN6Xpd+9mUToJBcODMFwdi7ZQoHYNPMIkmGkN3A8DMp0dx8APsPCgUwN3gVavv3wyxMIdf1gW7rVs4R4JIldn4YEzRb5/pJHQf9PCy3lTNybuQxO/vzHExF3XG5SJTyAL/Ey4EJ1modROyFcZ+SXg1OeRR+o9jeHB1kwrUZe8iht4zSeRNDqVQcxO6RFezksnVYEm5beYCxDp1r4tVKIsAhdHo1ihz2zzQzWSbzxIJCdesXmxVoi2W5tia035j8r3yllia3eS3CjO2QtxyVZZSF7T1fY4jrOY8LfdPIlqQR00iSp0DfNClpEEc/pcDwV1hqCucMtoyncW5zUsrumGt4+tTqpGbo12mtNFGzUa2uoFNJSbZfnU/8JS4VT9NqMLmqtmmP7kCP5i0lC6Dx2GNmBqzV/AWfpXMiPiCcM2KCRkxgfHIOibAZzTf2ZCO0D/75tMNhZNQEdoLhh48qONp/I/sSfOcETF6ZV90/zTyliKNGs03XKB8/tbki3pkhPlEtuDo1E1wf40x9AEF4REv9IzDTzXa2elB/XuuGCUkK4WU6E8w3rNuB4waJxZpwGDPQZcpjSmrXeYoXOxNaFBt2DgqvyUsYTg1HQymhWPbsnAIFvohqaxaWBsZ1i+rGi4AuYCczgvNgxL766sfIQdl5P8z3gyfbuyYrWpQub36aN8hW7YdLmxSuNYfYOBcf5pluGN6Vw0MWa3XGDsfyO0bDgVTXDZLRUY+d8R433YpG8qMOS/WqUhp6ztEGZwaJqL9/2PLzDsv9sspqztRczhk+l9hIFx/1cSX66NG2QAmd5HGRj332ZsQZdtR5jDR9P3UFN02jqatXHTZXtocQyCof8noTvMxdObzqbvkQbBGPMyy/4H0/tAgzQvmOmMhJtQEBHnLuVFZWpInkpqzcpJtZWQ57z2AqfRSFYfpDgm8ttUPu59ABfchpAi0EK5QLnk+cq7aW3ST3tMYYgKmDZ4FZl7VJK9vr8xl56mQ3ZMD4ZLAEaVQlHlIxVSJutpveFezYs2nmTLi3Usq6pOtnVMzgl/Yd/xRiUEnuUlVdaJ9J15vRXyNEheW9V+/xr5EQA7DcC2Jm3W7tblwqjrnD1tSteZi4/f/Y6WNVuhLbcr3NwL+WmJm8atdswNxmS/dsfsdyoEAGsCu24BFxfb0zHbTH7soHNeXVnWsWr7mux0ye0ds4PB332gd6WtvvglduSRsa7vTPwxwscqLWi+3B1D/Wtrz1pTP4zv9mds/8ffvAM4hxYGtgYHxnj7fVm7cN4eWV4bp1qAo1u16hbOAZWCsCrnFQbvnpO9WQxT+NaBVLm4dgOh3WBzaJmL6zc6qt9oqOUyF/eASO8o6Muaq0qZ1cOoWmX1Rs1VWWf1oK7cRs1VYWZvFl0V/mwg9uh4DXv0/qE9ImuPb+rKHmFgemZmGjAaWoa2C2PPC7aaJsZNrtBkWHnAHowZPwndxeYVmzTL03snAyPCz8y8xHl3aB3GAVjujKxo8OyBjTEejOH/FFPOSNY1fKML44YaEMdrAHUaKvpwrDEzgx4Vy11DSW/w4VhjZAo9ANN3eoMPRxsDc+gBlr5z6Hw41BiYRO86Q3NjcKG4k+K5wZEBaVq9dxi4SmxqfQPoNb2PMbD2xMz6BkCm914DF9gNzaL3Btdp4FqzkVn07uBCGbjSbGSmNgDTd48p7sf4Yi3gy/ruMmNd9B+vFuIRl6bBAd1GMwcYHweO2Nz9mMH2HejuNyHQ5d8= \ No newline at end of file diff --git a/docs/resources/images/javet_architecture.png b/docs/resources/images/javet_architecture.png new file mode 100644 index 000000000..295eeac6c Binary files /dev/null and b/docs/resources/images/javet_architecture.png differ diff --git a/docs/resources/images/javet_engine_pool.drawio b/docs/resources/images/javet_engine_pool.drawio new file mode 100644 index 000000000..c9e98852e --- /dev/null +++ b/docs/resources/images/javet_engine_pool.drawio @@ -0,0 +1 @@ +7VpZc5s6FP41fkyH3fCYOGm63KW9mdvbPCpIBqUy8sjy1l9/JSPMImHHtcHNOMlMgo4WxPed83E4MHBHk9U9A9P0TwoRGTgWXA3c24Hj2J4biH/SslYWy1aWhGGobKXhAf9ExUBlnWOIZrWBnFLC8bRujGmWoZjXbIAxuqwPG1NSP+sUJEgzPMSA6Nb/MORpbg19q7R/QDhJizPbluqZgGKwMsxSAOmyYnLvBu6IUcrzo8lqhIhEr8Aln/e+pXe7MYYy/pIJ0fOHv77S9FPwc+ZB/Agen937qzBfZQHIXF2w2ixfFwgwOs8gkotYA/dmmWKOHqYglr1LQbqwpXxCRMsWh2OacUWiLYfrm1T7XiDG0apiUpu+R3SCOFuLIarXVfgtS/jdobKlFeidwggU5cl2qRIVcaCAOQCkSAPpE1ggLkx3WYIzJA6+CJ/UkBMXyOvwzDijP9CIEsqEJaNirsAME9IwAYKTTDRjARsS9hsJFxZeea06JhhCeRojH3XGNpSoTTUpCk5DkaeQ3wa5zphvYqwrwobOIW5tv8CtKxQNHHc8RkEca3yKHjiMnqwTOX4DVU9HdSs2NVS7g9XVYP0WvgqvP56MYZ0MXyfD69XDAw13BMWNSzUp4ylNaAbIXWltIFSO+YPSqaLqGXG+VuoA5pzWiUQrzL/L6e981Xqs9Nyu1MqbxrpoZOJ6v1cbj+UKsllO27TKefBa3r9L9xCW91jCVBJcudPIdiUQvVv5uysQZ3TOYrQLY0/lG4AliO8a6OcDJQM7fYkhAjhe1FMLk29sporLB+vKgCnFGZ9VVv4iDa0uatfzAXGQL1g63XZnR/ihp0nCxxkVV4mOFNwGu5o0aIocQyfcqcgJAxCjmpOMx8A/lVr7dfTDcwuErxEzEphKbe6XGAAj6JuIAcizXec04EcN1zdkIP2ir98W+5TnijiXUt0mz2eX2WEn6rlXHq0G+bnMq0kl/8eqsN+HCg8vOdgNiXG/wR695WJdi0RxO92bi4X275iLFY/EnapAgdFbLtbIxezhmRWicMqL1GfHOjf6evHnLRlrw8o9TzIW9ZKMFX7XrQzrVbILCnZDtb6rYL8Zsmj1+fPXh8Vy9O9HmPwNn+MrXWmPRL0OKvRRCD0TqKHz5AZlPV1D0IBzK6h24wZmKv3aJlQ7g1UvqL++Vx8n4CXYWwWO+nR2vcbzCjO9kzJiKLuZGPG7YiTQGPkW/jPPOJ70nYS/VP2Pg7+tqFLB3/iutiv89UJIoVO/r0Bpr8uPoyT090VErzfkg74wuAiNMj2O9ipS+vcMlyVS0ZlFqkjd3lTqLCUaMyd6hlt85IOKTDcVZ3UsmhGx6euBExDJzRMTRwnfwJJb5qRpIbiwCFSsSrDl3WLD5Yi2OdtS3gFzts+dO+YIo75hiBeFSUMBSxBmOEsIuuIpQ0A4yrvKapW5huXaYds7tYKBBTJYv0IrBpn4+4Q2tYkZ4q1bOnmIETTmhgDjslD0C9HVKOpYm5/TRF2zHhIYnisdQ9RtX7EcEHaiWX7bmNdWyk9E3bv/AQ== \ No newline at end of file diff --git a/docs/resources/images/javet_engine_pool.png b/docs/resources/images/javet_engine_pool.png new file mode 100644 index 000000000..51a51630d Binary files /dev/null and b/docs/resources/images/javet_engine_pool.png differ diff --git a/docs/todo_list.rst b/docs/todo_list.rst index 383cbed75..5a57f3824 100644 --- a/docs/todo_list.rst +++ b/docs/todo_list.rst @@ -2,8 +2,8 @@ TODO List ========= -* To support more types that are not supported by ECMAScript, e.g. BigDecimal, etc. -* To revive NodeJS. +* To support more types that are not supported by ECMAScript, e.g. BigDecimal, byte[], etc. +* To build NodeJS in. * To implement runtime debugging capability. [`Home <../README.rst>`_] diff --git a/docs/tutorial/hello_javet.rst b/docs/tutorial/hello_javet.rst index 2141b833f..516700a38 100644 --- a/docs/tutorial/hello_javet.rst +++ b/docs/tutorial/hello_javet.rst @@ -13,24 +13,22 @@ Maven com.caoccao.javet javet - 0.7.1 + 0.7.2 -Gradle Kotlin -------------- +Gradle Kotlin DSL +----------------- .. code-block:: kotlin - dependencies { - implementation("com.caoccao.javet:javet:0.7.1") - } + implementation("com.caoccao.javet:javet:0.7.2") -Gradle Groovy -------------- +Gradle Groovy DSL +----------------- .. code-block:: groovy - compile group: 'com.caoccao.javet', name: 'javet', version: '0.7.1' + implementation 'com.caoccao.javet:javet:0.7.2' Print **Hello Javet** ===================== @@ -51,9 +49,8 @@ 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 2: Request a lock. + try (V8Runtime v8Runtime = V8Host.getInstance().createV8Runtime().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. diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index eb8575b86..39de783c0 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -6,6 +6,9 @@ Tutorial * `Know the Lock `_ * `Memory Management `_ * `Manipulate V8 Function `_ +* `Spring Integration `_ +* `Logging `_ +* `Termination `_ * `Polyfill `_ Complete tutorial is available at `here <../../src/test/java/com/caoccao/javet/tutorial>`_. diff --git a/docs/tutorial/logging.rst b/docs/tutorial/logging.rst new file mode 100644 index 000000000..a4d77f373 --- /dev/null +++ b/docs/tutorial/logging.rst @@ -0,0 +1,79 @@ +======= +Logging +======= + +As Javet is a fundamental SDK, it doesn't rely on any other libraries except JDK so that Javet users don't get dependency hell. That also means Javet has to use the JDK logging API, but Javet allows injecting 3rd party logging API. + +Step 1: Implement IJavetLogger +============================== + +``IJavetLogger`` is the the only logging interface accepted by Javet. You may implement ``IJavetLogger`` with ``slf4j`` as following. + +.. code-block:: java + + import com.caoccao.javet.interfaces.IJavetLogger; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + + public class MyJavetLogger implements IJavetLogger { + protected Logger logger; + + public MyJavetLogger(String name) { + logger = LoggerFactory.getLogger(name); + } + + @Override + public void debug(String message) { + if (logger.isDebugEnabled()) { + logger.debug(message); + } + } + + @Override + public void error(String message) { + if (logger.isDebugEnabled()) { + logger.error(message); + } + } + + @Override + public void error(String message, Throwable throwable) { + if (logger.isDebugEnabled()) { + logger.error(message, throwable); + } + } + + @Override + public void info(String message) { + if (logger.isInfoEnabled()) { + logger.info(message); + } + } + + @Override + public void warn(String message) { + if (logger.isWarnEnabled()) { + logger.warn(message); + } + } + } + +Step 2: Inject the Logger +========================= + +Injecting the logger is quite simple. + +* Create an instance of the logger. +* Set the logger to a config. +* Set the config to a pool. + +.. code-block:: java + + MyJavetLogger javetLogger = new MyJavetLogger("TestLogger"); + JavetEngineConfig javetEngineConfig = new JavetEngineConfig(); + javetEngineConfig.setJavetLogger(javetLogger); + JavetEnginePool javetEnginePool = new JavetEnginePool(javetEngineConfig); + +Now, Javet is integrated into your logging system. + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/manipulate_v8_function.rst b/docs/tutorial/manipulate_v8_function.rst index 9f9f48e6c..7defa8416 100644 --- a/docs/tutorial/manipulate_v8_function.rst +++ b/docs/tutorial/manipulate_v8_function.rst @@ -36,10 +36,14 @@ Option 2: The Recommended Way // Once this function is set to weak, its lifecycle is automatically managed by Javet + V8. // There is no need to call close() any more. + // Alternatively, setFunction() makes that easy with only one line. + v8ValueObject.setFunction("test", javetCallbackContext); + // An instance of V8ValueFunction is created and set to weak internally. + 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. +Javet is capable of automatically converting its internal ``V8Value`` to primitive types by inspecting the function signature. So, the following 4 functions are all the same and valid. .. code-block:: java @@ -68,4 +72,6 @@ Javet is capable of automatically converting its internal ``V8Value`` to other t 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. +Please review `test cases <../../src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java>`_ for more detail. + [`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/memory_management.rst b/docs/tutorial/memory_management.rst index 554b79a93..725474a79 100644 --- a/docs/tutorial/memory_management.rst +++ b/docs/tutorial/memory_management.rst @@ -24,8 +24,8 @@ V8 generally categorizes objects in memory to 3 types. 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 -======================== +Solution 1: Weak Reference +========================== Javet directly borrows the way V8 manages objects in JVM. The rule is simple in the following 2 patterns. @@ -59,4 +59,41 @@ Automatically Manage V8 Objects 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. +Solution 2: ArrayBuffer +======================= + +The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer. + +It is an array of bytes, often referred to in other languages as a "byte array".You cannot directly manipulate the contents of an ArrayBuffer; instead, you create one of the typed array objects or a DataView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer. + +Javet offers complete support to all the typed arrays as well as ``DataView`` as following. There is a ``java.nio.ByteBuffer`` inside every typed array and ``DataView``. That ``ByteBuffer`` directly links to the corresponding backing store of V8 typed array. In other words, Javet and V8 can both access the same address to achieve zero memory copy. Please consider using typed array in performance sensitive scenarios. + ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Type | Value Range | Size in bytes | Description | Web IDL type | Equivalent C type | ++===================+=============================+===============+====================================================================================+=====================+===============================+ +| Int8Array | -128 to 127 | 1 | 8-bit two's complement signed integer | byte | int8_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Uint8Array | 0 to 255 | 1 | 8-bit unsigned integer | octet | uint8_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Uint8ClampedArray | 0 to 255 | 1 | 8-bit unsigned integer (clamped) | octet | uint8_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Int16Array | -32768 to 32767 | 2 | 16-bit two's complement signed integer | short | int16_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Uint16Array | 0 to 65535 | 2 | 16-bit unsigned integer | unsigned short | uint16_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Int32Array | -2147483648 to 2147483647 | 4 | 32-bit two's complement signed integer | long | int32_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Uint32Array | 0 to 4294967295 | 4 | 32-bit unsigned integer | unsigned long | uint32_t | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Float32Array | 1.2×10-38 to 3.4×1038 | 4 | 32-bit IEEE floating point number (7 significant digits e.g., 1.234567) | unrestricted float | float | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| Float64Array | 5.0×10-324 to 1.8×10308 | 8 | 64-bit IEEE floating point number (16 significant digits e.g., 1.23456789012345) | unrestricted double | double | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| BigInt64Array | -263 to 263-1 | 8 | 64-bit two's complement signed integer | bigint | int64_t (signed long long) | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ +| BigUint64Array | 0 to 264-1 | 8 | 64-bit unsigned integer | bigint | uint64_t (unsigned long long) | ++-------------------+-----------------------------+---------------+------------------------------------------------------------------------------------+---------------------+-------------------------------+ + +Please refer to `TestV8ValueTypedArray <../../src/test/java/com/caoccao/javet/values/reference/TestV8ValueTypedArray.java>`_ and `TestV8ValueDataView <../../src/test/java/com/caoccao/javet/values/reference/TestV8ValueDataView.java>`_ for sample code snippets. + [`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/polyfill.rst b/docs/tutorial/polyfill.rst index 8ee325473..a5fb807f2 100644 --- a/docs/tutorial/polyfill.rst +++ b/docs/tutorial/polyfill.rst @@ -14,21 +14,36 @@ decimal.js 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(); + getLogger().logInfo("Loading {0}.", decimalJSFile.getAbsolutePath()); + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); 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."); + getLogger().logError("{0} is not found.", decimalJSFile.getAbsolutePath()); + getLogger().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( + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + getLogger().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()); + try (V8ValueFunction v8ValueFunctionDecimal = v8Runtime.getGlobalObject().get("Decimal")) { + try (V8ValueObject v8ValueObjectDecimal = v8ValueFunctionDecimal.call( + null, new V8ValueString("123.45"))) { + getLogger().logInfo(v8ValueObjectDecimal.toString()); + if (v8ValueObjectDecimal.hasOwnProperty("constructor")) { + try (V8ValueFunction v8ValueFunction = v8ValueObjectDecimal.get("constructor")) { + String name = v8ValueFunction.getString("name"); + if ("Decimal".equals(name)) { + BigDecimal bigDecimal = new BigDecimal(v8ValueObjectDecimal.toString()); + getLogger().logInfo("BigDecimal: {0}", bigDecimal.toString()); + } + } + } + } + } } Please refer to `source code <../../src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java>`_ for more detail. diff --git a/docs/tutorial/spring_integration.rst b/docs/tutorial/spring_integration.rst new file mode 100644 index 000000000..13ccd3ae5 --- /dev/null +++ b/docs/tutorial/spring_integration.rst @@ -0,0 +1,70 @@ +================== +Spring Integration +================== + +As Javet is a fundamental SDK, it doesn't rely on Spring Framework so that Javet users don't get dependency hell. But, Javet can be integrated with Spring easily. + +Configuration +============= + +* Create a Spring configuration. +* Declare ``IJavetEnginePool`` as ``@Bean``. +* Set the pool implement in ``@PostConstruct``. + +.. code-block:: java + + @Configuration + @PropertySource("classpath:javet-engine.properties") + @ConfigurationProperties(prefix = "javet.engine") + public class MyJavetEngineConfig { + @Value("32") + private int poolMaxSize; + @Value("8") + private int poolMinSize; + @Value("60") + private int poolIdleTimeoutSeconds; + @Value("1000") + private int poolDaemonCheckIntervalMillis; + @Value("3600") + private int resetEngineTimeoutSeconds; + private IJavetLogger javetLogger; + private IJavetEnginePool javetEnginePool; + + @PostConstruct + public void postConstruct() { + initializeJavet(); + } + + @PreDestroy + public void preDestroy() throws JavetException { + // There is no need to close Javet engine pool explicitly because spring does so. + } + + @Bean + public IJavetEnginePool getJavetEnginePool() { + return javetEnginePool; + } + + private void initializeJavet() { + javetLogger = new MyJavetLogger("SampleLogger"); + JavetEngineConfig javetEngineConfig = new JavetEngineConfig(); + javetEngineConfig.setPoolDaemonCheckIntervalMillis(getPoolDaemonCheckIntervalMillis()); + javetEngineConfig.setPoolIdleTimeoutSeconds(getPoolIdleTimeoutSeconds()); + javetEngineConfig.setPoolMinSize(getPoolMinSize()); + javetEngineConfig.setPoolMaxSize(getPoolMaxSize()); + javetEngineConfig.setResetEngineTimeoutSeconds(getResetEngineTimeoutSeconds()); + javetEngineConfig.setJavetLogger(javetLogger); + javetEnginePool = new MyJavetEnginePool(javetEngineConfig); + } + +Injection +========= + +You may easily inject your engine pool in the Spring way. + +.. code-block:: java + + @Resource + protected IJavetEnginePool javetEnginePool; + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/docs/tutorial/termination.rst b/docs/tutorial/termination.rst new file mode 100644 index 000000000..13281cffd --- /dev/null +++ b/docs/tutorial/termination.rst @@ -0,0 +1,71 @@ +=========== +Termination +=========== + +Terminating scripts that run out of control is quite important in terms of protecting the applications from being attacked by malicious scripts. In Javet, there are 2 typical ways of terminating scripts. + +Automatic Termination with Pool and Engine +========================================== + +``IJavetEngineGuard`` is the built-in support for terminating a script which runs out of control. + +.. code-block:: java + + // Get an engine from the pool as usual. + try (IJavetEngine iJavetEngine = iJavetEnginePool.getEngine()) { + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + // Get a guard from the engine and apply try-with-resource pattern. + try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(10000)) { + v8Runtime.getExecutor("while (true) {}").executeVoid(); + // That infinite loop will be terminated in 10 seconds by the guard. + } catch (JavetTerminatedException e) { + // JavetTerminatedException will be thrown to mark that. + assertFalse(e.isContinuable()); + } + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger(), + "The V8 runtime is not dead and still be able to execute code afterwards."); + } + +Does ``IJavetEngineGuard`` hang normal scripts till timeout is hit? No, it doesn't cause any overhead. If the script completes, ``IJavetEngineGuard.close()`` will be called via try-with-resource pattern and cancel the daemon thread immediately. + +Please refer to `source code <../../src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java>`_ for more detail. + +Manual Termination +================== + +Manual termination gives applications complete control. In return, the coding effort is considerable. + +.. code-block:: java + + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + // Create a daemon thread monitoring the V8 runtime status. + Thread daemonThread = new Thread(() -> { + // V8 runtime isInUse() does not require lock. + while (!v8Runtime.isInUse()) { + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // V8 runtime terminateExecution() does not require lock. + v8Runtime.terminateExecution(); + }); + daemonThread.start(); + v8Runtime.lock(); + try { + v8Runtime.getExecutor( + "var count = 0; while (true) { ++count; }") + .executeVoid(); + fail("Failed to throw exception when execution is terminated."); + } catch (JavetTerminatedException e) { + assertFalse(e.isContinuable()); + } + final int count = v8Runtime.getGlobalObject().getInteger("count"); + assertTrue(count > 0, "Count should be greater than 0."); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger(), + "V8 runtime should still be able to execute script after being terminated."); + } + +[`Home <../../README.rst>`_] [`Tutorial `_] diff --git a/pom.xml b/pom.xml index 028e5fa7d..ad8da3b90 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.caoccao.javet javet - 0.7.1 + 0.7.2 javet Javet is Java + V8 (JAVa + V + EighT). It is a way of embedding V8 in Java. https://github.com/caoccao/Javet @@ -28,7 +28,7 @@ scm:git:git://github.com/caoccao/Javet.git scm:git:git@github.com:caoccao/caoccao.git https://github.com/caoccao/Javet - javet-0.7.1 + javet-0.7.2 diff --git a/scripts/python/change_javet_version.py b/scripts/python/change_javet_version.py index 5eda91f5c..f82d40ac7 100644 --- a/scripts/python/change_javet_version.py +++ b/scripts/python/change_javet_version.py @@ -32,9 +32,9 @@ def __init__(self, version) -> None: 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'javet:(?P\d+\.\d+\.\d+)\''), re.compile(r'version: \'(?P\d+\.\d+\.\d+)\'')) self._update( 'build.gradle.kts', '\n', @@ -43,13 +43,14 @@ def update(self): 'docs/tutorial/hello_javet.rst', '\n', re.compile(r'^ (?P\d+\.\d+\.\d+)$'), re.compile(r'javet:(?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+)$'), re.compile(r'^ javet-(?P\d+\.\d+\.\d+)$')) self._update( - 'cpp/build.cmd', '\n', + 'cpp/build.cmd', '\r\n', re.compile(r'JAVET_VERSION=(?P\d+\.\d+\.\d+)$')) self._update( 'cpp/build.sh', '\n', @@ -58,7 +59,7 @@ def update(self): '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', + 'cpp/jni/javet.rc', '\r\n', re.compile(r'"(?P\d+\.\d+\.\d+)'), re.compile(r'v\.(?P\d+\.\d+\.\d+)'), re.compile(r'(?P\d+,\d+,\d+)')) @@ -67,7 +68,8 @@ 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): + original_buffer = file_path.read_bytes() + for line in original_buffer.decode('utf-8').split(line_separator): for pattern in patterns: match_object = pattern.search(line) if match_object is not None: @@ -86,10 +88,15 @@ def _update(self, relative_file_path: str, line_separator: str, *patterns: list) break lines.append(line) line_number += 1 - file_path.write_text(line_separator.join(lines), 'utf-8') + new_buffer = line_separator.join(lines).encode('utf-8') + if original_buffer == new_buffer: + logging.warn(' Skipped.') + else: + file_path.write_bytes(new_buffer) + logging.info(' Updated.') def main(): - change_javet_version = ChangeJavetVersion('0.7.1') + change_javet_version = ChangeJavetVersion('0.7.2') change_javet_version.update() return 0 diff --git a/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java b/src/main/java/com/caoccao/javet/entities/JavetEntityMap.java new file mode 100644 index 000000000..30c6c47a6 --- /dev/null +++ b/src/main/java/com/caoccao/javet/entities/JavetEntityMap.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.entities; + +import java.util.HashMap; +import java.util.Map; + +public class JavetEntityMap extends HashMap { + public JavetEntityMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public JavetEntityMap(int initialCapacity) { + super(initialCapacity); + } + + public JavetEntityMap() { + } + + public JavetEntityMap(Map m) { + super(m); + } + + @Override + public Object clone() { + return new JavetEntityMap(this); + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetTerminatedException.java b/src/main/java/com/caoccao/javet/exceptions/JavetTerminatedException.java new file mode 100644 index 000000000..d42fe569a --- /dev/null +++ b/src/main/java/com/caoccao/javet/exceptions/JavetTerminatedException.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.exceptions; + +import java.text.MessageFormat; + +public class JavetTerminatedException extends JavetException { + protected boolean continuable; + + public JavetTerminatedException(boolean continuable) { + super(MessageFormat.format("V8 execution is terminated, continuable: {0}", continuable)); + this.continuable = continuable; + } + + public boolean isContinuable() { + return continuable; + } +} diff --git a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java index ac5ba768b..0ea288f2c 100644 --- a/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java +++ b/src/main/java/com/caoccao/javet/exceptions/JavetV8RuntimeLockConflictException.java @@ -20,16 +20,16 @@ import java.text.MessageFormat; public class JavetV8RuntimeLockConflictException extends JavetException { - public JavetV8RuntimeLockConflictException(String message) { + protected JavetV8RuntimeLockConflictException(String message) { super(message); } - public JavetV8RuntimeLockConflictException() { - this("V8 runtime lock conflict is detected"); + public static JavetV8RuntimeLockConflictException lockNotRequired() { + return new JavetV8RuntimeLockConflictException("V8 runtime lock is not required"); } - public JavetV8RuntimeLockConflictException(long lockedThreadId, long currentThreadId) { - this(MessageFormat.format( + public static JavetV8RuntimeLockConflictException threadIdMismatch(long lockedThreadId, long currentThreadId) { + return new JavetV8RuntimeLockConflictException(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/interop/IV8Creatable.java b/src/main/java/com/caoccao/javet/interop/IV8Creatable.java index 1d99918e3..cab60d21c 100644 --- a/src/main/java/com/caoccao/javet/interop/IV8Creatable.java +++ b/src/main/java/com/caoccao/javet/interop/IV8Creatable.java @@ -19,16 +19,28 @@ import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.utils.JavetCallbackContext; +import com.caoccao.javet.values.primitive.V8ValueNull; +import com.caoccao.javet.values.primitive.V8ValueUndefined; import com.caoccao.javet.values.reference.*; public interface IV8Creatable { V8ValueArray createV8ValueArray() throws JavetException; + V8ValueArrayBuffer createV8ValueArrayBuffer(int length) throws JavetException; + + V8ValueDataView createV8ValueDataView(V8ValueArrayBuffer v8ValueArrayBuffer) throws JavetException; + V8ValueFunction createV8ValueFunction(JavetCallbackContext javetCallbackContext) throws JavetException; V8ValueMap createV8ValueMap() throws JavetException; + V8ValueNull createV8ValueNull(); + V8ValueObject createV8ValueObject() throws JavetException; V8ValueSet createV8ValueSet() throws JavetException; + + V8ValueTypedArray createV8ValueTypedArray(int type, int length) throws JavetException; + + V8ValueUndefined createV8ValueUndefined(); } diff --git a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java index 77d810009..e892b17d7 100644 --- a/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java +++ b/src/main/java/com/caoccao/javet/interop/JavetLibLoader.java @@ -1,122 +1,123 @@ -/* - * 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.JavetOSUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.text.MessageFormat; - -final class JavetLibLoader { - private static final String CHMOD = "chmod"; - private static final String XRR = "755"; - static final String LIB_VERSION = "0.7.1"; - 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); - } - } -} +/* + * 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.JavetOSUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.text.MessageFormat; + +final class JavetLibLoader { + private static final String CHMOD = "chmod"; + private static final String XRR = "755"; + static final String LIB_VERSION = "0.7.2"; + static final String V8_VERSION = "8.9.255"; + 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/V8Host.java b/src/main/java/com/caoccao/javet/interop/V8Host.java index 6ea72f257..6a8ad7b93 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Host.java +++ b/src/main/java/com/caoccao/javet/interop/V8Host.java @@ -29,6 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; public final class V8Host implements AutoCloseable { + public static final String GLOBAL_THIS = "globalThis"; 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"; @@ -66,10 +67,6 @@ public static V8Host getInstance() { return instance; } - public V8Runtime createV8Runtime() { - return createV8Runtime(null); - } - public V8Flags getFlags() { return flags; } @@ -78,6 +75,10 @@ public String getJavetVersion() { return JavetLibLoader.LIB_VERSION; } + public V8Runtime createV8Runtime() { + return createV8Runtime(GLOBAL_THIS); + } + public V8Runtime createV8Runtime(String globalName) { return createV8Runtime(false, globalName); } diff --git a/src/main/java/com/caoccao/javet/interop/V8Native.java b/src/main/java/com/caoccao/javet/interop/V8Native.java index ccc179f19..fb55a14d2 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Native.java +++ b/src/main/java/com/caoccao/javet/interop/V8Native.java @@ -37,6 +37,9 @@ native static Object call( long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object receiver, boolean returnResult, Object[] values); + native static Object callAsConstructor( + long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object[] values); + native static void clearWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); native static Object cloneV8Value( @@ -55,6 +58,8 @@ native static void compileOnly( native static boolean delete(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object key); + native static boolean equals(long v8RuntimeHandle, long v8ValueHandle1, long v8ValueHandle2); + native static Object execute( long v8RuntimeHandle, String script, boolean returnResult, String resourceName, int resourceLineOffset, int resourceColumnOffset, @@ -64,6 +69,8 @@ native static Object execute( native static Object getGlobalObject(long v8RuntimeHandle); + native static int getIdentityHash(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); + native static int getLength(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); native static int getSize(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); @@ -84,6 +91,10 @@ native static Object invoke( long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, String functionName, boolean returnResult, Object[] values); + native static boolean isDead(long v8RuntimeHandle); + + native static boolean isInUse(long v8RuntimeHandle); + native static boolean isWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); native static void lockV8Runtime(long v8RuntimeHandle); @@ -114,6 +125,12 @@ native static Object invoke( native static void setWeak(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType, Object objectReference); + native static boolean sameValue(long v8RuntimeHandle, long v8ValueHandle1, long v8ValueHandle2); + + native static boolean strictEquals(long v8RuntimeHandle, long v8ValueHandle1, long v8ValueHandle2); + + native static void terminateExecution(long v8RuntimeHandle); + native static String toProtoString(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); native static String toString(long v8RuntimeHandle, long v8ValueHandle, int v8ValueType); diff --git a/src/main/java/com/caoccao/javet/interop/V8Runtime.java b/src/main/java/com/caoccao/javet/interop/V8Runtime.java index fce3ad934..72427ff46 100644 --- a/src/main/java/com/caoccao/javet/interop/V8Runtime.java +++ b/src/main/java/com/caoccao/javet/interop/V8Runtime.java @@ -30,6 +30,9 @@ import com.caoccao.javet.utils.JavetDefaultLogger; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueNull; +import com.caoccao.javet.values.primitive.V8ValueUndefined; import com.caoccao.javet.values.reference.*; import java.nio.file.Path; @@ -42,6 +45,7 @@ public final class V8Runtime implements IJavetClosable, IV8Executable, IV8Creatable { private static final long INVALID_THREAD_ID = -1L; private static final long INVALID_HANDLE = 0L; + private static final String PROPERTY_DATA_VIEW = "DataView"; private String globalName; private long handle; @@ -83,13 +87,21 @@ public T call( handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), receiver, returnResult, v8Values)); } + public T callAsConstructor( + IV8ValueObject iV8ValueObject, V8Value... v8Values) throws JavetException { + checkLock(); + decorateV8Values(v8Values); + return decorateV8Value((T) V8Native.callAsConstructor( + handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), v8Values)); + } + public V8Runtime 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); + throw JavetV8RuntimeLockConflictException.threadIdMismatch(threadId, currentThreadId); } return this; } @@ -140,14 +152,30 @@ public void compileOnly(String scriptString, V8ScriptOrigin v8ScriptOrigin) thro @Override public V8ValueArray createV8ValueArray() throws JavetException { checkLock(); - return decorateV8Value((V8ValueArray) V8Native.createV8Value(handle, V8ValueReferenceType.Array, null)); + return decorateV8Value((V8ValueArray) V8Native.createV8Value( + handle, V8ValueReferenceType.Array, null)); + } + + @Override + public V8ValueArrayBuffer createV8ValueArrayBuffer(int length) throws JavetException { + checkLock(); + return decorateV8Value((V8ValueArrayBuffer) V8Native.createV8Value( + handle, V8ValueReferenceType.ArrayBuffer, new V8ValueInteger(length))); + } + + @Override + public V8ValueDataView createV8ValueDataView(V8ValueArrayBuffer v8ValueArrayBuffer) throws JavetException { + checkLock(); + try (V8ValueFunction v8ValueFunction = getGlobalObject().get(PROPERTY_DATA_VIEW)) { + return v8ValueFunction.callAsConstructor(v8ValueArrayBuffer); + } } @Override public V8ValueFunction createV8ValueFunction(JavetCallbackContext javetCallbackContext) throws JavetException { checkLock(); - V8ValueFunction v8ValueFunction = decorateV8Value( - (V8ValueFunction) V8Native.createV8Value(handle, V8ValueReferenceType.Function, javetCallbackContext)); + V8ValueFunction v8ValueFunction = decorateV8Value((V8ValueFunction) V8Native.createV8Value( + handle, V8ValueReferenceType.Function, javetCallbackContext)); v8ValueFunction.setV8CallbackContext(javetCallbackContext); return v8ValueFunction; } @@ -158,6 +186,16 @@ public V8ValueMap createV8ValueMap() throws JavetException { return decorateV8Value((V8ValueMap) V8Native.createV8Value(handle, V8ValueReferenceType.Map, null)); } + @Override + public V8ValueNull createV8ValueNull() { + V8ValueNull v8ValueNull = new V8ValueNull(); + try { + v8ValueNull.setV8Runtime(this); + } catch (JavetException javetException) { + } + return v8ValueNull; + } + @Override public V8ValueObject createV8ValueObject() throws JavetException { checkLock(); @@ -170,6 +208,23 @@ public V8ValueSet createV8ValueSet() throws JavetException { return decorateV8Value((V8ValueSet) V8Native.createV8Value(handle, V8ValueReferenceType.Set, null)); } + @Override + public V8ValueTypedArray createV8ValueTypedArray(int type, int length) throws JavetException { + try (V8ValueFunction v8ValueFunction = getGlobalObject().get(V8ValueTypedArray.getName(type))) { + return v8ValueFunction.callAsConstructor(new V8ValueInteger(length)); + } + } + + @Override + public V8ValueUndefined createV8ValueUndefined() { + V8ValueUndefined v8ValueUndefined = new V8ValueUndefined(); + try { + v8ValueUndefined.setV8Runtime(this); + } catch (JavetException javetException) { + } + return v8ValueUndefined; + } + public T decorateV8Value(T v8Value) throws JavetException { if (v8Value != null) { if (v8Value.getV8Runtime() == null) { @@ -197,6 +252,13 @@ public boolean delete(IV8ValueObject iV8ValueObject, V8Value key) throws JavetEx return V8Native.delete(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key); } + public boolean equals(V8ValueReference v8ValueReference1, V8ValueReference v8ValueReference2) + throws JavetException { + checkLock(); + decorateV8Values(v8ValueReference1, v8ValueReference2); + return V8Native.equals(handle, v8ValueReference1.getHandle(), v8ValueReference2.getHandle()); + } + @Override public T execute( String scriptString, V8ScriptOrigin v8ScriptOrigin, boolean resultRequired) throws JavetException { @@ -232,6 +294,11 @@ public void setGlobalName(String globalName) { this.globalName = globalName; } + public int getIdentityHash(IV8ValueObject iV8ValueObject) throws JavetException { + checkLock(); + return V8Native.getIdentityHash(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType()); + } + public IJavetLogger getLogger() { return logger; } @@ -253,6 +320,11 @@ public int getLength(IV8ValueArray iV8ValueArray) throws JavetException { return V8Native.getLength(handle, iV8ValueArray.getHandle(), iV8ValueArray.getType()); } + public int getLength(IV8ValueTypedArray iV8ValueTypedArray) throws JavetException { + checkLock(); + return V8Native.getLength(handle, iV8ValueTypedArray.getHandle(), iV8ValueTypedArray.getType()); + } + public IV8ValueArray getOwnPropertyNames( IV8ValueObject iV8ValueObject) throws JavetException { checkLock(); @@ -286,16 +358,16 @@ public int getSize(IV8ValueKeyContainer iV8ValueKeyContainer) throws JavetExcept return V8Native.getSize(handle, iV8ValueKeyContainer.getHandle(), iV8ValueKeyContainer.getType()); } - public boolean hasOwnProperty(IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { + public boolean has(IV8ValueObject iV8ValueObject, V8Value value) throws JavetException { checkLock(); - decorateV8Value(key); - return V8Native.hasOwnProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key); + decorateV8Value(value); + return V8Native.has(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), value); } - public boolean has(IV8ValueKeyContainer iV8ValueKeyContainer, V8Value value) throws JavetException { + public boolean hasOwnProperty(IV8ValueObject iV8ValueObject, V8Value key) throws JavetException { checkLock(); - decorateV8Value(value); - return V8Native.has(handle, iV8ValueKeyContainer.getHandle(), iV8ValueKeyContainer.getType(), value); + decorateV8Value(key); + return V8Native.hasOwnProperty(handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), key); } public T invoke( @@ -307,6 +379,14 @@ public T invoke( handle, iV8ValueObject.getHandle(), iV8ValueObject.getType(), functionName, returnResult, v8Values)); } + public boolean isDead() { + return V8Native.isDead(handle); + } + + public boolean isInUse() { + return V8Native.isInUse(handle); + } + public boolean isLocked() { return threadId != INVALID_THREAD_ID; } @@ -387,36 +467,32 @@ public void requestGarbageCollectionForTesting(boolean fullGC) * Reset V8 context. * This is a light-weight and recommended reset. * + * @return the self * @throws JavetException the javet exception */ - public void resetContext() throws JavetException { - removeReferences(); - boolean locked = isLocked(); - if (locked) { - unlock(); + public V8Runtime resetContext() throws JavetException { + if (isLocked()) { + throw JavetV8RuntimeLockConflictException.lockNotRequired(); } + removeReferences(); V8Native.resetV8Context(handle, globalName); - if (locked) { - lock(); - } + return this; } /** * Reset V8 isolate. * This is a heavy reset. Please avoid using it in performance sensitive scenario. * + * @return the self * @throws JavetException the javet exception */ - public void resetIsolate() throws JavetException { - removeReferences(); - boolean locked = isLocked(); - if (locked) { - unlock(); + public V8Runtime resetIsolate() throws JavetException { + if (isLocked()) { + throw JavetV8RuntimeLockConflictException.lockNotRequired(); } + removeReferences(); V8Native.resetV8Isolate(handle, globalName); - if (locked) { - lock(); - } + return this; } public boolean set(IV8ValueObject iV8ValueObject, V8Value key, V8Value value) throws JavetException { @@ -436,6 +512,33 @@ public void setWeak(IV8ValueReference iV8ValueReference) throws JavetException { V8Native.setWeak(handle, iV8ValueReference.getHandle(), iV8ValueReference.getType(), iV8ValueReference); } + public boolean sameValue(V8ValueReference v8ValueReference1, V8ValueReference v8ValueReference2) + throws JavetException { + checkLock(); + decorateV8Values(v8ValueReference1, v8ValueReference2); + return V8Native.sameValue(handle, v8ValueReference1.getHandle(), v8ValueReference2.getHandle()); + } + + public boolean strictEquals(V8ValueReference v8ValueReference1, V8ValueReference v8ValueReference2) + throws JavetException { + checkLock(); + decorateV8Values(v8ValueReference1, v8ValueReference2); + return V8Native.strictEquals(handle, v8ValueReference1.getHandle(), v8ValueReference2.getHandle()); + } + + /** + * Terminate execution. + *

+ * Forcefully terminate the current thread of JavaScript execution + * in the given isolate. + *

+ * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + public void terminateExecution() { + V8Native.terminateExecution(handle); + } + public String toProtoString(IV8ValueReference iV8ValueReference) throws JavetV8RuntimeLockConflictException, JavetV8RuntimeAlreadyClosedException { checkLock(); diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java index 4acceab5f..8635a392b 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngine.java @@ -22,9 +22,17 @@ import com.caoccao.javet.interop.V8Runtime; public interface IJavetEngine extends IJavetClosable { + JavetEngineConfig getConfig(); + V8Runtime getV8Runtime() throws JavetException; + IJavetEngineGuard getGuard(); + + IJavetEngineGuard getGuard(long timeoutMillis); + boolean isActive(); void resetContext() throws JavetException; + + void resetContext(boolean skipLock) throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEngineGuard.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngineGuard.java new file mode 100644 index 000000000..fdb75fe91 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEngineGuard.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.interop.engine; + +import com.caoccao.javet.interfaces.IJavetClosable; + +/** + * The interface Javet engine guard is the one guarding the script execution with a timeout. + *

+ * Usage: + * + * + * try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(5000)) { + * v8Runtime.getExecutor("while (true) {}").executeVoid(); + * // That infinite loop will be terminated in 5 seconds by the guard. + * } + * + */ +public interface IJavetEngineGuard extends IJavetClosable, Runnable { + /** + * Cancel. + */ + void cancel(); + + /** + * Gets timeout millis. + * + * @return the timeout millis + */ + long getTimeoutMillis(); + + /** + * Sets timeout millis. + * + * @param timeoutMillis the timeout millis + */ + void setTimeoutMillis(long timeoutMillis); +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java index 34cba8859..284b4035e 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/IJavetEnginePool.java @@ -28,5 +28,9 @@ public interface IJavetEnginePool extends IJavetClosable { int getIdleEngineCount(); + boolean isActive(); + + boolean isQuitting(); + 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 index 7591022eb..def94aca0 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngine.java @@ -55,6 +55,21 @@ protected void close(boolean forceClose) throws JavetException { } } + @Override + public JavetEngineConfig getConfig() { + return iJavetEnginePool.getConfig(); + } + + @Override + public IJavetEngineGuard getGuard() { + return getGuard(iJavetEnginePool.getConfig().getDefaultEngineGuardTimeoutMillis()); + } + + @Override + public IJavetEngineGuard getGuard(long timeoutMillis) { + return new JavetEngineGuard(this, v8Runtime, timeoutMillis); + } + protected JavetEngineUsage getUsage() { return usage; } @@ -79,13 +94,34 @@ protected ZonedDateTime getUTCNow() { @Override public void resetContext() throws JavetException { + resetContext(false); + } + + @Override + public void resetContext(boolean skipLock) throws JavetException { + if (!skipLock) { + v8Runtime.unlock(); + } v8Runtime.resetContext(); usage.reset(); + if (!skipLock) { + v8Runtime.lock(); + } } protected void resetIsolate() throws JavetException { + resetIsolate(false); + } + + protected void resetIsolate(boolean skipLock) throws JavetException { + if (!skipLock) { + v8Runtime.unlock(); + } v8Runtime.resetIsolate(); usage.reset(); + if (!skipLock) { + v8Runtime.lock(); + } } protected void 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 index 8f4f2d63e..c376a4353 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineConfig.java @@ -22,23 +22,31 @@ import com.caoccao.javet.utils.JavetOSUtils; import java.util.Objects; +import java.util.concurrent.ExecutorService; public final class JavetEngineConfig { + public static final int DEFAULT_ENGINE_GUARD_TIMEOUT_MILLIS = 30000; + public static final int DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS = 1000; 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 final int DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS = 5; public static IJavetLogger DEFAULT_JAVET_LOGGER = new JavetDefaultLogger(JavetEnginePool.class.getName()); private IJavetLogger javetLogger; private String globalName; + private int defaultEngineGuardTimeoutMillis; + private int engineGuardCheckIntervalMillis; private int maxEngineUsedCount; + private int poolDaemonCheckIntervalMillis; private int poolMaxSize; private int poolMinSize; private int poolIdleTimeoutSeconds; - private int poolDaemonCheckIntervalMillis; + private int poolShutdownTimeoutSeconds; private int resetEngineTimeoutSeconds; + private ExecutorService executorService; public JavetEngineConfig() { reset(); @@ -47,15 +55,42 @@ public JavetEngineConfig() { public void reset() { javetLogger = DEFAULT_JAVET_LOGGER; globalName = DEFAULT_GLOBAL_NAME; + defaultEngineGuardTimeoutMillis = DEFAULT_ENGINE_GUARD_TIMEOUT_MILLIS; + engineGuardCheckIntervalMillis = DEFAULT_ENGINE_GUARD_CHECK_INTERVAL_MILLIS; 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; + poolShutdownTimeoutSeconds = DEFAULT_POOL_SHUTDOWN_TIMEOUT_SECONDS; poolDaemonCheckIntervalMillis = DEFAULT_POOL_DAEMON_CHECK_INTERVAL_MILLIS; resetEngineTimeoutSeconds = DEFAULT_RESET_ENGINE_TIMEOUT_SECONDS; } + public ExecutorService getExecutorService() { + return executorService; + } + + void setExecutorService(ExecutorService executorService) { + this.executorService = executorService; + } + + public int getPoolShutdownTimeoutSeconds() { + return poolShutdownTimeoutSeconds; + } + + public void setPoolShutdownTimeoutSeconds(int poolShutdownTimeoutSeconds) { + this.poolShutdownTimeoutSeconds = poolShutdownTimeoutSeconds; + } + + public int getEngineGuardCheckIntervalMillis() { + return engineGuardCheckIntervalMillis; + } + + public void setEngineGuardCheckIntervalMillis(int engineGuardCheckIntervalMillis) { + this.engineGuardCheckIntervalMillis = engineGuardCheckIntervalMillis; + } + public String getGlobalName() { return globalName; } @@ -64,6 +99,14 @@ public void setGlobalName(String globalName) { this.globalName = globalName; } + public int getDefaultEngineGuardTimeoutMillis() { + return defaultEngineGuardTimeoutMillis; + } + + public void setDefaultEngineGuardTimeoutMillis(int defaultEngineGuardTimeoutMillis) { + this.defaultEngineGuardTimeoutMillis = defaultEngineGuardTimeoutMillis; + } + public int getResetEngineTimeoutSeconds() { return resetEngineTimeoutSeconds; } diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java new file mode 100644 index 000000000..bef1d31c7 --- /dev/null +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEngineGuard.java @@ -0,0 +1,109 @@ +/* + * 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.IJavetLogger; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.JavetDateTimeUtils; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +public class JavetEngineGuard implements IJavetEngineGuard { + protected long timeoutMillis; + protected IJavetEngine iJavetEngine; + protected boolean quitting; + protected V8Runtime v8Runtime; + protected Future future; + + public JavetEngineGuard(IJavetEngine iJavetEngine, V8Runtime v8Runtime, long timeoutMills) { + Objects.requireNonNull(iJavetEngine); + this.iJavetEngine = iJavetEngine; + this.timeoutMillis = timeoutMills; + quitting = false; + this.v8Runtime = v8Runtime; + future = this.iJavetEngine.getConfig().getExecutorService().submit(this); + } + + @Override + public void cancel() { + quitting = true; + } + + @Override + public void close() throws JavetException { + cancel(); + if (!future.isDone() && !future.isCancelled()) { + future.cancel(true); + } + } + + @Override + public long getTimeoutMillis() { + return timeoutMillis; + } + + @Override + public void setTimeoutMillis(long timeoutSeconds) { + this.timeoutMillis = timeoutSeconds; + } + + public boolean isQuitting() { + return quitting; + } + + protected ZonedDateTime getUTCNow() { + return JavetDateTimeUtils.getUTCNow(); + } + + @Override + public void run() { + JavetEngineConfig config = iJavetEngine.getConfig(); + IJavetLogger logger = config.getJavetLogger(); + ZonedDateTime startZonedDateTime = getUTCNow(); + while (!isQuitting() && iJavetEngine.isActive()) { + ZonedDateTime currentZonedDateTime = getUTCNow(); + if (startZonedDateTime.plusNanos(TimeUnit.MILLISECONDS.toNanos(timeoutMillis)) + .isBefore(currentZonedDateTime)) { + try { + if (v8Runtime.isInUse()) { + // Javet only terminates the execution when V8 runtime is in use. + v8Runtime.terminateExecution(); + Duration duration = Duration.between(startZonedDateTime, currentZonedDateTime); + logger.logWarn("Execution was terminated after {0}ms.", duration.toMillis()); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } finally { + break; + } + } else { + try { + Thread.sleep(config.getEngineGuardCheckIntervalMillis()); + } catch (InterruptedException e) { + // It's closed. + } + } + } + quitting = true; + } +} diff --git a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java index 85232cc2d..c3d8a868f 100644 --- a/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java +++ b/src/main/java/com/caoccao/javet/interop/engine/JavetEnginePool.java @@ -18,6 +18,7 @@ package com.caoccao.javet.interop.engine; import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interfaces.IJavetLogger; import com.caoccao.javet.interop.V8Host; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.utils.JavetDateTimeUtils; @@ -26,6 +27,7 @@ import java.time.temporal.ChronoUnit; import java.util.LinkedList; import java.util.Objects; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class JavetEnginePool implements IJavetEnginePool, Runnable { @@ -79,7 +81,8 @@ public JavetEngineConfig getConfig() { @Override public IJavetEngine getEngine() { - config.getJavetLogger().debug("JavetEnginePool.getEngine() begins."); + IJavetLogger logger = config.getJavetLogger(); + logger.debug("JavetEnginePool.getEngine() begins."); JavetEngine engine = null; while (!quitting) { synchronized (internalLock) { @@ -99,12 +102,12 @@ public IJavetEngine getEngine() { try { TimeUnit.MILLISECONDS.sleep(config.getPoolDaemonCheckIntervalMillis()); } catch (InterruptedException e) { - config.getJavetLogger().logError(e, "Failed to sleep a while to wait for an idle engine."); + logger.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."); + logger.debug("JavetEnginePool.getEngine() ends."); return engine; } @@ -119,22 +122,30 @@ protected ZonedDateTime getUTCNow() { return JavetDateTimeUtils.getUTCNow(); } + @Override public boolean isActive() { return active; } + @Override + public boolean isQuitting() { + return quitting; + } + @Override public void releaseEngine(IJavetEngine engine) { - config.getJavetLogger().debug("JavetEnginePool.releaseEngine() begins."); + IJavetLogger logger = config.getJavetLogger(); + logger.debug("JavetEnginePool.releaseEngine() begins."); synchronized (externalLock) { externalLock.notify(); } - config.getJavetLogger().debug("JavetEnginePool.releaseEngine() ends."); + logger.debug("JavetEnginePool.releaseEngine() ends."); } @Override public void run() { - config.getJavetLogger().debug("JavetEnginePool.run() begins."); + IJavetLogger logger = config.getJavetLogger(); + logger.debug("JavetEnginePool.run() begins."); while (!quitting) { synchronized (internalLock) { if (!activeEngineList.isEmpty()) { @@ -144,17 +155,19 @@ public void run() { 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."); + if (config.getMaxEngineUsedCount() > 0) { + JavetEngineUsage usage = engine.getUsage(); + ZonedDateTime resetEngineZonedDateTime = usage.getLastActiveZonedDatetime() + .plus(config.getResetEngineTimeoutSeconds(), ChronoUnit.SECONDS); + if (usage.getEngineUsedCount() >= config.getMaxEngineUsedCount() || + resetEngineZonedDateTime.isBefore(getUTCNow())) { + try { + logger.debug("JavetEnginePool reset engine begins."); + engine.resetContext(true); + logger.debug("JavetEnginePool reset engine ends."); + } catch (Exception e) { + logger.logError(e, "Failed to reset idle engine."); + } } } idleEngineList.push(engine); @@ -175,7 +188,7 @@ public void run() { try { engine.close(true); } catch (Throwable t) { - config.getJavetLogger().logError(t, "Failed to release idle engine."); + logger.logError(t, "Failed to release idle engine."); } } else { idleEngineList.push(engine); @@ -187,12 +200,12 @@ public void run() { try { externalLock.wait(config.getPoolDaemonCheckIntervalMillis()); } catch (InterruptedException e) { - config.getJavetLogger().logError(e, + logger.logError(e, "Failed to sleep a while to wait for next round in Javet engine pool daemon."); } } } - config.getJavetLogger().debug("JavetEnginePool daemon is quitting."); + logger.debug("JavetEnginePool daemon is quitting."); synchronized (internalLock) { if (!idleEngineList.isEmpty()) { final int idleEngineCount = getIdleEngineCount(); @@ -201,7 +214,7 @@ public void run() { try { engine.close(true); } catch (Throwable t) { - config.getJavetLogger().logError(t, "Failed to release idle engine."); + logger.logError(t, "Failed to release idle engine."); } } } @@ -212,39 +225,50 @@ public void run() { try { engine.close(true); } catch (Throwable t) { - config.getJavetLogger().logError(t, "Failed to release active engine."); + logger.logError(t, "Failed to release active engine."); } } } } - config.getJavetLogger().debug("JavetEnginePool.run() ends."); + logger.debug("JavetEnginePool.run() ends."); } protected void startDaemon() { - config.getJavetLogger().debug("JavetEnginePool.startDaemon() begins."); + IJavetLogger logger = config.getJavetLogger(); + logger.debug("JavetEnginePool.startDaemon() begins."); activeEngineList.clear(); idleEngineList.clear(); quitting = false; + config.setExecutorService(Executors.newCachedThreadPool()); daemonThread = new Thread(this); daemonThread.start(); active = true; - config.getJavetLogger().debug("JavetEnginePool.startDaemon() ends."); + logger.debug("JavetEnginePool.startDaemon() ends."); } protected void stopDaemon() { - config.getJavetLogger().debug("JavetEnginePool.stopDaemon() begins."); + IJavetLogger logger = config.getJavetLogger(); + logger.debug("JavetEnginePool.stopDaemon() begins."); quitting = true; + try { + config.getExecutorService().shutdown(); + config.getExecutorService().awaitTermination(config.getPoolShutdownTimeoutSeconds(), TimeUnit.SECONDS); + } catch (Exception e) { + logger.logError(e, e.getMessage()); + } finally { + config.setExecutorService(null); + } try { if (daemonThread != null) { daemonThread.join(); } } catch (Exception e) { - config.getJavetLogger().logError(e, e.getMessage()); + logger.logError(e, e.getMessage()); } finally { daemonThread = null; } active = false; quitting = false; - config.getJavetLogger().debug("JavetEnginePool.stopDaemon() ends."); + logger.debug("JavetEnginePool.stopDaemon() ends."); } } diff --git a/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java index 3b44b84c3..eba4a949b 100644 --- a/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java +++ b/src/main/java/com/caoccao/javet/utils/JavetDefaultLogger.java @@ -2,6 +2,10 @@ import com.caoccao.javet.interfaces.IJavetLogger; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.util.logging.Level; import java.util.logging.Logger; @@ -35,7 +39,13 @@ public void error(String message) { @Override public void error(String message, Throwable cause) { logger.severe(message); - logger.severe(cause.getMessage()); + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + try (PrintStream printStream = new PrintStream(byteArrayOutputStream)) { + cause.printStackTrace(printStream); + logger.severe(byteArrayOutputStream.toString(StandardCharsets.UTF_8.name())); + } + } catch (IOException e) { + } } @Override diff --git a/src/main/java/com/caoccao/javet/utils/V8ValueUtils.java b/src/main/java/com/caoccao/javet/utils/V8ValueUtils.java index 54a83ac3c..5ad496fbe 100644 --- a/src/main/java/com/caoccao/javet/utils/V8ValueUtils.java +++ b/src/main/java/com/caoccao/javet/utils/V8ValueUtils.java @@ -17,19 +17,12 @@ 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; import java.util.Arrays; import java.util.stream.Collectors; -@SuppressWarnings("unchecked") public final class V8ValueUtils { - public static final String FUNCTION_NEXT = "next"; - public static final String PROPERTY_DONE = "done"; - public static final String PROPERTY_VALUE = "value"; public static final String EMPTY = ""; private V8ValueUtils() { @@ -44,32 +37,6 @@ public static String concat(String delimiter, V8Value... v8Values) { } return String.join( delimiter, - Arrays.stream(v8Values).map(v8Value -> v8Value.toString()).collect(Collectors.toList())); - } - - 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; + Arrays.stream(v8Values).map(V8Value::toString).collect(Collectors.toList())); } } diff --git a/src/main/java/com/caoccao/javet/utils/converters/IJavetConverter.java b/src/main/java/com/caoccao/javet/utils/converters/IJavetConverter.java index e6986a1a2..bcaffe597 100644 --- a/src/main/java/com/caoccao/javet/utils/converters/IJavetConverter.java +++ b/src/main/java/com/caoccao/javet/utils/converters/IJavetConverter.java @@ -21,8 +21,9 @@ import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.values.V8Value; +@SuppressWarnings("unchecked") public interface IJavetConverter { Object toObject(V8Value v8Value) throws JavetException; - V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetException; + T toV8Value(V8Runtime v8Runtime, Object object) throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/utils/converters/JavetObjectConverter.java b/src/main/java/com/caoccao/javet/utils/converters/JavetObjectConverter.java index b7a459544..f65320f6b 100644 --- a/src/main/java/com/caoccao/javet/utils/converters/JavetObjectConverter.java +++ b/src/main/java/com/caoccao/javet/utils/converters/JavetObjectConverter.java @@ -17,21 +17,34 @@ package com.caoccao.javet.utils.converters; +import com.caoccao.javet.entities.JavetEntityMap; import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; import com.caoccao.javet.values.primitive.*; import com.caoccao.javet.values.reference.*; -import com.caoccao.javet.values.virtual.V8VirtualList; import java.util.*; +@SuppressWarnings("unchecked") public class JavetObjectConverter extends JavetPrimitiveConverter { + public static final String PROPERTY_CONSTRUCTOR = "constructor"; + public static final String PROPERTY_NAME = "name"; + public JavetObjectConverter() { super(); } + protected Map createEntityMap() { + return new JavetEntityMap(); + } + + protected boolean isEntityMap(Object object) { + return object instanceof JavetEntityMap; + } + @Override public Object toObject(V8Value v8Value) throws JavetException { Object returnObject = super.toObject(v8Value); @@ -61,21 +74,76 @@ public Object toObject(V8Value v8Value) throws JavetException { } else if (v8Value instanceof V8ValueSet) { V8ValueSet v8ValueSet = (V8ValueSet) v8Value; HashSet set = new HashSet<>(); - try (V8VirtualList items = v8ValueSet.getKeys()) { - for (V8Value item : items) { - set.add(toObject(item)); + try (IV8ValueIterator iterator = v8ValueSet.getKeys()) { + while (true) { + try (V8Value key = iterator.getNext()) { + if (key == null) { + break; + } + set.add(toObject(key)); + } } } return set; - } else if (v8Value instanceof V8ValueMap || v8Value instanceof V8ValueObject) { + } else if (v8Value instanceof V8ValueMap) { + V8ValueMap v8ValueMap = (V8ValueMap) v8Value; + Map map = createEntityMap(); + try (IV8ValueIterator iterator = v8ValueMap.getEntries()) { + while (true) { + try (V8ValueArray entry = iterator.getNext()) { + if (entry == null) { + break; + } + try (V8Value key = entry.get(0); V8Value value = entry.get(1);) { + map.put(key.toString(), toObject(value)); + } + } + } + } + return map; + } else if (v8Value instanceof V8ValueTypedArray) { + V8ValueTypedArray v8ValueTypedArray = (V8ValueTypedArray) v8Value; + switch (v8ValueTypedArray.getType()) { + case V8ValueReferenceType.Int8Array: + case V8ValueReferenceType.Uint8Array: + case V8ValueReferenceType.Uint8ClampedArray: + return v8ValueTypedArray.toBytes(); + case V8ValueReferenceType.Int16Array: + case V8ValueReferenceType.Uint16Array: + return v8ValueTypedArray.toShorts(); + case V8ValueReferenceType.Int32Array: + case V8ValueReferenceType.Uint32Array: + return v8ValueTypedArray.toIntegers(); + case V8ValueReferenceType.Float32Array: + return v8ValueTypedArray.toFloats(); + case V8ValueReferenceType.Float64Array: + return v8ValueTypedArray.toDoubles(); + case V8ValueReferenceType.BigInt64Array: + case V8ValueReferenceType.BigUint64Array: + return v8ValueTypedArray.toLongs(); + default: + break; + } + } else if (v8Value instanceof V8ValueObject) { V8ValueObject v8ValueObject = (V8ValueObject) v8Value; Map map = new HashMap<>(); try (IV8ValueArray iV8ValueArray = v8ValueObject.getOwnPropertyNames()) { final int length = iV8ValueArray.getLength(); for (int i = 0; i < length; ++i) { try (V8Value key = iV8ValueArray.get(i)) { - try (V8Value value = v8ValueObject.get(key)) { - map.put(toObject(key).toString(), toObject(value)); + String keyString = key.toString(); + /* + Constructor is treated differently because it references to itself. + Otherwise stack overflow will take place. + */ + if (PROPERTY_CONSTRUCTOR.equals(keyString)) { + try (V8ValueObject v8ValueObjectValue = v8ValueObject.get(keyString)) { + map.put(keyString, v8ValueObjectValue.getString(PROPERTY_NAME)); + } + } else { + try (V8Value value = v8ValueObject.get(key)) { + map.put(keyString, toObject(value)); + } } } } @@ -86,12 +154,22 @@ public Object toObject(V8Value v8Value) throws JavetException { } @Override - public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetException { + public T toV8Value(V8Runtime v8Runtime, Object object) throws JavetException { V8Value v8Value = super.toV8Value(v8Runtime, object); - if (v8Value != null && !(v8Value instanceof V8ValueUndefined)) { - return v8Value; + if (v8Value != null && !(v8Value.isUndefined())) { + return (T) v8Value; } - if (object instanceof Map) { + if (isEntityMap(object)) { + V8ValueMap v8ValueMap = v8Runtime.createV8ValueMap(); + Map mapObject = (Map) object; + for (Object key : mapObject.keySet()) { + try (V8Value childV8Value = toV8Value(v8Runtime, mapObject.get(key))) { + String childStringKey = key instanceof String ? (String) key : key.toString(); + v8ValueMap.set(childStringKey, childV8Value); + } + } + v8Value = v8ValueMap; + } else if (object instanceof Map) { V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject(); Map mapObject = (Map) object; for (Object key : mapObject.keySet()) { @@ -125,31 +203,41 @@ public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetExcepti } v8Value = v8ValueArray; } else if (object instanceof byte[]) { - // TODO To support byte[] + byte[] bytes = (byte[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.Int8Array, bytes.length); + v8ValueTypedArray.fromBytes(bytes); + v8Value = v8ValueTypedArray; } else if (object instanceof double[]) { - V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); - for (double item : (double[]) object) { - v8ValueArray.push(new V8ValueDouble(item)); - } - v8Value = v8ValueArray; + double[] doubles = (double[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.Float64Array, doubles.length); + v8ValueTypedArray.fromDoubles(doubles); + v8Value = v8ValueTypedArray; } else if (object instanceof float[]) { - V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); - for (float item : (float[]) object) { - v8ValueArray.push(new V8ValueDouble(item)); - } - v8Value = v8ValueArray; + float[] floats = (float[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.Float32Array, floats.length); + v8ValueTypedArray.fromFloats(floats); + v8Value = v8ValueTypedArray; } else if (object instanceof int[]) { - V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); - for (int item : (int[]) object) { - v8ValueArray.push(new V8ValueInteger(item)); - } - v8Value = v8ValueArray; + int[] integers = (int[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.Int32Array, integers.length); + v8ValueTypedArray.fromIntegers(integers); + v8Value = v8ValueTypedArray; } else if (object instanceof long[]) { - V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); - for (long item : (long[]) object) { - v8ValueArray.push(new V8ValueLong(item)); - } - v8Value = v8ValueArray; + long[] longs = (long[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.BigInt64Array, longs.length); + v8ValueTypedArray.fromLongs(longs); + v8Value = v8ValueTypedArray; + } else if (object instanceof short[]) { + short[] shorts = (short[]) object; + V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray( + V8ValueReferenceType.Int16Array, shorts.length); + v8ValueTypedArray.fromShorts(shorts); + v8Value = v8ValueTypedArray; } else if (object instanceof String[]) { V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray(); for (String item : (String[]) object) { @@ -165,6 +253,6 @@ public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetExcepti } v8Value = v8ValueArray; } - return v8Runtime.decorateV8Value(v8Value); + return (T) v8Runtime.decorateV8Value(v8Value); } } diff --git a/src/main/java/com/caoccao/javet/utils/converters/JavetPrimitiveConverter.java b/src/main/java/com/caoccao/javet/utils/converters/JavetPrimitiveConverter.java index 4eb030ba0..961735e7c 100644 --- a/src/main/java/com/caoccao/javet/utils/converters/JavetPrimitiveConverter.java +++ b/src/main/java/com/caoccao/javet/utils/converters/JavetPrimitiveConverter.java @@ -24,13 +24,14 @@ import java.time.ZonedDateTime; +@SuppressWarnings("unchecked") public class JavetPrimitiveConverter implements IJavetConverter { public JavetPrimitiveConverter() { } @Override public Object toObject(V8Value v8Value) throws JavetException { - if (v8Value == null || v8Value instanceof V8ValueNull || v8Value instanceof V8ValueUndefined) { + if (v8Value == null || v8Value.isNull() || v8Value.isUndefined()) { return null; } else if (v8Value instanceof V8ValuePrimitive) { return ((V8ValuePrimitive) v8Value).getValue(); @@ -39,10 +40,10 @@ public Object toObject(V8Value v8Value) throws JavetException { } @Override - public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetException { + public T toV8Value(V8Runtime v8Runtime, Object object) throws JavetException { V8Value v8Value = null; if (object == null) { - v8Value = new V8ValueNull(); + v8Value = v8Runtime.createV8ValueNull(); } else if (object instanceof V8Value) { v8Value = (V8Value) object; } else if (object instanceof Boolean) { @@ -55,6 +56,8 @@ public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetExcepti v8Value = new V8ValueInteger((Integer) object); } else if (object instanceof Long) { v8Value = new V8ValueLong((Long) object); + } else if (object instanceof Short) { + v8Value = new V8ValueInteger((Short) object); } else if (object instanceof String) { v8Value = new V8ValueString((String) object); } else if (object instanceof ZonedDateTime) { @@ -62,9 +65,9 @@ public V8Value toV8Value(V8Runtime v8Runtime, Object object) throws JavetExcepti } else if (object instanceof Byte) { v8Value = new V8ValueInteger((Byte) object); } else { - v8Value = new V8ValueUndefined(); + v8Value = v8Runtime.createV8ValueUndefined(); } - return v8Runtime.decorateV8Value(v8Value); + return (T) v8Runtime.decorateV8Value(v8Value); } } diff --git a/src/main/java/com/caoccao/javet/values/IV8Value.java b/src/main/java/com/caoccao/javet/values/IV8Value.java index 6a098a721..0699182b8 100644 --- a/src/main/java/com/caoccao/javet/values/IV8Value.java +++ b/src/main/java/com/caoccao/javet/values/IV8Value.java @@ -21,9 +21,69 @@ import com.caoccao.javet.interfaces.IJavetClosable; import com.caoccao.javet.interop.IV8Cloneable; import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.values.primitive.V8ValueDouble; +import com.caoccao.javet.values.primitive.V8ValueNull; +import com.caoccao.javet.values.primitive.V8ValueUndefined; +/** + * The interface V8 value. + */ public interface IV8Value extends IJavetClosable, IV8Cloneable { + /** + * Gets V8 runtime. + * + * @return the V8 runtime + */ V8Runtime getV8Runtime(); - T toClone() throws JavetException; + /** + * Equals. + *

+ * The behavior is different from JS behavior but is the same as Java behavior. + * + * @param v8Value the V8 value + * @return the boolean + * @throws JavetException the javet exception + */ + boolean equals(V8Value v8Value) throws JavetException; + + /** + * Is null. + * + * @return the boolean + */ + default boolean isNull() { + return this instanceof V8ValueNull; + } + + /** + * Is undefined. + * + * @return the boolean + */ + default boolean isUndefined() { + return this instanceof V8ValueUndefined; + } + + /** + * Same value. + *

+ * The behavior is different from JS behavior but is the same as Java behavior. + * + * @param v8Value the v 8 value + * @return the boolean + * @throws JavetException the javet exception + */ + boolean sameValue(V8Value v8Value) throws JavetException; + + /** + * Strict equals boolean. + *

+ * The behavior is different from JS behavior but is the same as Java behavior. + * + * @param v8Value the V8 value + * @return the boolean + * @throws JavetException the javet exception + */ + boolean strictEquals(V8Value v8Value) throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/values/V8Value.java b/src/main/java/com/caoccao/javet/values/V8Value.java index 3a872e399..38f8128e1 100644 --- a/src/main/java/com/caoccao/javet/values/V8Value.java +++ b/src/main/java/com/caoccao/javet/values/V8Value.java @@ -24,7 +24,7 @@ public abstract class V8Value implements IV8Value { protected V8Runtime v8Runtime; - public V8Value() { + protected V8Value() { v8Runtime = null; } @@ -46,6 +46,9 @@ public void close() throws JavetException { v8Runtime = null; } + @Override + public abstract boolean equals(V8Value v8Value) throws JavetException; + @Override public abstract T toClone() throws JavetException; @@ -61,5 +64,11 @@ public void setV8Runtime(V8Runtime v8Runtime) throws JavetException { this.v8Runtime.checkLock(); } + @Override + public abstract boolean sameValue(V8Value v8Value) throws JavetException; + + @Override + public abstract boolean strictEquals(V8Value v8Value) throws JavetException; + 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 index 124a210fd..aebfcf92f 100644 --- a/src/main/java/com/caoccao/javet/values/V8ValueReferenceType.java +++ b/src/main/java/com/caoccao/javet/values/V8ValueReferenceType.java @@ -18,6 +18,7 @@ package com.caoccao.javet.values; public final class V8ValueReferenceType { + public static final int Invalid = 0; public static final int Object = 1; public static final int Error = 2; public static final int RegExp = 3; @@ -29,4 +30,18 @@ public final class V8ValueReferenceType { public static final int Set = 9; public static final int Array = 10; public static final int Function = 11; + public static final int Iterator = 12; + public static final int DataView = 30; + public static final int ArrayBuffer = 31; + public static final int Int8Array = 32; // -128 to 127 1 8-bit two's complement signed integer byte int8_t + public static final int Uint8Array = 33; // 0 to 255 1 8-bit unsigned integer octet uint8_t + public static final int Uint8ClampedArray = 34; // 0 to 255 1 8-bit unsigned integer (clamped) octet uint8_t + public static final int Int16Array = 35; // -32768 to 32767 2 16-bit two's complement signed integer short int16_t + public static final int Uint16Array = 36; // 0 to 65535 2 16-bit unsigned integer unsigned short uint16_t + public static final int Int32Array = 37; // -2147483648 to 2147483647 4 32-bit two's complement signed integer long int32_t + public static final int Uint32Array = 38; // 0 to 4294967295 4 32-bit unsigned integer unsigned long uint32_t + public static final int Float32Array = 39; // 1.2×10^-38 to 3.4×10^38 4 32-bit IEEE floating point number (7 significant digits e.g., 1.234567) unrestricted float float + public static final int Float64Array = 40; // 5.0×10^-324 to 1.8×10^308 8 64-bit IEEE floating point number (16 significant digits e.g., 1.23456789012345) unrestricted double double + public static final int BigInt64Array = 41; // -2^63 to 2^63-1 8 64-bit two's complement signed integer bigint int64_t (signed long long) + public static final int BigUint64Array = 42; // 0 to 2^64-1 8 64-bit unsigned integer bigint uint64_t (unsigned long long) } diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java index c52c0d339..256db3eec 100644 --- a/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueDouble.java @@ -29,6 +29,18 @@ public V8ValueDouble(double value) { super(value); } + public boolean isFinite() { + return Double.isFinite(value); + } + + public boolean isInfinite() { + return Double.isInfinite(value); + } + + public boolean isNaN() { + return Double.isNaN(value); + } + @Override public V8ValueDouble toClone() throws JavetException { return v8Runtime.decorateV8Value(new V8ValueDouble(value)); diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java index ea085556e..1f45caf89 100644 --- a/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueNull.java @@ -33,13 +33,28 @@ public V8ValueNull() { protected void addReference() { } + @Override + public boolean equals(V8Value v8Value) { + return v8Value instanceof V8ValueNull; + } + @Override protected void removeReference() { } @Override - public V8ValueNull toClone() throws JavetException { - return v8Runtime.decorateV8Value(new V8ValueNull()); + public boolean sameValue(V8Value v8Value) { + return equals(v8Value); + } + + @Override + public boolean strictEquals(V8Value v8Value) { + return equals(v8Value); + } + + @Override + public V8ValueNull toClone() { + return v8Runtime.createV8ValueNull(); } @Override diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java index e608cc39e..5cc0f766c 100644 --- a/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValuePrimitive.java @@ -36,6 +36,17 @@ public V8ValuePrimitive(T value) { protected void addReference() { } + @Override + public boolean equals(V8Value v8Value) { + if (v8Value == null || !(v8Value instanceof V8ValuePrimitive)) { + return false; + } + if (v8Value.getClass() != this.getClass()) { + return false; + } + return getValue().equals(((V8ValuePrimitive) v8Value).getValue()); + } + public T getValue() { return value; } @@ -56,6 +67,16 @@ public boolean isPresent() { protected void removeReference() { } + @Override + public boolean sameValue(V8Value v8Value) { + return equals(v8Value); + } + + @Override + public boolean strictEquals(V8Value v8Value) { + return equals(v8Value); + } + @Override public String toString() { return isEmpty() ? null : value.toString(); diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java index 0137ed695..8a338a69b 100644 --- a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUndefined.java @@ -33,13 +33,28 @@ public V8ValueUndefined() { protected void addReference() { } + @Override + public boolean equals(V8Value v8Value) { + return v8Value instanceof V8ValueUndefined; + } + @Override protected void removeReference() { } + @Override + public boolean sameValue(V8Value v8Value) { + return equals(v8Value); + } + + @Override + public boolean strictEquals(V8Value v8Value) { + return equals(v8Value); + } + @Override public V8ValueUndefined toClone() throws JavetException { - return v8Runtime.decorateV8Value(new V8ValueUndefined()); + return v8Runtime.createV8ValueUndefined(); } @Override diff --git a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java index 0a5b275b3..c4c80bd85 100644 --- a/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java +++ b/src/main/java/com/caoccao/javet/values/primitive/V8ValueUnknown.java @@ -18,6 +18,7 @@ package com.caoccao.javet.values.primitive; import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; @SuppressWarnings("unchecked") public final class V8ValueUnknown extends V8ValuePrimitive { diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java index 1920deed5..b9e0f730c 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueArray.java @@ -22,9 +22,11 @@ import com.caoccao.javet.values.primitive.*; import com.caoccao.javet.values.virtual.V8VirtualList; +import java.util.List; + @SuppressWarnings("unchecked") public interface IV8ValueArray extends IV8ValueObject { - V8VirtualList getKeys() throws JavetException; + List getKeys() throws JavetException; int getLength() throws JavetException; @@ -91,10 +93,10 @@ default int push(String value) throws JavetException { } default int pushNull() throws JavetException { - return push(new V8ValueNull()); + return push(getV8Runtime().createV8ValueNull()); } default int pushUndefined() throws JavetException { - return push(new V8ValueUndefined()); + return push(getV8Runtime().createV8ValueUndefined()); } } diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java index 4aa26e899..8e50d31da 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueFunction.java @@ -30,6 +30,8 @@ default T call(IV8ValueObject receiver, V8Value... v8Values) return call(receiver, true, v8Values); } + T callAsConstructor(V8Value... v8Values) throws JavetException; + default Boolean callBoolean(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { return callObject(receiver, v8Values); } @@ -38,6 +40,11 @@ default Double callDouble(IV8ValueObject receiver, V8Value... v8Values) throws J return callObject(receiver, v8Values); } + default Float callFloat(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { + Double result = callDouble(receiver, v8Values); + return result == null ? null : result.floatValue(); + } + default Integer callInteger(IV8ValueObject receiver, V8Value... v8Values) throws JavetException { return callObject(receiver, v8Values); } diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueIterator.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueIterator.java new file mode 100644 index 000000000..06727e159 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueIterator.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.values.reference; + +import com.caoccao.javet.interfaces.IJavetClosable; +import com.caoccao.javet.values.V8Value; + +/** + * The interface V8 value iterator. + * + * @param the type parameter + */ +@SuppressWarnings("unchecked") +public interface IV8ValueIterator extends IJavetClosable { + /** + * Gets next. + * + * @return the next + */ + T getNext(); +} diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java index 40b5c5113..577ac766f 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueKeyContainer.java @@ -25,29 +25,7 @@ @SuppressWarnings("unchecked") public interface IV8ValueKeyContainer extends IV8ValueObject { - List getKeys() throws JavetException; + IV8ValueIterator 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 index d8d2fe514..c2376e2a0 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueMap.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueMap.java @@ -24,7 +24,7 @@ @SuppressWarnings("unchecked") public interface IV8ValueMap extends IV8ValueKeyContainer { - List getEntries() throws JavetException; + IV8ValueIterator getEntries() throws JavetException; - List getValues() throws JavetException; + IV8ValueIterator 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 index 602094e3c..de052d456 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueObject.java @@ -26,6 +26,28 @@ @SuppressWarnings("unchecked") public interface IV8ValueObject extends IV8ValueReference { + 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(getV8Runtime().createV8ValueNull()); + } + + default boolean hasUndefined() throws JavetException { + return has(getV8Runtime().createV8ValueUndefined()); + } + T invoke(String functionName, boolean returnResult, V8Value... v8Values) throws JavetException; default T invoke(String functionName, V8Value... v8Values) throws JavetException { @@ -40,6 +62,11 @@ default Double invokeDouble(String functionName, V8Value... v8Values) throws Jav return invokeObject(functionName, v8Values); } + default Float invokeFloat(String functionName, V8Value... v8Values) throws JavetException { + Double result = invokeDouble(functionName, v8Values); + return result == null ? null : result.floatValue(); + } + default Integer invokeInteger(String functionName, V8Value... v8Values) throws JavetException { return invokeObject(functionName, v8Values); } @@ -82,11 +109,11 @@ default boolean delete(String key) throws JavetException { boolean delete(V8Value key) throws JavetException; default boolean deleteNull() throws JavetException { - return delete(new V8ValueNull()); + return delete(getV8Runtime().createV8ValueNull()); } default boolean deleteUndefined() throws JavetException { - return delete(new V8ValueUndefined()); + return delete(getV8Runtime().createV8ValueUndefined()); } default T get(int key) throws JavetException { @@ -115,6 +142,16 @@ default Double getDouble(String key) throws JavetException { return getObject(key); } + default Float getFloat(int key) throws JavetException { + Double result = getDouble(key); + return result == null ? null : result.floatValue(); + } + + default Float getFloat(String key) throws JavetException { + Double result = getDouble(key); + return result == null ? null : result.floatValue(); + } + default Integer getInteger(int key) throws JavetException { return getObject(key); } @@ -189,6 +226,16 @@ default Double getPropertyDouble(String key) throws JavetException { return getPropertyObject(key); } + default Float getPropertyFloat(int index) throws JavetException { + Double result = getPropertyDouble(index); + return result == null ? null : result.floatValue(); + } + + default Float getPropertyFloat(String key) throws JavetException { + Double result = getPropertyDouble(key); + return result == null ? null : result.floatValue(); + } + default Integer getPropertyInteger(int index) throws JavetException { return getPropertyObject(index); } @@ -285,6 +332,16 @@ default boolean set(String key, V8Value value) throws JavetException { boolean set(V8Value key, V8Value value) throws JavetException; + /** + * Sets function by name and callback context. + *

+ * It is for creating a Java code based function in V8. + * + * @param functionName the function name + * @param javetCallbackContext the javet callback context + * @return true: function is set, false: function is not set + * @throws JavetException the javet exception + */ default boolean setFunction(String functionName, JavetCallbackContext javetCallbackContext) throws JavetException { V8ValueFunction v8ValueFunction = getV8Runtime().createV8ValueFunction(javetCallbackContext); boolean success = set(functionName, v8ValueFunction); @@ -292,12 +349,34 @@ default boolean setFunction(String functionName, JavetCallbackContext javetCallb return success; } + /** + * Sets function by name and string. + *

+ * It is for creating a string based function in V8. + *

+ * JS equivalent: + * + * obj.func = function(arg1, arg2) { ... }; + * + * + * @param functionName the function name + * @param codeString the code string + * @return true: function is set, false: function is not set + * @throws JavetException the javet exception + */ + default boolean setFunction(String functionName, String codeString) throws JavetException { + V8ValueFunction v8ValueFunction = getV8Runtime().getExecutor(codeString).execute(); + boolean success = set(functionName, v8ValueFunction); + v8ValueFunction.setWeak(); + return success; + } + default boolean setNull(int key) throws JavetException { - return set(new V8ValueInteger(key), new V8ValueNull()); + return set(new V8ValueInteger(key), getV8Runtime().createV8ValueNull()); } default boolean setNull(String key) throws JavetException { - return set(new V8ValueString(key), new V8ValueNull()); + return set(new V8ValueString(key), getV8Runtime().createV8ValueNull()); } default boolean setProperty(int key, V8Value value) throws JavetException { @@ -311,28 +390,36 @@ default boolean setProperty(String key, V8Value value) throws JavetException { boolean setProperty(V8Value key, V8Value value) throws JavetException; default boolean setPropertyNull(int key) throws JavetException { - return setProperty(new V8ValueInteger(key), new V8ValueNull()); + return setProperty(new V8ValueInteger(key), getV8Runtime().createV8ValueNull()); } default boolean setPropertyNull(String key) throws JavetException { - return setProperty(new V8ValueString(key), new V8ValueNull()); + return setProperty(new V8ValueString(key), getV8Runtime().createV8ValueNull()); } default boolean setPropertyUndefined(int key) throws JavetException { - return setProperty(new V8ValueInteger(key), new V8ValueUndefined()); + return setProperty(new V8ValueInteger(key), getV8Runtime().createV8ValueUndefined()); } default boolean setPropertyUndefined(String key) throws JavetException { - return setProperty(new V8ValueString(key), new V8ValueUndefined()); + return setProperty(new V8ValueString(key), getV8Runtime().createV8ValueUndefined()); } default boolean setUndefined(int key) throws JavetException { - return set(new V8ValueInteger(key), new V8ValueUndefined()); + return set(new V8ValueInteger(key), getV8Runtime().createV8ValueUndefined()); } default boolean setUndefined(String key) throws JavetException { - return set(new V8ValueString(key), new V8ValueUndefined()); + return set(new V8ValueString(key), getV8Runtime().createV8ValueUndefined()); } + /** + * To json string. + *

+ * JS equivalent: + * JSON.stringify(obj); + * + * @return the string + */ 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 index 43e0dc65f..f2562fa9e 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueReference.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueReference.java @@ -28,6 +28,18 @@ public interface IV8ValueReference extends IV8Value { long getHandle(); + /** + * Returns the identity hash for this object. The current implementation + * uses an inline property on the object to store the identity hash. + *

+ * The return value will never be 0. Also, it is not guaranteed to be + * unique. + * + * @return the identity hash + * @throws JavetException the javet exception + */ + int getIdentityHash() throws JavetException; + int getType(); boolean isWeak() throws JavetException; diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java index 47043ff42..062f77b88 100644 --- a/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueSet.java @@ -19,8 +19,6 @@ 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; @@ -43,10 +41,12 @@ default void add(String value) throws JavetException { void add(V8Value key) throws JavetException; default void addNull() throws JavetException { - add(new V8ValueNull()); + add(getV8Runtime().createV8ValueNull()); } default void addUndefined() throws JavetException { - add(new V8ValueUndefined()); + add(getV8Runtime().createV8ValueUndefined()); } + + IV8ValueIterator getEntries() throws JavetException; } diff --git a/src/main/java/com/caoccao/javet/values/reference/IV8ValueTypedArray.java b/src/main/java/com/caoccao/javet/values/reference/IV8ValueTypedArray.java new file mode 100644 index 000000000..4f7cb9a75 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/IV8ValueTypedArray.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; + +public interface IV8ValueTypedArray extends IV8ValueObject { + V8ValueArrayBuffer getBuffer() throws JavetException; + + int getByteLength() throws JavetException; + + int getByteOffset() throws JavetException; + + int getLength() throws JavetException; + + int getSizeInBytes(); + + boolean isValid(); +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java index 88436a9bc..2d1d95a35 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArguments.java @@ -20,7 +20,7 @@ import com.caoccao.javet.values.V8ValueReferenceType; public class V8ValueArguments extends V8ValueArray { - public V8ValueArguments(long handle) { + V8ValueArguments(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java index 374c30d24..ebcd3c0d1 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArray.java @@ -21,17 +21,21 @@ import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.V8ValueReferenceType; import com.caoccao.javet.values.primitive.V8ValueInteger; -import com.caoccao.javet.utils.V8ValueUtils; -import com.caoccao.javet.values.virtual.V8VirtualList; + +import java.util.ArrayList; +import java.util.List; @SuppressWarnings("unchecked") public class V8ValueArray extends V8ValueObject implements IV8ValueArray { + public static final String FUNCTION_NEXT = "next"; + public static final String PROPERTY_DONE = "done"; + public static final String PROPERTY_VALUE = "value"; 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) { + V8ValueArray(long handle) { super(handle); } @@ -42,16 +46,24 @@ public T get(int index) throws JavetException { } @Override - public V8VirtualList getKeys() throws JavetException { + public List getKeys() throws JavetException { checkV8Runtime(); - try (V8ValueObject arrayIterator = invoke(FUNCTION_KEYS)) { - return V8ValueUtils.convertIteratorToIntegerList(arrayIterator); + try (V8ValueObject iterator = invoke(FUNCTION_KEYS)) { + List keys = new ArrayList<>(); + while (true) { + try (V8ValueObject next = iterator.invoke(FUNCTION_NEXT)) { + if (next.getBoolean(PROPERTY_DONE)) { + break; + } + keys.add(next.getInteger(PROPERTY_VALUE)); + } + } + return keys; } } @Override - public int getLength() - throws JavetException { + public int getLength() throws JavetException { checkV8Runtime(); return v8Runtime.getLength(this); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java new file mode 100644 index 000000000..0dff22f4c --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueArrayBuffer.java @@ -0,0 +1,149 @@ +/* + * 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.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Objects; + +@SuppressWarnings("unchecked") +public class V8ValueArrayBuffer extends V8ValueObject { + + public static final String PROPERTY_BYTE_LENGTH = "byteLength"; + public static final int BYTE_LENGTH_1 = 1; + public static final int BYTE_LENGTH_2 = 2; + public static final int BYTE_LENGTH_3 = 3; + + protected ByteBuffer byteBuffer; + protected ByteOrder byteOrder; + + protected V8ValueArrayBuffer(long handle, ByteBuffer byteBuffer) { + super(handle); + this.byteBuffer = byteBuffer; + byteOrder = ByteOrder.nativeOrder(); + } + + public ByteOrder getByteOrder() { + return byteOrder; + } + + public void setByteOrder(ByteOrder byteOrder) { + Objects.requireNonNull(byteOrder); + this.byteOrder = byteOrder; + } + + public ByteBuffer getByteBuffer() { + return byteBuffer; + } + + public int getByteLength() throws JavetException { + return getInteger(PROPERTY_BYTE_LENGTH); + } + + @Override + public int getType() { + return V8ValueReferenceType.ArrayBuffer; + } + + public boolean fromBytes(byte[] bytes) { + if (bytes != null && bytes.length > 0 && bytes.length == byteBuffer.capacity()) { + byteBuffer.put(bytes); + return true; + } + return false; + } + + public boolean fromDoubles(double[] doubles) { + if (doubles != null && doubles.length > 0 && doubles.length == byteBuffer.capacity() >> BYTE_LENGTH_3) { + byteBuffer.order(byteOrder).asDoubleBuffer().put(doubles); + return true; + } + return false; + } + + public boolean fromFloats(float[] floats) { + if (floats != null && floats.length > 0 && floats.length == byteBuffer.capacity() >> BYTE_LENGTH_2) { + byteBuffer.order(byteOrder).asFloatBuffer().put(floats); + return true; + } + return false; + } + + public boolean fromIntegers(int[] integers) { + if (integers != null && integers.length > 0 && integers.length == byteBuffer.capacity() >> BYTE_LENGTH_2) { + byteBuffer.order(byteOrder).asIntBuffer().put(integers); + return true; + } + return false; + } + + public boolean fromLongs(long[] longs) { + if (longs != null && longs.length > 0 && longs.length == byteBuffer.capacity() >> BYTE_LENGTH_3) { + byteBuffer.order(byteOrder).asLongBuffer().put(longs); + return true; + } + return false; + } + + public boolean fromShorts(short[] shorts) { + if (shorts != null && shorts.length > 0 && shorts.length == byteBuffer.capacity() >> BYTE_LENGTH_1) { + byteBuffer.order(byteOrder).asShortBuffer().put(shorts); + return true; + } + return false; + } + + public byte[] toBytes() { + byte[] bytes = new byte[byteBuffer.capacity()]; + byteBuffer.get(bytes); + return bytes; + } + + public double[] toDoubles() { + double[] doubles = new double[byteBuffer.capacity() >> BYTE_LENGTH_3]; + byteBuffer.order(byteOrder).asDoubleBuffer().get(doubles); + return doubles; + } + + public float[] toFloats() { + float[] floats = new float[byteBuffer.capacity() >> BYTE_LENGTH_2]; + byteBuffer.order(byteOrder).asFloatBuffer().get(floats); + return floats; + } + + public int[] toIntegers() { + int[] integers = new int[byteBuffer.capacity() >> BYTE_LENGTH_2]; + byteBuffer.order(byteOrder).asIntBuffer().get(integers); + return integers; + } + + public long[] toLongs() { + long[] longs = new long[byteBuffer.capacity() >> BYTE_LENGTH_3]; + byteBuffer.order(byteOrder).asLongBuffer().get(longs); + return longs; + } + + public short[] toShorts() { + short[] shorts = new short[byteBuffer.capacity() >> BYTE_LENGTH_1]; + byteBuffer.order(byteOrder).asShortBuffer().get(shorts); + return shorts; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.java new file mode 100644 index 000000000..bdcfb05da --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueDataView.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.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.primitive.V8ValueBoolean; +import com.caoccao.javet.values.primitive.V8ValueDouble; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueLong; + +public class V8ValueDataView extends V8ValueObject { + /** + * The constant PROPERTY_BYTE_LENGTH. + */ + public static final String PROPERTY_BYTE_LENGTH = "byteLength"; + /** + * The constant PROPERTY_BUFFER. + */ + public static final String PROPERTY_BUFFER = "buffer"; + /** + * The constant PROPERTY_BYTE_OFFSET. + */ + public static final String PROPERTY_BYTE_OFFSET = "byteOffset"; + public static final String FUNCTION_GET_BIG_INT_64 = "getBigInt64"; + public static final String FUNCTION_GET_FLOAT_32 = "getFloat32"; + public static final String FUNCTION_GET_FLOAT_64 = "getFloat64"; + public static final String FUNCTION_GET_INT_8 = "getInt8"; + public static final String FUNCTION_GET_INT_16 = "getInt16"; + public static final String FUNCTION_GET_INT_32 = "getInt32"; + public static final String FUNCTION_SET_BIG_INT_64 = "setBigInt64"; + public static final String FUNCTION_SET_FLOAT_32 = "setFloat32"; + public static final String FUNCTION_SET_FLOAT_64 = "setFloat64"; + public static final String FUNCTION_SET_INT_8 = "setInt8"; + public static final String FUNCTION_SET_INT_16 = "setInt16"; + public static final String FUNCTION_SET_INT_32 = "setInt32"; + + V8ValueDataView(long handle) { + super(handle); + } + + public long getBigInt64(int byteOffset) throws JavetException { + return getBigInt64(byteOffset, true); + } + + public long getBigInt64(int byteOffset, boolean littleEndian) throws JavetException { + return invokeLong(FUNCTION_GET_BIG_INT_64, + new V8ValueInteger(byteOffset), new V8ValueBoolean(littleEndian)).longValue(); + } + + public V8ValueArrayBuffer getBuffer() throws JavetException { + return get(PROPERTY_BUFFER); + } + + public int getByteLength() throws JavetException { + return getInteger(PROPERTY_BYTE_LENGTH); + } + + public int getByteOffset() throws JavetException { + return getInteger(PROPERTY_BYTE_OFFSET); + } + + public float getFloat32(int byteOffset) throws JavetException { + return getFloat32(byteOffset, true); + } + + public float getFloat32(int byteOffset, boolean littleEndian) throws JavetException { + return invokeDouble(FUNCTION_GET_FLOAT_32, + new V8ValueInteger(byteOffset), new V8ValueBoolean(littleEndian)).floatValue(); + } + + public double getFloat64(int byteOffset) throws JavetException { + return getFloat64(byteOffset, true); + } + + public double getFloat64(int byteOffset, boolean littleEndian) throws JavetException { + return invokeDouble(FUNCTION_GET_FLOAT_64, + new V8ValueInteger(byteOffset), new V8ValueBoolean(littleEndian)).doubleValue(); + } + + public short getInt16(int byteOffset) throws JavetException { + return getInt16(byteOffset, true); + } + + public short getInt16(int byteOffset, boolean littleEndian) throws JavetException { + return invokeInteger(FUNCTION_GET_INT_16, + new V8ValueInteger(byteOffset), new V8ValueBoolean(littleEndian)).shortValue(); + } + + public int getInt32(int byteOffset) throws JavetException { + return getInt32(byteOffset, true); + } + + public int getInt32(int byteOffset, boolean littleEndian) throws JavetException { + return invokeInteger(FUNCTION_GET_INT_32, + new V8ValueInteger(byteOffset), new V8ValueBoolean(littleEndian)).intValue(); + } + + public byte getInt8(int byteOffset) throws JavetException { + return invokeInteger(FUNCTION_GET_INT_8, + new V8ValueInteger(byteOffset)).byteValue(); + } + + @Override + public int getType() { + return V8ValueReferenceType.DataView; + } + + public void setBigInt64(int byteOffset, long value) throws JavetException { + setBigInt64(byteOffset, value, true); + } + + public void setBigInt64(int byteOffset, long value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_BIG_INT_64, + new V8ValueInteger(byteOffset), new V8ValueLong(value), new V8ValueBoolean(littleEndian)); + } + + public void setFloat32(int byteOffset, float value) throws JavetException { + setFloat32(byteOffset, value, true); + } + + public void setFloat32(int byteOffset, float value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_FLOAT_32, + new V8ValueInteger(byteOffset), new V8ValueDouble(value), new V8ValueBoolean(littleEndian)); + } + + public void setFloat64(int byteOffset, double value) throws JavetException { + setFloat64(byteOffset, value, true); + } + + public void setFloat64(int byteOffset, double value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_FLOAT_64, + new V8ValueInteger(byteOffset), new V8ValueDouble(value), new V8ValueBoolean(littleEndian)); + } + + public void setInt32(int byteOffset, int value) throws JavetException { + setInt32(byteOffset, value, true); + } + + public void setInt32(int byteOffset, int value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_INT_32, + new V8ValueInteger(byteOffset), new V8ValueInteger(value), new V8ValueBoolean(littleEndian)); + } + + public void setInt16(int byteOffset, short value) throws JavetException { + setInt16(byteOffset, value, true); + } + + public void setInt16(int byteOffset, short value, boolean littleEndian) throws JavetException { + invokeVoid(FUNCTION_SET_INT_16, + new V8ValueInteger(byteOffset), new V8ValueInteger(value), new V8ValueBoolean(littleEndian)); + } + + public void setInt8(int byteOffset, byte value) throws JavetException { + invokeVoid(FUNCTION_SET_INT_8, new V8ValueInteger(byteOffset), new V8ValueInteger(value)); + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java index 75035f590..dce0af158 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueError.java @@ -25,7 +25,7 @@ public class V8ValueError extends V8ValueObject { public static final String STACK = "stack"; public static final String MESSAGE = "message"; - public V8ValueError(long handle) { + V8ValueError(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java index 2526d4ee0..a48469b13 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueFunction.java @@ -43,7 +43,7 @@ public class V8ValueFunction extends V8ValueObject implements IV8ValueFunction { */ protected JavetCallbackContext javetCallbackContext; - public V8ValueFunction(long handle) { + V8ValueFunction(long handle) { super(handle); javetCallbackContext = null; } @@ -70,6 +70,12 @@ public T call(IV8ValueObject receiver, boolean returnResult, return v8Runtime.call(this, receiver, returnResult, v8Values); } + @Override + public T callAsConstructor(V8Value... v8Values) throws JavetException { + checkV8Runtime(); + return v8Runtime.callAsConstructor(this, v8Values); + } + @Override public void close(boolean forceClose) throws JavetException { // V8 lock free @@ -194,6 +200,6 @@ public V8Value receiveCallback(V8Value thisObject, V8ValueArray args) throws Thr JavetResourceUtils.safeClose(values); } } - return v8Runtime.decorateV8Value(new V8ValueUndefined()); + return v8Runtime.createV8ValueUndefined(); } } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java index fb4969877..df8b1db1e 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueGlobalObject.java @@ -19,9 +19,21 @@ import com.caoccao.javet.exceptions.JavetException; +/** + * The type V8 value global object is a special object. + * 1. It lives as long as V8 runtime lives. + * 2. It does not have reference count. + * 3. It cannot be set to weak. + * 4. Its clone is itself. + */ @SuppressWarnings("unchecked") public final class V8ValueGlobalObject extends V8ValueObject { - public V8ValueGlobalObject(long handle) { + /** + * Instantiates a new V8 value global object. + * + * @param handle the handle + */ + V8ValueGlobalObject(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.java new file mode 100644 index 000000000..ede622305 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueIterator.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.values.reference; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.V8ValueReferenceType; + +@SuppressWarnings("unchecked") +public class V8ValueIterator extends V8ValueObject implements IV8ValueIterator { + public static final String FUNCTION_NEXT = "next"; + public static final String PROPERTY_DONE = "done"; + public static final String PROPERTY_VALUE = "value"; + + public V8ValueIterator(long handle) { + super(handle); + } + + @Override + public int getType() { + return V8ValueReferenceType.Iterator; + } + + @Override + public T getNext() { + try (V8ValueObject next = invoke(FUNCTION_NEXT)) { + if (!next.getBoolean(PROPERTY_DONE)) { + return next.get(PROPERTY_VALUE); + } + } catch (JavetException javetException) { + } + return null; + } +} diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java index 244093e46..d306dedb0 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueMap.java @@ -18,12 +18,8 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.utils.V8ValueUtils; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.V8ValueReferenceType; -import com.caoccao.javet.values.virtual.V8VirtualList; - -import java.util.List; @SuppressWarnings("unchecked") public class V8ValueMap extends V8ValueObject implements IV8ValueMap { @@ -32,24 +28,20 @@ public class V8ValueMap extends V8ValueObject implements IV8ValueMap { public static final String FUNCTION_VALUES = "values"; public static final String FUNCTION_ENTRIES = "entries"; - public V8ValueMap(long handle) { + V8ValueMap(long handle) { super(handle); } @Override - public V8VirtualList getEntries() throws JavetException { + public IV8ValueIterator getEntries() throws JavetException { checkV8Runtime(); - try (V8ValueObject mapIterator = invoke(FUNCTION_ENTRIES)) { - return V8ValueUtils.convertIteratorToV8ValueList(mapIterator); - } + return invoke(FUNCTION_ENTRIES); } @Override - public V8VirtualList getKeys() throws JavetException { + public IV8ValueIterator getKeys() throws JavetException { checkV8Runtime(); - try (V8ValueObject mapIterator = invoke(FUNCTION_KEYS)) { - return V8ValueUtils.convertIteratorToV8ValueList(mapIterator); - } + return invoke(FUNCTION_KEYS); } @Override @@ -64,16 +56,8 @@ public int getType() { } @Override - public List getValues() throws JavetException { - checkV8Runtime(); - try (V8ValueObject mapIterator = invoke(FUNCTION_VALUES)) { - return V8ValueUtils.convertIteratorToV8ValueList(mapIterator); - } - } - - @Override - public boolean has(V8Value value) throws JavetException { + public IV8ValueIterator getValues() throws JavetException { checkV8Runtime(); - return v8Runtime.has(this, value); + return invoke(FUNCTION_VALUES); } } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java index c1fbe6fa1..fbe65c597 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueObject.java @@ -17,7 +17,7 @@ package com.caoccao.javet.values.reference; -import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.*; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.V8ValueReferenceType; @@ -44,14 +44,14 @@ public T get(V8Value key) throws JavetException { } @Override - public int getType() { - return V8ValueReferenceType.Object; + public int getIdentityHash() throws JavetException { + checkV8Runtime(); + return v8Runtime.getIdentityHash(this); } @Override - public boolean hasOwnProperty(V8Value key) throws JavetException { - checkV8Runtime(); - return v8Runtime.hasOwnProperty(this, key); + public int getType() { + return V8ValueReferenceType.Object; } @Override @@ -73,6 +73,18 @@ public T getProperty(V8Value key) return v8Runtime.getProperty(this, key); } + @Override + public boolean has(V8Value value) throws JavetException { + checkV8Runtime(); + return v8Runtime.has(this, value); + } + + @Override + public boolean hasOwnProperty(V8Value key) throws JavetException { + checkV8Runtime(); + return v8Runtime.hasOwnProperty(this, key); + } + @Override public T invoke(String functionName, boolean returnResult, V8Value... v8Values) throws JavetException { diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java index 99ac751a5..254690364 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValuePromise.java @@ -21,7 +21,7 @@ public class V8ValuePromise extends V8ValueObject { - public V8ValuePromise(long handle) { + V8ValuePromise(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java index e6ff75956..9c0031262 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueProxy.java @@ -21,7 +21,7 @@ public class V8ValueProxy extends V8ValueObject { - public V8ValueProxy(long handle) { + V8ValueProxy(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java index 524a9c3a8..78391f8c9 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueReference.java @@ -20,12 +20,13 @@ import com.caoccao.javet.exceptions.*; import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.values.V8Value; +import com.caoccao.javet.values.primitive.V8ValuePrimitive; public abstract class V8ValueReference extends V8Value implements IV8ValueReference { protected long handle; protected boolean weak; - public V8ValueReference(long handle) { + V8ValueReference(long handle) { super(); this.handle = handle; weak = false; @@ -72,6 +73,21 @@ public void close(boolean forceClose) throws JavetException { } } + @Override + public boolean equals(V8Value v8Value) throws JavetException { + if (v8Value == null || !(v8Value instanceof V8ValueReference)) { + return false; + } + if (v8Value.getClass() != this.getClass()) { + return false; + } + V8ValueReference v8ValueReference = (V8ValueReference) v8Value; + if (getHandle() == v8ValueReference.getHandle()) { + return true; + } + return v8Runtime.equals(this, v8ValueReference); + } + @Override public abstract int getType(); @@ -80,6 +96,9 @@ public long getHandle() { return handle; } + @Override + public abstract int getIdentityHash() throws JavetException; + @Override public boolean isWeak() throws JavetException { // V8 lock free @@ -114,6 +133,36 @@ public void setWeak() throws JavetException { weak = true; } + @Override + public boolean sameValue(V8Value v8Value) throws JavetException { + if (v8Value == null || !(v8Value instanceof V8ValueReference)) { + return false; + } + if (v8Value.getClass() != this.getClass()) { + return false; + } + V8ValueReference v8ValueReference = (V8ValueReference) v8Value; + if (getHandle() == v8ValueReference.getHandle()) { + return true; + } + return v8Runtime.sameValue(this, v8ValueReference); + } + + @Override + public boolean strictEquals(V8Value v8Value) throws JavetException { + if (v8Value == null || !(v8Value instanceof V8ValueReference)) { + return false; + } + if (v8Value.getClass() != this.getClass()) { + return false; + } + V8ValueReference v8ValueReference = (V8ValueReference) v8Value; + if (getHandle() == v8ValueReference.getHandle()) { + return true; + } + return v8Runtime.strictEquals(this, v8ValueReference); + } + @Override public String toProtoString() { try { diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java index 18a910a77..6e6302ecf 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueRegExp.java @@ -21,7 +21,7 @@ public class V8ValueRegExp extends V8ValueObject { - public V8ValueRegExp(long handle) { + V8ValueRegExp(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java index a24e62c69..5f434e6c9 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSet.java @@ -18,17 +18,16 @@ package com.caoccao.javet.values.reference; import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.utils.V8ValueUtils; import com.caoccao.javet.values.V8Value; import com.caoccao.javet.values.V8ValueReferenceType; -import com.caoccao.javet.values.virtual.V8VirtualList; @SuppressWarnings("unchecked") public class V8ValueSet extends V8ValueObject implements IV8ValueSet { + public static final String FUNCTION_ENTRIES = "entries"; public static final String FUNCTION_KEYS = "keys"; - public V8ValueSet(long handle) { + V8ValueSet(long handle) { super(handle); } @@ -38,17 +37,21 @@ public void add(V8Value key) throws JavetException { v8Runtime.add(this, key); } + @Override + public IV8ValueIterator getEntries() throws JavetException { + checkV8Runtime(); + return invoke(FUNCTION_ENTRIES); + } + @Override public int getType() { return V8ValueReferenceType.Set; } @Override - public V8VirtualList getKeys() throws JavetException { + public IV8ValueIterator getKeys() throws JavetException { checkV8Runtime(); - try (V8ValueObject setIterator = invoke(FUNCTION_KEYS)) { - return V8ValueUtils.convertIteratorToV8ValueList(setIterator); - } + return invoke(FUNCTION_KEYS); } @Override @@ -56,10 +59,4 @@ 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 index a46cbcbc0..7c8e32933 100644 --- a/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueSymbol.java @@ -27,7 +27,7 @@ public class V8ValueSymbol extends V8ValueObject { public static final String DESCRIPTION = "description"; public static final String SYMBOL_0 = "Symbol({0})"; - public V8ValueSymbol(long handle) { + V8ValueSymbol(long handle) { super(handle); } diff --git a/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java b/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java new file mode 100644 index 000000000..9609ad931 --- /dev/null +++ b/src/main/java/com/caoccao/javet/values/reference/V8ValueTypedArray.java @@ -0,0 +1,455 @@ +/* + * 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; + +/** + * The type V8 value typed array. + * The typical way of manipulating the typed array is as following. + * 1. Get array buffer and apply try-with-resource. + * 2. Create the value array by length. + * 3. Fill the value array. + * E.g.: + * + * try (V8ValueArrayBuffer v8ValueArrayBuffer = v8ValueTypedArray.getArrayBuffer()) { + * long[] longs = new long[v8ValueArrayBuffer.getLength()]; + * v8ValueArrayBuffer.toLongs(longs); + * // ... + * } + * + * Or, play with ByteBuffer directly. + * 1. Get array buffer and apply try-with-resource. + * 2. Get native ByteBuffer. + * 3. Set order of the native ByteBuffer to native order. + * 4. Get typed buffer by type. + * 5. Read from or write to the typed buffer. + * E.g: + * + * try (V8ValueArrayBuffer v8ValueArrayBuffer = v8ValueTypedArray.getArrayBuffer()) { + * LongBuffer longBuffer = v8ValueArrayBuffer.getByteBuffer().order(ByteOrder.nativeOrder()).asLongBuffer(); + * // ... + * } + * + */ +public class V8ValueTypedArray extends V8ValueObject implements IV8ValueTypedArray { + + /** + * The constant NAME_INT_8_ARRAY. + */ + public static final String NAME_INT_8_ARRAY = "Int8Array"; + /** + * The constant NAME_UINT_8_ARRAY. + */ + public static final String NAME_UINT_8_ARRAY = "Uint8Array"; + /** + * The constant NAME_UINT_8_CLAMPED_ARRAY. + */ + public static final String NAME_UINT_8_CLAMPED_ARRAY = "Uint8ClampedArray"; + /** + * The constant NAME_INT_16_ARRAY. + */ + public static final String NAME_INT_16_ARRAY = "Int16Array"; + /** + * The constant NAME_UINT_16_ARRAY. + */ + public static final String NAME_UINT_16_ARRAY = "Uint16Array"; + /** + * The constant NAME_INT_32_ARRAY. + */ + public static final String NAME_INT_32_ARRAY = "Int32Array"; + /** + * The constant NAME_UINT_32_ARRAY. + */ + public static final String NAME_UINT_32_ARRAY = "Uint32Array"; + /** + * The constant NAME_FLOAT_32_ARRAY. + */ + public static final String NAME_FLOAT_32_ARRAY = "Float32Array"; + /** + * The constant NAME_FLOAT_64_ARRAY. + */ + public static final String NAME_FLOAT_64_ARRAY = "Float64Array"; + /** + * The constant NAME_BIG_INT_64_ARRAY. + */ + public static final String NAME_BIG_INT_64_ARRAY = "BigInt64Array"; + /** + * The constant NAME_BIG_UINT_64_ARRAY. + */ + public static final String NAME_BIG_UINT_64_ARRAY = "BigUint64Array"; + + /** + * The constant PROPERTY_BYTE_LENGTH. + */ + public static final String PROPERTY_BYTE_LENGTH = "byteLength"; + /** + * The constant PROPERTY_BUFFER. + */ + public static final String PROPERTY_BUFFER = "buffer"; + /** + * The constant PROPERTY_BYTE_OFFSET. + */ + public static final String PROPERTY_BYTE_OFFSET = "byteOffset"; + /** + * The constant PROPERTY_NAME. + */ + public static final String PROPERTY_NAME = "Name"; + /** + * The constant ONE_BYTE_PER_VALUE. + */ + public static final int ONE_BYTE_PER_VALUE = 1; + /** + * The constant TWO_BYTES_PER_VALUE. + */ + public static final int TWO_BYTES_PER_VALUE = 2; + /** + * The constant FOUR_BYTES_PER_VALUE. + */ + public static final int FOUR_BYTES_PER_VALUE = 4; + /** + * The constant EIGHT_BYTES_PER_VALUE. + */ + public static final int EIGHT_BYTES_PER_VALUE = 8; + /** + * The constant ZERO_BYTE_PER_VALUE. + */ + public static final int ZERO_BYTE_PER_VALUE = 0; + + /** + * The Size in bytes. + */ + protected int sizeInBytes; + /** + * The Type. + */ + protected int type; + + /** + * Instantiates a new V8 value typed array. + * + * @param handle the handle + * @param type the type + */ + V8ValueTypedArray(long handle, int type) { + super(handle); + setType(type); + } + + /** + * Gets JS constructor name by type. + * + * @param type the type + * @return the name + */ + public static String getName(int type) { + switch (type) { + case V8ValueReferenceType.Int8Array: + return NAME_INT_8_ARRAY; + case V8ValueReferenceType.Uint8Array: + return NAME_UINT_8_ARRAY; + case V8ValueReferenceType.Uint8ClampedArray: + return NAME_UINT_8_CLAMPED_ARRAY; + case V8ValueReferenceType.Int16Array: + return NAME_INT_16_ARRAY; + case V8ValueReferenceType.Uint16Array: + return NAME_UINT_16_ARRAY; + case V8ValueReferenceType.Int32Array: + return NAME_INT_32_ARRAY; + case V8ValueReferenceType.Uint32Array: + return NAME_UINT_32_ARRAY; + case V8ValueReferenceType.Float32Array: + return NAME_FLOAT_32_ARRAY; + case V8ValueReferenceType.Float64Array: + return NAME_FLOAT_64_ARRAY; + case V8ValueReferenceType.BigInt64Array: + return NAME_BIG_INT_64_ARRAY; + case V8ValueReferenceType.BigUint64Array: + return NAME_BIG_UINT_64_ARRAY; + default: + return null; + } + } + + @Override + public V8ValueArrayBuffer getBuffer() throws JavetException { + return get(PROPERTY_BUFFER); + } + + @Override + public int getByteLength() throws JavetException { + return getInteger(PROPERTY_BYTE_LENGTH); + } + + @Override + public int getByteOffset() throws JavetException { + return getInteger(PROPERTY_BYTE_OFFSET); + } + + @Override + public int getLength() throws JavetException { + checkV8Runtime(); + return v8Runtime.getLength(this); + } + + @Override + public int getSizeInBytes() { + return sizeInBytes; + } + + @Override + public int getType() { + return type; + } + + /** + * Sets type. + * + * @param type the type + */ + protected void setType(int type) { + switch (type) { + case V8ValueReferenceType.Int8Array: + case V8ValueReferenceType.Uint8Array: + case V8ValueReferenceType.Uint8ClampedArray: + sizeInBytes = ONE_BYTE_PER_VALUE; + break; + case V8ValueReferenceType.Int16Array: + case V8ValueReferenceType.Uint16Array: + sizeInBytes = TWO_BYTES_PER_VALUE; + break; + case V8ValueReferenceType.Int32Array: + case V8ValueReferenceType.Uint32Array: + case V8ValueReferenceType.Float32Array: + sizeInBytes = FOUR_BYTES_PER_VALUE; + break; + case V8ValueReferenceType.Float64Array: + case V8ValueReferenceType.BigInt64Array: + case V8ValueReferenceType.BigUint64Array: + sizeInBytes = EIGHT_BYTES_PER_VALUE; + break; + default: + type = V8ValueReferenceType.Invalid; + sizeInBytes = ZERO_BYTE_PER_VALUE; + break; + } + this.type = type; + } + + @Override + public boolean isValid() { + return type != V8ValueReferenceType.Invalid; + } + + /** + * From byte array. + * + * @param bytes the byte array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromBytes(byte[] bytes) throws JavetException { + if (getType() == V8ValueReferenceType.Int8Array || + getType() == V8ValueReferenceType.Uint8Array || + getType() == V8ValueReferenceType.Uint8ClampedArray) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromBytes(bytes); + } + } + return false; + } + + /** + * From double array. + * + * @param doubles the double array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromDoubles(double[] doubles) throws JavetException { + if (getType() == V8ValueReferenceType.Float64Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromDoubles(doubles); + } + } + return false; + } + + /** + * From float array. + * + * @param floats the float array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromFloats(float[] floats) throws JavetException { + if (getType() == V8ValueReferenceType.Float32Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromFloats(floats); + } + } + return false; + } + + /** + * From integer array. + * + * @param integers the integer array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromIntegers(int[] integers) throws JavetException { + if (getType() == V8ValueReferenceType.Int32Array || + getType() == V8ValueReferenceType.Uint32Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromIntegers(integers); + } + } + return false; + } + + /** + * From long array. + * + * @param longs the long array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromLongs(long[] longs) throws JavetException { + if (getType() == V8ValueReferenceType.BigInt64Array || + getType() == V8ValueReferenceType.BigUint64Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromLongs(longs); + } + } + return false; + } + + /** + * From short array. + * + * @param shorts the short array + * @return the boolean + * @throws JavetException the javet exception + */ + public boolean fromShorts(short[] shorts) throws JavetException { + if (getType() == V8ValueReferenceType.Int16Array || + getType() == V8ValueReferenceType.Uint16Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.fromShorts(shorts); + } + } + return false; + } + + /** + * To byte array. + * + * @return the byte array + * @throws JavetException the javet exception + */ + public byte[] toBytes() throws JavetException { + if (getType() == V8ValueReferenceType.Int8Array || + getType() == V8ValueReferenceType.Uint8Array || + getType() == V8ValueReferenceType.Uint8ClampedArray) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toBytes(); + } + } + return null; + } + + /** + * To float array. + * + * @return the float array + * @throws JavetException the javet exception + */ + public float[] toFloats() throws JavetException { + if (getType() == V8ValueReferenceType.Float32Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toFloats(); + } + } + return null; + } + + /** + * To double array. + * + * @return the double array + * @throws JavetException the javet exception + */ + public double[] toDoubles() throws JavetException { + if (getType() == V8ValueReferenceType.Float64Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toDoubles(); + } + } + return null; + } + + /** + * To int array. + * + * @return the int array + * @throws JavetException the javet exception + */ + public int[] toIntegers() throws JavetException { + if (getType() == V8ValueReferenceType.Int32Array || + getType() == V8ValueReferenceType.Uint32Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toIntegers(); + } + } + return null; + } + + /** + * To long array. + * + * @return the long array + * @throws JavetException the javet exception + */ + public long[] toLongs() throws JavetException { + if (getType() == V8ValueReferenceType.BigInt64Array || + getType() == V8ValueReferenceType.BigUint64Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toLongs(); + } + } + return null; + } + + /** + * To short array. + * + * @return the short array + * @throws JavetException the javet exception + */ + public short[] toShorts() throws JavetException { + if (getType() == V8ValueReferenceType.Int16Array || + getType() == V8ValueReferenceType.Uint16Array) { + try (V8ValueArrayBuffer v8ValueArrayBuffer = getBuffer()) { + return v8ValueArrayBuffer.toShorts(); + } + } + return null; + } +} diff --git a/src/test/java/com/caoccao/javet/BaseTestJavetPool.java b/src/test/java/com/caoccao/javet/BaseTestJavetPool.java new file mode 100644 index 000000000..e129fc47d --- /dev/null +++ b/src/test/java/com/caoccao/javet/BaseTestJavetPool.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; + +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.interop.engine.IJavetEnginePool; +import com.caoccao.javet.interop.engine.JavetEnginePool; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class BaseTestJavetPool extends BaseTestJavet { + protected IJavetEnginePool javetEnginePool; + + @BeforeEach + public void beforeEach() { + javetEnginePool = new JavetEnginePool(); + javetEnginePool.getConfig().setEngineGuardCheckIntervalMillis(1); + } + + @AfterEach + public void afterEach() throws JavetException { + javetEnginePool.close(); + } + +} diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Native.java b/src/test/java/com/caoccao/javet/interop/TestV8Native.java index 62bf1fbd0..b81258346 100644 --- a/src/test/java/com/caoccao/javet/interop/TestV8Native.java +++ b/src/test/java/com/caoccao/javet/interop/TestV8Native.java @@ -47,7 +47,6 @@ public void testLockAndUnlock() { @Test public void testGetVersion() { String versionString = V8Native.getVersion(); - assertNotNull(versionString); - assertTrue(versionString.startsWith("8.")); + assertEquals(JavetLibLoader.V8_VERSION, versionString); } } diff --git a/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java b/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java index de5a6038f..c35229a20 100644 --- a/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java +++ b/src/test/java/com/caoccao/javet/interop/TestV8Runtime.java @@ -19,12 +19,14 @@ import com.caoccao.javet.BaseTestJavet; import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetTerminatedException; 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 java.util.concurrent.TimeUnit; + import static org.junit.jupiter.api.Assertions.*; public class TestV8Runtime extends BaseTestJavet { @@ -80,9 +82,9 @@ public void testResetContext() throws JavetException { v8Runtime.lock(); assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); v8Runtime.getGlobalObject().set("a", new V8ValueString("1")); - v8Runtime.resetContext(); + v8Runtime.unlock().resetContext().lock(); assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); - assertTrue(v8Runtime.getGlobalObject().get("a") instanceof V8ValueUndefined); + assertTrue(v8Runtime.getGlobalObject().get("a").isUndefined()); v8Runtime.unlock(); } } @@ -98,8 +100,42 @@ public void testResetIsolate() throws JavetException { v8Runtime.resetIsolate(); v8Runtime.lock(); assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); - assertTrue(v8Runtime.getGlobalObject().get("a") instanceof V8ValueUndefined); + assertTrue(v8Runtime.getGlobalObject().get("a").isUndefined()); v8Runtime.unlock(); } } + + @Test + public void testTerminateExecution() throws JavetException { + V8Host v8Host = V8Host.getInstance(); + try (V8Runtime v8Runtime = v8Host.createV8Runtime()) { + // Create a daemon thread monitoring the V8 runtime status. + Thread daemonThread = new Thread(() -> { + // V8 runtime isInUse() does not require lock. + while (!v8Runtime.isInUse()) { + try { + TimeUnit.MILLISECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // V8 runtime terminateExecution() does not require lock. + v8Runtime.terminateExecution(); + }); + daemonThread.start(); + v8Runtime.lock(); + try { + v8Runtime.getExecutor( + "var count = 0; while (true) { ++count; }") + .executeVoid(); + fail("Failed to throw exception when execution is terminated."); + } catch (JavetTerminatedException e) { + assertFalse(e.isContinuable()); + } + final int count = v8Runtime.getGlobalObject().getInteger("count"); + assertTrue(count > 0, "Count should be greater than 0."); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger(), + "V8 runtime should still be able to execute script after being terminated."); + } + } } diff --git a/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.java new file mode 100644 index 000000000..d6a99ca8f --- /dev/null +++ b/src/test/java/com/caoccao/javet/interop/engine/TestJavetEngineGuard.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.interop.engine; + +import com.caoccao.javet.BaseTestJavetPool; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.exceptions.JavetTerminatedException; +import com.caoccao.javet.interop.V8Runtime; +import com.caoccao.javet.utils.JavetDateTimeUtils; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestJavetEngineGuard extends BaseTestJavetPool { + @Test + public void testWithoutTermination() throws JavetException { + final long timeoutMillis = 10000; + ZonedDateTime startZonedDateTime = JavetDateTimeUtils.getUTCNow(); + try (IJavetEngine iJavetEngine = javetEnginePool.getEngine()) { + try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(timeoutMillis)) { + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger()); + } + } + ZonedDateTime endZonedDateTime = JavetDateTimeUtils.getUTCNow(); + Duration duration = Duration.between(startZonedDateTime, endZonedDateTime); + assertTrue(duration.toMillis() < timeoutMillis); + } + + @Test + public void testTermination() throws JavetException { + // Get an engine from the pool as usual. + try (IJavetEngine iJavetEngine = javetEnginePool.getEngine()) { + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + // Get a guard from the engine and apply try-with-resource pattern. + try (IJavetEngineGuard iJavetEngineGuard = iJavetEngine.getGuard(1)) { + v8Runtime.getExecutor("while (true) {}").executeVoid(); + // That infinite loop will be terminated in 10 seconds by the guard. + } catch (JavetTerminatedException e) { + // JavetTerminatedException will be thrown to mark that. + assertFalse(e.isContinuable()); + } + assertEquals(2, v8Runtime.getExecutor("1 + 1").executeInteger(), + "The V8 runtime is not dead and still be able to execute code afterwards."); + } + } +} diff --git a/src/test/java/com/caoccao/javet/interop/TestPerformance.java b/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java similarity index 86% rename from src/test/java/com/caoccao/javet/interop/TestPerformance.java rename to src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java index 497d178a4..d54642e96 100644 --- a/src/test/java/com/caoccao/javet/interop/TestPerformance.java +++ b/src/test/java/com/caoccao/javet/interop/engine/TestPerformance.java @@ -15,17 +15,12 @@ * limitations under the License. */ -package com.caoccao.javet.interop; +package com.caoccao.javet.interop.engine; -import com.caoccao.javet.BaseTestJavet; -import com.caoccao.javet.exceptions.JavetException; -import com.caoccao.javet.interop.engine.IJavetEngine; -import com.caoccao.javet.interop.engine.IJavetEnginePool; -import com.caoccao.javet.interop.engine.JavetEnginePool; +import com.caoccao.javet.BaseTestJavetPool; +import com.caoccao.javet.interop.V8Runtime; import com.caoccao.javet.interop.executors.IV8Executor; import com.caoccao.javet.utils.JavetOSUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -40,19 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class TestPerformance extends BaseTestJavet { - protected IJavetEnginePool javetEnginePool; - - @BeforeEach - public void beforeEach() { - javetEnginePool = new JavetEnginePool(); - } - - @AfterEach - public void afterEach() throws JavetException { - javetEnginePool.close(); - } - +public class TestPerformance extends BaseTestJavetPool { @Test @Tag("performance") public void testAdHocContextAnd1Thread() throws Exception { @@ -121,7 +104,7 @@ public void testAdHocContextAnd8Threads() throws Exception { thread.join(); } final long stopTime = System.currentTimeMillis(); - final long tps = iterations * (long) threadCount * 1000 / (stopTime-startTime); + final long tps = iterations * (long) threadCount * 1000 / (stopTime - startTime); logger.logInfo("Ad-hoc Context with 8 Threads: {0}", tps); updateDoc("Ad-hoc Context with 8 Threads", tps); } @@ -154,7 +137,7 @@ public void testSingleContextAnd8Threads() throws Exception { thread.join(); } final long stopTime = System.currentTimeMillis(); - final long tps = iterations * (long) threadCount * 1000 / (stopTime-startTime); + final long tps = iterations * (long) threadCount * 1000 / (stopTime - startTime); logger.logInfo("Single Context with 8 Threads: {0}", tps); updateDoc("Single Context with 8 Threads", tps); } @@ -163,7 +146,7 @@ protected void updateDoc(String prefix, long tps) throws IOException { DecimalFormat decimalFormat = new DecimalFormat("#,###"); File docFile = new File( JavetOSUtils.WORKING_DIRECTORY, - "docs/performance.rst"); + "docs/development/performance.rst"); List lines = new ArrayList<>(); for (String line : Files.readAllLines(docFile.toPath(), StandardCharsets.UTF_8)) { if (line.startsWith(prefix)) { diff --git a/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java b/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java index 22798adeb..ab21c73d9 100644 --- a/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java +++ b/src/test/java/com/caoccao/javet/tutorial/DecimalJavet.java @@ -20,20 +20,25 @@ 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.interop.engine.IJavetEngine; +import com.caoccao.javet.interop.engine.IJavetEnginePool; +import com.caoccao.javet.interop.engine.JavetEnginePool; import com.caoccao.javet.utils.JavetOSUtils; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.reference.V8ValueFunction; +import com.caoccao.javet.values.reference.V8ValueObject; import java.io.File; +import java.math.BigDecimal; public class DecimalJavet implements IJavetClosable { - private IJavetLogger logger; - private V8Runtime v8Runtime; + private IJavetEnginePool iJavetEnginePool; + private IJavetEngine iJavetEngine; public DecimalJavet() { - logger = new JavetDefaultLogger(getClass().getName()); - v8Runtime = null; + iJavetEnginePool = new JavetEnginePool(); + iJavetEngine = iJavetEnginePool.getEngine(); } public static void main(String[] args) throws JavetException { @@ -53,31 +58,49 @@ public void loadJS() throws JavetException { 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(); + getLogger().logInfo("Loading {0}.", decimalJSFile.getAbsolutePath()); + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); 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."); + getLogger().logError("{0} is not found.", decimalJSFile.getAbsolutePath()); + getLogger().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( + V8Runtime v8Runtime = iJavetEngine.getV8Runtime(); + getLogger().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()); + try (V8ValueFunction v8ValueFunctionDecimal = v8Runtime.getGlobalObject().get("Decimal")) { + try (V8ValueObject v8ValueObjectDecimal = v8ValueFunctionDecimal.call( + null, new V8ValueString("123.45"))) { + getLogger().logInfo(v8ValueObjectDecimal.toString()); + if (v8ValueObjectDecimal.has("constructor")) { + try (V8ValueFunction v8ValueFunction = v8ValueObjectDecimal.get("constructor")) { + String name = v8ValueFunction.getString("name"); + if ("Decimal".equals(name)) { + BigDecimal bigDecimal = new BigDecimal(v8ValueObjectDecimal.toString()); + getLogger().logInfo("BigDecimal: {0}", bigDecimal.toString()); + } + } + } + } + } } public IJavetLogger getLogger() { - return logger; + return iJavetEnginePool.getConfig().getJavetLogger(); } @Override public void close() throws JavetException { - if (v8Runtime != null) { - v8Runtime.close(); + if (iJavetEngine != null) { + iJavetEngine.close(); + } + if (iJavetEnginePool != null) { + iJavetEnginePool.close(); } } } diff --git a/src/test/java/com/caoccao/javet/utils/converters/TestJavetObjectConverter.java b/src/test/java/com/caoccao/javet/utils/converters/TestJavetObjectConverter.java new file mode 100644 index 000000000..ee37406d5 --- /dev/null +++ b/src/test/java/com/caoccao/javet/utils/converters/TestJavetObjectConverter.java @@ -0,0 +1,247 @@ +/* + * 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.converters; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.entities.JavetEntityMap; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetDateTimeUtils; +import com.caoccao.javet.values.V8ValueReferenceType; +import com.caoccao.javet.values.primitive.V8ValueInteger; +import com.caoccao.javet.values.primitive.V8ValueString; +import com.caoccao.javet.values.reference.*; +import org.junit.jupiter.api.Test; + +import java.time.ZonedDateTime; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings("unchecked") +public class TestJavetObjectConverter extends BaseTestJavetRuntime { + protected JavetObjectConverter converter; + + public TestJavetObjectConverter() { + converter = new JavetObjectConverter(); + } + + @Test + public void testArray() throws JavetException { + try (V8ValueArray v8ValueArray = v8Runtime.createV8ValueArray()) { + v8ValueArray.push(new V8ValueString("abc")); + v8ValueArray.push(new V8ValueInteger(123)); + List list = (List) converter.toObject(v8ValueArray); + assertEquals(2, list.size()); + assertEquals("abc", list.get(0)); + assertEquals(123, list.get(1)); + } + // ArrayList + try (V8ValueArray v8ValueArray = converter.toV8Value( + v8Runtime, Arrays.asList("abc", 123))) { + assertEquals(2, v8ValueArray.getLength()); + assertEquals("abc", v8ValueArray.getString(0)); + assertEquals(123, v8ValueArray.getInteger(1)); + } + // boolean[] + try (V8ValueArray v8ValueArray = converter.toV8Value( + v8Runtime, new boolean[]{true, false})) { + assertEquals(2, v8ValueArray.getLength()); + assertEquals(true, v8ValueArray.getBoolean(0)); + assertEquals(false, v8ValueArray.getBoolean(1)); + } + // String[] + try (V8ValueArray v8ValueArray = converter.toV8Value( + v8Runtime, new String[]{"abc", "def"})) { + assertEquals(2, v8ValueArray.getLength()); + assertEquals("abc", v8ValueArray.getString(0)); + assertEquals("def", v8ValueArray.getString(1)); + } + // ZonedDateTime[] + try (V8ValueArray v8ValueArray = converter.toV8Value( + v8Runtime, new ZonedDateTime[]{ + JavetDateTimeUtils.toZonedDateTime(123L), + JavetDateTimeUtils.toZonedDateTime(456L)})) { + assertEquals(2, v8ValueArray.getLength()); + assertEquals(123L, v8ValueArray.getZonedDateTime(0).toInstant().toEpochMilli()); + assertEquals(456L, v8ValueArray.getZonedDateTime(1).toInstant().toEpochMilli()); + } + // Object[] + try (V8ValueArray v8ValueArray = converter.toV8Value( + v8Runtime, new Object[]{1, "abc"})) { + assertEquals(2, v8ValueArray.getLength()); + assertEquals(1, v8ValueArray.getInteger(0)); + assertEquals("abc", v8ValueArray.getString(1)); + } + } + + @Test + public void testMap() throws JavetException { + try (V8ValueMap v8ValueMap = v8Runtime.createV8ValueMap()) { + v8ValueMap.set("x", new V8ValueString("abc")); + assertEquals("abc", v8ValueMap.getString("x")); + JavetEntityMap map = (JavetEntityMap) converter.toObject(v8ValueMap); + assertEquals(1, map.size()); + assertEquals("abc", map.get("x")); + } + try (V8ValueMap v8ValueMap = converter.toV8Value( + v8Runtime, new JavetEntityMap() {{ + put("x", "abc"); + }})) { + assertEquals(1, v8ValueMap.getSize()); + assertEquals("abc", v8ValueMap.getString("x")); + } + } + + @Test + public void testObject() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject()) { + v8ValueObject.set("x", new V8ValueString("abc")); + assertEquals("abc", v8ValueObject.getString("x")); + Map map = (Map) converter.toObject(v8ValueObject); + assertTrue(map instanceof HashMap); + assertEquals(1, map.size()); + assertEquals("abc", map.get("x")); + } + try (V8ValueObject v8ValueObject = converter.toV8Value( + v8Runtime, new HashMap() {{ + put("x", "abc"); + }})) { + assertEquals("abc", v8ValueObject.getString("x")); + } + } + + @Test + public void testSet() throws JavetException { + try (V8ValueSet v8ValueSet = v8Runtime.createV8ValueSet()) { + v8ValueSet.add(new V8ValueString("abc")); + assertTrue(v8ValueSet.has("abc")); + Set set = (Set) converter.toObject(v8ValueSet); + assertEquals(1, set.size()); + assertTrue(set.contains("abc")); + } + try (V8ValueSet v8ValueSet = converter.toV8Value( + v8Runtime, new HashSet(Arrays.asList("a", "b", "c")))) { + assertEquals(3, v8ValueSet.getSize()); + assertTrue(v8ValueSet.has("a")); + assertTrue(v8ValueSet.has("b")); + assertTrue(v8ValueSet.has("c")); + } + } + + @Test + public void testTypedArrayByteArray() throws JavetException { + byte[] bytes = new byte[]{(byte) 0x01, (byte) 0x02, (byte) 0x03}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, bytes)) { + assertEquals(bytes.length, v8ValueTypedArray.getLength()); + assertEquals(bytes.length, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(bytes, v8ValueTypedArray.toBytes()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.Int8Array, bytes.length)) { + assertTrue(v8ValueTypedArray.fromBytes(bytes)); + byte[] newBytes = (byte[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(bytes, newBytes); + } + } + + @Test + public void testTypedArrayDoubleArray() throws JavetException { + double[] doubles = new double[]{1.23D, 2.34D, 3.45D}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, doubles)) { + assertEquals(doubles.length, v8ValueTypedArray.getLength()); + assertEquals(doubles.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(doubles, v8ValueTypedArray.toDoubles()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.Float64Array, doubles.length)) { + assertTrue(v8ValueTypedArray.fromDoubles(doubles)); + double[] newDoubles = (double[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(doubles, newDoubles, 0.001D); + } + } + + @Test + public void testTypedArrayFloatArray() throws JavetException { + float[] floats = new float[]{1.23F, 2.34F, 3.45F}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, floats)) { + assertEquals(floats.length, v8ValueTypedArray.getLength()); + assertEquals(floats.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(floats, v8ValueTypedArray.toFloats()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.Float32Array, floats.length)) { + assertTrue(v8ValueTypedArray.fromFloats(floats)); + float[] newFloats = (float[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(floats, newFloats, 0.001F); + } + } + + @Test + public void testTypedArrayLongArray() throws JavetException { + long[] longs = new long[]{1L, 2L, 3L}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, longs)) { + assertEquals(longs.length, v8ValueTypedArray.getLength()); + assertEquals(longs.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(longs, v8ValueTypedArray.toLongs()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.BigInt64Array, longs.length)) { + assertTrue(v8ValueTypedArray.fromLongs(longs)); + long[] newLongs = (long[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(longs, newLongs); + } + } + + @Test + public void testTypedArrayIntegerArray() throws JavetException { + int[] integers = new int[]{1, 2, 3}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, integers)) { + assertEquals(integers.length, v8ValueTypedArray.getLength()); + assertEquals(integers.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(integers, v8ValueTypedArray.toIntegers()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.Int32Array, integers.length)) { + assertTrue(v8ValueTypedArray.fromIntegers(integers)); + int[] newIntegers = (int[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(integers, newIntegers); + } + } + + @Test + public void testTypedArrayShortArray() throws JavetException { + short[] shorts = new short[]{(short) 0x01, (short) 0x02, (short) 0x03}; + try (V8ValueTypedArray v8ValueTypedArray = converter.toV8Value(v8Runtime, shorts)) { + assertEquals(shorts.length, v8ValueTypedArray.getLength()); + assertEquals(shorts.length * v8ValueTypedArray.getSizeInBytes(), v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertArrayEquals(shorts, v8ValueTypedArray.toShorts()); + } + try (V8ValueTypedArray v8ValueTypedArray = + v8Runtime.createV8ValueTypedArray(V8ValueReferenceType.Int16Array, shorts.length)) { + assertTrue(v8ValueTypedArray.fromShorts(shorts)); + short[] newShorts = (short[]) converter.toObject(v8ValueTypedArray); + assertArrayEquals(shorts, newShorts); + } + } +} diff --git a/src/test/java/com/caoccao/javet/utils/converters/TestJavetPrimitiveConverter.java b/src/test/java/com/caoccao/javet/utils/converters/TestJavetPrimitiveConverter.java new file mode 100644 index 000000000..0dd227c88 --- /dev/null +++ b/src/test/java/com/caoccao/javet/utils/converters/TestJavetPrimitiveConverter.java @@ -0,0 +1,86 @@ +/* + * 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.converters; + +import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetDateTimeUtils; +import com.caoccao.javet.values.primitive.*; +import org.junit.jupiter.api.Test; + +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestJavetPrimitiveConverter extends BaseTestJavetRuntime { + protected JavetPrimitiveConverter converter; + + public TestJavetPrimitiveConverter() { + converter = new JavetPrimitiveConverter(); + } + + @Test + public void testBoolean() throws JavetException { + assertTrue((boolean) converter.toObject(new V8ValueBoolean(true))); + assertTrue(((V8ValueBoolean) converter.toV8Value(v8Runtime, true)).getValue()); + assertFalse((boolean) converter.toObject(new V8ValueBoolean(false))); + assertFalse(((V8ValueBoolean) converter.toV8Value(v8Runtime, false)).getValue()); + } + + @Test + public void testDouble() throws JavetException { + assertEquals(1.23D, (double) converter.toObject(new V8ValueDouble(1.23D)), 0.001); + assertEquals(1.23D, ((V8ValueDouble) converter.toV8Value(v8Runtime, Double.valueOf(1.23D))).getValue(), 0.001); + } + + @Test + public void testFloat() throws JavetException { + assertEquals(1.23F, ((Double) converter.toObject(new V8ValueDouble(1.23F))).floatValue(), 0.001); + assertEquals(1.23F, ((V8ValueDouble) converter.toV8Value(v8Runtime, Float.valueOf(1.23F))).getValue(), 0.001); + } + + @Test + public void testLong() throws JavetException { + assertEquals(123L, (long) converter.toObject(new V8ValueLong(123L))); + assertEquals(123L, ((V8ValueLong) converter.toV8Value(v8Runtime, Long.valueOf(123L))).getValue()); + } + + @Test + public void testNull() throws JavetException { + assertNull(converter.toObject(null)); + assertNull(converter.toObject(v8Runtime.createV8ValueNull())); + assertTrue(converter.toV8Value(v8Runtime, null).isNull()); + } + + @Test + public void testString() throws JavetException { + assertEquals("abc", (String) converter.toObject(new V8ValueString("abc"))); + assertEquals("abc", ((V8ValueString) converter.toV8Value(v8Runtime, "abc")).getValue()); + } + + @Test + public void testUndefined() throws JavetException { + assertNull(converter.toObject(v8Runtime.createV8ValueUndefined())); + } + + @Test + public void testZonedDateTime() throws JavetException { + assertEquals(123L, ((ZonedDateTime) converter.toObject(new V8ValueZonedDateTime(123L))).toInstant().toEpochMilli()); + assertEquals(123L, ((V8ValueZonedDateTime) converter.toV8Value(v8Runtime, JavetDateTimeUtils.toZonedDateTime(123L))).getValue().toInstant().toEpochMilli()); + } +} diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java index fc22da7c3..d31c10e2f 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueBoolean.java @@ -49,4 +49,13 @@ public void testBooleanObject() throws JavetException { assertTrue(v8Runtime.getExecutor("Boolean(true)").executeBoolean()); assertFalse(v8Runtime.getExecutor("Boolean(false)").executeBoolean()); } + + @Test + public void testEquals() throws JavetException { + V8ValueBoolean v8ValueBoolean = v8Runtime.getExecutor("true").execute(); + assertTrue(v8ValueBoolean.equals(new V8ValueBoolean(true))); + assertFalse(v8ValueBoolean.equals(null)); + assertFalse(v8ValueBoolean.equals(new V8ValueBoolean(false))); + assertFalse(v8ValueBoolean.equals(v8Runtime.createV8ValueUndefined())); + } } diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java index 525e732f1..3d9fdd9f2 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueDouble.java @@ -21,12 +21,34 @@ 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; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueDouble extends BaseTestJavetRuntime { public static final double DELTA = 0.001; + @Test + public void testEquals() throws JavetException { + V8ValueDouble v8ValueDouble = v8Runtime.getExecutor("1.23").execute(); + assertTrue(v8ValueDouble.equals(new V8ValueDouble(1.23D))); + assertFalse(v8ValueDouble.equals(null)); + assertFalse(v8ValueDouble.equals(new V8ValueDouble(1.24D))); + assertFalse(v8ValueDouble.equals(v8Runtime.createV8ValueUndefined())); + } + + @Test + public void testNaNAndInfiniteAndFinite() throws JavetException { + V8ValueDouble v8ValueDouble = v8Runtime.getExecutor("NaN").execute(); + assertNotNull(v8ValueDouble); + assertEquals(v8Runtime, v8ValueDouble.getV8Runtime()); + assertEquals(Double.NaN, v8ValueDouble.getValue()); + assertTrue(v8ValueDouble.isNaN()); + v8ValueDouble = v8Runtime.getExecutor("1/0").execute(); + assertTrue(v8ValueDouble.isInfinite()); + v8ValueDouble = v8Runtime.getExecutor("1/2").execute(); + assertTrue(v8ValueDouble.isFinite()); + } + @Test public void testNumber() throws JavetException { try (V8ValueDouble v8ValueDouble = v8Runtime.getExecutor("1.23").execute()) { diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java index 801c9df82..a2e1ffe7a 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueInteger.java @@ -21,10 +21,19 @@ 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; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueInteger extends BaseTestJavetRuntime { + @Test + public void testEquals() throws JavetException { + V8ValueInteger v8ValueInteger = v8Runtime.getExecutor("1").execute(); + assertTrue(v8ValueInteger.equals(new V8ValueInteger(1))); + assertFalse(v8ValueInteger.equals(null)); + assertFalse(v8ValueInteger.equals(new V8ValueInteger(2))); + assertFalse(v8ValueInteger.equals(new V8ValueLong(1))); + } + @Test public void testInt32() throws JavetException { try (V8ValueInteger v8ValueInteger = v8Runtime.getExecutor("1 + 1").execute()) { diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java index eb1e522c0..369f8a71a 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueLong.java @@ -17,12 +17,12 @@ package com.caoccao.javet.values.primitive; -import com.caoccao.javet.exceptions.JavetException; import com.caoccao.javet.BaseTestJavetRuntime; +import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.values.reference.V8ValueObject; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; public class TestV8ValueLong extends BaseTestJavetRuntime { @Test @@ -34,6 +34,14 @@ public void testBigInt() throws JavetException { assertEquals(-2L, v8Runtime.getExecutor("-2n").executeLong()); assertEquals(v8Runtime, v8ValueLong.getV8Runtime()); } + try (V8ValueObject v8ValueObject = v8Runtime.createV8ValueObject()) { + v8ValueObject.set("a", new V8ValueLong(123L)); + assertEquals(123L, v8ValueObject.getLong("a")); + v8Runtime.getGlobalObject().set("x", v8ValueObject); + } + try (V8ValueObject v8ValueObject = v8Runtime.getGlobalObject().get("x")) { + assertEquals(123L, v8ValueObject.getLong("a")); + } } @Test @@ -41,6 +49,15 @@ public void testBigIntObject() throws JavetException { assertEquals(123L, v8Runtime.getExecutor("BigInt(123n)").executeLong()); } + @Test + public void testEquals() throws JavetException { + V8ValueLong v8ValueLong = v8Runtime.getExecutor("1n").execute(); + assertTrue(v8ValueLong.equals(new V8ValueLong(1L))); + assertFalse(v8ValueLong.equals(null)); + assertFalse(v8ValueLong.equals(new V8ValueLong(2L))); + assertFalse(v8ValueLong.equals(new V8ValueInteger(1))); + } + @Test public void testString() throws JavetException { assertEquals("4611686018427387904", v8Runtime.getExecutor("(2n ** 62n).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 index 1191dc479..d09b16e56 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueNull.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueNull.java @@ -19,13 +19,19 @@ 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; +import static org.junit.jupiter.api.Assertions.*; public class TestV8ValueNull extends BaseTestJavetRuntime { + @Test + public void testEquals() throws JavetException { + V8ValueNull v8ValueNull = v8Runtime.getExecutor("null").execute(); + assertTrue(v8ValueNull.equals(v8Runtime.createV8ValueNull())); + assertFalse(v8ValueNull.equals(null)); + assertFalse(v8ValueNull.equals(v8Runtime.createV8ValueUndefined())); + } + @Test public void testNull() throws JavetException { V8ValueNull v8ValueNull = v8Runtime.getExecutor("null").execute(); diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java index e210d004d..fca466acb 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueString.java @@ -21,10 +21,19 @@ 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; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; public class TestV8ValueString extends BaseTestJavetRuntime { + @Test + public void testEquals() throws JavetException { + V8ValueString v8ValueString = v8Runtime.getExecutor("'abc'").execute(); + assertTrue(v8ValueString.equals(new V8ValueString("abc"))); + assertFalse(v8ValueString.equals(null)); + assertFalse(v8ValueString.equals(new V8ValueString("def"))); + assertFalse(v8ValueString.equals(new V8ValueLong(1))); + } + @Test public void testString() throws JavetException { try (V8ValueString v8ValueString = v8Runtime.getExecutor("'abc' + 'def'").execute()) { diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java index fb444f1c7..1c56609f4 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueUndefined.java @@ -19,13 +19,19 @@ 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; +import static org.junit.jupiter.api.Assertions.*; public class TestV8ValueUndefined extends BaseTestJavetRuntime { + @Test + public void testEquals() throws JavetException { + V8ValueUndefined v8ValueUndefined = v8Runtime.getExecutor("undefined").execute(); + assertTrue(v8ValueUndefined.equals(v8Runtime.createV8ValueUndefined())); + assertFalse(v8ValueUndefined.equals(null)); + assertFalse(v8ValueUndefined.equals(v8Runtime.createV8ValueNull())); + } + @Test public void testUndefined() throws JavetException { try (V8ValueUndefined v8ValueUndefined = v8Runtime.getExecutor("undefined").execute()) { diff --git a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java index c63f858ea..b3fabe775 100644 --- a/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java +++ b/src/test/java/com/caoccao/javet/values/primitive/TestV8ValueZonedDateTime.java @@ -19,6 +19,7 @@ import com.caoccao.javet.BaseTestJavetRuntime; import com.caoccao.javet.exceptions.JavetException; +import com.caoccao.javet.utils.JavetDateTimeUtils; import org.junit.jupiter.api.Test; import java.time.ZoneId; @@ -30,6 +31,17 @@ public class TestV8ValueZonedDateTime extends BaseTestJavetRuntime { public static final int DELTA = 2000; + @Test + public void testEquals() throws JavetException { + V8ValueZonedDateTime v8ValueZonedDateTime = v8Runtime.getExecutor("new Date(123)").execute(); + assertTrue(v8ValueZonedDateTime.equals( + new V8ValueZonedDateTime(JavetDateTimeUtils.toZonedDateTime(123L)))); + assertFalse(v8ValueZonedDateTime.equals(null)); + assertFalse(v8ValueZonedDateTime.equals( + new V8ValueZonedDateTime(JavetDateTimeUtils.toZonedDateTime(234L)))); + assertFalse(v8ValueZonedDateTime.equals(new V8ValueLong(1))); + } + @Test public void testZonedDateTime() throws JavetException { ZonedDateTime now = ZonedDateTime.now(); diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java index beb854ae7..9a1e97c5b 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArray.java @@ -20,10 +20,10 @@ 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 java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -46,12 +46,11 @@ public void testGetAndSet() throws JavetException { 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)); - } + List keys = v8ValueArray.getKeys(); + assertEquals(3, keys.size()); + assertEquals(0, keys.get(0)); + assertEquals(1, keys.get(1)); + assertEquals(2, keys.get(2)); } } @@ -72,8 +71,8 @@ public void testGet() throws JavetException { 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); + assertTrue(v8ValueArray.get(-1).isUndefined()); + assertTrue(v8ValueArray.get(100).isUndefined()); assertEquals(1, v8Runtime.getReferenceCount()); try (V8ValueArray childV8ValueArray = v8ValueArray.get(5)) { assertNotNull(childV8ValueArray); @@ -81,7 +80,7 @@ public void testGet() throws JavetException { assertEquals(4, childV8ValueArray.getLength()); assertEquals(4, childV8ValueArray.getInteger(0)); assertEquals(5, childV8ValueArray.getInteger(1)); - assertTrue(childV8ValueArray.get(2) instanceof V8ValueNull); + assertTrue(childV8ValueArray.get(2).isNull()); assertEquals( "2021-01-27T01:17:03.719Z[UTC]", childV8ValueArray.getZonedDateTime(3).withZoneSameInstant(ZoneId.of("UTC")).toString()); diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArrayBuffer.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArrayBuffer.java new file mode 100644 index 000000000..203db2bd3 --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueArrayBuffer.java @@ -0,0 +1,63 @@ +/* + * 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; + +public class TestV8ValueArrayBuffer extends BaseTestJavetRuntime { + + @Test + public void testFromV8() throws JavetException { + final int byteLength = 16; + try (V8ValueArrayBuffer v8ValueArrayBuffer = + v8Runtime.getExecutor("const a = new ArrayBuffer(" + byteLength + "); a;").execute()) { + assertEquals("{}", v8ValueArrayBuffer.toJsonString()); + assertEquals("[object ArrayBuffer]", v8ValueArrayBuffer.toString()); + assertEquals(byteLength, v8ValueArrayBuffer.getByteLength()); + for (int i = 0; i < byteLength; i++) { + v8ValueArrayBuffer.getByteBuffer().put(i, (byte) i); + } + v8Runtime.getExecutor("const b = new Int8Array(a);").executeVoid(); + for (int i = 0; i < byteLength; i++) { + assertEquals(i, v8Runtime.getExecutor("b[" + i + "];").executeInteger()); + } + } + } + + @Test + public void testToV8() throws JavetException { + final int byteLength = 16; + try (V8ValueArrayBuffer v8ValueArrayBuffer = v8Runtime.createV8ValueArrayBuffer(byteLength)) { + assertEquals("{}", v8ValueArrayBuffer.toJsonString()); + assertEquals("[object ArrayBuffer]", v8ValueArrayBuffer.toString()); + assertEquals(byteLength, v8ValueArrayBuffer.getByteLength()); + for (int i = 0; i < byteLength; i++) { + v8ValueArrayBuffer.getByteBuffer().put(i, (byte) i); + } + v8Runtime.getGlobalObject().set("a", v8ValueArrayBuffer); + v8Runtime.getExecutor("const b = new Int8Array(a);").executeVoid(); + for (int i = 0; i < byteLength; i++) { + assertEquals(i, v8Runtime.getExecutor("b[" + i + "];").executeInteger()); + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueDataView.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueDataView.java new file mode 100644 index 000000000..a9dffea3b --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueDataView.java @@ -0,0 +1,70 @@ +/* + * 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; + +public class TestV8ValueDataView extends BaseTestJavetRuntime { + + @Test + public void testFromV8() throws JavetException { + final int byteLength = 16; + try (V8ValueArrayBuffer v8ValueArrayBuffer = v8Runtime.getExecutor( + "const a = new ArrayBuffer(" + byteLength + "); a;").execute()) { + assertEquals("{}", v8ValueArrayBuffer.toJsonString()); + assertEquals("[object ArrayBuffer]", v8ValueArrayBuffer.toString()); + assertEquals(byteLength, v8ValueArrayBuffer.getByteLength()); + for (int i = 0; i < byteLength; i++) { + v8ValueArrayBuffer.getByteBuffer().put(i, (byte) i); + } + } + try (V8ValueDataView v8ValueDataView = v8Runtime.getExecutor( + "const b = new DataView(a); b;").execute()) { + assertEquals("{}", v8ValueDataView.toJsonString()); + assertEquals("[object DataView]", v8ValueDataView.toString()); + assertEquals(byteLength, v8ValueDataView.getByteLength()); + for (int i = 0; i < byteLength; i++) { + assertEquals(i, v8ValueDataView.getInt8(i)); + } + } + } + + @Test + public void testToV8() throws JavetException { + final int byteLength = 16; + try (V8ValueArrayBuffer v8ValueArrayBuffer = v8Runtime.createV8ValueArrayBuffer(byteLength)) { + try (V8ValueDataView v8ValueDataView = v8Runtime.createV8ValueDataView(v8ValueArrayBuffer)) { + assertEquals("{}", v8ValueDataView.toJsonString()); + assertEquals("[object DataView]", v8ValueDataView.toString()); + assertEquals(byteLength, v8ValueDataView.getByteLength()); + for (int i = 0; i < byteLength; i++) { + v8ValueDataView.setInt8(i, (byte) i); + } + } + byte[] bytes = new byte[byteLength]; + v8ValueArrayBuffer.getByteBuffer().get(bytes); + for (int i = 0; i < byteLength; i++) { + assertEquals(i, bytes[i]); + } + } + } +} diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java index 3c8c23926..5a3246449 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueFunction.java @@ -26,7 +26,6 @@ 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; @@ -34,6 +33,25 @@ import static org.junit.jupiter.api.Assertions.*; public class TestV8ValueFunction extends BaseTestJavetRuntime { + @Test + public void testAnonymousFunction() throws JavetException { + String codeString = "() => '123測試'"; + try (V8Value v8Value = v8Runtime.getExecutor(codeString).execute()) { + assertNotNull(v8Value); + assertTrue(v8Value instanceof V8ValueFunction); + V8ValueFunction v8ValueFunction = (V8ValueFunction) v8Value; + assertEquals(codeString, v8ValueFunction.toString()); + assertEquals("123測試", v8ValueFunction.callString(null)); + } + v8Runtime.getGlobalObject().setFunction("a", codeString); + assertEquals("123測試", v8Runtime.getExecutor("a();").executeString()); + v8Runtime.getGlobalObject().setFunction("b", "(x) => x + 1;"); + assertEquals(2, v8Runtime.getExecutor("b(1);").executeInteger()); + v8Runtime.getGlobalObject().delete("a"); + v8Runtime.getGlobalObject().delete("b"); + v8Runtime.requestGarbageCollectionForTesting(true); + } + @Test public void testArrayPush() throws JavetException { try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = []; a;").execute()) { @@ -204,7 +222,7 @@ public void testCallbackEchoVIVOWithoutThis() throws JavetException, NoSuchMetho globalObject.delete("echo"); } assertTrue(mockCallbackReceiver.isCalled()); - assertTrue(globalObject.get("a") instanceof V8ValueUndefined); + assertTrue(globalObject.get("a").isUndefined()); } @Test diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java index 31d8f190e..ed7096a4a 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueMap.java @@ -19,16 +19,13 @@ 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.*; +@SuppressWarnings("unchecked") public class TestV8ValueMap extends BaseTestJavetRuntime { @Test public void testGetAndHas() throws JavetException { @@ -50,28 +47,29 @@ public void testGetAndHas() throws JavetException { 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)); + try (IV8ValueIterator iterator = v8ValueMap.getKeys()) { + assertEquals("x", ((V8ValueString) iterator.getNext()).getValue()); + assertEquals("y", ((V8ValueString) iterator.getNext()).getValue()); + assertEquals(3, ((V8ValueInteger) iterator.getNext()).getValue()); + } + try (IV8ValueIterator iterator = v8ValueMap.getValues()) { + assertEquals(1, ((V8ValueInteger) iterator.getNext()).getValue()); + assertEquals("b", ((V8ValueString) iterator.getNext()).getValue()); + assertEquals("c", ((V8ValueString) iterator.getNext()).getValue()); + } + try (IV8ValueIterator iterator = v8ValueMap.getEntries()) { + try (V8ValueArray entry = iterator.getNext()) { + assertEquals("x", entry.getString(0)); + assertEquals(1, entry.getInteger(1)); + } + try (V8ValueArray entry = iterator.getNext()) { + assertEquals("y", entry.getString(0)); + assertEquals("b", entry.getString(1)); + } + try (V8ValueArray entry = iterator.getNext()) { + assertEquals(3, entry.getInteger(0)); + assertEquals("c", entry.getString(1)); + } } assertEquals(1, v8Runtime.getReferenceCount()); } diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java index b18bf0de0..7793215b8 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueObject.java @@ -51,6 +51,28 @@ public void testClearWeak() throws JavetException { } } + @Test + public void testEquals() throws JavetException { + try (V8ValueObject v8ValueObject1 = v8Runtime.getExecutor( + "const a = {'x': '1'}; a;").execute()) { + assertFalse(v8ValueObject1.equals(null)); + assertFalse(v8ValueObject1.sameValue(null)); + assertFalse(v8ValueObject1.strictEquals(null)); + assertFalse(v8ValueObject1.equals(v8Runtime.createV8ValueNull())); + assertFalse(v8ValueObject1.sameValue(v8Runtime.createV8ValueNull())); + assertFalse(v8ValueObject1.strictEquals(v8Runtime.createV8ValueNull())); + assertTrue(v8ValueObject1.equals(v8ValueObject1)); + assertTrue(v8ValueObject1.sameValue(v8ValueObject1)); + assertTrue(v8ValueObject1.strictEquals(v8ValueObject1)); + try (V8ValueObject v8ValueObject2 = v8Runtime.getExecutor( + "const b = {'x': '1'}; b;").execute()) { + assertFalse(v8ValueObject1.equals(v8ValueObject2)); + assertFalse(v8ValueObject1.sameValue(v8ValueObject2)); + assertFalse(v8ValueObject1.strictEquals(v8ValueObject2)); + } + } + } + @Test public void testGetOwnPropertyNames() throws JavetException { try (V8ValueObject v8ValueObject = v8Runtime.getExecutor( @@ -118,9 +140,9 @@ public void testGetProperty() throws JavetException { assertEquals(3L, ((V8ValueLong) v8ValueObject.getProperty("c")).getValue()); assertEquals(3L, v8ValueObject.getPropertyLong("c")); assertEquals(1, v8ValueObject.getPropertyInteger("d")); - assertTrue(v8ValueObject.getProperty("e") instanceof V8ValueNull); + assertTrue(v8ValueObject.getProperty("e").isNull()); assertEquals("測試", v8ValueObject.getPropertyString("中文")); - assertTrue(v8ValueObject.getProperty("$") instanceof V8ValueUndefined); + assertTrue(v8ValueObject.getProperty("$").isUndefined()); assertEquals(1, v8Runtime.getReferenceCount()); try (V8ValueObject childV8ValueObject = v8ValueObject.getProperty("g")) { assertNotNull(childV8ValueObject); @@ -142,6 +164,13 @@ public void testGetProperty() throws JavetException { } } + @Test + public void testIdentityHash() throws JavetException { + try (V8ValueObject v8ValueObject = v8Runtime.getExecutor("const a = {}; a;").execute()) { + assertTrue(v8ValueObject.getIdentityHash() > 0); + } + } + @Test public void testInvoke() throws JavetException { try (V8ValueArray v8ValueArray = v8Runtime.getExecutor("const a = [1, 2, 3]; a;").execute()) { @@ -244,7 +273,7 @@ public void testSetWeakDirectDescendant() throws JavetException { v8Runtime.requestGarbageCollectionForTesting(true); assertEquals(0, v8Runtime.getReferenceCount()); assertEquals(0L, a.getHandle()); - assertTrue(globalObject.get("a") instanceof V8ValueUndefined); + assertTrue(globalObject.get("a").isUndefined()); } @Test diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java index ff0542375..bf47ce31c 100644 --- a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueSet.java @@ -22,7 +22,6 @@ 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.*; @@ -76,11 +75,10 @@ public void testHas() throws JavetException { 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()); + try (IV8ValueIterator iterator = v8ValueSet.getKeys()) { + assertEquals("x", ((V8ValueString) iterator.getNext()).getValue()); + assertEquals("y", ((V8ValueString) iterator.getNext()).getValue()); + assertEquals(3, ((V8ValueInteger) iterator.getNext()).getValue()); } } } diff --git a/src/test/java/com/caoccao/javet/values/reference/TestV8ValueTypedArray.java b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueTypedArray.java new file mode 100644 index 000000000..ac7f1959f --- /dev/null +++ b/src/test/java/com/caoccao/javet/values/reference/TestV8ValueTypedArray.java @@ -0,0 +1,272 @@ +/* + * 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.V8ValueReferenceType; +import org.junit.jupiter.api.Test; + +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestV8ValueTypedArray extends BaseTestJavetRuntime { + + @Test + public void testByte() throws JavetException { + final int length = 16; + int[] types = new int[]{V8ValueReferenceType.Int8Array, V8ValueReferenceType.Uint8Array, V8ValueReferenceType.Uint8ClampedArray}; + byte[] bytes = new byte[length]; + new Random().nextBytes(bytes); + for (int i = 0; i < length; i++) { + bytes[i] = (byte) (bytes[i] & 0x7F); + } + for (int i = 0; i < types.length; ++i) { + final int type = types[i]; + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(1, v8ValueTypedArray.getSizeInBytes()); + assertEquals(length, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + try (V8ValueArrayBuffer v8ValueArrayBuffer = v8ValueTypedArray.getBuffer()) { + v8ValueArrayBuffer.fromBytes(bytes); + } + for (int j = 0; j < length; j++) { + assertEquals(bytes[j], v8Runtime.getExecutor("a[" + j + "];").executeInteger()); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(1, v8ValueTypedArray.getSizeInBytes()); + assertEquals(length, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + try (V8ValueArrayBuffer v8ValueArrayBuffer = v8ValueTypedArray.getBuffer()) { + v8ValueArrayBuffer.fromBytes(bytes); + } + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int j = 0; j < length; j++) { + assertEquals(bytes[j], v8Runtime.getExecutor("b[" + j + "];").executeInteger()); + } + } + v8Runtime.unlock().resetContext().lock(); + } + } + + @Test + public void testDouble() throws JavetException { + final int length = 16; + final int size = 8; + final int type = V8ValueReferenceType.Float64Array; + final int byteLength = length * size; + final double[] doubles = new Random().doubles(length).toArray(); + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromDoubles(doubles)); + for (int i = 0; i < length; i++) { + assertEquals(doubles[i], v8Runtime.getExecutor("a[" + i + "];").executeDouble(), 0.001D); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromDoubles(doubles)); + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int i = 0; i < length; i++) { + assertEquals(doubles[i], v8Runtime.getExecutor("b[" + i + "];").executeDouble(), 0.001D); + } + } + } + + @Test + public void testFloat() throws JavetException { + final int length = 16; + final int size = 4; + final int type = V8ValueReferenceType.Float32Array; + final int byteLength = length * size; + final float[] floats = new float[length]; + Random random = new Random(); + for (int i = 0; i < length; i++) { + floats[i] = random.nextFloat(); + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromFloats(floats)); + for (int i = 0; i < length; i++) { + assertEquals(floats[i], v8Runtime.getExecutor("a[" + i + "];").executeDouble().floatValue(), 0.001F); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromFloats(floats)); + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int i = 0; i < length; i++) { + assertEquals(floats[i], v8Runtime.getExecutor("b[" + i + "];").executeDouble().floatValue(), 0.001F); + } + } + } + + @Test + public void testInteger() throws JavetException { + final int length = 16; + int[] sizes = new int[]{4, 4}; + int[] types = new int[]{V8ValueReferenceType.Int32Array, V8ValueReferenceType.Uint32Array}; + final int[] integers = new Random().ints(length).toArray(); + for (int i = 0; i < length; i++) { + if (integers[i] < 0) { + integers[i] = 0 - integers[i]; + } + } + for (int i = 0; i < types.length; ++i) { + final int size = sizes[i]; + final int type = types[i]; + final int byteLength = length * size; + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromIntegers(integers)); + for (int j = 0; j < length; j++) { + assertEquals(integers[j], v8Runtime.getExecutor("a[" + j + "];").executeInteger()); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromIntegers(integers)); + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int j = 0; j < length; j++) { + assertEquals(integers[j], v8Runtime.getExecutor("b[" + j + "];").executeInteger()); + } + } + v8Runtime.unlock().resetContext().lock(); + } + } + + @Test + public void testLong() throws JavetException { + final int length = 16; + int[] sizes = new int[]{8, 8}; + int[] types = new int[]{V8ValueReferenceType.BigInt64Array, V8ValueReferenceType.BigUint64Array}; + final long[] longs = new Random().longs(length).toArray(); + for (int i = 0; i < length; i++) { + if (longs[i] < 0) { + longs[i] = 0 - longs[i]; + } + } + for (int i = 0; i < types.length; ++i) { + final int size = sizes[i]; + final int type = types[i]; + final int byteLength = length * size; + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromLongs(longs)); + for (int j = 0; j < length; j++) { + assertEquals(longs[j], v8Runtime.getExecutor("a[" + j + "];").executeLong()); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromLongs(longs)); + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int j = 0; j < length; j++) { + assertEquals(longs[j], v8Runtime.getExecutor("b[" + j + "];").executeLong()); + } + } + v8Runtime.unlock().resetContext().lock(); + } + } + + @Test + public void testShort() throws JavetException { + final int length = 16; + int[] sizes = new int[]{2, 2}; + int[] types = new int[]{V8ValueReferenceType.Int16Array, V8ValueReferenceType.Uint16Array}; + final short[] shorts = new short[length]; + Random random = new Random(); + for (int i = 0; i < length; i++) { + shorts[i] = (short) (random.nextInt() & 0x7FFF); + } + for (int i = 0; i < types.length; ++i) { + final int size = sizes[i]; + final int type = types[i]; + final int byteLength = length * size; + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.getExecutor( + "const a = new " + V8ValueTypedArray.getName(type) + "(" + length + "); a;").execute()) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromShorts(shorts)); + for (int j = 0; j < length; j++) { + assertEquals(shorts[j], v8Runtime.getExecutor("a[" + j + "];").executeInteger()); + } + } + try (V8ValueTypedArray v8ValueTypedArray = v8Runtime.createV8ValueTypedArray(type, length)) { + assertEquals(length, v8ValueTypedArray.getLength()); + assertEquals(size, v8ValueTypedArray.getSizeInBytes()); + assertEquals(byteLength, v8ValueTypedArray.getByteLength()); + assertEquals(0, v8ValueTypedArray.getByteOffset()); + assertEquals(type, v8ValueTypedArray.getType()); + assertTrue(v8ValueTypedArray.fromShorts(shorts)); + v8Runtime.getGlobalObject().set("b", v8ValueTypedArray); + for (int j = 0; j < length; j++) { + assertEquals(shorts[j], v8Runtime.getExecutor("b[" + j + "];").executeInteger()); + } + } + v8Runtime.unlock().resetContext().lock(); + } + } +}