diff --git a/Makefile b/Makefile index 4038dce1b8a69588efbc50bdbb392c6c51d96556..684bc3c7631a35ac3cca9b97b6cfbdd6f9fb3805 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,7 @@ ifeq ($(BUILD_WITH), make) $(NODE_EXE): build_type:=Release $(NODE_G_EXE): build_type:=Debug $(NODE_EXE) $(NODE_G_EXE): config.gypi out/Makefile - $(MAKE) -C out BUILDTYPE=${build_type} V=$(V) + $(MAKE) -C out BUILDTYPE=${build_type} V=$(V) libjsvm -j$(NUM_CPUS) if [ ! -r $@ ] || [ ! -L $@ ]; then \ ln -fs out/${build_type}/$(NODE_EXE) $@; fi else diff --git a/jsvm/.clang-format b/jsvm/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..609a21c57ad5302fa784fc9b0f5c062266d77dcf --- /dev/null +++ b/jsvm/.clang-format @@ -0,0 +1,106 @@ +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: None +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakAfterJavaFieldAnnotations: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"(base|core|elements|rendering|adapter|dsl|frameworks|resource|accessibility|bridge)/' + Priority: 3 + - Regex: '<*>' + Priority: 1 + - Regex: '.*' + Priority: 2 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 39 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 120 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Never +... diff --git a/jsvm/interface/kits/jsvm.h b/jsvm/interface/kits/jsvm.h new file mode 100644 index 0000000000000000000000000000000000000000..41a7f81f32df13a16233bcaffd4f2f419929a7ea --- /dev/null +++ b/jsvm/interface/kits/jsvm.h @@ -0,0 +1,2857 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef ARK_RUNTIME_JSVM_JSVM_H +#define ARK_RUNTIME_JSVM_JSVM_H + +/** + * @addtogroup JSVM + * @{ + * + * @brief Provides the standard JavaScript engine capabilities. + * + * Provides API to Provide independent, standard, and complete JavaScript engine capabilities for developers, + * including managing the engine lifecycle, compiling and running JS code, implementing JS/C++ cross language calls, + * and taking snapshots. + * + * @since 11 + */ + +/** + * @file jsvm.h + * + * @brief Provides the JSVM API define. + * + * Provides API to Provide independent, standard, and complete JavaScript engine capabilities for developers, + * including managing the engine lifecycle, compiling and running JS code, implementing JS/C++ cross language calls, + * and taking snapshots. + * @library libjsvm.so + * @syscap SystemCapability.ArkCompiler.JSVM + * @since 11 + */ + +// This file needs to be compatible with C compilers. +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) + +// Use INT_MAX, this should only be consumed by the pre-processor anyway. +#define JSVM_VERSION_EXPERIMENTAL 2147483647 +#ifndef JSVM_VERSION +#ifdef JSVM_EXPERIMENTAL +#define JSVM_VERSION JSVM_VERSION_EXPERIMENTAL +#else +// The baseline version for JSVM-API. +// The JSVM_VERSION controls which version will be used by default when +// compilling a native addon. If the addon developer specifically wants to use +// functions available in a new version of JSVM-API that is not yet ported in all +// LTS versions, they can set JSVM_VERSION knowing that they have specifically +// depended on that version. +#define JSVM_VERSION 8 +#endif +#endif + +#include "jsvm_types.h" + +#ifndef JSVM_EXTERN +#ifdef _WIN32 +/** + * @brief externally visible. + * + * @since 11 + */ +#define JSVM_EXTERN __declspec(dllexport) +#elif defined(__wasm__) +#define JSVM_EXTERN \ + __attribute__((visibility("default"))) \ + __attribute__((__import_module__("jsvm"))) +#else +#define JSVM_EXTERN __attribute__((visibility("default"))) +#endif +#endif + +/** + * @brief auto length. + * + * @since 11 + */ +#define JSVM_AUTO_LENGTH SIZE_MAX + +#ifdef __cplusplus +#define EXTERN_C_START extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_START +#define EXTERN_C_END +#endif + +EXTERN_C_START + +/** + * @brief Init a JavaScript vm. + * + * @param options: The options for initialize the JavaScript VM. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options); + +/** + * @brief This API create a new VM instance. + * + * @param options: The options for create the VM instance. + * @param result: The new VM instance. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, + JSVM_VM* result); + +/** + * @brief Destroys VM instance. + * + * @param vm: The VM instance to be Destroyed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm); + +/** + * @brief This API open a new VM scope for the VM instance. + * + * @param vm: The VM instance to open scope for. + * @param result: The new VM scope. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenVMScope(JSVM_VM vm, + JSVM_VMScope* result); + +/** + * @brief This function close the VM scope for the VM instance. + * + * @param vm: The VM instance to close scope for. + * @param scope: The VM scope to be closed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CloseVMScope(JSVM_VM vm, + JSVM_VMScope scope); + +/** + * @brief This function create a new environment with optional properties for the context of the new environment. + * + * @param vm: The VM instance that the env will be created in. + * @param propertyCount: The number of elements in the properties array. + * @param properties: The array of property descriptor. + * @param result: The new environment created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateEnv(JSVM_VM vm, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Env* result); + +/** + * @brief This function create a new environment from the start snapshot of the vm. + * + * @param vm: The VM instance that the env will be created in. + * @param index: The index of the environment in the snapshot. + * @param result: The new environment created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateEnvFromSnapshot(JSVM_VM vm, + size_t index, + JSVM_Env* result); + +/** + * @brief This function destroys the environment. + * + * @param env: The environment to be destroyed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DestroyEnv(JSVM_Env env); + +/** + * @brief This function open a new environment scope. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param result: The new environment scope. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenEnvScope(JSVM_Env env, + JSVM_EnvScope* result); + +/** + * @brief This function closes the environment scope of the environment. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param scope: The environment scope to be closed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CloseEnvScope(JSVM_Env env, + JSVM_EnvScope scope); + +/** + * @brief This function retrieves the VM instance of the given environment. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param result: The VM instance of the environment. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetVM(JSVM_Env env, + JSVM_VM* result); + +/** + * @brief This function compiles a string of JavaScript code and returns the compiled script. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param script: A JavaScript string containing the script yo be compiled. + * @param cachedData: Optional code cache data for the script. + * @param cacheDataLength: The length of cachedData array. + * @param eagerCompile: Whether to compile the script eagerly. + * @param cacheRejected: Whether the code cache rejected by compilation. + * @param result: The compiled script. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CompileScript(JSVM_Env env, + JSVM_Value script, + const uint8_t* cachedData, + size_t cacheDataLength, + bool eagerCompile, + bool* cacheRejected, + JSVM_Script* result); + +/** + * @brief This function compiles a string of JavaScript code with the source code information + * and returns the compiled script. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param script: A JavaScript string containing the script to be compiled. + * @param cachedData: Optional code cache data for the script. + * @param cacheDataLength: The length of cachedData array. + * @param eagerCompile: Whether to compile the script eagerly. + * @param cacheRejected: Whether the code cache rejected by compilation. + * @param origin: The information of source code. + * @param result: The compiled script. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CompileScriptWithOrigin(JSVM_Env env, + JSVM_Value script, + const uint8_t* cachedData, + size_t cacheDataLength, + bool eagerCompile, + bool* cacheRejected, + JSVM_ScriptOrigin* origin, + JSVM_Script* result); + +/** + * @brief This function compiles a string of JavaScript code with the source code information + * and returns the compiled script. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param script: A JavaScript string containing the script to be compiled. + * @param optionCount: length of option array. + * @param options: Compile options to be passed. + * @param result: The compiled script. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CompileScriptWithOptions(JSVM_Env env, + JSVM_Value script, + size_t optionCount, + JSVM_CompileOptions options[], + JSVM_Script* result); + +/** + * @brief This function creates code cache for the compiled script. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param script: A compiled script to create code cache for. + * @param data: The data of the code cache. + * @param length: The length of the code cache data. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateCodeCache(JSVM_Env env, + JSVM_Script script, + const uint8_t** data, + size_t* length); + +/** + * @brief This function executes a string of JavaScript code and returns its result with the following caveats: + * Unlike eval, this function does not allow the script to access the current lexical scope, and therefore also + * does not allow to access the module scope, meaning that pseudo-globals such as require will not be available. + * The script can access the global scope. Function and var declarations in the script will be added to the + * global object. Variable declarations made using let and const will be visible globally, but will not be added + * to the global object.The value of this is global within the script. + * + * @param env: The environment that the API is invoked under. + * @param script: A JavaScript string containing the script to execute. + * @param result: The value resulting from having executed the script. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_RunScript(JSVM_Env env, + JSVM_Script script, + JSVM_Value* result); + +/** + * @brief This API associates data with the currently running JSVM environment. data can later be retrieved + * using OH_JSVM_GetInstanceData(). + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param data: The data item to make available to bindings of this instance. + * @param finalizeCb: The function to call when the environment is being torn down. The function receives + * data so that it might free it. JSVM_Finalize provides more details. + * @param finalizeHint: Optional hint to pass to the finalize callback during collection. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetInstanceData(JSVM_Env env, + void* data, + JSVM_Finalize finalizeCb, + void* finalizeHint); + +/** + * @brief This API retrieves data that was previously associated with the currently running JSVM environment + * via OH_JSVM_SetInstanceData(). If no data is set, the call will succeed and data will be set to NULL. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param data: The data item that was previously associated with the currently running JSVM environment by + * a call to OH_JSVM_SetInstanceData(). + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetInstanceData(JSVM_Env env, + void** data); + +/** + * @brief This API retrieves a JSVM_ExtendedErrorInfo structure with information about the last error that + * occurred. + * + * @param env: The environment that the JSVM-API call is invoked under. + * @param result: The JSVM_ExtendedErrorInfo structure with more information about the error. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetLastErrorInfo(JSVM_Env env, + const JSVM_ExtendedErrorInfo** result); + +/** + * @brief This API throws the JavaScript value provided. + * + * @param env: The environment that the API is invoked under. + * @param error: The JavaScript value to be thrown. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Throw(JSVM_Env env, + JSVM_Value error); + +/** + * @brief This API throws a JavaScript Error with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional error code to be set on the error. + * @param msg: C string representing the text to be associated with the error. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ThrowError(JSVM_Env env, + const char* code, + const char* msg); + +/** + * @brief This API throws a JavaScript TypeError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional error code to be set on the error. + * @param msg: C string representing the text to be associated with the error. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ThrowTypeError(JSVM_Env env, + const char* code, + const char* msg); + +/** + * @brief This API throws a JavaScript RangeError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional error code to be set on the error. + * @param msg: C string representing the text to be associated with the error. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ThrowRangeError(JSVM_Env env, + const char* code, + const char* msg); + +/** + * @brief This API throws a JavaScript SyntaxError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional error code to be set on the error. + * @param msg: C string representing the text to be associated with the error. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ThrowSyntaxError(JSVM_Env env, + const char* code, + const char* msg); + +/** + * @brief This API queries a JSVM_Value to check if it represents an error object. + * + * @param env: The environment that the API is invoked under. + * @param value: The JSVM_Value to be checked. + * @param result: Boolean value that is set to true if JSVM_Value represents an error, + * false otherwise. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsError(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API returns a JavaScript Error with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional JSVM_Value with the string for the error code to be associated with the error. + * @param msg: JSVM_Value that references a JavaScript string to be used as the message for the Error. + * @param result: JSVM_Value representing the error created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateError(JSVM_Env env, + JSVM_Value code, + JSVM_Value msg, + JSVM_Value* result); + +/** + * @brief This API returns a JavaScript TypeError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional JSVM_Value with the string for the error code to be associated with the error. + * @param msg: JSVM_Value that references a JavaScript string to be used as the message for the Error. + * @param result: JSVM_Value representing the error created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateTypeError(JSVM_Env env, + JSVM_Value code, + JSVM_Value msg, + JSVM_Value* result); + +/** + * @brief This API returns a JavaScript RangeError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional JSVM_Value with the string for the error code to be associated with the error. + * @param msg: JSVM_Value that references a JavaScript string to be used as the message for the Error. + * @param result: JSVM_Value representing the error created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateRangeError(JSVM_Env env, + JSVM_Value code, + JSVM_Value msg, + JSVM_Value* result); + +/** + * @brief This API returns a JavaScript SyntaxError with the text provided. + * + * @param env: The environment that the API is invoked under. + * @param code: Optional JSVM_Value with the string for the error code to be associated with the error. + * @param msg: JSVM_Value that references a JavaScript string to be used as the message for the Error. + * @param result: JSVM_Value representing the error created. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateSyntaxError(JSVM_Env env, + JSVM_Value code, + JSVM_Value msg, + JSVM_Value* result); + +/** + * @brief This API returns a JavaScript exception if one is pending, NULL otherwise. + * + * @param env: The environment that the API is invoked under. + * @param result: The exception if one is pending, NULL otherwise. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetAndClearLastException(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API returns true if an exception is pending, false otherwise. + * + * @param env: The environment that the API is invoked under. + * @param result: Boolean value that is set to true if an exception is pending. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsExceptionPending(JSVM_Env env, + bool* result); + +/** + * @brief This API opens a new scope. + * + * @param env: The environment that the API is invoked under. + * @param result: JSVM_Value representing the new scope. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenHandleScope(JSVM_Env env, + JSVM_HandleScope* result); + +/** + * @brief This API closes the scope passed in. Scopes must be closed in the reverse + * order from which they were created. + * + * @param env: The environment that the API is invoked under. + * @param scope: JSVM_Value representing the scope to be closed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CloseHandleScope(JSVM_Env env, + JSVM_HandleScope scope); + +/** + * @brief This API opens a new scope from which one object can be promoted to the outer scope. + * + * @param env: The environment that the API is invoked under. + * @param result: JSVM_Value representing the new scope. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenEscapableHandleScope(JSVM_Env env, + JSVM_EscapableHandleScope* result); + +/** + * @brief This API closes the scope passed in. Scopes must be closed in the reverse order + * from which they were created. + * + * @param env: The environment that the API is invoked under. + * @param scope: JSVM_Value representing the scope to be closed. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CloseEscapableHandleScope(JSVM_Env env, + JSVM_EscapableHandleScope scope); + +/** + * @brief This API promotes the handle to the JavaScript object so that it is valid for the lifetime + * of the outer scope. It can only be called once per scope. If it is called more than once an error + * will be returned. + * + * @param env: The environment that the API is invoked under. + * @param scope: JSVM_Value representing the current scope. + * @param escapee: JSVM_Value representing the JavaScript Object to be escaped. + * @param result: JSVM_Value representing the handle to the escaped Object in the outer scope. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_EscapeHandle(JSVM_Env env, + JSVM_EscapableHandleScope scope, + JSVM_Value escapee, + JSVM_Value* result); + +/** + * @brief This API creates a new reference with the specified reference count to the value passed in. + * + * @param env: The environment that the API is invoked under. + * @param value: The JSVM_Value for which a reference is being created. + * @param initialRefcount: Initial reference count for the new reference. + * @param result: JSVM_Ref pointing to the new reference. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateReference(JSVM_Env env, + JSVM_Value value, + uint32_t initialRefcount, + JSVM_Ref* result); + +/** + * @brief his API deletes the reference passed in. + * + * @param env: The environment that the API is invoked under. + * @param ref: JSVM_Ref to be deleted. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DeleteReference(JSVM_Env env, + JSVM_Ref ref); + +/** + * @brief his API increments the reference count for the reference passed in and + * returns the resulting reference count. + * + * @param env: The environment that the API is invoked under. + * @param ref: JSVM_Ref for which the reference count will be incremented. + * @param result: The new reference count. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ReferenceRef(JSVM_Env env, + JSVM_Ref ref, + uint32_t* result); + +/** + * @brief This API decrements the reference count for the reference passed in and + * returns the resulting reference count. + * + * @param env: The environment that the API is invoked under. + * @param ref: JSVM_Ref for which the reference count will be decremented. + * @param result: The new reference count. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ReferenceUnref(JSVM_Env env, + JSVM_Ref ref, + uint32_t* result); + +/** + * @brief If still valid, this API returns the JSVM_Value representing the + * JavaScript value associated with the JSVM_Ref. Otherwise, result will be NULL. + * + * @param env: The environment that the API is invoked under. + * @param ref: The JSVM_Ref for which the corresponding value is being requested. + * @param result: The JSVM_Value referenced by the JSVM_Ref. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetReferenceValue(JSVM_Env env, + JSVM_Ref ref, + JSVM_Value* result); + +/** + * @brief This API returns a JSVM-API value corresponding to a JavaScript Array type. + * + * @param env: The environment that the API is invoked under. + * @param result: A JSVM_Value representing a JavaScript Array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateArray(JSVM_Env env, + JSVM_Value* result); + + +/** + * @brief This API returns a JSVM-API value corresponding to a JavaScript Array type. The Array's length property + * is set to the passed-in length parameter. However, the underlying buffer is not guaranteed to be pre-allocated + * by the VM when the array is created. That behavior is left to the underlying VM implementation. + * + * @param env: The environment that the API is invoked under. + * @param length: The initial length of the Array. + * @param result: A JSVM_Value representing a JavaScript Array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateArrayWithLength(JSVM_Env env, + size_t length, + JSVM_Value* result); + +/** + * @brief This API returns a JSVM-API value corresponding to a JavaScript ArrayBuffer. ArrayBuffers are used to + * represent fixed-length binary data buffers. They are normally used as a backing-buffer for TypedArray objects. + * The ArrayBuffer allocated will have an underlying byte buffer whose size is determined by the length parameter + * that's passed in. The underlying buffer is optionally returned back to the caller in case the caller wants to + * directly manipulate the buffer. This buffer can only be written to directly from native code. To write to this + * buffer from JavaScript, a typed array or DataView object would need to be created. + * + * @param env: The environment that the API is invoked under. + * @param byteLength: The length in bytes of the array buffer to create. + * @param data: Pointer to the underlying byte buffer of the ArrayBuffer.data can optionally be ignored by passing NULL. + * @param result: A JSVM_Value representing a JavaScript Array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateArraybuffer(JSVM_Env env, + size_t byteLength, + void** data, + JSVM_Value* result); + +/** + * @brief This API allocate the memory of array buffer backing store. + * + * @param byteLength: size of backing store memory. + * @param initialized: initialization status of the backing store memory. + * @param data: pointer that recieve the backing store memory pointer. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if allocation succeed.\n + * Returns {@link JSVM_INVALID_ARG } if data is null pointer.\n + * Returns {@link JSVM_GENERIC_FAILURE } if allocation failed.\n + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_AllocateArrayBufferBackingStoreData(size_t byteLength, + JSVM_InitializedFlag initialized, + void **data); + +/** + * @brief This API release the memory of an array buffer backing store. + * + * @param data: pointer to the backing store memory. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if run succeed.\n + * Returns {@link JSVM_INVALID_ARG } if data is null pointer.\n + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_FreeArrayBufferBackingStoreData(void *data); + +/** + * @brief This API create an array buffer using the backing store data. + * + * @param env: The environment that the API is invoked under. + * @param data: pointer to the backing store memory. + * @param backingStoreSize: size of backing store memory. + * @param offset: start position of the array buffer in the backing store memory. + * @param arrayBufferSize: size of the array buffer. + * @param result: pointer that recieve the array buffer. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if creation succeed.\n + * Returns {@link JSVM_INVALID_ARG } if any of the following condition reached:\n + * 1. offset + arrayBufferSize > backingStoreSize\n + * 2. backingStoreSize or arrayBufferSize equals zero + * 3. data or result is null pointer + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayBufferFromBackingStoreData(JSVM_Env env, + void *data, + size_t backingStoreSize, + size_t offset, + size_t arrayBufferSize, + JSVM_Value *result); + +/** + * @brief This API does not observe leap seconds; they are ignored, as ECMAScript aligns with POSIX time specification. + * This API allocates a JavaScript Date object. + * + * @param env: The environment that the API is invoked under. + * @param time: ECMAScript time value in milliseconds since 01 January, 1970 UTC. + * @param result: A JSVM_Value representing a JavaScript Date. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateDate(JSVM_Env env, + double time, + JSVM_Value* result); + +/** + * @brief This API allocates a JavaScript value with external data attached to it. This is used to pass external + * data through JavaScript code, so it can be retrieved later by native code using OH_JSVM_GetValueExternal. + * The API adds a JSVM_Finalize callback which will be called when the JavaScript object just created has been garbage + * collected.The created value is not an object, and therefore does not support additional properties. It is considered + * a distinct value type: calling OH_JSVM_Typeof() with an external value yields JSVM_EXTERNAL. + * + * @param env: The environment that the API is invoked under. + * @param data: Raw pointer to the external data. + * @param finalizeCb: Optional callback to call when the external value is being collected. JSVM_Finalize provides + * more details. + * @param finalizeHint: Optional hint to pass to the finalize callback during collection. + * @param result: A JSVM_Value representing an external value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateExternal(JSVM_Env env, + void* data, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Value* result); + +/** + * @brief This API allocates a default JavaScript Object. It is the equivalent of doing new Object() in JavaScript. + * + * @param env: The environment that the API is invoked under. + * @param result: A JSVM_Value representing a JavaScript Object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateObject(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript symbol value from a UTF8-encoded C string. + * + * @param env: The environment that the API is invoked under. + * @param description: Optional JSVM_Value which refers to a JavaScript string to be set as the description + * for the symbol. + * @param result: A JSVM_Value representing a JavaScript symbol. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateSymbol(JSVM_Env env, + JSVM_Value description, + JSVM_Value* result); + +/** + * @brief This API searches in the global registry for an existing symbol with the given description. + * If the symbol already exists it will be returned, otherwise a new symbol will be created in the registry. + * + * @param env: The environment that the API is invoked under. + * @param utf8description: UTF-8 C string representing the text to be used as the description for the symbol. + * @param length: The length of the description string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param result: A JSVM_Value representing a JavaScript symbol. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SymbolFor(JSVM_Env env, + const char* utf8description, + size_t length, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript TypedArray object over an existing ArrayBuffer. TypedArray + * objects provide an array-like view over an underlying data buffer where each element has the + * same underlying binary scalar datatype.It's required that (length * size_of_element) + byte_offset should + * be <= the size in bytes of the array passed in. If not, a RangeError exception is raised. + * + * @param env: The environment that the API is invoked under. + * @param type: Scalar datatype of the elements within the TypedArray. + * @param length: Number of elements in the TypedArray. + * @param arraybuffer: ArrayBuffer underlying the typed array. + * @param byteOffset: The byte offset within the ArrayBuffer from which to start projecting the TypedArray. + * @param result: A JSVM_Value representing a JavaScript TypedArray + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateTypedarray(JSVM_Env env, + JSVM_TypedarrayType type, + size_t length, + JSVM_Value arraybuffer, + size_t byteOffset, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript DataView object over an existing ArrayBuffer. DataView + * objects provide an array-like view over an underlying data buffer, but one which allows items + * of different size and type in the ArrayBuffer.It is required that byte_length + byte_offset is + * less than or equal to the size in bytes of the array passed in. If not, a RangeError exception + * is raised. + * + * @param env: The environment that the API is invoked under. + * @param length: Number of elements in the DataView. + * @param arraybuffer: ArrayBuffer underlying the DataView. + * @param byteOffset: The byte offset within the ArrayBuffer from which to start projecting the DataView. + * @param result:A JSVM_Value representing a JavaScript DataView. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateDataview(JSVM_Env env, + size_t length, + JSVM_Value arraybuffer, + size_t byteOffset, + JSVM_Value* result); + +/** + * @brief This API is used to convert from the C int32_t type to the JavaScript number type. + * + * @param env: The environment that the API is invoked under. + * @param value: Integer value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateInt32(JSVM_Env env, + int32_t value, + JSVM_Value* result); + +/** + * @brief This API is used to convert from the C uint32_t type to the JavaScript number type. + * + * @param env: The environment that the API is invoked under. + * @param value: Unsigned integer value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateUint32(JSVM_Env env, + uint32_t value, + JSVM_Value* result); + +/** + * @brief This API is used to convert from the C int64_t type to the JavaScript number type. + * + * @param env: The environment that the API is invoked under. + * @param value: Integer value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateInt64(JSVM_Env env, + int64_t value, + JSVM_Value* result); + +/** + * @brief This API is used to convert from the C double type to the JavaScript number type. + * + * @param env: The environment that the API is invoked under. + * @param value: Double-precision value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateDouble(JSVM_Env env, + double value, + JSVM_Value* result); + +/** + * @brief This API converts the C int64_t type to the JavaScript BigInt type. + * + * @param env: The environment that the API is invoked under. + * @param value: Integer value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript BigInt. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateBigintInt64(JSVM_Env env, + int64_t value, + JSVM_Value* result); + +/** + * @brief This API converts the C uint64_t type to the JavaScript BigInt type. + * + * @param env: The environment that the API is invoked under. + * @param value: Unsigned integer value to be represented in JavaScript. + * @param result: A JSVM_Value representing a JavaScript BigInt. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateBigintUint64(JSVM_Env env, + uint64_t value, + JSVM_Value* result); + +/** + * @brief This API converts an array of unsigned 64-bit words into a single BigInt value. + * The resulting BigInt is calculated as: (–1)sign_bit (words[0] × (264)0 + words[1] × (264)1 + …) + * + * @param env: The environment that the API is invoked under. + * @param signBit: Determines if the resulting BigInt will be positive or negative. + * @param wordCount: The length of the words array. + * @param words: An array of uint64_t little-endian 64-bit words. + * @param result: A JSVM_Value representing a JavaScript BigInt. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateBigintWords(JSVM_Env env, + int signBit, + size_t wordCount, + const uint64_t* words, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript string value from an ISO-8859-1-encoded C + * string. The native string is copied. + * + * @param env: The environment that the API is invoked under. + * @param str: Character buffer representing an ISO-8859-1-encoded string. + * @param length: The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param result: A JSVM_Value representing a JavaScript string. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringLatin1(JSVM_Env env, + const char* str, + size_t length, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript string value from a UTF16-LE-encoded C + * string. The native string is copied. + * + * @param env: The environment that the API is invoked under. + * @param str: Character buffer representing a UTF16-LE-encoded string. + * @param length: The length of the string in two-byte code units, or JSVM_AUTO_LENGTH + * if it is null-terminated. + * @param result: A JSVM_Value representing a JavaScript string. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringUtf16(JSVM_Env env, + const char16_t* str, + size_t length, + JSVM_Value* result); + +/** + * @brief This API creates a JavaScript string value from a UTF8-encoded C + * string. The native string is copied. + * + * @param env: The environment that the API is invoked under. + * @param str: Character buffer representing a UTF8-encoded string. + * @param length: The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param result: A JSVM_Value representing a JavaScript string. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringUtf8(JSVM_Env env, + const char* str, + size_t length, + JSVM_Value* result); + +/** + * @brief This API returns the length of an array. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing the JavaScript Array whose length is being queried. + * @param result: uint32 representing length of the array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetArrayLength(JSVM_Env env, + JSVM_Value value, + uint32_t* result); + +/** + * @brief This API is used to retrieve the underlying data buffer of an ArrayBuffer and its length. + * + * @param env: The environment that the API is invoked under. + * @param arraybuffer: JSVM_Value representing the ArrayBuffer being queried. + * @param data: The underlying data buffer of the ArrayBuffer. If byte_length is 0, this may be NULL + * or any other pointer value. + * @param byteLength: Length in bytes of the underlying data buffer. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetArraybufferInfo(JSVM_Env env, + JSVM_Value arraybuffer, + void** data, + size_t* byteLength); + +/** + * @brief This API returns the length of an array. + * + * @param env: The environment that the API is invoked under. + * @param object: JSVM_Value representing JavaScript Object whose prototype to return. This returns + * the equivalent of Object.getPrototypeOf (which is not the same as the function's prototype property). + * @param result: JSVM_Value representing prototype of the given object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetPrototype(JSVM_Env env, + JSVM_Value object, + JSVM_Value* result); + +/** + * @brief This API returns various properties of a typed array. + * + * @param env: The environment that the API is invoked under. + * @param typedarray: JSVM_Value representing the TypedArray whose properties to query. + * @param type: Scalar datatype of the elements within the TypedArray. + * @param length: The number of elements in the TypedArray. + * @param data: The data buffer underlying the TypedArray adjusted by the byte_offset value so that it + * points to the first element in the TypedArray. If the length of the array is 0, this may be NULL or + * any other pointer value. + * @param arraybuffer: The ArrayBuffer underlying the TypedArray. + * @param byteOffset: The byte offset within the underlying native array at which the first element of + * the arrays is located. The value for the data parameter has already been adjusted so that data points + * to the first element in the array. Therefore, the first byte of the native array would be at data - byte_offset. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetTypedarrayInfo(JSVM_Env env, + JSVM_Value typedarray, + JSVM_TypedarrayType* type, + size_t* length, + void** data, + JSVM_Value* arraybuffer, + size_t* byteOffset); + +/** + * @brief Any of the out parameters may be NULL if that property is unneeded. + * This API returns various properties of a DataView. + * + * @param env: The environment that the API is invoked under. + * @param dataview: JSVM_Value representing the DataView whose properties to query. + * @param bytelength: Number of bytes in the DataView. + * @param data: The data buffer underlying the DataView. + * If byte_length is 0, this may be NULL or any other pointer value. + * @param arraybuffer: ArrayBuffer underlying the DataView. + * @param byteOffset: The byte offset within the data buffer from which to start projecting the DataView. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetDataviewInfo(JSVM_Env env, + JSVM_Value dataview, + size_t* bytelength, + void** data, + JSVM_Value* arraybuffer, + size_t* byteOffset); + +/** + * @brief Returns JSVM_OK if the API succeeded. If a non-date JSVM_Value is + * passed in it returns JSVM_date_expected.This API returns the C double + * primitive of time value for the given JavaScript Date. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing a JavaScript Date. + * @param result: Time value as a double represented as milliseconds + * since midnight at the beginning of 01 January, 1970 UTC. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetDateValue(JSVM_Env env, + JSVM_Value value, + double* result); + +/** + * @brief This API returns the C boolean primitive equivalent of the given JavaScript Boolean. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript Boolean. + * @param result: C boolean primitive equivalent of the given JavaScript Boolean. + * @return Returns JSVM_OK if the API succeeded. + * If a non-boolean JSVM_Value is passed in it returns JSVM_BOOLEAN_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueBool(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API returns the C double primitive equivalent of the given JavaScript number. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript number. + * @param result: C double primitive equivalent of the given JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * If a non-number JSVM_Value is passed in it returns JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueDouble(JSVM_Env env, + JSVM_Value value, + double* result); + +/** + * @brief This API returns the C int64_t primitive equivalent of the given JavaScript BigInt. + * If needed it will truncate the value, setting lossless to false. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript BigInt. + * @param result: C int64_t primitive equivalent of the given JavaScript BigInt. + * @param lossless: Indicates whether the BigInt value was converted losslessly. + * @return Returns JSVM_OK if the API succeeded. If a non-BigInt is passed in it returns JSVM_BIGINT_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueBigintInt64(JSVM_Env env, + JSVM_Value value, + int64_t* result, + bool* lossless); + +/** + * @brief This API returns the C uint64_t primitive equivalent of the given JavaScript BigInt. + * If needed it will truncate the value, setting lossless to false. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript BigInt. + * @param result: C uint64_t primitive equivalent of the given JavaScript BigInt. + * @param lossless: Indicates whether the BigInt value was converted losslessly. + * @return Returns JSVM_OK if the API succeeded. If a non-BigInt is passed in it returns JSVM_BIGINT_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueBigintUint64(JSVM_Env env, + JSVM_Value value, + uint64_t* result, + bool* lossless); + +/** + * @brief This API converts a single BigInt value into a sign bit, 64-bit little-endian array, and the number + * of elements in the array. signBit and words may be both set to NULL, in order to get only wordCount. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript BigInt. + * @param signBit: Integer representing if the JavaScript BigInt is positive or negative. + * @param wordCount: Must be initialized to the length of the words array. Upon return, it will be set to + * the actual number of words that would be needed to store this BigInt. + * @param words: Pointer to a pre-allocated 64-bit word array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueBigintWords(JSVM_Env env, + JSVM_Value value, + int* signBit, + size_t* wordCount, + uint64_t* words); + +/** + * @brief This API retrieves the external data pointer that was previously passed to OH_JSVM_CreateExternal(). + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript external value. + * @param result: Pointer to the data wrapped by the JavaScript external value. + * @return Returns JSVM_OK if the API succeeded. If a non-external JSVM_Value is passed in it returns JSVM_INVALID_ARG. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueExternal(JSVM_Env env, + JSVM_Value value, + void** result); + +/** + * @brief This API returns the C int32 primitive equivalent of the given JavaScript number. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript number. + * @param result: C int32 primitive equivalent of the given JavaScript number. + * @return Returns JSVM_OK if the API succeeded. If a non-number JSVM_Value is passed in JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueInt32(JSVM_Env env, + JSVM_Value value, + int32_t* result); + +/** + * @brief This API returns the C int64 primitive equivalent of the given JavaScript number. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript number. + * @param result: C int64 primitive equivalent of the given JavaScript number. + * @return Returns JSVM_OK if the API succeeded. If a non-number JSVM_Value is passed in JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueInt64(JSVM_Env env, + JSVM_Value value, + int64_t* result); + +/** + * @brief This API returns the ISO-8859-1-encoded string corresponding the value passed in. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript string. + * @param buf: Buffer to write the ISO-8859-1-encoded string into. If NULL is passed in, the + * length of the string in bytes and excluding the null terminator is returned in result. + * @param bufsize: Size of the destination buffer. When this value is insufficient, the returned string + * is truncated and null-terminated. + * @param result: Number of bytes copied into the buffer, excluding the null terminator. + * @return Returns JSVM_OK if the API succeeded. If a non-number JSVM_Value is passed in JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueStringLatin1(JSVM_Env env, + JSVM_Value value, + char* buf, + size_t bufsize, + size_t* result); + +/** + * @brief This API returns the UTF8-encoded string corresponding the value passed in. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript string. + * @param buf: Buffer to write the UTF8-encoded string into. If NULL is passed in, the length + * of the string in bytes and excluding the null terminator is returned in result. + * @param bufsize: Size of the destination buffer. When this value is insufficient, the returned + * string is truncated and null-terminated. + * @param result: Number of bytes copied into the buffer, excluding the null terminator. + * @return Returns JSVM_OK if the API succeeded. If a non-number JSVM_Value is passed in JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueStringUtf8(JSVM_Env env, + JSVM_Value value, + char* buf, + size_t bufsize, + size_t* result); + +/** + * @brief This API returns the UTF16-encoded string corresponding the value passed in. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript string. + * @param buf: Buffer to write the UTF16-LE-encoded string into. If NULL is passed in, + * the length of the string in 2-byte code units and excluding the null terminator is returned. + * @param bufsize: Size of the destination buffer. When this value is insufficient, + * the returned string is truncated and null-terminated. + * @param result: Number of 2-byte code units copied into the buffer, excluding the null terminator. + * @return Returns JSVM_OK if the API succeeded. If a non-number JSVM_Value is passed in JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueStringUtf16(JSVM_Env env, + JSVM_Value value, + char16_t* buf, + size_t bufsize, + size_t* result); + +/** + * @brief This API returns the C primitive equivalent of the given JSVM_Value as a uint32_t. + * + * @param env: The environment that the API is invoked under. + * @param value: JSVM_Value representing JavaScript number. + * @param result: C primitive equivalent of the given JSVM_Value as a uint32_t. + * @return Returns JSVM_OK if the API succeeded. + * If a non-number JSVM_Value is passed in it returns JSVM_NUMBER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetValueUint32(JSVM_Env env, + JSVM_Value value, + uint32_t* result); + +/** + * @brief This API is used to return the JavaScript singleton object that is used to represent the given boolean value. + * + * @param env: The environment that the API is invoked under. + * @param value: The value of the boolean to retrieve. + * @param result: JSVM_Value representing JavaScript Boolean singleton to retrieve. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetBoolean(JSVM_Env env, + bool value, + JSVM_Value* result); + +/** + * @brief This API returns the global object. + * + * @param env: The environment that the API is invoked under. + * @param result: JSVM_Value representing JavaScript global object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetGlobal(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API returns the null object. + * + * @param env: The environment that the API is invoked under. + * @param result: JSVM_Value representing JavaScript null object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetNull(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API returns the Undefined object. + * + * @param env: The environment that the API is invoked under. + * @param result: JSVM_Value representing JavaScript Undefined value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetUndefined(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API implements the abstract operation ToBoolean() + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to coerce. + * @param result: JSVM_Value representing the coerced JavaScript Boolean. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CoerceToBool(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + +/** + * @brief This API implements the abstract operation ToNumber() as defined. This + * function potentially runs JS code if the passed-in value is an object. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to coerce. + * @param result: JSVM_Value representing the coerced JavaScript number. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CoerceToNumber(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + +/** + * @brief This API implements the abstract operation ToObject(). + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to coerce. + * @param result: JSVM_Value representing the coerced JavaScript Object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CoerceToObject(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + +/** + * @brief This API implements the abstract operation ToString().This + * function potentially runs JS code if the passed-in value is an object. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to coerce. + * @param result: JSVM_Value representing the coerced JavaScript string. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CoerceToString(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + +/** + * @brief This API represents behavior similar to invoking the typeof Operator + * on the object as defined. However, there are some differences:It has support + * for detecting an External value.It detects null as a separate type, while + * ECMAScript typeof would detect object.If value has a type that is invalid, + * an error is returned. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value whose type to query. + * @param result: The type of the JavaScript value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Typeof(JSVM_Env env, + JSVM_Value value, + JSVM_ValueType* result); + +/** + * @brief This API represents invoking the instanceof Operator on the object. + * + * @param env: The environment that the API is invoked under. + * @param object: The JavaScript value to check. + * @param constructor: The JavaScript function object of the constructor function + * to check against. + * @param result: Boolean that is set to true if object instanceof constructor is true. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, + JSVM_Value object, + JSVM_Value constructor, + bool* result); + +/** + * @brief This API represents invoking the IsArray operation on the object + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given object is an array. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsArray(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API checks if the Object passed in is an array buffer. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given object is an ArrayBuffer. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsArraybuffer(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API checks if the Object passed in is a date. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given JSVM_Value represents a JavaScript Date object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsDate(JSVM_Env env, + JSVM_Value value, + bool* isDate); + +/** + * @brief This API checks if the Object passed in is a typed array. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given JSVM_Value represents a TypedArray. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsTypedarray(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API checks if the Object passed in is a DataView. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given JSVM_Value represents a DataView. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsDataview(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API represents the invocation of the Strict Equality algorithm. + * Returns true only when both the type and value are equal. + * + * @param env: The environment that the API is invoked under. + * @param lhs: The JavaScript value to check. + * @param rhs: The JavaScript value to check against. + * @param result: Whether the two JSVM_Value objects are equal. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_StrictEquals(JSVM_Env env, + JSVM_Value lhs, + JSVM_Value rhs, + bool* result); + +/** + * @brief This API represents the invocation of the Relaxed Equality algorithm. + * Returns true as long as the values are equal, regardless of type. + * + * @param env: The environment that the API is invoked under. + * @param lhs: The JavaScript value to check. + * @param rhs: The JavaScript value to check against. + * @param result: Whether the two JSVM_Value objects are relaxed equal. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Equals(JSVM_Env env, + JSVM_Value lhs, + JSVM_Value rhs, + bool* result); + +/** + * @brief This API represents the invocation of the ArrayBuffer detach operation. + * + * @param env: The environment that the API is invoked under. + * @param arraybuffer: The JavaScript ArrayBuffer to be detached. + * @return Returns JSVM_OK if the API succeeded.If a non-detachable ArrayBuffer + * is passed in it returns JSVM_DETACHABLE_ARRAYBUFFER_EXPECTED. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DetachArraybuffer(JSVM_Env env, + JSVM_Value arraybuffer); + +/** + * @brief This API represents the invocation of the ArrayBuffer IsDetachedBuffer operation. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript ArrayBuffer to be checked. + * @param result: Whether the arraybuffer is detached. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsDetachedArraybuffer(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API returns the names of the enumerable properties of object as an array of + * strings. The properties of object whose key is a symbol will not be included. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the properties. + * @param result: A JSVM_Value representing an array of JavaScript values that represent + * the property names of the object. The API can be used to iterate over result using + * OH_JSVM_GetArrayLength and OH_JSVM_GetElement. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetPropertyNames(JSVM_Env env, + JSVM_Value object, + JSVM_Value* result); + +/** + * @brief This API returns an array containing the names of the available properties + * of this object. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the properties. + * @param keyMode: Whether to retrieve prototype properties as well. + * @param keyFilter: Which properties to retrieve (enumerable/readable/writable). + * @param keyConversion: Whether to convert numbered property keys to strings. + * @param result: result: A JSVM_Value representing an array of JavaScript values + * that represent the property names of the object. OH_JSVM_GetArrayLength and + * OH_JSVM_GetElement can be used to iterate over result. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetAllPropertyNames(JSVM_Env env, + JSVM_Value object, + JSVM_KeyCollectionMode keyMode, + JSVM_KeyFilter keyFilter, + JSVM_KeyConversion keyConversion, + JSVM_Value* result); + +/** + * @brief This API set a property on the Object passed in. + * + * @param env: The environment that the API is invoked under. + * @param object: The object on which to set the property. + * @param key: The name of the property to set. + * @param value: The property value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetProperty(JSVM_Env env, + JSVM_Value object, + JSVM_Value key, + JSVM_Value value); + +/** + * @brief This API gets the requested property from the Object passed in. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the property. + * @param key: The name of the property to retrieve. + * @param result: The value of the property. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetProperty(JSVM_Env env, + JSVM_Value object, + JSVM_Value key, + JSVM_Value* result); + +/** + * @brief This API checks if the Object passed in has the named property. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param key: The name of the property whose existence to check. + * @param result: Whether the property exists on the object or not. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_HasProperty(JSVM_Env env, + JSVM_Value object, + JSVM_Value key, + bool* result); + +/** + * @brief This API attempts to delete the key own property from object. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param key: The name of the property to delete. + * @param result: Whether the property deletion succeeded or not. result + * can optionally be ignored by passing NULL. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DeleteProperty(JSVM_Env env, + JSVM_Value object, + JSVM_Value key, + bool* result); + +/** + * @brief This API checks if the Object passed in has the named own property. + * key must be a string or a symbol, or an error will be thrown. JSVM-API will + * not perform any conversion between data types. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param key: The name of the own property whose existence to check. + * @param result: Whether the own property exists on the object or not. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_HasOwnProperty(JSVM_Env env, + JSVM_Value object, + JSVM_Value key, + bool* result); + +/** + * @brief This method is equivalent to calling OH_JSVM_SetProperty with + * a JSVM_Value created from the string passed in as utf8Name. + * + * @param env: The environment that the API is invoked under. + * @param object: The object on which to set the property. + * @param utf8Name: The name of the property to set. + * @param value: The property value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetNamedProperty(JSVM_Env env, + JSVM_Value object, + const char* utf8name, + JSVM_Value value); + +/** + * @brief This method is equivalent to calling OH_JSVM_SetProperty with + * a JSVM_Value created from the string passed in as utf8Name. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the property. + * @param utf8Name: The name of the property to get. + * @param result: The value of the property. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetNamedProperty(JSVM_Env env, + JSVM_Value object, + const char* utf8name, + JSVM_Value* result); + +/** + * @brief This method is equivalent to calling OH_JSVM_SetProperty with + * a JSVM_Value created from the string passed in as utf8Name. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param utf8Name: The name of the property whose existence to check. + * @param result: Whether the property exists on the object or not. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_HasNamedProperty(JSVM_Env env, + JSVM_Value object, + const char* utf8name, + bool* result); + +/** + * @brief This API sets an element on the Object passed in. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to set the properties. + * @param index: The index of the property to set. + * @param value: The property value. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetElement(JSVM_Env env, + JSVM_Value object, + uint32_t index, + JSVM_Value value); + +/** + * @brief This API gets the element at the requested index. + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the property. + * @param index: The index of the property to get. + * @param result: The value of the property. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetElement(JSVM_Env env, + JSVM_Value object, + uint32_t index, + JSVM_Value* result); + +/** + * @brief This API returns if the Object passed in has an element + * at the requested index. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param index: The index of the property whose existence to check. + * @param result: Whether the property exists on the object or not. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_HasElement(JSVM_Env env, + JSVM_Value object, + uint32_t index, + bool* result); + +/** + * @brief This API attempts to delete the specified index from object. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to query. + * @param index: The index of the property to delete. + * @param result: Whether the element deletion succeeded or not. result + * can optionally be ignored by passing NULL. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DeleteElement(JSVM_Env env, + JSVM_Value object, + uint32_t index, + bool* result); + +/** + * @brief This method allows the efficient definition of multiple properties + * on a given object. The properties are defined using property descriptors. + * Given an array of such property descriptors, this API will set the properties + * on the object one at a time, as defined by DefineOwnProperty(). + * + * @param env: The environment that the API is invoked under. + * @param object: The object from which to retrieve the properties. + * @param propertyCount: The number of elements in the properties array. + * @param properties: The array of property descriptors. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DefineProperties(JSVM_Env env, + JSVM_Value object, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties); + +/** + * @brief This method freezes a given object. This prevents new properties + * from being added to it, existing properties from being removed, prevents + * changing the enumerability, configurability, or writability of existing + * properties, and prevents the values of existing properties from being changed. + * It also prevents the object's prototype from being changed. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to freeze. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ObjectFreeze(JSVM_Env env, + JSVM_Value object); + +/** + * @brief This method seals a given object. This prevents new properties + * from being added to it, as well as marking all existing properties as non-configurable. + * + * @param env: The environment that the API is invoked under. + * @param object: The object to seal. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ObjectSeal(JSVM_Env env, + JSVM_Value object); + +/** + * @brief This method allows a JavaScript function object to be called from + * a native add-on. This is the primary mechanism of calling back from the + * add-on's native code into JavaScript. + * + * @param env: The environment that the API is invoked under. + * @param recv: The this value passed to the called function. + * @param func: JSVM_Value representing the JavaScript function to be invoked. + * @param argc: The count of elements in the argv array. + * @param argv: Array of JSVM_values representing JavaScript values passed in as arguments to the function. + * @param result: JSVM_Value representing the JavaScript object returned. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CallFunction(JSVM_Env env, + JSVM_Value recv, + JSVM_Value func, + size_t argc, + const JSVM_Value* argv, + JSVM_Value* result); + + /** + * @brief This API allows an add-on author to create a function object in native + * code. This is the primary mechanism to allow calling into the add-on's native + * code from JavaScript.The newly created function is not automatically visible + * from script after this call. Instead, a property must be explicitly set on any + * object that is visible to JavaScript, in order for the function to be accessible + * from script. + * + * @param env: The environment that the API is invoked under. + * @param utf8Name: Optional name of the function encoded as UTF8. This is visible + * within JavaScript as the new function object's name property. + * @param length: The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param cb: The native function which should be called when this function + * object is invoked and data. JSVM_Callback provides more details. + * @param result: JSVM_Value representing the JavaScript function object for the newly + * created function. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateFunction(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback cb, + JSVM_Value* result); + + /** + * @brief This method is used within a callback function to retrieve details about + * the call like the arguments and the this pointer from a given callback info. + * + * @param env: The environment that the API is invoked under. + * @param cbinfo: The callback info passed into the callback function. + * @param argc: Specifies the length of the provided argv array and receives the + * actual count of arguments. argc can optionally be ignored by passing NULL. + * @param argv: C array of JSVM_values to which the arguments will be copied. If + * there are more arguments than the provided count, only the requested number of + * arguments are copied. If there are fewer arguments provided than claimed, the + * rest of argv is filled with JSVM_Value values that represent undefined. argv + * can optionally be ignored by passing NULL. + * @param thisArg: Receives the JavaScript this argument for the call. thisArg + * can optionally be ignored by passing NULL. + * @param data: Receives the data pointer for the callback. data can optionally + * be ignored by passing NULL. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetCbInfo(JSVM_Env env, + JSVM_CallbackInfo cbinfo, + size_t* argc, + JSVM_Value* argv, + JSVM_Value* thisArg, + void** data); + +/** + * @brief This API returns the new.target of the constructor call. If the + * current callback is not a constructor call, the result is NULL. + * + * @param env: The environment that the API is invoked under. + * @param cbinfo: The callback info passed into the callback function. + * @param result: The new.target of the constructor call. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetNewTarget(JSVM_Env env, + JSVM_CallbackInfo cbinfo, + JSVM_Value* result); + +/** + * @brief his method is used to instantiate a new JavaScript value using + * a given JSVM_Value that represents the constructor for the object. + * + * @param env: The environment that the API is invoked under. + * @param constructor: JSVM_Value representing the JavaScript function to be invoked as a constructor. + * @param argc: The count of elements in the argv array. + * @param argv: Array of JavaScript values as JSVM_Value representing the arguments to + * the constructor. If argc is zero this parameter may be omitted by passing in NULL. + * @param result: JSVM_Value representing the JavaScript object returned, which + * in this case is the constructed object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_NewInstance(JSVM_Env env, + JSVM_Value constructor, + size_t argc, + const JSVM_Value* argv, + JSVM_Value* result); + +/** + * @brief When wrapping a C++ class, the C++ constructor callback passed via constructor + * should be a static method on the class that calls the actual class constructor, then + * wraps the new C++ instance in a JavaScript object, and returns the wrapper object. + * + * @param env: The environment that the API is invoked under. + * @param utf8name: Name of the JavaScript constructor function. For clarity, it is + * recommended to use the C++ class name when wrapping a C++ class. + * @param length: The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param constructor: Struct include callback function that handles constructing instances of the class. + * When wrapping a C++ class, this method must be a static member with the JSVM_Callback.callback + * signature. A C++ class constructor cannot be used. + * Include Optional data to be passed to the constructor callback as the data + * property of the callback info. JSVM_Callback provides more details. + * @param propertyCount: Number of items in the properties array argument. + * @param properties: Array of property descriptors describing static and instance data + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param result: A JSVM_Value representing the constructor function for the class. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DefineClass(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value* result); + +/** + * @brief Wraps a native instance in a JavaScript object. The native instance can + * be retrieved later using OH_JSVM_Unwrap(). + * + * @param env: The environment that the API is invoked under. + * @param jsObject: The JavaScript object that will be the wrapper for the native object. + * @param nativeObject: The native instance that will be wrapped in the JavaScript object. + * @param finalizeCb: Optional native callback that can be used to free the native instance + * when the JavaScript object has been garbage-collected. + * @param finalizeHint: Optional contextual hint that is passed to the finalize callback. + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param result: Optional reference to the wrapped object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Wrap(JSVM_Env env, + JSVM_Value jsObject, + void* nativeObject, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Ref* result); + +/** + * @brief When JavaScript code invokes a method or property accessor on the class, the corresponding + * JSVM_Callback is invoked. If the callback is for an instance method or accessor, then the this + * argument to the callback is the wrapper object; the wrapped C++ instance that is the target of + * the call can be obtained then by calling OH_JSVM_Unwrap() on the wrapper object. + * + * @param env: The environment that the API is invoked under. + * @param jsObject: The object associated with the native instance. + * @param result: Pointer to the wrapped native instance. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_Unwrap(JSVM_Env env, + JSVM_Value jsObject, + void** result); + +/** + * @brief Retrieves a native instance that was previously wrapped in the JavaScript object jsObject + * using OH_JSVM_Wrap() and removes the wrapping. If a finalize callback was associated with the wrapping, + * it will no longer be called when the JavaScript object becomes garbage-collected. + * + * @param env: The environment that the API is invoked under. + * @param jsObject: The object associated with the native instance. + * @param result: Pointer to the wrapped native instance. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_RemoveWrap(JSVM_Env env, + JSVM_Value jsObject, + void** result); + +/** + * @brief Associates the value of the typeTag pointer with the JavaScript object or external. + * OH_JSVM_CheckObjectTypeTag() can then be used to compare the tag that was attached to the + * object with one owned by the addon to ensure that the object has the right type. + * If the object already has an associated type tag, this API will return JSVM_INVALID_ARG. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript object or external to be marked. + * @param typeTag: The tag with which the object is to be marked. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TypeTagObject(JSVM_Env env, + JSVM_Value value, + const JSVM_TypeTag* typeTag); + +/** + * @brief Compares the pointer given as typeTag with any that can be found on js object. + * If no tag is found on js object or, if a tag is found but it does not match typeTag, + * then result is set to false. If a tag is found and it matches typeTag, then result is set to true. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript object or external whose type tag to examine. + * @param typeTag: The tag with which to compare any tag found on the object. + * @param result: Whether the type tag given matched the type tag on the object. false is also returned + * if no type tag was found on the object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CheckObjectTypeTag(JSVM_Env env, + JSVM_Value value, + const JSVM_TypeTag* typeTag, + bool* result); + +/** + * @brief This API can be called multiple times on a single JavaScript object. + * + * @param env: The environment that the API is invoked under. + * @param jsObject: The JavaScript object to which the native data will be attached. + * @param finalizeData: Optional data to be passed to finalizeCb. + * @param finalizeCb: Native callback that will be used to free the native data when the + * JavaScript object has been garbage-collected. JSVM_Finalize provides more details. + * @param finalizeHint: Optional contextual hint that is passed to the finalize callback. + * @param result: Optional reference to the JavaScript object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_AddFinalizer(JSVM_Env env, + JSVM_Value jsObject, + void* finalizeData, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Ref* result); + +/** + * @brief This API returns the highest JSVM-API version supported by the JSVM runtime. + * + * JSVM-API is planned to be additive such that newer releases of JSVM may support additional + * API functions. In order to allow an addon to use a newer function when running with versions + * of JSVM that support it, while providing fallback behavior when running with JSVM + * versions that don't support it. + * @param env: The environment that the API is invoked under. + * @param result: The highest version of JSVM-API supported. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetVersion(JSVM_Env env, + uint32_t* result); + +/** + * @brief Return information of the VM. + * + * @param result: The information of the VM. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetVMInfo(JSVM_VMInfo* result); + +/** + * @brief This function gives V8 an indication of the amount of externally + * allocated memory that is kept alive by JavaScript objects (i.e. a JavaScript + * object that points to its own memory allocated by a native addon). Registering + * externally allocated memory will trigger global garbage collections more often + * than it would otherwise. + * + * @param env: The environment that the API is invoked under. + * @param changeInBytes: The change in externally allocated memory that is kept + * alive by JavaScript objects. + * @param result: The adjusted value + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_AdjustExternalMemory(JSVM_Env env, + int64_t changeInBytes, + int64_t* result); + +/** + * @brief This function notifies the VM that the system is running low on memory + * and optionally triggers a garbage collection. + * + * @param env: The environment that the API is invoked under. + * @param level: The memory pressure level set to the current VM. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_MemoryPressureNotification(JSVM_Env env, + JSVM_MemoryPressureLevel level); + +/** + * @brief This API creates a deferred object and a JavaScript promise. + * + * @param env: The environment that the API is invoked under. + * @param deferred: A newly created deferred object which can later be + * passed to OH_JSVM_ResolveDeferred() or OH_JSVM_RejectDeferred() to resolve + * resp. reject the associated promise. + * @param promise: The JavaScript promise associated with the deferred object. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreatePromise(JSVM_Env env, + JSVM_Deferred* deferred, + JSVM_Value* promise); + +/** + * @brief This API resolves a JavaScript promise by way of the deferred object with + * which it is associated. Thus, it can only be used to resolve JavaScript promises + * for which the corresponding deferred object is available. This effectively means + * that the promise must have been created using OH_JSVM_CreatePromise() and the deferred + * object returned from that call must have been retained in order to be passed to this API. + * + * @param env: The environment that the API is invoked under. + * @param deferred: The deferred object whose associated promise to resolve. + * @param resolution: The value with which to resolve the promise. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ResolveDeferred(JSVM_Env env, + JSVM_Deferred deferred, + JSVM_Value resolution); + +/** + * @brief This API rejects a JavaScript promise by way of the deferred object with + * which it is associated. Thus, it can only be used to reject JavaScript promises + * for which the corresponding deferred object is available. This effectively means + * that the promise must have been created using OH_JSVM_CreatePromise() and the deferred + * object returned from that call must have been retained in order to be passed to this API. + * + * @param env: The environment that the API is invoked under. + * @param deferred: The deferred object whose associated promise to resolve. + * @param rejection: The value with which to reject the promise. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_RejectDeferred(JSVM_Env env, + JSVM_Deferred deferred, + JSVM_Value rejection); + +/** + * @brief This API return indicating whether promise is a native promise object. + * @param env: The environment that the API is invoked under. + * @param value: The value to examine + * @param isPromise: Flag indicating whether promise is a native promise object + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsPromise(JSVM_Env env, + JSVM_Value value, + bool* isPromise); + +/** + * @brief This API parses a JSON string and returns it as value if successful. + * @param env: The environment that the API is invoked under. + * @param jsonString: The string to parse. + * @param result: The parse value if successful. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_JsonParse(JSVM_Env env, + JSVM_Value jsonString, + JSVM_Value* result); + +/** + * @brief This API stringifies the object and returns it as string if successful. + * @param env: The environment that the API is invoked under. + * @param jsonObject: The object to stringify. + * @param result: The string if successfully stringified. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_JsonStringify(JSVM_Env env, + JSVM_Value jsonObject, + JSVM_Value* result); + +/** + * @brief This API create the startup snapshot of the VM. + * @param vm: The environment that the API is invoked under. + * @param contextCount: The object to stringify. + * @param contexts: The array of contexts to add to the snapshot. + * @param blobData: The snapshot data. + * @param blobSize: The size of snapshot data. + * @return Returns JSVM_OK if the API succeeded. + * @since 11 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateSnapshot(JSVM_VM vm, + size_t contextCount, + const JSVM_Env* contexts, + const char** blobData, + size_t* blobSize); + +/** + * @brief This function returns a set of statistics data of the heap of the VM. + * + * @param vm: The VM whose heap statistics are returned. + * @param result: The heap statistics data. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetHeapStatistics(JSVM_VM vm, + JSVM_HeapStatistics* result); + +/** + * @brief This function creates and starts a CPU profiler. + * + * @param vm: The VM to start CPU profiler for. + * @param result: The pointer to the CPU profiler. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_StartCpuProfiler(JSVM_VM vm, + JSVM_CpuProfiler* result); + +/** + * @brief This function stops the CPU profiler and output to the stream. + * + * @param vm: THe VM to start CPU profiler for. + * @param profiler: The CPU profiler to stop. + * @param stream: The output stream callback for receiving the data. + * @param streamData: Optional data to be passed to the stream callback. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_StopCpuProfiler(JSVM_VM vm, + JSVM_CpuProfiler profiler, + JSVM_OutputStream stream, + void* streamData); + +/** + * @brief This funciton takes the current heap snapshot and output to the stream. + * + * @param vm: The VM whose heap snapshot is taken. + * @param stream: The output stream callback for receiving the data. + * @param streamData: Optional data to be passed to the stream callback. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TakeHeapSnapshot(JSVM_VM vm, + JSVM_OutputStream stream, + void* streamData); + +/** + * @brief This functiong activates insepctor on host and port. + * + * @param env: The environment that the API is invoked under. + * @param host: The host to listen to for inspector connections. + * @param port: The port to listen to for inspector connections. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenInspector(JSVM_Env env, + const char* host, + uint16_t port); + +/** + * @brief This function attempts to close all remaining inspector connections. + * + * @param env: The environment that the API is invoked under. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CloseInspector(JSVM_Env env); + +/** + * @brief This function will block until a client (existing or connected later) + * has sent Runtime.runIfWaitingForDebugger command. + * + * @param env: The environment that the API is invoked under. + * @param breakNextLine: Whether break on the next line of JavaScript code. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_WaitForDebugger(JSVM_Env env, + bool breakNextLine); + +/** + * @brief When packaging C++classes, the C++constructor callback passed through the constructor + * triggers the corresponding callback function when getter, setter, call, and other + * behaviors occur on the instance object, handles the user's custom behavior, and then wraps + * the new C++instance in a JavaScript object and returns the wrapper object. + * + * @param env: The environment that the API is invoked under. + * @param utf8name: Name of the JavaScript constructor function. For clarity, it is + * recommended to use the C++ class name when wrapping a C++ class. + * @param length: The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param constructor: Struct include callback function that handles constructing instances of the class. + * When wrapping a C++ class, this method must be a static member with the JSVM_Callback.callback + * signature. A C++ class constructor cannot be used. + * Include Optional data to be passed to the constructor callback as the data + * property of the callback info. JSVM_Callback provides more details. + * @param propertyCount: Number of items in the properties array argument. + * @param properties: Array of property descriptors describing static and instance data + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param propertyHandlerCfg: The instance object triggers the corresponding callback function. + * @param callAsFunctionCallback: Calling an instance object as a function will trigger this callback. + * @param result: A JSVM_Value representing the constructor function for the class. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_PropertyHandlerCfg propertyHandlerCfg, + JSVM_Callback callAsFunctionCallback, + JSVM_Value* result); + +/** + * @brief Determines whether the current thread holds the lock for the specified environment. + * Only threads that hold locks can use the environment. + * + * @param env: The environment that the API is invoked under. + * @param isLocked: Flag indicating whether the current thread holds the lock for the specified environment. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsLocked(JSVM_Env env, + bool* isLocked); + +/** + * @brief Acquire the lock for the specified environment. Only threads that hold locks can use the environment. + * + * @param env: The environment that the API is invoked under. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_AcquireLock(JSVM_Env env); + +/** + * @brief Release the lock for the specified environment. Only threads that hold locks can use the environment. + * + * @param env: The environment that the API is invoked under. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseLock(JSVM_Env env); + +/** + * @brief Starts the running of the task queue inside the VM. + * This task queue can be executed by an external event loop. + * + * @param env: The VM instance on which to start the task queue. + * @param result: Whether the task queue was successfully started. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_PumpMessageLoop(JSVM_VM vm, + bool* result); + +/** + * @brief Check to see if there are any microtasks waiting in the queue, and if there are, execute them. + * + * @param env: The VM instance on which to check microtasks. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_PerformMicrotaskCheckpoint(JSVM_VM vm); + +/** + * @brief This API checks if the value passed in is callable. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isCallable: Whether the given value is callable. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsCallable(JSVM_Env env, + JSVM_Value value, + bool* isCallable); + +/** + * @brief This API checks if the value passed in is undefined. + * This equals to `value === undefined` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isUndefined: Whether the given value is Undefined. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsUndefined(JSVM_Env env, + JSVM_Value value, + bool* isUndefined); + +/** + * @brief This API checks if the value passed in is a null object. + * This equals to `value === null` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isNull: Whether the given value is Null. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsNull(JSVM_Env env, + JSVM_Value value, + bool* isNull); + +/** + * @brief This API checks if the value passed in is either a null or an undefined object. + * This is equivalent to `value == null` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isNullOrUndefined: Whether the given value is Null or Undefined. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsNullOrUndefined(JSVM_Env env, + JSVM_Value value, + bool* isNullOrUndefined); + +/** + * @brief This API checks if the value passed in is a boolean. + * This equals to `typeof value === 'boolean'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isBoolean: Whether the given value is Boolean. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBoolean(JSVM_Env env, + JSVM_Value value, + bool* isBoolean); + +/** + * @brief This API checks if the value passed in is a number. + * This equals to `typeof value === 'number'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isNumber: Whether the given value is Number. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsNumber(JSVM_Env env, + JSVM_Value value, + bool* isNumber); + +/** + * @brief This API checks if the value passed in is a string. + * This equals to `typeof value === 'string'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isString: Whether the given value is String. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsString(JSVM_Env env, + JSVM_Value value, + bool* isString); + +/** + * @brief This API checks if the value passed in is a symbol. + * This equals to `typeof value === 'symbol'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isSymbol: Whether the given value is Symbol. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsSymbol(JSVM_Env env, + JSVM_Value value, + bool* isSymbol); + +/** + * @brief This API checks if the value passed in is a function. + * This equals to `typeof value === 'function'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isFunction: Whether the given value is Function. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsFunction(JSVM_Env env, + JSVM_Value value, + bool* isFunction); + +/** + * @brief This API checks if the value passed in is an object. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isObject: Whether the given value is Object. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsObject(JSVM_Env env, + JSVM_Value value, + bool* isObject); + +/** + * @brief This API checks if the value passed in is a bigInt. + * This equals to `typeof value === 'bigint'` in JS. + * + * @param env: The VM instance on which to check microtasks. + * @param value: The JavaScript value to check. + * @param isBigInt: Whether the given value is BigInt. + * @return Only returns JSVM_OK, because this API will not trigger any exception. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBigInt(JSVM_Env env, + JSVM_Value value, + bool* isBigInt); + +/** + * @brief This API checks if the value passed in is a constructor. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param isConstructor: Whether the given value is Constructor. + * @return Returns JSVM_OK if the API succeeded. Returns JSVM_INVALID_ARG if the API failed. + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_IsConstructor(JSVM_Env env, + JSVM_Value value, + bool* isConstructor); + +/** + * @brief This API returns the JavaScript value of the regular expression + * corresponding to the input. + * The interface may throw an exception. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript string to convert to a regular expression. + * @param flags: Regular expression flag bits. + * @param result: A JSVM_Value representing a JavaScript RegExp. + * @return Only returns JSVM function's result code. + * {@link JSVM_OK } If the API succeeded.\n + * {@link JSVM_INVALID_ARG } If the input parameter is invalid.\n + * {@link JSVM_STRING_EXPECTED } If the value of 'value' is not a string.\n + * {@link JSVM_GENERIC_FAILURE } If create RegExp failed.\n + * {@link JSVM_PENDING_EXCEPTION } If the API throws an exception during runtime.\n + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_CreateRegExp(JSVM_Env env, + JSVM_Value value, + JSVM_RegExpFlags flags, + JSVM_Value* result); + +/** + * @brief This API returns a JSVM-API value corresponding to a JavaScript Map type. + * + * @param env: The environment that the API is invoked under. + * @param result: A JSVM_Value representing a JavaScript Map. + * @return Returns JSVM_OK if the API succeeded. Returns JSVM_INVALID_ARG if the API failed. + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_CreateMap(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API checks if the value passed in is a Map. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param isMap: Whether the given value is Map. + * @return Returns JSVM_OK if the API succeeded. Returns JSVM_INVALID_ARG if the API failed. + * @since 12 + */ +JSVM_Status JSVM_CDECL OH_JSVM_IsMap(JSVM_Env env, + JSVM_Value value, + bool* isMap); + +/** + * @brief This API returns a JSVM-API value corresponding to a JavaScript Set type. + * + * @param env: The environment that the API is invoked under. + * @param result: A JSVM_Value representing a JavaScript Set. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateSet(JSVM_Env env, + JSVM_Value* result); + +/** + * @brief This API checks if the value passed in is a Set. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param isSet: Whether the given value is Set. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsSet(JSVM_Env env, + JSVM_Value value, + bool* isSet); + +/** + * @brief This API returns the Object prototype. + * + * @param env: The environment that the API is invoked under. + * @param object: JSVM_Value representing JavaScript Object whose prototype to return. + * @param result: JSVM_Value representing prototype of the given object. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ObjectGetPrototypeOf(JSVM_Env env, + JSVM_Value object, + JSVM_Value* result); + +/** + * @brief This API set the prototype on the Object passed in. + * + * @param env: The environment that the API is invoked under. + * @param object: The object on which to set the prototype. + * @param prototype: The prototype value. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env, + JSVM_Value object, + JSVM_Value prototype); + +/** + * @brief This API implements the abstract operation `ToBigInt()`. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to coerce. + * @param result: JSVM_Value representing the coerced JavaScript BigInt. + * @return Returns JSVM_OK if the API succeeded. Returns JSVM_BIGINT_EXPECTED if the + * JavaScript value fails to coerce. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CoerceToBigInt(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + +/** + * @brief This API checks if the value passed in is a JavaScript RegExp object. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given value is RegExp. + * @return Returns JSVM_OK if the API succeeded. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsRegExp(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Creates a function with a given script as its body. + * + * @param env: The environment that the API is invoked under. + * @param funcName: A string containing the function's name. Pass NULL to create an anonymous function. + * @param length: The length of the funcName in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param argc: The count of elements in the argv array. + * @param argv: Array of JSVM_Values representing JavaScript strings passed in as arguments to the function. + * @param script: A JavaScript string containing the script to use as the function's body. + * @param result: JSVM_Value representing the JavaScript function object for the newly + * created function. + * @return Returns JSVM_OK if the API succeeded. Returns JSVM_GENERIC_FAILURE if the input script fails to + * be compiled. + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateFunctionWithScript(JSVM_Env env, + const char* funcName, + size_t length, + size_t argc, + const JSVM_Value* argv, + JSVM_Value script, + JSVM_Value* result); + +/** + * @brief This function keep persistently save a JSVM_Script and extend its lifecycle + * beyond the current scope. + * + * @param env: The environment that the API is invoked under. + * @param script: A JavaScript string containing the script to be retained. + * @return Returns JSVM functions result code + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the script is empty or already retained. \n + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script); + +/** + * @brief This function release the script retained by OH_JSVM_RetainScript + * + * @param env: The environment that the API is invoked under. + * @param script: A JavaScript string containing the script to be retained. + * @return Returns JSVM functions result code + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the script is empty or not retained. \n + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script); + +/** + * @brief This function activates insepctor with pid and alias it. + * + * @param env: The environment that the API is invoked under. + * @param pid: A process id to identify the inspector connection. + * @param name: An alias for the inspector that under a specific pid. + * default name is jsvm if a nullptr is passed in. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_PENDING_EXCEPTION } if an exception occurs.\n + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_OpenInspectorWithName(JSVM_Env env, + int pid, + const char* name); + +/** + * @brief Compile WebAssembly bytecode into a WebAssembly module. + * If WebAssembly cache provided, deserialization will be performed. + * + * @param env: The environment that the API is invoked under. + * @param wasmBytecode: WebAssembly bytecode. + * @param wasmBytecodeLength: WebAssembly bytecode length in byte. + * @param cacheData: Optional WebAssembly cache. + * @param cacheDataLength: Optional WebAssembly cache length in byte. + * @param cacheRejected: Output parameter representing whether the provided cacheData is rejected. + * @param wasmModule: Output parameter representing compiled WebAssembly module. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of env, wasmBytecode is NULL, or data length is invalid.\n + * Returns {@link JSVM_GENERIC_FAILURE } if compile failed.\n + * Returns {@link JSVM_PENDING_EXCEPTION } if an exception occurs.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CompileWasmModule(JSVM_Env env, + const uint8_t *wasmBytecode, + size_t wasmBytecodeLength, + const uint8_t *cacheData, + size_t cacheDataLength, + bool *cacheRejected, + JSVM_Value *wasmModule); + +/** + * @brief Compile the function with the specified index in the WebAssembly module + * into the specified optimization level. + * + * @param env: The environment that the API is invoked under. + * @param wasmModule: The WebAssembly module to which the function to compiled belongs. + * @param functionIndex: The index of the function to be compiled, should never be out of range. + * @param optLevel: Optimization level the function will be compiled with. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if env is NULL, or wasmModule is NULL or is not a WebAssembly module.\n + * Returns {@link JSVM_GENERIC_FAILURE } if functionIndex out of range or compile failed.\n + * Returns {@link JSVM_PENDING_EXCEPTION } if an exception occurs.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CompileWasmFunction(JSVM_Env env, + JSVM_Value wasmModule, + uint32_t functionIndex, + JSVM_WasmOptLevel optLevel); + +/** + * @brief Check whether the given JSVM_Value is a WebAssembly module. + * + * @param env: The environment that the API is invoked under. + * @param value: The JavaScript value to check. + * @param result: Whether the given value is a WebAssembly module. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of the input arguments is NULL.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsWasmModuleObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Create cache for compiled WebAssembly module. + * + * @param env: The environment that the API is invoked under. + * @param wasmModule: The compiled WebAssembly module. + * @param data: Output parameter representing generated WebAssembly module cache. + * @param length: Output parameter representing byte length of generated WebAssembly module cache. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of the input arguments is NULL.\n + * Returns {@link JSVM_GENERIC_FAILURE } if create wasm cache failed.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateWasmCache(JSVM_Env env, + JSVM_Value wasmModule, + const uint8_t** data, + size_t* length); + +/** + * @brief Release cache data with specified cache type. + * + * @param env: The environment that the API is invoked under. + * @param cacheData: The cache data to be released, double free is undefined behaviors. + * @param cacheType: The type of cache data. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL or cacheType is illegal.\n + * + * @since 12 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseCache(JSVM_Env env, + const uint8_t* cacheData, + JSVM_CacheType cacheType); + +EXTERN_C_END + +/** @} */ +#endif /* ARK_RUNTIME_JSVM_JSVM_H */ + diff --git a/jsvm/interface/kits/jsvm_types.h b/jsvm/interface/kits/jsvm_types.h new file mode 100644 index 0000000000000000000000000000000000000000..139fec1b6568546217b6b611ca6f6ef18d8d163e --- /dev/null +++ b/jsvm/interface/kits/jsvm_types.h @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef ARK_RUNTIME_JSVM_JSVM_TYPE_H +#define ARK_RUNTIME_JSVM_JSVM_TYPE_H + +/** + * @addtogroup JSVM + * @{ + * + * @brief Provides the standard JavaScript engine capabilities. + * + * Provides API to Provide independent, standard, and complete JavaScript engine capabilities for developers, + * including managing the engine lifecycle, compiling and running JS code, implementing JS/C++ cross language calls, + * and taking snapshots. + * + * @since 11 + */ + +/** + * @file jsvm_types.h + * + * @brief Provides the JSVM API type define. + * + * Provides API to Provide independent, standard, and complete JavaScript engine capabilities for developers, + * including managing the engine lifecycle, compiling and running JS code, implementing JS/C++ cross language calls, + * and taking snapshots. + * @library libjsvm.so + * @syscap SystemCapability.ArkCompiler.JSVM + * @since 11 + */ + +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) +#include // NOLINT(modernize-deprecated-headers) + +#if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) +typedef uint16_t char16_t; +#endif + +#ifndef JSVM_CDECL +#ifdef _WIN32 +#define JSVM_CDECL __cdecl +#else +#define JSVM_CDECL +#endif +#endif + +/** + * @brief To represent a JavaScript VM instance. + * + * @since 11 + */ +typedef struct JSVM_VM__* JSVM_VM; + +/** + * @brief To represent a JavaScript VM scope. + * + * @since 11 + */ +typedef struct JSVM_VMScope__* JSVM_VMScope; + +/** + * @brief To represent a JavaScript VM environment scope. + * + * @since 11 + */ +typedef struct JSVM_EnvScope__* JSVM_EnvScope; + +/** + * @brief To represent a JavaScript code. + * + * @since 11 + */ +typedef struct JSVM_Script__* JSVM_Script; + +/** + * @brief To represent a JavaScript VM instance. + * + * @since 11 + */ +typedef struct JSVM_Env__* JSVM_Env; + +/** + * @brief To represent a JavaScript profiler. + * + * @since 12 + */ +typedef struct JSVM_CpuProfiler__* JSVM_CpuProfiler; + +/** + * @brief To represent a JavaScript VM environment. + * + * @since 11 + */ +typedef struct JSVM_Value__* JSVM_Value; + +/** + * @brief To represent a JavaScript value references. + * + * @since 11 + */ +typedef struct JSVM_Ref__* JSVM_Ref; + +/** + * @brief To represent a JavaScript VM handle scope. + * + * @since 11 + */ +typedef struct JSVM_HandleScope__* JSVM_HandleScope; + +/** + * @brief To represent a JavaScript VM escapable handle scope. + * + * @since 11 + */ +typedef struct JSVM_EscapableHandleScope__* JSVM_EscapableHandleScope; + +/** + * @brief To represent a JavaScript VM callback additional information. + * + * @since 11 + */ +typedef struct JSVM_CallbackInfo__* JSVM_CallbackInfo; + +/** + * @brief To represent a JavaScript VM value deferred. + * + * @since 11 + */ +typedef struct JSVM_Deferred__* JSVM_Deferred; + + +/** + * @brief Callback function pointer and data for user-provided native function which are to exposed to js via JSVM-API. + * + * @since 11 + */ +typedef struct { + JSVM_Value(JSVM_CDECL* callback)(JSVM_Env env, + JSVM_CallbackInfo info); + void* data; +} JSVM_CallbackStruct; + +/** + * @brief Function pointer type for user-provided native function which are to exposed to js via JSVM-API. + * + * @since 11 + */ +typedef JSVM_CallbackStruct* JSVM_Callback; + +/** + * @brief Function pointer type for add-on provided function that allow the user to be notified. + * + * @since 11 + */ +typedef void(JSVM_CDECL* JSVM_Finalize)(JSVM_Env env, + void* finalizeData, + void* finalizeHint); + +/** + * @brief Function pointer type for callback of ASCII output stream. + * + * @since 12 + */ +typedef bool(JSVM_CDECL* JSVM_OutputStream)(const char* data, + int size, + void* streamData); + +/** + * @brief JSVM_PropertyAttributes are flag used to control the behavior of properties set on a js object. + * + * @since 11 + */ +typedef enum { + /** No explicit attributes are set on the property. */ + JSVM_DEFAULT = 0, + /** The property is writable. */ + JSVM_WRITABLE = 1 << 0, + /** The property is enumeable. */ + JSVM_ENUMERABLE = 1 << 1, + /** The property is configurable. */ + JSVM_CONFIGURABLE = 1 << 2, + /** Used to mark the receiver of a native method need not be checked. + * If JSVM_NO_RECEIVER_CHECK is not set, the method only accept instance of the defined class as receiver, + * Otherwise Exception "Type Error: Illegal Ivocation" will be throw into JSVM. + */ + JSVM_NO_RECEIVER_CHECK = 1 << 3, + /** Used with OH_JSVM_DefineClass to distinguish static properties from instance properties. */ + JSVM_STATIC = 1 << 10, + /** Default for class methods. */ + JSVM_DEFAULT_METHOD = JSVM_WRITABLE | JSVM_CONFIGURABLE, + /** Class method with no receiver check*/ + JSVM_METHOD_NO_RECEIVER_CHECK = JSVM_DEFAULT_METHOD | JSVM_NO_RECEIVER_CHECK, + /** Default for object properties, like in JS obj[prop]. */ + JSVM_DEFAULT_JSPROPERTY = JSVM_WRITABLE | JSVM_ENUMERABLE | JSVM_CONFIGURABLE, + /** Object properties with no receiver check*/ + JSVM_JSPROPERTY_NO_RECEIVER_CHECK = JSVM_DEFAULT_JSPROPERTY | JSVM_NO_RECEIVER_CHECK, +} JSVM_PropertyAttributes; + +/** + * @brief Describes the type of a JSVM_Value. + * + * @since 11 + */ +typedef enum { + /** undefined type. */ + JSVM_UNDEFINED, + /** null type. */ + JSVM_NULL, + /** boolean type. */ + JSVM_BOOLEAN, + /** number type. */ + JSVM_NUMBER, + /** string type. */ + JSVM_STRING, + /** symbol type. */ + JSVM_SYMBOL, + /** object type. */ + JSVM_OBJECT, + /** function type. */ + JSVM_FUNCTION, + /** external type. */ + JSVM_EXTERNAL, + /** bigint type. */ + JSVM_BIGINT, +} JSVM_ValueType; + +/** + * @brief Describes the type of a typedarray. + * + * @since 11 + */ +typedef enum { + /** int8 type. */ + JSVM_INT8_ARRAY, + /** uint8 type. */ + JSVM_UINT8_ARRAY, + /** uint8 clamped type. */ + JSVM_UINT8_CLAMPED_ARRAY, + /** int16 type. */ + JSVM_INT16_ARRAY, + /** uint16 type. */ + JSVM_UINT16_ARRAY, + /** int32 type. */ + JSVM_INT32_ARRAY, + /** uint32 type. */ + JSVM_UINT32_ARRAY, + /** float32 type. */ + JSVM_FLOAT32_ARRAY, + /** float64 type. */ + JSVM_FLOAT64_ARRAY, + /** bigint64 type. */ + JSVM_BIGINT64_ARRAY, + /** biguint64 type. */ + JSVM_BIGUINT64_ARRAY, +} JSVM_TypedarrayType; + +/** + * @brief Integral status code indicating the success or failure of a JSVM-API call. + * + * @since 11 + */ +typedef enum { + /** success status. */ + JSVM_OK, + /** invalidarg status. */ + JSVM_INVALID_ARG, + /** object expected status. */ + JSVM_OBJECT_EXPECTED, + /** string expected status. */ + JSVM_STRING_EXPECTED, + /** name expected status. */ + JSVM_NAME_EXPECTED, + /** function expected status. */ + JSVM_FUNCTION_EXPECTED, + /** number expected status. */ + JSVM_NUMBER_EXPECTED, + /** boolean expected status. */ + JSVM_BOOLEAN_EXPECTED, + /** array expected status. */ + JSVM_ARRAY_EXPECTED, + /** generic failure status. */ + JSVM_GENERIC_FAILURE, + /** pending exception status. */ + JSVM_PENDING_EXCEPTION, + /** cancelled status. */ + JSVM_CANCELLED, + /** escape called twice status. */ + JSVM_ESCAPE_CALLED_TWICE, + /** handle scope mismatch status. */ + JSVM_HANDLE_SCOPE_MISMATCH, + /** callback scope mismatch status. */ + JSVM_CALLBACK_SCOPE_MISMATCH, + /** queue full status. */ + JSVM_QUEUE_FULL, + /** closing status. */ + JSVM_CLOSING, + /** bigint expected status. */ + JSVM_BIGINT_EXPECTED, + /** date expected status. */ + JSVM_DATE_EXPECTED, + /** arraybuffer expected status. */ + JSVM_ARRAYBUFFER_EXPECTED, + /** detachable arraybuffer expected status. */ + JSVM_DETACHABLE_ARRAYBUFFER_EXPECTED, + /** would deadlock status. */ + JSVM_WOULD_DEADLOCK, + /** no external buffers allowed status. */ + JSVM_NO_EXTERNAL_BUFFERS_ALLOWED, + /** cannot run +js status. */ + JSVM_CANNOT_RUN_JS, +} JSVM_Status; + +/** + * @brief limits the range of collected properties.. + * + * @since 11 + */ +typedef enum { + /** will include all keys of the objects's prototype chain as well. */ + JSVM_KEY_INCLUDE_PROTOTYPES, + /** limits the collected properties to the given object only. */ + JSVM_KEY_OWN_ONLY +} JSVM_KeyCollectionMode; + +/** + * @brief Property filter bits. They can be or'ed to build a composite filter.. + * + * @since 11 + */ +typedef enum { + /** key all properties. */ + JSVM_KEY_ALL_PROPERTIES = 0, + /** key writable. */ + JSVM_KEY_WRITABLE = 1, + /** key enumerable. */ + JSVM_KEY_ENUMERABLE = 1 << 1, + /** key configurable. */ + JSVM_KEY_CONFIGURABLE = 1 << 2, + /** key skip strings. */ + JSVM_KEY_SKIP_STRINGS = 1 << 3, + /** key skip symbols. */ + JSVM_KEY_SKIP_SYMBOLS = 1 << 4 +} JSVM_KeyFilter; + +/** + * @brief key conversion select. + * + * @since 11 + */ +typedef enum { + /** will return numbers for integer indices. */ + JSVM_KEY_KEEP_NUMBERS, + /** will convert integer indices to strings. */ + JSVM_KEY_NUMBERS_TO_STRINGS +} JSVM_KeyConversion; + +/** + * @brief Memory pressure level. + * + * @since 11 + */ +typedef enum { + /** none pressure. */ + JSVM_MEMORY_PRESSURE_LEVEL_NONE, + /** moderate pressure. */ + JSVM_MEMORY_PRESSURE_LEVEL_MODERATE, + /** critical pressure. */ + JSVM_MEMORY_PRESSURE_LEVEL_CRITICAL, +} JSVM_MemoryPressureLevel; + +/** + * + * @brief Compile mode + * + * @since 12 + */ +typedef enum { + /** default mode. */ + JSVM_COMPILE_MODE_DEFAULT, + /** consume code cache. */ + JSVM_COMPILE_MODE_CONSUME_CODE_CACHE, + /** apply eager compile. */ + JSVM_COMPILE_MODE_EAGER_COMPILE, + /** preset for compile profile. */ + JSVM_COMPILE_MODE_PRODUCE_COMPILE_PROFILE, + /** consume compile profile. */ + JSVM_COMPILE_MODE_CONSUME_COMPILE_PROFILE, +} JSVM_CompileMode; + +/** + * @brief Compile option id + * + * @since 12 + */ +typedef enum { + /** compile mode. */ + JSVM_COMPILE_MODE, + /** code cache content. */ + JSVM_COMPILE_CODE_CACHE, + /** script origin. */ + JSVM_COMPILE_SCRIPT_ORIGIN, + /** compile profile content. */ + JSVM_COMPILE_COMPILE_PROFILE, + /** switch for source map support. */ + JSVM_COMPILE_ENABLE_SOURCE_MAP, +} JSVM_CompileOptionId; + +/** + * @brief Heap statisics. + * + * @since 12 + */ +typedef struct { + /** the size of the total heap. */ + size_t totalHeapSize; + /** the executable size of the total heap. */ + size_t totalHeapSizeExecutable; + /** the physical size of the total heap. */ + size_t totalPhysicalSize; + /** the available size of the total heap. */ + size_t totalAvailableSize; + /** used size of the heap. */ + size_t usedHeapSize; + /** heap size limit. */ + size_t heapSizeLimit; + /** memory requested by the heap. */ + size_t mallocedMemory; + /** heap-requested external memory. */ + size_t externalMemory; + /** peak memory requested by the heap. */ + size_t peakMallocedMemory; + /** the number of native contexts. */ + size_t numberOfNativeContexts; + /** the number of detached contexts. */ + size_t numberOfDetachedContexts; + /** the size of the total global handles. */ + size_t totalGlobalHandlesSize; + /** the size of the used global handles. */ + size_t usedGlobalHandlesSize; +} JSVM_HeapStatistics; + +/** + * @brief Init the JavaScript VM with init option. + * + * @since 11 + */ +typedef struct { + /** + * Optional nullptr-terminated array of raw adddresses in the embedder that the + * VM can match against during serialization and use for deserialization. This + * array and its content must stay valid for the entire lifetime of the VM + * instance. + */ + const intptr_t* externalReferences; + + /** + * Flags for the VM. IF removeFlags is true, recognized flags will be removed + * from (argc, argv). Note that these flags are specific to VM. + * They are mainly used for development. Do not include them in production as + * they might not take effect if the VM is different from the development + * environment. + */ + int* argc; + /** argv . */ + char** argv; + /** remove flags. */ + bool removeFlags; +} JSVM_InitOptions; + +/** + * @brief Create the JavaScript VM with init option. + * + * @since 11 + */ +typedef struct { + /** optional limits of memory use of the vm. */ + size_t maxOldGenerationSize; + /** optional limits of memory use of the vm. */ + size_t maxYoungGenerationSize; + /** optional limits of memory use of the vm. */ + size_t initialOldGenerationSize; + /** optional limits of memory use of the vm. */ + size_t initialYoungGenerationSize; + /** Optional startup snapshot data. */ + const char* snapshotBlobData; + /** Optional size of the startup snapshot data. */ + size_t snapshotBlobSize; + /** Whether the VM is used for creating snapshot. */ + bool isForSnapshotting; +} JSVM_CreateVMOptions; + +/** + * @brief JavaScript VM info. + * + * @since 11 + */ +typedef struct { + /** The highest API version this VM supports. */ + uint32_t apiVersion; + /** The engine name implementing the VM. */ + const char* engine; + /** The version of the VM. */ + const char* version; + /** The cached data version tag. */ + uint32_t cachedDataVersionTag; +} JSVM_VMInfo; + +/** + * @brief Property descriptor. + * + * @since 11 + */ +typedef struct { + /** Optional string describing the key for the property, encoded as UTF8. + * One of utf8name or name must be provided for the property. + */ + const char* utf8name; + /** Optional value that points to a JavaScript string or symbol to be used as the key for the property. */ + JSVM_Value name; + /** Set this to make the property descriptor object's value property to be + * a JavaScript function represented by method. + */ + JSVM_Callback method; + /** A function to call when a get access of the property is performed. */ + JSVM_Callback getter; + /** A function to call when a set access of the property is performed. */ + JSVM_Callback setter; + /** The value that's retrieved by a get access of the property if the property is a data property. */ + JSVM_Value value; + /** The attributes associated with the particular property. */ + JSVM_PropertyAttributes attributes; +} JSVM_PropertyDescriptor; + +/** + * @brief JSVM-API uses both return values and JavaScript exceptions for error handling + * @since 11 + */ +typedef struct { + /** UTF8-encoded string containing a VM-neutral description of the error. */ + const char* errorMessage; + /** Reserved for VM-specific error details. This is currently not implemented for any VM. */ + void* engineReserved; + /** VM-specific error code. This is currently not implemented for any VM. */ + uint32_t engineErrorCode; + /** The JSVM-API status code that originated with the last error. */ + JSVM_Status errorCode; +} JSVM_ExtendedErrorInfo; + +/** + * @brief A 128-bit value stored as two unsigned 64-bit integers. + * It serves as a UUID with which JavaScript objects or externals can be "tagged" + * in order to ensure that they are of a certain type. + * + * @since 11 + */ +typedef struct { + /** lower type. */ + uint64_t lower; + /** upper type. */ + uint64_t upper; +} JSVM_TypeTag; + +/** + * @brief When the getter, setter, call, etc. behavior occurs on the object, the corresponding + * the corresponding callback will be triggered. + * + * @since 12 + */ +typedef struct { + /** A callback function triggered by getting a named property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericNamedPropertyGetterCallback)(JSVM_Env env, + JSVM_Value name, + JSVM_Value thisArg, + JSVM_Value namedPropertyData); + + /** A callback function triggered by setting a named property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericNamedPropertySetterCallback)(JSVM_Env env, + JSVM_Value name, + JSVM_Value property, + JSVM_Value thisArg, + JSVM_Value namedPropertyData); + + /** A callback function triggered by deleting a named property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericNamedPropertyDeleterCallback)(JSVM_Env env, + JSVM_Value name, + JSVM_Value thisArg, + JSVM_Value namedPropertyData); + + /** A callback function triggered by getting all named properties requests on an object. */ + JSVM_Value(JSVM_CDECL* genericNamedPropertyEnumeratorCallback)(JSVM_Env env, + JSVM_Value thisArg, + JSVM_Value namedPropertyData); + + /** A callback function triggered by getting an indexed property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericIndexedPropertyGetterCallback)(JSVM_Env env, + JSVM_Value index, + JSVM_Value thisArg, + JSVM_Value indexedPropertyData); + + /** A callback function triggered by setting an indexed property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericIndexedPropertySetterCallback)(JSVM_Env env, + JSVM_Value index, + JSVM_Value property, + JSVM_Value thisArg, + JSVM_Value indexedPropertyData); + + /** A callback function triggered by deleting an indexed property of an instance object. */ + JSVM_Value(JSVM_CDECL* genericIndexedPropertyDeleterCallback)(JSVM_Env env, + JSVM_Value index, + JSVM_Value thisArg, + JSVM_Value indexedPropertyData); + + /** A callback function triggered by getting all indexed properties requests on an object. */ + JSVM_Value(JSVM_CDECL* genericIndexedPropertyEnumeratorCallback)(JSVM_Env env, + JSVM_Value thisArg, + JSVM_Value indexedPropertyData); + + /** data will be utilized by the named property callbacks in this struct. */ + JSVM_Value namedPropertyData; + + /** data will be utilized by the indexed property callbacks in this struct. */ + JSVM_Value indexedPropertyData; +} JSVM_PropertyHandlerConfigurationStruct; + +/** + * @brief The function pointer type of the callback provided by the instance object, which triggers the + * corresponding callback when getter, setter, call, and other behaviors occur on the object. + * + * @since 12 + */ +typedef JSVM_PropertyHandlerConfigurationStruct* JSVM_PropertyHandlerCfg; + +/** + * + * @brief Source code information. + * + * @since 12 + */ +typedef struct { + /** Sourcemap url. */ + const char* sourceMapUrl; + /** Resource name. */ + const char* resourceName; + /** Resource line offset. */ + size_t resourceLineOffset; + /** Resource column offset. */ + size_t resourceColumnOffset; +} JSVM_ScriptOrigin; + +/** + * @brief Compile Options + * + * @since 12 + */ +typedef struct { + /** compile option id. */ + JSVM_CompileOptionId id; + /** option content. */ + union { + /** ptr type. */ + void *ptr; + /** int type. */ + int num; + /** bool type. */ + bool boolean; + } content; +} JSVM_CompileOptions; + +/** + * @brief code cache passed with JSVM_COMPILE_CODE_CACHE + * + * @since 12 + */ +typedef struct { + /** cache pointer. */ + uint8_t *cache; + /** length. */ + size_t length; +} JSVM_CodeCache; + +/** + * @brief WebAssembly function optimization level + * + * @since 12 + */ +typedef enum { + /** baseline optimization level. */ + JSVM_WASM_OPT_BASELINE = 10, + /** high optimization level. */ + JSVM_WASM_OPT_HIGH = 20, +} JSVM_WasmOptLevel; + +/** + * @brief Cache data type + * + * @since 12 + */ +typedef enum { + /** js code cache, generated by OH_JSVM_CreateCodeCache */ + JSVM_CACHE_TYPE_JS, + /** WebAssembly cache, generated by OH_JSVM_CreateWasmCache */ + JSVM_CACHE_TYPE_WASM, +} JSVM_CacheType; + +/** + * @brief compile profile passed with JSVM_COMPILE_COMPILE_PROFILE + * + * @since 12 + */ +typedef const struct { + /** profile pointer. */ + int *profile; + /** length. */ + size_t length; +} JSVM_CompileProfile; + +/** + * @brief Regular expression flag bits. They can be or'ed to enable a set of flags. + * + * @since 12 + */ +typedef enum { + /** None mode. */ + JSVM_REGEXP_NONE = 0, + /** Global mode. */ + JSVM_REGEXP_GLOBAL = 1 << 0, + /** Ignore Case mode. */ + JSVM_REGEXP_IGNORE_CASE = 1 << 1, + /** Multiline mode. */ + JSVM_REGEXP_MULTILINE = 1 << 2, + /** Sticky mode. */ + JSVM_REGEXP_STICKY = 1 << 3, + /** Unicode mode. */ + JSVM_REGEXP_UNICODE = 1 << 4, + /** dotAll mode. */ + JSVM_REGEXP_DOT_ALL = 1 << 5, + /** Linear mode. */ + JSVM_REGEXP_LINEAR = 1 << 6, + /** Has Indices mode. */ + JSVM_REGEXP_HAS_INDICES = 1 << 7, + /** Unicode Sets mode. */ + JSVM_REGEXP_UNICODE_SETS = 1 << 8, +} JSVM_RegExpFlags; + +/** + * @brief initialization flag + * + * @since 12 + */ +typedef enum { + /** initialize with zero. */ + JSVM_ZERO_INITIALIZED, + /** leave uninitialized. */ + JSVM_UNINITIALIZED, +} JSVM_InitializedFlag; +/** @} */ +#endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */ diff --git a/jsvm/src/inspector/inspector_socket.cc b/jsvm/src/inspector/inspector_socket.cc new file mode 100644 index 0000000000000000000000000000000000000000..ec97804d1ccb64f7c0199d93be1bff26f6f24be2 --- /dev/null +++ b/jsvm/src/inspector/inspector_socket.cc @@ -0,0 +1,856 @@ +#include "inspector_socket.h" + +#include +#include +#include + +#include "inspector/inspector_utils.h" +#include "llhttp.h" +#include "openssl/sha.h" // Sha-1 hash + +#define ACCEPT_KEY_LENGTH Base64EncodeSize(20) + +#define DUMP_READS 0 +#define DUMP_WRITES 0 + +namespace jsvm { +namespace inspector { + +class TcpHolder { +public: + static void DisconnectAndDispose(TcpHolder* holder); + using Pointer = DeleteFnPtr; + + static Pointer Accept(uv_stream_t* server, InspectorSocket::DelegatePointer delegate); + void SetHandler(ProtocolHandler* handler); + int WriteRaw(const std::vector& buffer, uv_write_cb writeCb); + uv_tcp_t* GetTcp() + { + return &tcp; + } + InspectorSocket::Delegate* GetDelegate(); + +private: + static TcpHolder* From(void* handle) + { + return jsvm::inspector::ContainerOf(&TcpHolder::tcp, reinterpret_cast(handle)); + } + static void OnClosed(uv_handle_t* handle); + static void OnDataReceivedCb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); + explicit TcpHolder(InspectorSocket::DelegatePointer delegate); + ~TcpHolder() = default; + void ReclaimUvBuf(const uv_buf_t* buf, ssize_t read); + + uv_tcp_t tcp; + const InspectorSocket::DelegatePointer delegate; + ProtocolHandler* handler; + std::vector buffer; +}; + +class ProtocolHandler { +public: + ProtocolHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp); + + virtual void AcceptUpgrade(const std::string& acceptKey) = 0; + virtual void OnData(std::vector* data) = 0; + virtual void OnEof() = 0; + virtual void Write(const std::vector data) = 0; + virtual void CancelHandshake() = 0; + + std::string GetHost() const; + + InspectorSocket* GetInspectorSocket() + { + return inspector; + } + virtual void Shutdown() = 0; + +protected: + virtual ~ProtocolHandler() = default; + int WriteRaw(const std::vector& buffer, uv_write_cb writeCb); + InspectorSocket::Delegate* GetDelegate(); + + InspectorSocket* const inspector; + TcpHolder::Pointer tcp; +}; + +namespace { + +#if DUMP_READS || DUMP_WRITES +static void dump_hex(const char* buf, size_t len) +{ + const char* ptr = buf; + const char* end = ptr + len; + const char* cptr; + char c; + int i; + + while (ptr < end) { + cptr = ptr; + for (i = 0; i < 16 && ptr < end; i++) { + printf("%2.2X ", static_cast(*(ptr++))); + } + for (i = 72 - (i * 4); i > 0; i--) { + printf(" "); + } + for (i = 0; i < 16 && cptr < end; i++) { + c = *(cptr++); + printf("%c", (c > 0x19) ? c : '.'); + } + printf("\n"); + } + printf("\n\n"); +} +#endif + +class WriteRequest { +public: + WriteRequest(ProtocolHandler* handler, const std::vector& buffer) + : handler(handler), storage(buffer), req(uv_write_t()), buf(uv_buf_init(storage.data(), storage.size())) + {} + + static WriteRequest* from_write_req(uv_write_t* req) + { + return jsvm::inspector::ContainerOf(&WriteRequest::req, req); + } + + static void Cleanup(uv_write_t* req, int status) + { + delete WriteRequest::from_write_req(req); + } + + ProtocolHandler* const handler; + std::vector storage; + uv_write_t req; + uv_buf_t buf; +}; + +void allocate_buffer(uv_handle_t* stream, size_t len, uv_buf_t* buf) +{ + *buf = uv_buf_init(new char[len], len); +} + +static void remove_from_beginning(std::vector* buffer, size_t count) +{ + buffer->erase(buffer->begin(), buffer->begin() + count); +} + +static const char CLOSE_FRAME[] = { '\x88', '\x00' }; + +enum WsDecodeResult { FRAME_OK, FRAME_INCOMPLETE, FRAME_CLOSE, FRAME_ERROR }; + +static void generate_accept_string(const std::string& clientKey, char (*buffer)[ACCEPT_KEY_LENGTH]) +{ + // Magic string from websockets spec. + static constexpr char WS_MAGIC[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + std::string input(clientKey + WS_MAGIC); + char hash[SHA_DIGEST_LENGTH]; + USE(SHA1(reinterpret_cast(input.data()), input.size(), + reinterpret_cast(hash))); + jsvm::inspector::Base64Encode(hash, sizeof(hash), *buffer, sizeof(*buffer)); +} + +static std::string TrimPort(const std::string& host) +{ + size_t lastColonPos = host.rfind(':'); + if (lastColonPos == std::string::npos) { + return host; + } + size_t bracket = host.rfind(']'); + if (bracket == std::string::npos || lastColonPos > bracket) { + return host.substr(0, lastColonPos); + } + return host; +} + +static bool IsIPAddress(const std::string& host) +{ + // To avoid DNS rebinding attacks, we are aware of the following requirements: + // * the host name must be an IP address (CVE-2018-7160, CVE-2022-32212), + // * the IP address must be routable (hackerone.com/reports/1632921), and + // * the IP address must be formatted unambiguously (CVE-2022-43548). + + // The logic below assumes that the string is null-terminated, so ensure that + // we did not somehow end up with null characters within the string. + if (host.find('\0') != std::string::npos) { + return false; + } + + // All IPv6 addresses must be enclosed in square brackets, and anything + // enclosed in square brackets must be an IPv6 address. + if (host.length() >= 4 && host.front() == '[' && host.back() == ']') { + // INET6_ADDRSTRLEN is the maximum length of the dual format (including the + // terminating null character), which is the longest possible representation + // of an IPv6 address: xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd + if (host.length() - 2 >= INET6_ADDRSTRLEN) { + return false; + } + + // Annoyingly, libuv's implementation of inet_pton() deviates from other + // implementations of the function in that it allows '%' in IPv6 addresses. + if (host.find('%') != std::string::npos) { + return false; + } + + // Parse the IPv6 address to ensure it is syntactically valid. + char ipv6Str[INET6_ADDRSTRLEN]; + std::copy(host.begin() + 1, host.end() - 1, ipv6Str); + ipv6Str[host.length()] = '\0'; + unsigned char ipv6[sizeof(struct in6_addr)]; + if (uv_inet_pton(AF_INET6, ipv6Str, ipv6) != 0) { + return false; + } + + // The only non-routable IPv6 address is ::/128. It should not be necessary + // to explicitly reject it because it will still be enclosed in square + // brackets and not even macOS should make DNS requests in that case, but + // history has taught us that we cannot be careful enough. + // Note that RFC 4291 defines both "IPv4-Compatible IPv6 Addresses" and + // "IPv4-Mapped IPv6 Addresses", which means that there are IPv6 addresses + // (other than ::/128) that represent non-routable IPv4 addresses. However, + // this translation assumes that the host is interpreted as an IPv6 address + // in the first place, at which point DNS rebinding should not be an issue. + if (std::all_of(ipv6, ipv6 + sizeof(ipv6), [](auto b) { return b == 0; })) { + return false; + } + + // It is a syntactically valid and routable IPv6 address enclosed in square + // brackets. No client should be able to misinterpret this. + return true; + } + + // Anything not enclosed in square brackets must be an IPv4 address. It is + // important here that inet_pton() accepts only the so-called dotted-decimal + // notation, which is a strict subset of the so-called numbers-and-dots + // notation that is allowed by inet_aton() and inet_addr(). This subset does + // not allow hexadecimal or octal number formats. + unsigned char ipv4[sizeof(struct in_addr)]; + if (uv_inet_pton(AF_INET, host.c_str(), ipv4) != 0) { + return false; + } + + // The only strictly non-routable IPv4 address is 0.0.0.0, and macOS will make + // DNS requests for this IP address, so we need to explicitly reject it. In + // fact, we can safely reject all of 0.0.0.0/8 (see Section 3.2 of RFC 791 and + // Section 3.2.1.3 of RFC 1122). + // Note that inet_pton() stores the IPv4 address in network byte order. + if (ipv4[0] == 0) { + return false; + } + + // It is a routable IPv4 address in dotted-decimal notation. + return true; +} + +// Constants for hybi-10 frame format. + +typedef int OpCode; + +const OpCode K_OP_CODE_CONTINUATION = 0x0; +const OpCode K_OP_CODE_TEXT = 0x1; +const OpCode K_OP_CODE_BINARY = 0x2; +const OpCode K_OP_CODE_CLOSE = 0x8; +const OpCode K_OP_CODE_PING = 0x9; +const OpCode K_OP_CODE_PONG = 0xA; + +const unsigned char K_FINAL_BIT = 0x80; +const unsigned char K_RESERVED_1_BIT = 0x40; +const unsigned char K_RESERVED_2_BIT = 0x20; +const unsigned char K_RESERVED_3_BIT = 0x10; +const unsigned char K_OP_CODE_MASK = 0xF; +const unsigned char K_MASK_BIT = 0x80; +const unsigned char K_PAYLOAD_LENGTH_MASK = 0x7F; + +const size_t K_MAX_SINGLE_BYTE_PAYLOAD_LENGTH = 125; +const size_t K_TWO_BYTE_PAYLOAD_LENGTH_FIELD = 126; +const size_t K_EIGHT_BYTE_PAYLOAD_LENGTH_FIELD = 127; +const size_t K_MASKING_KEY_WIDTH_IN_BYTES = 4; + +static std::vector encode_frame_hybi17(const std::vector& message) +{ + std::vector frame; + OpCode opCode = K_OP_CODE_TEXT; + frame.push_back(K_FINAL_BIT | opCode); + const size_t dataLength = message.size(); + if (dataLength <= K_MAX_SINGLE_BYTE_PAYLOAD_LENGTH) { + frame.push_back(static_cast(dataLength)); + } else if (dataLength <= 0xFFFF) { + frame.push_back(K_TWO_BYTE_PAYLOAD_LENGTH_FIELD); + frame.push_back((dataLength & 0xFF00) >> 8); + frame.push_back(dataLength & 0xFF); + } else { + frame.push_back(K_EIGHT_BYTE_PAYLOAD_LENGTH_FIELD); + char extendedPayloadLength[8]; + size_t remaining = dataLength; + // Fill the length into extendedPayloadLength in the network byte order. + for (int i = 0; i < 8; ++i) { + extendedPayloadLength[7 - i] = remaining & 0xFF; + remaining >>= 8; + } + frame.insert(frame.end(), extendedPayloadLength, extendedPayloadLength + 8); + CHECK_EQ(0, remaining); + } + frame.insert(frame.end(), message.begin(), message.end()); + return frame; +} + +static WsDecodeResult DecodeFrameHybi17(const std::vector& buffer, + bool clientFrame, + int* bytesConsumed, + std::vector* output, + bool* compressed) +{ + *bytesConsumed = 0; + if (buffer.size() < 2) { + return FRAME_INCOMPLETE; + } + + auto it = buffer.begin(); + + unsigned char firstByte = *it++; + unsigned char secondByte = *it++; + + bool final = (firstByte & K_FINAL_BIT) != 0; + bool reserved1 = (firstByte & K_RESERVED_1_BIT) != 0; + bool reserved2 = (firstByte & K_RESERVED_2_BIT) != 0; + bool reserved3 = (firstByte & K_RESERVED_3_BIT) != 0; + int opCode = firstByte & K_OP_CODE_MASK; + bool masked = (secondByte & K_MASK_BIT) != 0; + *compressed = reserved1; + if (!final || reserved2 || reserved3) { + return FRAME_ERROR; // Only compression extension is supported. + } + + bool closed = false; + switch (opCode) { + case K_OP_CODE_CLOSE: + closed = true; + break; + case K_OP_CODE_TEXT: + break; + case K_OP_CODE_BINARY: // We don't support binary frames yet. + case K_OP_CODE_CONTINUATION: // We don't support binary frames yet. + case K_OP_CODE_PING: // We don't support binary frames yet. + case K_OP_CODE_PONG: // We don't support binary frames yet. + default: + return FRAME_ERROR; + } + + // In Hybi-17 spec client MUST mask its frame. + if (clientFrame && !masked) { + return FRAME_ERROR; + } + + uint64_t payloadLength64 = secondByte & K_PAYLOAD_LENGTH_MASK; + if (payloadLength64 > K_MAX_SINGLE_BYTE_PAYLOAD_LENGTH) { + int extendedPayloadLengthSize; + if (payloadLength64 == K_TWO_BYTE_PAYLOAD_LENGTH_FIELD) { + extendedPayloadLengthSize = 2; + } else if (payloadLength64 == K_EIGHT_BYTE_PAYLOAD_LENGTH_FIELD) { + extendedPayloadLengthSize = 8; + } else { + return FRAME_ERROR; + } + if ((buffer.end() - it) < extendedPayloadLengthSize) { + return FRAME_INCOMPLETE; + } + payloadLength64 = 0; + for (int i = 0; i < extendedPayloadLengthSize; ++i) { + payloadLength64 <<= 8; + payloadLength64 |= static_cast(*it++); + } + } + + static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFull; + static const size_t maxLength = SIZE_MAX; + if (payloadLength64 > maxPayloadLength || payloadLength64 > maxLength - K_MASKING_KEY_WIDTH_IN_BYTES) { + // WebSocket frame length too large. + return FRAME_ERROR; + } + size_t payloadLength = static_cast(payloadLength64); + + if (buffer.size() - K_MASKING_KEY_WIDTH_IN_BYTES < payloadLength) { + return FRAME_INCOMPLETE; + } + + std::vector::const_iterator maskingKey = it; + std::vector::const_iterator payload = it + K_MASKING_KEY_WIDTH_IN_BYTES; + for (size_t i = 0; i < payloadLength; ++i) { // Unmask the payload. + output->insert(output->end(), payload[i] ^ maskingKey[i % K_MASKING_KEY_WIDTH_IN_BYTES]); + } + + size_t pos = it + K_MASKING_KEY_WIDTH_IN_BYTES + payloadLength - buffer.begin(); + *bytesConsumed = pos; + return closed ? FRAME_CLOSE : FRAME_OK; +} + +// WS protocol +class WsHandler : public ProtocolHandler { +public: + WsHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp) + : ProtocolHandler(inspector, std::move(tcp)), onCloseSent(&WsHandler::WaitForCloseReply), + onCloseReceived(&WsHandler::CloseFrameReceived), dispose(false) + {} + + void AcceptUpgrade(const std::string& acceptKey) override {} + void CancelHandshake() override {} + + void OnEof() override + { + tcp.reset(); + if (dispose) { + delete this; + } + } + + void OnData(std::vector* data) override + { + // 1. Parse. + int processed = 0; + do { + processed = ParseWsFrames(*data); + // 2. Fix the data size & length + if (processed > 0) { + remove_from_beginning(data, processed); + } + } while (processed > 0 && !data->empty()); + } + + void Write(const std::vector data) override + { + std::vector output = encode_frame_hybi17(data); + WriteRaw(output, WriteRequest::Cleanup); + } + +protected: + void Shutdown() override + { + if (tcp) { + dispose = true; + SendClose(); + } else { + delete this; + } + } + +private: + using Callback = void (WsHandler::*)(); + + static void OnCloseFrameWritten(uv_write_t* req, int status) + { + WriteRequest* wr = WriteRequest::from_write_req(req); + WsHandler* handler = static_cast(wr->handler); + delete wr; + Callback cb = handler->onCloseSent; + (handler->*cb)(); + } + + void WaitForCloseReply() + { + onCloseReceived = &WsHandler::OnEof; + } + + void SendClose() + { + WriteRaw(std::vector(CLOSE_FRAME, CLOSE_FRAME + sizeof(CLOSE_FRAME)), OnCloseFrameWritten); + } + + void CloseFrameReceived() + { + onCloseSent = &WsHandler::OnEof; + SendClose(); + } + + int ParseWsFrames(const std::vector& buffer) + { + int bytesConsumed = 0; + std::vector output; + bool compressed = false; + + WsDecodeResult r = DecodeFrameHybi17(buffer, true /* clientFrame */, &bytesConsumed, &output, &compressed); + // Compressed frame means client is ignoring the headers and misbehaves + if (compressed || r == FRAME_ERROR) { + OnEof(); + bytesConsumed = 0; + } else if (r == FRAME_CLOSE) { + (this->*onCloseReceived)(); + bytesConsumed = 0; + } else if (r == FRAME_OK) { + GetDelegate()->OnWsFrame(output); + } + return bytesConsumed; + } + + Callback onCloseSent; + Callback onCloseReceived; + bool dispose; +}; + +// HTTP protocol +class HttpEvent { +public: + HttpEvent(const std::string& path, bool upgrade, bool isGET, const std::string& wsKey, const std::string& host) + : path(path), upgrade(upgrade), isGET(isGET), wsKey(wsKey), host(host) + {} + + std::string path; + bool upgrade; + bool isGET; + std::string wsKey; + std::string host; +}; + +class HttpHandler : public ProtocolHandler { +public: + explicit HttpHandler(InspectorSocket* inspector, TcpHolder::Pointer tcp) + : ProtocolHandler(inspector, std::move(tcp)), parsingValue(false) + { + llhttp_init(&parser, HTTP_REQUEST, &parserSettings); + llhttp_settings_init(&parserSettings); + parserSettings.on_header_field = OnHeaderField; + parserSettings.on_header_value = OnHeaderValue; + parserSettings.on_message_complete = OnMessageComplete; + parserSettings.on_url = OnPath; + } + + void AcceptUpgrade(const std::string& acceptKey) override + { + char acceptString[ACCEPT_KEY_LENGTH]; + generate_accept_string(acceptKey, &acceptString); + const char acceptWsPrefix[] = "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: "; + const char acceptWsSuffix[] = "\r\n\r\n"; + std::vector reply(acceptWsPrefix, acceptWsPrefix + sizeof(acceptWsPrefix) - 1); + reply.insert(reply.end(), acceptString, acceptString + sizeof(acceptString)); + reply.insert(reply.end(), acceptWsSuffix, acceptWsSuffix + sizeof(acceptWsSuffix) - 1); + if (WriteRaw(reply, WriteRequest::Cleanup) >= 0) { + inspector->SwitchProtocol(new WsHandler(inspector, std::move(tcp))); + } else { + tcp.reset(); + } + } + + void CancelHandshake() override + { + const char handshakeFailedResponse[] = "HTTP/1.0 400 Bad Request\r\n" + "Content-Type: text/html; charset=UTF-8\r\n\r\n" + "WebSockets request was expected\r\n"; + WriteRaw(std::vector(handshakeFailedResponse, + handshakeFailedResponse + sizeof(handshakeFailedResponse) - 1), + ThenCloseAndReportFailure); + } + + void OnEof() override + { + tcp.reset(); + } + + void OnData(std::vector* data) override + { + llhttp_errno_t err; + err = llhttp_execute(&parser, data->data(), data->size()); + + if (err == HPE_PAUSED_UPGRADE) { + err = HPE_OK; + llhttp_resume_after_upgrade(&parser); + } + data->clear(); + if (err != HPE_OK) { + CancelHandshake(); + } + // Event handling may delete *this + std::vector httpEvents; + std::swap(httpEvents, events); + for (const HttpEvent& event : httpEvents) { + if (!IsAllowedHost(event.host) || !event.isGET) { + CancelHandshake(); + return; + } else if (!event.upgrade) { + GetDelegate()->OnHttpGet(event.host, event.path); + } else if (event.wsKey.empty()) { + CancelHandshake(); + return; + } else { + GetDelegate()->OnSocketUpgrade(event.host, event.path, event.wsKey); + } + } + } + + void Write(const std::vector data) override + { + WriteRaw(data, WriteRequest::Cleanup); + } + +protected: + void Shutdown() override + { + delete this; + } + +private: + static void ThenCloseAndReportFailure(uv_write_t* req, int status) + { + ProtocolHandler* handler = WriteRequest::from_write_req(req)->handler; + WriteRequest::Cleanup(req, status); + handler->GetInspectorSocket()->SwitchProtocol(nullptr); + } + + static int OnHeaderValue(llhttp_t* parser, const char* at, size_t length) + { + HttpHandler* handler = From(parser); + handler->parsingValue = true; + handler->headers[handler->currentHeader].append(at, length); + return 0; + } + + static int OnHeaderField(llhttp_t* parser, const char* at, size_t length) + { + HttpHandler* handler = From(parser); + if (handler->parsingValue) { + handler->parsingValue = false; + handler->currentHeader.clear(); + } + handler->currentHeader.append(at, length); + return 0; + } + + static int OnPath(llhttp_t* parser, const char* at, size_t length) + { + HttpHandler* handler = From(parser); + handler->path.append(at, length); + return 0; + } + + static HttpHandler* From(llhttp_t* parser) + { + return jsvm::inspector::ContainerOf(&HttpHandler::parser, parser); + } + + static int OnMessageComplete(llhttp_t* parser) + { + // Event needs to be fired after the parser is done. + HttpHandler* handler = From(parser); + handler->events.emplace_back(handler->path, parser->upgrade, parser->method == HTTP_GET, + handler->HeaderValue("Sec-WebSocket-Key"), handler->HeaderValue("Host")); + handler->path = ""; + handler->parsingValue = false; + handler->headers.clear(); + handler->currentHeader = ""; + return 0; + } + + std::string HeaderValue(const std::string& header) const + { + bool headerFound = false; + std::string value; + for (const auto& header_value : headers) { + if (jsvm::inspector::StringEqualNoCaseN(header_value.first.data(), header.data(), header.length())) { + if (headerFound) { + return ""; + } + value = header_value.second; + headerFound = true; + } + } + return value; + } + + bool IsAllowedHost(const std::string& hostWithPort) const + { + std::string host = TrimPort(hostWithPort); + return host.empty() || IsIPAddress(host) || jsvm::inspector::StringEqualNoCase(host.data(), "localhost"); + } + + bool parsingValue; + llhttp_t parser; + llhttp_settings_t parserSettings; + std::vector events; + std::string currentHeader; + std::map headers; + std::string path; +}; + +} // namespace + +// Any protocol +ProtocolHandler::ProtocolHandler(InspectorSocket* inspector, TcpHolder::Pointer tcpParam) + : inspector(inspector), tcp(std::move(tcpParam)) +{ + CHECK_NOT_NULL(tcp); + tcp->SetHandler(this); +} + +int ProtocolHandler::WriteRaw(const std::vector& buffer, uv_write_cb writeCb) +{ + return tcp->WriteRaw(buffer, writeCb); +} + +InspectorSocket::Delegate* ProtocolHandler::GetDelegate() +{ + return tcp->GetDelegate(); +} + +std::string ProtocolHandler::GetHost() const +{ + char ip[INET6_ADDRSTRLEN]; + sockaddr_storage addr; + int len = sizeof(addr); + int err = uv_tcp_getsockname(tcp->GetTcp(), reinterpret_cast(&addr), &len); + if (err != 0) { + return ""; + } + if (addr.ss_family == AF_INET6) { + const sockaddr_in6* v6 = reinterpret_cast(&addr); + err = uv_ip6_name(v6, ip, sizeof(ip)); + } else { + const sockaddr_in* v4 = reinterpret_cast(&addr); + err = uv_ip4_name(v4, ip, sizeof(ip)); + } + if (err != 0) { + return ""; + } + return ip; +} + +// RAII uv_tcp_t wrapper +TcpHolder::TcpHolder(InspectorSocket::DelegatePointer delegate) : tcp(), delegate(std::move(delegate)), handler(nullptr) +{} + +// static +TcpHolder::Pointer TcpHolder::Accept(uv_stream_t* server, InspectorSocket::DelegatePointer delegate) +{ + TcpHolder* result = new TcpHolder(std::move(delegate)); + uv_stream_t* tcp = reinterpret_cast(&result->tcp); + int err = uv_tcp_init(server->loop, &result->tcp); + if (err == 0) { + err = uv_accept(server, tcp); + } + if (err == 0) { + err = uv_read_start(tcp, allocate_buffer, OnDataReceivedCb); + } + if (err == 0) { + return TcpHolder::Pointer(result); + } else { + delete result; + return nullptr; + } +} + +void TcpHolder::SetHandler(ProtocolHandler* protocalHandler) +{ + handler = protocalHandler; +} + +int TcpHolder::WriteRaw(const std::vector& buffer, uv_write_cb writeCb) +{ +#if DUMP_WRITES + printf("%s (%ld bytes):\n", __FUNCTION__, buffer.size()); + dump_hex(buffer.data(), buffer.size()); + printf("\n"); +#endif + + // Freed in write_request_cleanup + WriteRequest* wr = new WriteRequest(handler, buffer); + uv_stream_t* stream = reinterpret_cast(&tcp); + int err = uv_write(&wr->req, stream, &wr->buf, 1, writeCb); + if (err < 0) { + delete wr; + } + return err < 0; +} + +InspectorSocket::Delegate* TcpHolder::GetDelegate() +{ + return delegate.get(); +} + +// static +void TcpHolder::OnClosed(uv_handle_t* handle) +{ + delete From(handle); +} + +void TcpHolder::OnDataReceivedCb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) +{ +#if DUMP_READS + if (nread >= 0) { + printf("%s (%ld bytes)\n", __FUNCTION__, nread); + dump_hex(buf->base, nread); + } else { + printf("[%s:%d] %s\n", __FUNCTION__, __LINE__, uv_err_name(nread)); + } +#endif + TcpHolder* holder = From(tcp); + holder->ReclaimUvBuf(buf, nread); + if (nread < 0 || nread == UV_EOF) { + holder->handler->OnEof(); + } else { + holder->handler->OnData(&holder->buffer); + } +} + +// static +void TcpHolder::DisconnectAndDispose(TcpHolder* holder) +{ + uv_handle_t* handle = reinterpret_cast(&holder->tcp); + uv_close(handle, OnClosed); +} + +void TcpHolder::ReclaimUvBuf(const uv_buf_t* buf, ssize_t read) +{ + if (read > 0) { + buffer.insert(buffer.end(), buf->base, buf->base + read); + } + delete[] buf->base; +} + +InspectorSocket::~InspectorSocket() = default; + +// static +void InspectorSocket::Shutdown(ProtocolHandler* handler) +{ + handler->Shutdown(); +} + +// static +InspectorSocket::Pointer InspectorSocket::Accept(uv_stream_t* server, DelegatePointer delegate) +{ + auto tcp = TcpHolder::Accept(server, std::move(delegate)); + if (tcp) { + InspectorSocket* inspector = new InspectorSocket(); + inspector->SwitchProtocol(new HttpHandler(inspector, std::move(tcp))); + return InspectorSocket::Pointer(inspector); + } else { + return InspectorSocket::Pointer(nullptr); + } +} + +void InspectorSocket::AcceptUpgrade(const std::string& wsKey) +{ + protocolHandler->AcceptUpgrade(wsKey); +} + +void InspectorSocket::CancelHandshake() +{ + protocolHandler->CancelHandshake(); +} + +std::string InspectorSocket::GetHost() +{ + return protocolHandler->GetHost(); +} + +void InspectorSocket::SwitchProtocol(ProtocolHandler* handler) +{ + protocolHandler.reset(std::move(handler)); +} + +void InspectorSocket::Write(const char* data, size_t len) +{ + protocolHandler->Write(std::vector(data, data + len)); +} + +} // namespace inspector +} // namespace jsvm diff --git a/jsvm/src/inspector/inspector_socket.h b/jsvm/src/inspector/inspector_socket.h new file mode 100644 index 0000000000000000000000000000000000000000..266549078b0023009c46a0eb8f05329739f3c203 --- /dev/null +++ b/jsvm/src/inspector/inspector_socket.h @@ -0,0 +1,54 @@ +#ifndef SRC_INSPECTOR_SOCKET_H_ +#define SRC_INSPECTOR_SOCKET_H_ + +#include +#include + +#include "inspector_utils.h" +#include "uv.h" + +namespace jsvm { +namespace inspector { + +class ProtocolHandler; + +// HTTP Wrapper around a uv_tcp_t +class InspectorSocket { +public: + class Delegate { + public: + virtual void OnHttpGet(const std::string& host, const std::string& path) = 0; + virtual void OnSocketUpgrade(const std::string& host, + const std::string& path, + const std::string& acceptKey) = 0; + virtual void OnWsFrame(const std::vector& frame) = 0; + virtual ~Delegate() = default; + }; + + using DelegatePointer = std::unique_ptr; + using Pointer = std::unique_ptr; + + static Pointer Accept(uv_stream_t* server, DelegatePointer delegate); + + ~InspectorSocket(); + + void AcceptUpgrade(const std::string& acceptKey); + void CancelHandshake(); + void Write(const char* data, size_t len); + void SwitchProtocol(ProtocolHandler* handler); + std::string GetHost(); + + InspectorSocket(const InspectorSocket&) = delete; + InspectorSocket& operator=(const InspectorSocket&) = delete; + +private: + static void Shutdown(ProtocolHandler*); + InspectorSocket() = default; + + DeleteFnPtr protocolHandler; +}; + +} // namespace inspector +} // namespace jsvm + +#endif // SRC_INSPECTOR_SOCKET_H_ diff --git a/jsvm/src/inspector/inspector_socket_server.cc b/jsvm/src/inspector/inspector_socket_server.cc new file mode 100644 index 0000000000000000000000000000000000000000..7a5c6f929108f4f4a96f42451bd0fa1cb8e5ef4e --- /dev/null +++ b/jsvm/src/inspector/inspector_socket_server.cc @@ -0,0 +1,606 @@ +#include "inspector_socket_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jsvm_version.h" +#include "uv.h" +#include "zlib.h" + +namespace jsvm { +namespace inspector { + +// Function is declared in inspector_io.h so the rest of the node does not +// depend on inspector_socket_server.h +std::string FormatWsAddress(const std::string& host, int port, const std::string& targetId, bool includeProtocol); +namespace { + +static const uint8_t PROTOCOL_JSON[] = { +#include "v8_inspector_protocol_json.h" // NOLINT(build/include_order) +}; + +void Escape(std::string* string) +{ + for (char& c : *string) { + c = (c == '\"' || c == '\\') ? '_' : c; + } +} + +std::string FormatHostPort(const std::string& host, int port) +{ + // Host is valid (socket was bound) so colon means it's a v6 IP address + bool v6 = host.find(':') != std::string::npos; + std::ostringstream url; + if (v6) { + url << '['; + } + url << host; + if (v6) { + url << ']'; + } + url << ':' << port; + return url.str(); +} + +std::string FormatAddress(const std::string& host, const std::string& targetId, bool includeProtocol) +{ + std::ostringstream url; + if (includeProtocol) { + url << "ws://"; + } + url << host << '/' << targetId; + return url.str(); +} + +std::string MapToString(const std::map& object) +{ + bool first = true; + std::ostringstream json; + json << "{\n"; + for (const auto& nameValue : object) { + if (!first) { + json << ",\n"; + } + first = false; + json << " \"" << nameValue.first << "\": \""; + json << nameValue.second << "\""; + } + json << "\n} "; + return json.str(); +} + +std::string MapsToString(const std::vector>& array) +{ + bool first = true; + std::ostringstream json; + json << "[ "; + for (const auto& object : array) { + if (!first) { + json << ", "; + } + first = false; + json << MapToString(object); + } + json << "]\n\n"; + return json.str(); +} + +const char* MatchPathSegment(const char* path, const char* expected) +{ + size_t len = strlen(expected); + if (StringEqualNoCaseN(path, expected, len)) { + if (path[len] == '/') { + return path + len + 1; + } + if (path[len] == '\0') { + return path + len; + } + } + return nullptr; +} + +void SendHttpResponse(InspectorSocket* socket, const std::string& response, int code) +{ + const char headers[] = "HTTP/1.0 %d OK\r\n" + "Content-Type: application/json; charset=UTF-8\r\n" + "Cache-Control: no-cache\r\n" + "Content-Length: %zu\r\n" + "\r\n"; + char header[sizeof(headers) + 20]; + int headerLen = snprintf(header, sizeof(header), headers, code, response.size()); + socket->Write(header, headerLen); + socket->Write(response.data(), response.size()); +} + +void SendVersionResponse(InspectorSocket* socket) +{ + std::map response; + response["Browser"] = "jsvm/" JSVM_VERSION_STRING; + response["Protocol-Version"] = "1.1"; + SendHttpResponse(socket, MapToString(response), 200); +} + +void SendHttpNotFound(InspectorSocket* socket) +{ + SendHttpResponse(socket, "", 404); +} + +void SendProtocolJson(InspectorSocket* socket) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + CHECK_EQ(Z_OK, inflateInit(&strm)); + static const size_t K_DECOMPRESSED_SIZE = + PROTOCOL_JSON[0] * 0x10000u + PROTOCOL_JSON[1] * 0x100u + PROTOCOL_JSON[2]; + strm.next_in = const_cast(PROTOCOL_JSON + 3); + strm.avail_in = sizeof(PROTOCOL_JSON) - 3; + std::string data(K_DECOMPRESSED_SIZE, '\0'); + strm.next_out = reinterpret_cast(data.data()); + strm.avail_out = data.size(); + CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH)); + CHECK_EQ(0, strm.avail_out); + CHECK_EQ(Z_OK, inflateEnd(&strm)); + SendHttpResponse(socket, data, 200); +} +} // namespace + +std::string FormatWsAddress(const std::string& host, int port, const std::string& targetId, bool includeProtocol) +{ + return FormatAddress(FormatHostPort(host, port), targetId, includeProtocol); +} + +class SocketSession { +public: + SocketSession(InspectorSocketServer* server, int id, int serverPort); + void Close() + { + wsSocket.reset(); + } + void Send(const std::string& message); + void Own(InspectorSocket::Pointer wsSocketParam) + { + wsSocket = std::move(wsSocketParam); + } + int GetId() const + { + return id; + } + int ServerPort() + { + return serverPort; + } + InspectorSocket* GetWsSocket() + { + return wsSocket.get(); + } + void Accept(const std::string& wsKey) + { + wsSocket->AcceptUpgrade(wsKey); + } + void Decline() + { + wsSocket->CancelHandshake(); + } + + class Delegate : public InspectorSocket::Delegate { + public: + Delegate(InspectorSocketServer* server, int sessionId) : server(server), usessionId(sessionId) {} + ~Delegate() override + { + server->SessionTerminated(usessionId); + } + void OnHttpGet(const std::string& host, const std::string& path) override; + void OnSocketUpgrade(const std::string& host, const std::string& path, const std::string& wsKey) override; + void OnWsFrame(const std::vector& data) override; + + private: + SocketSession* Session() + { + return server->Session(usessionId); + } + + InspectorSocketServer* server; + int usessionId; + }; + +private: + const int id; + InspectorSocket::Pointer wsSocket; + const int serverPort; +}; + +class ServerSocket { +public: + explicit ServerSocket(InspectorSocketServer* server) + : tcpSocket(uv_tcp_t()), server(server), unixSocket(uv_pipe_t()) + {} + int Listen(sockaddr* addr, uv_loop_t* loop, int pid = -1); + void Close() + { + uv_close(reinterpret_cast(&tcpSocket), FreeOnCloseCallback); + } + void CloseUnix() + { + if (unixSocketOn) { + uv_close(reinterpret_cast(&unixSocket), nullptr); + unixSocketOn = false; + } + } + int GetPort() const + { + return port; + } + +private: + template + static ServerSocket* FromTcpSocket(UvHandle* socket) + { + return jsvm::inspector::ContainerOf(&ServerSocket::tcpSocket, reinterpret_cast(socket)); + } + static void SocketConnectedCallback(uv_stream_t* tcpSocket, int status); + static void UnixSocketConnectedCallback(uv_stream_t* unixSocket, int status); + static void FreeOnCloseCallback(uv_handle_t* tcpSocket) + { + delete FromTcpSocket(tcpSocket); + } + int DetectPort(uv_loop_t* loop, int pid); + ~ServerSocket() = default; + + uv_tcp_t tcpSocket; + InspectorSocketServer* server; + uv_pipe_t unixSocket; + int port = -1; + bool unixSocketOn = false; +}; + +void PrintDebuggerReadyMessage(const std::string& host, + const std::vector& serverSockets, + const std::vector& ids, + const char* verb, + bool publishUidStderr, + FILE* out) +{ + if (!publishUidStderr || out == nullptr) { + return; + } + for (const auto& serverSocket : serverSockets) { + for (const std::string& id : ids) { + fprintf(out, "Debugger %s on %s\n", verb, FormatWsAddress(host, serverSocket->GetPort(), id, true).c_str()); + } + } + fprintf(out, "For help, see: %s\n", "https://nodejs.org/en/docs/inspector"); + fflush(out); +} + +InspectorSocketServer::InspectorSocketServer(std::unique_ptr delegateParam, + uv_loop_t* loop, + const std::string& host, + int port, + const InspectPublishUid& inspectPublishUid, + FILE* out, + int pid) + : loop(loop), delegate(std::move(delegateParam)), host(host), port(port), inspectPublishUid(inspectPublishUid), + nextSessionId(0), out(out), pid(pid) +{ + delegate->AssignServer(this); + state = ServerState::kNew; +} + +InspectorSocketServer::~InspectorSocketServer() = default; + +SocketSession* InspectorSocketServer::Session(int sessionId) +{ + auto it = connectedSessions.find(sessionId); + return it == connectedSessions.end() ? nullptr : it->second.second.get(); +} + +void InspectorSocketServer::SessionStarted(int sessionId, const std::string& id, const std::string& wsKey) +{ + SocketSession* session = Session(sessionId); + if (!TargetExists(id)) { + session->Decline(); + return; + } + connectedSessions[sessionId].first = id; + session->Accept(wsKey); + delegate->StartSession(sessionId, id); +} + +void InspectorSocketServer::SessionTerminated(int sessionId) +{ + if (Session(sessionId) == nullptr) { + return; + } + bool wasAttached = connectedSessions[sessionId].first != ""; + if (wasAttached) { + delegate->EndSession(sessionId); + } + connectedSessions.erase(sessionId); + if (connectedSessions.empty()) { + if (wasAttached && state == ServerState::kRunning && !serverSockets.empty()) { + PrintDebuggerReadyMessage(host, serverSockets, delegate->GetTargetIds(), "ending", + inspectPublishUid.console, out); + } + if (state == ServerState::kStopped) { + delegate.reset(); + } + } +} + +bool InspectorSocketServer::HandleGetRequest(int sessionId, const std::string& host, const std::string& path) +{ + SocketSession* session = Session(sessionId); + InspectorSocket* socket = session->GetWsSocket(); + if (!inspectPublishUid.http) { + SendHttpNotFound(socket); + return true; + } + const char* command = MatchPathSegment(path.c_str(), "/json"); + if (command == nullptr) + return false; + + if (MatchPathSegment(command, "list") || command[0] == '\0') { + SendListResponse(socket, host, session); + return true; + } else if (MatchPathSegment(command, "protocol")) { + SendProtocolJson(socket); + return true; + } else if (MatchPathSegment(command, "version")) { + SendVersionResponse(socket); + return true; + } + return false; +} + +void InspectorSocketServer::SendListResponse(InspectorSocket* socket, const std::string& host, SocketSession* session) +{ + std::vector> response; + for (const std::string& id : delegate->GetTargetIds()) { + response.push_back(std::map()); + std::map& targetMap = response.back(); + targetMap["description"] = "jsvm instance"; + targetMap["id"] = id; + targetMap["title"] = delegate->GetTargetTitle(id); + Escape(&targetMap["title"]); + targetMap["type"] = "node"; + // This attribute value is a "best effort" URL that is passed as a JSON + // string. It is not guaranteed to resolve to a valid resource. + targetMap["url"] = delegate->GetTargetUrl(id); + Escape(&targetMap["url"]); + + std::string detectedHost = host; + if (detectedHost.empty()) { + detectedHost = FormatHostPort(socket->GetHost(), session->ServerPort()); + } + std::string formattedAddress = FormatAddress(detectedHost, id, false); + targetMap["devtoolsFrontendUrl"] = GetFrontendURL(false, formattedAddress); + // The compat URL is for Chrome browsers older than 66.0.3345.0 + targetMap["devtoolsFrontendUrlCompat"] = GetFrontendURL(true, formattedAddress); + targetMap["webSocketDebuggerUrl"] = FormatAddress(detectedHost, id, true); + } + SendHttpResponse(socket, MapsToString(response), 200); +} + +std::string InspectorSocketServer::GetFrontendURL(bool isCompat, const std::string& formattedAddress) +{ + std::ostringstream frontendUrl; + frontendUrl << "devtools://devtools/bundled/"; + frontendUrl << (isCompat ? "inspector" : "js_app"); + frontendUrl << ".html?v8only=true&ws="; + frontendUrl << formattedAddress; + return frontendUrl.str(); +} + +bool InspectorSocketServer::Start() +{ + CHECK_NOT_NULL(delegate); + CHECK_EQ(state, ServerState::kNew); + std::unique_ptr delegateHolder; + // We will return it if startup is successful + delegate.swap(delegateHolder); + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICSERV; + hints.ai_socktype = SOCK_STREAM; + uv_getaddrinfo_t req; + const std::string port_string = std::to_string(port); + int err = uv_getaddrinfo(loop, &req, nullptr, host.c_str(), port_string.c_str(), &hints); + if (err < 0) { + if (out != nullptr) { + fprintf(out, "Unable to resolve \"%s\": %s\n", host.c_str(), uv_strerror(err)); + } + return false; + } + for (addrinfo* address = req.addrinfo; address != nullptr; address = address->ai_next) { + auto serverSocket = ServerSocketPtr(new ServerSocket(this)); + err = serverSocket->Listen(address->ai_addr, loop, pid); + if (err == 0) { + serverSockets.push_back(std::move(serverSocket)); + } + } + uv_freeaddrinfo(req.addrinfo); + + // We only show error if we failed to start server on all addresses. We only + // show one error, for the last address. + if (serverSockets.empty()) { + if (out != nullptr) { + fprintf(out, "Starting inspector on %s:%d failed: %s\n", host.c_str(), port, uv_strerror(err)); + fflush(out); + } + return false; + } + delegate.swap(delegateHolder); + state = ServerState::kRunning; + PrintDebuggerReadyMessage(host, serverSockets, delegate->GetTargetIds(), "listening", inspectPublishUid.console, + out); + return true; +} + +void InspectorSocketServer::Stop() +{ + if (state == ServerState::kStopped) { + return; + } + CHECK_EQ(state, ServerState::kRunning); + state = ServerState::kStopped; + serverSockets.clear(); + if (done()) { + delegate.reset(); + } +} + +void InspectorSocketServer::TerminateConnections() +{ + for (const auto& keyValue : connectedSessions) { + keyValue.second.second->Close(); + } +} + +bool InspectorSocketServer::TargetExists(const std::string& id) +{ + const std::vector& targetIds = delegate->GetTargetIds(); + const auto& found = std::find(targetIds.begin(), targetIds.end(), id); + return found != targetIds.end(); +} + +int InspectorSocketServer::GetPort() const +{ + if (!serverSockets.empty()) { + return serverSockets[0]->GetPort(); + } + return port; +} + +void InspectorSocketServer::Accept(int serverPort, uv_stream_t* serverSocket) +{ + std::unique_ptr session(new SocketSession(this, nextSessionId++, serverPort)); + + InspectorSocket::DelegatePointer delegatePointer = + InspectorSocket::DelegatePointer(new SocketSession::Delegate(this, session->GetId())); + + InspectorSocket::Pointer inspector = InspectorSocket::Accept(serverSocket, std::move(delegatePointer)); + if (inspector) { + session->Own(std::move(inspector)); + connectedSessions[session->GetId()].second = std::move(session); + } +} + +void InspectorSocketServer::Send(int sessionId, const std::string& message) +{ + SocketSession* session = Session(sessionId); + if (session != nullptr) { + session->Send(message); + } +} + +void InspectorSocketServer::CloseServerSocket(ServerSocket* server) +{ + server->Close(); + server->CloseUnix(); +} + +// InspectorSession tracking +SocketSession::SocketSession(InspectorSocketServer* server, int id, int serverPort) : id(id), serverPort(serverPort) {} + +void SocketSession::Send(const std::string& message) +{ + wsSocket->Write(message.data(), message.length()); +} + +void SocketSession::Delegate::OnHttpGet(const std::string& host, const std::string& path) +{ + if (!server->HandleGetRequest(usessionId, host, path)) { + Session()->GetWsSocket()->CancelHandshake(); + } +} + +void SocketSession::Delegate::OnSocketUpgrade(const std::string& host, + const std::string& path, + const std::string& wsKey) +{ + std::string id = path.empty() ? path : path.substr(1); + server->SessionStarted(usessionId, id, wsKey); +} + +void SocketSession::Delegate::OnWsFrame(const std::vector& data) +{ + server->MessageReceived(usessionId, std::string(data.data(), data.size())); +} + +// ServerSocket implementation +int ServerSocket::DetectPort(uv_loop_t* loop, int pid) +{ + sockaddr_storage addr; + int len = sizeof(addr); + int err = uv_tcp_getsockname(&tcpSocket, reinterpret_cast(&addr), &len); + if (err != 0) { + return err; + } + int portNum; + if (addr.ss_family == AF_INET6) { + portNum = reinterpret_cast(&addr)->sin6_port; + } else { + portNum = reinterpret_cast(&addr)->sin_port; + } + port = ntohs(portNum); + if (!unixSocketOn && pid != -1) { + auto unixDomainSocketPath = "jsvm_devtools_remote_" + std::to_string(port) + "_" + std::to_string(pid); + auto* abstract = new char[unixDomainSocketPath.length() + 2]; + abstract[0] = '\0'; + strcpy(abstract + 1, unixDomainSocketPath.c_str()); + auto status = uv_pipe_init(loop, &unixSocket, 0); + if (status == 0) { + status = uv_pipe_bind2(&unixSocket, abstract, unixDomainSocketPath.length() + 1, 0); + } + if (status == 0) { + constexpr int unixBacklog = 128; + status = uv_listen(reinterpret_cast(&unixSocket), unixBacklog, + ServerSocket::UnixSocketConnectedCallback); + } + unixSocketOn = status == 0; + delete[] abstract; + } + return err; +} + +int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop, int pid) +{ + uv_tcp_t* server = &tcpSocket; + CHECK_EQ(0, uv_tcp_init(loop, server)); + int err = uv_tcp_bind(server, addr, 0); + if (err == 0) { + // 511 is the value used by a 'net' module by default + err = uv_listen(reinterpret_cast(server), 511, ServerSocket::SocketConnectedCallback); + } + if (err == 0) { + err = DetectPort(loop, pid); + } + return err; +} + +// static +void ServerSocket::SocketConnectedCallback(uv_stream_t* tcpSocket, int status) +{ + if (status == 0) { + ServerSocket* serverSocket = ServerSocket::FromTcpSocket(tcpSocket); + // Memory is freed when the socket closes. + serverSocket->server->Accept(serverSocket->port, tcpSocket); + } +} + +void ServerSocket::UnixSocketConnectedCallback(uv_stream_t* unixSocket, int status) +{ + if (status == 0) { + (void)unixSocket; + } +} +} // namespace inspector +} // namespace jsvm diff --git a/jsvm/src/inspector/inspector_socket_server.h b/jsvm/src/inspector/inspector_socket_server.h new file mode 100644 index 0000000000000000000000000000000000000000..5ec31712a1a5da7a718e14cebf3f3e38eb262200 --- /dev/null +++ b/jsvm/src/inspector/inspector_socket_server.h @@ -0,0 +1,103 @@ +#ifndef SRC_INSPECTOR_SOCKET_SERVER_H_ +#define SRC_INSPECTOR_SOCKET_SERVER_H_ + +#include +#include +#include + +#include "inspector_socket.h" +#include "jsvm_host_port.h" +#include "uv.h" + +#ifndef ENABLE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +namespace jsvm { +namespace inspector { + +std::string FormatWsAddress(const std::string& host, int port, const std::string& targetId, bool includeProtocol); + +class InspectorSocketServer; +class SocketSession; +class ServerSocket; + +class SocketServerDelegate { +public: + virtual void AssignServer(InspectorSocketServer* server) = 0; + virtual void StartSession(int sessionId, const std::string& targetId) = 0; + virtual void EndSession(int sessionId) = 0; + virtual void MessageReceived(int sessionId, const std::string& message) = 0; + virtual std::vector GetTargetIds() = 0; + virtual std::string GetTargetTitle(const std::string& id) = 0; + virtual std::string GetTargetUrl(const std::string& id) = 0; + virtual ~SocketServerDelegate() = default; +}; + +// HTTP Server, writes messages requested as TransportActions, and responds +// to HTTP requests and WS upgrades. + +class InspectorSocketServer { +public: + InspectorSocketServer(std::unique_ptr delegateParam, + uv_loop_t* loop, + const std::string& host, + int port, + const InspectPublishUid& inspectPublishUid, + FILE* out = stderr, + int pid = -1); + ~InspectorSocketServer(); + + // Start listening on host/port + bool Start(); + + // Called by the TransportAction sent with InspectorIo::Write(): + // kKill and kStop + void Stop(); + // kSendMessage + void Send(int sessionId, const std::string& message); + // kKill + void TerminateConnections(); + int GetPort() const; + + // Session connection lifecycle + void Accept(int serverPort, uv_stream_t* serverSocket); + bool HandleGetRequest(int sessionId, const std::string& host, const std::string& path); + void SessionStarted(int sessionId, const std::string& targetId, const std::string& wsId); + void SessionTerminated(int sessionId); + void MessageReceived(int sessionId, const std::string& message) + { + delegate->MessageReceived(sessionId, message); + } + SocketSession* Session(int sessionId); + bool done() const + { + return serverSockets.empty() && connectedSessions.empty(); + } + + static void CloseServerSocket(ServerSocket*); + using ServerSocketPtr = DeleteFnPtr; + +private: + void SendListResponse(InspectorSocket* socket, const std::string& host, SocketSession* session); + std::string GetFrontendURL(bool is_compat, const std::string& formattedAddress); + bool TargetExists(const std::string& id); + + enum class ServerState { kNew, kRunning, kStopped }; + uv_loop_t* loop; + std::unique_ptr delegate; + const std::string host; + int port; + InspectPublishUid inspectPublishUid; + std::vector serverSockets; + std::map>> connectedSessions; + int nextSessionId; + FILE* out; + ServerState state; + int pid; +}; + +} // namespace inspector +} // namespace jsvm + +#endif // SRC_INSPECTOR_SOCKET_SERVER_H_ diff --git a/jsvm/src/inspector/inspector_utils.cpp b/jsvm/src/inspector/inspector_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d1c10f33ef534457ebb64e9b5d934cd9b6328ff6 --- /dev/null +++ b/jsvm/src/inspector/inspector_utils.cpp @@ -0,0 +1,192 @@ +#include "inspector_utils.h" + +#include "jsvm_dfx.h" +#include "unicode/unistr.h" + +#if HAVE_OPENSSL +#include "openssl/opensslv.h" +#endif + +#if OPENSSL_VERSION_MAJOR >= 3 +#include "openssl/provider.h" +#endif + +#include +#include + +namespace jsvm { +namespace inspector { +namespace { +inline icu::UnicodeString Utf8ToUtf16(const char* data, size_t len) +{ + icu::UnicodeString utf16Str = icu::UnicodeString::fromUTF8(icu::StringPiece(data, len)); + + return utf16Str; +} + +inline std::string Utf16toUtf8(const char16_t* data, size_t length) +{ + icu::UnicodeString unicodeStr(data, length); + std::string utf8Str; + unicodeStr.toUTF8String(utf8Str); + + return utf8Str; +} +} // namespace + +std::unique_ptr Utf8ToStringView(const std::string_view message) +{ + icu::UnicodeString utf16Str = Utf8ToUtf16(message.data(), message.length()); + size_t utf16Len = utf16Str.length(); + + v8_inspector::StringView view(reinterpret_cast(utf16Str.getBuffer()), utf16Len); + return v8_inspector::StringBuffer::create(view); +} + +std::string StringViewToUtf8(v8_inspector::StringView view) +{ + if (view.length() == 0) { + return ""; + } + if (view.is8Bit()) { + return std::string(reinterpret_cast(view.characters8()), view.length()); + } + const char16_t* source = reinterpret_cast(view.characters16()); + + return Utf16toUtf8(source, view.length()); +} + +constexpr size_t TO_TRANSFORM_CHAR_NUM = 3; +constexpr size_t TRANSFORMED_CHAR_NUM = 4; + +static constexpr char base64CharSet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +size_t Base64Encode(const char* inputString, size_t slen, char* outputBuffer, size_t dlen) +{ + // 1: caluate encode size and check + size_t strLen = slen; + size_t encodedStrLen = Base64EncodeSize(slen); + + CHECK_GE(dlen, encodedStrLen); + + // 2: the index do not exceed the range of outputBuffer and form a complete four-character block + for (size_t i = 0, j = 0; j < strLen - 2; i += TRANSFORMED_CHAR_NUM, j += TO_TRANSFORM_CHAR_NUM) { + // convert three 8bit into four 6bit; then add two 0 bit in each 6 bit + // former 00 + first 6 bits of the first char + outputBuffer[i] = base64CharSet[(static_cast(inputString[j]) & 0xff) >> 2]; + // 00 + the last 2 bits of the first char + the first 4 bits of the second char + outputBuffer[i + 1] = base64CharSet[(static_cast(inputString[j]) & 0x03) << 4 | + (static_cast(inputString[j + 1]) & 0xf0) >> 4]; + // 00 + last 4 bits of the second char + the first 2 bits of the third char + outputBuffer[i + 2] = base64CharSet[(static_cast(inputString[j + 1]) & 0x0f) << 2 | + (static_cast(inputString[j + 2]) & 0xc0) >> 6]; + // 00 + the last 6 bits of the third char + outputBuffer[i + 3] = base64CharSet[static_cast(inputString[j + 2]) & 0x3f]; + } + switch (strLen % TO_TRANSFORM_CHAR_NUM) { + // the original string is less than three bytes, and the missing place is filled with '=' to patch four bytes + case 1: + // 1,2: the original character is one, and two characters are missing after conversion + outputBuffer[encodedStrLen - 4] = + base64CharSet[(static_cast(inputString[strLen - 1]) & 0xff) >> 2]; + outputBuffer[encodedStrLen - 3] = + base64CharSet[(static_cast(inputString[strLen - 1]) & 0x03) << 4]; + outputBuffer[encodedStrLen - 2] = '='; + outputBuffer[encodedStrLen - 1] = '='; + break; + case 2: + // 1: the original character is two, and a character are missing after conversion + outputBuffer[encodedStrLen - 4] = + base64CharSet[(static_cast(inputString[strLen - 2]) & 0xff) >> 2]; + outputBuffer[encodedStrLen - 3] = + base64CharSet[(static_cast(inputString[strLen - 2]) & 0x03) << 4 | + (static_cast(inputString[strLen - 1]) & 0xf0) >> 4]; + outputBuffer[encodedStrLen - 2] = + base64CharSet[(static_cast(inputString[strLen - 1]) & 0x0f) << 2]; + outputBuffer[encodedStrLen - 1] = '='; + break; + default: + break; + } + + return encodedStrLen; +} + +std::string GetHumanReadableProcessName() +{ + return "JSVM[" + std::to_string(platform::OS::GetPid()) + "]"; +} + +MUST_USE_RESULT bool CSPRNG(void* buffer, size_t length) +{ + unsigned char* buf = static_cast(buffer); + do { + if (1 == RAND_status()) { +#if OPENSSL_VERSION_MAJOR >= 3 + if (1 == RAND_bytes_ex(nullptr, buf, length, 0)) { + return true; + } +#else + while (length > INT_MAX && 1 == RAND_bytes(buf, INT_MAX)) { + buf += INT_MAX; + length -= INT_MAX; + } + if (length <= INT_MAX && 1 == RAND_bytes(buf, static_cast(length))) { + return true; + } +#endif + } +#if OPENSSL_VERSION_MAJOR >= 3 + const auto code = ERR_peek_last_error(); + // A misconfigured OpenSSL 3 installation may report 1 from RAND_poll() + // and RAND_status() but fail in RAND_bytes() if it cannot look up + // a matching algorithm for the CSPRNG. + if (ERR_GET_LIB(code) == ERR_LIB_RAND) { + const auto reason = ERR_GET_REASON(code); + if (reason == RAND_R_ERROR_INSTANTIATING_DRBG || reason == RAND_R_UNABLE_TO_FETCH_DRBG || + reason == RAND_R_UNABLE_TO_CREATE_DRBG) { + return false; + } + } +#endif + } while (1 == RAND_poll()); + + return false; +} + +void CheckedUvLoopClose(uv_loop_t* loop) +{ + if (uv_loop_close(loop) == 0) { + return; + } + + // Finally, abort. + UNREACHABLE("uv_loop_close() while having open handles"); +} + +using v8::Isolate; +using v8::Local; +using v8::String; +using v8::Value; + +TwoByteValue::TwoByteValue(Isolate* isolate, Local value) +{ + if (value.IsEmpty()) { + return; + } + + Local string; + if (!value->ToString(isolate->GetCurrentContext()).ToLocal(&string)) { + return; + } + + // Allocate enough space to include the null terminator + const size_t storage = string->Length() + 1; + AllocateSufficientStorage(storage); + + const int flags = String::NO_NULL_TERMINATION; + const int length = string->Write(isolate, out(), 0, storage, flags); + SetLengthAndZeroTerminate(length); +} +} // namespace inspector +} // namespace jsvm \ No newline at end of file diff --git a/jsvm/src/inspector/inspector_utils.h b/jsvm/src/inspector/inspector_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..2b2b4c0d81f2322ecb5979eb9d0ac621e5e205a8 --- /dev/null +++ b/jsvm/src/inspector/inspector_utils.h @@ -0,0 +1,308 @@ +#ifndef INSPECTOR_UTILS_H +#define INSPECTOR_UTILS_H +#include +#include +#include + +#include "jsvm_dfx.h" +#include "jsvm_util.h" +#include "uv.h" +#include "v8-inspector.h" + +#ifdef __GNUC__ +#define MUST_USE_RESULT __attribute__((warn_unused_result)) +#else +#define MUST_USE_RESULT +#endif + +namespace jsvm { +namespace inspector { +// // The helper is for doing safe downcasts from base types to derived types. +template +class ContainerOfHelper { +public: + inline ContainerOfHelper(Inner Outer::*field, Inner* pointer); + template + inline operator TypeName*() const; + +private: + Outer* const pointer; +}; + +inline char ToLower(char c) +{ + return std::tolower(c, std::locale::classic()); +} + +inline bool StringEqualNoCase(const char* a, const char* b) +{ + while (ToLower(*a) == ToLower(*b++)) { + if (*a++ == '\0') { + return true; + } + } + return false; +} + +inline bool StringEqualNoCaseN(const char* a, const char* b, size_t length) +{ + for (size_t i = 0; i < length; i++) { + if (ToLower(a[i]) != ToLower(b[i])) { + return false; + } + if (a[i] == '\0') { + return true; + } + } + return true; +} + +// Use this when a variable or parameter is unused in order to explicitly +// silence a compiler warning about that. +template +inline void USE(T&&) +{} + +// Used to be a macro, hence the uppercase name. +template +inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, const char (&data)[N]) +{ + return OneByteString(isolate, data, N - 1); +} + +template +inline v8::Local FIXED_ONE_BYTE_STRING(v8::Isolate* isolate, const std::array& arr) +{ + return OneByteString(isolate, arr.data(), N - 1); +} + +template +struct FunctionDeleter { + void operator()(T* pointer) const + { + function(pointer); + } + typedef std::unique_ptr Pointer; +}; + +template +using DeleteFnPtr = typename FunctionDeleter::Pointer; + +template +constexpr uintptr_t OffsetOf(Inner Outer::*field) +{ + return reinterpret_cast(&(static_cast(nullptr)->*field)); +} + +template +ContainerOfHelper::ContainerOfHelper(Inner Outer::*field, Inner* pointer) + : pointer(reinterpret_cast(reinterpret_cast(pointer) - OffsetOf(field))) +{} + +template +template +ContainerOfHelper::operator TypeName*() const +{ + return static_cast(pointer); +} + +// Calculate the address of the outer (i.e. embedding) struct from +// the interior pointer to a data member. +template +constexpr ContainerOfHelper ContainerOf(Inner Outer::*field, Inner* pointer) +{ + return ContainerOfHelper(field, pointer); +} + +// util.h +// Allocates an array of member type T. For up to kStackStorageSize items, +// the stack is used, otherwise malloc(). +template +class MaybeStackBuffer { +public: + const T* out() const + { + return buf; + } + + T* out() + { + return buf; + } + + // operator* for compatibility with `v8::String::(Utf8)Value` + T* operator*() + { + return buf; + } + + const T* operator*() const + { + return buf; + } + + T& operator[](size_t index) + { + CHECK_LT(index, GetLength()); + return buf[index]; + } + + const T& operator[](size_t index) const + { + CHECK_LT(index, GetLength()); + return buf[index]; + } + + size_t GetLength() const + { + return length; + } + + // Current maximum capacity of the buffer with which SetLength() can be used + // without first calling AllocateSufficientStorage(). + size_t GetCapacity() const + { + return capacity; + } + + // Make sure enough space for `storage` entries is available. + // This method can be called multiple times throughout the lifetime of the + // buffer, but once this has been called Invalidate() cannot be used. + // Content of the buffer in the range [0, GetLength()) is preserved. + void AllocateSufficientStorage(size_t storage); + + void SetLength(size_t lengthParam) + { + // GetCapacity() returns how much memory is actually available. + CHECK_LE(lengthParam, GetCapacity()); + length = lengthParam; + } + + void SetLengthAndZeroTerminate(size_t length) + { + // GetCapacity() returns how much memory is actually available. + CHECK_LE(length + 1, GetCapacity()); + SetLength(length); + + // T() is 0 for integer types, nullptr for pointers, etc. + buf[length] = T(); + } + + // Make dereferencing this object return nullptr. + // This method can be called multiple times throughout the lifetime of the + // buffer, but once this has been called AllocateSufficientStorage() cannot + // be used. + void Invalidate() + { + CHECK(!IsAllocated()); + capacity = 0; + length = 0; + buf = nullptr; + } + + // If the buffer is stored in the heap rather than on the stack. + bool IsAllocated() const + { + return !IsInvalidated() && buf != bufSt; + } + + // If Invalidate() has been called. + bool IsInvalidated() const + { + return buf == nullptr; + } + + // Release ownership of the malloc'd buffer. + // Note: This does not free the buffer. + void Release() + { + CHECK(IsAllocated()); + buf = bufSt; + length = 0; + capacity = jsvm::ArraySize(bufSt); + } + + MaybeStackBuffer() : length(0), capacity(jsvm::ArraySize(bufSt)), buf(bufSt) + { + // Default to a zero-length, null-terminated buffer. + buf[0] = T(); + } + + explicit MaybeStackBuffer(size_t storage) : MaybeStackBuffer() + { + AllocateSufficientStorage(storage); + } + + ~MaybeStackBuffer() + { + if (IsAllocated()) { + free(buf); + } + } + + inline std::basic_string ToString() const + { + return { out(), GetLength() }; + } + inline std::basic_string_view ToStringView() const + { + return { out(), GetLength() }; + } + +private: + size_t length; + // capacity of the malloc'ed buf + size_t capacity; + T* buf; + T bufSt[kStackStorageSize]; +}; + +class TwoByteValue : public MaybeStackBuffer { +public: + explicit TwoByteValue(v8::Isolate* isolate, v8::Local value); +}; + +// TODO: replace realloc with Realloc +template +void MaybeStackBuffer::AllocateSufficientStorage(size_t storage) +{ + CHECK(!IsInvalidated()); + if (storage > GetCapacity()) { + bool wasAllocated = IsAllocated(); + T* allocatedPtr = wasAllocated ? buf : nullptr; + buf = reinterpret_cast(realloc(allocatedPtr, storage)); + capacity = storage; + if (!wasAllocated && length > 0) { + memcpy(buf, bufSt, length * sizeof(buf[0])); + } + } + + length = storage; +} + +// Convertion between v8_inspector::StringView and std::string +std::string StringViewToUtf8(v8_inspector::StringView view); +std::unique_ptr Utf8ToStringView(const std::string_view message); + +// Encode base64 +static inline constexpr size_t Base64EncodeSize(size_t size) +{ + return ((size + 2) / 3 * 4); +} + +// Be careful: If dlen is less than expected encode size, it will crash. +size_t Base64Encode(const char* inputString, size_t slen, char* outputBuffer, size_t dlen); + +std::string GetHumanReadableProcessName(); + +// Either succeeds with exactly |length| bytes of cryptographically +// strong pseudo-random data, or fails. This function may block. +// Don't assume anything about the contents of |buffer| on error. +// As a special case, |length == 0| can be used to check if the CSPRNG +// is properly seeded without consuming entropy. +MUST_USE_RESULT bool CSPRNG(void* buffer, size_t length); + +void CheckedUvLoopClose(uv_loop_t* loop); +} // namespace inspector +} // namespace jsvm +#endif \ No newline at end of file diff --git a/jsvm/src/inspector/js_native_api_v8_inspector.cc b/jsvm/src/inspector/js_native_api_v8_inspector.cc new file mode 100644 index 0000000000000000000000000000000000000000..056851212f92d6d8e90c3b9737c41c52f00a40b5 --- /dev/null +++ b/jsvm/src/inspector/js_native_api_v8_inspector.cc @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_native_api_v8_inspector.h" + +#include + +#include "inspector_socket_server.h" +#include "inspector_utils.h" +#include "js_native_api_v8.h" +#include "jsvm_mutex.h" +#include "libplatform/libplatform.h" +#include "v8-inspector.h" +#include "v8-platform.h" + +#ifdef __POSIX__ +#include // PTHREAD_STACK_MIN +#include +#endif // __POSIX__ + +#include +#include +#include +#include +#include + +namespace v8impl { + +namespace { +using jsvm::ConditionVariable; +using jsvm::Mutex; +using jsvm::inspector::StringViewToUtf8; +using jsvm::inspector::Utf8ToStringView; +using v8_inspector::StringBuffer; +using v8_inspector::StringView; + +class MainThreadInterface; + +class Request { +public: + virtual void Call(MainThreadInterface*) = 0; + virtual ~Request() = default; +}; + +class Deletable { +public: + virtual ~Deletable() = default; +}; + +using MessageQueue = std::deque>; + +class MainThreadHandle : public std::enable_shared_from_this { +public: + explicit MainThreadHandle(MainThreadInterface* mainThread) : mainThread(mainThread) {} + ~MainThreadHandle() + { + Mutex::ScopedLock scopedLock(blockLock); + CHECK_NULL(mainThread); // mainThread should have called Reset + } + std::unique_ptr Connect(std::unique_ptr delegate, bool preventShutdown); + int NewObjectId() + { + return ++nextObjectId; + } + bool Post(std::unique_ptr request); + +private: + void Reset(); + + MainThreadInterface* mainThread; + Mutex blockLock; + int nextSessionId = 0; + std::atomic_int nextObjectId = { 1 }; + + friend class MainThreadInterface; +}; + +class MainThreadInterface : public std::enable_shared_from_this { +public: + explicit MainThreadInterface(Agent* agent); + ~MainThreadInterface(); + + void DispatchMessages(); + void Post(std::unique_ptr request); + bool WaitForFrontendEvent(); + std::shared_ptr GetHandle(); + Agent* inspector_agent() + { + return agent; + } + void AddObject(int handle, std::unique_ptr object); + Deletable* GetObject(int id); + Deletable* GetObjectIfExists(int id); + void RemoveObject(int handle); + +private: + MessageQueue requests; + Mutex requestsLock; // requests live across threads + // This queue is to maintain the order of the messages for the cases + // when we reenter the DispatchMessages function. + MessageQueue dispatchingMessageQueue; + bool dispatchingMessages = false; + ConditionVariable incomingMessageCond; + // Used from any thread + Agent* const agent; + std::shared_ptr handle; + std::unordered_map> managedObjects; +}; + +template +class DeletableWrapper : public Deletable { +public: + explicit DeletableWrapper(std::unique_ptr object) : object(std::move(object)) {} + ~DeletableWrapper() override = default; + + static T* Get(MainThreadInterface* thread, int id) + { + return static_cast*>(thread->GetObject(id))->object.get(); + } + +private: + std::unique_ptr object; +}; + +template +std::unique_ptr WrapInDeletable(std::unique_ptr object) +{ + return std::unique_ptr>(new DeletableWrapper(std::move(object))); +} + +template +class CreateObjectRequest : public Request { +public: + CreateObjectRequest(int objectId, Factory factory) : objectId(objectId), factory(std::move(factory)) {} + + void Call(MainThreadInterface* thread) override + { + thread->AddObject(objectId, WrapInDeletable(factory(thread))); + } + +private: + int objectId; + Factory factory; +}; + +template +std::unique_ptr NewCreateRequest(int objectId, Factory factory) +{ + return std::unique_ptr(new CreateObjectRequest(objectId, std::move(factory))); +} + +class DeleteRequest : public Request { +public: + explicit DeleteRequest(int objectId) : objectId(objectId) {} + + void Call(MainThreadInterface* thread) override + { + thread->RemoveObject(objectId); + } + +private: + int objectId; +}; + +template +class CallRequest : public Request { +public: + CallRequest(int id, Fn fn) : id(id), fn(std::move(fn)) {} + + void Call(MainThreadInterface* thread) override + { + fn(DeletableWrapper::Get(thread, id)); + } + +private: + int id; + Fn fn; +}; + +template +class AnotherThreadObjectReference { +public: + AnotherThreadObjectReference(std::shared_ptr thread, int objectId) + : thread(thread), objectId(objectId) + {} + + template + AnotherThreadObjectReference(std::shared_ptr thread, Factory factory) + : AnotherThreadObjectReference(thread, thread->NewObjectId()) + { + thread->Post(NewCreateRequest(objectId, std::move(factory))); + } + AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete; + + ~AnotherThreadObjectReference() + { + // Disappearing thread may cause a memory leak + thread->Post(std::make_unique(objectId)); + } + + template + void Call(Fn fn) const + { + using Request = CallRequest; + thread->Post(std::unique_ptr(new Request(objectId, std::move(fn)))); + } + + template + void Call(void (T::*fn)(Arg), Arg argument) const + { + Call(std::bind(Apply, std::placeholders::_1, fn, std::move(argument))); + } + +private: + // This has to use non-const reference to support std::bind with non-copyable + // types + template + static void Apply(T* target, void (T::*fn)(Argument), Argument& argument) /* NOLINT (runtime/references) */ + { + (target->*fn)(std::move(argument)); + } + + std::shared_ptr thread; + const int objectId; +}; + +class MainThreadSessionState { +public: + MainThreadSessionState(MainThreadInterface* thread, bool preventShutdown) + : thread(thread), preventShutdown(preventShutdown) + {} + + static std::unique_ptr Create(MainThreadInterface* thread, bool preventShutdown) + { + return std::make_unique(thread, preventShutdown); + } + + void Connect(std::unique_ptr delegate) + { + Agent* agent = thread->inspector_agent(); + if (agent != nullptr) { + session = agent->Connect(std::move(delegate), preventShutdown); + } + } + + void Dispatch(std::unique_ptr message) + { + session->Dispatch(message->string()); + } + +private: + MainThreadInterface* thread; + bool preventShutdown; + std::unique_ptr session; +}; + +class CrossThreadInspectorSession : public InspectorSession { +public: + CrossThreadInspectorSession(int id, + std::shared_ptr thread, + std::unique_ptr delegate, + bool preventShutdown) + : state(thread, std::bind(MainThreadSessionState::Create, std::placeholders::_1, preventShutdown)) + { + state.Call(&MainThreadSessionState::Connect, std::move(delegate)); + } + + void Dispatch(const StringView& message) override + { + state.Call(&MainThreadSessionState::Dispatch, StringBuffer::create(message)); + } + +private: + AnotherThreadObjectReference state; +}; + +class ThreadSafeDelegate : public InspectorSessionDelegate { +public: + ThreadSafeDelegate(std::shared_ptr thread, int objectId) + : thread(thread), delegate(thread, objectId) + {} + + void SendMessageToFrontend(const v8_inspector::StringView& message) override + { + delegate.Call([m = StringBuffer::create(message)](InspectorSessionDelegate* delegate) { + delegate->SendMessageToFrontend(m->string()); + }); + } + +private: + std::shared_ptr thread; + AnotherThreadObjectReference delegate; +}; + +MainThreadInterface::MainThreadInterface(Agent* agent) : agent(agent) {} + +MainThreadInterface::~MainThreadInterface() +{ + if (handle) { + handle->Reset(); + } +} + +void MainThreadInterface::Post(std::unique_ptr request) +{ + CHECK_NOT_NULL(agent); + Mutex::ScopedLock scopedLock(requestsLock); + bool needsNotify = requests.empty(); + requests.push_back(std::move(request)); + if (needsNotify) { + std::weak_ptr weakSelf { shared_from_this() }; + agent->env()->RequestInterrupt([weakSelf](Environment*) { + if (auto iface = weakSelf.lock()) { + iface->DispatchMessages(); + } + }); + } + incomingMessageCond.Broadcast(scopedLock); +} + +bool MainThreadInterface::WaitForFrontendEvent() +{ + // We allow DispatchMessages reentry as we enter the pause. This is important + // to support debugging the code invoked by an inspector call, such + // as Runtime.evaluate + dispatchingMessages = false; + if (dispatchingMessageQueue.empty()) { + Mutex::ScopedLock scopedLock(requestsLock); + while (requests.empty()) + incomingMessageCond.Wait(scopedLock); + } + return true; +} + +void MainThreadInterface::DispatchMessages() +{ + if (dispatchingMessages) { + return; + } + dispatchingMessages = true; + bool hadMessages = false; + do { + if (dispatchingMessageQueue.empty()) { + Mutex::ScopedLock scopedLock(requestsLock); + requests.swap(dispatchingMessageQueue); + } + hadMessages = !dispatchingMessageQueue.empty(); + while (!dispatchingMessageQueue.empty()) { + MessageQueue::value_type task; + std::swap(dispatchingMessageQueue.front(), task); + dispatchingMessageQueue.pop_front(); + + v8::SealHandleScope sealHandleScope(agent->env()->isolate); + task->Call(this); + } + } while (hadMessages); + dispatchingMessages = false; +} + +std::shared_ptr MainThreadInterface::GetHandle() +{ + if (handle == nullptr) { + handle = std::make_shared(this); + } + return handle; +} + +void MainThreadInterface::AddObject(int id, std::unique_ptr object) +{ + CHECK_NOT_NULL(object); + managedObjects[id] = std::move(object); +} + +void MainThreadInterface::RemoveObject(int id) +{ + CHECK_EQ(1, managedObjects.erase(id)); +} + +Deletable* MainThreadInterface::GetObject(int id) +{ + Deletable* pointer = GetObjectIfExists(id); + // This would mean the object is requested after it was disposed, which is + // a coding error. + CHECK_NOT_NULL(pointer); + return pointer; +} + +Deletable* MainThreadInterface::GetObjectIfExists(int id) +{ + auto iterator = managedObjects.find(id); + if (iterator == managedObjects.end()) { + return nullptr; + } + return iterator->second.get(); +} + +std::unique_ptr MainThreadHandle::Connect(std::unique_ptr delegate, + bool preventShutdown) +{ + return std::unique_ptr( + new CrossThreadInspectorSession(++nextSessionId, shared_from_this(), std::move(delegate), preventShutdown)); +} + +bool MainThreadHandle::Post(std::unique_ptr request) +{ + Mutex::ScopedLock scopedLock(blockLock); + if (!mainThread) { + return false; + } + mainThread->Post(std::move(request)); + return true; +} + +void MainThreadHandle::Reset() +{ + Mutex::ScopedLock scopedLock(blockLock); + mainThread = nullptr; +} +} // namespace + +namespace { +using jsvm::InspectPublishUid; +using jsvm::inspector::CheckedUvLoopClose; +using jsvm::inspector::CSPRNG; +using jsvm::inspector::FormatWsAddress; +using jsvm::inspector::GetHumanReadableProcessName; +using jsvm::inspector::InspectorSocketServer; + +// K_KILL closes connections and stops the server, K_STOP only stops the server +enum class TransportAction { K_KILL, K_SEND_MESSAGE, K_STOP }; + +std::string ScriptPath(uv_loop_t* loop, const std::string& scriptName) +{ + std::string scriptPath; + + if (!scriptName.empty()) { + uv_fs_t req; + req.ptr = nullptr; + if (0 == uv_fs_realpath(loop, &req, scriptName.c_str(), nullptr)) { + CHECK_NOT_NULL(req.ptr); + scriptPath = std::string(static_cast(req.ptr)); + } + uv_fs_req_cleanup(&req); + } + + return scriptPath; +} + +// UUID RFC: https://www.ietf.org/rfc/rfc4122.txt +// Used ver 4 - with numbers +std::string GenerateID() +{ + uint16_t buffer[8]; + CHECK(CSPRNG(buffer, sizeof(buffer))); + + char uuid[256]; + snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + buffer[0], // time_low + buffer[1], // time_mid + buffer[2], // time_low + (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version + (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low + buffer[5], // node + buffer[6], buffer[7]); + return uuid; +} + +class RequestToServer { +public: + RequestToServer(TransportAction action, int sessionId, std::unique_ptr message) + : action(action), sessionId(sessionId), message(std::move(message)) + {} + + void Dispatch(InspectorSocketServer* server) const + { + switch (action) { + case TransportAction::K_KILL: + server->TerminateConnections(); + [[fallthrough]]; + case TransportAction::K_STOP: + server->Stop(); + break; + case TransportAction::K_SEND_MESSAGE: + server->Send(sessionId, StringViewToUtf8(message->string())); + break; + } + } + +private: + TransportAction action; + int sessionId; + std::unique_ptr message; +}; + +class RequestQueue; + +class RequestQueueData { +public: + using MessageQueue = std::deque; + + explicit RequestQueueData(uv_loop_t* loop) : handle(std::make_shared(this)) + { + int err = uv_async_init(loop, &async, [](uv_async_t* async) { + RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async); + wrapper->DoDispatch(); + }); + CHECK_EQ(0, err); + } + + static void CloseAndFree(RequestQueueData* queue); + + void Post(int sessionId, TransportAction action, std::unique_ptr message) + { + Mutex::ScopedLock scopedLock(stateLock); + bool notify = messages.empty(); + messages.emplace_back(action, sessionId, std::move(message)); + if (notify) { + CHECK_EQ(0, uv_async_send(&async)); + incomingMessageCond.Broadcast(scopedLock); + } + } + + void Wait() + { + Mutex::ScopedLock scopedLock(stateLock); + if (messages.empty()) { + incomingMessageCond.Wait(scopedLock); + } + } + + void SetServer(InspectorSocketServer* serverParam) + { + server = serverParam; + } + + std::shared_ptr GetHandle() + { + return handle; + } + +private: + ~RequestQueueData() = default; + + MessageQueue GetMessages() + { + Mutex::ScopedLock scopedLock(stateLock); + MessageQueue messagesQ; + messages.swap(messagesQ); + return messagesQ; + } + + void DoDispatch() + { + if (server == nullptr) { + return; + } + for (const auto& request : GetMessages()) { + request.Dispatch(server); + } + } + + std::shared_ptr handle; + uv_async_t async; + InspectorSocketServer* server = nullptr; + MessageQueue messages; + Mutex stateLock; // Locked before mutating the queue. + ConditionVariable incomingMessageCond; +}; + +class RequestQueue { +public: + explicit RequestQueue(RequestQueueData* data) : data(data) {} + + void Reset() + { + Mutex::ScopedLock scopedLock(lock); + data = nullptr; + } + + void Post(int sessionId, TransportAction action, std::unique_ptr message) + { + Mutex::ScopedLock scopedLock(lock); + if (data != nullptr) { + data->Post(sessionId, action, std::move(message)); + } + } + + bool Expired() + { + Mutex::ScopedLock scopedLock(lock); + return data == nullptr; + } + +private: + RequestQueueData* data; + Mutex lock; +}; + +class IoSessionDelegate : public InspectorSessionDelegate { +public: + explicit IoSessionDelegate(std::shared_ptr queue, int id) : requestQueue(queue), id(id) {} + void SendMessageToFrontend(const v8_inspector::StringView& message) override + { + requestQueue->Post(id, TransportAction::K_SEND_MESSAGE, StringBuffer::create(message)); + } + +private: + std::shared_ptr requestQueue; + int id; +}; + +// Passed to InspectorSocketServer to handle WS inspector protocol events, +// mostly session start, message received, and session end. +class InspectorIoDelegate : public jsvm::inspector::SocketServerDelegate { +public: + InspectorIoDelegate(std::shared_ptr queue, + std::shared_ptr mainThread, + const std::string& targetId, + const std::string& scriptPath, + const std::string& scriptName); + ~InspectorIoDelegate() override = default; + + void StartSession(int sessionId, const std::string& targetId) override; + void MessageReceived(int sessionId, const std::string& message) override; + void EndSession(int sessionId) override; + + std::vector GetTargetIds() override; + std::string GetTargetTitle(const std::string& id) override; + std::string GetTargetUrl(const std::string& id) override; + void AssignServer(InspectorSocketServer* server) override + { + requestQueue->SetServer(server); + } + +private: + std::shared_ptr requestQueue; + std::shared_ptr mainThread; + std::unordered_map> sessions; + const std::string scriptName; + const std::string scriptPath; + const std::string targetId; +}; + +InspectorIoDelegate::InspectorIoDelegate(std::shared_ptr queue, + std::shared_ptr mainThread, + const std::string& targetId, + const std::string& scriptPath, + const std::string& scriptName) + : requestQueue(queue), mainThread(mainThread), scriptName(scriptName), scriptPath(scriptPath), targetId(targetId) +{} + +void InspectorIoDelegate::StartSession(int sessionId, const std::string& targetId) +{ + auto session = mainThread->Connect( + std::unique_ptr(new IoSessionDelegate(requestQueue->GetHandle(), sessionId)), true); + if (session) { + sessions[sessionId] = std::move(session); + fprintf(stderr, "Debugger attached.\n"); + } +} + +void InspectorIoDelegate::MessageReceived(int sessionId, const std::string& message) +{ + auto session = sessions.find(sessionId); + if (session != sessions.end()) { + session->second->Dispatch(Utf8ToStringView(message)->string()); + } +} + +void InspectorIoDelegate::EndSession(int sessionId) +{ + sessions.erase(sessionId); +} + +std::vector InspectorIoDelegate::GetTargetIds() +{ + return { targetId }; +} + +std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) +{ + return scriptName.empty() ? GetHumanReadableProcessName() : scriptName; +} + +std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) +{ + return "file://" + scriptPath; +} + +// static +void RequestQueueData::CloseAndFree(RequestQueueData* queue) +{ + queue->handle->Reset(); + queue->handle.reset(); + uv_close(reinterpret_cast(&queue->async), [](uv_handle_t* handle) { + uv_async_t* async = reinterpret_cast(handle); + RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async); + delete wrapper; + }); +} +} // namespace + +class InspectorIo { +public: + // Start the inspector agent thread, waiting for it to initialize + // bool Start(); + // Returns empty pointer if thread was not started + static std::unique_ptr Start(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const jsvm::InspectPublishUid& inspectPublishUid); + + // Will block till the transport thread shuts down + ~InspectorIo(); + + void StopAcceptingNewConnections(); + std::string GetWsUrl() const; + +private: + InspectorIo(std::shared_ptr handle, + const std::string& path, + std::shared_ptr> hostPortParam, + const jsvm::InspectPublishUid& inspectPublishUid); + + // Wrapper for agent->ThreadMain() + static void ThreadMain(void* agent); + + // Runs a uv_loop_t + void ThreadMain(); + + // This is a thread-safe object that will post async tasks. It lives as long + // as an Inspector object lives (almost as long as an Isolate). + std::shared_ptr mainThread; + // Used to post on a frontend interface thread, lives while the server is + // running + std::shared_ptr requestQueue; + std::shared_ptr> hostPort; + jsvm::InspectPublishUid inspectPublishUid; + + // The IO thread runs its own uv_loop to implement the TCP server off + // the main thread. + uv_thread_t thread; + + // For setting up interthread communications + Mutex threadStartLock; + jsvm::ConditionVariable threadStartCondition; + std::string scriptName; + // May be accessed from any thread + const std::string id; +}; + +// static +std::unique_ptr InspectorIo::Start(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const InspectPublishUid& inspectPublishUid) +{ + auto io = std::unique_ptr(new InspectorIo(mainThread, path, hostPortParam, inspectPublishUid)); + if (io->requestQueue->Expired()) { // Thread is not running + return nullptr; + } + return io; +} + +InspectorIo::InspectorIo(std::shared_ptr mainThread, + const std::string& path, + std::shared_ptr> hostPortParam, + const InspectPublishUid& inspectPublishUid) + : mainThread(mainThread), hostPort(hostPortParam), inspectPublishUid(inspectPublishUid), thread(), scriptName(path), + id(GenerateID()) +{ + Mutex::ScopedLock scopedLock(threadStartLock); + CHECK_EQ(uv_thread_create(&thread, InspectorIo::ThreadMain, this), 0); + threadStartCondition.Wait(scopedLock); +} + +InspectorIo::~InspectorIo() +{ + requestQueue->Post(0, TransportAction::K_KILL, nullptr); + int err = uv_thread_join(&thread); + CHECK_EQ(err, 0); +} + +void InspectorIo::StopAcceptingNewConnections() +{ + requestQueue->Post(0, TransportAction::K_STOP, nullptr); +} + +// static +void InspectorIo::ThreadMain(void* io) +{ + static_cast(io)->ThreadMain(); +} + +void InspectorIo::ThreadMain() +{ + uv_loop_t loop; + loop.data = nullptr; + int err = uv_loop_init(&loop); + CHECK_EQ(err, 0); + std::shared_ptr queue(new RequestQueueData(&loop), RequestQueueData::CloseAndFree); + std::string scriptPath = ScriptPath(&loop, scriptName); + std::unique_ptr delegate( + new InspectorIoDelegate(queue, mainThread, id, scriptPath, scriptName)); + std::string host; + int port; + int pid; + { + ExclusiveAccess::Scoped scopedHostPort(hostPort); + host = scopedHostPort->GetHost(); + port = scopedHostPort->GetPort(); + pid = scopedHostPort->GetPid(); + } + InspectorSocketServer server(std::move(delegate), &loop, std::move(host), port, inspectPublishUid, stderr, pid); + requestQueue = queue->GetHandle(); + // Its lifetime is now that of the server delegate + queue.reset(); + { + Mutex::ScopedLock scopedLock(threadStartLock); + if (server.Start()) { + ExclusiveAccess::Scoped scopedHostPort(hostPort); + scopedHostPort->SetPort(server.GetPort()); + } + threadStartCondition.Broadcast(scopedLock); + } + uv_run(&loop, UV_RUN_DEFAULT); + CheckedUvLoopClose(&loop); +} + +std::string InspectorIo::GetWsUrl() const +{ + ExclusiveAccess::Scoped scopedHostPort(hostPort); + return FormatWsAddress(scopedHostPort->GetHost(), scopedHostPort->GetPort(), id, true); +} + +namespace { + +using jsvm::inspector::TwoByteValue; + +using v8::Context; +using v8::Function; +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Message; +using v8::Object; +using v8::Value; + +using v8_inspector::StringBuffer; +using v8_inspector::StringView; +using v8_inspector::V8Inspector; +using v8_inspector::V8InspectorClient; + +// namespace per_process = jsvm::per_process; + +std::unique_ptr ToProtocolString(Isolate* isolate, Local value) +{ + TwoByteValue buffer(isolate, value); + return StringBuffer::create(StringView(*buffer, buffer.GetLength())); +} + +const int CONTEXT_GROUP_ID = 1; + +std::string GetWorkerLabel(Environment* env) +{ + std::ostringstream result; + // TODO: use thread ID as part of worker label. + result << "Worker[" + << "env->thread_id()" + << "]"; + return result.str(); +} + +class ChannelImpl final : public v8_inspector::V8Inspector::Channel { +public: + explicit ChannelImpl(const std::unique_ptr& inspector, + std::unique_ptr delegate, + std::shared_ptr mainThread, + bool preventShutdown) + : delegate(std::move(delegate)), preventShutdown(preventShutdown) + { + session = + inspector->connect(CONTEXT_GROUP_ID, this, StringView(), V8Inspector::ClientTrustLevel::kFullyTrusted); + } + + ~ChannelImpl() = default; + + void dispatchProtocolMessage(const StringView& message) + { + session->dispatchProtocolMessage(message); + } + + void schedulePauseOnNextStatement(const std::string& reason) + { + std::unique_ptr buffer = Utf8ToStringView(reason); + session->schedulePauseOnNextStatement(buffer->string(), buffer->string()); + } + + bool PreventShutdown() + { + return preventShutdown; + } + +private: + void sendResponse(int callId, std::unique_ptr message) override + { + sendMessageToFrontend(message->string()); + } + + void sendNotification(std::unique_ptr message) override + { + sendMessageToFrontend(message->string()); + } + + void flushProtocolNotifications() override {} + + void sendMessageToFrontend(const StringView& message) + { + delegate->SendMessageToFrontend(message); + } + + void sendMessageToFrontend(const std::string& message) + { + sendMessageToFrontend(Utf8ToStringView(message)->string()); + } + + std::unique_ptr delegate; + std::unique_ptr session; + bool preventShutdown; +}; + +class SameThreadInspectorSession : public InspectorSession { +public: + SameThreadInspectorSession(int sessionId, std::shared_ptr client) + : sessionId(sessionId), client(client) + {} + ~SameThreadInspectorSession() override; + void Dispatch(const v8_inspector::StringView& message) override; + +private: + int sessionId; + std::weak_ptr client; +}; + +} // namespace + +class InspectorClient : public V8InspectorClient { +public: + explicit InspectorClient(Environment* env, bool isMain) : env(env), isMain(isMain) + { + client = V8Inspector::create(env->isolate, this); + std::string name = isMain ? GetHumanReadableProcessName() : GetWorkerLabel(env); + ContextInfo info(name); + info.isDefault = true; + contextCreated(env->context(), info); + } + + void runMessageLoopOnPause(int contextGroupId) override + { + waitingForResume = true; + runMessageLoop(); + } + + void waitForSessionsDisconnect() + { + waitingForSessionsDisconnect = true; + runMessageLoop(); + } + + void waitForFrontend() + { + waitingForFrontend = true; + runMessageLoop(); + } + + void maxAsyncCallStackDepthChanged(int depth) override + { + if (waitingForSessionsDisconnect) { + // V8 isolate is mostly done and is only letting Inspector protocol + // clients gather data. + return; + } + } + + void contextCreated(Local context, const ContextInfo& info) + { + auto nameBuffer = Utf8ToStringView(info.name); + auto originBuffer = Utf8ToStringView(info.origin); + std::unique_ptr auxDataBuffer; + + v8_inspector::V8ContextInfo v8info(context, CONTEXT_GROUP_ID, nameBuffer->string()); + v8info.origin = originBuffer->string(); + + if (info.isDefault) { + auxDataBuffer = Utf8ToStringView("{\"isDefault\":true}"); + } else { + auxDataBuffer = Utf8ToStringView("{\"isDefault\":false}"); + } + v8info.auxData = auxDataBuffer->string(); + + client->contextCreated(v8info); + } + + void contextDestroyed(Local context) + { + client->contextDestroyed(context); + } + + void quitMessageLoopOnPause() override + { + waitingForResume = false; + } + + void runIfWaitingForDebugger(int contextGroupId) override + { + waitingForFrontend = false; + } + + int connectFrontend(std::unique_ptr delegate, bool preventShutdown) + { + int sessionId = nextSessionId++; + channels[sessionId] = + std::make_unique(client, std::move(delegate), getThreadHandle(), preventShutdown); + return sessionId; + } + + void disconnectFrontend(int sessionId) + { + auto it = channels.find(sessionId); + if (it == channels.end()) { + return; + } + channels.erase(it); + if (waitingForSessionsDisconnect && !isMain) { + waitingForSessionsDisconnect = false; + } + } + + void dispatchMessageFromFrontend(int sessionId, const StringView& message) + { + channels[sessionId]->dispatchProtocolMessage(message); + } + + Local ensureDefaultContextInGroup(int contextGroupId) override + { + return env->context(); + } + + void ReportUncaughtException(Local error, Local message) + { + Isolate* isolate = env->isolate; + Local context = env->context(); + + int scriptId = message->GetScriptOrigin().ScriptId(); + + Local stackTrace = message->GetStackTrace(); + + if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0 && + scriptId == stackTrace->GetFrame(isolate, 0)->GetScriptId()) { + scriptId = 0; + } + + const uint8_t DETAILS[] = "Uncaught"; + + client->exceptionThrown(context, StringView(DETAILS, sizeof(DETAILS) - 1), error, + ToProtocolString(isolate, message->Get())->string(), + ToProtocolString(isolate, message->GetScriptResourceName())->string(), + message->GetLineNumber(context).FromMaybe(0), + message->GetStartColumn(context).FromMaybe(0), client->createStackTrace(stackTrace), + scriptId); + } + + void startRepeatingTimer(double interval, TimerCallback callback, void* data) override + { + // TODO: implement this for supporting heap profiler. + } + + void cancelTimer(void* data) override + { + // TODO: implement this for supporting heap profiler. + } + + void schedulePauseOnNextStatement(const std::string& reason) + { + for (const auto& idChannel : channels) { + idChannel.second->schedulePauseOnNextStatement(reason); + } + } + + bool hasConnectedSessions() + { + for (const auto& idChannel : channels) { + // Other sessions are "invisible" more most purposes + if (idChannel.second->PreventShutdown()) { + return true; + } + } + return false; + } + + std::shared_ptr getThreadHandle() + { + if (!interface) { + interface = std::make_shared(static_cast(env->GetInspectorAgent())); + } + return interface->GetHandle(); + } + + bool IsActive() + { + return !channels.empty(); + } + +private: + bool shouldRunMessageLoop() + { + if (waitingForFrontend) { + return true; + } + if (waitingForSessionsDisconnect || waitingForResume) { + return hasConnectedSessions(); + } + return false; + } + + void runMessageLoop() + { + if (runningNestedLoop) { + return; + } + + runningNestedLoop = true; + + while (shouldRunMessageLoop()) { + if (interface) { + interface->WaitForFrontendEvent(); + } + env->RunAndClearInterrupts(); + } + runningNestedLoop = false; + } + + double currentTimeMS() override + { + return env->platform()->CurrentClockTimeMillis(); + } + + Environment* env; + bool isMain; + bool runningNestedLoop = false; + std::unique_ptr client; + std::unordered_map> channels; + int nextSessionId = 1; + bool waitingForResume = false; + bool waitingForFrontend = false; + bool waitingForSessionsDisconnect = false; + // Allows accessing Inspector from non-main threads + std::shared_ptr interface; +}; + +Agent::Agent(Environment* env) : parentEnv(env) {} + +Agent::~Agent() = default; + +bool Agent::Start(const std::string& pathParam, + std::shared_ptr> hostPortParam, + bool isMain, + bool waitForConnect) +{ + path = pathParam; + CHECK_NOT_NULL(hostPortParam); + hostPort = hostPortParam; + + client = std::make_shared(parentEnv, isMain); + + if (!StartIoThread()) { + return false; + } + + if (waitForConnect) { + client->waitForFrontend(); + } + return true; +} + +int FindAvailablePort() +{ + constexpr int startPort = 9229; + constexpr int endPort = 9999; + constexpr int invalidPort = -1; + int sockfd = -1; + + for (auto port = startPort; port <= endPort; ++port) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + continue; + } + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + if (bind(sockfd, reinterpret_cast(&addr), sizeof(addr)) < 0) { + close(sockfd); + if (errno == EADDRINUSE) { + continue; + } else { + break; + } + } + close(sockfd); + return port; + } + return invalidPort; +} + +bool Agent::Start(const std::string& path, int pid) +{ + int port = FindAvailablePort(); + if (port < 0) { + return false; + } + auto hostPort = std::make_shared>("localhost", port, pid); + return Start(path, hostPort, true, false); +} + +bool Agent::StartIoThread() +{ + if (io != nullptr) { + return true; + } + + if (!client) { + return false; + } + + CHECK_NOT_NULL(client); + + io = InspectorIo::Start(client->getThreadHandle(), path, hostPort, { false, true }); + if (io == nullptr) { + return false; + } + return true; +} + +void Agent::Stop() +{ + io.reset(); +} + +std::unique_ptr Agent::Connect(std::unique_ptr delegate, + bool preventShutdown) +{ + if (!client) { + return std::unique_ptr {}; + } + + CHECK_NOT_NULL(client); + + int sessionId = client->connectFrontend(std::move(delegate), preventShutdown); + return std::unique_ptr(new SameThreadInspectorSession(sessionId, client)); +} + +void Agent::WaitForDisconnect() +{ + CHECK_NOT_NULL(client); + if (client->hasConnectedSessions()) { + fprintf(stderr, "Waiting for the debugger to disconnect...\n"); + fflush(stderr); + } + + // TODO: if client->notifyWaitingForDisconnect() + client->contextDestroyed(parentEnv->context()); + + if (io != nullptr) { + io->StopAcceptingNewConnections(); + client->waitForSessionsDisconnect(); + } +} + +void Agent::PauseOnNextJavascriptStatement(const std::string& reason) +{ + client->schedulePauseOnNextStatement(reason); +} + +bool Agent::IsActive() +{ + if (client == nullptr) { + return false; + } + return io != nullptr || client->IsActive(); +} + +void Agent::WaitForConnect() +{ + CHECK_NOT_NULL(client); + client->waitForFrontend(); +} + +SameThreadInspectorSession::~SameThreadInspectorSession() +{ + auto clientLock = client.lock(); + if (clientLock) { + clientLock->disconnectFrontend(sessionId); + } +} + +void SameThreadInspectorSession::Dispatch(const v8_inspector::StringView& message) +{ + auto clientLock = client.lock(); + if (clientLock) { + clientLock->dispatchMessageFromFrontend(sessionId, message); + } +} + +} // namespace v8impl + +jsvm::InspectorAgent* jsvm::InspectorAgent::New(JSVM_Env env) +{ + return new v8impl::Agent(env); +} \ No newline at end of file diff --git a/jsvm/src/inspector/js_native_api_v8_inspector.h b/jsvm/src/inspector/js_native_api_v8_inspector.h new file mode 100644 index 0000000000000000000000000000000000000000..57b205845d214678ccbad4e9a9d1b2fc3b48ab5c --- /dev/null +++ b/jsvm/src/inspector/js_native_api_v8_inspector.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * 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. + */ + +#ifndef JS_NATIVE_API_V8_INSPECTOR_H +#define JS_NATIVE_API_V8_INSPECTOR_H + +#include +#include + +#include "js_native_api_v8.h" +#include "jsvm_host_port.h" +#include "jsvm_inspector_agent.h" +#include "v8.h" + +#ifndef ENABLE_INSPECTOR +#error("This header can only be used when inspector is enabled") +#endif + +namespace v8_inspector { +class StringView; +} // namespace v8_inspector + +namespace jsvm { +namespace inspector { +class InspectorSessionDelegate; +class InspectorSession; + +class InspectorSession { +public: + virtual ~InspectorSession() = default; + virtual void Dispatch(const v8_inspector::StringView& message) = 0; +}; + +class InspectorSessionDelegate { +public: + virtual ~InspectorSessionDelegate() = default; + virtual void SendMessageToFrontend(const v8_inspector::StringView& message) = 0; +}; +} // namespace inspector +} // namespace jsvm + +namespace v8impl { + +using jsvm::ExclusiveAccess; +using jsvm::HostPort; +using jsvm::inspector::InspectorSession; +using jsvm::inspector::InspectorSessionDelegate; + +class IsolateData; +using Environment = JSVM_Env__; + +class InspectorClient; +class InspectorIo; + +struct ContextInfo { + explicit ContextInfo(const std::string& name) : name(name) {} + const std::string name; + std::string origin; + bool isDefault = false; +}; + +class Agent : public jsvm::InspectorAgent { +public: + explicit Agent(Environment* env); + ~Agent(); + +public: + bool Start(const std::string& path, const std::string& hostName, int port, int pid = -1) override + { + auto hostPort = std::make_shared>(hostName, port, pid); + return Start(path, hostPort, true, false); + } + + virtual bool Start(const std::string& path, int pid) override; + + // Stop and destroy io_ + void Stop() override; + + // Returns true if the inspector is actually in use. + bool IsActive() override; + + // Blocks till frontend connects and sends "runIfWaitingForDebugger" + void WaitForConnect() override; + + // Blocks till all the sessions with "WaitForDisconnectOnShutdown" disconnect + void WaitForDisconnect() override; + + void PauseOnNextJavascriptStatement(const std::string& reason) override; + +public: + // Called to create inspector sessions that can be used from the same thread. + // The inspector responds by using the delegate to send messages back. + std::unique_ptr Connect(std::unique_ptr delegate, bool preventShutdown); + + // Can only be called from the main thread. + bool StartIoThread(); + + std::shared_ptr> GetHostPort() + { + return hostPort; + } + + inline Environment* env() const + { + return parentEnv; + } + +private: + // Create client_, may create io if option enabled + bool Start(const std::string& path, + std::shared_ptr> hostPortParam, + bool isMain, + bool waitForConnect); + + Environment* parentEnv; + // Encapsulates majority of the Inspector functionality + std::shared_ptr client; + // Interface for transports, e.g. WebSocket server + std::unique_ptr io; + std::string path; + + std::shared_ptr> hostPort; +}; + +} // namespace v8impl + +#endif \ No newline at end of file diff --git a/jsvm/src/inspector/jsvm_host_port.h b/jsvm/src/inspector/jsvm_host_port.h new file mode 100644 index 0000000000000000000000000000000000000000..9ef6a6cb92cca78732cc3cdf5c52f6996aea3600 --- /dev/null +++ b/jsvm/src/inspector/jsvm_host_port.h @@ -0,0 +1,68 @@ +#ifndef SRC_JSVM_HOST_PORT_H_ +#define SRC_JSVM_HOST_PORT_H_ + +#include "jsvm_dfx.h" +#include "jsvm_mutex.h" +#include "jsvm_util.h" + +namespace jsvm { + +class HostPort { +public: + HostPort(const std::string& hostName, int port, int pid = -1) : hostName(hostName), port(port), pid(pid) {} + HostPort(const HostPort&) = default; + HostPort& operator=(const HostPort&) = default; + HostPort(HostPort&&) = default; + HostPort& operator=(HostPort&&) = default; + + void SetHost(const std::string& host) + { + hostName = host; + } + + void SetPort(int portParam) + { + port = portParam; + } + + const std::string& GetHost() const + { + return hostName; + } + + int GetPort() const + { + // TODO(joyeecheung): make port a uint16_t + CHECK_GE(port, 0); + return port; + } + + int GetPid() const + { + return pid; + } + + void Update(const HostPort& other) + { + if (!other.hostName.empty()) { + hostName = other.hostName; + } + if (other.port >= 0) { + port = other.port; + } + } + +private: + std::string hostName; + int port; + int pid; +}; + +struct InspectPublishUid { + bool console; + bool http; +}; + +} // namespace jsvm + +#endif // SRC_JSVM_HOST_PORT_H_ diff --git a/jsvm/src/inspector/jsvm_mutex.h b/jsvm/src/inspector/jsvm_mutex.h new file mode 100644 index 0000000000000000000000000000000000000000..44b8d02505f5c2c2e7f65faba35f4d9ce703dc26 --- /dev/null +++ b/jsvm/src/inspector/jsvm_mutex.h @@ -0,0 +1,360 @@ +#ifndef SRC_JSVM_MUTEX_H_ +#define SRC_JSVM_MUTEX_H_ + +// #include "util.h" +#include // std::shared_ptr +#include // std::forward + +#include "jsvm_util.h" +#include "uv.h" + +namespace jsvm { + +template +class ConditionVariableBase; +template +class MutexBase; +struct LibuvMutexTraits; +struct LibuvRwlockTraits; + +using ConditionVariable = ConditionVariableBase; +using Mutex = MutexBase; +using RwLock = MutexBase; + +template +class ExclusiveAccess { +public: + ExclusiveAccess() = default; + + template + explicit ExclusiveAccess(Args&&... args) : item(std::forward(args)...) + {} + + ExclusiveAccess(const ExclusiveAccess&) = delete; + ExclusiveAccess& operator=(const ExclusiveAccess&) = delete; + + class Scoped { + public: + // ExclusiveAccess will commonly be used in conjunction with std::shared_ptr + // and without this constructor it's too easy to forget to keep a reference + // around to the shared_ptr while operating on the ExclusiveAccess object. + explicit Scoped(const std::shared_ptr& shared) + : shared(shared), scopedLock(shared->mutex), pointer(&shared->item) + {} + + explicit Scoped(ExclusiveAccess* exclusiveAccess) + : shared(), scopedLock(exclusiveAccess->mutex), pointer(&exclusiveAccess->item) + {} + + T& operator*() const + { + return *pointer; + } + T* operator->() const + { + return pointer; + } + + Scoped(const Scoped&) = delete; + Scoped& operator=(const Scoped&) = delete; + + private: + std::shared_ptr shared; + typename MutexT::ScopedLock scopedLock; + T* const pointer; + }; + +private: + friend class ScopedLock; + MutexT mutex; + T item; +}; + +template +class MutexBase { +public: + inline MutexBase(); + inline ~MutexBase(); + inline void Lock(); + inline void Unlock(); + inline void RdLock(); + inline void RdUnlock(); + + MutexBase(const MutexBase&) = delete; + MutexBase& operator=(const MutexBase&) = delete; + + class ScopedLock; + class ScopedUnlock; + + class ScopedLock { + public: + inline explicit ScopedLock(const MutexBase& mutex); + inline explicit ScopedLock(const ScopedUnlock& scoped_unlock); + inline ~ScopedLock(); + + ScopedLock(const ScopedLock&) = delete; + ScopedLock& operator=(const ScopedLock&) = delete; + + private: + template + friend class ConditionVariableBase; + friend class ScopedUnlock; + const MutexBase& mutex; + }; + + class ScopedReadLock { + public: + inline explicit ScopedReadLock(const MutexBase& mutex); + inline ~ScopedReadLock(); + + ScopedReadLock(const ScopedReadLock&) = delete; + ScopedReadLock& operator=(const ScopedReadLock&) = delete; + + private: + template + friend class ConditionVariableBase; + const MutexBase& mutex; + }; + + using ScopedWriteLock = ScopedLock; + + class ScopedUnlock { + public: + inline explicit ScopedUnlock(const ScopedLock& scopedLock); + inline ~ScopedUnlock(); + + ScopedUnlock(const ScopedUnlock&) = delete; + ScopedUnlock& operator=(const ScopedUnlock&) = delete; + + private: + friend class ScopedLock; + const MutexBase& mutex; + }; + +private: + template + friend class ConditionVariableBase; + mutable typename Traits::MutexT mutex; +}; + +template +class ConditionVariableBase { +public: + using ScopedLock = typename MutexBase::ScopedLock; + + inline ConditionVariableBase(); + inline ~ConditionVariableBase(); + inline void Broadcast(const ScopedLock&); + inline void Signal(const ScopedLock&); + inline void Wait(const ScopedLock& scopedLock); + + ConditionVariableBase(const ConditionVariableBase&) = delete; + ConditionVariableBase& operator=(const ConditionVariableBase&) = delete; + +private: + typename Traits::CondT cond; +}; + +struct LibuvMutexTraits { + using CondT = uv_cond_t; + using MutexT = uv_mutex_t; + + static inline int CondInit(CondT* cond) + { + return uv_cond_init(cond); + } + + static inline int MutexInit(MutexT* mutex) + { + return uv_mutex_init(mutex); + } + + static inline void CondBroadcast(CondT* cond) + { + uv_cond_broadcast(cond); + } + + static inline void CondDestroy(CondT* cond) + { + uv_cond_destroy(cond); + } + + static inline void CondSignal(CondT* cond) + { + uv_cond_signal(cond); + } + + static inline void CondWait(CondT* cond, MutexT* mutex) + { + uv_cond_wait(cond, mutex); + } + + static inline void MutexDestroy(MutexT* mutex) + { + uv_mutex_destroy(mutex); + } + + static inline void MutexLock(MutexT* mutex) + { + uv_mutex_lock(mutex); + } + + static inline void MutexUnlock(MutexT* mutex) + { + uv_mutex_unlock(mutex); + } + + static inline void MutexRdlock(MutexT* mutex) + { + uv_mutex_lock(mutex); + } + + static inline void MutexRdunlock(MutexT* mutex) + { + uv_mutex_unlock(mutex); + } +}; + +struct LibuvRwlockTraits { + using MutexT = uv_rwlock_t; + + static inline int MutexInit(MutexT* mutex) + { + return uv_rwlock_init(mutex); + } + + static inline void MutexDestroy(MutexT* mutex) + { + uv_rwlock_destroy(mutex); + } + + static inline void MutexLock(MutexT* mutex) + { + uv_rwlock_wrlock(mutex); + } + + static inline void MutexUnlock(MutexT* mutex) + { + uv_rwlock_wrunlock(mutex); + } + + static inline void MutexRdlock(MutexT* mutex) + { + uv_rwlock_rdlock(mutex); + } + + static inline void MutexRdunlock(MutexT* mutex) + { + uv_rwlock_rdunlock(mutex); + } +}; + +template +ConditionVariableBase::ConditionVariableBase() +{ + CHECK_EQ(0, Traits::CondInit(&cond)); +} + +template +ConditionVariableBase::~ConditionVariableBase() +{ + Traits::CondDestroy(&cond); +} + +template +void ConditionVariableBase::Broadcast(const ScopedLock&) +{ + Traits::CondBroadcast(&cond); +} + +template +void ConditionVariableBase::Signal(const ScopedLock&) +{ + Traits::CondSignal(&cond); +} + +template +void ConditionVariableBase::Wait(const ScopedLock& scopedLock) +{ + Traits::CondWait(&cond, &scopedLock.mutex.mutex); +} + +template +MutexBase::MutexBase() +{ + CHECK_EQ(0, Traits::MutexInit(&mutex)); +} + +template +MutexBase::~MutexBase() +{ + Traits::MutexDestroy(&mutex); +} + +template +void MutexBase::Lock() +{ + Traits::MutexLock(&mutex); +} + +template +void MutexBase::Unlock() +{ + Traits::MutexUnlock(&mutex); +} + +template +void MutexBase::RdLock() +{ + Traits::MutexRdlock(&mutex); +} + +template +void MutexBase::RdUnlock() +{ + Traits::MutexRdunlock(&mutex); +} + +template +MutexBase::ScopedLock::ScopedLock(const MutexBase& mutex) : mutex(mutex) +{ + Traits::MutexLock(&mutex.mutex); +} + +template +MutexBase::ScopedLock::ScopedLock(const ScopedUnlock& scoped_unlock) : MutexBase(scoped_unlock.mutex) +{} + +template +MutexBase::ScopedLock::~ScopedLock() +{ + Traits::MutexUnlock(&mutex.mutex); +} + +template +MutexBase::ScopedReadLock::ScopedReadLock(const MutexBase& mutex) : mutex(mutex) +{ + Traits::MutexRdlock(&mutex.mutex); +} + +template +MutexBase::ScopedReadLock::~ScopedReadLock() +{ + Traits::MutexRdunlock(&mutex.mutex); +} + +template +MutexBase::ScopedUnlock::ScopedUnlock(const ScopedLock& scopedLock) : mutex(scopedLock.mutex) +{ + Traits::MutexUnlock(&mutex.mutex); +} + +template +MutexBase::ScopedUnlock::~ScopedUnlock() +{ + Traits::MutexLock(&mutex.mutex); +} + +} // namespace jsvm + +#endif // SRC_JSVM_MUTEX_H_ diff --git a/jsvm/src/inspector/v8_inspector_protocol_json.h b/jsvm/src/inspector/v8_inspector_protocol_json.h new file mode 100644 index 0000000000000000000000000000000000000000..deb97dfab398bce8fcbc0bd46138413f0732dd67 --- /dev/null +++ b/jsvm/src/inspector/v8_inspector_protocol_json.h @@ -0,0 +1,667 @@ +1, 66, 204, 120, 218, 237, 125, 235, 115, 220, 70, 146, 231, 191, 130, 208, 110, 132, 200, 139, 102, 123, 188, 27, 55, + 183, 235, 253, 36, 145, 178, 205, 9, 75, 100, 136, 242, 248, 34, 182, 29, 209, 232, 6, 154, 132, 213, 93, 232, 67, + 1, 164, 122, 230, 252, 191, 95, 190, 234, 1, 160, 10, 13, 144, 148, 173, 155, 241, 23, 91, 108, 0, 245, 204, 202, + 204, 202, 199, 47, 255, 254, 226, 62, 175, 116, 81, 170, 23, 223, 252, 253, 197, 46, 253, 165, 172, 94, 124, 243, + 226, 235, 23, 179, 23, 187, 66, 209, 191, 255, 244, 226, 215, 217, 139, 172, 220, 165, 133, 210, 47, 190, 249, 239, + 191, 203, 191, 225, 201, 121, 169, 116, 185, 205, 225, 221, 44, 215, 235, 170, 216, 215, 212, 204, 139, 15, 119, + 133, 78, 248, 173, 4, 255, 149, 239, 171, 124, 157, 214, 121, 150, 156, 37, 141, 206, 147, 247, 141, 170, 139, 93, + 158, 148, 85, 242, 67, 121, 155, 64, 187, 117, 158, 102, 115, 106, 199, 188, 250, 226, 155, 186, 106, 114, 250, 37, + 87, 89, 174, 214, 69, 142, 189, 191, 144, 111, 95, 252, 60, 123, 81, 31, 246, 57, 143, 168, 200, 220, 104, 222, 230, + 90, 167, 183, 253, 65, 201, 227, 100, 199, 207, 177, 55, 108, 0, 158, 148, 171, 95, 242, 117, 13, 127, 239, 171, + 114, 159, 87, 117, 33, 173, 170, 116, 135, 143, 117, 217, 84, 235, 126, 123, 210, 79, 194, 143, 189, 230, 116, 93, + 21, 234, 22, 254, 206, 85, 179, 195, 33, 127, 218, 109, 225, 175, 95, 210, 251, 148, 191, 135, 63, 84, 94, 63, 148, + 213, 71, 248, 215, 154, 135, 117, 150, 238, 11, 248, 75, 215, 101, 197, 131, 79, 247, 251, 117, 186, 190, 195, 127, + 86, 184, 0, 210, 166, 206, 215, 77, 85, 212, 7, 248, 103, 89, 223, 229, 149, 183, 102, 56, 172, 217, 11, 108, 22, + 126, 254, 249, 215, 153, 157, 192, 54, 191, 207, 183, 241, 241, 195, 83, 108, 113, 104, 6, 219, 18, 255, 122, 72, + 43, 37, 191, 87, 85, 201, 93, 175, 26, 252, 187, 80, 155, 178, 213, 101, 157, 127, 170, 163, 61, 226, 195, 126, 111, + 222, 215, 77, 213, 31, 238, 143, 239, 127, 72, 202, 77, 2, 115, 54, 59, 8, 228, 83, 220, 22, 10, 91, 42, 233, 173, + 116, 107, 136, 38, 218, 242, 182, 80, 253, 157, 252, 1, 126, 76, 96, 166, 171, 188, 2, 90, 164, 46, 170, 156, 183, + 21, 254, 72, 235, 228, 54, 87, 121, 69, 244, 91, 35, 101, 155, 254, 79, 190, 62, 91, 165, 58, 207, 78, 7, 134, 80, + 168, 58, 191, 133, 253, 240, 198, 176, 46, 183, 205, 78, 5, 232, 19, 127, 254, 236, 227, 248, 249, 215, 159, 145, + 232, 118, 187, 84, 101, 45, 50, 95, 111, 243, 180, 146, 45, 210, 189, 209, 93, 148, 185, 78, 20, 144, 28, 44, 232, + 220, 159, 77, 86, 232, 116, 21, 224, 1, 23, 252, 187, 78, 132, 192, 133, 31, 204, 18, 32, 214, 251, 92, 213, 58, + 217, 52, 21, 82, 176, 125, 65, 166, 3, 15, 170, 114, 151, 172, 114, 232, 9, 230, 191, 47, 43, 154, 112, 73, 235, + 177, 222, 22, 240, 109, 107, 0, 185, 10, 246, 255, 70, 133, 187, 215, 112, 152, 180, 79, 70, 248, 202, 118, 11, 12, + 0, 122, 209, 101, 178, 73, 171, 118, 103, 201, 234, 0, 175, 166, 74, 11, 249, 45, 212, 82, 190, 124, 149, 101, 121, + 182, 196, 85, 41, 54, 5, 31, 64, 24, 25, 44, 47, 79, 208, 95, 92, 255, 139, 222, 80, 47, 181, 110, 160, 243, 135, + 187, 28, 182, 63, 127, 232, 46, 8, 178, 208, 20, 63, 196, 237, 221, 167, 21, 180, 88, 3, 199, 14, 52, 127, 140, 231, + 49, 21, 221, 165, 26, 86, 23, 250, 178, 141, 254, 107, 149, 111, 250, 12, 20, 73, 5, 215, 217, 178, 251, 11, 60, + 237, 183, 194, 115, 90, 123, 45, 15, 12, 207, 207, 63, 237, 75, 13, 11, 251, 23, 96, 121, 55, 244, 94, 66, 156, 226, + 22, 183, 116, 157, 238, 211, 85, 177, 45, 144, 205, 206, 147, 203, 58, 73, 183, 219, 242, 65, 195, 198, 212, 53, 62, + 7, 186, 132, 109, 223, 149, 247, 240, 199, 66, 173, 170, 60, 253, 184, 47, 129, 124, 53, 236, 93, 157, 239, 247, + 248, 78, 125, 87, 149, 205, 237, 29, 116, 4, 236, 16, 7, 49, 195, 62, 183, 37, 30, 119, 120, 43, 93, 127, 76, 234, + 42, 93, 231, 240, 77, 94, 175, 69, 176, 140, 20, 35, 175, 109, 143, 151, 253, 173, 114, 15, 147, 2, 90, 195, 141, + 207, 171, 48, 47, 99, 153, 4, 147, 251, 22, 119, 44, 208, 22, 62, 3, 90, 135, 135, 227, 218, 250, 161, 180, 76, 190, + 195, 191, 228, 129, 97, 26, 194, 50, 214, 101, 54, 69, 202, 81, 131, 129, 97, 202, 6, 186, 33, 38, 64, 63, 246, 92, + 74, 151, 75, 67, 2, 115, 254, 244, 58, 173, 128, 33, 45, 61, 226, 146, 5, 159, 223, 152, 126, 58, 92, 249, 29, 241, + 189, 49, 188, 153, 31, 39, 39, 127, 242, 184, 222, 49, 110, 27, 105, 61, 200, 115, 67, 237, 31, 231, 170, 102, 151, + 120, 126, 215, 165, 46, 30, 179, 87, 64, 197, 32, 140, 119, 176, 212, 189, 190, 142, 236, 95, 107, 13, 71, 47, 71, + 124, 30, 102, 160, 239, 83, 21, 96, 44, 118, 26, 21, 62, 78, 30, 10, 20, 12, 73, 169, 204, 234, 61, 109, 46, 30, 45, + 142, 160, 30, 56, 239, 85, 237, 94, 237, 108, 64, 75, 82, 100, 241, 215, 126, 238, 159, 217, 222, 172, 61, 126, 182, + 182, 135, 119, 158, 188, 170, 170, 244, 128, 210, 193, 253, 8, 66, 172, 172, 118, 44, 69, 240, 71, 226, 73, 227, 15, + 227, 122, 50, 219, 72, 72, 231, 246, 14, 41, 252, 85, 170, 237, 33, 185, 79, 183, 5, 202, 150, 98, 155, 211, 112, + 238, 139, 170, 110, 210, 109, 178, 3, 229, 18, 207, 22, 188, 183, 79, 65, 41, 111, 9, 2, 175, 123, 111, 253, 54, + 141, 90, 227, 32, 222, 133, 214, 6, 127, 52, 10, 154, 183, 78, 230, 27, 90, 6, 224, 23, 165, 98, 221, 197, 91, 191, + 33, 77, 208, 124, 254, 88, 222, 215, 61, 183, 50, 65, 219, 156, 207, 132, 30, 217, 197, 64, 147, 33, 61, 214, 91, + 27, 97, 52, 42, 229, 171, 16, 188, 61, 95, 168, 11, 119, 89, 130, 254, 54, 233, 61, 60, 129, 101, 109, 52, 203, 61, + 96, 181, 102, 160, 115, 115, 76, 150, 168, 176, 160, 166, 184, 189, 231, 61, 70, 93, 249, 190, 72, 147, 148, 148, + 173, 162, 108, 244, 246, 176, 80, 26, 149, 153, 8, 163, 78, 72, 101, 9, 223, 192, 162, 155, 163, 215, 64, 187, 231, + 119, 168, 28, 244, 133, 6, 60, 74, 214, 248, 12, 79, 194, 192, 158, 167, 120, 120, 240, 10, 81, 231, 59, 141, 87, + 80, 123, 62, 161, 133, 23, 191, 250, 119, 10, 104, 163, 215, 209, 18, 127, 93, 38, 124, 158, 98, 93, 117, 152, 200, + 123, 208, 46, 234, 252, 138, 143, 160, 215, 65, 149, 215, 77, 165, 254, 154, 110, 155, 208, 149, 54, 199, 163, 212, + 228, 86, 49, 197, 119, 243, 108, 150, 20, 76, 243, 150, 208, 81, 91, 171, 229, 121, 66, 170, 194, 0, 33, 30, 27, + 210, 58, 85, 175, 243, 247, 57, 177, 184, 128, 230, 248, 87, 58, 220, 116, 206, 221, 9, 255, 235, 91, 119, 168, 73, + 155, 42, 84, 134, 234, 41, 48, 37, 80, 49, 73, 229, 166, 53, 162, 229, 89, 40, 232, 2, 230, 132, 244, 195, 157, 32, + 41, 130, 74, 59, 79, 222, 193, 136, 88, 99, 76, 147, 37, 142, 122, 41, 43, 0, 45, 160, 62, 205, 23, 130, 133, 186, + 109, 64, 39, 5, 1, 34, 47, 27, 2, 251, 23, 105, 145, 24, 9, 73, 8, 238, 214, 99, 46, 240, 43, 108, 211, 10, 6, 161, + 155, 53, 104, 107, 122, 211, 108, 103, 201, 170, 1, 109, 163, 198, 57, 192, 197, 244, 144, 108, 139, 143, 249, 246, + 16, 19, 40, 17, 193, 188, 42, 65, 145, 77, 85, 91, 48, 35, 65, 133, 9, 213, 251, 109, 60, 147, 166, 183, 194, 237, + 225, 163, 161, 251, 244, 237, 182, 92, 165, 200, 27, 240, 44, 227, 255, 113, 121, 208, 24, 176, 45, 117, 83, 97, + 171, 176, 95, 107, 252, 101, 5, 111, 160, 153, 192, 90, 14, 242, 123, 250, 96, 87, 102, 13, 221, 122, 30, 82, 189, + 59, 131, 149, 129, 213, 70, 35, 206, 25, 73, 154, 214, 93, 220, 78, 163, 61, 82, 38, 54, 212, 228, 224, 83, 20, 27, + 194, 95, 232, 84, 207, 147, 111, 129, 12, 150, 60, 206, 37, 81, 209, 18, 199, 184, 228, 199, 26, 55, 200, 126, 201, + 215, 169, 116, 141, 114, 101, 161, 184, 187, 255, 146, 179, 200, 132, 101, 4, 3, 127, 60, 147, 237, 5, 226, 192, + 139, 83, 1, 210, 8, 180, 117, 165, 233, 182, 37, 39, 25, 87, 10, 239, 186, 164, 208, 227, 87, 11, 117, 159, 86, 5, + 223, 233, 82, 236, 31, 72, 220, 110, 203, 132, 67, 174, 88, 120, 141, 182, 24, 16, 13, 63, 70, 252, 224, 97, 171, + 100, 202, 9, 181, 162, 167, 9, 36, 80, 87, 158, 220, 47, 94, 118, 199, 244, 234, 29, 147, 60, 173, 214, 119, 111, + 133, 252, 58, 196, 77, 207, 64, 117, 128, 135, 180, 191, 198, 60, 49, 254, 208, 140, 87, 245, 173, 233, 3, 174, 195, + 181, 8, 39, 233, 131, 95, 234, 222, 32, 206, 249, 189, 112, 187, 196, 127, 120, 224, 253, 246, 204, 174, 187, 85, + 160, 123, 158, 183, 250, 127, 220, 161, 62, 255, 29, 106, 214, 229, 172, 195, 103, 212, 113, 211, 76, 214, 237, 166, + 6, 33, 183, 99, 18, 64, 5, 128, 76, 167, 40, 134, 95, 252, 236, 109, 237, 79, 192, 48, 209, 56, 165, 117, 190, 91, + 109, 15, 231, 119, 141, 250, 248, 228, 155, 151, 14, 42, 12, 42, 255, 4, 55, 5, 236, 0, 25, 96, 102, 59, 133, 61, + 167, 143, 134, 212, 160, 14, 105, 122, 171, 179, 58, 212, 57, 30, 244, 171, 205, 70, 231, 117, 184, 103, 243, 14, + 244, 75, 47, 137, 132, 91, 89, 38, 143, 252, 8, 7, 149, 195, 53, 128, 6, 51, 98, 44, 118, 167, 122, 215, 221, 31, + 82, 212, 2, 110, 67, 86, 184, 102, 135, 221, 236, 75, 144, 78, 48, 113, 67, 24, 91, 249, 64, 15, 73, 73, 167, 42, + 195, 143, 63, 229, 171, 87, 178, 101, 47, 92, 247, 116, 100, 110, 14, 187, 85, 185, 213, 97, 179, 84, 162, 249, 105, + 2, 141, 21, 91, 148, 31, 196, 185, 210, 4, 5, 167, 119, 91, 125, 130, 204, 255, 0, 63, 26, 9, 151, 249, 125, 14, 77, + 238, 93, 73, 150, 232, 27, 226, 113, 111, 211, 61, 252, 251, 13, 156, 32, 52, 201, 93, 252, 244, 234, 253, 183, 248, + 247, 167, 58, 175, 128, 254, 249, 111, 95, 164, 231, 242, 4, 20, 253, 33, 67, 185, 121, 77, 198, 227, 185, 42, 142, + 136, 191, 184, 145, 184, 68, 69, 161, 201, 63, 148, 81, 209, 116, 46, 175, 104, 103, 157, 75, 144, 111, 65, 247, + 251, 124, 141, 2, 63, 217, 90, 233, 133, 92, 16, 189, 28, 3, 182, 205, 227, 247, 50, 184, 253, 152, 113, 193, 191, + 143, 220, 201, 128, 240, 111, 243, 218, 42, 161, 122, 2, 159, 73, 21, 158, 138, 117, 83, 85, 200, 99, 132, 167, 140, + 53, 131, 27, 38, 69, 212, 119, 91, 192, 117, 11, 84, 116, 116, 67, 141, 55, 101, 183, 154, 192, 253, 245, 154, 73, + 206, 201, 88, 173, 19, 125, 87, 54, 219, 12, 53, 115, 144, 49, 26, 148, 40, 214, 202, 45, 109, 146, 13, 214, 152, + 128, 23, 138, 187, 204, 100, 135, 68, 95, 107, 182, 254, 85, 138, 201, 128, 247, 106, 157, 67, 151, 67, 134, 232, + 244, 19, 159, 88, 125, 142, 219, 122, 83, 252, 45, 124, 149, 130, 247, 138, 29, 176, 6, 13, 47, 160, 204, 64, 126, + 69, 6, 118, 207, 22, 207, 237, 36, 39, 56, 21, 216, 79, 80, 106, 212, 26, 126, 95, 29, 18, 242, 124, 193, 53, 36, + 221, 139, 186, 168, 79, 23, 202, 205, 16, 125, 10, 112, 169, 185, 43, 183, 217, 60, 185, 110, 106, 188, 167, 0, 143, + 219, 161, 214, 9, 172, 200, 12, 156, 76, 36, 240, 99, 205, 19, 154, 112, 187, 48, 250, 199, 207, 70, 192, 180, 214, + 192, 12, 34, 160, 7, 252, 168, 138, 255, 211, 248, 118, 155, 22, 219, 184, 101, 243, 111, 104, 32, 29, 5, 128, 219, + 185, 112, 29, 181, 104, 17, 175, 8, 13, 136, 194, 43, 21, 55, 101, 189, 145, 119, 240, 148, 154, 187, 3, 26, 103, + 82, 33, 170, 246, 5, 58, 188, 217, 147, 173, 84, 120, 82, 205, 224, 18, 190, 106, 13, 91, 156, 220, 216, 250, 19, + 112, 195, 246, 90, 29, 52, 38, 49, 173, 124, 87, 149, 205, 190, 175, 161, 209, 219, 230, 246, 113, 139, 239, 176, + 97, 6, 26, 223, 55, 181, 57, 21, 32, 2, 203, 228, 68, 60, 22, 85, 186, 47, 208, 87, 1, 55, 77, 205, 54, 1, 124, 199, + 107, 230, 14, 206, 13, 28, 220, 133, 98, 235, 205, 146, 95, 149, 75, 9, 141, 99, 121, 58, 201, 125, 89, 168, 245, + 182, 201, 64, 211, 165, 35, 137, 122, 221, 171, 235, 203, 254, 92, 152, 207, 122, 247, 125, 115, 134, 81, 208, 39, + 240, 137, 225, 18, 112, 239, 119, 98, 81, 188, 94, 102, 41, 51, 143, 50, 102, 64, 160, 155, 20, 38, 7, 115, 169, + 209, 71, 182, 213, 67, 98, 196, 222, 190, 189, 107, 84, 177, 13, 233, 230, 151, 42, 225, 39, 201, 14, 181, 149, 252, + 211, 58, 167, 71, 154, 92, 60, 15, 42, 201, 26, 218, 23, 25, 20, 238, 118, 10, 151, 27, 102, 9, 162, 62, 227, 196, + 178, 146, 126, 35, 163, 7, 48, 53, 35, 120, 230, 201, 213, 125, 94, 85, 64, 129, 58, 89, 130, 30, 116, 141, 207, + 175, 212, 27, 211, 205, 18, 21, 161, 122, 226, 84, 248, 200, 191, 62, 132, 13, 69, 63, 89, 19, 139, 101, 165, 5, 29, + 50, 230, 106, 176, 122, 184, 232, 201, 95, 110, 174, 222, 25, 50, 33, 6, 237, 118, 68, 139, 211, 145, 140, 45, 211, + 134, 102, 28, 196, 215, 104, 246, 203, 31, 162, 131, 219, 243, 115, 175, 83, 231, 90, 246, 238, 237, 48, 248, 199, + 218, 93, 124, 179, 29, 108, 228, 149, 186, 129, 77, 120, 179, 217, 132, 44, 17, 118, 201, 74, 222, 117, 216, 81, 71, + 9, 200, 178, 117, 129, 180, 65, 31, 35, 103, 199, 157, 70, 139, 85, 67, 6, 101, 56, 156, 61, 34, 153, 182, 106, 200, + 82, 161, 153, 190, 160, 202, 171, 93, 161, 144, 87, 57, 77, 38, 221, 160, 228, 128, 47, 232, 152, 67, 223, 39, 114, + 241, 1, 70, 190, 43, 182, 219, 66, 131, 222, 13, 74, 211, 233, 216, 133, 235, 112, 246, 15, 240, 159, 139, 124, 91, + 167, 49, 233, 194, 219, 18, 179, 230, 60, 0, 79, 218, 123, 26, 130, 119, 110, 220, 126, 142, 180, 148, 216, 45, 184, + 200, 107, 224, 17, 58, 192, 130, 205, 30, 101, 252, 198, 8, 67, 231, 155, 110, 163, 45, 201, 5, 154, 217, 181, 220, + 20, 156, 167, 181, 223, 241, 123, 94, 22, 119, 171, 48, 58, 34, 185, 93, 18, 231, 51, 158, 39, 230, 74, 142, 74, 6, + 95, 122, 144, 93, 228, 232, 99, 38, 207, 149, 251, 210, 158, 5, 86, 37, 244, 160, 232, 51, 62, 167, 174, 16, 145, + 91, 21, 183, 13, 244, 172, 217, 98, 98, 7, 234, 134, 230, 245, 92, 168, 35, 58, 43, 59, 174, 186, 74, 97, 246, 152, + 158, 146, 19, 216, 86, 144, 33, 64, 190, 167, 243, 228, 39, 10, 57, 128, 227, 36, 170, 57, 218, 179, 161, 179, 133, + 130, 150, 141, 254, 5, 220, 139, 45, 201, 154, 150, 205, 244, 57, 209, 187, 130, 86, 192, 170, 88, 215, 31, 202, + 111, 197, 80, 222, 39, 97, 52, 99, 99, 236, 3, 156, 246, 202, 27, 244, 195, 93, 1, 19, 67, 182, 111, 140, 11, 40, + 151, 65, 45, 84, 103, 112, 135, 6, 150, 117, 234, 140, 239, 48, 72, 218, 154, 49, 12, 32, 124, 190, 108, 191, 1, 51, + 138, 51, 100, 14, 46, 243, 8, 239, 70, 219, 190, 244, 107, 247, 16, 176, 14, 125, 19, 14, 60, 51, 212, 47, 230, 49, + 115, 212, 229, 102, 77, 134, 46, 214, 225, 138, 1, 69, 61, 106, 170, 186, 204, 156, 177, 150, 26, 4, 218, 130, 17, + 121, 189, 13, 26, 164, 194, 139, 170, 135, 230, 99, 188, 97, 220, 193, 73, 190, 219, 215, 7, 220, 234, 53, 168, 74, + 56, 22, 52, 216, 88, 131, 198, 233, 160, 134, 103, 222, 234, 139, 24, 191, 141, 33, 226, 0, 126, 95, 29, 94, 244, + 174, 118, 98, 183, 193, 102, 222, 26, 203, 123, 136, 187, 63, 195, 98, 123, 221, 61, 98, 161, 107, 32, 172, 93, 160, + 39, 52, 231, 111, 241, 234, 155, 176, 231, 64, 207, 140, 139, 42, 77, 248, 35, 14, 187, 146, 195, 150, 101, 5, 175, + 15, 155, 175, 240, 106, 182, 80, 110, 100, 135, 196, 122, 141, 82, 184, 169, 177, 243, 6, 104, 142, 253, 52, 163, + 213, 217, 186, 132, 165, 99, 179, 226, 213, 230, 135, 168, 25, 141, 94, 75, 156, 156, 37, 219, 153, 97, 6, 254, 160, + 58, 129, 133, 1, 203, 162, 97, 20, 175, 203, 236, 48, 100, 63, 51, 102, 51, 232, 141, 46, 50, 134, 191, 172, 202, + 172, 32, 7, 6, 119, 142, 78, 126, 208, 221, 254, 155, 120, 206, 215, 196, 59, 191, 158, 45, 20, 253, 249, 111, 244, + 39, 252, 119, 62, 159, 255, 44, 70, 121, 108, 139, 226, 207, 144, 155, 17, 35, 198, 85, 155, 100, 122, 243, 172, + 178, 98, 185, 236, 143, 126, 83, 84, 58, 104, 120, 60, 120, 103, 55, 104, 7, 109, 17, 62, 154, 47, 35, 214, 210, + 190, 145, 67, 72, 150, 150, 165, 109, 247, 228, 237, 50, 108, 138, 201, 15, 248, 124, 5, 34, 97, 15, 170, 18, 153, + 36, 75, 142, 172, 99, 74, 156, 39, 151, 173, 65, 39, 108, 133, 216, 111, 225, 96, 205, 216, 40, 129, 119, 24, 114, + 30, 22, 138, 2, 28, 210, 58, 23, 187, 38, 211, 255, 66, 113, 24, 25, 83, 56, 168, 147, 196, 84, 104, 72, 243, 228, + 149, 58, 0, 205, 174, 116, 14, 119, 104, 197, 161, 28, 110, 124, 10, 244, 79, 105, 84, 90, 91, 40, 234, 73, 26, 163, + 208, 87, 61, 159, 118, 254, 221, 169, 236, 26, 218, 130, 167, 56, 190, 179, 49, 139, 242, 180, 141, 5, 126, 142, 47, + 189, 142, 113, 203, 15, 29, 187, 143, 139, 9, 152, 39, 63, 106, 188, 44, 180, 36, 212, 112, 0, 247, 163, 89, 226, + 79, 206, 64, 251, 44, 66, 40, 42, 28, 90, 2, 200, 59, 140, 33, 97, 128, 83, 71, 15, 235, 7, 12, 47, 140, 139, 102, + 23, 130, 232, 11, 228, 165, 182, 159, 94, 178, 99, 103, 10, 13, 185, 79, 3, 115, 247, 159, 70, 101, 131, 55, 238, + 232, 247, 237, 233, 210, 133, 58, 160, 231, 150, 123, 205, 161, 60, 66, 146, 126, 84, 139, 241, 194, 180, 76, 155, + 123, 190, 121, 191, 210, 7, 181, 62, 103, 223, 76, 104, 238, 99, 201, 7, 126, 133, 239, 110, 218, 107, 18, 9, 74, + 165, 211, 75, 253, 115, 136, 109, 138, 99, 96, 3, 151, 183, 55, 254, 150, 21, 162, 69, 182, 130, 162, 34, 75, 221, + 210, 113, 119, 229, 189, 119, 113, 9, 144, 7, 190, 208, 138, 139, 245, 238, 42, 81, 93, 109, 213, 142, 72, 245, 85, + 72, 243, 107, 103, 28, 46, 240, 34, 48, 6, 246, 69, 163, 29, 180, 46, 214, 13, 40, 5, 158, 177, 143, 149, 0, 220, + 214, 85, 126, 91, 40, 12, 247, 199, 208, 50, 16, 136, 219, 108, 230, 241, 129, 133, 90, 229, 119, 233, 125, 193, + 193, 73, 75, 191, 199, 37, 46, 31, 94, 72, 234, 244, 224, 71, 161, 144, 209, 204, 6, 125, 159, 95, 92, 27, 14, 163, + 129, 89, 211, 173, 58, 53, 65, 40, 232, 155, 129, 147, 127, 151, 163, 168, 200, 36, 182, 13, 53, 142, 53, 237, 226, + 190, 42, 65, 210, 236, 52, 111, 159, 124, 3, 35, 157, 193, 17, 6, 102, 205, 60, 220, 122, 4, 220, 221, 189, 216, + 237, 242, 172, 72, 107, 144, 40, 220, 97, 225, 5, 67, 219, 238, 196, 20, 254, 0, 226, 136, 124, 19, 237, 213, 48, + 204, 201, 133, 203, 176, 145, 116, 161, 22, 234, 131, 76, 122, 5, 4, 114, 246, 144, 86, 25, 139, 45, 208, 241, 97, + 184, 179, 238, 42, 225, 32, 217, 44, 164, 49, 192, 6, 237, 95, 203, 133, 114, 214, 105, 182, 20, 73, 136, 5, 137, + 68, 114, 18, 211, 123, 109, 35, 246, 174, 208, 154, 166, 223, 238, 97, 161, 88, 86, 161, 153, 130, 199, 71, 49, 76, + 21, 198, 131, 25, 105, 70, 54, 38, 86, 71, 188, 44, 29, 10, 247, 176, 214, 93, 77, 59, 154, 110, 31, 210, 131, 102, + 65, 10, 173, 161, 24, 176, 209, 141, 38, 228, 209, 208, 142, 245, 100, 255, 11, 239, 191, 132, 151, 105, 35, 40, + 102, 108, 242, 2, 53, 5, 141, 163, 165, 130, 35, 247, 215, 255, 96, 90, 161, 224, 169, 99, 43, 46, 218, 216, 239, + 105, 153, 222, 133, 229, 102, 96, 131, 118, 141, 174, 189, 157, 164, 197, 5, 221, 18, 123, 124, 121, 83, 231, 251, + 75, 85, 151, 47, 103, 236, 211, 120, 40, 208, 124, 217, 33, 20, 226, 95, 180, 149, 180, 147, 211, 12, 114, 61, 39, + 150, 233, 242, 197, 207, 49, 229, 195, 119, 141, 117, 98, 60, 209, 100, 232, 248, 228, 96, 184, 96, 236, 230, 235, + 92, 18, 190, 46, 75, 60, 121, 64, 176, 146, 220, 240, 251, 166, 184, 187, 84, 29, 194, 99, 56, 98, 132, 242, 165, + 93, 116, 12, 1, 210, 249, 92, 163, 8, 48, 240, 38, 204, 186, 155, 93, 91, 124, 56, 75, 119, 244, 44, 212, 198, 138, + 121, 165, 222, 135, 219, 189, 97, 106, 196, 65, 210, 255, 3, 102, 207, 102, 47, 22, 68, 178, 123, 122, 246, 245, 75, + 69, 156, 182, 74, 117, 77, 174, 1, 51, 57, 219, 200, 27, 151, 162, 65, 154, 59, 81, 179, 56, 80, 240, 208, 209, 227, + 220, 200, 133, 133, 242, 38, 119, 82, 204, 243, 57, 5, 178, 58, 51, 230, 169, 176, 103, 55, 52, 99, 145, 97, 73, + 131, 10, 222, 66, 33, 191, 162, 112, 52, 96, 245, 188, 152, 192, 115, 128, 177, 240, 237, 150, 205, 53, 102, 132, + 226, 135, 134, 115, 130, 28, 11, 196, 205, 66, 1, 175, 245, 164, 6, 112, 113, 118, 249, 66, 99, 214, 201, 144, 201, + 116, 220, 57, 71, 73, 162, 74, 49, 83, 143, 178, 63, 249, 238, 17, 178, 222, 93, 170, 88, 12, 19, 71, 94, 201, 53, + 202, 40, 44, 100, 244, 70, 187, 166, 132, 164, 187, 184, 166, 103, 48, 67, 136, 61, 177, 101, 155, 28, 138, 63, 130, + 187, 84, 117, 136, 185, 213, 92, 123, 162, 186, 71, 77, 2, 104, 244, 185, 201, 21, 70, 229, 223, 247, 41, 21, 133, + 32, 174, 165, 29, 157, 102, 43, 145, 54, 95, 76, 51, 252, 23, 250, 61, 220, 170, 63, 197, 187, 193, 123, 27, 250, + 212, 121, 22, 110, 175, 41, 122, 11, 190, 124, 188, 153, 49, 98, 198, 55, 54, 70, 237, 69, 218, 229, 99, 12, 138, + 126, 216, 222, 175, 29, 210, 170, 173, 214, 77, 44, 231, 34, 223, 215, 119, 209, 32, 3, 160, 175, 204, 196, 44, 120, + 154, 50, 49, 61, 77, 92, 239, 35, 165, 0, 14, 68, 0, 132, 59, 120, 43, 46, 255, 12, 159, 146, 97, 165, 219, 250, 60, + 185, 145, 28, 48, 160, 151, 229, 159, 140, 228, 163, 3, 69, 198, 37, 51, 50, 19, 37, 64, 233, 98, 216, 10, 70, 49, + 187, 65, 158, 136, 179, 50, 24, 213, 214, 89, 154, 215, 91, 248, 100, 85, 126, 186, 78, 107, 140, 149, 9, 249, 26, + 246, 91, 188, 19, 152, 72, 250, 100, 37, 95, 0, 57, 240, 39, 172, 134, 238, 241, 190, 141, 177, 216, 152, 213, 246, + 109, 9, 55, 73, 77, 138, 32, 218, 204, 145, 252, 63, 22, 123, 155, 191, 246, 21, 178, 16, 62, 188, 11, 101, 236, + 236, 212, 74, 83, 109, 121, 211, 73, 241, 81, 185, 227, 110, 220, 215, 28, 163, 187, 105, 89, 234, 234, 128, 237, 2, + 141, 221, 231, 118, 76, 54, 108, 34, 89, 29, 64, 151, 204, 43, 52, 80, 97, 83, 47, 177, 107, 232, 238, 37, 167, 251, + 98, 196, 45, 28, 100, 61, 3, 29, 76, 25, 46, 89, 86, 102, 237, 249, 109, 80, 54, 94, 162, 124, 107, 188, 232, 236, + 105, 119, 214, 125, 108, 81, 109, 198, 12, 157, 162, 189, 102, 125, 80, 162, 193, 217, 221, 128, 65, 61, 119, 57, 8, + 90, 153, 16, 174, 12, 57, 119, 204, 242, 91, 207, 237, 216, 8, 186, 200, 206, 231, 25, 165, 56, 233, 0, 193, 126, + 244, 246, 208, 110, 160, 238, 196, 56, 98, 244, 138, 91, 126, 242, 141, 116, 182, 201, 219, 163, 109, 161, 233, 246, + 100, 54, 125, 5, 87, 164, 207, 176, 75, 11, 101, 18, 155, 80, 183, 199, 181, 70, 225, 128, 9, 252, 232, 185, 40, + 140, 95, 5, 77, 146, 237, 245, 36, 142, 122, 135, 115, 128, 107, 215, 183, 100, 70, 196, 147, 83, 129, 252, 133, 71, + 234, 37, 8, 121, 55, 89, 147, 249, 228, 185, 174, 203, 106, 32, 154, 230, 137, 82, 105, 164, 32, 178, 19, 28, 147, + 99, 210, 206, 1, 235, 145, 72, 252, 42, 127, 131, 214, 225, 224, 61, 62, 161, 132, 9, 150, 209, 54, 85, 231, 105, + 209, 109, 120, 93, 240, 58, 56, 234, 47, 68, 15, 116, 56, 253, 176, 29, 53, 131, 151, 233, 20, 111, 159, 94, 227, + 246, 91, 241, 14, 122, 158, 193, 172, 101, 81, 161, 172, 19, 93, 151, 123, 49, 4, 249, 185, 186, 156, 20, 83, 180, + 130, 139, 114, 27, 113, 36, 186, 230, 152, 56, 196, 176, 33, 111, 40, 67, 215, 81, 205, 26, 133, 55, 6, 140, 185, + 97, 33, 3, 49, 22, 8, 27, 82, 54, 143, 217, 84, 188, 171, 1, 41, 147, 199, 163, 241, 105, 206, 94, 127, 146, 148, + 149, 81, 220, 80, 112, 211, 58, 20, 119, 137, 38, 240, 134, 142, 13, 185, 41, 142, 16, 96, 209, 126, 125, 148, 53, + 169, 243, 77, 32, 36, 167, 221, 166, 10, 230, 232, 185, 11, 229, 42, 135, 85, 205, 249, 24, 88, 101, 31, 51, 87, + 188, 223, 127, 2, 217, 102, 35, 93, 221, 75, 63, 255, 206, 27, 28, 61, 238, 175, 15, 63, 6, 82, 247, 134, 207, 124, + 251, 196, 187, 115, 147, 228, 5, 13, 7, 88, 61, 133, 230, 86, 244, 63, 86, 31, 147, 43, 69, 216, 14, 133, 6, 13, + 198, 217, 219, 11, 2, 4, 152, 177, 187, 232, 19, 136, 12, 209, 62, 181, 23, 22, 73, 103, 240, 142, 4, 139, 139, 148, + 112, 4, 231, 220, 31, 68, 125, 11, 101, 211, 6, 245, 210, 36, 206, 28, 128, 197, 203, 90, 89, 157, 67, 132, 26, 118, + 134, 127, 138, 231, 67, 98, 223, 60, 175, 9, 52, 232, 250, 125, 47, 189, 58, 59, 15, 77, 64, 12, 119, 219, 242, 182, + 0, 245, 204, 95, 46, 106, 86, 55, 213, 61, 172, 25, 197, 175, 98, 20, 93, 153, 102, 122, 128, 81, 142, 203, 111, + 232, 115, 203, 244, 136, 75, 240, 8, 216, 136, 73, 67, 209, 129, 166, 203, 105, 224, 35, 208, 83, 248, 190, 65, 191, + 26, 29, 207, 122, 162, 96, 16, 122, 196, 40, 208, 16, 63, 79, 222, 48, 145, 45, 161, 143, 37, 208, 24, 108, 143, + 233, 109, 105, 45, 80, 150, 36, 39, 141, 154, 7, 250, 125, 170, 239, 98, 142, 19, 184, 249, 222, 125, 134, 229, 26, + 204, 58, 97, 231, 173, 81, 197, 40, 174, 49, 182, 245, 83, 144, 89, 254, 144, 155, 207, 42, 55, 199, 5, 211, 184, + 80, 159, 65, 249, 201, 70, 40, 19, 154, 48, 226, 78, 28, 137, 175, 105, 113, 250, 43, 101, 226, 145, 196, 43, 53, + 154, 229, 179, 124, 227, 4, 26, 186, 125, 74, 252, 44, 203, 1, 107, 163, 38, 107, 82, 170, 56, 100, 221, 198, 17, + 160, 131, 195, 172, 179, 53, 155, 83, 92, 147, 184, 56, 137, 188, 184, 41, 184, 6, 206, 248, 130, 75, 87, 198, 218, + 152, 208, 116, 105, 140, 86, 108, 48, 111, 201, 254, 241, 250, 55, 7, 161, 134, 66, 70, 204, 96, 37, 76, 181, 200, + 142, 4, 15, 182, 247, 254, 183, 56, 76, 24, 49, 96, 15, 82, 242, 143, 118, 142, 162, 68, 171, 95, 173, 131, 230, 49, + 250, 153, 166, 248, 21, 172, 83, 106, 255, 66, 234, 108, 11, 12, 49, 42, 220, 14, 132, 56, 166, 225, 62, 208, 7, + 192, 105, 224, 237, 80, 75, 178, 184, 162, 56, 239, 222, 203, 35, 246, 206, 94, 44, 118, 40, 127, 107, 67, 161, 35, + 236, 187, 45, 149, 31, 30, 206, 189, 36, 231, 28, 134, 36, 254, 20, 67, 12, 172, 55, 153, 119, 103, 112, 55, 94, + 167, 205, 237, 93, 237, 255, 184, 80, 37, 58, 62, 187, 63, 147, 9, 215, 254, 137, 246, 109, 160, 195, 116, 27, 31, + 3, 234, 107, 75, 5, 103, 116, 57, 24, 45, 90, 247, 87, 242, 58, 208, 228, 174, 141, 217, 211, 83, 182, 21, 103, 141, + 241, 184, 225, 31, 102, 102, 8, 24, 7, 236, 235, 231, 238, 34, 191, 31, 128, 52, 56, 167, 123, 126, 219, 21, 72, 2, + 21, 22, 209, 32, 155, 216, 228, 0, 146, 90, 14, 215, 128, 246, 221, 218, 18, 166, 49, 28, 149, 63, 132, 7, 132, 164, + 229, 15, 38, 192, 110, 144, 73, 191, 170, 110, 233, 122, 210, 163, 168, 193, 168, 201, 55, 89, 209, 102, 229, 194, + 104, 183, 104, 53, 70, 239, 232, 165, 146, 32, 248, 237, 204, 242, 105, 49, 76, 161, 123, 212, 249, 0, 228, 244, + 176, 27, 8, 189, 210, 18, 143, 158, 67, 15, 8, 227, 5, 154, 216, 66, 165, 9, 234, 207, 91, 47, 165, 225, 27, 227, + 194, 149, 247, 124, 192, 136, 154, 162, 220, 246, 103, 187, 82, 215, 210, 48, 123, 40, 83, 12, 198, 165, 49, 200, + 91, 188, 15, 124, 178, 173, 247, 35, 245, 112, 86, 252, 209, 145, 123, 70, 240, 48, 180, 196, 53, 227, 124, 105, 8, + 214, 220, 230, 236, 70, 236, 167, 247, 18, 145, 219, 142, 72, 81, 76, 209, 207, 35, 67, 109, 129, 94, 52, 117, 9, + 215, 8, 84, 245, 49, 20, 207, 186, 83, 158, 199, 29, 129, 35, 30, 105, 0, 26, 12, 54, 125, 199, 248, 106, 53, 65, + 13, 116, 173, 75, 81, 85, 52, 171, 14, 208, 99, 204, 79, 192, 220, 254, 78, 144, 144, 96, 81, 41, 249, 206, 248, + 159, 48, 221, 99, 191, 223, 162, 218, 157, 92, 84, 135, 164, 106, 20, 92, 183, 14, 190, 161, 19, 67, 155, 248, 146, + 181, 80, 94, 15, 68, 74, 152, 99, 96, 219, 2, 246, 80, 108, 14, 38, 137, 247, 88, 44, 107, 223, 225, 65, 126, 183, + 15, 229, 158, 54, 20, 79, 4, 115, 151, 152, 247, 227, 142, 98, 150, 188, 229, 148, 72, 0, 104, 196, 88, 104, 57, + 252, 220, 199, 63, 41, 153, 131, 152, 197, 69, 58, 132, 227, 128, 215, 64, 178, 214, 199, 105, 125, 36, 133, 63, 26, + 9, 228, 137, 222, 110, 27, 146, 156, 243, 186, 145, 195, 239, 14, 211, 45, 84, 110, 0, 150, 208, 234, 75, 33, 232, + 37, 252, 156, 141, 243, 10, 63, 202, 95, 78, 227, 98, 6, 158, 69, 243, 105, 132, 101, 121, 46, 150, 132, 134, 71, + 84, 132, 150, 9, 9, 187, 1, 234, 116, 52, 197, 66, 97, 210, 208, 67, 132, 246, 79, 239, 207, 111, 237, 85, 221, 232, + 193, 68, 49, 52, 199, 164, 246, 98, 224, 49, 100, 131, 197, 67, 185, 17, 203, 171, 143, 75, 208, 235, 224, 7, 212, + 233, 124, 208, 28, 159, 171, 91, 28, 32, 190, 114, 160, 230, 64, 81, 63, 169, 34, 24, 0, 252, 26, 94, 58, 116, 165, + 193, 6, 36, 125, 220, 116, 31, 211, 71, 174, 48, 76, 245, 188, 220, 237, 225, 227, 55, 2, 10, 251, 26, 33, 107, 242, + 236, 245, 129, 181, 212, 239, 56, 179, 44, 244, 200, 75, 5, 177, 79, 128, 59, 253, 128, 120, 181, 111, 52, 7, 218, + 51, 145, 119, 178, 223, 167, 38, 38, 153, 109, 229, 117, 52, 145, 56, 15, 119, 18, 147, 9, 219, 67, 156, 109, 233, + 207, 100, 249, 228, 108, 38, 84, 73, 62, 22, 251, 87, 219, 45, 169, 122, 49, 15, 19, 217, 191, 80, 100, 144, 167, + 165, 106, 246, 53, 233, 176, 234, 96, 34, 162, 78, 156, 150, 61, 115, 250, 196, 12, 209, 54, 189, 60, 185, 188, 94, + 159, 14, 8, 91, 24, 200, 17, 93, 158, 60, 92, 210, 229, 88, 37, 254, 175, 130, 201, 51, 172, 97, 114, 31, 192, 200, + 13, 132, 15, 242, 210, 148, 248, 146, 40, 154, 124, 129, 100, 176, 16, 3, 49, 100, 18, 61, 117, 179, 247, 50, 61, + 197, 152, 181, 80, 187, 166, 166, 235, 213, 46, 85, 36, 28, 135, 20, 13, 104, 47, 98, 71, 18, 128, 18, 47, 187, 128, + 129, 115, 224, 24, 226, 81, 100, 127, 29, 7, 89, 88, 68, 51, 33, 163, 151, 4, 222, 244, 114, 150, 188, 20, 216, 166, + 151, 52, 192, 151, 4, 221, 244, 18, 125, 124, 6, 10, 138, 231, 34, 114, 19, 190, 166, 131, 41, 179, 92, 27, 223, 25, + 204, 163, 216, 55, 219, 222, 156, 226, 230, 42, 179, 154, 65, 60, 62, 179, 51, 17, 251, 125, 43, 204, 127, 64, 31, + 183, 59, 54, 78, 35, 159, 141, 10, 193, 99, 5, 207, 110, 191, 192, 210, 150, 219, 76, 219, 254, 98, 17, 120, 45, + 250, 51, 161, 108, 253, 168, 19, 118, 211, 138, 69, 166, 133, 65, 120, 36, 204, 182, 29, 162, 124, 60, 162, 88, 212, + 237, 94, 56, 18, 167, 96, 112, 136, 67, 157, 234, 143, 18, 122, 212, 10, 108, 197, 32, 90, 178, 33, 81, 8, 53, 181, + 247, 244, 116, 91, 60, 197, 104, 92, 11, 6, 39, 154, 135, 214, 160, 162, 157, 195, 130, 61, 215, 189, 100, 100, 248, + 98, 207, 184, 141, 226, 156, 102, 71, 214, 132, 65, 30, 51, 207, 49, 194, 232, 175, 189, 173, 189, 106, 234, 200, + 206, 162, 78, 92, 110, 66, 123, 219, 109, 226, 62, 112, 228, 165, 141, 123, 145, 189, 94, 188, 250, 16, 243, 252, + 140, 107, 138, 67, 249, 108, 107, 26, 132, 163, 238, 187, 110, 250, 214, 191, 162, 50, 152, 212, 190, 93, 205, 115, + 49, 193, 233, 130, 203, 47, 223, 77, 204, 77, 141, 64, 6, 142, 58, 189, 87, 35, 33, 150, 155, 46, 92, 198, 4, 147, + 115, 200, 56, 214, 180, 189, 80, 254, 64, 7, 29, 179, 28, 241, 55, 180, 68, 33, 100, 85, 185, 5, 36, 45, 191, 44, + 106, 116, 78, 102, 35, 16, 145, 50, 120, 38, 100, 189, 130, 230, 97, 181, 138, 116, 68, 108, 179, 14, 135, 54, 139, + 58, 59, 56, 160, 17, 198, 243, 240, 189, 3, 230, 161, 3, 107, 203, 166, 44, 126, 56, 100, 190, 74, 119, 171, 226, + 182, 41, 73, 27, 198, 120, 41, 202, 161, 62, 191, 185, 254, 107, 81, 110, 221, 174, 1, 151, 21, 132, 11, 248, 243, + 226, 234, 45, 66, 33, 33, 9, 227, 41, 67, 101, 146, 142, 138, 44, 33, 21, 31, 232, 58, 180, 175, 232, 27, 83, 31, + 97, 95, 149, 187, 66, 231, 239, 115, 84, 49, 248, 133, 255, 253, 253, 123, 42, 182, 144, 239, 91, 138, 101, 150, + 214, 105, 44, 179, 94, 2, 104, 240, 158, 68, 187, 121, 102, 209, 140, 210, 230, 83, 177, 45, 210, 234, 208, 1, 36, + 140, 28, 221, 178, 151, 104, 127, 87, 212, 67, 217, 238, 223, 23, 109, 119, 223, 229, 197, 0, 104, 209, 4, 28, 177, + 39, 221, 209, 126, 143, 27, 217, 35, 224, 20, 98, 119, 178, 180, 21, 17, 25, 27, 205, 59, 140, 194, 50, 215, 133, + 153, 53, 152, 113, 158, 79, 244, 174, 244, 236, 129, 224, 147, 57, 143, 124, 230, 71, 135, 247, 236, 99, 223, 210, + 125, 239, 67, 73, 40, 131, 67, 29, 116, 27, 223, 208, 133, 10, 97, 113, 240, 203, 142, 5, 109, 178, 165, 175, 11, + 132, 228, 197, 31, 132, 179, 175, 2, 6, 191, 168, 7, 191, 18, 108, 229, 64, 211, 201, 9, 211, 213, 112, 62, 57, 131, + 127, 70, 107, 134, 112, 150, 112, 167, 125, 193, 66, 111, 85, 239, 240, 242, 204, 112, 96, 39, 116, 221, 18, 219, + 102, 122, 171, 143, 192, 47, 210, 40, 206, 7, 171, 134, 60, 114, 36, 195, 253, 230, 12, 49, 212, 159, 123, 10, 218, + 206, 182, 80, 121, 212, 130, 26, 110, 44, 50, 133, 31, 114, 117, 91, 219, 0, 130, 237, 163, 26, 55, 132, 78, 241, + 243, 159, 130, 16, 156, 86, 59, 51, 225, 242, 232, 158, 35, 205, 145, 191, 9, 16, 219, 155, 126, 179, 62, 219, 14, + 133, 68, 72, 0, 127, 43, 38, 130, 159, 207, 146, 155, 239, 95, 157, 253, 219, 255, 252, 243, 32, 197, 117, 103, 242, + 170, 249, 116, 17, 146, 74, 130, 21, 88, 133, 196, 16, 138, 49, 129, 77, 118, 129, 61, 127, 47, 244, 5, 135, 69, + 127, 147, 200, 181, 97, 70, 151, 211, 111, 146, 151, 18, 47, 253, 242, 255, 190, 44, 116, 73, 55, 80, 248, 39, 23, + 18, 130, 235, 237, 134, 239, 94, 223, 72, 24, 252, 175, 83, 68, 155, 54, 65, 95, 3, 72, 133, 66, 153, 187, 116, 143, + 136, 117, 229, 186, 72, 141, 27, 197, 194, 136, 122, 103, 117, 116, 248, 8, 236, 0, 27, 173, 67, 93, 127, 32, 3, + 183, 241, 18, 107, 27, 199, 34, 131, 145, 163, 49, 37, 147, 192, 194, 66, 140, 232, 8, 254, 245, 230, 230, 207, 146, + 10, 63, 173, 163, 45, 29, 150, 112, 198, 182, 193, 214, 164, 87, 166, 69, 190, 232, 184, 26, 224, 185, 204, 80, 65, + 245, 45, 246, 176, 121, 28, 68, 220, 103, 175, 116, 231, 160, 235, 174, 245, 4, 145, 36, 55, 14, 197, 39, 203, 243, + 118, 168, 129, 65, 67, 13, 58, 51, 236, 224, 12, 76, 41, 110, 128, 135, 39, 58, 179, 222, 148, 68, 231, 226, 196, + 104, 133, 23, 185, 141, 154, 112, 69, 11, 173, 242, 48, 88, 234, 7, 98, 127, 50, 196, 30, 247, 155, 176, 90, 214, + 127, 215, 65, 103, 245, 249, 140, 240, 143, 160, 237, 136, 144, 6, 216, 48, 147, 39, 230, 77, 178, 195, 161, 27, + 203, 65, 67, 78, 27, 92, 8, 108, 185, 181, 48, 12, 120, 60, 69, 23, 33, 98, 179, 227, 224, 112, 67, 166, 60, 242, + 83, 97, 57, 39, 250, 152, 16, 95, 65, 121, 251, 168, 16, 83, 14, 47, 169, 141, 178, 72, 147, 46, 26, 159, 2, 140, 8, + 17, 19, 57, 166, 143, 201, 248, 135, 94, 243, 135, 94, 243, 135, 94, 243, 135, 94, 227, 228, 253, 15, 64, 107, 232, + 65, 31, 45, 241, 29, 194, 34, 69, 187, 73, 80, 181, 161, 14, 235, 135, 179, 174, 192, 103, 48, 5, 255, 161, 124, + 253, 161, 124, 253, 161, 124, 125, 121, 202, 87, 54, 132, 90, 63, 184, 104, 55, 254, 162, 201, 217, 69, 32, 168, 22, + 210, 125, 27, 106, 235, 177, 195, 111, 65, 235, 127, 97, 154, 99, 187, 46, 229, 247, 121, 186, 191, 174, 202, 77, + 177, 21, 219, 112, 208, 38, 55, 178, 252, 35, 54, 118, 163, 210, 189, 190, 43, 235, 171, 88, 104, 52, 190, 148, 104, + 121, 171, 29, 31, 29, 41, 222, 120, 147, 238, 246, 168, 85, 122, 99, 125, 23, 196, 162, 146, 247, 18, 234, 66, 222, + 76, 20, 70, 122, 37, 223, 147, 135, 148, 224, 202, 10, 12, 60, 85, 12, 63, 71, 177, 0, 232, 92, 54, 153, 71, 104, 7, + 215, 117, 177, 214, 164, 233, 130, 152, 36, 60, 248, 172, 149, 71, 62, 182, 0, 93, 60, 42, 60, 224, 61, 241, 125, + 194, 226, 59, 240, 163, 5, 182, 155, 32, 30, 252, 171, 173, 7, 194, 218, 194, 131, 119, 152, 108, 153, 192, 229, 17, + 90, 28, 205, 168, 202, 213, 96, 69, 152, 34, 96, 206, 197, 102, 96, 151, 146, 75, 65, 224, 19, 255, 82, 186, 174, + 74, 205, 81, 210, 123, 94, 112, 191, 38, 236, 42, 175, 31, 242, 92, 208, 99, 237, 246, 16, 138, 11, 176, 100, 243, + 195, 145, 2, 42, 50, 226, 64, 132, 68, 104, 115, 162, 249, 170, 17, 34, 106, 21, 228, 232, 191, 67, 63, 5, 214, 221, + 132, 168, 106, 122, 206, 121, 8, 41, 255, 69, 9, 103, 252, 245, 132, 18, 162, 195, 187, 219, 217, 220, 180, 134, 35, + 178, 106, 188, 226, 190, 60, 142, 193, 93, 197, 149, 26, 14, 186, 111, 225, 10, 202, 20, 16, 199, 65, 14, 209, 224, + 54, 149, 85, 134, 137, 214, 125, 174, 6, 68, 125, 6, 15, 73, 144, 202, 106, 201, 187, 18, 56, 66, 133, 108, 17, 41, + 55, 78, 81, 85, 14, 243, 5, 201, 76, 110, 255, 137, 36, 101, 11, 14, 12, 109, 115, 156, 151, 76, 222, 201, 187, 60, + 245, 75, 101, 198, 200, 206, 59, 220, 180, 40, 250, 81, 36, 44, 228, 249, 107, 188, 234, 72, 154, 101, 151, 74, 51, + 130, 58, 126, 120, 21, 174, 104, 214, 45, 249, 76, 85, 17, 55, 185, 173, 231, 76, 108, 164, 133, 143, 75, 224, 50, + 255, 250, 41, 57, 209, 64, 31, 226, 115, 76, 126, 48, 32, 249, 200, 126, 118, 37, 65, 99, 81, 116, 215, 66, 193, + 171, 54, 52, 124, 32, 216, 234, 206, 142, 113, 188, 236, 48, 200, 240, 12, 165, 74, 208, 194, 94, 221, 105, 232, + 184, 139, 225, 239, 241, 221, 160, 204, 106, 25, 88, 132, 157, 125, 151, 86, 171, 174, 46, 34, 133, 75, 250, 101, + 72, 218, 168, 135, 223, 183, 231, 52, 49, 151, 168, 111, 16, 49, 216, 247, 28, 253, 236, 213, 242, 192, 181, 8, 3, + 60, 118, 115, 141, 194, 17, 189, 119, 99, 4, 184, 99, 24, 119, 161, 237, 8, 161, 147, 26, 132, 143, 138, 70, 17, 76, + 138, 58, 190, 15, 48, 87, 126, 240, 250, 48, 117, 65, 7, 251, 24, 89, 228, 130, 180, 57, 184, 83, 251, 245, 45, 48, + 54, 69, 208, 124, 77, 60, 186, 212, 169, 72, 118, 88, 206, 130, 152, 29, 87, 89, 121, 116, 198, 84, 4, 237, 230, + 205, 100, 116, 250, 46, 20, 167, 112, 19, 199, 1, 3, 157, 239, 35, 236, 145, 115, 99, 156, 228, 241, 5, 6, 23, 249, + 180, 74, 192, 124, 144, 25, 118, 163, 210, 60, 166, 30, 55, 29, 202, 11, 151, 130, 174, 209, 151, 155, 8, 0, 114, + 107, 133, 179, 67, 225, 16, 249, 57, 79, 174, 203, 2, 110, 236, 10, 65, 113, 88, 146, 74, 34, 6, 237, 162, 81, 157, + 204, 119, 154, 96, 28, 49, 189, 128, 76, 34, 38, 219, 71, 39, 255, 254, 111, 255, 235, 207, 255, 33, 77, 190, 56, + 90, 241, 166, 87, 138, 132, 55, 70, 159, 155, 149, 122, 125, 120, 155, 254, 82, 86, 223, 157, 247, 227, 122, 14, + 166, 136, 200, 172, 189, 230, 116, 2, 101, 225, 43, 41, 235, 33, 197, 139, 133, 238, 28, 4, 60, 38, 252, 113, 246, + 35, 26, 76, 172, 3, 220, 108, 91, 161, 93, 210, 59, 178, 246, 62, 137, 80, 110, 180, 47, 101, 103, 210, 56, 47, 28, + 198, 82, 227, 210, 101, 57, 195, 131, 81, 14, 188, 159, 125, 160, 25, 238, 140, 20, 23, 14, 161, 166, 60, 6, 74, 62, + 3, 161, 121, 56, 227, 164, 176, 29, 80, 109, 117, 128, 22, 169, 54, 19, 25, 8, 54, 219, 244, 86, 0, 27, 214, 82, 50, + 211, 44, 193, 66, 181, 215, 0, 35, 171, 208, 120, 45, 107, 236, 107, 250, 73, 186, 162, 232, 55, 89, 24, 216, 251, + 117, 10, 138, 73, 70, 128, 63, 59, 92, 250, 228, 187, 115, 51, 39, 78, 179, 191, 43, 31, 228, 111, 111, 18, 20, 176, + 195, 128, 224, 32, 161, 97, 201, 209, 192, 198, 163, 198, 114, 53, 116, 9, 174, 56, 103, 227, 187, 115, 137, 10, + 158, 104, 149, 137, 18, 72, 161, 254, 32, 144, 223, 137, 64, 112, 233, 61, 2, 113, 115, 226, 149, 106, 104, 62, 105, + 130, 182, 82, 181, 134, 177, 26, 92, 53, 78, 97, 98, 61, 126, 161, 72, 49, 130, 185, 239, 128, 31, 1, 117, 80, 178, + 78, 81, 31, 38, 131, 222, 33, 167, 252, 32, 136, 98, 78, 28, 234, 56, 148, 33, 190, 235, 93, 23, 39, 247, 231, 118, + 117, 170, 168, 88, 151, 180, 136, 65, 154, 156, 38, 29, 202, 253, 148, 41, 51, 181, 67, 83, 183, 152, 199, 28, 205, + 64, 123, 201, 239, 249, 202, 129, 249, 230, 165, 1, 229, 48, 225, 75, 206, 16, 205, 185, 34, 86, 249, 193, 220, 123, + 146, 123, 117, 250, 17, 107, 195, 217, 211, 99, 80, 223, 24, 24, 217, 230, 54, 77, 168, 176, 131, 62, 137, 239, 168, + 152, 176, 76, 248, 149, 126, 95, 150, 117, 40, 231, 55, 88, 254, 124, 153, 127, 218, 151, 58, 39, 105, 9, 61, 234, + 229, 83, 19, 148, 214, 233, 30, 118, 31, 179, 2, 242, 170, 88, 135, 227, 223, 109, 34, 156, 226, 183, 128, 218, 61, + 188, 92, 57, 123, 182, 124, 171, 89, 198, 73, 11, 211, 153, 86, 124, 8, 252, 162, 102, 121, 142, 175, 90, 27, 165, + 116, 251, 164, 2, 217, 182, 54, 226, 199, 220, 39, 161, 47, 144, 42, 63, 23, 221, 217, 133, 78, 147, 42, 125, 112, + 189, 219, 20, 76, 87, 171, 186, 194, 6, 250, 227, 158, 47, 212, 31, 196, 251, 155, 19, 111, 48, 188, 61, 205, 50, + 159, 230, 76, 209, 138, 72, 60, 181, 60, 29, 8, 79, 160, 43, 37, 136, 109, 253, 227, 62, 11, 101, 242, 195, 68, 253, + 98, 148, 142, 95, 58, 116, 113, 129, 174, 166, 148, 90, 3, 41, 136, 89, 192, 26, 255, 209, 80, 179, 164, 87, 16, + 230, 163, 152, 29, 54, 112, 1, 216, 229, 106, 64, 58, 232, 129, 65, 189, 82, 2, 253, 135, 107, 93, 225, 53, 14, 46, + 112, 201, 27, 66, 52, 231, 63, 77, 81, 96, 164, 9, 219, 25, 195, 189, 111, 44, 244, 223, 45, 99, 140, 83, 174, 133, + 188, 178, 0, 45, 33, 203, 63, 137, 150, 70, 245, 206, 252, 87, 83, 41, 216, 178, 46, 27, 78, 178, 54, 203, 98, 238, + 36, 166, 29, 110, 160, 190, 43, 42, 255, 123, 204, 157, 231, 6, 200, 94, 216, 50, 23, 244, 219, 152, 94, 182, 216, + 248, 242, 82, 93, 223, 192, 214, 196, 77, 4, 143, 219, 212, 42, 191, 69, 76, 253, 45, 111, 45, 174, 134, 73, 195, + 117, 153, 118, 91, 66, 75, 214, 216, 134, 179, 121, 144, 173, 190, 109, 119, 64, 240, 199, 26, 52, 10, 139, 254, + 254, 128, 254, 62, 73, 209, 53, 71, 151, 13, 24, 5, 2, 137, 81, 140, 1, 29, 7, 210, 60, 85, 139, 154, 58, 68, 220, + 229, 191, 92, 117, 144, 243, 145, 210, 68, 229, 15, 73, 119, 133, 248, 139, 1, 132, 174, 254, 138, 14, 148, 248, 54, + 115, 139, 24, 57, 91, 130, 38, 36, 64, 162, 195, 200, 24, 34, 99, 160, 107, 36, 174, 225, 42, 65, 160, 219, 234, 59, + 10, 69, 154, 34, 62, 49, 78, 188, 22, 173, 207, 164, 130, 90, 39, 149, 231, 160, 138, 57, 163, 102, 214, 249, 22, + 240, 75, 13, 57, 141, 198, 187, 137, 92, 158, 218, 115, 120, 137, 138, 129, 10, 183, 230, 224, 30, 183, 186, 127, 6, + 95, 211, 93, 81, 159, 35, 247, 233, 187, 128, 92, 154, 39, 91, 172, 173, 15, 189, 208, 98, 31, 78, 117, 7, 194, 192, + 165, 207, 79, 68, 19, 59, 238, 244, 129, 117, 26, 186, 85, 79, 169, 6, 149, 229, 208, 200, 251, 112, 122, 206, 135, + 59, 147, 156, 131, 115, 98, 109, 10, 51, 107, 177, 219, 29, 48, 217, 76, 152, 190, 89, 110, 129, 167, 160, 38, 249, + 5, 226, 34, 105, 245, 145, 163, 87, 224, 136, 33, 18, 172, 121, 58, 41, 50, 196, 96, 198, 124, 40, 214, 31, 245, + 160, 208, 18, 247, 182, 249, 0, 216, 33, 98, 68, 191, 152, 156, 19, 119, 237, 245, 120, 9, 135, 161, 229, 46, 139, + 249, 78, 174, 167, 186, 76, 232, 232, 132, 163, 10, 4, 234, 108, 239, 157, 82, 109, 209, 117, 97, 176, 70, 190, 162, + 98, 217, 61, 47, 209, 73, 181, 220, 127, 221, 59, 117, 177, 139, 205, 136, 48, 30, 169, 198, 138, 229, 192, 40, 72, + 118, 5, 122, 172, 184, 116, 233, 160, 3, 14, 24, 215, 145, 214, 9, 240, 250, 81, 109, 59, 39, 82, 215, 88, 175, 253, + 19, 139, 71, 211, 50, 170, 103, 56, 57, 181, 41, 185, 170, 131, 142, 63, 103, 66, 181, 174, 224, 52, 251, 37, 93, + 163, 188, 52, 67, 234, 206, 210, 211, 161, 50, 108, 153, 237, 78, 152, 226, 118, 159, 219, 98, 103, 134, 30, 236, + 150, 61, 105, 62, 30, 73, 119, 233, 61, 30, 50, 152, 250, 121, 239, 50, 151, 182, 95, 22, 212, 23, 160, 245, 20, 19, + 224, 219, 199, 113, 252, 201, 216, 134, 98, 45, 111, 12, 250, 146, 131, 238, 60, 249, 154, 115, 241, 143, 68, 141, + 214, 65, 198, 241, 238, 200, 60, 188, 120, 25, 236, 51, 138, 12, 79, 43, 120, 94, 178, 241, 157, 179, 104, 251, 161, + 144, 98, 154, 167, 120, 68, 10, 45, 54, 77, 219, 202, 168, 35, 29, 230, 184, 243, 145, 216, 41, 31, 169, 170, 5, 88, + 37, 209, 81, 182, 76, 51, 33, 240, 216, 162, 167, 131, 129, 170, 207, 210, 21, 180, 115, 68, 158, 7, 37, 175, 53, 0, + 123, 58, 136, 189, 33, 120, 187, 211, 93, 194, 192, 246, 88, 196, 70, 217, 136, 81, 59, 228, 77, 210, 71, 18, 26, + 183, 81, 230, 139, 96, 204, 83, 160, 229, 227, 160, 11, 85, 24, 115, 254, 198, 91, 4, 130, 119, 198, 202, 215, 173, + 60, 119, 242, 95, 175, 253, 9, 142, 73, 230, 109, 17, 244, 175, 173, 216, 73, 66, 94, 137, 46, 165, 197, 18, 234, + 45, 41, 169, 76, 118, 88, 116, 41, 194, 150, 146, 91, 24, 60, 222, 128, 196, 32, 28, 84, 152, 57, 132, 129, 186, + 121, 236, 38, 246, 194, 167, 143, 157, 181, 88, 172, 126, 159, 250, 139, 167, 4, 232, 247, 155, 227, 88, 253, 10, + 43, 24, 12, 146, 132, 245, 32, 68, 245, 96, 109, 178, 144, 61, 195, 141, 4, 135, 18, 130, 6, 130, 138, 78, 164, 140, + 222, 89, 26, 8, 195, 24, 27, 42, 240, 26, 100, 255, 155, 13, 236, 216, 208, 230, 18, 55, 8, 146, 149, 197, 218, 75, + 36, 106, 155, 197, 105, 251, 85, 81, 81, 11, 101, 10, 131, 38, 25, 85, 47, 90, 168, 91, 142, 113, 176, 53, 66, 248, + 156, 143, 119, 74, 159, 143, 26, 210, 216, 226, 2, 173, 117, 109, 67, 3, 29, 243, 0, 27, 244, 156, 243, 235, 31, + 157, 231, 199, 58, 32, 140, 86, 50, 79, 222, 10, 188, 51, 222, 83, 40, 94, 141, 238, 240, 222, 87, 168, 123, 172, + 41, 94, 233, 214, 175, 107, 24, 131, 135, 143, 12, 135, 224, 208, 186, 189, 15, 232, 119, 225, 138, 43, 84, 55, 189, + 251, 195, 53, 140, 175, 208, 121, 148, 90, 56, 176, 7, 19, 177, 241, 53, 14, 22, 54, 228, 48, 79, 250, 27, 214, 43, + 144, 229, 150, 197, 102, 247, 248, 141, 33, 248, 186, 180, 209, 163, 171, 121, 242, 198, 251, 132, 109, 38, 85, 35, + 149, 233, 236, 245, 136, 134, 196, 152, 235, 84, 55, 216, 202, 56, 108, 186, 193, 197, 210, 195, 136, 14, 231, 67, + 66, 19, 163, 130, 26, 52, 50, 243, 93, 148, 90, 68, 109, 244, 128, 150, 55, 93, 80, 128, 192, 75, 154, 66, 158, 189, + 68, 86, 243, 18, 111, 119, 230, 135, 105, 38, 115, 142, 117, 10, 164, 97, 153, 177, 16, 151, 23, 212, 36, 187, 9, + 143, 64, 33, 52, 145, 233, 108, 142, 210, 193, 192, 193, 7, 198, 245, 245, 202, 232, 56, 115, 41, 93, 211, 17, 211, + 19, 83, 186, 10, 130, 105, 21, 192, 218, 199, 33, 1, 250, 22, 169, 14, 76, 87, 9, 203, 89, 42, 65, 182, 4, 210, 192, + 187, 180, 49, 208, 37, 39, 168, 27, 51, 245, 159, 58, 135, 179, 37, 40, 177, 237, 82, 48, 62, 58, 48, 12, 227, 150, + 73, 205, 135, 237, 95, 232, 109, 123, 164, 171, 178, 239, 156, 140, 58, 36, 143, 157, 191, 11, 41, 187, 20, 57, 128, + 252, 24, 23, 68, 2, 135, 48, 14, 82, 229, 24, 200, 134, 81, 5, 93, 125, 143, 153, 145, 118, 69, 65, 245, 66, 201, + 59, 189, 51, 213, 2, 46, 192, 245, 59, 54, 212, 73, 130, 101, 22, 60, 178, 137, 61, 177, 201, 117, 152, 73, 168, 60, + 207, 8, 2, 129, 234, 47, 120, 44, 245, 11, 144, 50, 179, 47, 145, 160, 131, 94, 26, 9, 211, 20, 170, 252, 214, 217, + 92, 35, 130, 41, 27, 82, 158, 142, 215, 209, 65, 196, 49, 238, 113, 46, 71, 227, 141, 202, 78, 78, 189, 243, 97, 83, + 31, 66, 53, 117, 220, 121, 235, 158, 38, 127, 197, 235, 184, 41, 41, 161, 167, 38, 138, 16, 115, 192, 208, 214, 197, + 192, 105, 132, 165, 218, 30, 220, 201, 152, 132, 171, 118, 136, 167, 191, 156, 55, 76, 148, 1, 64, 123, 3, 124, 136, + 38, 126, 99, 130, 112, 250, 129, 43, 125, 156, 112, 137, 178, 222, 176, 142, 160, 168, 61, 255, 54, 141, 223, 163, + 223, 117, 253, 247, 109, 206, 68, 22, 165, 136, 107, 238, 189, 132, 48, 57, 38, 69, 70, 34, 118, 226, 216, 100, 209, + 61, 176, 178, 228, 68, 170, 199, 112, 112, 190, 146, 212, 46, 76, 130, 164, 11, 216, 12, 165, 61, 62, 91, 168, 101, + 128, 61, 46, 227, 42, 245, 91, 52, 178, 42, 198, 39, 38, 223, 79, 177, 137, 233, 68, 93, 135, 87, 43, 209, 90, 66, + 71, 57, 209, 12, 163, 140, 218, 169, 63, 117, 57, 163, 49, 228, 159, 200, 44, 51, 91, 40, 83, 179, 192, 233, 231, + 188, 231, 62, 63, 52, 149, 154, 115, 6, 29, 119, 70, 40, 83, 0, 139, 56, 213, 52, 216, 241, 47, 134, 29, 122, 177, + 186, 235, 117, 170, 131, 53, 152, 92, 184, 244, 134, 11, 50, 162, 108, 108, 128, 65, 242, 145, 148, 190, 153, 167, + 14, 219, 25, 126, 243, 75, 78, 219, 237, 229, 124, 91, 157, 35, 192, 191, 39, 252, 162, 141, 16, 240, 52, 247, 74, + 222, 240, 195, 224, 37, 246, 218, 149, 131, 101, 72, 208, 130, 139, 35, 75, 144, 242, 66, 245, 226, 138, 57, 232, + 193, 70, 254, 33, 6, 178, 255, 13, 95, 159, 121, 12, 246, 39, 152, 248, 204, 84, 254, 172, 114, 1, 129, 146, 0, 56, + 70, 16, 232, 128, 211, 245, 67, 169, 253, 114, 15, 210, 172, 171, 250, 144, 92, 193, 65, 160, 20, 18, 227, 97, 166, + 240, 69, 92, 14, 119, 197, 55, 97, 130, 10, 110, 113, 228, 36, 56, 208, 76, 132, 41, 192, 136, 183, 197, 186, 168, + 169, 50, 32, 105, 94, 228, 168, 225, 185, 202, 223, 41, 197, 112, 146, 213, 136, 172, 206, 165, 55, 26, 227, 67, 46, + 236, 240, 40, 48, 220, 236, 187, 231, 125, 188, 137, 89, 79, 196, 223, 103, 45, 39, 62, 82, 95, 36, 21, 238, 167, + 124, 117, 81, 129, 166, 94, 133, 3, 87, 222, 155, 181, 102, 167, 8, 123, 205, 53, 98, 226, 109, 139, 244, 111, 142, + 197, 216, 102, 146, 215, 197, 69, 97, 208, 23, 77, 136, 226, 93, 93, 239, 245, 55, 95, 125, 245, 240, 239, 235, 249, + 45, 76, 190, 89, 205, 139, 242, 171, 135, 124, 149, 209, 55, 103, 171, 34, 43, 198, 27, 143, 232, 173, 56, 172, 93, + 131, 97, 213, 184, 101, 240, 155, 106, 8, 57, 212, 190, 163, 12, 238, 172, 185, 126, 192, 191, 112, 223, 107, 82, + 23, 177, 250, 37, 174, 0, 75, 9, 78, 224, 68, 108, 60, 57, 110, 118, 88, 27, 135, 148, 188, 75, 241, 11, 182, 227, + 62, 228, 233, 71, 254, 27, 255, 197, 191, 229, 2, 195, 12, 51, 249, 116, 112, 240, 119, 50, 252, 204, 52, 77, 255, + 95, 53, 155, 13, 13, 78, 177, 91, 249, 161, 80, 89, 249, 208, 194, 196, 187, 151, 77, 138, 57, 38, 212, 161, 159, + 125, 112, 153, 141, 20, 157, 68, 15, 157, 180, 142, 8, 133, 217, 64, 137, 17, 20, 246, 163, 18, 122, 249, 91, 28, + 155, 248, 26, 164, 6, 135, 182, 50, 133, 113, 64, 44, 156, 94, 169, 230, 240, 151, 155, 171, 119, 103, 220, 48, 215, + 204, 74, 46, 57, 102, 74, 155, 56, 170, 229, 217, 159, 150, 179, 100, 249, 46, 125, 135, 255, 187, 84, 24, 53, 80, + 31, 150, 32, 226, 150, 103, 238, 47, 226, 80, 188, 227, 32, 182, 107, 172, 47, 161, 7, 134, 222, 74, 119, 232, 9, + 170, 22, 211, 50, 172, 132, 110, 77, 134, 151, 120, 12, 148, 95, 155, 76, 228, 65, 244, 67, 124, 54, 4, 236, 24, 34, + 84, 255, 84, 12, 29, 7, 75, 245, 114, 46, 90, 183, 211, 102, 53, 52, 44, 121, 156, 220, 97, 213, 163, 228, 198, 150, + 220, 67, 198, 187, 228, 49, 45, 105, 236, 102, 207, 80, 227, 1, 1, 241, 238, 234, 195, 27, 170, 199, 113, 40, 27, + 83, 196, 0, 40, 185, 190, 227, 208, 222, 10, 56, 255, 14, 132, 57, 150, 168, 203, 109, 176, 53, 75, 122, 216, 93, + 233, 117, 137, 220, 115, 201, 35, 185, 198, 234, 192, 249, 195, 146, 118, 123, 121, 45, 245, 245, 236, 175, 171, 28, + 174, 185, 199, 245, 73, 15, 40, 83, 78, 169, 112, 19, 57, 158, 93, 126, 113, 140, 21, 16, 185, 49, 242, 250, 173, + 135, 194, 254, 40, 14, 129, 138, 2, 78, 134, 58, 88, 165, 146, 189, 207, 242, 9, 127, 75, 245, 142, 89, 69, 235, 78, + 2, 138, 172, 14, 58, 77, 12, 170, 38, 190, 144, 156, 160, 238, 77, 97, 239, 101, 117, 202, 190, 147, 209, 155, 57, + 37, 246, 224, 62, 34, 112, 252, 132, 46, 91, 6, 135, 106, 61, 144, 219, 190, 197, 40, 168, 66, 54, 50, 7, 243, 39, + 226, 99, 20, 140, 182, 80, 97, 109, 68, 4, 12, 31, 186, 59, 116, 56, 102, 243, 20, 94, 149, 68, 152, 21, 168, 85, + 185, 166, 135, 100, 161, 88, 210, 103, 192, 139, 86, 77, 141, 41, 15, 90, 74, 78, 218, 58, 144, 113, 64, 207, 16, + 43, 109, 89, 11, 221, 56, 35, 181, 223, 219, 186, 83, 59, 158, 111, 210, 238, 61, 12, 235, 13, 29, 125, 32, 220, + 173, 69, 49, 159, 128, 136, 208, 209, 87, 194, 210, 110, 156, 204, 98, 132, 36, 85, 170, 179, 46, 85, 157, 14, 129, + 170, 70, 107, 172, 237, 153, 193, 4, 8, 134, 126, 247, 65, 107, 211, 213, 10, 127, 164, 224, 100, 179, 239, 210, + 247, 164, 195, 54, 97, 225, 90, 172, 177, 229, 33, 110, 116, 93, 238, 174, 237, 224, 39, 180, 121, 222, 250, 212, + 247, 217, 143, 104, 115, 124, 242, 115, 0, 54, 28, 125, 80, 189, 115, 230, 80, 124, 56, 200, 14, 24, 238, 156, 191, + 63, 225, 94, 102, 184, 7, 155, 226, 86, 204, 38, 11, 117, 89, 187, 90, 220, 191, 96, 68, 214, 219, 31, 36, 228, 137, + 174, 15, 149, 211, 126, 253, 109, 31, 188, 227, 173, 202, 236, 240, 93, 142, 125, 135, 195, 88, 237, 208, 228, 6, + 164, 57, 58, 191, 3, 67, 228, 77, 32, 213, 175, 161, 73, 169, 106, 72, 193, 173, 94, 15, 82, 120, 79, 38, 145, 180, + 137, 211, 197, 231, 26, 199, 176, 204, 138, 251, 13, 245, 134, 109, 199, 22, 139, 3, 215, 116, 43, 97, 177, 181, + 104, 19, 78, 141, 35, 149, 54, 93, 30, 199, 123, 246, 143, 78, 213, 23, 22, 243, 39, 81, 219, 63, 141, 214, 245, + 207, 167, 253, 252, 150, 194, 17, 45, 48, 27, 80, 50, 131, 24, 88, 73, 177, 193, 112, 74, 7, 25, 232, 72, 145, 240, + 232, 209, 38, 151, 219, 164, 140, 178, 109, 151, 72, 178, 34, 35, 85, 98, 83, 212, 161, 128, 142, 150, 137, 220, 16, + 248, 80, 185, 215, 54, 74, 250, 241, 56, 71, 95, 149, 110, 57, 56, 100, 220, 131, 157, 201, 59, 61, 50, 5, 98, 17, + 101, 29, 136, 101, 105, 73, 122, 28, 193, 198, 6, 251, 6, 58, 243, 70, 218, 138, 50, 109, 205, 226, 41, 12, 67, 165, + 225, 240, 75, 150, 232, 71, 3, 143, 142, 242, 155, 228, 21, 65, 80, 96, 192, 47, 153, 221, 136, 129, 123, 251, 118, + 64, 167, 107, 190, 221, 80, 170, 135, 18, 192, 138, 178, 74, 124, 101, 242, 179, 50, 44, 211, 99, 152, 119, 133, + 117, 252, 31, 65, 139, 61, 219, 0, 37, 168, 108, 123, 232, 232, 63, 98, 228, 155, 126, 147, 136, 73, 144, 119, 116, + 7, 144, 198, 69, 69, 27, 16, 84, 81, 53, 233, 15, 54, 252, 4, 54, 236, 14, 95, 235, 84, 62, 229, 228, 125, 204, 15, + 81, 77, 91, 216, 13, 188, 210, 221, 10, 88, 143, 51, 242, 215, 120, 14, 15, 195, 149, 30, 65, 20, 247, 145, 27, 98, + 107, 24, 221, 98, 77, 157, 214, 250, 140, 233, 66, 90, 43, 171, 24, 181, 217, 51, 147, 217, 87, 39, 4, 199, 31, 229, + 89, 40, 135, 248, 132, 39, 222, 91, 131, 156, 44, 188, 18, 31, 172, 205, 184, 139, 103, 233, 243, 176, 145, 122, 99, + 235, 250, 89, 21, 53, 5, 187, 69, 36, 172, 103, 173, 30, 234, 217, 68, 24, 177, 213, 41, 75, 78, 200, 3, 227, 22, + 149, 143, 233, 233, 180, 136, 154, 219, 64, 84, 239, 43, 47, 102, 148, 236, 5, 192, 2, 239, 49, 46, 154, 42, 139, + 147, 50, 111, 181, 117, 51, 56, 114, 39, 46, 45, 71, 94, 202, 180, 42, 82, 188, 85, 41, 159, 45, 212, 137, 229, 250, + 163, 6, 126, 100, 101, 245, 212, 209, 235, 199, 141, 94, 127, 150, 209, 243, 149, 165, 169, 142, 210, 6, 177, 226, + 82, 192, 72, 3, 199, 169, 75, 25, 168, 157, 20, 155, 16, 241, 32, 140, 11, 198, 166, 121, 181, 227, 219, 153, 131, + 61, 211, 111, 40, 123, 151, 18, 131, 143, 140, 218, 31, 41, 2, 137, 32, 70, 118, 146, 53, 21, 39, 122, 80, 3, 190, + 226, 234, 235, 149, 170, 63, 174, 133, 26, 51, 48, 224, 226, 31, 238, 170, 242, 65, 13, 174, 166, 92, 11, 201, 239, + 74, 111, 155, 97, 145, 214, 103, 29, 128, 83, 145, 100, 175, 142, 116, 235, 148, 32, 10, 60, 243, 144, 118, 142, 42, + 237, 33, 132, 94, 163, 211, 68, 56, 163, 112, 68, 115, 59, 14, 141, 129, 127, 90, 242, 155, 75, 123, 101, 28, 69, + 196, 78, 14, 152, 36, 238, 241, 242, 192, 228, 114, 7, 5, 3, 7, 8, 120, 227, 84, 47, 49, 179, 9, 174, 252, 232, 97, + 191, 47, 24, 224, 172, 80, 190, 187, 98, 221, 78, 124, 122, 140, 68, 57, 47, 21, 122, 196, 105, 226, 174, 247, 163, + 42, 241, 111, 34, 72, 124, 153, 139, 165, 141, 243, 41, 162, 151, 62, 128, 11, 88, 190, 205, 58, 242, 247, 217, 239, + 17, 220, 213, 231, 94, 188, 118, 47, 143, 224, 186, 79, 151, 120, 237, 33, 204, 22, 106, 140, 232, 75, 190, 52, 201, + 55, 113, 22, 250, 249, 102, 225, 153, 94, 253, 18, 160, 3, 238, 252, 86, 173, 70, 27, 123, 53, 79, 222, 112, 52, 67, + 23, 88, 207, 92, 98, 46, 179, 229, 204, 243, 194, 24, 103, 198, 66, 181, 93, 39, 93, 71, 13, 149, 73, 144, 64, 9, + 224, 146, 100, 121, 183, 11, 115, 138, 171, 178, 243, 107, 50, 26, 157, 125, 60, 11, 186, 31, 229, 171, 65, 189, + 214, 31, 229, 47, 192, 241, 116, 215, 65, 251, 59, 186, 138, 30, 239, 251, 137, 122, 63, 218, 14, 53, 208, 102, 178, + 109, 254, 40, 67, 237, 155, 227, 229, 15, 24, 200, 17, 3, 227, 188, 240, 93, 91, 247, 32, 144, 164, 22, 108, 248, + 98, 192, 82, 231, 61, 147, 174, 76, 49, 129, 228, 161, 172, 182, 217, 243, 166, 203, 247, 167, 129, 200, 178, 29, + 124, 70, 38, 86, 12, 85, 147, 253, 237, 125, 101, 106, 154, 248, 161, 82, 142, 216, 97, 108, 104, 127, 111, 225, 26, + 14, 215, 132, 96, 155, 96, 160, 208, 118, 167, 95, 49, 30, 14, 215, 27, 14, 9, 158, 239, 155, 93, 138, 209, 90, 105, + 102, 139, 22, 27, 120, 18, 84, 234, 24, 176, 181, 191, 179, 253, 214, 57, 44, 43, 84, 90, 16, 180, 41, 93, 231, 187, + 51, 9, 220, 234, 173, 153, 31, 91, 146, 252, 168, 36, 198, 18, 55, 103, 198, 154, 112, 23, 223, 23, 107, 80, 11, 64, + 38, 108, 56, 50, 212, 92, 207, 18, 93, 154, 237, 170, 242, 45, 214, 50, 62, 216, 125, 147, 14, 14, 54, 88, 201, 118, + 205, 152, 71, 18, 51, 184, 80, 178, 65, 132, 30, 130, 61, 157, 73, 251, 176, 46, 247, 197, 237, 96, 121, 136, 232, + 194, 164, 255, 63, 86, 220, 240, 121, 193, 145, 162, 239, 23, 146, 184, 18, 0, 195, 115, 5, 87, 79, 208, 220, 141, + 150, 167, 83, 182, 107, 246, 175, 15, 166, 138, 10, 85, 130, 151, 203, 77, 101, 19, 20, 166, 36, 141, 218, 94, 3, + 196, 232, 42, 212, 23, 71, 178, 105, 145, 60, 6, 62, 199, 199, 6, 205, 207, 29, 112, 33, 184, 91, 206, 221, 36, 189, + 203, 43, 58, 203, 124, 153, 66, 89, 91, 37, 33, 226, 81, 219, 32, 50, 35, 245, 211, 127, 240, 18, 184, 45, 11, 51, + 61, 89, 228, 239, 147, 63, 141, 74, 237, 94, 83, 33, 157, 72, 79, 82, 17, 232, 185, 250, 138, 230, 131, 202, 117, + 228, 242, 34, 222, 199, 128, 48, 155, 84, 199, 41, 214, 254, 76, 192, 160, 105, 27, 109, 200, 177, 169, 126, 148, + 114, 100, 7, 67, 227, 12, 34, 241, 5, 11, 63, 141, 40, 53, 226, 213, 95, 237, 213, 13, 137, 205, 59, 88, 27, 196, + 47, 211, 27, 163, 96, 163, 239, 141, 235, 39, 166, 73, 231, 99, 84, 133, 46, 6, 181, 227, 190, 168, 33, 187, 125, + 184, 75, 247, 251, 92, 13, 234, 70, 195, 226, 210, 54, 245, 22, 152, 82, 144, 235, 94, 20, 164, 10, 35, 147, 229, + 227, 233, 124, 112, 187, 28, 13, 130, 200, 121, 173, 235, 101, 189, 45, 48, 46, 219, 221, 165, 22, 74, 46, 83, 24, + 135, 111, 58, 3, 217, 211, 32, 10, 170, 14, 176, 64, 239, 26, 166, 242, 26, 57, 242, 66, 73, 184, 18, 200, 172, 188, + 94, 79, 132, 96, 235, 51, 232, 15, 209, 248, 122, 135, 205, 176, 43, 182, 219, 66, 130, 233, 37, 221, 1, 168, 120, + 125, 23, 142, 146, 183, 205, 82, 46, 197, 200, 102, 7, 154, 58, 143, 2, 13, 17, 245, 210, 30, 28, 232, 110, 101, 98, + 207, 73, 84, 72, 166, 26, 149, 171, 198, 36, 236, 223, 19, 54, 224, 177, 89, 236, 159, 63, 123, 125, 64, 70, 244, + 27, 109, 193, 126, 60, 131, 108, 232, 247, 176, 110, 73, 139, 193, 62, 60, 68, 130, 56, 119, 164, 210, 234, 164, + 191, 48, 6, 157, 35, 135, 196, 168, 20, 112, 112, 53, 194, 249, 78, 32, 144, 17, 17, 2, 192, 16, 243, 173, 53, 27, + 123, 156, 121, 158, 124, 75, 227, 192, 130, 217, 244, 3, 71, 245, 25, 43, 114, 218, 170, 17, 216, 10, 199, 65, 252, + 62, 74, 151, 101, 236, 58, 105, 195, 164, 117, 141, 150, 37, 3, 133, 232, 71, 208, 246, 180, 186, 243, 251, 180, 10, + 25, 23, 168, 92, 56, 168, 111, 170, 108, 90, 185, 35, 190, 0, 35, 38, 138, 153, 77, 121, 70, 243, 53, 171, 56, 123, + 154, 104, 227, 17, 197, 74, 152, 63, 219, 152, 38, 132, 198, 5, 202, 136, 75, 52, 60, 222, 89, 76, 226, 220, 229, + 192, 37, 212, 151, 143, 38, 23, 200, 175, 130, 57, 238, 190, 209, 57, 74, 225, 152, 180, 101, 102, 199, 179, 164, + 164, 195, 188, 189, 66, 160, 127, 227, 73, 227, 172, 55, 206, 89, 49, 95, 48, 94, 157, 185, 98, 193, 53, 227, 158, + 233, 184, 18, 32, 237, 133, 226, 172, 98, 130, 253, 65, 208, 70, 185, 67, 217, 239, 9, 26, 111, 158, 220, 228, 121, + 178, 236, 215, 54, 147, 176, 16, 155, 104, 72, 216, 231, 25, 103, 210, 53, 246, 124, 63, 222, 238, 58, 156, 32, 153, + 249, 219, 20, 181, 203, 116, 54, 116, 160, 114, 202, 67, 90, 32, 36, 161, 184, 214, 59, 148, 154, 101, 98, 157, 33, + 68, 113, 241, 192, 251, 21, 82, 204, 79, 173, 98, 23, 225, 244, 58, 121, 245, 106, 124, 5, 16, 249, 194, 199, 90, + 137, 6, 186, 114, 0, 225, 235, 67, 36, 244, 87, 64, 106, 60, 191, 16, 233, 69, 92, 49, 198, 84, 86, 225, 168, 109, + 63, 227, 203, 51, 255, 17, 250, 229, 193, 57, 206, 167, 248, 94, 25, 0, 56, 22, 13, 98, 6, 39, 129, 32, 94, 167, 14, + 242, 216, 34, 44, 217, 42, 24, 143, 195, 48, 136, 100, 251, 9, 1, 152, 230, 147, 159, 10, 66, 145, 224, 0, 206, 42, + 255, 133, 151, 73, 34, 44, 55, 142, 16, 40, 164, 253, 151, 110, 5, 140, 184, 10, 126, 228, 134, 238, 116, 126, 41, + 168, 131, 157, 241, 161, 215, 114, 217, 208, 163, 56, 114, 207, 22, 208, 78, 132, 70, 9, 34, 82, 231, 74, 5, 37, + 185, 238, 224, 41, 49, 181, 103, 249, 122, 155, 26, 135, 38, 223, 185, 248, 129, 24, 106, 147, 43, 47, 73, 206, 80, + 177, 165, 55, 148, 170, 176, 207, 69, 203, 45, 91, 99, 133, 5, 223, 210, 27, 62, 60, 102, 52, 23, 110, 4, 1, 251, + 134, 55, 186, 174, 84, 47, 173, 248, 142, 71, 8, 62, 166, 54, 15, 89, 234, 109, 55, 112, 243, 53, 198, 122, 211, 26, + 105, 63, 189, 187, 144, 16, 57, 86, 186, 106, 91, 214, 39, 71, 184, 27, 47, 129, 14, 107, 100, 246, 241, 60, 121, + 133, 52, 221, 250, 45, 217, 49, 50, 14, 101, 64, 186, 26, 99, 185, 47, 149, 201, 132, 155, 164, 218, 219, 45, 223, + 75, 61, 49, 0, 176, 229, 9, 105, 193, 50, 22, 219, 144, 254, 114, 137, 69, 209, 240, 9, 214, 106, 244, 110, 160, 93, + 163, 148, 159, 240, 90, 229, 173, 235, 63, 73, 171, 172, 164, 223, 72, 84, 249, 182, 170, 228, 234, 30, 84, 211, 2, + 147, 214, 48, 206, 241, 26, 159, 95, 41, 123, 124, 150, 132, 2, 59, 145, 223, 61, 59, 43, 238, 26, 175, 190, 56, 94, + 252, 180, 42, 188, 176, 228, 213, 119, 112, 51, 110, 170, 248, 106, 121, 176, 188, 118, 68, 132, 93, 207, 201, 202, + 78, 93, 95, 145, 49, 185, 50, 73, 230, 63, 94, 78, 68, 222, 25, 82, 7, 162, 131, 89, 210, 103, 172, 1, 241, 170, 32, + 77, 138, 123, 151, 0, 84, 168, 144, 82, 137, 215, 121, 122, 149, 83, 76, 72, 134, 32, 95, 52, 74, 218, 124, 34, 186, + 252, 132, 218, 215, 125, 99, 62, 83, 213, 45, 97, 255, 91, 74, 19, 172, 107, 99, 143, 143, 113, 55, 239, 8, 57, 150, + 86, 122, 76, 47, 226, 55, 124, 148, 173, 232, 179, 22, 236, 194, 28, 71, 175, 3, 246, 2, 163, 119, 200, 134, 40, + 226, 6, 218, 121, 17, 160, 132, 247, 186, 89, 176, 142, 100, 123, 68, 124, 58, 241, 179, 43, 117, 3, 188, 232, 205, + 102, 19, 74, 110, 181, 156, 163, 100, 230, 199, 78, 61, 107, 38, 7, 77, 1, 177, 16, 115, 250, 216, 203, 211, 173, + 26, 180, 249, 163, 137, 171, 199, 43, 159, 225, 236, 146, 166, 29, 39, 64, 132, 43, 222, 82, 28, 12, 185, 92, 31, + 48, 139, 199, 185, 231, 130, 254, 188, 32, 221, 45, 128, 52, 118, 120, 163, 204, 4, 137, 68, 104, 142, 246, 90, 46, + 244, 85, 78, 187, 45, 165, 37, 61, 135, 147, 241, 77, 221, 54, 160, 30, 192, 151, 134, 205, 194, 46, 251, 14, 47, + 242, 76, 21, 61, 210, 17, 80, 51, 140, 227, 166, 11, 96, 186, 109, 251, 233, 81, 173, 177, 99, 207, 138, 13, 229, + 28, 147, 254, 172, 40, 6, 72, 225, 13, 246, 36, 159, 223, 206, 187, 9, 69, 206, 87, 213, 25, 52, 168, 5, 101, 163, + 178, 20, 13, 158, 167, 148, 229, 195, 19, 216, 53, 117, 67, 1, 66, 84, 234, 85, 211, 138, 162, 118, 182, 236, 159, + 198, 229, 35, 75, 7, 247, 69, 197, 17, 52, 130, 128, 60, 147, 195, 111, 244, 231, 101, 59, 47, 113, 57, 179, 254, + 126, 218, 45, 3, 172, 131, 208, 132, 163, 64, 9, 146, 163, 203, 209, 146, 192, 156, 213, 73, 76, 86, 24, 179, 11, + 155, 96, 52, 159, 130, 171, 144, 222, 195, 246, 102, 143, 46, 180, 49, 9, 219, 10, 201, 187, 95, 208, 239, 217, 174, + 13, 143, 191, 25, 144, 83, 47, 103, 213, 47, 224, 96, 162, 167, 164, 173, 96, 133, 1, 225, 32, 97, 141, 221, 189, + 19, 24, 175, 121, 196, 135, 153, 90, 29, 182, 234, 154, 186, 247, 49, 60, 216, 166, 218, 138, 234, 228, 227, 241, + 246, 32, 80, 3, 104, 235, 48, 236, 66, 215, 145, 25, 59, 249, 249, 224, 145, 185, 140, 56, 179, 96, 192, 126, 220, + 0, 182, 214, 10, 146, 121, 170, 228, 142, 199, 48, 16, 119, 98, 71, 184, 25, 74, 213, 40, 91, 11, 195, 238, 11, 133, + 65, 238, 138, 90, 204, 153, 32, 188, 157, 182, 108, 4, 152, 141, 120, 48, 154, 147, 141, 87, 216, 72, 153, 68, 41, + 47, 11, 205, 222, 230, 19, 101, 121, 248, 112, 68, 77, 245, 174, 246, 168, 219, 192, 9, 222, 196, 223, 240, 188, 24, + 192, 215, 8, 30, 160, 150, 75, 8, 69, 28, 111, 250, 27, 168, 19, 68, 82, 98, 73, 220, 110, 21, 75, 210, 157, 51, + 222, 214, 155, 72, 18, 215, 5, 191, 229, 87, 226, 246, 174, 71, 92, 44, 133, 203, 252, 98, 169, 94, 54, 233, 5, 144, + 105, 99, 53, 130, 199, 142, 188, 5, 63, 212, 19, 68, 231, 172, 163, 47, 165, 52, 202, 66, 253, 100, 156, 181, 174, + 125, 202, 183, 231, 209, 100, 18, 156, 76, 24, 108, 126, 221, 21, 31, 239, 138, 208, 178, 82, 58, 16, 12, 251, 212, + 6, 19, 149, 8, 24, 127, 166, 76, 238, 121, 172, 188, 106, 139, 163, 161, 97, 163, 165, 20, 63, 3, 139, 51, 3, 24, + 97, 126, 248, 221, 234, 210, 246, 234, 82, 74, 181, 103, 140, 95, 0, 2, 10, 69, 147, 80, 125, 71, 143, 55, 246, 234, + 67, 59, 198, 104, 141, 86, 207, 17, 132, 254, 15, 100, 43, 88, 63, 155, 0, 240, 22, 243, 183, 16, 0, 35, 148, 210, + 206, 221, 96, 105, 162, 112, 74, 84, 145, 145, 67, 161, 31, 162, 119, 55, 176, 33, 96, 145, 203, 1, 82, 61, 246, 90, + 86, 54, 126, 44, 39, 21, 60, 101, 170, 183, 145, 96, 185, 186, 47, 170, 82, 153, 26, 88, 143, 186, 122, 254, 211, + 89, 213, 255, 176, 228, 252, 30, 150, 156, 223, 229, 238, 111, 78, 48, 2, 150, 35, 75, 19, 93, 230, 117, 133, 137, + 169, 30, 34, 211, 211, 72, 2, 125, 135, 208, 127, 63, 255, 129, 132, 7, 149, 89, 179, 123, 144, 110, 144, 87, 33, + 186, 49, 106, 29, 48, 232, 19, 21, 140, 96, 57, 157, 232, 247, 117, 17, 50, 125, 213, 141, 167, 27, 5, 116, 94, 225, + 99, 194, 212, 212, 118, 9, 253, 64, 199, 167, 45, 14, 136, 157, 237, 219, 80, 189, 178, 155, 188, 174, 89, 74, 154, + 178, 199, 228, 170, 109, 4, 158, 29, 183, 107, 139, 89, 249, 85, 126, 230, 251, 100, 82, 194, 0, 223, 159, 109, 65, + 131, 218, 26, 90, 70, 52, 47, 140, 54, 39, 214, 195, 159, 221, 167, 85, 193, 205, 160, 254, 96, 64, 78, 109, 99, + 185, 73, 171, 59, 24, 168, 3, 204, 173, 97, 12, 85, 51, 228, 37, 229, 7, 232, 28, 136, 93, 63, 125, 33, 200, 31, + 253, 163, 210, 233, 38, 71, 157, 140, 42, 110, 96, 37, 236, 243, 155, 235, 96, 230, 12, 49, 107, 96, 157, 55, 176, + 21, 88, 71, 35, 185, 70, 237, 232, 144, 156, 192, 7, 167, 150, 173, 137, 111, 105, 87, 220, 222, 9, 42, 123, 242, + 178, 161, 78, 206, 240, 28, 188, 196, 2, 190, 84, 228, 217, 224, 214, 225, 175, 39, 167, 179, 196, 56, 199, 240, + 223, 32, 233, 63, 48, 13, 159, 156, 210, 2, 195, 15, 166, 34, 193, 201, 169, 212, 0, 150, 202, 2, 36, 247, 16, 188, + 8, 255, 38, 250, 241, 220, 47, 174, 132, 245, 234, 64, 24, 187, 58, 129, 209, 218, 74, 33, 45, 161, 236, 144, 191, + 19, 111, 192, 243, 68, 194, 138, 181, 33, 135, 47, 214, 128, 103, 52, 94, 88, 219, 47, 204, 118, 231, 173, 178, 13, + 47, 53, 186, 250, 151, 96, 202, 91, 127, 65, 22, 188, 85, 30, 51, 213, 37, 99, 225, 67, 63, 187, 45, 173, 7, 101, + 251, 101, 89, 212, 128, 1, 93, 114, 160, 127, 48, 15, 72, 208, 166, 80, 191, 230, 183, 36, 132, 36, 180, 108, 129, + 53, 9, 36, 204, 124, 232, 181, 53, 0, 201, 13, 163, 195, 210, 161, 63, 234, 80, 165, 0, 127, 116, 158, 67, 152, 202, + 169, 82, 156, 15, 131, 117, 73, 109, 62, 174, 72, 75, 191, 187, 232, 97, 63, 229, 218, 12, 10, 85, 19, 189, 46, 247, + 166, 124, 218, 30, 43, 71, 175, 177, 32, 82, 98, 98, 141, 198, 47, 0, 30, 244, 27, 160, 206, 16, 158, 73, 102, 42, + 191, 254, 141, 50, 105, 87, 135, 58, 31, 46, 175, 71, 83, 8, 182, 134, 165, 54, 184, 108, 245, 168, 38, 59, 75, 124, + 29, 135, 253, 49, 107, 236, 231, 133, 111, 48, 25, 115, 124, 88, 69, 18, 141, 170, 240, 253, 244, 97, 11, 198, 163, + 66, 30, 68, 7, 246, 134, 188, 225, 84, 219, 163, 177, 10, 112, 9, 31, 88, 11, 91, 13, 187, 234, 47, 10, 199, 40, + 144, 94, 136, 218, 138, 196, 42, 228, 219, 156, 192, 233, 25, 111, 103, 70, 164, 133, 87, 200, 154, 190, 133, 237, + 132, 109, 89, 168, 245, 93, 90, 76, 180, 48, 152, 236, 79, 55, 218, 43, 232, 246, 248, 136, 187, 88, 63, 56, 244, + 19, 14, 160, 161, 188, 210, 175, 56, 189, 244, 148, 102, 241, 95, 189, 20, 241, 66, 138, 143, 147, 7, 210, 130, 113, + 115, 138, 230, 211, 69, 253, 243, 223, 24, 159, 65, 243, 3, 109, 233, 18, 107, 101, 231, 217, 212, 181, 70, 61, 171, + 224, 79, 219, 176, 10, 227, 97, 24, 159, 36, 120, 218, 176, 47, 211, 192, 186, 188, 148, 246, 86, 173, 185, 54, 200, + 64, 240, 144, 24, 146, 41, 187, 221, 39, 39, 116, 52, 140, 82, 211, 58, 26, 167, 143, 8, 213, 25, 64, 60, 104, 5, + 30, 183, 146, 245, 67, 67, 238, 36, 233, 183, 23, 108, 194, 54, 197, 23, 53, 6, 23, 240, 235, 239, 36, 243, 201, + 124, 252, 67, 254, 9, 11, 38, 220, 160, 168, 123, 23, 12, 2, 55, 204, 31, 189, 129, 112, 47, 35, 112, 71, 93, 179, + 233, 158, 96, 127, 221, 61, 141, 56, 187, 152, 165, 73, 120, 14, 89, 165, 159, 205, 185, 180, 45, 203, 143, 32, 113, + 252, 126, 221, 152, 158, 197, 7, 164, 100, 97, 142, 84, 113, 53, 186, 75, 107, 153, 65, 237, 175, 14, 76, 92, 122, + 40, 234, 150, 229, 192, 180, 184, 91, 254, 198, 147, 118, 166, 30, 192, 104, 81, 247, 140, 214, 252, 14, 195, 61, + 134, 27, 31, 92, 232, 210, 174, 83, 71, 179, 33, 100, 85, 18, 81, 158, 175, 32, 142, 103, 96, 121, 34, 13, 45, 2, + 195, 254, 222, 84, 185, 106, 35, 22, 120, 145, 164, 67, 129, 210, 143, 84, 74, 168, 203, 129, 189, 137, 14, 63, 188, + 73, 118, 14, 236, 171, 247, 230, 33, 136, 127, 46, 108, 210, 168, 107, 182, 54, 196, 208, 188, 142, 144, 132, 95, + 102, 34, 150, 237, 212, 154, 73, 163, 46, 55, 63, 165, 5, 154, 139, 190, 45, 43, 19, 243, 30, 176, 183, 97, 80, 175, + 51, 226, 195, 191, 234, 148, 170, 233, 148, 232, 52, 78, 28, 70, 247, 3, 55, 198, 117, 85, 76, 70, 0, 206, 178, 174, + 83, 76, 68, 107, 119, 30, 113, 156, 131, 38, 175, 109, 62, 166, 191, 237, 9, 89, 234, 123, 217, 234, 225, 21, 27, + 237, 33, 150, 89, 28, 201, 231, 250, 103, 115, 187, 127, 129, 190, 197, 127, 32, 215, 221, 23, 239, 38, 253, 231, + 139, 67, 254, 71, 242, 14, 77, 186, 139, 0, 187, 253, 34, 173, 95, 112, 214, 40, 219, 15, 3, 222, 40, 129, 236, 2, + 94, 190, 139, 198, 156, 112, 37, 47, 254, 183, 203, 185, 228, 12, 24, 205, 169, 106, 130, 67, 91, 229, 89, 81, 225, + 188, 92, 193, 187, 168, 20, 217, 165, 159, 194, 221, 190, 77, 63, 21, 187, 102, 7, 179, 220, 163, 250, 179, 233, + 247, 137, 57, 112, 226, 137, 41, 147, 229, 159, 150, 204, 183, 217, 205, 6, 34, 12, 174, 91, 50, 94, 139, 93, 138, + 104, 232, 216, 202, 66, 249, 67, 63, 17, 216, 143, 211, 163, 229, 127, 243, 154, 113, 250, 121, 219, 190, 53, 32, + 240, 188, 66, 217, 164, 122, 110, 185, 253, 38, 88, 98, 220, 245, 8, 235, 96, 55, 8, 205, 95, 31, 202, 243, 116, 47, + 78, 220, 241, 221, 105, 54, 156, 13, 205, 174, 54, 78, 63, 43, 173, 6, 252, 130, 38, 191, 146, 80, 170, 64, 34, 246, + 106, 22, 179, 19, 147, 83, 181, 80, 149, 217, 178, 233, 75, 26, 96, 120, 48, 9, 54, 42, 27, 248, 249, 108, 87, 106, + 155, 254, 236, 206, 118, 46, 57, 241, 253, 153, 250, 252, 34, 203, 94, 23, 100, 199, 12, 25, 38, 2, 193, 240, 200, + 75, 119, 123, 132, 220, 132, 79, 117, 178, 226, 143, 29, 194, 27, 107, 64, 156, 129, 172, 88, 61, 104, 69, 29, 177, + 29, 112, 187, 245, 52, 0, 19, 122, 53, 19, 135, 21, 203, 7, 172, 200, 182, 22, 255, 57, 218, 87, 171, 217, 66, 73, + 111, 26, 203, 243, 220, 163, 107, 1, 100, 119, 153, 102, 88, 249, 77, 102, 225, 101, 70, 165, 31, 41, 254, 41, 93, + 99, 149, 180, 82, 57, 159, 149, 56, 95, 108, 9, 72, 143, 241, 147, 92, 159, 177, 187, 68, 170, 192, 164, 10, 190, + 38, 182, 89, 168, 125, 3, 31, 123, 25, 213, 21, 58, 178, 124, 143, 53, 150, 160, 195, 8, 174, 85, 119, 52, 116, 104, + 128, 101, 102, 13, 166, 107, 27, 123, 176, 188, 118, 206, 94, 54, 144, 240, 182, 142, 217, 180, 10, 135, 2, 172, 20, + 213, 78, 198, 96, 100, 108, 92, 70, 195, 140, 235, 24, 154, 173, 165, 213, 49, 206, 84, 174, 149, 151, 217, 172, 42, + 243, 141, 167, 149, 120, 0, 86, 27, 171, 40, 82, 106, 109, 119, 28, 104, 62, 88, 74, 106, 2, 250, 31, 177, 72, 164, + 215, 51, 203, 109, 211, 29, 174, 96, 32, 94, 79, 20, 74, 182, 12, 27, 71, 84, 75, 87, 29, 29, 93, 78, 195, 129, 38, + 46, 16, 185, 142, 205, 226, 64, 8, 155, 244, 190, 172, 130, 65, 128, 60, 124, 46, 63, 143, 100, 0, 155, 13, 124, 8, + 147, 133, 115, 34, 31, 46, 23, 8, 92, 156, 180, 110, 170, 214, 189, 179, 136, 255, 39, 235, 10, 158, 204, 215, 229, + 238, 171, 175, 191, 254, 243, 127, 254, 249, 223, 255, 243, 116, 30, 12, 120, 55, 58, 53, 138, 217, 93, 121, 239, + 84, 234, 77, 131, 188, 108, 78, 91, 105, 6, 124, 36, 98, 224, 24, 120, 73, 127, 126, 19, 8, 165, 189, 93, 45, 23, + 234, 185, 201, 205, 129, 117, 199, 74, 135, 2, 40, 133, 253, 206, 40, 44, 146, 84, 143, 118, 12, 38, 18, 13, 7, 78, + 180, 251, 0, 190, 3, 154, 198, 66, 97, 22, 55, 149, 231, 90, 118, 103, 53, 87, 180, 49, 68, 114, 148, 230, 199, 27, + 229, 136, 2, 67, 242, 151, 215, 120, 241, 128, 198, 152, 247, 126, 40, 77, 208, 228, 149, 122, 151, 63, 92, 148, + 107, 98, 14, 203, 167, 81, 212, 227, 189, 157, 237, 91, 61, 238, 123, 140, 79, 211, 240, 96, 104, 119, 165, 87, 253, + 137, 63, 233, 243, 33, 223, 222, 38, 218, 47, 37, 17, 192, 181, 182, 89, 17, 232, 26, 6, 79, 136, 132, 50, 96, 40, + 176, 80, 240, 23, 125, 122, 156, 115, 233, 231, 96, 93, 29, 71, 211, 81, 8, 48, 127, 13, 24, 78, 199, 89, 249, 144, + 12, 246, 229, 190, 217, 114, 80, 78, 55, 19, 153, 48, 61, 22, 202, 147, 194, 111, 188, 90, 123, 173, 32, 19, 98, + 120, 22, 46, 32, 161, 56, 95, 231, 110, 119, 138, 115, 87, 17, 149, 194, 57, 134, 133, 154, 193, 88, 144, 231, 78, + 73, 84, 52, 86, 152, 176, 97, 233, 149, 30, 27, 169, 15, 50, 134, 70, 232, 32, 204, 166, 45, 57, 97, 150, 68, 205, + 134, 232, 103, 205, 253, 106, 131, 184, 64, 124, 67, 162, 203, 50, 105, 250, 29, 248, 42, 79, 157, 142, 25, 169, + 130, 90, 127, 64, 99, 159, 160, 142, 7, 107, 139, 183, 8, 179, 143, 28, 228, 17, 42, 185, 24, 181, 110, 208, 19, 5, + 183, 232, 131, 183, 200, 30, 183, 225, 8, 152, 231, 21, 199, 251, 244, 128, 106, 203, 83, 37, 246, 17, 84, 43, 250, + 5, 133, 38, 210, 201, 46, 205, 242, 35, 152, 143, 129, 146, 226, 112, 137, 143, 44, 228, 37, 47, 28, 7, 9, 121, 225, + 248, 216, 151, 91, 178, 72, 61, 230, 80, 105, 139, 15, 22, 35, 61, 143, 100, 180, 187, 242, 20, 219, 146, 185, 32, + 200, 79, 180, 169, 171, 77, 233, 149, 149, 120, 72, 43, 37, 108, 178, 168, 248, 191, 159, 118, 136, 119, 100, 48, + 252, 107, 129, 249, 33, 89, 77, 165, 71, 210, 202, 154, 144, 220, 31, 231, 112, 241, 73, 247, 154, 239, 38, 42, 51, + 47, 48, 252, 15, 99, 124, 72, 89, 120, 87, 84, 30, 91, 45, 27, 178, 3, 33, 49, 225, 47, 63, 183, 83, 218, 143, 102, + 179, 31, 247, 181, 181, 175, 191, 191, 47, 205, 204, 70, 20, 217, 166, 9, 218, 199, 94, 131, 14, 55, 108, 28, 78, + 221, 141, 143, 18, 195, 23, 57, 15, 36, 175, 61, 236, 228, 131, 133, 55, 98, 156, 9, 114, 150, 147, 250, 208, 192, + 45, 20, 24, 0, 215, 252, 182, 86, 182, 13, 98, 61, 146, 86, 85, 98, 124, 26, 85, 219, 166, 129, 99, 57, 228, 111, + 146, 37, 239, 59, 214, 123, 37, 66, 195, 127, 208, 72, 240, 31, 66, 114, 75, 6, 102, 226, 203, 2, 125, 215, 1, 89, + 18, 151, 189, 131, 176, 65, 81, 133, 10, 93, 67, 69, 199, 29, 242, 12, 136, 188, 30, 44, 141, 91, 152, 185, 65, 32, + 90, 50, 48, 248, 100, 4, 35, 217, 243, 16, 138, 58, 29, 100, 27, 41, 230, 42, 20, 144, 122, 70, 248, 25, 8, 232, 84, + 170, 51, 185, 248, 219, 195, 111, 190, 57, 65, 237, 67, 126, 156, 255, 143, 211, 111, 22, 234, 101, 10, 239, 31, + 118, 101, 163, 255, 133, 99, 218, 206, 224, 252, 194, 44, 207, 138, 236, 165, 109, 23, 155, 109, 20, 142, 207, 222, + 7, 103, 201, 75, 252, 123, 224, 35, 20, 169, 73, 235, 155, 103, 80, 182, 172, 76, 122, 159, 223, 151, 31, 143, 176, + 190, 70, 49, 140, 142, 151, 162, 36, 224, 41, 244, 109, 156, 7, 98, 161, 249, 128, 149, 224, 61, 253, 236, 227, 223, + 62, 220, 29, 226, 109, 15, 136, 143, 56, 2, 41, 5, 82, 101, 92, 84, 157, 90, 242, 113, 4, 83, 237, 142, 4, 166, 184, + 218, 71, 92, 152, 97, 121, 204, 206, 211, 121, 127, 112, 241, 218, 211, 18, 131, 56, 23, 88, 151, 85, 29, 144, 33, + 81, 118, 99, 185, 74, 15, 101, 243, 24, 235, 9, 104, 35, 163, 140, 129, 145, 220, 176, 193, 185, 171, 252, 33, 132, + 67, 108, 175, 62, 241, 153, 199, 14, 239, 43, 108, 19, 56, 154, 185, 59, 5, 97, 177, 35, 172, 220, 71, 193, 30, 156, + 26, 188, 88, 87, 229, 225, 200, 228, 130, 19, 203, 204, 167, 79, 242, 240, 59, 7, 153, 109, 47, 241, 87, 164, 123, + 15, 126, 212, 189, 247, 199, 24, 146, 180, 224, 165, 13, 15, 98, 4, 86, 218, 208, 18, 235, 115, 212, 75, 142, 172, + 112, 203, 22, 98, 62, 76, 30, 80, 132, 175, 249, 115, 10, 227, 67, 251, 84, 59, 20, 80, 172, 110, 239, 77, 189, 226, + 193, 94, 228, 2, 224, 236, 99, 206, 102, 71, 160, 250, 249, 167, 20, 109, 26, 179, 110, 208, 176, 188, 118, 114, + 154, 8, 50, 25, 195, 62, 130, 126, 200, 214, 227, 211, 99, 190, 228, 227, 182, 126, 44, 125, 166, 123, 104, 139, + 191, 129, 50, 52, 33, 61, 35, 172, 100, 211, 222, 243, 77, 143, 220, 185, 119, 249, 46, 13, 223, 103, 229, 58, 72, + 103, 199, 208, 117, 216, 218, 67, 202, 6, 45, 35, 65, 240, 93, 112, 235, 71, 240, 236, 109, 68, 200, 26, 75, 111, + 209, 39, 79, 172, 194, 194, 253, 142, 168, 28, 130, 217, 228, 33, 160, 43, 254, 94, 30, 7, 3, 4, 34, 104, 119, 160, + 48, 241, 199, 241, 96, 36, 221, 236, 69, 174, 241, 92, 53, 187, 91, 122, 119, 210, 44, 210, 142, 169, 45, 25, 108, + 231, 136, 202, 46, 27, 242, 107, 111, 251, 223, 149, 89, 142, 234, 153, 220, 111, 34, 156, 195, 219, 90, 210, 229, + 206, 169, 240, 212, 232, 189, 2, 98, 41, 171, 44, 152, 143, 131, 148, 89, 149, 160, 213, 221, 149, 15, 108, 104, 32, + 237, 154, 203, 238, 129, 230, 92, 194, 161, 38, 68, 226, 41, 213, 3, 185, 191, 31, 225, 108, 109, 191, 229, 2, 130, + 252, 11, 118, 86, 168, 6, 20, 65, 10, 131, 228, 31, 95, 233, 183, 205, 250, 238, 149, 190, 46, 53, 213, 6, 106, 93, + 152, 196, 21, 157, 157, 3, 177, 223, 150, 85, 56, 110, 144, 95, 73, 214, 252, 206, 1, 116, 98, 204, 235, 24, 220, + 150, 126, 244, 85, 156, 174, 6, 250, 254, 14, 19, 188, 29, 61, 212, 188, 145, 102, 36, 18, 8, 24, 32, 177, 117, 188, + 201, 87, 192, 42, 187, 116, 22, 110, 247, 49, 145, 101, 116, 187, 13, 221, 171, 170, 90, 118, 158, 13, 42, 94, 197, + 194, 1, 21, 172, 69, 138, 70, 177, 242, 126, 236, 116, 93, 6, 66, 52, 224, 199, 104, 199, 88, 96, 90, 234, 48, 123, + 72, 0, 252, 90, 43, 135, 94, 234, 227, 128, 68, 67, 255, 116, 185, 89, 40, 164, 216, 243, 238, 55, 124, 177, 227, 4, + 76, 89, 82, 204, 223, 193, 114, 106, 146, 197, 255, 34, 108, 82, 106, 181, 22, 60, 66, 84, 211, 27, 47, 117, 205, + 250, 99, 78, 187, 231, 70, 236, 79, 47, 190, 152, 166, 164, 204, 145, 93, 53, 114, 174, 237, 125, 108, 79, 166, 191, + 202, 197, 45, 156, 90, 83, 207, 85, 136, 137, 0, 98, 202, 253, 94, 92, 51, 126, 169, 32, 159, 7, 232, 100, 159, 139, + 9, 121, 219, 232, 187, 25, 105, 30, 4, 81, 142, 202, 6, 149, 163, 195, 180, 24, 4, 145, 44, 24, 187, 188, 187, 238, + 243, 23, 1, 158, 247, 19, 21, 134, 232, 143, 148, 105, 158, 46, 120, 90, 250, 53, 216, 202, 104, 125, 196, 79, 19, + 46, 42, 145, 92, 178, 146, 81, 86, 54, 228, 107, 16, 158, 213, 227, 161, 220, 249, 229, 197, 139, 56, 134, 45, 167, + 77, 209, 43, 35, 16, 115, 57, 130, 12, 37, 2, 93, 224, 169, 152, 132, 131, 145, 137, 244, 33, 163, 96, 227, 213, 56, + 70, 206, 51, 39, 77, 70, 142, 155, 157, 74, 191, 206, 111, 28, 144, 171, 168, 183, 131, 47, 48, 60, 248, 88, 209, + 139, 251, 244, 150, 247, 232, 67, 25, 219, 87, 244, 69, 59, 101, 67, 182, 52, 193, 210, 213, 102, 165, 198, 6, 81, + 202, 183, 131, 160, 58, 178, 123, 99, 52, 62, 111, 159, 140, 201, 196, 238, 125, 91, 77, 15, 163, 138, 0, 21, 2, + 141, 113, 236, 164, 213, 143, 75, 47, 174, 80, 34, 248, 20, 123, 82, 113, 117, 180, 192, 176, 146, 239, 202, 189, + 132, 55, 66, 121, 97, 161, 24, 150, 242, 144, 24, 134, 29, 94, 11, 140, 196, 241, 98, 35, 175, 212, 77, 144, 191, + 123, 57, 214, 94, 39, 62, 186, 15, 1, 29, 39, 232, 133, 224, 88, 131, 77, 69, 121, 168, 25, 29, 66, 237, 176, 146, + 35, 65, 153, 203, 133, 50, 91, 42, 81, 151, 152, 67, 59, 63, 18, 160, 17, 197, 152, 201, 233, 52, 25, 12, 104, 140, + 82, 109, 175, 31, 71, 196, 217, 120, 26, 90, 65, 9, 101, 241, 231, 103, 214, 16, 3, 234, 104, 29, 81, 123, 110, 1, + 46, 227, 135, 177, 238, 189, 164, 35, 97, 56, 30, 129, 90, 10, 27, 8, 241, 244, 136, 48, 68, 89, 65, 73, 99, 24, 73, + 244, 32, 181, 238, 128, 134, 235, 80, 164, 46, 15, 114, 220, 120, 162, 135, 34, 133, 119, 110, 149, 231, 221, 151, + 179, 105, 203, 79, 1, 57, 124, 133, 224, 229, 232, 241, 244, 65, 239, 251, 51, 156, 117, 185, 22, 51, 186, 54, 223, + 194, 223, 90, 245, 63, 3, 225, 190, 131, 36, 36, 123, 245, 45, 108, 213, 136, 21, 203, 34, 91, 251, 152, 85, 179, + 100, 98, 214, 200, 171, 33, 117, 148, 153, 200, 18, 26, 190, 57, 48, 124, 118, 88, 33, 153, 115, 49, 15, 162, 240, + 30, 35, 53, 237, 185, 137, 105, 147, 248, 123, 98, 198, 119, 121, 145, 112, 101, 78, 130, 131, 163, 112, 229, 14, + 181, 181, 220, 184, 167, 79, 36, 37, 202, 246, 179, 188, 93, 202, 25, 34, 51, 73, 205, 168, 143, 209, 77, 148, 211, + 135, 46, 81, 194, 161, 6, 53, 10, 225, 206, 20, 120, 128, 138, 132, 1, 84, 177, 49, 178, 33, 221, 33, 36, 242, 104, + 157, 14, 136, 248, 228, 241, 194, 66, 175, 75, 165, 66, 89, 3, 28, 227, 198, 213, 90, 189, 177, 206, 31, 2, 31, 47, + 7, 172, 99, 3, 97, 111, 65, 110, 242, 48, 102, 112, 100, 105, 224, 100, 114, 76, 227, 47, 42, 137, 161, 207, 83, 50, + 114, 89, 214, 31, 181, 4, 46, 233, 108, 45, 148, 12, 207, 230, 206, 114, 83, 214, 61, 243, 206, 95, 241, 13, 168, + 245, 26, 15, 79, 74, 112, 228, 89, 11, 44, 3, 139, 31, 131, 8, 244, 195, 245, 185, 4, 7, 203, 36, 82, 4, 51, 59, + 161, 57, 211, 195, 255, 3, 143, 42, 151, 58 \ No newline at end of file diff --git a/jsvm/src/js_native_api_v8.cc b/jsvm/src/js_native_api_v8.cc new file mode 100644 index 0000000000000000000000000000000000000000..fba9d5e05540f8ed580e224fc34f7decc5fde0d1 --- /dev/null +++ b/jsvm/src/js_native_api_v8.cc @@ -0,0 +1,4539 @@ +#include +#include +#include // INT_MAX +#include +#include +#include + +#include "v8-debug.h" +#include "v8-internal.h" +#include "v8-local-handle.h" +#include "v8-primitive.h" +#include "v8-statistics.h" +#include "v8-version-string.h" +#define JSVM_EXPERIMENTAL +#include "js_native_api_v8.h" +#include "jsvm.h" +#include "jsvm_env.h" +#include "jsvm_log.h" +#include "jsvm_reference.h" +#include "jsvm_util.h" +#include "libplatform/libplatform.h" +#include "platform/platform.h" +#include "sourcemap.def" + +namespace v8impl { + +namespace { + +enum IsolateDataSlot { + kIsolateData = 0, + kIsolateSnapshotCreatorSlot = 1, +}; + +enum ContextEmbedderIndex { + kContextEnvIndex = 1, +}; + +struct IsolateData { + IsolateData(v8::StartupData* blob) : blob(blob) {} + + ~IsolateData() + { + delete blob; + } + + v8::StartupData* blob; + v8::Eternal typeTagKey; + v8::Eternal wrapperKey; +}; + +static void CreateIsolateData(v8::Isolate* isolate, v8::StartupData* blob) +{ + auto data = new IsolateData(blob); + v8::Isolate::Scope isolateScope(isolate); + v8::HandleScope handleScope(isolate); + if (blob) { + // NOTE: The order of getting the data must be consistent with the order of + // adding data in OH_JSVM_CreateSnapshot. + auto wrapperKey = isolate->GetDataFromSnapshotOnce(0); + auto typeTagKey = isolate->GetDataFromSnapshotOnce(1); + data->wrapperKey.Set(isolate, wrapperKey.ToLocalChecked()); + data->typeTagKey.Set(isolate, typeTagKey.ToLocalChecked()); + } else { + data->wrapperKey.Set(isolate, v8::Private::New(isolate)); + data->typeTagKey.Set(isolate, v8::Private::New(isolate)); + } + isolate->SetData(v8impl::kIsolateData, data); +} + +static IsolateData* GetIsolateData(v8::Isolate* isolate) +{ + auto data = isolate->GetData(v8impl::kIsolateData); + return reinterpret_cast(data); +} + +static void SetIsolateSnapshotCreator(v8::Isolate* isolate, v8::SnapshotCreator* creator) +{ + isolate->SetData(v8impl::kIsolateSnapshotCreatorSlot, creator); +} + +static v8::SnapshotCreator* GetIsolateSnapshotCreator(v8::Isolate* isolate) +{ + auto data = isolate->GetData(v8impl::kIsolateSnapshotCreatorSlot); + return reinterpret_cast(data); +} + +static void SetContextEnv(v8::Local context, JSVM_Env env) +{ + context->SetAlignedPointerInEmbedderData(kContextEnvIndex, env); +} + +static JSVM_Env GetContextEnv(v8::Local context) +{ + auto data = context->GetAlignedPointerFromEmbedderData(kContextEnvIndex); + return reinterpret_cast(data); +} + +class OutputStream : public v8::OutputStream { +public: + OutputStream(JSVM_OutputStream stream, void* data, int chunkSize = 65536) + : stream(stream), streamData(data), chunkSize(chunkSize) + {} + + int GetChunkSize() override + { + return chunkSize; + } + + void EndOfStream() override + { + stream(nullptr, 0, streamData); + } + + WriteResult WriteAsciiChunk(char* data, const int size) override + { + return stream(data, size, streamData) ? kContinue : kAbort; + } + +private: + JSVM_OutputStream stream; + void* streamData; + int chunkSize; +}; + +static std::unique_ptr g_platform = v8::platform::NewDefaultPlatform(); + +static std::vector externalReferenceRegistry; + +static std::unordered_map sourceMapUrlMap; + +static std::unique_ptr defaultArrayBufferAllocator; + +static v8::ArrayBuffer::Allocator* GetOrCreateDefaultArrayBufferAllocator() +{ + if (!defaultArrayBufferAllocator) { + defaultArrayBufferAllocator.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator()); + } + return defaultArrayBufferAllocator.get(); +} + +static void SetFileToSourceMapMapping(std::string&& file, std::string&& sourceMapUrl) +{ + auto it = sourceMapUrlMap.find(file); + if (it == sourceMapUrlMap.end()) { + sourceMapUrlMap.emplace(file, sourceMapUrl); + return; + } + auto&& prevSourceMapUrl = it->second; + CHECK(prevSourceMapUrl == sourceMapUrl); +} + +static std::string GetSourceMapFromFileName(std::string&& file) +{ + auto it = sourceMapUrlMap.find(file); + if (it != sourceMapUrlMap.end()) { + return it->second; + } + return ""; +} + +template +JSVM_Status NewString(JSVM_Env env, const CCharType* str, size_t length, JSVM_Value* result, StringMaker stringMaker) +{ + CHECK_NEW_STRING_ARGS(env, str, length, result); + + auto isolate = env->isolate; + auto strMaybe = stringMaker(isolate); + CHECK_MAYBE_EMPTY(env, strMaybe, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(strMaybe.ToLocalChecked()); + return ClearLastError(env); +} + +inline JSVM_Status V8NameFromPropertyDescriptor(JSVM_Env env, + const JSVM_PropertyDescriptor* p, + v8::Local* result) +{ + if (p->utf8name != nullptr) { + CHECK_NEW_FROM_UTF8(env, *result, p->utf8name); + } else { + v8::Local propertyValue = v8impl::V8LocalValueFromJsValue(p->name); + + RETURN_STATUS_IF_FALSE(env, propertyValue->IsName(), JSVM_NAME_EXPECTED); + *result = propertyValue.As(); + } + + return JSVM_OK; +} + +// convert from jsvm-api property attributes to v8::PropertyAttribute +inline v8::PropertyAttribute V8PropertyAttributesFromDescriptor(const JSVM_PropertyDescriptor* descriptor) +{ + unsigned int attributeFlags = v8::PropertyAttribute::None; + + // The JSVM_WRITABLE attribute is ignored for accessor descriptors, but + // V8 would throw `TypeError`s on assignment with nonexistence of a setter. + if ((descriptor->getter == nullptr && descriptor->setter == nullptr) && + (descriptor->attributes & JSVM_WRITABLE) == 0) { + attributeFlags |= v8::PropertyAttribute::ReadOnly; + } + + if ((descriptor->attributes & JSVM_ENUMERABLE) == 0) { + attributeFlags |= v8::PropertyAttribute::DontEnum; + } + if ((descriptor->attributes & JSVM_CONFIGURABLE) == 0) { + attributeFlags |= v8::PropertyAttribute::DontDelete; + } + + return static_cast(attributeFlags); +} + +inline JSVM_Status ConcludeDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value result, bool isResolved) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8impl::Persistent* deferredRef = PersistentFromJsDeferred(deferred); + v8::Local v8Deferred = v8::Local::New(env->isolate, *deferredRef); + + auto resolver = v8Deferred.As(); + + v8::Maybe success = isResolved ? resolver->Resolve(context, v8impl::V8LocalValueFromJsValue(result)) + : resolver->Reject(context, v8impl::V8LocalValueFromJsValue(result)); + + delete deferredRef; + RETURN_STATUS_IF_FALSE(env, success.FromMaybe(false), JSVM_GENERIC_FAILURE); + + return GET_RETURN_STATUS(env); +} + +enum UnwrapAction { KeepWrap, RemoveWrap }; + +inline JSVM_Status Unwrap(JSVM_Env env, JSVM_Value jsObject, void** result, UnwrapAction action) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, jsObject); + if (action == KeepWrap) { + CHECK_ARG(env, result); + } + + v8::Local context = env->context(); + + v8::Local value = v8impl::V8LocalValueFromJsValue(jsObject); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG); + v8::Local obj = value.As(); + + auto val = obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).ToLocalChecked(); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG); + RuntimeReference* reference = static_cast(val.As()->Value()); + + if (result) { + *result = reference->GetData(); + } + + if (action == RemoveWrap) { + CHECK(obj->DeletePrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).FromJust()); + v8impl::RuntimeReference::DeleteReference(reference); + } + + return GET_RETURN_STATUS(env); +} + +//=== Function JSVM_Callback wrapper ================================= + +// Use this data structure to associate callback data with each N-API function +// exposed to JavaScript. The structure is stored in a v8::External which gets +// passed into our callback wrapper. This reduces the performance impact of +// calling through N-API. +// Ref: benchmark/misc/function_call +// Discussion (incl. perf. data): https://github.com/nodejs/node/pull/21072 +class CallbackBundle { +public: + // Creates an object to be made available to the static function callback + // wrapper, used to retrieve the native callback function and data pointer. + static inline v8::Local New(JSVM_Env env, JSVM_Callback cb) + { + return v8::External::New(env->isolate, cb); + } + + static inline v8::Local New(JSVM_Env env, v8impl::JSVM_PropertyHandlerCfgStruct* cb) + { + return v8::External::New(env->isolate, cb); + } +}; + +// Base class extended by classes that wrap V8 function and property callback +// info. +class CallbackWrapper { +public: + inline CallbackWrapper(JSVM_Value thisArg, size_t argsLength, void* data) + : receiver(thisArg), argsLength(argsLength), data(data) + {} + + virtual JSVM_Value GetNewTarget() = 0; + + virtual void GetArgs(JSVM_Value* buffer, size_t bufferLength) = 0; + + virtual void SetReturnValue(JSVM_Value value) = 0; + + JSVM_Value This() + { + return receiver; + } + + size_t ArgsLength() + { + return argsLength; + } + + void* Data() + { + return data; + } + +protected: + const JSVM_Value receiver; + const size_t argsLength; + void* data; +}; + +class CallbackWrapperBase : public CallbackWrapper { +public: + inline CallbackWrapperBase(const v8::FunctionCallbackInfo& cbinfo, const size_t argsLength) + : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), argsLength, nullptr), cbinfo(cbinfo) + { + cb = static_cast(cbinfo.Data().As()->Value()); + data = cb->data; + } + +protected: + inline const v8::FunctionCallbackInfo& GetCbInfo() + { + return cbinfo; + } + inline void InvokeCallback() + { + JSVM_CallbackInfo cbinfoWrapper = reinterpret_cast(static_cast(this)); + + // All other pointers we need are stored in `_bundle` + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto func = cb->callback; + + JSVM_Value result = nullptr; + bool exceptionOccurred = false; + env->CallIntoModule([&](JSVM_Env env) { result = func(env, cbinfoWrapper); }, + [&](JSVM_Env env, v8::Local value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(value); + }); + + if (!exceptionOccurred && (result != nullptr)) { + this->SetReturnValue(result); + } + } + +private: + const v8::FunctionCallbackInfo& cbinfo; + JSVM_Callback cb; +}; + +class FunctionCallbackWrapper : public CallbackWrapperBase { +public: + static void Invoke(const v8::FunctionCallbackInfo& info) + { + FunctionCallbackWrapper cbwrapper(info); + cbwrapper.InvokeCallback(); + } + + static inline JSVM_Status NewFunction(JSVM_Env env, JSVM_Callback cb, v8::Local* result) + { + v8::Local cbdata = v8impl::CallbackBundle::New(env, cb); + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE); + + v8::MaybeLocal maybeFunction = v8::Function::New(env->context(), Invoke, cbdata); + CHECK_MAYBE_EMPTY(env, maybeFunction, JSVM_GENERIC_FAILURE); + + *result = maybeFunction.ToLocalChecked(); + return ClearLastError(env); + } + + static inline JSVM_Status NewTemplate(JSVM_Env env, + JSVM_Callback cb, + v8::Local* result, + v8::Local sig = v8::Local()) + { + v8::Local cbdata = v8impl::CallbackBundle::New(env, cb); + RETURN_STATUS_IF_FALSE(env, !cbdata.IsEmpty(), JSVM_GENERIC_FAILURE); + + *result = v8::FunctionTemplate::New(env->isolate, Invoke, cbdata, sig); + return ClearLastError(env); + } + + explicit FunctionCallbackWrapper(const v8::FunctionCallbackInfo& cbinfo) + : CallbackWrapperBase(cbinfo, cbinfo.Length()) + {} + + JSVM_Value GetNewTarget() override + { + if (GetCbInfo().IsConstructCall()) { + return v8impl::JsValueFromV8LocalValue(GetCbInfo().NewTarget()); + } else { + return nullptr; + } + } + + /*virtual*/ + void GetArgs(JSVM_Value* buffer, size_t bufferLength) override + { + size_t i = 0; + size_t min = std::min(bufferLength, argsLength); + + const auto& info = GetCbInfo(); + for (; i < min; i += 1) { + buffer[i] = v8impl::JsValueFromV8LocalValue(info[i]); + } + + if (i < bufferLength) { + JSVM_Value undefined = v8impl::JsValueFromV8LocalValue(v8::Undefined(info.GetIsolate())); + for (; i < bufferLength; i += 1) { + buffer[i] = undefined; + } + } + } + + /*virtual*/ + void SetReturnValue(JSVM_Value value) override + { + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + GetCbInfo().GetReturnValue().Set(val); + } +}; + +template +class PropertyCallbackWrapperBase : public CallbackWrapper { +public: + inline PropertyCallbackWrapperBase(uint32_t index, + v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& cbinfo, + const size_t argsLength) + : CallbackWrapper(JsValueFromV8LocalValue(cbinfo.This()), argsLength, nullptr), cbinfo(cbinfo), + property(property), value(value), index(index) + { + propertyHandler = + static_cast(cbinfo.Data().template As()->Value()); + } + + JSVM_Value GetNewTarget() override + { + return nullptr; + } + + virtual void GetArgs(JSVM_Value* buffer, size_t bufferlength) override {} + +protected: + inline void NameSetterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto setterCb = propertyHandler->namedSetterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->namedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->namedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value name = JsValueFromV8LocalValue(property); + JSVM_Value v8Value = JsValueFromV8LocalValue(value); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (setterCb) { + result = setterCb(env, name, v8Value, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + this->SetReturnValue(result); + } + } + + inline void NameGetterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto getterCb = propertyHandler->namedGetterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->namedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->namedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value name = JsValueFromV8LocalValue(property); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (getterCb) { + result = getterCb(env, name, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + this->SetReturnValue(result); + } + } + + inline void NameDeleterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto deleterCb = propertyHandler->nameDeleterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->namedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->namedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value name = JsValueFromV8LocalValue(property); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (deleterCb) { + result = deleterCb(env, name, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) { + this->SetReturnValue(result); + } + } + } + + inline void NameEnumeratorInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto enumeratorCb = propertyHandler->namedEnumeratorCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->namedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->namedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (enumeratorCb) { + result = enumeratorCb(env, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) { + this->SetReturnValue(result); + } + } + } + + inline void IndexSetterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto indexSetterCb = propertyHandler->indexedSetterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->indexedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->indexedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); + JSVM_Value v8Value = JsValueFromV8LocalValue(value); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (indexSetterCb) { + result = indexSetterCb(env, v8Index, v8Value, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + this->SetReturnValue(result); + } + } + + inline void IndexGetterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto indexGetterCb = propertyHandler->indexedGetterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->indexedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->indexedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + JSVM_Value result = nullptr; + bool exceptionOccurred = false; + JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (indexGetterCb) { + result = indexGetterCb(env, v8Index, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + this->SetReturnValue(result); + } + } + + inline void IndexDeleterInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto indexDeleterCb = propertyHandler->indexedDeleterCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->indexedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->indexedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value v8Index = JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, index)); + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (indexDeleterCb) { + result = indexDeleterCb(env, v8Index, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + if (v8impl::V8LocalValueFromJsValue(result)->IsBoolean()) { + this->SetReturnValue(result); + } + } + } + + inline void IndexEnumeratorInvokeCallback() + { + auto context = cbinfo.GetIsolate()->GetCurrentContext(); + auto env = v8impl::GetContextEnv(context); + auto enumeratorCb = propertyHandler->indexedEnumeratorCallback_; + + JSVM_Value innerData = nullptr; + if (propertyHandler->indexedPropertyData_ != nullptr) { + v8impl::UserReference* reference = + reinterpret_cast(propertyHandler->indexedPropertyData_); + innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + } + + bool exceptionOccurred = false; + JSVM_Value result = nullptr; + JSVM_Value thisArg = this->This(); + env->CallIntoModule( + [&](JSVM_Env env) { + if (enumeratorCb) { + result = enumeratorCb(env, thisArg, innerData); + } + }, + [&](JSVM_Env env, v8::Local v8Value) { + exceptionOccurred = true; + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(v8Value); + }); + if (!exceptionOccurred && (result != nullptr)) { + if (v8impl::V8LocalValueFromJsValue(result)->IsArray()) { + this->SetReturnValue(result); + } + } + } + + const v8::PropertyCallbackInfo& cbinfo; + JSVM_PropertyHandlerCfgStruct* propertyHandler; + v8::Local property; + v8::Local value; + uint32_t index; +}; + +template +class PropertyCallbackWrapper : public PropertyCallbackWrapperBase { +public: + static void NameSetterInvoke(v8::Local property, + v8::Local value, + const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(property, value, info); + propertyCbWrapper.NameSetterInvokeCallback(); + } + + static void NameGetterInvoke(v8::Local property, const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(property, v8::Local(), info); + propertyCbWrapper.NameGetterInvokeCallback(); + } + + static void NameDeleterInvoke(v8::Local property, const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(property, v8::Local(), info); + propertyCbWrapper.NameDeleterInvokeCallback(); + } + + static void NameEnumeratorInvoke(const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(v8::Local(), v8::Local(), info); + propertyCbWrapper.NameEnumeratorInvokeCallback(); + } + + static void IndexSetterInvoke(uint32_t index, + v8::Local value, + const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(index, value, info); + propertyCbWrapper.IndexSetterInvokeCallback(); + } + + static void IndexGetterInvoke(uint32_t index, const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(index, v8::Local(), info); + propertyCbWrapper.IndexGetterInvokeCallback(); + } + + static void IndexDeleterInvoke(uint32_t index, const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(index, v8::Local(), info); + propertyCbWrapper.IndexDeleterInvokeCallback(); + } + + static void IndexEnumeratorInvoke(const v8::PropertyCallbackInfo& info) + { + PropertyCallbackWrapper propertyCbWrapper(0, v8::Local(), info); + propertyCbWrapper.IndexEnumeratorInvokeCallback(); + } + + explicit PropertyCallbackWrapper(v8::Local name, + v8::Local value, + const v8::PropertyCallbackInfo& cbinfo) + : PropertyCallbackWrapperBase(0, name, value, cbinfo, 0), cbinfo(cbinfo) + {} + + explicit PropertyCallbackWrapper(uint32_t index, + v8::Local value, + const v8::PropertyCallbackInfo& cbinfo) + : PropertyCallbackWrapperBase(index, v8::Local(), value, cbinfo, 0), cbinfo(cbinfo) + {} + + /*virtual*/ + void SetReturnValue(JSVM_Value value) override + { + v8::Local val = v8impl::V8LocalValueFromJsValue(value).As(); + cbinfo.GetReturnValue().Set(val); + } + +protected: + const v8::PropertyCallbackInfo& cbinfo; +}; + +inline JSVM_Status Wrap(JSVM_Env env, + JSVM_Value jsObject, + void* nativeObject, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Ref* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, jsObject); + + v8::Local context = env->context(); + + v8::Local value = v8impl::V8LocalValueFromJsValue(jsObject); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), JSVM_INVALID_ARG); + v8::Local obj = value.As(); + + // If we've already wrapped this object, we error out. + RETURN_STATUS_IF_FALSE(env, !obj->HasPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper)).FromJust(), + JSVM_INVALID_ARG); + + auto reference = v8impl::RuntimeReference::New(env, obj, finalizeCb, nativeObject, finalizeHint); + if (result != nullptr) { + auto* userRef = v8impl::UserReference::New(env, obj, 0); + *result = reinterpret_cast(userRef); + } + + CHECK(obj->SetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, wrapper), v8::External::New(env->isolate, reference)) + .FromJust()); + + return GET_RETURN_STATUS(env); +} + +} // end of anonymous namespace + +} // end of namespace v8impl + +v8::Platform* JSVM_Env__::platform() +{ + return v8impl::g_platform.get(); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Init(const JSVM_InitOptions* options) +{ + static std::atomic initialized(false); + if (initialized.load()) { + return JSVM_GENERIC_FAILURE; + } + initialized.store(true); + + OHOS_API_CALL(platform::ohos::WriteHisysevent()); + OHOS_API_CALL(platform::ohos::ReportKeyThread(platform::ohos::ThreadRole::IMPORTANT_DISPLAY)); + v8::V8::InitializePlatform(v8impl::g_platform.get()); + + OHOS_API_CALL(platform::ohos::SetSecurityMode()); + + if (options && options->argc && options->argv) { + v8::V8::SetFlagsFromCommandLine(options->argc, options->argv, options->removeFlags); + } + v8::V8::Initialize(); + + const auto cb = v8impl::FunctionCallbackWrapper::Invoke; + v8impl::externalReferenceRegistry.push_back((intptr_t)cb); + if (auto p = options ? options->externalReferences : nullptr) { + for (; *p != 0; p++) { + v8impl::externalReferenceRegistry.push_back(*p); + } + } + v8impl::externalReferenceRegistry.push_back(0); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetVM(JSVM_Env env, JSVM_VM* result) +{ + *result = reinterpret_cast(env->isolate); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) +{ + OHOS_API_CALL(platform::ohos::ReportKeyThread(platform::ohos::ThreadRole::USER_INTERACT)); + + v8::Isolate::CreateParams createParams; + auto externalReferences = v8impl::externalReferenceRegistry.data(); + createParams.external_references = externalReferences; + + v8::StartupData* snapshotBlob = nullptr; + if (options && options->snapshotBlobData) { + snapshotBlob = new v8::StartupData(); + snapshotBlob->data = options->snapshotBlobData; + snapshotBlob->raw_size = options->snapshotBlobSize; + + if (!snapshotBlob->IsValid()) { + // TODO: Is VerifyCheckSum necessay if there has been a validity check? + delete snapshotBlob; + return JSVM_INVALID_ARG; + } + createParams.snapshot_blob = snapshotBlob; + } + + v8::Isolate* isolate; + if (options && options->isForSnapshotting) { + isolate = v8::Isolate::Allocate(); + auto creator = new v8::SnapshotCreator(isolate, externalReferences); + v8impl::SetIsolateSnapshotCreator(isolate, creator); + } else { + createParams.array_buffer_allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); + isolate = v8::Isolate::New(createParams); + } + v8impl::CreateIsolateData(isolate, snapshotBlob); + *result = reinterpret_cast(isolate); + + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_DestroyVM(JSVM_VM vm) +{ + if (!vm) { + return JSVM_INVALID_ARG; + } + auto isolate = reinterpret_cast(vm); + auto creator = v8impl::GetIsolateSnapshotCreator(isolate); + auto data = v8impl::GetIsolateData(isolate); + + if (creator != nullptr) { + delete creator; + } else { + isolate->Dispose(); + } + if (data != nullptr) { + delete data; + } + + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_OpenVMScope(JSVM_VM vm, JSVM_VMScope* result) +{ + auto isolate = reinterpret_cast(vm); + auto scope = new v8::Isolate::Scope(isolate); + *result = reinterpret_cast(scope); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_CloseVMScope(JSVM_VM vm, JSVM_VMScope scope) +{ + auto v8scope = reinterpret_cast(scope); + delete v8scope; + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateEnv(JSVM_VM vm, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Env* result) +{ + auto isolate = reinterpret_cast(vm); + auto env = new JSVM_Env__(isolate, JSVM_API_VERSION); + v8::HandleScope handleScope(isolate); + auto globalTemplate = v8::ObjectTemplate::New(isolate); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + + if ((p->attributes & JSVM_STATIC) != 0) { + // Ignore static properties. + continue; + } + + v8::Local propertyName = + v8::String::NewFromUtf8(isolate, p->utf8name, v8::NewStringType::kInternalized).ToLocalChecked(); + + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); + + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local getterTpl; + v8::Local setterTpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); + } + + globalTemplate->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes); + } else if (p->method != nullptr) { + v8::Local methodTpl; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &methodTpl)); + + globalTemplate->Set(propertyName, methodTpl, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + globalTemplate->Set(propertyName, value, attributes); + } + } + + v8::Local context = v8::Context::New(isolate, nullptr, globalTemplate); + env->contextPersistent.Reset(isolate, context); + v8impl::SetContextEnv(context, env); + *result = env; + // The error code is set in constructor function, just return JSVM_OK here. + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_CreateEnvFromSnapshot(JSVM_VM vm, size_t index, JSVM_Env* result) +{ + auto isolate = reinterpret_cast(vm); + v8::HandleScope handleScope(isolate); + auto maybe = v8::Context::FromSnapshot(isolate, index); + + if (maybe.IsEmpty()) { + *result = nullptr; + // TODO: return error message. + return JSVM_GENERIC_FAILURE; + } + + auto env = new JSVM_Env__(isolate, JSVM_API_VERSION); + auto context = maybe.ToLocalChecked(); + env->contextPersistent.Reset(isolate, context); + v8impl::SetContextEnv(context, env); + *result = env; + + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_DestroyEnv(JSVM_Env env) +{ + env->DeleteMe(); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_OpenEnvScope(JSVM_Env env, JSVM_EnvScope* result) +{ + auto v8scope = new v8::Context::Scope(env->context()); + *result = reinterpret_cast(v8scope); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CloseEnvScope(JSVM_Env env, JSVM_EnvScope scope) +{ + auto v8scope = reinterpret_cast(scope); + delete v8scope; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CompileScript(JSVM_Env env, + JSVM_Value script, + const uint8_t* cachedData, + size_t cachedDataLength, + bool eagerCompile, + bool* cacheRejected, + JSVM_Script* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); + + RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); + + v8::Local context = env->context(); + + v8::ScriptCompiler::CachedData* cache = + cachedData ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr; + v8::ScriptCompiler::Source scriptSource(v8Script.As(), cache); + auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache + : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions); + + auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, option); + + if (cache && cacheRejected) { + *cacheRejected = cache->rejected; + } + + CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); + v8::Local compiledScript = maybeScript.ToLocalChecked(); + *result = reinterpret_cast(env->NewJsvmData(compiledScript)); + + return GET_RETURN_STATUS(env); +} + +v8::ScriptOrigin CreateScriptOrigin(v8::Isolate* isolate, v8::Local resourceName, v8::ScriptType type) +{ + const int kOptionsLength = 2; + const uint32_t kOptionsMagicConstant = 0xF1F2F3F0; + auto options = v8::PrimitiveArray::New(isolate, kOptionsLength); + options->Set(isolate, 0, v8::Uint32::New(isolate, kOptionsMagicConstant)); + options->Set(isolate, 1, resourceName); + return v8::ScriptOrigin(isolate, resourceName, 0, 0, false, -1, v8::Local(), false, false, + type == v8::ScriptType::kModule, options); +} + +v8::MaybeLocal PrepareStackTraceCallback(v8::Local context, + v8::Local error, + v8::Local trace) +{ + auto* isolate = context->GetIsolate(); + v8::TryCatch tryCatch(isolate); + v8::Local moduleName = v8::String::NewFromUtf8(isolate, "sourcemap").ToLocalChecked(); + v8::Local moduleSourceString = + v8::String::NewFromUtf8(isolate, SourceMapRunner.c_str()).ToLocalChecked(); + + v8::ScriptOrigin moduleOrigin = CreateScriptOrigin(isolate, moduleName, v8::ScriptType::kClassic); + v8::Local moduleContext = v8::Context::New(isolate); + v8::ScriptCompiler::Source moduleSource(moduleSourceString, moduleOrigin); + auto script = v8::Script::Compile(moduleContext, moduleSourceString, &moduleOrigin).ToLocalChecked(); + auto result = script->Run(moduleContext).ToLocalChecked(); + auto resultFunc = v8::Local::Cast(result); + + v8::Local element = trace->Get(context, 0).ToLocalChecked(); + std::string fileName = ""; + if (element->IsObject()) { + auto obj = element->ToObject(context); + auto getFileName = v8::String::NewFromUtf8(isolate, "getFileName", v8::NewStringType::kNormal); + auto function = obj.ToLocalChecked()->Get(context, getFileName.ToLocalChecked()).ToLocalChecked(); + auto lineNumberFunction = v8::Local::Cast(function); + auto fileNameObj = lineNumberFunction->Call(context, obj.ToLocalChecked(), 0, {}); + fileName = std::string(*v8::String::Utf8Value(isolate, fileNameObj.ToLocalChecked())); + } + auto&& sourceMapUrl = (!fileName.empty()) ? v8impl::GetSourceMapFromFileName(std::move(fileName)) : ""; + std::ifstream sourceMapfile(sourceMapUrl); + std::string content = ""; + if (sourceMapfile.good()) { + std::stringstream buffer; + buffer << sourceMapfile.rdbuf(); + content = buffer.str(); + } + auto sourceMapObject = + v8::String::NewFromUtf8(isolate, content.c_str(), v8::NewStringType::kNormal, content.length()); + v8::Local args[] = { error, trace, sourceMapObject.ToLocalChecked() }; + return resultFunc->Call(moduleContext, v8::Undefined(isolate), jsvm::ArraySize(args), args); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CompileScriptWithOrigin(JSVM_Env env, + JSVM_Value script, + const uint8_t* cachedData, + size_t cachedDataLength, + bool eagerCompile, + bool* cacheRejected, + JSVM_ScriptOrigin* origin, + JSVM_Script* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + CHECK_NOT_NULL(origin->resourceName); + + v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); + + RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); + + v8::Local context = env->context(); + auto* isolate = context->GetIsolate(); + + if (origin->sourceMapUrl) { + v8impl::SetFileToSourceMapMapping(origin->resourceName, origin->sourceMapUrl); + isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback); + } + auto sourceMapUrl = !origin->sourceMapUrl + ? v8::Local() + : v8::String::NewFromUtf8(isolate, origin->sourceMapUrl).ToLocalChecked().As(); + auto resourceName = v8::String::NewFromUtf8(isolate, origin->resourceName).ToLocalChecked(); + v8::ScriptOrigin scriptOrigin(isolate, resourceName, origin->resourceLineOffset, origin->resourceColumnOffset, + false, -1, sourceMapUrl); + + v8::ScriptCompiler::CachedData* cache = + cachedData ? new v8::ScriptCompiler::CachedData(cachedData, cachedDataLength) : nullptr; + v8::ScriptCompiler::Source scriptSource(v8Script.As(), scriptOrigin, cache); + auto option = cache ? v8::ScriptCompiler::kConsumeCodeCache + : (eagerCompile ? v8::ScriptCompiler::kEagerCompile : v8::ScriptCompiler::kNoCompileOptions); + + auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, option); + + if (cache && cacheRejected) { + *cacheRejected = cache->rejected; + } + CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); + v8::Local compiledScript = maybeScript.ToLocalChecked(); + *result = reinterpret_cast(env->NewJsvmData(compiledScript)); + + return GET_RETURN_STATUS(env); +} + +class CompileOptionResolver { +public: + CompileOptionResolver(size_t length, JSVM_CompileOptions options[], v8::Isolate* isolate) + { + for (size_t i = 0; i < length; i++) { + switch (options[i].id) { + case JSVM_COMPILE_MODE: { + v8Option = static_cast(options[i].content.num); + break; + } + case JSVM_COMPILE_CODE_CACHE: { + auto cache = static_cast(options[i].content.ptr); + cachedData = + cache->cache ? new v8::ScriptCompiler::CachedData(cache->cache, cache->length) : nullptr; + break; + } + case JSVM_COMPILE_SCRIPT_ORIGIN: { + jsvmOrigin = static_cast(options[i].content.ptr); + break; + } + case JSVM_COMPILE_COMPILE_PROFILE: { + profile = static_cast(options[i].content.ptr); + break; + } + case JSVM_COMPILE_ENABLE_SOURCE_MAP: { + enableSourceMap = options[i].content.boolean; + break; + } + default: { + continue; + } + } + } + auto sourceString = jsvmOrigin ? jsvmOrigin->resourceName : "script_" + std::to_string(compileCount++); + auto sourceMapPtr = jsvmOrigin && jsvmOrigin->sourceMapUrl ? jsvmOrigin->sourceMapUrl : nullptr; + auto sourceMapUrl = + (jsvmOrigin && jsvmOrigin->sourceMapUrl) + ? v8::String::NewFromUtf8(isolate, jsvmOrigin->sourceMapUrl).ToLocalChecked().As() + : v8::Local(); + auto resourceName = v8::String::NewFromUtf8(isolate, sourceString.c_str()).ToLocalChecked(); + v8Origin = new v8::ScriptOrigin(isolate, resourceName, jsvmOrigin ? jsvmOrigin->resourceLineOffset : 0, + jsvmOrigin ? jsvmOrigin->resourceColumnOffset : 0, false, -1, sourceMapUrl); + if (enableSourceMap && sourceMapPtr) { + v8impl::SetFileToSourceMapMapping(jsvmOrigin->resourceName, sourceMapPtr); + isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback); + } + if (v8Option == v8::ScriptCompiler::kConsumeCodeCache && !cachedData) { + hasInvalidOption = true; + } + } + + ~CompileOptionResolver() + { + delete v8Origin; + v8Origin = nullptr; + } + + v8::ScriptCompiler::CompileOptions v8Option = v8::ScriptCompiler::kNoCompileOptions; + v8::ScriptCompiler::CachedData* cachedData = nullptr; + v8::ScriptOrigin* v8Origin = nullptr; + JSVM_CompileProfile* profile = nullptr; + JSVM_ScriptOrigin* jsvmOrigin = nullptr; + bool enableSourceMap = false; + static size_t compileCount; + bool hasInvalidOption = false; +}; + +size_t CompileOptionResolver::compileCount = 0; + +JSVM_Status JSVM_CDECL OH_JSVM_CompileScriptWithOptions(JSVM_Env env, + JSVM_Value script, + size_t optionCount, + JSVM_CompileOptions options[], + JSVM_Script* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + auto* isolate = context->GetIsolate(); + CompileOptionResolver optionResolver(optionCount, options, isolate); + RETURN_STATUS_IF_FALSE(env, !optionResolver.hasInvalidOption, JSVM_INVALID_ARG); + + v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); + + RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); + + v8::ScriptCompiler::Source scriptSource(v8Script.As(), *optionResolver.v8Origin, + optionResolver.cachedData); + auto maybeScript = v8::ScriptCompiler::Compile(context, &scriptSource, optionResolver.v8Option); + CHECK_MAYBE_EMPTY(env, maybeScript, JSVM_GENERIC_FAILURE); + v8::Local compiledScript = maybeScript.ToLocalChecked(); + *result = reinterpret_cast(env->NewJsvmData(compiledScript)); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateCodeCache(JSVM_Env env, JSVM_Script script, const uint8_t** data, size_t* length) +{ + CHECK_ENV(env); + CHECK_ARG(env, script); + CHECK_ARG(env, data); + CHECK_ARG(env, length); + + auto jsvmData = reinterpret_cast(script); + auto v8script = jsvmData->ToV8Local(env->isolate); + v8::ScriptCompiler::CachedData* cache; + cache = v8::ScriptCompiler::CreateCodeCache(v8script->GetUnboundScript()); + + if (cache == nullptr) { + return SetLastError(env, JSVM_GENERIC_FAILURE); + } + + *data = cache->data; + *length = cache->length; + cache->buffer_policy = v8::ScriptCompiler::CachedData::BufferNotOwned; + delete cache; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_RunScript(JSVM_Env env, JSVM_Script script, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + auto jsvmData = reinterpret_cast(script); + auto v8script = jsvmData->ToV8Local(env->isolate); + auto scriptResult = v8script->Run(env->context()); + CHECK_MAYBE_EMPTY(env, scriptResult, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(scriptResult.ToLocalChecked()); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_JsonParse(JSVM_Env env, JSVM_Value json_string, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, json_string); + + v8::Local val = v8impl::V8LocalValueFromJsValue(json_string); + RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); + + auto maybe = v8::JSON::Parse(env->context(), val.As()); + CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_JsonStringify(JSVM_Env env, JSVM_Value json_object, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, json_object); + + v8::Local val = v8impl::V8LocalValueFromJsValue(json_object); + + auto maybe = v8::JSON::Stringify(env->context(), val); + CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateSnapshot(JSVM_VM vm, + size_t contextCount, + const JSVM_Env* contexts, + const char** blobData, + size_t* blobSize) +{ + auto isolate = reinterpret_cast(vm); + auto creator = v8impl::GetIsolateSnapshotCreator(isolate); + + if (creator == nullptr) { + // TODO: return specific error message. + return JSVM_GENERIC_FAILURE; + } + { + v8::HandleScope scope(isolate); + v8::Local defaultContext = v8::Context::New(isolate); + creator->SetDefaultContext(defaultContext); + // NOTE: The order of the added data must be consistent with the order of + // getting data in v8impl::CreateIsolateData. + creator->AddData(JSVM_PRIVATE_KEY(isolate, wrapper)); + creator->AddData(JSVM_PRIVATE_KEY(isolate, typeTag)); + + for (size_t i = 0; i < contextCount; i++) { + auto ctx = contexts[i]->context(); + creator->AddData(ctx, ctx); + creator->AddContext(ctx); + } + } + auto blob = creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep); + *blobData = blob.data; + *blobSize = blob.raw_size; + + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_GetVMInfo(JSVM_VMInfo* result) +{ + result->apiVersion = 1; + result->engine = "v8"; + result->version = V8_VERSION_STRING; + result->cachedDataVersionTag = v8::ScriptCompiler::CachedDataVersionTag(); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_MemoryPressureNotification(JSVM_Env env, JSVM_MemoryPressureLevel level) +{ + CHECK_ENV(env); + env->isolate->MemoryPressureNotification(v8::MemoryPressureLevel(level)); + return ClearLastError(env); +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_GetHeapStatistics(JSVM_VM vm, JSVM_HeapStatistics* result) +{ + auto isolate = reinterpret_cast(vm); + v8::HeapStatistics stats; + isolate->GetHeapStatistics(&stats); + result->totalHeapSize = stats.total_heap_size(); + result->totalHeapSizeExecutable = stats.total_heap_size_executable(); + result->totalPhysicalSize = stats.total_physical_size(); + result->totalAvailableSize = stats.total_available_size(); + result->usedHeapSize = stats.used_heap_size(); + result->heapSizeLimit = stats.heap_size_limit(); + result->mallocedMemory = stats.malloced_memory(); + result->externalMemory = stats.external_memory(); + result->peakMallocedMemory = stats.peak_malloced_memory(); + result->numberOfNativeContexts = stats.number_of_native_contexts(); + result->numberOfDetachedContexts = stats.number_of_detached_contexts(); + result->totalGlobalHandlesSize = stats.total_global_handles_size(); + result->usedGlobalHandlesSize = stats.used_global_handles_size(); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_StartCpuProfiler(JSVM_VM vm, JSVM_CpuProfiler* result) +{ + auto isolate = reinterpret_cast(vm); + auto profiler = v8::CpuProfiler::New(isolate); + v8::HandleScope scope(isolate); + v8::CpuProfilingOptions options; + profiler->Start(v8::String::Empty(isolate), std::move(options)); + *result = reinterpret_cast(profiler); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_StopCpuProfiler(JSVM_VM vm, + JSVM_CpuProfiler profiler, + JSVM_OutputStream stream, + void* streamData) +{ + CHECK_ARG_WITHOUT_ENV(stream); + auto isolate = reinterpret_cast(vm); + auto v8profiler = reinterpret_cast(profiler); + v8::HandleScope scope(isolate); + auto profile = v8profiler->StopProfiling(v8::String::Empty(isolate)); + v8impl::OutputStream os(stream, streamData); + profile->Serialize(&os); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_TakeHeapSnapshot(JSVM_VM vm, JSVM_OutputStream stream, void* streamData) +{ + CHECK_ARG_WITHOUT_ENV(stream); + auto isolate = reinterpret_cast(vm); + auto profiler = isolate->GetHeapProfiler(); + auto snapshot = profiler->TakeHeapSnapshot(); + v8impl::OutputStream os(stream, streamData); + snapshot->Serialize(&os); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_OpenInspector(JSVM_Env env, const char* host, uint16_t port) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, host); + + std::string inspectorPath; + std::string hostName(host); + auto agent = env->GetInspectorAgent(); + + if (!agent->Start(inspectorPath, hostName, port)) { + LOG(Error) << "Open Inspector failed: Please check the internet permisson."; + return SetLastError(env, JSVM_GENERIC_FAILURE); + } + return GET_RETURN_STATUS(env); +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_CloseInspector(JSVM_Env env) +{ + JSVM_PREAMBLE(env); + auto agent = env->GetInspectorAgent(); + if (!agent->IsActive()) { + return JSVM_GENERIC_FAILURE; + } + agent->Stop(); + return GET_RETURN_STATUS(env); +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_WaitForDebugger(JSVM_Env env, bool breakNextLine) +{ + JSVM_PREAMBLE(env); + auto* agent = env->GetInspectorAgent(); + if (!agent->IsActive()) { + return JSVM_GENERIC_FAILURE; + } + + agent->WaitForConnect(); + if (breakNextLine) { + agent->PauseOnNextJavascriptStatement("Break on debugger attached"); + } + + return GET_RETURN_STATUS(env); +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_PumpMessageLoop(JSVM_VM vm, bool* result) +{ + auto isolate = reinterpret_cast(vm); + *result = v8::platform::PumpMessageLoop(v8impl::g_platform.get(), isolate); + return JSVM_OK; +} + +JSVM_EXTERN JSVM_Status JSVM_CDECL OH_JSVM_PerformMicrotaskCheckpoint(JSVM_VM vm) +{ + auto isolate = reinterpret_cast(vm); + isolate->PerformMicrotaskCheckpoint(); + return JSVM_OK; +} + +// Warning: Keep in-sync with JSVM_Status enum +static const char* errorMessages[] = { + nullptr, + "Invalid argument", + "An object was expected", + "A string was expected", + "A string or symbol was expected", + "A function was expected", + "A number was expected", + "A boolean was expected", + "An array was expected", + "Unknown failure", + "An exception is pending", + "The async work item was cancelled", + "OH_JSVM_EscapeHandle already called on scope", + "Invalid handle scope usage", + "Invalid callback scope usage", + "Thread-safe function queue is full", + "Thread-safe function handle is closing", + "A bigint was expected", + "A date was expected", + "An arraybuffer was expected", + "A detachable arraybuffer was expected", + "Main thread would deadlock", + "External buffers are not allowed", + "Cannot run JavaScript", +}; + +JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo(JSVM_Env env, const JSVM_ExtendedErrorInfo** result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + // The value of the constant below must be updated to reference the last + // message in the `JSVM_Status` enum each time a new error message is added. + // We don't have a jsvm_status_last as this would result in an ABI + // change each time a message was added. + const int lastStatus = JSVM_CANNOT_RUN_JS; + + static_assert(jsvm::ArraySize(errorMessages) == lastStatus + 1, + "Count of error messages must match count of error values"); + CHECK_LE(env->lastError.errorCode, lastStatus); + // Wait until someone requests the last error information to fetch the error + // message string + env->lastError.errorMessage = errorMessages[env->lastError.errorCode]; + + if (env->lastError.errorCode == JSVM_OK) { + ClearLastError(env); + } + *result = &(env->lastError); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL +OH_JSVM_CreateFunction(JSVM_Env env, const char* utf8name, size_t length, JSVM_Callback cb, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, cb); + + v8::Local returnValue; + v8::EscapableHandleScope scope(env->isolate); + v8::Local fn; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, cb, &fn)); + returnValue = scope.Escape(fn); + + if (utf8name != nullptr) { + v8::Local nameString; + CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); + returnValue->SetName(nameString); + } + + *result = v8impl::JsValueFromV8LocalValue(returnValue); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateFunctionWithScript(JSVM_Env env, + const char* funcName, + size_t length, + size_t argc, + const JSVM_Value* argv, + JSVM_Value script, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + if (argc > 0) { + CHECK_ARG(env, argv); + for (auto i = 0; i < argc; i++) { + RETURN_STATUS_IF_FALSE(env, v8impl::V8LocalValueFromJsValue(argv[i])->IsString(), JSVM_STRING_EXPECTED); + } + } + + v8::Local v8Script = v8impl::V8LocalValueFromJsValue(script); + + RETURN_STATUS_IF_FALSE(env, v8Script->IsString(), JSVM_STRING_EXPECTED); + + v8::ScriptCompiler::Source scriptSource(v8Script.As()); + + v8::Local context = env->context(); + + v8::MaybeLocal maybeFunc = + v8::ScriptCompiler::CompileFunction(context, &scriptSource, argc, + reinterpret_cast*>(const_cast(argv))); + CHECK_MAYBE_EMPTY(env, maybeFunc, JSVM_GENERIC_FAILURE); + + v8::Local func = maybeFunc.ToLocalChecked(); + + if (funcName != nullptr) { + v8::Local funcNameString; + CHECK_NEW_FROM_UTF8_LEN(env, funcNameString, funcName, length); + func->SetName(funcNameString); + } + + *result = v8impl::JsValueFromV8LocalValue(func); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DefineClass(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, constructor); + + if (propertyCount > 0) { + CHECK_ARG(env, properties); + } + + v8::Isolate* isolate = env->isolate; + + v8::EscapableHandleScope scope(isolate); + v8::Local tpl; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, constructor, &tpl)); + + v8::Local nameString; + CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); + tpl->SetClassName(nameString); + + size_t staticPropertyCount = 0; + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + + if ((p->attributes & JSVM_STATIC) != 0) { + // Static properties are handled separately below. + staticPropertyCount++; + continue; + } + + v8::Local propertyName; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); + + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in OH_JSVM_DefineProperties(); the + // difference is it applies to a template instead of an object, + // and preferred PropertyAttribute for lack of PropertyDescriptor + // support on ObjectTemplate. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local getterTpl; + v8::Local setterTpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); + } + + tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes, + v8::AccessControl::DEFAULT); + } else if (p->method != nullptr) { + v8::Local t; + if (p->attributes & JSVM_NO_RECEIVER_CHECK) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t)); + } else { + STATUS_CALL( + v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t, v8::Signature::New(isolate, tpl))); + } + + tpl->PrototypeTemplate()->Set(propertyName, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(propertyName, value, attributes); + } + } + + v8::Local context = env->context(); + *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction(context).ToLocalChecked())); + + if (staticPropertyCount > 0) { + std::vector staticDescriptors; + staticDescriptors.reserve(staticPropertyCount); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + if ((p->attributes & JSVM_STATIC) != 0) { + staticDescriptors.push_back(*p); + } + } + + STATUS_CALL(OH_JSVM_DefineProperties(env, *result, staticDescriptors.size(), staticDescriptors.data())); + } + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetPropertyNames(JSVM_Env env, JSVM_Value object, JSVM_Value* result) +{ + return OH_JSVM_GetAllPropertyNames(env, object, JSVM_KEY_INCLUDE_PROTOTYPES, + static_cast(JSVM_KEY_ENUMERABLE | JSVM_KEY_SKIP_SYMBOLS), + JSVM_KEY_NUMBERS_TO_STRINGS, result); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetAllPropertyNames(JSVM_Env env, + JSVM_Value object, + JSVM_KeyCollectionMode keyMode, + JSVM_KeyFilter keyFilter, + JSVM_KeyConversion keyConversion, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES; + if (keyFilter & JSVM_KEY_WRITABLE) { + filter = static_cast(filter | v8::PropertyFilter::ONLY_WRITABLE); + } + if (keyFilter & JSVM_KEY_ENUMERABLE) { + filter = static_cast(filter | v8::PropertyFilter::ONLY_ENUMERABLE); + } + if (keyFilter & JSVM_KEY_CONFIGURABLE) { + filter = static_cast(filter | v8::PropertyFilter::ONLY_CONFIGURABLE); + } + if (keyFilter & JSVM_KEY_SKIP_STRINGS) { + filter = static_cast(filter | v8::PropertyFilter::SKIP_STRINGS); + } + if (keyFilter & JSVM_KEY_SKIP_SYMBOLS) { + filter = static_cast(filter | v8::PropertyFilter::SKIP_SYMBOLS); + } + v8::KeyCollectionMode collectionMode; + v8::KeyConversionMode conversionMode; + + switch (keyMode) { + case JSVM_KEY_INCLUDE_PROTOTYPES: + collectionMode = v8::KeyCollectionMode::kIncludePrototypes; + break; + case JSVM_KEY_OWN_ONLY: + collectionMode = v8::KeyCollectionMode::kOwnOnly; + break; + default: + return SetLastError(env, JSVM_INVALID_ARG); + } + + switch (keyConversion) { + case JSVM_KEY_KEEP_NUMBERS: + conversionMode = v8::KeyConversionMode::kKeepNumbers; + break; + case JSVM_KEY_NUMBERS_TO_STRINGS: + conversionMode = v8::KeyConversionMode::kConvertToString; + break; + default: + return SetLastError(env, JSVM_INVALID_ARG); + } + + v8::MaybeLocal maybeAllPropertynames = + obj->GetPropertyNames(context, collectionMode, filter, v8::IndexFilter::kIncludeIndices, conversionMode); + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeAllPropertynames, JSVM_GENERIC_FAILURE); + + *result = v8impl::JsValueFromV8LocalValue(maybeAllPropertynames.ToLocalChecked()); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, JSVM_Value value) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, key); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe setMaybe = obj->Set(context, k, val); + RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_HasProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, key); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Maybe hasMaybe = obj->Has(context, k); + CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); + + *result = hasMaybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto getMaybe = obj->Get(context, k); + CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); + + v8::Local val = getMaybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DeleteProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, key); + + v8::Local context = env->context(); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Maybe deleteMaybe = obj->Delete(context, k); + CHECK_MAYBE_NOTHING(env, deleteMaybe, JSVM_GENERIC_FAILURE); + + if (result != nullptr) + *result = deleteMaybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_HasOwnProperty(JSVM_Env env, JSVM_Value object, JSVM_Value key, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Local k = v8impl::V8LocalValueFromJsValue(key); + RETURN_STATUS_IF_FALSE(env, k->IsName(), JSVM_NAME_EXPECTED); + v8::Maybe hasMaybe = obj->HasOwnProperty(context, k.As()); + CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); + *result = hasMaybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetNamedProperty(JSVM_Env env, JSVM_Value object, const char* utf8name, JSVM_Value value) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe setMaybe = obj->Set(context, key, val); + RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_HasNamedProperty(JSVM_Env env, JSVM_Value object, const char* utf8name, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Maybe hasMaybe = obj->Has(context, key); + CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); + + *result = hasMaybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetNamedProperty(JSVM_Env env, + JSVM_Value object, + const char* utf8name, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local key; + CHECK_NEW_FROM_UTF8(env, key, utf8name); + + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto getMaybe = obj->Get(context, key); + CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); + + v8::Local val = getMaybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetElement(JSVM_Env env, JSVM_Value object, uint32_t index, JSVM_Value value) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + auto setMaybe = obj->Set(context, index, val); + RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_HasElement(JSVM_Env env, JSVM_Value object, uint32_t index, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Maybe hasMaybe = obj->Has(context, index); + CHECK_MAYBE_NOTHING(env, hasMaybe, JSVM_GENERIC_FAILURE); + + *result = hasMaybe.FromMaybe(false); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetElement(JSVM_Env env, JSVM_Value object, uint32_t index, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + auto getMaybe = obj->Get(context, index); + CHECK_MAYBE_EMPTY(env, getMaybe, JSVM_GENERIC_FAILURE); + + *result = v8impl::JsValueFromV8LocalValue(getMaybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DeleteElement(JSVM_Env env, JSVM_Value object, uint32_t index, bool* result) +{ + JSVM_PREAMBLE(env); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Maybe deleteMaybe = obj->Delete(context, index); + CHECK_MAYBE_NOTHING(env, deleteMaybe, JSVM_GENERIC_FAILURE); + + if (result != nullptr) + *result = deleteMaybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DefineProperties(JSVM_Env env, + JSVM_Value object, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties) +{ + JSVM_PREAMBLE(env); + if (propertyCount > 0) { + CHECK_ARG(env, properties); + } + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = &properties[i]; + + v8::Local propertyName; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); + + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local localGetter; + v8::Local localSetter; + + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->getter, &localGetter)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->setter, &localSetter)); + } + + v8::PropertyDescriptor descriptor(localGetter, localSetter); + descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); + descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); + + auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); + + if (!defineMaybe.FromMaybe(false)) { + return SetLastError(env, JSVM_INVALID_ARG); + } + } else if (p->method != nullptr) { + v8::Local method; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewFunction(env, p->method, &method)); + v8::PropertyDescriptor descriptor(method, (p->attributes & JSVM_WRITABLE) != 0); + descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); + descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); + + auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); + + if (!defineMaybe.FromMaybe(false)) { + return SetLastError(env, JSVM_GENERIC_FAILURE); + } + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + bool definedSuccessfully = false; + + if ((p->attributes & JSVM_ENUMERABLE) && (p->attributes & JSVM_WRITABLE) && + (p->attributes & JSVM_CONFIGURABLE)) { + // Use a fast path for this type of data property. + auto defineMaybe = obj->CreateDataProperty(context, propertyName, value); + definedSuccessfully = defineMaybe.FromMaybe(false); + } else { + v8::PropertyDescriptor descriptor(value, (p->attributes & JSVM_WRITABLE) != 0); + descriptor.set_enumerable((p->attributes & JSVM_ENUMERABLE) != 0); + descriptor.set_configurable((p->attributes & JSVM_CONFIGURABLE) != 0); + + auto defineMaybe = obj->DefineProperty(context, propertyName, descriptor); + definedSuccessfully = defineMaybe.FromMaybe(false); + } + + if (!definedSuccessfully) { + return SetLastError(env, JSVM_INVALID_ARG); + } + } + } + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ObjectFreeze(JSVM_Env env, JSVM_Value object) +{ + JSVM_PREAMBLE(env); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Maybe setFrozen = obj->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen); + + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, setFrozen.FromMaybe(false), JSVM_GENERIC_FAILURE); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) +{ + JSVM_PREAMBLE(env); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Maybe setSealed = obj->SetIntegrityLevel(context, v8::IntegrityLevel::kSealed); + + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, setSealed.FromMaybe(false), JSVM_GENERIC_FAILURE); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env, JSVM_Value value, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsArray(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsRegExp(JSVM_Env env, JSVM_Value value, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsRegExp(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetArrayLength(JSVM_Env env, JSVM_Value value, uint32_t* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsArray(), JSVM_ARRAY_EXPECTED); + + v8::Local arr = val.As(); + *result = arr->Length(); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_StrictEquals(JSVM_Env env, JSVM_Value lhs, JSVM_Value rhs, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, lhs); + CHECK_ARG(env, rhs); + CHECK_ARG(env, result); + + v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); + v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); + + *result = a->StrictEquals(b); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Equals(JSVM_Env env, JSVM_Value lhs, JSVM_Value rhs, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, lhs); + CHECK_ARG(env, rhs); + CHECK_ARG(env, result); + + v8::Local a = v8impl::V8LocalValueFromJsValue(lhs); + v8::Local b = v8impl::V8LocalValueFromJsValue(rhs); + v8::Local context = env->context(); + + *result = a->Equals(context, b).FromJust(); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetPrototype(JSVM_Env env, JSVM_Value object, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = obj->GetPrototype(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateObject(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Object::New(env->isolate)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateArray(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayWithLength(JSVM_Env env, size_t length, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate, length)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateStringLatin1(JSVM_Env env, const char* str, size_t length, JSVM_Value* result) +{ + return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { + return v8::String::NewFromOneByte(isolate, reinterpret_cast(str), v8::NewStringType::kNormal, + length); + }); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateStringUtf8(JSVM_Env env, const char* str, size_t length, JSVM_Value* result) +{ + return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { + return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kNormal, static_cast(length)); + }); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateStringUtf16(JSVM_Env env, const char16_t* str, size_t length, JSVM_Value* result) +{ + return v8impl::NewString(env, str, length, result, [&](v8::Isolate* isolate) { + return v8::String::NewFromTwoByte(isolate, reinterpret_cast(str), v8::NewStringType::kNormal, + length); + }); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateDouble(JSVM_Env env, double value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, value)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateInt32(JSVM_Env env, int32_t value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Integer::New(env->isolate, value)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateUint32(JSVM_Env env, uint32_t value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Integer::NewFromUnsigned(env->isolate, value)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateInt64(JSVM_Env env, int64_t value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Number::New(env->isolate, static_cast(value))); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateBigintInt64(JSVM_Env env, int64_t value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::New(env->isolate, value)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateBigintUint64(JSVM_Env env, uint64_t value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::BigInt::NewFromUnsigned(env->isolate, value)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL +OH_JSVM_CreateBigintWords(JSVM_Env env, int signBit, size_t wordCount, const uint64_t* words, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, words); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + RETURN_STATUS_IF_FALSE(env, wordCount <= INT_MAX, JSVM_INVALID_ARG); + + v8::MaybeLocal b = v8::BigInt::NewFromWords(context, signBit, wordCount, words); + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, b, JSVM_GENERIC_FAILURE); + + *result = v8impl::JsValueFromV8LocalValue(b.ToLocalChecked()); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetBoolean(JSVM_Env env, bool value, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (value) { + *result = v8impl::JsValueFromV8LocalValue(v8::True(isolate)); + } else { + *result = v8impl::JsValueFromV8LocalValue(v8::False(isolate)); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateSymbol(JSVM_Env env, JSVM_Value description, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (description == nullptr) { + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate)); + } else { + v8::Local desc = v8impl::V8LocalValueFromJsValue(description); + RETURN_STATUS_IF_FALSE(env, desc->IsString(), JSVM_STRING_EXPECTED); + + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::New(isolate, desc.As())); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SymbolFor(JSVM_Env env, const char* utf8description, size_t length, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + JSVM_Value jsDescriptionString; + STATUS_CALL(OH_JSVM_CreateStringUtf8(env, utf8description, length, &jsDescriptionString)); + v8::Local descriptionString = v8impl::V8LocalValueFromJsValue(jsDescriptionString).As(); + + *result = v8impl::JsValueFromV8LocalValue(v8::Symbol::For(env->isolate, descriptionString)); + + return ClearLastError(env); +} + +static inline JSVM_Status SetErrorCode(JSVM_Env env, + v8::Local error, + JSVM_Value code, + const char* codeCstring) +{ + if ((code != nullptr) || (codeCstring != nullptr)) { + v8::Local context = env->context(); + v8::Local errObject = error.As(); + + v8::Local codeValue = v8impl::V8LocalValueFromJsValue(code); + if (code != nullptr) { + codeValue = v8impl::V8LocalValueFromJsValue(code); + RETURN_STATUS_IF_FALSE(env, codeValue->IsString(), JSVM_STRING_EXPECTED); + } else { + CHECK_NEW_FROM_UTF8(env, codeValue, codeCstring); + } + + v8::Local codeKey; + CHECK_NEW_FROM_UTF8(env, codeKey, "code"); + + v8::Maybe setMaybe = errObject->Set(context, codeKey, codeValue); + RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + } + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); + + v8::Local errorObj = v8::Exception::Error(messageValue.As()); + STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); + + *result = v8impl::JsValueFromV8LocalValue(errorObj); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateTypeError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); + + v8::Local errorObj = v8::Exception::TypeError(messageValue.As()); + STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); + + *result = v8impl::JsValueFromV8LocalValue(errorObj); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateRangeError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); + + v8::Local errorObj = v8::Exception::RangeError(messageValue.As()); + STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); + + *result = v8impl::JsValueFromV8LocalValue(errorObj); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateSyntaxError(JSVM_Env env, JSVM_Value code, JSVM_Value msg, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, msg); + CHECK_ARG(env, result); + + v8::Local messageValue = v8impl::V8LocalValueFromJsValue(msg); + RETURN_STATUS_IF_FALSE(env, messageValue->IsString(), JSVM_STRING_EXPECTED); + + v8::Local errorObj = v8::Exception::SyntaxError(messageValue.As()); + STATUS_CALL(SetErrorCode(env, errorObj, code, nullptr)); + + *result = v8impl::JsValueFromV8LocalValue(errorObj); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Typeof(JSVM_Env env, JSVM_Value value, JSVM_ValueType* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + + if (v->IsNumber()) { + *result = JSVM_NUMBER; + } else if (v->IsBigInt()) { + *result = JSVM_BIGINT; + } else if (v->IsString()) { + *result = JSVM_STRING; + } else if (v->IsFunction()) { + // This test has to come before IsObject because IsFunction + // implies IsObject + *result = JSVM_FUNCTION; + } else if (v->IsExternal()) { + // This test has to come before IsObject because IsExternal + // implies IsObject + *result = JSVM_EXTERNAL; + } else if (v->IsObject()) { + *result = JSVM_OBJECT; + } else if (v->IsBoolean()) { + *result = JSVM_BOOLEAN; + } else if (v->IsUndefined()) { + *result = JSVM_UNDEFINED; + } else if (v->IsSymbol()) { + *result = JSVM_SYMBOL; + } else if (v->IsNull()) { + *result = JSVM_NULL; + } else { + // Should not get here unless V8 has added some new kind of value. + return SetLastError(env, JSVM_INVALID_ARG); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetUndefined(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Undefined(env->isolate)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetNull(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Null(env->isolate)); + + return ClearLastError(env); +} + +// Gets all callback info in a single call. (Ugly, but faster.) +JSVM_Status JSVM_CDECL OH_JSVM_GetCbInfo(JSVM_Env env, // [in] JSVM environment handle + JSVM_CallbackInfo cbinfo, // [in] Opaque callback-info handle + size_t* argc, // [in-out] Specifies the size of the provided argv array + // and receives the actual count of args. + JSVM_Value* argv, // [out] Array of values + JSVM_Value* thisArg, // [out] Receives the JS 'this' arg for the call + void** data) +{ // [out] Receives the data pointer for the callback. + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); + + if (argv != nullptr) { + CHECK_ARG(env, argc); + info->GetArgs(argv, *argc); + } + if (argc != nullptr) { + *argc = info->ArgsLength(); + } + if (thisArg != nullptr) { + *thisArg = info->This(); + } + if (data != nullptr) { + *data = info->Data(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetNewTarget(JSVM_Env env, JSVM_CallbackInfo cbinfo, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, cbinfo); + CHECK_ARG(env, result); + + v8impl::CallbackWrapper* info = reinterpret_cast(cbinfo); + + *result = info->GetNewTarget(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CallFunction(JSVM_Env env, + JSVM_Value recv, + JSVM_Value func, + size_t argc, + const JSVM_Value* argv, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, recv); + if (argc > 0) { + CHECK_ARG(env, argv); + } + + v8::Local context = env->context(); + + v8::Local v8recv = v8impl::V8LocalValueFromJsValue(recv); + + v8::Local v8func; + CHECK_TO_FUNCTION(env, v8func, func); + + auto maybe = + v8func->Call(context, v8recv, argc, reinterpret_cast*>(const_cast(argv))); + + RETURN_IF_EXCEPTION_HAS_CAUGHT(env); + + if (result != nullptr) { + CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + } + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetGlobal(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(env->context()->Global()); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Throw(JSVM_Env env, JSVM_Value error) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, error); + + v8::Isolate* isolate = env->isolate; + + isolate->ThrowException(v8impl::V8LocalValueFromJsValue(error)); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ThrowError(JSVM_Env env, const char* code, const char* msg) +{ + JSVM_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local errorObj = v8::Exception::Error(str); + STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); + + isolate->ThrowException(errorObj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ThrowTypeError(JSVM_Env env, const char* code, const char* msg) +{ + JSVM_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local errorObj = v8::Exception::TypeError(str); + STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); + + isolate->ThrowException(errorObj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ThrowRangeError(JSVM_Env env, const char* code, const char* msg) +{ + JSVM_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local errorObj = v8::Exception::RangeError(str); + STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); + + isolate->ThrowException(errorObj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ThrowSyntaxError(JSVM_Env env, const char* code, const char* msg) +{ + JSVM_PREAMBLE(env); + + v8::Isolate* isolate = env->isolate; + v8::Local str; + CHECK_NEW_FROM_UTF8(env, str, msg); + + v8::Local errorObj = v8::Exception::SyntaxError(str); + STATUS_CALL(SetErrorCode(env, errorObj, nullptr, code)); + + isolate->ThrowException(errorObj); + // any VM calls after this point and before returning + // to the javascript invoker will fail + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsError(JSVM_Env env, JSVM_Value value, bool* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot + // throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsNativeError(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueDouble(JSVM_Env env, JSVM_Value value, double* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); + + *result = val.As()->Value(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueInt32(JSVM_Env env, JSVM_Value value, int32_t* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + if (val->IsInt32()) { + *result = val.As()->Value(); + } else { + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); + + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->Int32Value(context).FromJust(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueUint32(JSVM_Env env, JSVM_Value value, uint32_t* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + if (val->IsUint32()) { + *result = val.As()->Value(); + } else { + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); + + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->Uint32Value(context).FromJust(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueInt64(JSVM_Env env, JSVM_Value value, int64_t* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + // This is still a fast path very likely to be taken. + if (val->IsInt32()) { + *result = val.As()->Value(); + return ClearLastError(env); + } + + RETURN_STATUS_IF_FALSE(env, val->IsNumber(), JSVM_NUMBER_EXPECTED); + + // v8::Value::IntegerValue() converts NaN, +Inf, and -Inf to INT64_MIN, + // inconsistent with v8::Value::Int32Value() which converts those values to 0. + // Special-case all non-finite values to match that behavior. + double doubleValue = val.As()->Value(); + if (std::isfinite(doubleValue)) { + // Empty context: https://github.com/nodejs/node/issues/14379 + v8::Local context; + *result = val->IntegerValue(context).FromJust(); + } else { + *result = 0; + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueBigintInt64(JSVM_Env env, JSVM_Value value, int64_t* result, bool* lossless) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); + + *result = val.As()->Int64Value(lossless); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueBigintUint64(JSVM_Env env, JSVM_Value value, uint64_t* result, bool* lossless) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + CHECK_ARG(env, lossless); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); + + *result = val.As()->Uint64Value(lossless); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL +OH_JSVM_GetValueBigintWords(JSVM_Env env, JSVM_Value value, int* signBit, size_t* wordCount, uint64_t* words) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, wordCount); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsBigInt(), JSVM_BIGINT_EXPECTED); + + v8::Local big = val.As(); + + int wordCountInt = *wordCount; + + if (signBit == nullptr && words == nullptr) { + wordCountInt = big->WordCount(); + } else { + CHECK_ARG(env, signBit); + CHECK_ARG(env, words); + big->ToWordsArray(signBit, &wordCountInt, words); + } + + *wordCount = wordCountInt; + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueBool(JSVM_Env env, JSVM_Value value, bool* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsBoolean(), JSVM_BOOLEAN_EXPECTED); + + *result = val.As()->Value(); + + return ClearLastError(env); +} + +// Copies a JavaScript string into a LATIN-1 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +JSVM_Status JSVM_CDECL +OH_JSVM_GetValueStringLatin1(JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Length(); + } else if (bufsize != 0) { + int copied = val.As()->WriteOneByte(env->isolate, reinterpret_cast(buf), 0, bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } else if (result != nullptr) { + *result = 0; + } + + return ClearLastError(env); +} + +// Copies a JavaScript string into a UTF-8 string buffer. The result is the +// number of bytes (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in bytes) +// via the result parameter. +// The result argument is optional unless buf is NULL. +JSVM_Status JSVM_CDECL +OH_JSVM_GetValueStringUtf8(JSVM_Env env, JSVM_Value value, char* buf, size_t bufsize, size_t* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); + + if (!buf) { + CHECK_ARG(env, result); + *result = val.As()->Utf8Length(env->isolate); + } else if (bufsize != 0) { + int copied = + val.As()->WriteUtf8(env->isolate, buf, bufsize - 1, nullptr, + v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } else if (result != nullptr) { + *result = 0; + } + + return ClearLastError(env); +} + +// Copies a JavaScript string into a UTF-16 string buffer. The result is the +// number of 2-byte code units (excluding the null terminator) copied into buf. +// A sufficient buffer size should be greater than the length of string, +// reserving space for null terminator. +// If bufsize is insufficient, the string will be truncated and null terminated. +// If buf is NULL, this method returns the length of the string (in 2-byte +// code units) via the result parameter. +// The result argument is optional unless buf is NULL. +JSVM_Status JSVM_CDECL +OH_JSVM_GetValueStringUtf16(JSVM_Env env, JSVM_Value value, char16_t* buf, size_t bufsize, size_t* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsString(), JSVM_STRING_EXPECTED); + + if (!buf) { + CHECK_ARG(env, result); + // V8 assumes UTF-16 length is the same as the number of characters. + *result = val.As()->Length(); + } else if (bufsize != 0) { + int copied = val.As()->Write(env->isolate, reinterpret_cast(buf), 0, bufsize - 1, + v8::String::NO_NULL_TERMINATION); + + buf[copied] = '\0'; + if (result != nullptr) { + *result = copied; + } + } else if (result != nullptr) { + *result = 0; + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CoerceToBool(JSVM_Env env, JSVM_Value value, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local b = v8impl::V8LocalValueFromJsValue(value)->ToBoolean(isolate); + *result = v8impl::JsValueFromV8LocalValue(b); + return GET_RETURN_STATUS(env); +} + +#define GEN_COERCE_FUNCTION(UpperCaseName, MixedCaseName, LowerCaseName) \ + JSVM_Status JSVM_CDECL OH_JSVM_CoerceTo##MixedCaseName(JSVM_Env env, JSVM_Value value, JSVM_Value* result) \ + { \ + JSVM_PREAMBLE(env); \ + CHECK_ARG(env, value); \ + CHECK_ARG(env, result); \ + \ + v8::Local context = env->context(); \ + v8::Local str; \ + \ + CHECK_TO_##UpperCaseName(env, context, str, value); \ + \ + *result = v8impl::JsValueFromV8LocalValue(str); \ + return GET_RETURN_STATUS(env); \ + } + +GEN_COERCE_FUNCTION(NUMBER, Number, number) +GEN_COERCE_FUNCTION(OBJECT, Object, object) +GEN_COERCE_FUNCTION(STRING, String, string) +GEN_COERCE_FUNCTION(BIGINT, BigInt, bigint) + +#undef GEN_COERCE_FUNCTION + +JSVM_Status JSVM_CDECL OH_JSVM_Wrap(JSVM_Env env, + JSVM_Value jsObject, + void* nativeObject, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Ref* result) +{ + return v8impl::Wrap(env, jsObject, nativeObject, finalizeCb, finalizeHint, result); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Unwrap(JSVM_Env env, JSVM_Value obj, void** result) +{ + return v8impl::Unwrap(env, obj, result, v8impl::KeepWrap); +} + +JSVM_Status JSVM_CDECL OH_JSVM_RemoveWrap(JSVM_Env env, JSVM_Value obj, void** result) +{ + return v8impl::Unwrap(env, obj, result, v8impl::RemoveWrap); +} + +JSVM_Status JSVM_CDECL +OH_JSVM_CreateExternal(JSVM_Env env, void* data, JSVM_Finalize finalizeCb, void* finalizeHint, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + v8::Local externalValue = v8::External::New(isolate, data); + + v8impl::RuntimeReference::New(env, externalValue, finalizeCb, data, finalizeHint); + + *result = v8impl::JsValueFromV8LocalValue(externalValue); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_TypeTagObject(JSVM_Env env, JSVM_Value object, const JSVM_TypeTag* typeTag) +{ + JSVM_PREAMBLE(env); + v8::Local context = env->context(); + v8::Local obj; + CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object); + CHECK_ARG_WITH_PREAMBLE(env, typeTag); + + auto key = JSVM_PRIVATE_KEY(env->isolate, typeTag); + auto maybeHas = obj->HasPrivate(context, key); + CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybeHas, JSVM_GENERIC_FAILURE); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, !maybeHas.FromJust(), JSVM_INVALID_ARG); + + auto tag = v8::BigInt::NewFromWords(context, 0, 2, reinterpret_cast(typeTag)); + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, tag, JSVM_GENERIC_FAILURE); + + auto maybeSet = obj->SetPrivate(context, key, tag.ToLocalChecked()); + CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybeSet, JSVM_GENERIC_FAILURE); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, maybeSet.FromJust(), JSVM_GENERIC_FAILURE); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CheckObjectTypeTag(JSVM_Env env, + JSVM_Value object, + const JSVM_TypeTag* typeTag, + bool* result) +{ + JSVM_PREAMBLE(env); + v8::Local context = env->context(); + v8::Local obj; + CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, obj, object); + CHECK_ARG_WITH_PREAMBLE(env, typeTag); + CHECK_ARG_WITH_PREAMBLE(env, result); + + auto maybeValue = obj->GetPrivate(context, JSVM_PRIVATE_KEY(env->isolate, typeTag)); + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeValue, JSVM_GENERIC_FAILURE); + v8::Local val = maybeValue.ToLocalChecked(); + + // We consider the type check to have failed unless we reach the line below + // where we set whether the type check succeeded or not based on the + // comparison of the two type tags. + *result = false; + if (val->IsBigInt()) { + int sign; + int size = 2; + JSVM_TypeTag tag; + val.As()->ToWordsArray(&sign, &size, reinterpret_cast(&tag)); + if (sign == 0) { + if (size == 2) { + *result = (tag.lower == typeTag->lower && tag.upper == typeTag->upper); + } else if (size == 1) { + *result = (tag.lower == typeTag->lower && 0 == typeTag->upper); + } else if (size == 0) { + *result = (0 == typeTag->lower && 0 == typeTag->upper); + } + } + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetValueExternal(JSVM_Env env, JSVM_Value value, void** result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsExternal(), JSVM_INVALID_ARG); + + v8::Local externalValue = val.As(); + *result = externalValue->Value(); + + return ClearLastError(env); +} + +// Set initialRefcount to 0 for a weak reference, >0 for a strong reference. +JSVM_Status JSVM_CDECL OH_JSVM_CreateReference(JSVM_Env env, + JSVM_Value value, + uint32_t initialRefcount, + JSVM_Ref* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v8Value = v8impl::V8LocalValueFromJsValue(value); + v8impl::UserReference* reference = v8impl::UserReference::New(env, v8Value, initialRefcount); + + *result = reinterpret_cast(reference); + return ClearLastError(env); +} + +// Deletes a reference. The referenced value is released, and may be GC'd unless +// there are other references to it. +JSVM_Status JSVM_CDECL OH_JSVM_DeleteReference(JSVM_Env env, JSVM_Ref ref) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + delete reinterpret_cast(ref); + + return ClearLastError(env); +} + +// Increments the reference count, optionally returning the resulting count. +// After this call the reference will be a strong reference because its +// refcount is >0, and the referenced object is effectively "pinned". +// Calling this when the refcount is 0 and the object is unavailable +// results in an error. +JSVM_Status JSVM_CDECL OH_JSVM_ReferenceRef(JSVM_Env env, JSVM_Ref ref, uint32_t* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::UserReference* reference = reinterpret_cast(ref); + uint32_t count = reference->Ref(); + + if (result != nullptr) { + *result = count; + } + + return ClearLastError(env); +} + +// Decrements the reference count, optionally returning the resulting count. If +// the result is 0 the reference is now weak and the object may be GC'd at any +// time if there are no other references. Calling this when the refcount is +// already 0 results in an error. +JSVM_Status JSVM_CDECL OH_JSVM_ReferenceUnref(JSVM_Env env, JSVM_Ref ref, uint32_t* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + + v8impl::UserReference* reference = reinterpret_cast(ref); + + if (reference->RefCount() == 0) { + return SetLastError(env, JSVM_GENERIC_FAILURE); + } + + uint32_t count = reference->Unref(); + + if (result != nullptr) { + *result = count; + } + + return ClearLastError(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value might +// no longer be available, in that case the call is still successful but the +// result is NULL. +JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceValue(JSVM_Env env, JSVM_Ref ref, JSVM_Value* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + v8impl::UserReference* reference = reinterpret_cast(ref); + *result = v8impl::JsValueFromV8LocalValue(reference->Get()); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_OpenHandleScope(JSVM_Env env, JSVM_HandleScope* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsHandleScopeFromV8HandleScope(new v8impl::HandleScopeWrapper(env->isolate)); + env->openHandleScopes++; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CloseHandleScope(JSVM_Env env, JSVM_HandleScope scope) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + if (env->openHandleScopes == 0) { + return JSVM_HANDLE_SCOPE_MISMATCH; + } + + env->ReleaseJsvmData(); + env->openHandleScopes--; + delete v8impl::V8HandleScopeFromJsHandleScope(scope); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_OpenEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandleScope* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = + v8impl::JsEscapableHandleScopeFromV8EscapableHandleScope(new v8impl::EscapableHandleScopeWrapper(env->isolate)); + env->openHandleScopes++; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CloseEscapableHandleScope(JSVM_Env env, JSVM_EscapableHandleScope scope) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + if (env->openHandleScopes == 0) { + return JSVM_HANDLE_SCOPE_MISMATCH; + } + + delete v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + env->openHandleScopes--; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_EscapeHandle(JSVM_Env env, + JSVM_EscapableHandleScope scope, + JSVM_Value escapee, + JSVM_Value* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, scope); + CHECK_ARG(env, escapee); + CHECK_ARG(env, result); + + v8impl::EscapableHandleScopeWrapper* s = v8impl::V8EscapableHandleScopeFromJsEscapableHandleScope(scope); + if (!s->IsEscapeCalled()) { + *result = v8impl::JsValueFromV8LocalValue(s->Escape(v8impl::V8LocalValueFromJsValue(escapee))); + return ClearLastError(env); + } + return SetLastError(env, JSVM_ESCAPE_CALLED_TWICE); +} + +JSVM_Status JSVM_CDECL +OH_JSVM_NewInstance(JSVM_Env env, JSVM_Value constructor, size_t argc, const JSVM_Value* argv, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, constructor); + if (argc > 0) { + CHECK_ARG(env, argv); + } + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local ctor; + CHECK_TO_FUNCTION(env, ctor, constructor); + + auto maybe = + ctor->NewInstance(context, argc, reinterpret_cast*>(const_cast(argv))); + CHECK_MAYBE_EMPTY(env, maybe, JSVM_PENDING_EXCEPTION); + + *result = v8impl::JsValueFromV8LocalValue(maybe.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value object, JSVM_Value constructor, bool* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, result); + + *result = false; + + v8::Local ctor; + v8::Local context = env->context(); + + CHECK_TO_OBJECT(env, context, ctor, constructor); + + if (!ctor->IsFunction()) { + OH_JSVM_ThrowTypeError(env, "ERR_NAPI_CONS_FUNCTION", "Constructor must be a function"); + + return SetLastError(env, JSVM_FUNCTION_EXPECTED); + } + + JSVM_Status status = JSVM_GENERIC_FAILURE; + + v8::Local val = v8impl::V8LocalValueFromJsValue(object); + auto maybeResult = val->InstanceOf(context, ctor); + CHECK_MAYBE_NOTHING(env, maybeResult, status); + *result = maybeResult.FromJust(); + return GET_RETURN_STATUS(env); +} + +// Methods to support catching exceptions +JSVM_Status JSVM_CDECL OH_JSVM_IsExceptionPending(JSVM_Env env, bool* result) +{ + // JSVM_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = !env->lastException.IsEmpty(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetAndClearLastException(JSVM_Env env, JSVM_Value* result) +{ + // JSVM_PREAMBLE is not used here: this function must execute when there is a + // pending exception. + CHECK_ENV(env); + CHECK_ARG(env, result); + + if (env->lastException.IsEmpty()) { + return OH_JSVM_GetUndefined(env, result); + } else { + *result = v8impl::JsValueFromV8LocalValue(v8::Local::New(env->isolate, env->lastException)); + env->lastException.Reset(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsArraybuffer(JSVM_Env env, JSVM_Value value, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsArrayBuffer(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateArraybuffer(JSVM_Env env, size_t byteLength, void** data, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + v8::Local buffer = v8::ArrayBuffer::New(isolate, byteLength); + + // Optionally return a pointer to the buffer's data, to avoid another call to + // retrieve it. + if (data != nullptr) { + *data = buffer->Data(); + } + + *result = v8impl::JsValueFromV8LocalValue(buffer); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AllocateArrayBufferBackingStoreData(size_t byteLength, + JSVM_InitializedFlag initialized, + void** data) +{ + if (!data) { + return JSVM_INVALID_ARG; + } + auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); + *data = (initialized == JSVM_ZERO_INITIALIZED) ? allocator->Allocate(byteLength) + : allocator->AllocateUninitialized(byteLength); + return *data ? JSVM_OK : JSVM_GENERIC_FAILURE; +} + +JSVM_Status JSVM_CDECL OH_JSVM_FreeArrayBufferBackingStoreData(void* data) +{ + if (!data) { + return JSVM_INVALID_ARG; + } + auto allocator = v8impl::GetOrCreateDefaultArrayBufferAllocator(); + allocator->Free(data, JSVM_AUTO_LENGTH); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayBufferFromBackingStoreData(JSVM_Env env, + void* data, + size_t backingStoreSize, + size_t offset, + size_t arrayBufferSize, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, data); + CHECK_ARG(env, result); + CHECK_ARG_NOT_ZERO(env, backingStoreSize); + CHECK_ARG_NOT_ZERO(env, arrayBufferSize); + void* dataPtr = static_cast(data) + offset; + RETURN_STATUS_IF_FALSE(env, offset + arrayBufferSize <= backingStoreSize, JSVM_INVALID_ARG); + auto backingStore = + v8::ArrayBuffer::NewBackingStore(dataPtr, arrayBufferSize, v8::BackingStore::EmptyDeleter, nullptr); + v8::Local arrayBuffer = v8::ArrayBuffer::New(env->isolate, std::move(backingStore)); + *result = v8impl::JsValueFromV8LocalValue(arrayBuffer); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetArraybufferInfo(JSVM_Env env, JSVM_Value arraybuffer, void** data, size_t* byteLength) +{ + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); + + v8::Local ab = value.As(); + + if (data != nullptr) { + *data = ab->Data(); + } + + if (byteLength != nullptr) { + *byteLength = ab->ByteLength(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsTypedarray(JSVM_Env env, JSVM_Value value, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsTypedArray(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateTypedarray(JSVM_Env env, + JSVM_TypedarrayType type, + size_t length, + JSVM_Value arraybuffer, + size_t byteOffset, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); + + v8::Local buffer = value.As(); + v8::Local typedArray; + + switch (type) { + case JSVM_INT8_ARRAY: + CREATE_TYPED_ARRAY(env, Int8Array, 1, buffer, byteOffset, length, typedArray); + break; + case JSVM_UINT8_ARRAY: + CREATE_TYPED_ARRAY(env, Uint8Array, 1, buffer, byteOffset, length, typedArray); + break; + case JSVM_UINT8_CLAMPED_ARRAY: + CREATE_TYPED_ARRAY(env, Uint8ClampedArray, 1, buffer, byteOffset, length, typedArray); + break; + case JSVM_INT16_ARRAY: + CREATE_TYPED_ARRAY(env, Int16Array, 2, buffer, byteOffset, length, typedArray); + break; + case JSVM_UINT16_ARRAY: + CREATE_TYPED_ARRAY(env, Uint16Array, 2, buffer, byteOffset, length, typedArray); + break; + case JSVM_INT32_ARRAY: + CREATE_TYPED_ARRAY(env, Int32Array, 4, buffer, byteOffset, length, typedArray); + break; + case JSVM_UINT32_ARRAY: + CREATE_TYPED_ARRAY(env, Uint32Array, 4, buffer, byteOffset, length, typedArray); + break; + case JSVM_FLOAT32_ARRAY: + CREATE_TYPED_ARRAY(env, Float32Array, 4, buffer, byteOffset, length, typedArray); + break; + case JSVM_FLOAT64_ARRAY: + CREATE_TYPED_ARRAY(env, Float64Array, 8, buffer, byteOffset, length, typedArray); + break; + case JSVM_BIGINT64_ARRAY: + CREATE_TYPED_ARRAY(env, BigInt64Array, 8, buffer, byteOffset, length, typedArray); + break; + case JSVM_BIGUINT64_ARRAY: + CREATE_TYPED_ARRAY(env, BigUint64Array, 8, buffer, byteOffset, length, typedArray); + break; + default: + return SetLastError(env, JSVM_INVALID_ARG); + } + + *result = v8impl::JsValueFromV8LocalValue(typedArray); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetTypedarrayInfo(JSVM_Env env, + JSVM_Value typedarray, + JSVM_TypedarrayType* type, + size_t* length, + void** data, + JSVM_Value* arraybuffer, + size_t* byteOffset) +{ + CHECK_ENV(env); + CHECK_ARG(env, typedarray); + + v8::Local value = v8impl::V8LocalValueFromJsValue(typedarray); + RETURN_STATUS_IF_FALSE(env, value->IsTypedArray(), JSVM_INVALID_ARG); + + v8::Local array = value.As(); + + if (type != nullptr) { + if (value->IsInt8Array()) { + *type = JSVM_INT8_ARRAY; + } else if (value->IsUint8Array()) { + *type = JSVM_UINT8_ARRAY; + } else if (value->IsUint8ClampedArray()) { + *type = JSVM_UINT8_CLAMPED_ARRAY; + } else if (value->IsInt16Array()) { + *type = JSVM_INT16_ARRAY; + } else if (value->IsUint16Array()) { + *type = JSVM_UINT16_ARRAY; + } else if (value->IsInt32Array()) { + *type = JSVM_INT32_ARRAY; + } else if (value->IsUint32Array()) { + *type = JSVM_UINT32_ARRAY; + } else if (value->IsFloat32Array()) { + *type = JSVM_FLOAT32_ARRAY; + } else if (value->IsFloat64Array()) { + *type = JSVM_FLOAT64_ARRAY; + } else if (value->IsBigInt64Array()) { + *type = JSVM_BIGINT64_ARRAY; + } else if (value->IsBigUint64Array()) { + *type = JSVM_BIGUINT64_ARRAY; + } + } + + if (length != nullptr) { + *length = array->Length(); + } + + v8::Local buffer; + if (data != nullptr || arraybuffer != nullptr) { + // Calling Buffer() may have the side effect of allocating the buffer, + // so only do this when it’s needed. + buffer = array->Buffer(); + } + + if (data != nullptr) { + *data = static_cast(buffer->Data()) + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byteOffset != nullptr) { + *byteOffset = array->ByteOffset(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL +OH_JSVM_CreateDataview(JSVM_Env env, size_t byteLength, JSVM_Value arraybuffer, size_t byteOffset, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer(), JSVM_INVALID_ARG); + + v8::Local buffer = value.As(); + // TODO: should here throw an exception ? + if (byteLength + byteOffset > buffer->ByteLength()) { + OH_JSVM_ThrowRangeError(env, "ERR_JSVM_INVALID_DATAVIEW_ARGS", + "byteOffset + byteLength should be less than or " + "equal to the size in bytes of the array passed in"); + return SetLastError(env, JSVM_PENDING_EXCEPTION); + } + v8::Local DataView = v8::DataView::New(buffer, byteOffset, byteLength); + + *result = v8impl::JsValueFromV8LocalValue(DataView); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsDataview(JSVM_Env env, JSVM_Value value, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsDataView(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetDataviewInfo(JSVM_Env env, + JSVM_Value dataview, + size_t* byteLength, + void** data, + JSVM_Value* arraybuffer, + size_t* byteOffset) +{ + CHECK_ENV(env); + CHECK_ARG(env, dataview); + + v8::Local value = v8impl::V8LocalValueFromJsValue(dataview); + RETURN_STATUS_IF_FALSE(env, value->IsDataView(), JSVM_INVALID_ARG); + + v8::Local array = value.As(); + + if (byteLength != nullptr) { + *byteLength = array->ByteLength(); + } + + v8::Local buffer; + if (data != nullptr || arraybuffer != nullptr) { + // Calling Buffer() may have the side effect of allocating the buffer, + // so only do this when it’s needed. + buffer = array->Buffer(); + } + + if (data != nullptr) { + *data = static_cast(buffer->Data()) + array->ByteOffset(); + } + + if (arraybuffer != nullptr) { + *arraybuffer = v8impl::JsValueFromV8LocalValue(buffer); + } + + if (byteOffset != nullptr) { + *byteOffset = array->ByteOffset(); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetVersion(JSVM_Env env, uint32_t* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + *result = JSVM_API_VERSION; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreatePromise(JSVM_Env env, JSVM_Deferred* deferred, JSVM_Value* promise) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, deferred); + CHECK_ARG(env, promise); + + auto maybe = v8::Promise::Resolver::New(env->context()); + CHECK_MAYBE_EMPTY(env, maybe, JSVM_GENERIC_FAILURE); + + auto resolver = maybe.ToLocalChecked(); + auto v8Deferred = new v8impl::Persistent(); + v8Deferred->Reset(env->isolate, resolver); + + *deferred = v8impl::JsDeferredFromPersistent(v8Deferred); + *promise = v8impl::JsValueFromV8LocalValue(resolver->GetPromise()); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ResolveDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value resolution) +{ + return v8impl::ConcludeDeferred(env, deferred, resolution, true); +} + +JSVM_Status JSVM_CDECL OH_JSVM_RejectDeferred(JSVM_Env env, JSVM_Deferred deferred, JSVM_Value resolution) +{ + return v8impl::ConcludeDeferred(env, deferred, resolution, false); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsPromise(JSVM_Env env, JSVM_Value value, bool* isPromise) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isPromise); + + *isPromise = v8impl::V8LocalValueFromJsValue(value)->IsPromise(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateDate(JSVM_Env env, double time, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::MaybeLocal maybeDate = v8::Date::New(env->context(), time); + CHECK_MAYBE_EMPTY(env, maybeDate, JSVM_GENERIC_FAILURE); + + *result = v8impl::JsValueFromV8LocalValue(maybeDate.ToLocalChecked()); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsDate(JSVM_Env env, JSVM_Value value, bool* isDate) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isDate); + + *isDate = v8impl::V8LocalValueFromJsValue(value)->IsDate(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetDateValue(JSVM_Env env, JSVM_Value value, double* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, val->IsDate(), JSVM_DATE_EXPECTED); + + v8::Local date = val.As(); + *result = date->ValueOf(); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AddFinalizer(JSVM_Env env, + JSVM_Value jsObject, + void* finalizeData, + JSVM_Finalize finalizeCb, + void* finalizeHint, + JSVM_Ref* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, jsObject); + CHECK_ARG(env, finalizeCb); + + v8::Local v8Value = v8impl::V8LocalValueFromJsValue(jsObject); + RETURN_STATUS_IF_FALSE(env, v8Value->IsObject(), JSVM_INVALID_ARG); + + v8impl::RuntimeReference::New(env, v8Value, finalizeCb, finalizeData, finalizeHint); + + if (result != nullptr) { + auto* ref = v8impl::UserReference::New(env, v8Value, 0); + *result = reinterpret_cast(ref); + } + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AdjustExternalMemory(JSVM_Env env, int64_t changeInBytes, int64_t* adjustedValue) +{ + CHECK_ENV(env); + CHECK_ARG(env, adjustedValue); + + *adjustedValue = env->isolate->AdjustAmountOfExternalAllocatedMemory(changeInBytes); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetInstanceData(JSVM_Env env, void* data, JSVM_Finalize finalizeCb, void* finalizeHint) +{ + CHECK_ENV(env); + + v8impl::FinalizerTracker* oldData = static_cast(env->instanceData); + if (oldData != nullptr) { + // Our contract so far has been to not finalize any old data there may be. + // So we simply delete it. + delete oldData; + } + + env->instanceData = v8impl::FinalizerTracker::New(env, finalizeCb, data, finalizeHint); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetInstanceData(JSVM_Env env, void** data) +{ + CHECK_ENV(env); + CHECK_ARG(env, data); + + v8impl::FinalizerTracker* idata = static_cast(env->instanceData); + + *data = (idata == nullptr ? nullptr : idata->GetData()); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DetachArraybuffer(JSVM_Env env, JSVM_Value arraybuffer) +{ + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + RETURN_STATUS_IF_FALSE(env, value->IsArrayBuffer() || value->IsSharedArrayBuffer(), JSVM_ARRAYBUFFER_EXPECTED); + + v8::Local it = value.As(); + RETURN_STATUS_IF_FALSE(env, it->IsDetachable(), JSVM_DETACHABLE_ARRAYBUFFER_EXPECTED); + + it->Detach(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsDetachedArraybuffer(JSVM_Env env, JSVM_Value arraybuffer, bool* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + + *result = value->IsArrayBuffer() && value.As()->WasDetached(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_PropertyHandlerCfg propertyHandlerCfg, + JSVM_Callback callAsFunctionCallback, + JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, constructor); + CHECK_ARG(env, constructor->callback); + CHECK_ARG(env, propertyHandlerCfg); + + if (propertyCount > 0) { + CHECK_ARG(env, properties); + } + + v8::Isolate* isolate = env->isolate; + v8::EscapableHandleScope scope(isolate); + v8::Local tpl; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, constructor, &tpl)); + + v8::Local nameString; + CHECK_NEW_FROM_UTF8_LEN(env, nameString, utf8name, length); + tpl->SetClassName(nameString); + + size_t staticPropertyCount = 0; + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + + if ((p->attributes & JSVM_STATIC) != 0) { // attributes + // Static properties are handled separately below. + staticPropertyCount++; + continue; + } + + v8::Local propertyName; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in OH_JSVM_DefineProperties(); the + // difference is it applies to a template instead of an object, + // and preferred PropertyAttribute for lack of PropertyDescriptor + // support on ObjectTemplate. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local getterTpl; + v8::Local setterTpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->getter, &getterTpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->setter, &setterTpl)); + } + + tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, getterTpl, setterTpl, attributes, + v8::AccessControl::DEFAULT); + } else if (p->method != nullptr) { + v8::Local t; + if (p->attributes & JSVM_NO_RECEIVER_CHECK) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t)); + } else { + STATUS_CALL( + v8impl::FunctionCallbackWrapper::NewTemplate(env, p->method, &t, v8::Signature::New(isolate, tpl))); + } + + tpl->PrototypeTemplate()->Set(propertyName, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(propertyName, value, attributes); + } + } + + /* register property handler for instance object */ + v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandleCfg = v8impl::CreatePropertyCfg(env, propertyHandlerCfg); + if (propertyHandleCfg == nullptr) { + return JSVM_Status::JSVM_GENERIC_FAILURE; + } + v8::Local cbdata = v8impl::CallbackBundle::New(env, propertyHandleCfg); + + // register named property handler + v8::NamedPropertyHandlerConfiguration namedPropertyHandler; + if (propertyHandlerCfg->genericNamedPropertyGetterCallback) { + namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper::NameGetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertySetterCallback) { + namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper::NameSetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) { + namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::NameDeleterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) { + namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::NameEnumeratorInvoke; + } + namedPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(namedPropertyHandler); + + // register indexed property handle + v8::IndexedPropertyHandlerConfiguration indexPropertyHandler; + if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) { + indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper::IndexGetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertySetterCallback) { + indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper::IndexSetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) { + indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::IndexDeleterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) { + indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::IndexEnumeratorInvoke; + } + indexPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(indexPropertyHandler); + + // register call as function + if (callAsFunctionCallback && callAsFunctionCallback->callback) { + v8::Local funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback); + tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata); + } + + v8::Local context = env->context(); + *result = v8impl::JsValueFromV8LocalValue(scope.Escape(tpl->GetFunction(context).ToLocalChecked())); + + v8impl::RuntimeReference::New(env, v8impl::V8LocalValueFromJsValue(*result), v8impl::CfgFinalizedCallback, + propertyHandleCfg, nullptr); + + if (staticPropertyCount > 0) { + std::vector staticDescriptors; + staticDescriptors.reserve(staticPropertyCount); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + if ((p->attributes & JSVM_STATIC) != 0) { + staticDescriptors.push_back(*p); + } + } + + STATUS_CALL(OH_JSVM_DefineProperties(env, *result, staticDescriptors.size(), staticDescriptors.data())); + } + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsLocked(JSVM_Env env, bool* isLocked) +{ + CHECK_ENV(env); + CHECK_ARG(env, isLocked); + + *isLocked = v8::Locker::IsLocked(env->isolate); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AcquireLock(JSVM_Env env) +{ + CHECK_ENV(env); + + bool isLocked = v8::Locker::IsLocked(env->isolate); + if (!isLocked) { + env->locker = new v8::Locker(env->isolate); + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ReleaseLock(JSVM_Env env) +{ + CHECK_ENV(env); + + bool isLocked = v8::Locker::IsLocked(env->isolate); + if (isLocked && env->locker != nullptr) { + delete env->locker; + env->locker = nullptr; + } + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsCallable(JSVM_Env env, JSVM_Value value, bool* isCallable) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isCallable); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *isCallable = val->IsFunction(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsUndefined(JSVM_Env env, JSVM_Value value, bool* isUndefined) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isUndefined); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isUndefined = val->IsUndefined(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsNull(JSVM_Env env, JSVM_Value value, bool* isNull) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isNull); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isNull = val->IsNull(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsNullOrUndefined(JSVM_Env env, JSVM_Value value, bool* isNullOrUndefined) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isNullOrUndefined); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isNullOrUndefined = val->IsNullOrUndefined(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsBoolean(JSVM_Env env, JSVM_Value value, bool* isBoolean) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isBoolean); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isBoolean = val->IsBoolean(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsNumber(JSVM_Env env, JSVM_Value value, bool* isNumber) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isNumber); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isNumber = val->IsNumber(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsString(JSVM_Env env, JSVM_Value value, bool* isString) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isString); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isString = val->IsString(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsSymbol(JSVM_Env env, JSVM_Value value, bool* isSymbol) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isSymbol); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isSymbol = val->IsSymbol(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsFunction(JSVM_Env env, JSVM_Value value, bool* isFunction) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isFunction); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isFunction = val->IsFunction(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsObject(JSVM_Env env, JSVM_Value value, bool* isObject) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isObject); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isObject = val->IsObject(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsBigInt(JSVM_Env env, JSVM_Value value, bool* isBigInt) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isBigInt); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isBigInt = val->IsBigInt(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsConstructor(JSVM_Env env, JSVM_Value value, bool* isConstructor) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isConstructor); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + if (!val->IsObject()) { + *isConstructor = false; + return ClearLastError(env); + } + v8::Local obj = val.As(); + *isConstructor = obj->IsConstructor(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateRegExp(JSVM_Env env, JSVM_Value value, JSVM_RegExpFlags flags, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local pattern = v8impl::V8LocalValueFromJsValue(value); + RETURN_STATUS_IF_FALSE(env, pattern->IsString(), JSVM_STRING_EXPECTED); + v8::Local context = env->context(); + v8::MaybeLocal regExp = + v8::RegExp::New(context, pattern.As(), static_cast(flags)); + CHECK_MAYBE_EMPTY(env, regExp, JSVM_GENERIC_FAILURE); + *result = v8impl::JsValueFromV8LocalValue(regExp.ToLocalChecked()); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateMap(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Map::New(env->isolate)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsMap(JSVM_Env env, JSVM_Value value, bool* isMap) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isMap); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *isMap = val->IsMap(); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) +{ + CHECK_ENV(env); + auto jsvmData = reinterpret_cast(script); + + RETURN_STATUS_IF_FALSE(env, jsvmData && !jsvmData->isGlobal, JSVM_INVALID_ARG); + + jsvmData->taggedPointer = v8::Global(env->isolate, jsvmData->ToV8Local(env->isolate)); + + jsvmData->isGlobal = true; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script) +{ + CHECK_ENV(env); + auto jsvmData = reinterpret_cast(script); + + RETURN_STATUS_IF_FALSE(env, jsvmData && jsvmData->isGlobal, JSVM_INVALID_ARG); + + std::get>(jsvmData->taggedPointer).Reset(); + delete jsvmData; + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_OpenInspectorWithName(JSVM_Env env, int pid, const char* name) +{ + JSVM_PREAMBLE(env); + RETURN_STATUS_IF_FALSE(env, !name || strlen(name) < SIZE_MAX, JSVM_INVALID_ARG); + RETURN_STATUS_IF_FALSE(env, pid >= 0, JSVM_INVALID_ARG); + std::string path(name ? name : "jsvm"); + + if (!env->GetInspectorAgent()->Start(path, pid)) { + LOG(Error) << "Open Inspector failed: Please check the internet permisson."; + return SetLastError(env, JSVM_GENERIC_FAILURE); + } + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateSet(JSVM_Env env, JSVM_Value* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, result); + + *result = v8impl::JsValueFromV8LocalValue(v8::Set::New(env->isolate)); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsSet(JSVM_Env env, JSVM_Value value, bool* isSet) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isSet); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isSet = val->IsSet(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ObjectGetPrototypeOf(JSVM_Env env, JSVM_Value object, JSVM_Value* result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = obj->GetPrototypeV2(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env, JSVM_Value object, JSVM_Value prototype) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, prototype); + + v8::Local context = env->context(); + + v8::Local obj; + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local type = v8impl::V8LocalValueFromJsValue(prototype); + RETURN_STATUS_IF_FALSE(env, type->IsObject(), JSVM_INVALID_ARG); + v8::Maybe setMaybe = obj->SetPrototypeV2(context, type); + + RETURN_STATUS_IF_FALSE(env, setMaybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS((env)); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CompileWasmModule(JSVM_Env env, + const uint8_t* wasmBytecode, + size_t wasmBytecodeLength, + const uint8_t* cacheData, + size_t cacheDataLength, + bool* cacheRejected, + JSVM_Value* wasmModule) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, wasmBytecode); + RETURN_STATUS_IF_FALSE(env, wasmBytecodeLength > 0, JSVM_INVALID_ARG); + v8::MaybeLocal maybeModule; + if (cacheData == nullptr) { + maybeModule = v8::WasmModuleObject::Compile(env->isolate, { wasmBytecode, wasmBytecodeLength }); + } else { + RETURN_STATUS_IF_FALSE(env, cacheDataLength > 0, JSVM_INVALID_ARG); + bool rejected; + maybeModule = v8::WasmModuleObject::DeserializeOrCompile(env->isolate, { wasmBytecode, wasmBytecodeLength }, + { cacheData, cacheDataLength }, rejected); + if (cacheRejected != nullptr) { + *cacheRejected = rejected; + } + } + // To avoid the status code caused by exception being override, check exception once v8 API finished + RETURN_IF_EXCEPTION_HAS_CAUGHT(env); + CHECK_MAYBE_EMPTY(env, maybeModule, JSVM_GENERIC_FAILURE); + *wasmModule = v8impl::JsValueFromV8LocalValue(maybeModule.ToLocalChecked()); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CompileWasmFunction(JSVM_Env env, + JSVM_Value wasmModule, + uint32_t functionIndex, + JSVM_WasmOptLevel optLevel) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, wasmModule); + v8::Local val = v8impl::V8LocalValueFromJsValue(wasmModule); + RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG); + + v8::Local v8WasmModule = val.As(); + v8::WasmExecutionTier tier = v8::WasmExecutionTier::kNone; + if (optLevel == JSVM_WASM_OPT_BASELINE) { + // v8 liftoff has bug, keep BASELINE same as HIGH. + tier = v8::WasmExecutionTier::kTurbofan; + } else if (optLevel == JSVM_WASM_OPT_HIGH) { + tier = v8::WasmExecutionTier::kTurbofan; + } else { + // Unsupported optLevel + return SetLastError(env, JSVM_INVALID_ARG); + } + bool compileSuccess = v8WasmModule->CompileFunction(env->isolate, functionIndex, tier); + // To avoid the status code caused by exception being override, check exception once v8 API finished + RETURN_IF_EXCEPTION_HAS_CAUGHT(env); + RETURN_STATUS_IF_FALSE(env, compileSuccess, JSVM_GENERIC_FAILURE); + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsWasmModuleObject(JSVM_Env env, JSVM_Value value, bool* result) +{ + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 + // calls here cannot throw JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *result = val->IsWasmModuleObject(); + + return ClearLastError(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateWasmCache(JSVM_Env env, + JSVM_Value wasmModule, + const uint8_t** data, + size_t* length) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, wasmModule); + CHECK_ARG(env, data); + CHECK_ARG(env, length); + + v8::Local val = v8impl::V8LocalValueFromJsValue(wasmModule); + RETURN_STATUS_IF_FALSE(env, val->IsWasmModuleObject(), JSVM_INVALID_ARG); + + v8::Local v8WasmModule = val.As(); + v8::CompiledWasmModule compiledWasmModule = v8WasmModule->GetCompiledModule(); + v8::OwnedBuffer serializedBytes = compiledWasmModule.Serialize(); + // To avoid the status code caused by exception being override, check exception once v8 API finished + RETURN_IF_EXCEPTION_HAS_CAUGHT(env); + // If buffer size is 0, create wasm cache failed. + RETURN_STATUS_IF_FALSE(env, serializedBytes.size > 0, JSVM_GENERIC_FAILURE); + *data = serializedBytes.buffer.get(); + *length = serializedBytes.size; + // Release the ownership of buffer, OH_JSVM_ReleaseCache must be called explicitly to release the buffer + serializedBytes.buffer.release(); + + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ReleaseCache(JSVM_Env env, const uint8_t* cacheData, JSVM_CacheType cacheType) +{ + CHECK_ENV(env); + CHECK_ARG(env, cacheData); + if (cacheType == JSVM_CACHE_TYPE_JS) { + // The release behavior MUST match the memory allocation of OH_JSVM_CreateCodeCache. + delete[] cacheData; + } else if (cacheType == JSVM_CACHE_TYPE_WASM) { + // The release behavior MUST match the memory allocation of OH_JSVM_CreateWasmCache. + delete[] cacheData; + } else { + // Unsupported cacheType + return SetLastError(env, JSVM_INVALID_ARG); + } + return ClearLastError(env); +} diff --git a/jsvm/src/js_native_api_v8.h b/jsvm/src/js_native_api_v8.h new file mode 100644 index 0000000000000000000000000000000000000000..fa168535d5db9768c13771e4d54b79869b0c002c --- /dev/null +++ b/jsvm/src/js_native_api_v8.h @@ -0,0 +1,264 @@ +#ifndef SRC_JS_NATIVE_API_V8_H_ +#define SRC_JS_NATIVE_API_V8_H_ + +#include "jsvm_env.h" +#include "jsvm_util.h" +#include "type_conversion.h" + +inline JSVM_Status SetLastError(JSVM_Env env, + JSVM_Status errorCode, + uint32_t engineErrorCode = 0, + void* engineReserved = nullptr) +{ + env->lastError.errorCode = errorCode; + env->lastError.engineErrorCode = engineErrorCode; + env->lastError.engineReserved = engineReserved; + return errorCode; +} + +#define RETURN_STATUS_IF_FALSE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return SetLastError((env), (status)); \ + } \ + } while (0) + +#define RETURN_STATUS_IF_FALSE_WITHOUT_ENV(condition, status) \ + do { \ + if (!(condition)) { \ + return status; \ + } \ + } while (0) + +#define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \ + do { \ + if (!(condition)) { \ + return SetLastError((env), tryCatch.HasCaught() ? JSVM_PENDING_EXCEPTION : (status)); \ + } \ + } while (0) + +#define RETURN_IF_EXCEPTION_HAS_CAUGHT(env) \ + do { \ + if (tryCatch.HasCaught()) { \ + return SetLastError((env), JSVM_PENDING_EXCEPTION); \ + } \ + } while (0) + +#define CHECK_ENV(env) \ + do { \ + if ((env) == nullptr) { \ + return JSVM_INVALID_ARG; \ + } \ + } while (0) + +#define CHECK_ENV_NOT_IN_GC(env) \ + do { \ + CHECK_ENV((env)); \ + (env)->CheckGCAccess(); \ + } while (0) + +#define CHECK_ARG(env, arg) RETURN_STATUS_IF_FALSE((env), ((arg) != nullptr), JSVM_INVALID_ARG) + +#define CHECK_ARG_WITHOUT_ENV(arg) RETURN_STATUS_IF_FALSE_WITHOUT_ENV(((arg) != nullptr), JSVM_INVALID_ARG) + +#define CHECK_ARG_NOT_ZERO(env, arg) RETURN_STATUS_IF_FALSE((env), ((arg) != 0), JSVM_INVALID_ARG) + +#define CHECK_ARG_WITH_PREAMBLE(env, arg) \ + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), ((arg) != nullptr), JSVM_INVALID_ARG) + +#define CHECK_MAYBE_EMPTY(env, maybe, status) RETURN_STATUS_IF_FALSE((env), !((maybe).IsEmpty()), (status)) + +#define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \ + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status)) + +#define CHECK_MAYBE_NOTHING(env, maybe, status) RETURN_STATUS_IF_FALSE((env), !((maybe).IsNothing()), (status)) + +#define CHECK_MAYBE_NOTHING_WITH_PREAMBLE(env, maybe, status) \ + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsNothing()), (status)) + +// JSVM_PREAMBLE is not wrapped in do..while: tryCatch must have function scope +#define JSVM_PREAMBLE(env) \ + CHECK_ENV((env)); \ + RETURN_STATUS_IF_FALSE((env), (env)->lastException.IsEmpty(), JSVM_PENDING_EXCEPTION); \ + RETURN_STATUS_IF_FALSE((env), (env)->CanCallIntoJS(), \ + (env->GetVersion() == JSVM_VERSION_EXPERIMENTAL ? JSVM_CANNOT_RUN_JS \ + : JSVM_PENDING_EXCEPTION)); \ + ClearLastError((env)); \ + v8impl::TryCatch tryCatch((env)) + +#define CHECK_TO_TYPE(env, type, context, result, src, status) \ + do { \ + CHECK_ARG((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_TO_TYPE_WITH_PREAMBLE(env, type, context, result, src, status) \ + do { \ + CHECK_ARG_WITH_PREAMBLE((env), (src)); \ + auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \ + CHECK_MAYBE_EMPTY_WITH_PREAMBLE((env), maybe, (status)); \ + (result) = maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_TO_FUNCTION(env, result, src) \ + do { \ + CHECK_ARG((env), (src)); \ + v8::Local v8value = v8impl::V8LocalValueFromJsValue((src)); \ + RETURN_STATUS_IF_FALSE((env), v8value->IsFunction(), JSVM_INVALID_ARG); \ + (result) = v8value.As(); \ + } while (0) + +#define CHECK_TO_OBJECT(env, context, result, src) \ + CHECK_TO_TYPE((env), Object, (context), (result), (src), JSVM_OBJECT_EXPECTED) + +#define CHECK_TO_BIGINT(env, context, result, src) \ + CHECK_TO_TYPE((env), BigInt, (context), (result), (src), JSVM_BIGINT_EXPECTED) + +#define CHECK_TO_OBJECT_WITH_PREAMBLE(env, context, result, src) \ + CHECK_TO_TYPE_WITH_PREAMBLE((env), Object, (context), (result), (src), JSVM_OBJECT_EXPECTED) + +#define CHECK_TO_STRING(env, context, result, src) \ + CHECK_TO_TYPE((env), String, (context), (result), (src), JSVM_STRING_EXPECTED) + +#define CHECK_TO_NUMBER(env, context, result, src) \ + CHECK_TO_TYPE((env), Number, (context), (result), (src), JSVM_NUMBER_EXPECTED) + +#define GET_RETURN_STATUS(env) (!tryCatch.HasCaught() ? JSVM_OK : SetLastError((env), JSVM_PENDING_EXCEPTION)) + +#define THROW_RANGE_ERROR_IF_FALSE(env, condition, error, message) \ + do { \ + if (!(condition)) { \ + OH_JSVM_ThrowRangeError((env), (error), (message)); \ + return SetLastError((env), JSVM_GENERIC_FAILURE); \ + } \ + } while (0) + +// jsvm-api defines JSVM_AUTO_LENGTH as the indicator that a string +// is null terminated. For V8 the equivalent is -1. The assert +// validates that our cast of JSVM_AUTO_LENGTH results in -1 as +// needed by V8. +#define CHECK_NEW_FROM_UTF8_LEN(env, result, str, len) \ + do { \ + static_assert(static_cast(JSVM_AUTO_LENGTH) == -1, "Casting JSVM_AUTO_LENGTH to int must result in -1"); \ + RETURN_STATUS_IF_FALSE((env), (len == JSVM_AUTO_LENGTH) || len <= INT_MAX, JSVM_INVALID_ARG); \ + RETURN_STATUS_IF_FALSE((env), (str) != nullptr, JSVM_INVALID_ARG); \ + auto str_maybe = \ + v8::String::NewFromUtf8((env)->isolate, (str), v8::NewStringType::kInternalized, static_cast(len)); \ + CHECK_MAYBE_EMPTY((env), str_maybe, JSVM_GENERIC_FAILURE); \ + (result) = str_maybe.ToLocalChecked(); \ + } while (0) + +#define CHECK_NEW_FROM_UTF8(env, result, str) CHECK_NEW_FROM_UTF8_LEN((env), (result), (str), JSVM_AUTO_LENGTH) + +#define CHECK_NEW_STRING_ARGS(env, str, length, result) \ + do { \ + CHECK_ENV_NOT_IN_GC((env)); \ + if ((length) > 0) \ + CHECK_ARG((env), (str)); \ + CHECK_ARG((env), (result)); \ + RETURN_STATUS_IF_FALSE((env), ((length) == JSVM_AUTO_LENGTH) || (length) <= INT_MAX, JSVM_INVALID_ARG); \ + } while (0) + +#define CREATE_TYPED_ARRAY(env, type, sizeOfElement, buffer, byteOffset, length, out) \ + do { \ + if ((sizeOfElement) > 1) { \ + THROW_RANGE_ERROR_IF_FALSE((env), (byteOffset) % (sizeOfElement) == 0, \ + "ERR_JSVM_INVALID_TYPEDARRAY_ALIGNMENT", \ + "start offset of " #type " should be a multiple of " #sizeOfElement); \ + } \ + THROW_RANGE_ERROR_IF_FALSE((env), (length) * (sizeOfElement) + (byteOffset) <= buffer->ByteLength(), \ + "ERR_JSVM_INVALID_TYPEDARRAY_LENGTH", "Invalid typed array length"); \ + (out) = v8::type::New((buffer), (byteOffset), (length)); \ + } while (0) + +#define JSVM_PRIVATE_KEY(isolate, suffix) (v8impl::GetIsolateData(isolate)->suffix##Key.Get(isolate)) + +#define STATUS_CALL(call) \ + do { \ + JSVM_Status status = (call); \ + if (status != JSVM_OK) \ + return status; \ + } while (0) + +namespace v8impl { + +class TryCatch : public v8::TryCatch { +public: + explicit TryCatch(JSVM_Env env) : v8::TryCatch(env->isolate), env(env) {} + + ~TryCatch() + { + if (HasCaught()) { + env->lastException.Reset(env->isolate, Exception()); + } + } + +private: + JSVM_Env env; +}; + +typedef JSVM_Value (*GetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value); +typedef JSVM_Value (*SetterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value, JSVM_Value); +typedef JSVM_Value (*DeleterCallback)(JSVM_Env, JSVM_Value, JSVM_Value, JSVM_Value); +typedef JSVM_Value (*EnumeratorCallback)(JSVM_Env, JSVM_Value, JSVM_Value); + +struct JSVM_PropertyHandlerCfgStruct { + GetterCallback namedGetterCallback_; + SetterCallback namedSetterCallback_; + DeleterCallback nameDeleterCallback_; + EnumeratorCallback namedEnumeratorCallback_; + GetterCallback indexedGetterCallback_; + SetterCallback indexedSetterCallback_; + DeleterCallback indexedDeleterCallback_; + EnumeratorCallback indexedEnumeratorCallback_; + JSVM_Ref namedPropertyData_; + JSVM_Ref indexedPropertyData_; +}; + +inline JSVM_PropertyHandlerCfgStruct* CreatePropertyCfg(JSVM_Env env, JSVM_PropertyHandlerCfg propertyCfg) +{ + JSVM_PropertyHandlerCfgStruct* newPropertyCfg = new JSVM_PropertyHandlerCfgStruct; + if (newPropertyCfg != nullptr && propertyCfg != nullptr) { + newPropertyCfg->namedGetterCallback_ = propertyCfg->genericNamedPropertyGetterCallback; + newPropertyCfg->namedSetterCallback_ = propertyCfg->genericNamedPropertySetterCallback; + newPropertyCfg->nameDeleterCallback_ = propertyCfg->genericNamedPropertyDeleterCallback; + newPropertyCfg->namedEnumeratorCallback_ = propertyCfg->genericNamedPropertyEnumeratorCallback; + newPropertyCfg->indexedGetterCallback_ = propertyCfg->genericIndexedPropertyGetterCallback; + newPropertyCfg->indexedSetterCallback_ = propertyCfg->genericIndexedPropertySetterCallback; + newPropertyCfg->indexedDeleterCallback_ = propertyCfg->genericIndexedPropertyDeleterCallback; + newPropertyCfg->indexedEnumeratorCallback_ = propertyCfg->genericIndexedPropertyEnumeratorCallback; + newPropertyCfg->namedPropertyData_ = nullptr; + newPropertyCfg->indexedPropertyData_ = nullptr; + if (propertyCfg->namedPropertyData != nullptr) { + v8::Local v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->namedPropertyData); + v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1); + newPropertyCfg->namedPropertyData_ = reinterpret_cast(reference); + } + + if (propertyCfg->indexedPropertyData != nullptr) { + v8::Local v8_value = v8impl::V8LocalValueFromJsValue(propertyCfg->indexedPropertyData); + v8impl::UserReference* reference = v8impl::UserReference::New(env, v8_value, 1); + newPropertyCfg->indexedPropertyData_ = reinterpret_cast(reference); + } + } + + return newPropertyCfg; +} + +inline void CfgFinalizedCallback(JSVM_Env env, void* finalizeData, void* finalizeHint) +{ + auto cfg = reinterpret_cast(finalizeData); + if (cfg->namedPropertyData_ != nullptr) { + delete reinterpret_cast(cfg->namedPropertyData_); + } + if (cfg->indexedPropertyData_ != nullptr) { + delete reinterpret_cast(cfg->indexedPropertyData_); + } + delete cfg; +} + +} // end of namespace v8impl + +#endif // SRC_JS_NATIVE_API_V8_H_ diff --git a/jsvm/src/jsvm_dfx.h b/jsvm/src/jsvm_dfx.h new file mode 100644 index 0000000000000000000000000000000000000000..850979cdc3e53ceddc4a3f205f959960d2d66fa9 --- /dev/null +++ b/jsvm/src/jsvm_dfx.h @@ -0,0 +1,74 @@ +#ifndef JSVM_DFX_H +#define JSVM_DFX_H + +#include + +#include "v8.h" + +#define ERROR_AND_ABORT(expr) \ + do { \ + /* Make sure that this struct does not end up in inline code, but */ \ + /* rather in a read-only data section when modifying this code. */ \ + abort(); \ + } while (0) + +#define UNREACHABLE(...) ERROR_AND_ABORT("Unreachable code reached" __VA_OPT__(": ") __VA_ARGS__) + +#define CHECK(expr) \ + do { \ + if (UNLIKELY(!(expr))) { \ + ERROR_AND_ABORT(expr); \ + } \ + } while (0) + +#define CHECK_EQ(a, b) CHECK((a) == (b)) +#define CHECK_GE(a, b) CHECK((a) >= (b)) +#define CHECK_GT(a, b) CHECK((a) > (b)) +#define CHECK_LE(a, b) CHECK((a) <= (b)) +#define CHECK_LT(a, b) CHECK((a) < (b)) +#define CHECK_NE(a, b) CHECK((a) != (b)) +#define CHECK_NULL(val) CHECK((val) == nullptr) +#define CHECK_NOT_NULL(val) CHECK((val) != nullptr) +#define CHECK_IMPLIES(a, b) CHECK(!(a) || (b)) + +#ifdef DEBUG +#define DCHECK(expr) CHECK(expr) +#define DCHECK_EQ(a, b) CHECK((a) == (b)) +#define DCHECK_GE(a, b) CHECK((a) >= (b)) +#define DCHECK_GT(a, b) CHECK((a) > (b)) +#define DCHECK_LE(a, b) CHECK((a) <= (b)) +#define DCHECK_LT(a, b) CHECK((a) < (b)) +#define DCHECK_NE(a, b) CHECK((a) != (b)) +#define DCHECK_NULL(val) CHECK((val) == nullptr) +#define DCHECK_NOT_NULL(val) CHECK((val) != nullptr) +#define DCHECK_IMPLIES(a, b) CHECK(!(a) || (b)) +#else +#define DCHECK(expr) +#define DCHECK_EQ(a, b) +#define DCHECK_GE(a, b) +#define DCHECK_GT(a, b) +#define DCHECK_LE(a, b) +#define DCHECK_LT(a, b) +#define DCHECK_NE(a, b) +#define DCHECK_NULL(val) +#define DCHECK_NOT_NULL(val) +#define DCHECK_IMPLIES(a, b) +#endif + +namespace jsvm { +class DebugSealHandleScope { +public: + explicit inline DebugSealHandleScope(v8::Isolate* isolate = nullptr) +#ifdef DEBUG + : sealHandleScope(isolate != nullptr ? isolate : v8::Isolate::GetCurrent()) +#endif + {} + +private: +#ifdef DEBUG + v8::SealHandleScope sealHandleScope; +#endif +}; +} // namespace jsvm + +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_env.cc b/jsvm/src/jsvm_env.cc new file mode 100644 index 0000000000000000000000000000000000000000..5efbf82e166e305d27d2c8c6d716b99cb7550cb5 --- /dev/null +++ b/jsvm/src/jsvm_env.cc @@ -0,0 +1,116 @@ +#include "jsvm_env.h" + +#include "libplatform/libplatform.h" + +void JSVM_Env__::RunAndClearInterrupts() +{ + while (messageQueue.size() > 0) { + std::vector messageQueueTmp {}; + { + const std::lock_guard lock(messageQueueMutex); + messageQueueTmp.swap(messageQueue); + } + jsvm::DebugSealHandleScope sealHandleScope(isolate); + + for (auto& cb : messageQueueTmp) { + cb(this); + } + } +} + +JSVM_Env__::JSVM_Env__(v8::Isolate* isolate, int32_t apiVersion) : isolate(isolate), apiVersion(apiVersion) +{ + inspectorAgent = jsvm::InspectorAgent::New(this); + ClearLastError(this); +} + +void JSVM_Env__::DeleteMe() +{ + v8impl::RefTracker::FinalizeAll(&finalizerList); + v8impl::RefTracker::FinalizeAll(&userReferenceList); + + { + v8::Context::Scope context_scope(context()); + if (inspectorAgent->IsActive()) { + inspectorAgent->WaitForDisconnect(); + } + delete inspectorAgent; + } + + // release lock + if (locker) { + delete locker; + locker = nullptr; + } + + delete this; +} + +#ifndef ENABLE_INSPECTOR +#include "jsvm_log.h" +namespace { +/* + * If inspector is not enabled, using fake jsvm inspect agent. + * All Interface in fake agent log error. + */ +class FakeAgent : public jsvm::InspectorAgent { +public: + explicit FakeAgent(JSVM_Env env) + { + LogError(); + } + ~FakeAgent() = default; + +public: + bool Start(const std::string& path, const std::string& hostName, int port, int pid = -1) override + { + LogError(); + return false; + } + + bool Start(const std::string& path, int pid) override + { + LogError(); + return false; + } + + void Stop() override + { + LogError(); + } + + bool IsActive() override + { + LogError(); + return false; + } + + void WaitForConnect() override + { + LogError(); + } + + void WaitForDisconnect() override + { + LogError(); + } + + void PauseOnNextJavascriptStatement(const std::string& reason) override + { + LogError(); + } + +private: + void LogError() + { + LOG(Error) << "JSVM Inspector is not enabled"; + } +}; + +} // namespace + +jsvm::InspectorAgent* jsvm::InspectorAgent::New(JSVM_Env env) +{ + return new FakeAgent(env); +} +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_env.h b/jsvm/src/jsvm_env.h new file mode 100644 index 0000000000000000000000000000000000000000..1ddec089649ba30a1f3814fce89f3f03991d72f0 --- /dev/null +++ b/jsvm/src/jsvm_env.h @@ -0,0 +1,191 @@ +#ifndef JSVM_ENV_H +#define JSVM_ENV_H +#include +#include +#include + +#include "jsvm_dfx.h" +#include "jsvm_inspector_agent.h" +#include "jsvm_reference.h" +#include "jsvm_types.h" +#include "jsvm_util.h" +#include "type_conversion.h" +#include "v8.h" + +inline JSVM_Status ClearLastError(JSVM_Env env); + +struct JSVM_Env__ final { +public: + explicit JSVM_Env__(v8::Local context, int32_t apiVersion) + : isolate(context->GetIsolate()), contextPersistent(isolate, context), apiVersion(apiVersion) + { + ClearLastError(this); + } + + // Constructor for creating partial env. + explicit JSVM_Env__(v8::Isolate* isolate, int32_t apiVersion); + + int32_t GetVersion() + { + return apiVersion; + } + + using Callback = std::function; + + inline void RequestInterrupt(Callback cb) + { + { + const std::lock_guard lock(messageQueueMutex); + messageQueue.emplace_back(std::move(cb)); + } + isolate->RequestInterrupt( + [](v8::Isolate* isolate, void* data) { static_cast(data)->RunAndClearInterrupts(); }, this); + } + + void RunAndClearInterrupts(); + + jsvm::InspectorAgent* GetInspectorAgent() + { + return inspectorAgent; + } + + v8::Platform* platform(); + + inline v8::Local context() const + { + return v8impl::PersistentToLocal::Strong(contextPersistent); + } + + bool CanCallIntoJS() const + { + return true; + } + + static inline void HandleThrow(JSVM_Env env, v8::Local value) + { + if (env->IsTerminatedOrTerminating()) { + return; + } + env->isolate->ThrowException(value); + } + + // i.e. whether v8 exited or is about to exit + inline bool IsTerminatedOrTerminating() + { + return this->isolate->IsExecutionTerminating() || !CanCallIntoJS(); + } + + // v8 uses a special exception to indicate termination, the + // `handle_exception` callback should identify such case using + // IsTerminatedOrTerminating() before actually handle the exception + template + inline void CallIntoModule(T&& call, U&& handle_exception = HandleThrow) + { + int open_handle_scopes_before = openHandleScopes; + int open_callback_scopes_before = openCallbackScopes; + ClearLastError(this); + call(this); + CHECK_EQ(openHandleScopes, open_handle_scopes_before); + CHECK_EQ(openCallbackScopes, open_callback_scopes_before); + if (!lastException.IsEmpty()) { + handle_exception(this, lastException.Get(this->isolate)); + lastException.Reset(); + } + } + + // Call finalizer immediately. + void CallFinalizer(JSVM_Finalize cb, void* data, void* hint) + { + v8::HandleScope handle_scope(isolate); + CallIntoModule([&](JSVM_Env env) { cb(env, data, hint); }); + } + + void DeleteMe(); + + void CheckGCAccess() + { + if (inGcFinalizer) { + jsvm::OnFatalError(nullptr, "Finalizer is calling a function that may affect GC state.\n" + "The finalizers are run directly from GC and must not affect GC " + "state.\n" + "Use `node_api_post_finalizer` from inside of the finalizer to work " + "around this issue.\n" + "It schedules the call as a new task in the event loop."); + } + } + + template + JSVM_Data__* NewJsvmData(T srcPtr, JSVM_Data__::DataType type = JSVM_Data__::kJsvmScript) + { + if (dataStack.empty() || openHandleScopes != dataStack.top().first) { + dataStack.emplace(openHandleScopes, std::vector()); + } + auto newData = new JSVM_Data__(srcPtr, false, type); + dataStack.top().second.push_back(newData); + return newData; + } + + void ReleaseJsvmData() + { + if (dataStack.empty() || openHandleScopes != dataStack.top().first) { + return; + } + for (auto data : dataStack.top().second) { + if (!data->isGlobal) { + delete data; + } + } + dataStack.pop(); + } + + // Shortcut for context()->GetIsolate() + v8::Isolate* const isolate; + v8impl::Persistent contextPersistent; + + // Error info and execption + JSVM_ExtendedErrorInfo lastError; + v8impl::Persistent lastException; + + // We store references in two different lists, depending on whether they have + // `JSVM_Finalizer` callbacks, because we must first finalize the ones that + // have such a callback. See `~JSVM_Env__()` above for details. + v8impl::RefList userReferenceList; + v8impl::RefList finalizerList; + + // Store v8::Data + std::stack>> dataStack; + + // Store external instance data + void* instanceData = nullptr; + + // Store v8::Locker + v8::Locker* locker = nullptr; + + int32_t apiVersion; + + int openHandleScopes = 0; + int openCallbackScopes = 0; + bool inGcFinalizer = false; + +private: + // Used for inspector + jsvm::InspectorAgent* inspectorAgent; + std::mutex messageQueueMutex; + std::vector messageQueue; + +protected: + // Should not be deleted directly. Delete with `JSVM_Env__::DeleteMe()` + // instead. + ~JSVM_Env__() = default; +}; + +inline JSVM_Status ClearLastError(JSVM_Env env) +{ + env->lastError.errorCode = JSVM_OK; + env->lastError.engineErrorCode = 0; + env->lastError.engineReserved = nullptr; + env->lastError.errorMessage = nullptr; + return JSVM_OK; +} + +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_inspector_agent.h b/jsvm/src/jsvm_inspector_agent.h new file mode 100644 index 0000000000000000000000000000000000000000..d82e1c6cac63a7567602d5b3b902d7ca7eb15073 --- /dev/null +++ b/jsvm/src/jsvm_inspector_agent.h @@ -0,0 +1,33 @@ +/* Interface for JSVM inspector */ + +#ifndef JSVM_INSPECTOR_AGENT_H +#define JSVM_INSPECTOR_AGENT_H +#include + +#include "jsvm_types.h" + +namespace jsvm { +class InspectorAgent { +public: + static InspectorAgent* New(JSVM_Env); + virtual ~InspectorAgent() = default; + +public: + virtual bool Start(const std::string& path, const std::string& hostName, int port, int pid = -1) = 0; + + // Find avaliable port and open inspector. + virtual bool Start(const std::string& path, int pid) = 0; + + virtual void Stop() = 0; + + virtual bool IsActive() = 0; + + virtual void WaitForConnect() = 0; + + virtual void WaitForDisconnect() = 0; + + virtual void PauseOnNextJavascriptStatement(const std::string& reason) = 0; +}; +} // namespace jsvm + +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_log.h b/jsvm/src/jsvm_log.h new file mode 100644 index 0000000000000000000000000000000000000000..9bf538775f668c03954c333ab7e9361e86babcf3 --- /dev/null +++ b/jsvm/src/jsvm_log.h @@ -0,0 +1,64 @@ +#ifndef JSVM_LOG_H +#define JSVM_LOG_H +#include +#include +#include + +#include "platform/platform.h" + +namespace jsvm { +class LogStream : public std::stringstream { +public: + LogStream() : std::stringstream() {} + + virtual ~LogStream() = default; +}; + +class LogConsole : public LogStream { +public: + explicit LogConsole(platform::OS::LogLevel level) : LogStream(), level(level) {} + + ~LogConsole() override + { + platform::OS::PrintString(level, str().c_str()); + } + +private: + platform::OS::LogLevel level; +}; + +class LogFile : public LogStream { +public: + explicit LogFile(const char* filename) : file(filename, std::ios::app) {}; + + ~LogFile() override + { + file << str() << std::endl; + } + +private: + std::ofstream file; +}; + +class LogInfo : public LogConsole { +public: + LogInfo() : LogConsole(platform::OS::LogLevel::LOG_INFO) {} +}; + +class LogError : public LogConsole { +public: + LogError() : LogConsole(platform::OS::LogLevel::LOG_ERROR) {} +}; + +class LogFatal : public LogConsole { +public: + LogFatal() : LogConsole(platform::OS::LogLevel::LOG_FATAL) {} +}; +} // namespace jsvm + +// Support LOG(Info), LOG(Error), LOG(Fatal) +#define LOG(level) jsvm::Log##level() + +// Print Log to file +#define LOG_FILE(filename) jsvm::LogFile(filename) +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_reference.cpp b/jsvm/src/jsvm_reference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9200e26ce1b419efe6151bd2c6662e22f03e883 --- /dev/null +++ b/jsvm/src/jsvm_reference.cpp @@ -0,0 +1,257 @@ +#include "jsvm_reference.h" + +#include "js_native_api_v8.h" + +namespace v8impl { +namespace { +// In JavaScript, weak references can be created for object types (Object, +// Function, and external Object) and for local symbols that are created with +// the `Symbol` function call. Global symbols created with the `Symbol.for` +// method cannot be weak references because they are never collected. +// +// Currently, V8 has no API to detect if a symbol is local or global. +// Until we have a V8 API for it, we consider that all symbols can be weak. +inline bool CanBeHeldWeakly(v8::Local value) +{ + return value->IsObject() || value->IsSymbol(); +} +} // namespace + +// RefTracker +inline void RefTracker::Link(RefList* list) +{ + DCHECK(list != nullptr); + prev = list; + next = list->next; + if (next != nullptr) { + next->prev = this; + } + list->next = this; +} + +inline void RefTracker::Unlink() +{ + if (prev != nullptr) { + prev->next = next; + } + if (next != nullptr) { + next->prev = prev; + } + prev = nullptr; + next = nullptr; +} + +void RefTracker::FinalizeAll(RefList* list) +{ + while (list->next != nullptr) { + list->next->Finalize(); + } +} + +void RefTracker::Finalize() +{ + UNREACHABLE("Finalize need to be realized"); +} + +// UserReference +UserReference* UserReference::New(JSVM_Env env, v8::Local value, uint32_t initialRefcount) +{ + auto ref = new UserReference(env, value, initialRefcount); + + return ref; +} + +UserReference::UserReference(JSVM_Env env, v8::Local value, uint32_t initialRefcount) + : persistent(env->isolate, value), env(env), refcount(initialRefcount), canBeWeak(CanBeHeldWeakly(value)) +{ + if (refcount == 0) { + SetWeak(); + } + + Link(&env->userReferenceList); +} + +UserReference::~UserReference() +{ + persistent.Reset(); + Unlink(); +} + +void UserReference::Finalize() +{ + persistent.Reset(); + Unlink(); +} + +v8::Local UserReference::Get() +{ + if (persistent.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(env->isolate, persistent); + } +} + +void UserReference::SetWeak() +{ + if (canBeWeak) { + persistent.SetWeak(); + } else { + persistent.Reset(); + } +} + +uint32_t UserReference::Ref() +{ + // If persistent is cleared by GC, return 0 unconditionally. + if (persistent.IsEmpty()) { + return 0; + } + + if (++refcount == 1) { + // If persistent can not be weak, it will be cleared in SetWeak(). + DCHECK(canBeWeak); + persistent.ClearWeak(); + } + + return refcount; +} + +uint32_t UserReference::Unref() +{ + // If persistent is cleared by GC, return 0 unconditionally. + if (persistent.IsEmpty() || refcount == 0) { + return 0; + } + + if (--refcount == 0) { + SetWeak(); + } + + return refcount; +} + +uint32_t UserReference::RefCount() +{ + return refcount; +} + +// FinalizerTracker +FinalizerTracker* FinalizerTracker::New(JSVM_Env env, JSVM_Finalize cb, void* data, void* hint) +{ + return new FinalizerTracker(env, cb, data, hint); +} + +FinalizerTracker::FinalizerTracker(JSVM_Env env, JSVM_Finalize cb, void* data, void* hint) + : env(env), cb(cb), data(data), hint(hint) +{ + Link(&env->finalizerList); +} + +FinalizerTracker::~FinalizerTracker() +{ + Unlink(); +} + +void FinalizerTracker::ResetFinalizer() +{ + cb = nullptr; + data = nullptr; + hint = nullptr; +} + +void FinalizerTracker::CallFinalizer() +{ + if (!cb) { + return; + } + + JSVM_Finalize cbTemp = cb; + void* dataTemp = data; + void* hintTemp = hint; + ResetFinalizer(); + + if (!env) { + cbTemp(env, dataTemp, hintTemp); + } else { + // env->CallFinalizer(cbTemp, dataTemp, hintTemp); + env->CallIntoModule([&](JSVM_Env env) { cbTemp(env, dataTemp, hintTemp); }); + } +} + +void FinalizerTracker::Finalize() +{ + CallFinalizer(); + delete this; +} + +RuntimeReference::RuntimeReference(JSVM_Env env, v8::Local value, JSVM_Finalize cb, void* data, void* hint) + : FinalizerTracker(env, cb, data, hint), persistent(env->isolate, value) +{ + DCHECK(CanBeHeldWeakly(value)); +} + +RuntimeReference* RuntimeReference::New(JSVM_Env env, v8::Local value, void* data) +{ + auto* ref = new RuntimeReference(env, value, nullptr, data, nullptr); + // Delete self in first pass callback + ref->SetWeak(false); + + return ref; +} + +RuntimeReference* +RuntimeReference::New(JSVM_Env env, v8::Local value, JSVM_Finalize cb, void* data, void* hint) +{ + auto* ref = new RuntimeReference(env, value, cb, data, hint); + // Need second pass callback to call finalizer + ref->SetWeak(cb != nullptr); + + return ref; +} + +void RuntimeReference::DeleteReference(RuntimeReference* ref) +{ + // If reference is not added into first pass callbacks, delete this direct. + if (ref->persistent.IsWeak()) { + delete ref; + return; + } + + // If reference is added into first pass callbacks, reset finalizer function. + ref->ResetFinalizer(); +} + +inline void RuntimeReference::SetWeak(bool needSecondPass) +{ + if (needSecondPass) { + persistent.SetWeak(this, FirstPassCallback, v8::WeakCallbackType::kParameter); + } else { + persistent.SetWeak(this, FirstPassCallbackWithoutFinalizer, v8::WeakCallbackType::kParameter); + } +} + +void RuntimeReference::FirstPassCallback(const v8::WeakCallbackInfo& data) +{ + RuntimeReference* reference = data.GetParameter(); + + reference->persistent.Reset(); + data.SetSecondPassCallback(RuntimeReference::SecondPassCallback); +} + +void RuntimeReference::SecondPassCallback(const v8::WeakCallbackInfo& data) +{ + RuntimeReference* reference = data.GetParameter(); + + reference->Finalize(); +} + +void RuntimeReference::FirstPassCallbackWithoutFinalizer(const v8::WeakCallbackInfo& data) +{ + RuntimeReference* reference = data.GetParameter(); + + reference->persistent.Reset(); + delete reference; +} + +} // namespace v8impl \ No newline at end of file diff --git a/jsvm/src/jsvm_reference.h b/jsvm/src/jsvm_reference.h new file mode 100644 index 0000000000000000000000000000000000000000..0ef1c15156acebbde7adc4d0e8ead0a7ef4914a2 --- /dev/null +++ b/jsvm/src/jsvm_reference.h @@ -0,0 +1,109 @@ +#ifndef SRC_JSVM_REFERENCE_ +#define SRC_JSVM_REFERENCE_ +#include + +#include "jsvm_types.h" +#include "jsvm_util.h" + +namespace v8impl { +class RefTracker; +using RefList = RefTracker; + +class RefTracker { +public: + RefTracker() : next(nullptr), prev(nullptr) {} + + virtual ~RefTracker() = default; + + static void FinalizeAll(RefList* list); + +protected: + virtual void Finalize(); + + inline void Link(RefList* list); + + inline void Unlink(); + +private: + RefList* next; + RefList* prev; +}; + +class UserReference final : public RefTracker { +public: + static UserReference* New(JSVM_Env env, v8::Local value, uint32_t initialRefcount); + ~UserReference() override; + + // Increase and decrease reference + uint32_t Ref(); + uint32_t Unref(); + uint32_t RefCount(); + + // Get v8::Local value + v8::Local Get(); + +protected: + UserReference(JSVM_Env env, v8::Local value, uint32_t initialRefcount); + + void Finalize() override; + +private: + void SetWeak(); + +private: + v8impl::Persistent persistent; + JSVM_Env env; + uint32_t refcount; + bool canBeWeak; +}; + +class FinalizerTracker : public RefTracker { +protected: + FinalizerTracker(JSVM_Env env, JSVM_Finalize cb, void* data, void* hint); + +public: + static FinalizerTracker* New(JSVM_Env env, JSVM_Finalize cb, void* data, void* hint); + + ~FinalizerTracker() override; + + void* GetData() + { + return data; + } + +protected: + void ResetFinalizer(); + + void CallFinalizer(); + + void Finalize() override; + +private: + JSVM_Env env; + JSVM_Finalize cb; + void* data; + void* hint; +}; + +class RuntimeReference : public FinalizerTracker { +protected: + RuntimeReference(JSVM_Env env, v8::Local value, JSVM_Finalize cb, void* data, void* hint); + +public: + static RuntimeReference* New(JSVM_Env env, v8::Local value, void* data); + static RuntimeReference* New(JSVM_Env env, v8::Local value, JSVM_Finalize cb, void* data, void* hint); + static void DeleteReference(RuntimeReference* ref); + +private: + inline void SetWeak(bool needSecondPass); + static void FirstPassCallback(const v8::WeakCallbackInfo& data); + static void SecondPassCallback(const v8::WeakCallbackInfo& data); + static void FirstPassCallbackWithoutFinalizer(const v8::WeakCallbackInfo& data); + +private: + v8impl::Persistent persistent; +}; + +} // namespace v8impl + +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_util.h b/jsvm/src/jsvm_util.h new file mode 100644 index 0000000000000000000000000000000000000000..a750112eceb95c8e53ad66436b4232020faa5f46 --- /dev/null +++ b/jsvm/src/jsvm_util.h @@ -0,0 +1,109 @@ +#ifndef SRC_JSVM_UTIL_H_ +#define SRC_JSVM_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// jsvm header +#include "jsvm_dfx.h" +#include "jsvm_log.h" +#include "jsvm_version.h" +#include "platform/platform.h" + +// v8 header +#include "v8-inspector.h" +#include "v8-platform.h" +#include "v8-profiler.h" +#include "v8.h" + +// Use FORCE_INLINE on functions that have a debug-category-enabled check first +// and then ideally only a single function call following it, to maintain +// performance for the common case (no debugging used). +#ifdef __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) +#define COLD_NOINLINE __attribute__((cold, noinline)) +#else +#define FORCE_INLINE +#define COLD_NOINLINE +#endif + +#ifdef __GNUC__ +#define LIKELY(expr) __builtin_expect(!!(expr), 1) +#define UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +#define LIKELY(expr) expr +#define UNLIKELY(expr) expr +#endif + +namespace jsvm { +template +constexpr size_t ArraySize(const T (&)[N]) +{ + return N; +} + +[[noreturn]] inline void OnFatalError(const char* location, const char* message) +{ + LOG(Fatal) << "JSVM Fatal Error Position : " << (location ? location : "Unkown"); + LOG(Fatal) << "JSVM Fatal Error Message : " << (message ? message : "Unkown"); + platform::OS::Abort(); +} +} // namespace jsvm + +namespace v8impl { +template +using Persistent = v8::Global; + +// Convert a v8::PersistentBase, e.g. v8::Global, to a Local, with an extra +// optimization for strong persistent handles. +class PersistentToLocal { +public: + // If persistent.IsWeak() == false, then do not call persistent.Reset() + // while the returned Local is still in scope, it will destroy the + // reference to the object. + template + static inline v8::Local Default(v8::Isolate* isolate, const v8::PersistentBase& persistent) + { + if (persistent.IsWeak()) { + return PersistentToLocal::Weak(isolate, persistent); + } else { + return PersistentToLocal::Strong(persistent); + } + } + + // Unchecked conversion from a non-weak Persistent to Local, + // use with care! + // + // Do not call persistent.Reset() while the returned Local is still in + // scope, it will destroy the reference to the object. + template + static inline v8::Local Strong(const v8::PersistentBase& persistent) + { + DCHECK(!persistent.IsWeak()); + return *reinterpret_cast*>(const_cast*>(&persistent)); + } + + template + static inline v8::Local Weak(v8::Isolate* isolate, const v8::PersistentBase& persistent) + { + return v8::Local::New(isolate, persistent); + } +}; +} // namespace v8impl +#endif \ No newline at end of file diff --git a/jsvm/src/jsvm_version.h b/jsvm/src/jsvm_version.h new file mode 100644 index 0000000000000000000000000000000000000000..47535dfd0a51ccf67a17fd51955cac46b3022fba --- /dev/null +++ b/jsvm/src/jsvm_version.h @@ -0,0 +1,25 @@ +#ifndef JSVM_VERSION_H +#define JSVM_VERSION_H + +// JSVM version +#define JSVM_MAJOR_VERSION 1 +#define JSVM_MINOR_VERSION 0 +#define JSVM_PATCH_VERSION 0 + +#ifndef DEBUG +#define JSVM_COMPILE_STATUS "Debug" +#else +#define JSVM_COMPILE_STATUS "Release" +#endif + +#define STRINGIFY_HELPER(x) #x +#define STRINGIFY(x) STRINGIFY_HELPER(x) + +#define JSVM_VERSION_STRING \ + "v" STRINGIFY(JSVM_MAJOR_VERSION) "." STRINGIFY(JSVM_MINOR_VERSION) "." STRINGIFY( \ + JSVM_PATCH_VERSION) "(" JSVM_COMPILE_STATUS ")" + +// JSVM_API_VERSION +#define JSVM_API_VERSION 9 + +#endif diff --git a/jsvm/src/platform/platform.cc b/jsvm/src/platform/platform.cc new file mode 100644 index 0000000000000000000000000000000000000000..d8438983e1bbf3a80ca18ca375a71efe35c0db31 --- /dev/null +++ b/jsvm/src/platform/platform.cc @@ -0,0 +1,57 @@ +#include "platform/platform.h" + +#include + +#include "unistd.h" + +namespace platform { +void OS::Abort() +{ + std::abort(); +} + +uint64_t OS::GetUid() +{ + return static_cast(getuid()); +} + +uint64_t OS::GetPid() +{ + return static_cast(getppid()); +} + +uint64_t OS::GetTid() +{ + return static_cast(gettid()); +} + +void OS::PrintString(OS::LogLevel level, const char* str) +{ + (void)level; + printf("%s", str); +} + +void VPrint(const char* format, va_list args) +{ + vprintf(format, args); +} + +void VPrintError(const char* format, va_list args) +{ + vfprintf(stderr, format, args); +} + +void OS::Print(OS::LogLevel level, const char* format, ...) +{ + va_list args; + va_start(args, format); + if (static_cast(level) > static_cast(OS::LogLevel::LOG_WARN)) { + VPrintError(format, args); + } else { + VPrint(format, args); + } + va_end(args); +} + + +} // namespace platform \ No newline at end of file diff --git a/jsvm/src/platform/platform.h b/jsvm/src/platform/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..182ce26e7f1abee51dd26dd69e20890f40a56887 --- /dev/null +++ b/jsvm/src/platform/platform.h @@ -0,0 +1,42 @@ +#ifndef JSVM_PLATFORM_H +#define JSVM_PLATFORM_H +#include +#include +#include +#include +#include + +#ifdef TARGET_OHOS +#include "platform/platform_ohos.h" +#define OHOS_API_CALL(api_call) api_call +#else +#define OHOS_API_CALL(api_call) +#endif + +namespace platform { +class OS { +public: + [[noreturn]] static void Abort(); + static uint64_t GetUid(); + static uint64_t GetPid(); + static uint64_t GetTid(); + + enum class LogLevel : uint64_t { LOG_DEBUG = 0, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL }; + + static void Print(LogLevel level, const char* format, ...) __attribute__((format(printf, 2, 3))); + static void PrintString(LogLevel level, const char* str); +}; + +class RunJsTrace { +public: + RunJsTrace(bool RunJs); + RunJsTrace(const char* name); + + ~RunJsTrace(); + +private: + bool RunJs; +}; +} // namespace platform + +#endif \ No newline at end of file diff --git a/jsvm/src/platform/platform_ohos.cc b/jsvm/src/platform/platform_ohos.cc new file mode 100644 index 0000000000000000000000000000000000000000..c7274a49741ed27ffbb65f2fca8401ea2cc4c49e --- /dev/null +++ b/jsvm/src/platform/platform_ohos.cc @@ -0,0 +1,209 @@ +#include "platform.h" + +// v8 header +#include "v8.h" + +// OHOS API header +#include "hilog/log.h" +#include "hitrace_meter.h" +#include "init_param.h" +#include "unistd.h" +#ifdef ENABLE_HISYSEVENT +#include "hisysevent.h" +#endif + +extern "C" void ReportData(uint32_t resType, int64_t value, + const std::unordered_map& mapPayLoad); + +namespace ResourceSchedule { +namespace ResType { +enum : uint32_t { + RES_TYPE_REPORT_KEY_THREAD = 39 +}; + +enum ReportChangeStatus : int64_t { + CREATE = 0, + REMOVE = 1 +}; + +enum ThreadRole : int64_t { + USER_INTERACT = 0, + NORMAL_DISPLAY = 1, + IMPORTANT_DISPLAY = 2, + NORMAL_AUDIO = 3, + IMPORTANT_AUDIO = 4, + IMAGE_DECODE = 5 +}; +} // namespace ResType +} // namespace ResourceSchedule + +namespace platform { +void OS::Abort() +{ + std::abort(); +} + +uint64_t OS::GetUid() +{ + return static_cast(getuid()); +} + +uint64_t OS::GetPid() +{ + return static_cast(getprocpid()); +} + +uint64_t OS::GetTid() +{ + return static_cast(getproctid()); +} + +#ifdef LOG_DOMAIN +#undef LOG_DOMAIN +#endif +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +// TODO: set log domain +#define LOG_DOMAIN 0xD003900 +#define LOG_TAG "JSVM" + +void OS::PrintString(LogLevel level, const char* string) +{ + // convert platform defined LogLevel to hilog LogLevel + static constexpr ::LogLevel convertArray[] = { ::LogLevel::LOG_DEBUG, ::LogLevel::LOG_INFO, ::LogLevel::LOG_WARN, + ::LogLevel::LOG_ERROR, ::LogLevel::LOG_FATAL }; + static_assert(sizeof(convertArray) / sizeof(::LogLevel) == static_cast(OS::LogLevel::LOG_FATAL) + 1); + + // TODO: use LOG_APP or other LogType + HiLogPrint(LOG_APP, convertArray[static_cast(level)], LOG_DOMAIN, LOG_TAG, "%{public}s", string); +} + +void OS::Print(LogLevel level, const char* format, ...) +{ + constexpr size_t MAX_STRING_SIZE = 1024; + char string[MAX_STRING_SIZE]; + va_list arguments; + va_start(arguments, format); + vsnprintf(string, MAX_STRING_SIZE, format, arguments); + va_end(arguments); + + PrintString(level, string); +} + +RunJsTrace::RunJsTrace(bool RunJs) : RunJs(RunJs) +{ + if (RunJs) { + StartTrace(HITRACE_TAG_APP, "PureJS"); + } else { + FinishTrace(HITRACE_TAG_APP); + } +} + +RunJsTrace::RunJsTrace(const char* name) : RunJs(true) +{ + StartTrace(HITRACE_TAG_APP, name); +} + +RunJsTrace::~RunJsTrace() +{ + if (RunJs) { + FinishTrace(HITRACE_TAG_APP); + } else { + StartTrace(HITRACE_TAG_APP, "PureJS"); + } +} + +namespace ohos { +void ReportKeyThread(ThreadRole role) +{ + uint64_t uid = OS::GetUid(); + uint64_t tid = OS::GetTid(); + uint64_t pid = OS::GetPid(); + std::unordered_map payLoad = { { "uid", std::to_string(uid) }, + { "pid", std::to_string(pid) }, + { "tid", std::to_string(tid) }, + { "role", std::to_string(role) } }; + ReportData( + ResourceSchedule::ResType::RES_TYPE_REPORT_KEY_THREAD, + ResourceSchedule::ResType::ReportChangeStatus::CREATE, payLoad); +} + +inline bool ReadSystemXpmState() +{ + constexpr size_t ARG_BUFF_SIZE = 32; + char buffer[ARG_BUFF_SIZE] = { 0 }; + uint32_t buffSize = sizeof(buffer); + + if (SystemGetParameter("ohos.boot.advsecmode.state", buffer, &buffSize) == 0 && strcmp(buffer, "0") != 0) { + return true; + } + return false; +} + +void SetSecurityMode() +{ + constexpr size_t SEC_ARG_CNT = 2; + if (ReadSystemXpmState()) { + int secArgc = SEC_ARG_CNT; + constexpr bool removeFlag = false; + const char* secArgv[SEC_ARG_CNT] = { "jsvm", "--jitless" }; + v8::V8::SetFlagsFromCommandLine(&secArgc, const_cast(reinterpret_cast(secArgv)), + removeFlag); + } +} + +constexpr int MAX_FILE_LENGTH = 32 * 1024 * 1024; + +bool LoadStringFromFile(const std::string& filePath, std::string& content) +{ + std::ifstream file(filePath.c_str()); + if (!file.is_open()) { + return false; + } + + file.seekg(0, std::ios::end); + const long fileLength = file.tellg(); + if (fileLength > MAX_FILE_LENGTH) { + return false; + } + + content.clear(); + file.seekg(0, std::ios::beg); + std::copy(std::istreambuf_iterator(file), std::istreambuf_iterator(), std::back_inserter(content)); + return true; +} + +bool ProcessBundleName(std::string& bundleName) +{ + int pid = getprocpid(); + std::string filePath = "/proc/" + std::to_string(pid) + "/cmdline"; + if (!LoadStringFromFile(filePath, bundleName)) { + return false; + } + if (bundleName.empty()) { + return false; + } + auto pos = bundleName.find(":"); + if (pos != std::string::npos) { + bundleName = bundleName.substr(0, pos); + } + bundleName = bundleName.substr(0, strlen(bundleName.c_str())); + return true; +} + +void WriteHisysevent() { +#ifdef ENABLE_HISYSEVENT + std::string bundleName; + if (!ProcessBundleName(bundleName)) { + bundleName = "INVALID_BUNDLE_NAME"; + } + HiSysEventWrite(OHOS::HiviewDFX::HiSysEvent::Domain::JSVM_RUNTIME, "APP_STATS", + OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC, + "BUNDLE_NAME", bundleName); +#endif +} +} // namespace ohos + +} // namespace platform \ No newline at end of file diff --git a/jsvm/src/platform/platform_ohos.h b/jsvm/src/platform/platform_ohos.h new file mode 100644 index 0000000000000000000000000000000000000000..68fa34eaa0966aeddb648696c506c2d3e19ce809 --- /dev/null +++ b/jsvm/src/platform/platform_ohos.h @@ -0,0 +1,24 @@ +#ifndef TARGET_OHOS +#error "PLATFORM OHOS is required" +#endif + +#ifndef JSVM_PLATFORM_OHOS_H +#define JSVM_PLATFORM_OHOS_H +#include +#include + +namespace platform { +namespace ohos { +enum ThreadRole : int64_t { + USER_INTERACT = 0, + IMPORTANT_DISPLAY = 2, +}; + +void ReportKeyThread(ThreadRole role); + +void SetSecurityMode(); + +void WriteHisysevent(); +} // namespace ohos +} // namespace platform +#endif \ No newline at end of file diff --git a/jsvm/src/sourcemap.def b/jsvm/src/sourcemap.def new file mode 100644 index 0000000000000000000000000000000000000000..749bd4bd96065aefbb141df266988bdcfecb5bcd --- /dev/null +++ b/jsvm/src/sourcemap.def @@ -0,0 +1,394 @@ +// This file is a modified version of: +// https://cs.chromium.org/chromium/src/v8/tools/SourceMap.js?rcl=dd10454c1d +// from the V8 codebase. Logic specific to WebInspector is removed and linting +// is made to match the Node.js style guide. + +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a copy from blink dev tools, see: +// http://src.chromium.org/viewvc/blink/trunk/Source/devtools/front_end/SourceMap.js +// revision: 153407 + +/* + * Copyright (C) 2012 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static std::string SourceMapRunner = R"JS( +let base64Map; + +const VLQ_BASE_SHIFT = 5; +const VLQ_BASE_MASK = (1 << 5) - 1; +const VLQ_CONTINUATION_MASK = 1 << 5; + +class StringCharIterator { + /** + * @constructor + * @param {string} string + */ + constructor(string) { + this._string = string; + this._position = 0; + } + + /** + * @return {string} + */ + next() { + return this._string.charAt(this._position++); + } + + /** + * @return {string} + */ + peek() { + return this._string.charAt(this._position); + } + + /** + * @return {boolean} + */ + hasNext() { + return this._position < this._string.length; + } +} + +/** + * Implements Source Map V3 model. + * See https://github.com/google/closure-compiler/wiki/Source-Maps + * for format description. + */ +class SourceMap { + #payload; + #mappings = []; + #sources = {}; + #sourceContentByURL = {}; + + /** + * @constructor + * @param {SourceMapV3} payload + */ + constructor(payload) { + if (!base64Map) { + const base64Digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + base64Map = {}; + for (let i = 0; i < base64Digits.length; ++i) + base64Map[base64Digits[i]] = i; + } + this.#payload = cloneSourceMapV3(payload); + this.#parseMappingPayload(); + } + + /** + * @return {object} raw source map v3 payload. + */ + get payload() { + return cloneSourceMapV3(this.#payload); + } + + #parseMappingPayload = () => { + if (this.#payload.sections) { + this.#parseSections(this.#payload.sections); + } else { + this.#parseMap(this.#payload, 0, 0); + } + this.#mappings.sort(compareSourceMapEntry); + }; + + /** + * @param {Array.} sections + */ + #parseSections = (sections) => { + for (let i = 0; i < sections.length; ++i) { + const section = sections[i]; + this.#parseMap(section.map, section.offset.line, section.offset.column); + } + }; + + /** + * @param {number} lineOffset 0-indexed line offset in compiled resource + * @param {number} columnOffset 0-indexed column offset in compiled resource + * @return {object} representing start of range if found, or empty object + */ + findEntry(lineOffset, columnOffset) { + let first = 0; + let count = this.#mappings.length; + while (count > 1) { + const step = count >> 1; + const middle = first + step; + const mapping = this.#mappings[middle]; + if (lineOffset < mapping[0] || + (lineOffset === mapping[0] && columnOffset < mapping[1])) { + count = step; + } else { + first = middle; + count -= step; + } + } + const entry = this.#mappings[first]; + if (!first && entry && (lineOffset < entry[0] || + (lineOffset === entry[0] && columnOffset < entry[1]))) { + return {}; + } else if (!entry) { + return {}; + } + return { + generatedLine: entry[0], + generatedColumn: entry[1], + originalSource: entry[2], + originalLine: entry[3], + originalColumn: entry[4], + name: entry[5], + }; + } + + /** + * @param {number} lineNumber 1-indexed line number in compiled resource call site + * @param {number} columnNumber 1-indexed column number in compiled resource call site + * @return {object} representing origin call site if found, or empty object + */ + findOrigin(lineNumber, columnNumber) { + const range = this.findEntry(lineNumber - 1, columnNumber - 1); + if ( + range.originalSource === undefined || + range.originalLine === undefined || + range.originalColumn === undefined || + range.generatedLine === undefined || + range.generatedColumn === undefined + ) { + return {}; + } + const lineOffset = lineNumber - range.generatedLine; + const columnOffset = columnNumber - range.generatedColumn; + return { + name: range.name, + fileName: range.originalSource, + lineNumber: range.originalLine + lineOffset, + columnNumber: range.originalColumn + columnOffset, + }; + } + + /** + * @override + */ + #parseMap(map, lineNumber, columnNumber) { + let sourceIndex = 0; + let sourceLineNumber = 0; + let sourceColumnNumber = 0; + let nameIndex = 0; + + const sources = []; + const originalToCanonicalURLMap = {}; + for (let i = 0; i < map.sources.length; ++i) { + const url = map.sources[i]; + originalToCanonicalURLMap[url] = url; + sources.push(url); + this.#sources[url] = true; + + if (map.sourcesContent && map.sourcesContent[i]) + this.#sourceContentByURL[url] = map.sourcesContent[i]; + } + + const stringCharIterator = new StringCharIterator(map.mappings); + let sourceURL = sources[sourceIndex]; + while (true) { + if (stringCharIterator.peek() === ',') + stringCharIterator.next(); + else { + while (stringCharIterator.peek() === ';') { + lineNumber += 1; + columnNumber = 0; + stringCharIterator.next(); + } + if (!stringCharIterator.hasNext()) + break; + } + + columnNumber += decodeVLQ(stringCharIterator); + if (isSeparator(stringCharIterator.peek())) { + this.#mappings.push([lineNumber, columnNumber]); + continue; + } + + const sourceIndexDelta = decodeVLQ(stringCharIterator); + if (sourceIndexDelta) { + sourceIndex += sourceIndexDelta; + sourceURL = sources[sourceIndex]; + } + sourceLineNumber += decodeVLQ(stringCharIterator); + sourceColumnNumber += decodeVLQ(stringCharIterator); + + let name; + if (!isSeparator(stringCharIterator.peek())) { + nameIndex += decodeVLQ(stringCharIterator); + name = map.names?.[nameIndex]; + } + + this.#mappings.push([ + lineNumber, columnNumber, sourceURL, sourceLineNumber, + sourceColumnNumber, name],); + } + } +} + +/** + * @param {string} char + * @return {boolean} + */ +function isSeparator(char) { + return char === ',' || char === ';'; +} + +/** + * @param {SourceMap.StringCharIterator} stringCharIterator + * @return {number} + */ +function decodeVLQ(stringCharIterator) { + // Read unsigned value. + let result = 0; + let shift = 0; + let digit; + do { + digit = base64Map[stringCharIterator.next()]; + result += (digit & VLQ_BASE_MASK) << shift; + shift += VLQ_BASE_SHIFT; + } while (digit & VLQ_CONTINUATION_MASK); + + // Fix the sign. + const negative = result & 1; + // Use unsigned right shift, so that the 32nd bit is properly shifted to the + // 31st, and the 32nd becomes unset. + result >>>= 1; + if (!negative) { + return result; + } + + // We need to OR here to ensure the 32nd bit (the sign bit in an Int32) is + // always set for negative numbers. If `result` were 1, (meaning `negate` is + // true and all other bits were zeros), `result` would now be 0. But -0 + // doesn't flip the 32nd bit as intended. All other numbers will successfully + // set the 32nd bit without issue, so doing this is a noop for them. + return -result | (1 << 31); +} + +/** + * @param {SourceMapV3} payload + * @return {SourceMapV3} + */ +function cloneSourceMapV3(payload) { + payload = { ...payload }; + for (const key in payload) { + if (Object.prototype.hasOwnProperty.call(payload, key) && + Array.isArray(payload[key])) { + payload[key] = payload[key].slice(); + } + } + return payload; +} + +/** + * @param {Array} entry1 source map entry [lineNumber, columnNumber, sourceURL, + * sourceLineNumber, sourceColumnNumber] + * @param {Array} entry2 source map entry. + * @return {number} + */ +function compareSourceMapEntry(entry1, entry2) { + const { 0: lineNumber1, 1: columnNumber1 } = entry1; + const { 0: lineNumber2, 1: columnNumber2 } = entry2; + if (lineNumber1 !== lineNumber2) { + return lineNumber1 - lineNumber2; + } + return columnNumber1 - columnNumber2; +} + +result = function(error, trace, sourceMap) { + try { + const payload = JSON.parse(sourceMap) + const sm = new SourceMap(payload) + const preparedStack = trace.map((t, i) => { + const str = i !== 0 ? '\n at ' : ''; + const { + originalLine, + originalColumn, + originalSource, + } = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); + if (originalSource && originalLine !== undefined && + originalColumn !== undefined) { + // A stack trace will often have several call sites in a row within the + // same file, cache the source map and file content accordingly: + let fileName = t.getFileName(); + if (fileName === undefined) { + fileName = t.getEvalOrigin(); + } + const fnName = t.getFunctionName() ?? t.getMethodName(); + const typeName = t.getTypeName(); + const namePrefix = typeName !== null && typeName !== 'global' ? `${typeName}.` : ''; + const originalName = `${namePrefix}${fnName || ''}`; + const hasName = !!originalName; + return `${str}${originalName}${hasName ? ' (' : ''}` + + `${originalSource}:${originalLine + 1}:` + + `${originalColumn + 1}${hasName ? ')' : ''}`; + } + return `${str}${t}` + }).join(''); + return `${error}\n at ${preparedStack}` + } catch(e) { + const originStack = trace.map((t, i) => { + const str = i !== 0 ? '\n at ' : ''; + return `${str}${t}` + }).join('') + return `${error}\n at ${originStack}` + } +} +)JS"; diff --git a/jsvm/src/type_conversion.h b/jsvm/src/type_conversion.h new file mode 100644 index 0000000000000000000000000000000000000000..9d614c7ba607cc4ddbb5c5ecc126b3e397513523 --- /dev/null +++ b/jsvm/src/type_conversion.h @@ -0,0 +1,110 @@ +#ifndef JSVM_VALUE_H +#define JSVM_VALUE_H +#include + +#include "jsvm_util.h" + +struct JSVM_Data__ { +public: + using SourcePtr = std::variant, v8::Global>; + using DataType = enum { kJsvmScript }; + + template + JSVM_Data__(T ptr, bool retained, DataType type = kJsvmScript) : taggedPointer(ptr), isGlobal(retained), type(type) + {} + + template + v8::Local ToV8Local(v8::Isolate* isolate) + { + if (isGlobal) { + return v8::Local::New(isolate, std::get>(taggedPointer)); + } else { + return std::get>(taggedPointer); + } + } + + SourcePtr taggedPointer; + bool isGlobal = false; + DataType type; +}; + +namespace v8impl { +//=== Conversion between V8 Handles and JSVM_Value ======================== + +// This asserts v8::Local<> will always be implemented with a single +// pointer field so that we can pass it around as a void*. +static_assert(sizeof(v8::Local) == sizeof(JSVM_Value), + "Cannot convert between v8::Local and JSVM_Value"); + +inline JSVM_Value JsValueFromV8LocalValue(v8::Local local) +{ + return reinterpret_cast(*local); +} + +inline v8::Local V8LocalValueFromJsValue(JSVM_Value v) +{ + v8::Local local; + memcpy(static_cast(&local), &v, sizeof(v)); + return local; +} + +inline JSVM_Deferred JsDeferredFromPersistent(v8impl::Persistent* local) +{ + return reinterpret_cast(local); +} + +inline v8impl::Persistent* PersistentFromJsDeferred(JSVM_Deferred local) +{ + return reinterpret_cast*>(local); +} + +class HandleScopeWrapper { +public: + explicit HandleScopeWrapper(v8::Isolate* isolate) : scope(isolate) {} + +private: + v8::HandleScope scope; +}; + +class EscapableHandleScopeWrapper { +public: + explicit EscapableHandleScopeWrapper(v8::Isolate* isolate) : scope(isolate), escapeCalled(false) {} + bool IsEscapeCalled() const + { + return escapeCalled; + } + template + v8::Local Escape(v8::Local handle) + { + escapeCalled = true; + return scope.Escape(handle); + } + +private: + v8::EscapableHandleScope scope; + bool escapeCalled; +}; + +inline JSVM_HandleScope JsHandleScopeFromV8HandleScope(HandleScopeWrapper* s) +{ + return reinterpret_cast(s); +} + +inline HandleScopeWrapper* V8HandleScopeFromJsHandleScope(JSVM_HandleScope s) +{ + return reinterpret_cast(s); +} + +inline JSVM_EscapableHandleScope JsEscapableHandleScopeFromV8EscapableHandleScope(EscapableHandleScopeWrapper* s) +{ + return reinterpret_cast(s); +} + +inline EscapableHandleScopeWrapper* V8EscapableHandleScopeFromJsEscapableHandleScope(JSVM_EscapableHandleScope s) +{ + return reinterpret_cast(s); +} + +} // namespace v8impl + +#endif \ No newline at end of file diff --git a/node.gyp b/node.gyp index 2549192534c97bc250a86d1fd57a4073bc1411d6..2838088224844f617582e7340047f64c02327b0e 100644 --- a/node.gyp +++ b/node.gyp @@ -1,8 +1,9 @@ { 'variables': { + 'deps': '../', 'v8_use_siphash%': 0, 'v8_trace_maps%': 0, - 'v8_enable_pointer_compression%': 0, + 'v8_enable_pointer_compression%': 1, 'v8_enable_31bit_smis_on_64bit_arch%': 0, 'node_use_dtrace%': 'false', 'node_use_etw%': 'false', @@ -43,17 +44,17 @@ '<@(linked_module_files)', ], 'deps_files': [ - 'deps/v8/tools/splaytree.mjs', - 'deps/v8/tools/codemap.mjs', - 'deps/v8/tools/consarray.mjs', - 'deps/v8/tools/csvparser.mjs', - 'deps/v8/tools/profile.mjs', - 'deps/v8/tools/profile_view.mjs', - 'deps/v8/tools/logreader.mjs', - 'deps/v8/tools/arguments.mjs', - 'deps/v8/tools/tickprocessor.mjs', - 'deps/v8/tools/sourcemap.mjs', - 'deps/v8/tools/tickprocessor-driver.mjs', + '<(deps)/v8/tools/splaytree.mjs', + '<(deps)/v8/tools/codemap.mjs', + '<(deps)/v8/tools/consarray.mjs', + '<(deps)/v8/tools/csvparser.mjs', + '<(deps)/v8/tools/profile.mjs', + '<(deps)/v8/tools/profile_view.mjs', + '<(deps)/v8/tools/logreader.mjs', + '<(deps)/v8/tools/arguments.mjs', + '<(deps)/v8/tools/tickprocessor.mjs', + '<(deps)/v8/tools/sourcemap.mjs', + '<(deps)/v8/tools/tickprocessor-driver.mjs', 'deps/acorn/acorn/dist/acorn.js', 'deps/acorn/acorn-walk/dist/walk.js', 'deps/minimatch/index.js', @@ -159,7 +160,7 @@ 'include_dirs': [ 'src', - 'deps/v8/include', + '<(deps)/v8/include', 'deps/postject' ], @@ -459,537 +460,85 @@ { 'target_name': '<(node_lib_target_name)', 'type': '<(node_intermediate_lib_type)', - 'includes': [ - 'node.gypi', - ], - - 'include_dirs': [ - 'src', - 'deps/postject', - '../../base/startup/init/interfaces/innerkits/include/param', - '../../base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent/include', - '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h - ], 'dependencies': [ - 'deps/base64/base64.gyp:base64', - 'deps/googletest/googletest.gyp:gtest_prod', - 'deps/histogram/histogram.gyp:histogram', - 'deps/uvwasi/uvwasi.gyp:uvwasi', - 'deps/simdutf/simdutf.gyp:simdutf', - 'deps/ada/ada.gyp:ada', + 'deps/llhttp/llhttp.gyp:llhttp', ], 'sources': [ - 'src/api/async_resource.cc', - 'src/api/callback.cc', - 'src/api/embed_helpers.cc', - 'src/api/encoding.cc', - 'src/api/environment.cc', - 'src/api/exceptions.cc', - 'src/api/hooks.cc', - 'src/api/utils.cc', - 'src/async_wrap.cc', - 'src/base_object.cc', - 'src/cares_wrap.cc', - 'src/cleanup_queue.cc', - 'src/connect_wrap.cc', - 'src/connection_wrap.cc', - 'src/debug_utils.cc', - 'src/env.cc', - 'src/fs_event_wrap.cc', - 'src/handle_wrap.cc', - 'src/heap_utils.cc', - 'src/histogram.cc', - 'src/jsvm.h', - 'src/jsvm_types.h', - 'src/js_native_api_v8.cc', - 'src/js_native_api_v8.h', - 'src/js_native_api_v8_inspector.cc', - 'src/js_native_api_v8_inspector.h', - 'src/js_native_api_v8_internals.h', - 'src/js_stream.cc', - 'src/json_utils.cc', - 'src/js_udp_wrap.cc', - 'src/module_wrap.cc', - 'src/node.cc', - 'src/node_api.cc', - 'src/node_binding.cc', - 'src/node_blob.cc', - 'src/node_buffer.cc', - 'src/node_builtins.cc', - 'src/node_config.cc', - 'src/node_constants.cc', - 'src/node_contextify.cc', - 'src/node_credentials.cc', - 'src/node_dir.cc', - 'src/node_env_var.cc', - 'src/node_errors.cc', - 'src/node_external_reference.cc', - 'src/node_file.cc', - 'src/node_http_parser.cc', - 'src/node_http2.cc', - 'src/node_i18n.cc', - 'src/node_main_instance.cc', - 'src/node_messaging.cc', - 'src/node_metadata.cc', - 'src/node_options.cc', - 'src/node_os.cc', - 'src/node_perf.cc', - 'src/node_platform.cc', - 'src/node_postmortem_metadata.cc', - 'src/node_process_events.cc', - 'src/node_process_methods.cc', - 'src/node_process_object.cc', - 'src/node_realm.cc', - 'src/node_report.cc', - 'src/node_report_module.cc', - 'src/node_report_utils.cc', - 'src/node_sea.cc', - 'src/node_serdes.cc', - 'src/node_shadow_realm.cc', - 'src/node_snapshotable.cc', - 'src/node_sockaddr.cc', - 'src/node_stat_watcher.cc', - 'src/node_symbols.cc', - 'src/node_task_queue.cc', - 'src/node_trace_events.cc', - 'src/node_types.cc', - 'src/node_url.cc', - 'src/node_util.cc', - 'src/node_v8.cc', - 'src/node_wasi.cc', - 'src/node_wasm_web_api.cc', - 'src/node_watchdog.cc', - 'src/node_worker.cc', - 'src/node_zlib.cc', - 'src/pipe_wrap.cc', - 'src/process_wrap.cc', - 'src/signal_wrap.cc', - 'src/spawn_sync.cc', - 'src/stream_base.cc', - 'src/stream_pipe.cc', - 'src/stream_wrap.cc', - 'src/string_bytes.cc', - 'src/string_decoder.cc', - 'src/tcp_wrap.cc', - 'src/timers.cc', - 'src/timer_wrap.cc', - 'src/tracing/agent.cc', - 'src/tracing/node_trace_buffer.cc', - 'src/tracing/node_trace_writer.cc', - 'src/tracing/trace_event.cc', - 'src/tracing/traced_value.cc', - 'src/tty_wrap.cc', - 'src/udp_wrap.cc', - 'src/util.cc', - 'src/uv.cc', - # headers to make for a more pleasant IDE experience - 'src/aliased_buffer.h', - 'src/aliased_buffer-inl.h', - 'src/aliased_struct.h', - 'src/aliased_struct-inl.h', - 'src/async_wrap.h', - 'src/async_wrap-inl.h', - 'src/base_object.h', - 'src/base_object-inl.h', - 'src/base_object_types.h', - 'src/base64.h', - 'src/base64-inl.h', - 'src/callback_queue.h', - 'src/callback_queue-inl.h', - 'src/cleanup_queue.h', - 'src/cleanup_queue-inl.h', - 'src/connect_wrap.h', - 'src/connection_wrap.h', - 'src/debug_utils.h', - 'src/debug_utils-inl.h', - 'src/env_properties.h', - 'src/env.h', - 'src/env-inl.h', - 'src/handle_wrap.h', - 'src/histogram.h', - 'src/histogram-inl.h', - 'src/js_stream.h', - 'src/json_utils.h', - 'src/large_pages/node_large_page.cc', - 'src/large_pages/node_large_page.h', - 'src/memory_tracker.h', - 'src/memory_tracker-inl.h', - 'src/module_wrap.h', - 'src/node.h', - 'src/jsvm_node_api.h', - 'src/jsvm_node_api_types.h', - 'src/node_binding.h', - 'src/node_blob.h', - 'src/node_buffer.h', - 'src/node_builtins.h', - 'src/node_constants.h', - 'src/node_context_data.h', - 'src/node_contextify.h', - 'src/node_dir.h', - 'src/node_errors.h', - 'src/node_external_reference.h', - 'src/node_file.h', - 'src/node_file-inl.h', - 'src/node_http_common.h', - 'src/node_http_common-inl.h', - 'src/node_http2.h', - 'src/node_http2_state.h', - 'src/node_i18n.h', - 'src/node_internals.h', - 'src/node_main_instance.h', - 'src/node_mem.h', - 'src/node_mem-inl.h', - 'src/node_messaging.h', - 'src/node_metadata.h', - 'src/node_mutex.h', - 'src/node_object_wrap.h', - 'src/node_options.h', - 'src/node_options-inl.h', - 'src/node_perf.h', - 'src/node_perf_common.h', - 'src/node_platform.h', - 'src/node_process.h', - 'src/node_process-inl.h', - 'src/node_realm.h', - 'src/node_realm-inl.h', - 'src/node_report.h', - 'src/node_revert.h', - 'src/node_root_certs.h', - 'src/node_sea.h', - 'src/node_shadow_realm.h', - 'src/node_snapshotable.h', - 'src/node_snapshot_builder.h', - 'src/node_sockaddr.h', - 'src/node_sockaddr-inl.h', - 'src/node_stat_watcher.h', - 'src/node_union_bytes.h', - 'src/node_url.h', - 'src/node_util.h', - 'src/node_version.h', - 'src/node_v8.h', - 'src/node_v8_platform-inl.h', - 'src/node_wasi.h', - 'src/node_watchdog.h', - 'src/node_worker.h', - 'src/pipe_wrap.h', - 'src/req_wrap.h', - 'src/req_wrap-inl.h', - 'src/spawn_sync.h', - 'src/stream_base.h', - 'src/stream_base-inl.h', - 'src/stream_pipe.h', - 'src/stream_wrap.h', - 'src/string_bytes.h', - 'src/string_decoder.h', - 'src/string_decoder-inl.h', - 'src/string_search.h', - 'src/tcp_wrap.h', - 'src/tracing/agent.h', - 'src/tracing/node_trace_buffer.h', - 'src/tracing/node_trace_writer.h', - 'src/tracing/trace_event.h', - 'src/tracing/trace_event_common.h', - 'src/tracing/traced_value.h', - 'src/timer_wrap.h', - 'src/timer_wrap-inl.h', - 'src/tty_wrap.h', - 'src/udp_wrap.h', - 'src/util.h', - 'src/util-inl.h', + 'jsvm/src/platform/platform_ohos.cc', + 'jsvm/src/inspector/js_native_api_v8_inspector.cc', + 'jsvm/src/inspector/inspector_socket_server.cc', + 'jsvm/src/inspector/inspector_socket.cc', + 'jsvm/src/inspector/inspector_utils.cpp', + 'jsvm/src/js_native_api_v8.cc', + 'jsvm/src/jsvm_env.cc', + 'jsvm/src/jsvm_reference.cpp', # Dependency headers - 'deps/v8/include/v8.h', + '<(deps)/v8/include/v8.h', 'deps/postject/postject-api.h' - # javascript files to make for an even more pleasant IDE experience - '<@(library_files)', - '<@(deps_files)', - # node.gyp is added by default, common.gypi is added for change detection - 'common.gypi', ], - 'variables': { - 'openssl_system_ca_path%': '', - 'openssl_default_cipher_list%': '', - }, - 'cflags': ['-Werror=unused-result'], + 'cflags': [ + '-fstack-protector-strong', + '--target=aarch64-linux-ohos', + '-march=armv8-a', + '-mfpu=neon', + '-m64', + '-msign-return-address=all', + '-mbranch-protection=pac-ret+b-key+bti', + '-pthread', + '-Wall', + '-Wextra', + '-Wno-unused-parameter', + '-fPIC', + '-Werror=unused-result', + '-O3', + '-fno-omit-frame-pointer', + '-fno-rtti', + '-fno-exceptions', + '-std=gnu++17', + '-fvisibility=hidden' + ], 'defines': [ - 'NODE_ARCH="<(target_arch)"', - 'NODE_PLATFORM="<(OS)"', - 'NODE_WANT_INTERNALS=1', - # Warn when using deprecated V8 APIs. - 'V8_DEPRECATION_WARNINGS=1', - 'NODE_OPENSSL_SYSTEM_CERT_PATH="<(openssl_system_ca_path)"', + '_GLIBCXX_USE_CXX11_ABI=1', + '__STDC_FORMAT_MACROS', + '__POSIX__', 'TARGET_OHOS', 'ENABLE_HISYSEVENT', + 'ENABLE_INSPECTOR', + 'HAVE_OPENSSL=1' + ], + 'include_dirs': [ + 'jsvm/interface/innerkits', + 'jsvm/interface/kits', + 'jsvm/src', + '<(NDK_SYS_ROOT)/../../../base/hiviewdfx/hisysevent/interfaces/native/innerkits/hisysevent/include', + '<(NDK_SYS_ROOT)/../../../base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter', + '<(NDK_SYS_ROOT)/../../../base/hiviewdfx/hilog/interfaces/native/innerkits/include', + '<(NDK_SYS_ROOT)/../../../base/startup/init/interfaces/innerkits/include/param', + '<(NDK_SYS_ROOT)/../../../third_party/libuv/include', + '<(NDK_SYS_ROOT)/../../../third_party/zlib/include', + '<(NDK_SYS_ROOT)/../../../third_party/icu/icu4c/source/common', + '<(NDK_SYS_ROOT)/../../../third_party/zlib', + '<(NDK_SYS_ROOT)/obj/third_party/openssl/build_all_generated/linux-aarch64/include', + '../openssl/include', + '<(obj_dir)/../../../v8-include' ], - - # - "C4244: conversion from 'type1' to 'type2', possible loss of data" - # Ususaly safe. Disable for `dep`, enable for `src` - 'msvs_disabled_warnings!': [4244], - 'ldflags' : [ + '-fvisibility=hidden', '<(NDK_SYS_ROOT)/resourceschedule/resource_schedule_service/libressched_client.z.so', - '<(NDK_SYS_ROOT)/startup/init/libbegetutil.z.so' - ], - 'conditions': [ - [ 'openssl_default_cipher_list!=""', { - 'defines': [ - 'NODE_OPENSSL_DEFAULT_CIPHER_LIST="<(openssl_default_cipher_list)"' - ] - }], - [ 'error_on_warn=="true"', { - 'cflags': ['-Werror'], - 'xcode_settings': { - 'WARNING_CFLAGS': [ '-Werror' ], - }, - }], - [ 'node_builtin_modules_path!=""', { - 'defines': [ 'NODE_BUILTIN_MODULES_PATH="<(node_builtin_modules_path)"' ] - }], - [ 'node_shared=="true"', { - 'sources': [ - 'src/node_snapshot_stub.cc', - ] - }], - [ 'node_shared=="true" and node_module_version!="" and OS!="win"', { - 'product_extension': '<(shlib_suffix)', - 'xcode_settings': { - 'LD_DYLIB_INSTALL_NAME': - '@rpath/lib<(node_core_target_name).<(shlib_suffix)' - }, - }], - [ 'node_use_node_code_cache=="true"', { - 'defines': [ - 'NODE_USE_NODE_CODE_CACHE=1', - ], - }], - ['node_shared=="true" and OS in "aix os400"', { - 'product_name': 'node_base', - }], - [ 'v8_enable_inspector==1', { - 'includes' : [ 'src/inspector/node_inspector.gypi' ], - }, { - 'defines': [ 'HAVE_INSPECTOR=0' ] - }], - [ 'OS=="win"', { - 'conditions': [ - [ 'node_intermediate_lib_type!="static_library"', { - 'sources': [ - 'src/res/node.rc', - ], - }], - ], - 'libraries': [ - 'Dbghelp', - 'Psapi', - 'Winmm', - 'Ws2_32', - ], - }], - [ 'node_use_etw=="true"', { - 'defines': [ 'HAVE_ETW=1' ], - 'dependencies': [ 'node_etw' ], - 'include_dirs': [ - 'src', - 'tools/msvs/genfiles', - '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h - ], - 'sources': [ - 'src/node_win32_etw_provider.h', - 'src/node_win32_etw_provider-inl.h', - 'src/node_win32_etw_provider.cc', - 'src/node_dtrace.h', - 'src/node_dtrace.cc', - 'tools/msvs/genfiles/node_etw_provider.h', - ], - 'conditions': [ - ['node_intermediate_lib_type != "static_library"', { - 'sources': [ - 'tools/msvs/genfiles/node_etw_provider.rc', - ], - }], - ], - }], - [ 'node_use_dtrace=="true"', { - 'defines': [ 'HAVE_DTRACE=1' ], - 'dependencies': [ - 'node_dtrace_header', - 'specialize_node_d', - ], - 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)' ], - # - # DTrace is supported on linux, solaris, mac, and bsd. There are - # three object files associated with DTrace support, but they're - # not all used all the time: - # - # node_dtrace.o all configurations - # node_dtrace_ustack.o not supported on mac and linux - # node_dtrace_provider.o All except OS X. "dtrace -G" is not - # used on OS X. - # - # Note that node_dtrace_provider.cc and node_dtrace_ustack.cc do not - # actually exist. They're listed here to trick GYP into linking the - # corresponding object files into the final "node" executable. These - # object files are generated by "dtrace -G" using custom actions - # below, and the GYP-generated Makefiles will properly build them when - # needed. - # - 'sources': [ - 'src/node_dtrace.h', - 'src/node_dtrace.cc', - ], - 'conditions': [ - [ 'OS=="linux"', { - 'sources': [ - '<(SHARED_INTERMEDIATE_DIR)/node_dtrace_provider.o' - ], - }], - [ 'OS!="mac" and OS!="linux"', { - 'sources': [ - 'src/node_dtrace_ustack.cc', - 'src/node_dtrace_provider.cc', - ] - } - ] ] - } ], - [ 'node_use_openssl=="true"', { - 'sources': [ - 'src/crypto/crypto_aes.cc', - 'src/crypto/crypto_bio.cc', - 'src/crypto/crypto_common.cc', - 'src/crypto/crypto_dsa.cc', - 'src/crypto/crypto_hkdf.cc', - 'src/crypto/crypto_pbkdf2.cc', - 'src/crypto/crypto_sig.cc', - 'src/crypto/crypto_timing.cc', - 'src/crypto/crypto_cipher.cc', - 'src/crypto/crypto_context.cc', - 'src/crypto/crypto_ec.cc', - 'src/crypto/crypto_hmac.cc', - 'src/crypto/crypto_random.cc', - 'src/crypto/crypto_rsa.cc', - 'src/crypto/crypto_spkac.cc', - 'src/crypto/crypto_util.cc', - 'src/crypto/crypto_clienthello.cc', - 'src/crypto/crypto_dh.cc', - 'src/crypto/crypto_hash.cc', - 'src/crypto/crypto_keys.cc', - 'src/crypto/crypto_keygen.cc', - 'src/crypto/crypto_scrypt.cc', - 'src/crypto/crypto_tls.cc', - 'src/crypto/crypto_aes.cc', - 'src/crypto/crypto_x509.cc', - 'src/crypto/crypto_bio.h', - 'src/crypto/crypto_clienthello-inl.h', - 'src/crypto/crypto_dh.h', - 'src/crypto/crypto_hmac.h', - 'src/crypto/crypto_rsa.h', - 'src/crypto/crypto_spkac.h', - 'src/crypto/crypto_util.h', - 'src/crypto/crypto_cipher.h', - 'src/crypto/crypto_common.h', - 'src/crypto/crypto_dsa.h', - 'src/crypto/crypto_hash.h', - 'src/crypto/crypto_keys.h', - 'src/crypto/crypto_keygen.h', - 'src/crypto/crypto_scrypt.h', - 'src/crypto/crypto_tls.h', - 'src/crypto/crypto_clienthello.h', - 'src/crypto/crypto_context.h', - 'src/crypto/crypto_ec.h', - 'src/crypto/crypto_hkdf.h', - 'src/crypto/crypto_pbkdf2.h', - 'src/crypto/crypto_sig.h', - 'src/crypto/crypto_random.h', - 'src/crypto/crypto_timing.h', - 'src/crypto/crypto_x509.h', - 'src/node_crypto.cc', - 'src/node_crypto.h' - ], - }], - [ 'OS in "linux freebsd mac solaris" and ' - 'target_arch=="x64" and ' - 'node_target_type=="executable"', { - 'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ], - }], - [ 'use_openssl_def==1', { - # TODO(bnoordhuis) Make all platforms export the same list of symbols. - # Teach mkssldef.py to generate linker maps that UNIX linkers understand. - 'variables': { - 'mkssldef_flags': [ - # Categories to export. - '-CAES,BF,BIO,DES,DH,DSA,EC,ECDH,ECDSA,ENGINE,EVP,HMAC,MD4,MD5,' - 'PSK,RC2,RC4,RSA,SHA,SHA0,SHA1,SHA256,SHA512,SOCK,STDIO,TLSEXT,' - 'UI,FP_API,TLS1_METHOD,TLS1_1_METHOD,TLS1_2_METHOD,SCRYPT,OCSP,' - 'NEXTPROTONEG,RMD160,CAST,DEPRECATEDIN_1_1_0,DEPRECATEDIN_1_2_0,' - 'DEPRECATEDIN_3_0', - # Defines. - '-DWIN32', - # Symbols to filter from the export list. - '-X^DSO', - '-X^_', - '-X^private_', - # Base generated DEF on zlib.def - '-Bdeps/zlib/win32/zlib.def' - ], - }, - 'conditions': [ - ['openssl_is_fips!=""', { - 'variables': { 'mkssldef_flags': ['-DOPENSSL_FIPS'] }, - }], - ], - 'actions': [ - { - 'action_name': 'mkssldef', - 'inputs': [ - 'deps/openssl/openssl/util/libcrypto.num', - 'deps/openssl/openssl/util/libssl.num', - ], - 'outputs': ['<(SHARED_INTERMEDIATE_DIR)/openssl.def'], - 'process_outputs_as_sources': 1, - 'action': [ - '<(python)', - 'tools/mkssldef.py', - '<@(mkssldef_flags)', - '-o', - '<@(_outputs)', - '<@(_inputs)', - ], - }, - ], - }], - [ 'debug_nghttp2==1', { - 'defines': [ 'NODE_DEBUG_NGHTTP2=1' ] - }], - ], - 'actions': [ - { - 'action_name': 'node_js2c', - 'process_outputs_as_sources': 1, - 'inputs': [ - # Put the code first so it's a dependency and can be used for invocation. - 'tools/js2c.py', - '<@(library_files)', - '<@(deps_files)', - 'config.gypi' - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc', - ], - 'action': [ - '<(python)', - 'tools/js2c.py', - '--directory', - 'lib', - '--target', - '<@(_outputs)', - 'config.gypi', - '<@(deps_files)', - '<@(linked_module_files)', - ], - }, + '<(NDK_SYS_ROOT)/startup/init/libbegetutil.z.so', + '<(NDK_SYS_ROOT)/hiviewdfx/hilog_override/libhilog.so', + '<(NDK_SYS_ROOT)/hiviewdfx/hitrace/libhitrace_meter.so', + '<(NDK_SYS_ROOT)/thirdparty/libuv/libuv.so', + '<(NDK_SYS_ROOT)/obj/third_party/zlib/libz.a', + '<(NDK_SYS_ROOT)/thirdparty/icu/libhmicuuc.z.so', + '<(NDK_SYS_ROOT)/thirdparty/icu/libhmicui18n.z.so', + '<(NDK_SYS_ROOT)/thirdparty/openssl/libcrypto_openssl.z.so', + '<(NDK_SYS_ROOT)/thirdparty/openssl/libssl_openssl.z.so', + '<(obj_dir)/../../../libv8_shared.so', ], }, # node_lib_target_name { @@ -1198,7 +747,7 @@ 'include_dirs': [ 'src', 'tools/msvs/genfiles', - 'deps/v8/include', + '<(deps)/v8/include', 'deps/cares/include', 'deps/uv/include', 'deps/uvwasi/include', @@ -1252,7 +801,7 @@ # 'include_dirs': [ # 'src', # 'tools/msvs/genfiles', - # 'deps/v8/include', + # '<(deps)/v8/include', # 'deps/cares/include', # 'deps/uv/include', # 'deps/uvwasi/include', @@ -1356,7 +905,7 @@ # 'include_dirs': [ # 'src', # 'tools/msvs/genfiles', - # 'deps/v8/include', + # '<(deps)/v8/include', # 'deps/cares/include', # 'deps/uv/include', # 'deps/uvwasi/include', @@ -1434,7 +983,7 @@ 'include_dirs': [ 'src', 'tools/msvs/genfiles', - 'deps/v8/include', + '<(deps)/v8/include', 'deps/cares/include', 'deps/uv/include', 'deps/uvwasi/include', @@ -1445,8 +994,7 @@ 'ldflags' : [ '<(NDK_SYS_ROOT)/resourceschedule/resource_schedule_service/libressched_client.z.so', - '<(NDK_SYS_ROOT)/startup/init/libbegetutil.z.so', - '<(NDK_SYS_ROOT)/hiviewdfx/hisysevent/libhisysevent.z.so' + '<(NDK_SYS_ROOT)/startup/init/libbegetutil.z.so' ], 'sources': [ @@ -1500,7 +1048,7 @@ 'dependencies': ['<(node_lib_target_name)'], 'include_dirs': [ 'src', - 'deps/v8/include', + '<(deps)/v8/include', ], 'sources': [ '<@(library_files)', diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index a61038398dde4a2caf40b949197f3c2fbf4108b5..8c31a6835526b884b2ba5ffb0c516704627368ab 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -10,12 +10,14 @@ #include "v8-primitive.h" #include "v8-statistics.h" #include "v8-version-string.h" +#include "v8-proxy.h" #define JSVM_EXPERIMENTAL #include "env-inl.h" #include "jsvm.h" #include "js_native_api_v8.h" #include "js_native_api_v8_inspector.h" #include "libplatform/libplatform.h" +#include "libplatform/v8-tracing.h" #include "util-inl.h" #include "util.h" #include "sourcemap.def" @@ -24,6 +26,10 @@ #include "hisysevent.h" #endif +#ifdef V8_USE_PERFETTO +#error Unsupported Perfetto. +#endif // V8_USE_PERFETTO + #define SECARGCNT 2 #define CHECK_MAYBE_NOTHING(env, maybe, status) \ @@ -143,12 +149,65 @@ namespace { enum IsolateDataSlot { kIsolateData = 0, kIsolateSnapshotCreatorSlot = 1, + kIsolateHandlerPoolSlot = 2, +}; +// Always compare the final element of IsolateDataSlot with v8 limit. +static_assert(kIsolateHandlerPoolSlot < v8::internal::Internals::kNumIsolateDataSlots); + +struct GCHandlerWrapper { + GCHandlerWrapper(JSVM_GCType gcType, JSVM_HandlerForGC handler, void* userData) + : gcType(gcType), handler(handler), userData(userData) {} + + JSVM_GCType gcType; + JSVM_HandlerForGC handler; + void *userData; +}; + +using GCHandlerWrappers = std::list; + +struct IsolateHandlerPool { + GCHandlerWrappers handlerWrappersBeforeGC; + GCHandlerWrappers handlerWrappersAfterGC; + JSVM_HandlerForOOMError handlerForOOMError = nullptr; + JSVM_HandlerForFatalError handlerForFatalError = nullptr; + JSVM_HandlerForPromiseReject handlerForPromiseReject = nullptr; + + ~IsolateHandlerPool() { + for (auto *handler : handlerWrappersBeforeGC) { + delete handler; + } + handlerWrappersBeforeGC.clear(); + for (auto *handler : handlerWrappersAfterGC) { + delete handler; + } + handlerWrappersAfterGC.clear(); + } }; +static IsolateHandlerPool* GetIsolateHandlerPool(v8::Isolate* isolate) { + auto pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + return reinterpret_cast(pool); +} + +static IsolateHandlerPool* GetOrCreateIsolateHandlerPool(v8::Isolate* isolate) { + auto *pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + if (pool != nullptr) { + return reinterpret_cast(pool); + } + auto *createdPool = new v8impl::IsolateHandlerPool(); + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, createdPool); + return createdPool; +} + enum ContextEmbedderIndex { kContextEnvIndex = 1, }; +static JSVM_Env GetEnvByContext(v8::Local context) { + auto env = context->GetAlignedPointerFromEmbedderData(v8impl::kContextEnvIndex); + return reinterpret_cast(env); +} + struct IsolateData { IsolateData(v8::StartupData* blob) : blob(blob) {} @@ -239,6 +298,23 @@ static v8::ArrayBuffer::Allocator *GetOrCreateDefaultArrayBufferAllocator() { return defaultArrayBufferAllocator.get(); } +static std::unique_ptr g_trace_stream; + +constexpr uint32_t g_trace_catrgory_count = 7; +static constexpr const char* g_internal_trace_categories[] = { + "v8", + TRACE_DISABLED_BY_DEFAULT("v8.compile"), + "v8.execute", + TRACE_DISABLED_BY_DEFAULT("v8.runtime"), + TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), + "v8.wasm", + TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), +}; + +constexpr uint32_t g_default_catrgory_count = 4; +static constexpr JSVM_TraceCategory g_default_categories[] = { + JSVM_TRACE_VM, JSVM_TRACE_EXECUTE, JSVM_TRACE_COMPILE, JSVM_TRACE_RUNTIME }; + static void SetFileToSourceMapMapping(std::string &&file, std::string &&sourceMapUrl) { auto it = sourceMapUrlMap.find(file); if (it == sourceMapUrlMap.end()) { @@ -728,7 +804,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -761,7 +837,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; @@ -792,7 +868,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -826,7 +902,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -859,7 +935,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -892,7 +968,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } JSVM_Value result = nullptr; @@ -924,7 +1000,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -958,7 +1034,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -1117,7 +1193,11 @@ inline JSVM_Status Wrap(JSVM_Env env, // Currently, V8 has no API to detect if a symbol is local or global. // Until we have a V8 API for it, we consider that all symbols can be weak. // This matches the current Node-API behavior. -inline bool CanBeHeldWeakly(v8::Local value) { +inline bool CanBeHeldWeakly(v8::Local data) { + if (data->IsPrivate()) { + return false; + } + auto value = data.As(); return value->IsObject() || value->IsSymbol(); } @@ -1233,10 +1313,11 @@ void RefBase::Finalize() { } template -Reference::Reference(JSVM_Env env, v8::Local value, Args&&... args) +Reference::Reference(JSVM_Env env, v8::Local data, bool isValue, Args&&... args) : RefBase(env, std::forward(args)...), - persistent_(env->isolate, value), - can_be_weak_(CanBeHeldWeakly(value)), + persistent_(env->isolate, data), + is_value(isValue), + can_be_weak_(isValue && CanBeHeldWeakly(data.As())), deleted_by_user(false), wait_callback(false) { if (RefCount() == 0) { @@ -1250,14 +1331,16 @@ Reference::~Reference() { } Reference* Reference::New(JSVM_Env env, - v8::Local value, + v8::Local data, uint32_t initialRefcount, Ownership ownership, JSVM_Finalize finalizeCallback, void* finalizeData, - void* finalizeHint) { + void* finalizeHint, + bool isValue) { return new Reference(env, - value, + data, + isValue, initialRefcount, ownership, finalizeCallback, @@ -1293,11 +1376,20 @@ uint32_t Reference::Unref() { return refcount; } -v8::Local Reference::Get() { +v8::Local Reference::GetValue() { + DCHECK(is_value); if (persistent_.IsEmpty()) { return v8::Local(); } else { - return v8::Local::New(env_->isolate, persistent_); + return v8::Local::New(env_->isolate, persistent_).As(); + } +} + +v8::Local Reference::GetData() { + if (persistent_.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(env_->isolate, persistent_); } } @@ -1394,6 +1486,171 @@ static bool ProcessBundleName(std::string& bundleName) return true; } +JSVM_Status OH_JSVM_IsBooleanObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsBooleanObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsBigIntObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsBigIntObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsStringObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsStringObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsNumberObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsNumberObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsSymbolObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsSymbolObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolToStringTag(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolToStringTag = v8::Symbol::GetToStringTag(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolToStringTag); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolIterator(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolIterator = v8::Symbol::GetIterator(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolIterator); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolAsyncIterator(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolAsyncIterator = v8::Symbol::GetAsyncIterator(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolAsyncIterator); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolHasInstance(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolHasInstance = v8::Symbol::GetHasInstance(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolHasInstance); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolUnscopables(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolUnscopables = v8::Symbol::GetUnscopables(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolUnscopables); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolIsConcatSpreadable(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolIsConcatSpreadable = v8::Symbol::GetIsConcatSpreadable(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolIsConcatSpreadable); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolMatch(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolMatch = v8::Symbol::GetMatch(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolMatch); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolReplace(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolReplace = v8::Symbol::GetReplace(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolReplace); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolSearch(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolSearch = v8::Symbol::GetSearch(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolSearch); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolSplit(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolSplit = v8::Symbol::GetSplit(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolSplit); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolToPrimitive(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolToPrimitive = v8::Symbol::GetToPrimitive(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolToPrimitive); + + return jsvm_clear_last_error(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_Init(const JSVM_InitOptions* options) { static std::atomic initialized(false); @@ -1416,11 +1673,13 @@ OH_JSVM_Init(const JSVM_InitOptions* options) { #endif v8::V8::InitializePlatform(v8impl::g_platform.get()); +#ifdef TARGET_OHOS if (node::ReadSystemXpmState()) { int secArgc = SECARGCNT; char *secArgv[SECARGCNT] = {const_cast("jsvm"), const_cast("--jitless")}; v8::V8::SetFlagsFromCommandLine(&secArgc, secArgv, false); } +#endif if (options && options->argc && options->argv) { v8::V8::SetFlagsFromCommandLine(options->argc, options->argv, options->removeFlags); @@ -1480,6 +1739,26 @@ OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) { } v8impl::CreateIsolateData(isolate, snapshotBlob); *result = reinterpret_cast(isolate); + // Create nullptr placeholder + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, nullptr); + + return JSVM_OK; +} + +JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, + JSVM_MicrotaskPolicy policy) { + static constexpr v8::MicrotasksPolicy converter[] = { + v8::MicrotasksPolicy::kExplicit, + v8::MicrotasksPolicy::kAuto + }; + constexpr size_t policyCount = node::arraysize(converter); + + if (!vm || policy >= policyCount) { + return JSVM_INVALID_ARG; + } + + auto isolate = reinterpret_cast(vm); + isolate->SetMicrotasksPolicy(converter[policy]); return JSVM_OK; } @@ -1493,6 +1772,7 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { auto creator = v8impl::GetIsolateSnapshotCreator(isolate); auto data = v8impl::GetIsolateData(isolate); + auto *handlerPool = v8impl::GetIsolateHandlerPool(isolate); if (creator != nullptr) { delete creator; } else { @@ -1502,9 +1782,65 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { delete data; } + if (handlerPool != nullptr) { + delete handlerPool; + } return JSVM_OK; } +JSVM_Status JSVM_CDECL OH_JSVM_CreateProxy(JSVM_Env env, JSVM_Value target, JSVM_Value handler, JSVM_Value *result) +{ + // Check args is not null + JSVM_PREAMBLE(env); + CHECK_ARG(env, target); + CHECK_ARG(env, handler); + CHECK_ARG(env, result); + + // Check target and handler are v8 Object + auto localTarget = v8impl::V8LocalValueFromJsValue(target); + RETURN_STATUS_IF_FALSE(env, localTarget->IsObject(), JSVM_OBJECT_EXPECTED); + auto localHandler = v8impl::V8LocalValueFromJsValue(handler); + RETURN_STATUS_IF_FALSE(env, localHandler->IsObject(), JSVM_OBJECT_EXPECTED); + + v8::Local context = env->context(); + + v8::MaybeLocal maybeProxy = + v8::Proxy::New(context, localTarget.As(), localHandler.As()); + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeProxy, JSVM_GENERIC_FAILURE); + + v8::Local proxy = maybeProxy.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(proxy); + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsProxy(JSVM_Env env, JSVM_Value value, bool *isProxy) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isProxy); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isProxy = val->IsProxy(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ProxyGetTarget(JSVM_Env env, JSVM_Value value, JSVM_Value *result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsProxy(), JSVM_INVALID_TYPE); + + *result = v8impl::JsValueFromV8LocalValue(val.As()->GetTarget()); + return jsvm_clear_last_error(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_OpenVMScope(JSVM_VM vm, JSVM_VMScope* result) { auto isolate = reinterpret_cast(vm); auto scope = new v8::Isolate::Scope(isolate); @@ -1836,6 +2172,48 @@ class CompileOptionResolver { bool hasInvalidOption = false; }; +JSVM_Status JSVM_CDECL OH_JSVM_PromiseRegisterHandler( + JSVM_Env env, JSVM_Value promise, JSVM_Value onFulfilled, JSVM_Value onRejected, JSVM_Value *result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, promise); + RETURN_STATUS_IF_FALSE(env, onFulfilled || onRejected, JSVM_INVALID_ARG); + + v8::Local value = v8impl::V8LocalValueFromJsValue(promise); + RETURN_STATUS_IF_FALSE(env, value->IsPromise(), JSVM_INVALID_TYPE); + auto localPromise = value.As(); + + v8::Local ctx = env->context(); + v8::MaybeLocal maybe; + if (!onFulfilled) { + // Only pass onRejected, call v8::Promise::Catch + auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); + RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = localPromise->Catch(ctx, rejectedHandler.As()); + } else if (!onRejected) { + // Only pass onFulfilled, call v8::Promise::Then + auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); + RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = value.As()->Then(ctx, fulfiledHandler.As()); + } else { + // Pass onFulfilled and onRejected, call v8::Promise::Then + auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); + RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); + auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); + RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = value.As()->Then(ctx, fulfiledHandler.As(), rejectedHandler.As()); + } + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, JSVM_GENERIC_FAILURE); + + if (result) { + auto retPromise = maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(retPromise); + } + + return jsvm_clear_last_error(env); +} + size_t CompileOptionResolver::compileCount = 0; JSVM_Status JSVM_CDECL @@ -1877,7 +2255,7 @@ OH_JSVM_CreateCodeCache(JSVM_Env env, CHECK_ARG(env, script); CHECK_ARG(env, data); CHECK_ARG(env, length); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); v8::ScriptCompiler::CachedData* cache; cache = v8::ScriptCompiler::CreateCodeCache(v8script->GetUnboundScript()); @@ -1900,7 +2278,7 @@ OH_JSVM_RunScript(JSVM_Env env, JSVM_Script script, JSVM_Value* result) { CHECK_ARG(env, script); CHECK_ARG(env, result); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); auto script_result = v8script->Run(env->context()); CHECK_MAYBE_EMPTY(env, script_result, JSVM_GENERIC_FAILURE); @@ -2120,6 +2498,7 @@ static const char* error_messages[] = { "Main thread would deadlock", "External buffers are not allowed", "Cannot run JavaScript", + "Invalid type" }; JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo( @@ -2131,7 +2510,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo( // message in the `JSVM_Status` enum each time a new error message is added. // We don't have a jsvm_status_last as this would result in an ABI // change each time a message was added. - const int last_status = JSVM_CANNOT_RUN_JS; + const int last_status = JSVM_INVALID_TYPE; static_assert(JSVM_ARRAYSIZE(error_messages) == last_status + 1, "Count of error messages must match count of error values"); @@ -2323,6 +2702,86 @@ OH_JSVM_DefineClass(JSVM_Env env, return GET_RETURN_STATUS(env); } +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStart(size_t count, + const JSVM_TraceCategory* categories, + const char* tag, + size_t eventsCount) { + if (count > v8impl::g_trace_catrgory_count || + ((count != 0) != (categories != nullptr))) { + return JSVM_INVALID_ARG; + } + + for (size_t i = 0; i < count; ++i) { + if (categories[i] >= v8impl::g_trace_catrgory_count) { + return JSVM_INVALID_ARG; + } + } + + using namespace v8::platform::tracing; + TraceConfig* trace_config = new TraceConfig(); + + if (count == 0) { + count = v8impl::g_default_catrgory_count; + categories = v8impl::g_default_categories; + } + + for (size_t i = 0; i < count; ++i) { + trace_config->AddIncludedCategory( + v8impl::g_internal_trace_categories[categories[i]]); + } + + v8::Platform* platform = v8impl::g_platform.get(); + TracingController* controller = + static_cast(platform->GetTracingController()); + v8impl::g_trace_stream.reset(new std::stringstream()); + auto stream = v8impl::g_trace_stream.get(); + + TraceWriter* writer = nullptr; + if (tag != nullptr) { + writer = TraceWriter::CreateJSONTraceWriter(*stream, tag); + } else { + writer = TraceWriter::CreateJSONTraceWriter(*stream); + } + + size_t max_chunks; + if (eventsCount != 0) { + size_t chunk_size = TraceBufferChunk::kChunkSize; + max_chunks = (eventsCount + chunk_size - 1) / chunk_size; + } else { + max_chunks = TraceBuffer::kRingBufferChunks; + } + + TraceBuffer* ring_buffer = + TraceBuffer::CreateTraceBufferRingBuffer(max_chunks, writer); + controller->Initialize(ring_buffer); + controller->StartTracing(trace_config); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_TraceStop(JSVM_OutputStream stream, + void* streamData) { + if (stream == nullptr || streamData == nullptr || + v8impl::g_trace_stream.get() == nullptr) { + return JSVM_INVALID_ARG; + } + + using namespace v8::platform::tracing; + v8::Platform* platform = v8impl::g_platform.get(); + auto controller = + static_cast(platform->GetTracingController()); + DCHECK(controller != nullptr); + controller->StopTracing(); + + // Call the destructor of TraceBuffer to print the JSON end. + controller->Initialize(nullptr); + + std::string output = v8impl::g_trace_stream.get()->rdbuf()->str(); + stream(output.c_str(), output.size(), streamData); + + v8impl::g_trace_stream.reset(nullptr); + return JSVM_OK; +} + JSVM_Status JSVM_CDECL OH_JSVM_GetPropertyNames(JSVM_Env env, JSVM_Value object, JSVM_Value* result) { @@ -2791,37 +3250,275 @@ JSVM_Status JSVM_CDECL OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) { return GET_RETURN_STATUS(env); } -JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env, - JSVM_Value value, - bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); +static void OnOOMError(const char* location, const v8::OOMDetails& details) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForOOMError; + if (handler == nullptr) { + return; + } + (*handler)(location, details.detail, details.is_heap_oom); +} - v8::Local val = v8impl::V8LocalValueFromJsValue(value); +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForOOMError(JSVM_VM vm, JSVM_HandlerForOOMError handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForOOMError = handler; + isolate->SetOOMErrorHandler(OnOOMError); + return JSVM_OK; +} - *result = val->IsArray(); - return jsvm_clear_last_error(env); +static void OnFatalError(const char* location, const char* message) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForFatalError; + if (handler == nullptr) { + return; + } + (*handler)(location, message); } -JSVM_Status JSVM_CDECL OH_JSVM_IsRegExp(JSVM_Env env, - JSVM_Value value, - bool* result) { - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, + JSVM_HandlerForFatalError handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForFatalError = handler; + isolate->SetFatalErrorHandler(OnFatalError); + return JSVM_OK; +} - v8::Local val = v8impl::V8LocalValueFromJsValue(value); +static void OnPromiseReject(v8::PromiseRejectMessage rejectMessage) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForPromiseReject; + if (handler == nullptr) { + return; + } + auto context = isolate->GetCurrentContext(); + auto env = v8impl::GetEnvByContext(context); + v8::HandleScope scope(isolate); + v8::Local rejectInfo = v8::Object::New(isolate); + auto strPromise = + v8::String::NewFromUtf8(isolate, "promise").ToLocalChecked(); + (void)rejectInfo->Set(context, strPromise, rejectMessage.GetPromise()); + auto strValue = v8::String::NewFromUtf8(isolate, "value").ToLocalChecked(); + (void)rejectInfo->Set(context, strValue, rejectMessage.GetValue()); + JSVM_Value jsvmRejectInfo = v8impl::JsValueFromV8LocalValue(rejectInfo); + JSVM_PromiseRejectEvent rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; + switch (rejectMessage.GetEvent()) { + case v8::kPromiseRejectWithNoHandler: { + rejectEvent = JSVM_PROMISE_REJECT_WITH_NO_HANDLER; + break; + } + case v8::kPromiseHandlerAddedAfterReject: { + rejectEvent = JSVM_PROMISE_ADD_HANDLER_AFTER_REJECTED; + break; + } + case v8::kPromiseRejectAfterResolved: { + rejectEvent = JSVM_PROMISE_REJECT_AFTER_RESOLVED; + break; + } + case v8::kPromiseResolveAfterResolved: { + rejectEvent = JSVM_PROMISE_RESOLVE_AFTER_RESOLVED; + break; + } + default: { + rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; + } + } + (*handler)(env, rejectEvent, jsvmRejectInfo); +} - *result = val->IsRegExp(); - return jsvm_clear_last_error(env); +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForPromiseReject( + JSVM_VM vm, JSVM_HandlerForPromiseReject handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForPromiseReject = handler; + isolate->SetPromiseRejectCallback(OnPromiseReject); + return JSVM_OK; } -JSVM_Status JSVM_CDECL OH_JSVM_GetArrayLength(JSVM_Env env, - JSVM_Value value, - uint32_t* result) { - JSVM_PREAMBLE(env); - CHECK_ARG(env, value); +JSVM_GCType GetJSVMGCType(v8::GCType gcType) { + switch (gcType) { + case v8::GCType::kGCTypeScavenge: + return JSVM_GC_TYPE_SCAVENGE; + case v8::GCType::kGCTypeMinorMarkCompact: + return JSVM_GC_TYPE_MINOR_MARK_COMPACT; + case v8::GCType::kGCTypeMarkSweepCompact: + return JSVM_GC_TYPE_MARK_SWEEP_COMPACT; + case v8::GCType::kGCTypeIncrementalMarking: + return JSVM_GC_TYPE_INCREMENTAL_MARKING; + case v8::GCType::kGCTypeProcessWeakCallbacks: + return JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS; + default: + return JSVM_GC_TYPE_ALL; + } +} + +static v8::GCType GetV8GCType(JSVM_GCType gcType) { + switch(gcType) { + case JSVM_GC_TYPE_SCAVENGE: + return v8::GCType::kGCTypeScavenge; + case JSVM_GC_TYPE_MINOR_MARK_COMPACT: + return v8::GCType::kGCTypeMinorMarkCompact; + case JSVM_GC_TYPE_MARK_SWEEP_COMPACT: + return v8::GCType::kGCTypeMarkSweepCompact; + case JSVM_GC_TYPE_INCREMENTAL_MARKING: + return v8::GCType::kGCTypeIncrementalMarking; + case JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS: + return v8::GCType::kGCTypeProcessWeakCallbacks; + default: + return v8::GCType::kGCTypeAll; + } +} + +JSVM_GCCallbackFlags GetJSVMGCCallbackFlags(v8::GCCallbackFlags flag) { + switch (flag) { + case v8::GCCallbackFlags::kGCCallbackFlagConstructRetainedObjectInfos: + return JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS; + case v8::GCCallbackFlags::kGCCallbackFlagForced: + return JSVM_GC_CALLBACK_FORCED; + case v8::GCCallbackFlags::kGCCallbackFlagSynchronousPhantomCallbackProcessing: + return JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage: + return JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllExternalMemory: + return JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY; + case v8::GCCallbackFlags::kGCCallbackScheduleIdleGarbageCollection: + return JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION; + default: + return JSVM_NO_GC_CALLBACK_FLAGS; + } +} + +static void OnBeforeGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + DCHECK_NOT_NULL(pool); + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +static void OnAfterGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + DCHECK_NOT_NULL(pool); + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AddHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* data) { + if (!vm || !handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, data](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == data; + }); + if (it != handlers.end()) { + return JSVM_INVALID_ARG; + } + auto *callbackData = new v8impl::GCHandlerWrapper(gcType, handler, data); + handlers.push_back(callbackData); + + if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { + isolate->AddGCPrologueCallback( + OnBeforeGC, callbackData, GetV8GCType(gcType)); + } else { + isolate->AddGCEpilogueCallback( + OnAfterGC, callbackData, GetV8GCType(gcType)); + } + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + void* userData) { + if (!vm || !handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + if (pool == nullptr) { + return JSVM_INVALID_ARG; + } + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == userData; + }); + if (it == handlers.end()) { + return JSVM_INVALID_ARG; + } + handlers.erase(it); + if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { + isolate->RemoveGCPrologueCallback(OnBeforeGC, (*it)); + } else { + isolate->RemoveGCEpilogueCallback(OnAfterGC, (*it)); + } + delete (*it); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env, + JSVM_Value value, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsArray(); + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsRegExp(JSVM_Env env, + JSVM_Value value, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + *result = val->IsRegExp(); + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetArrayLength(JSVM_Env env, + JSVM_Value value, + uint32_t* result) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, value); CHECK_ARG(env, result); v8::Local val = v8impl::V8LocalValueFromJsValue(value); @@ -2912,6 +3609,60 @@ JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayWithLength(JSVM_Env env, return jsvm_clear_last_error(env); } +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, + char* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied) { + CHECK_ARG(env, copied); + return v8impl::NewExternalString( + env, + str, + length, + finalizeCallback, + finalizeHint, + result, + copied, + OH_JSVM_CreateStringLatin1, + [&](v8::Isolate* isolate) { + if (length == JSVM_AUTO_LENGTH) { + length = (std::string_view(str)).length(); + } + auto resource = new v8impl::ExternalOneByteStringResource( + env, str, length, finalizeCallback, finalizeHint); + return v8::String::NewExternalOneByte(isolate, resource); + }); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, + char16_t* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied) { + CHECK_ARG(env, copied); + return v8impl::NewExternalString( + env, + str, + length, + finalizeCallback, + finalizeHint, + result, + copied, + OH_JSVM_CreateStringUtf16, + [&](v8::Isolate* isolate) { + if (length == JSVM_AUTO_LENGTH) { + length = (std::u16string_view(str)).length(); + } + auto resource = new v8impl::ExternalStringResource( + env, str, length, finalizeCallback, finalizeHint); + return v8::String::NewExternalTwoByte(isolate, resource); + }); +} + JSVM_Status JSVM_CDECL OH_JSVM_CreateStringLatin1(JSVM_Env env, const char* str, size_t length, @@ -3097,6 +3848,90 @@ JSVM_Status JSVM_CDECL OH_JSVM_SymbolFor(JSVM_Env env, return jsvm_clear_last_error(env); } +JSVM_Status JSVM_CDECL OH_JSVM_CreatePrivate(JSVM_Env env, + JSVM_Value description, + JSVM_Data* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (description == nullptr) { + *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate)); + } else { + v8::Local v8Name = v8impl::V8LocalValueFromJsValue(description); + RETURN_STATUS_IF_FALSE(env, v8Name->IsString(), JSVM_STRING_EXPECTED); + + *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate, v8Name.As())); + } + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value value) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + CHECK_ARG(env, value); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + auto val = v8impl::V8LocalValueFromJsValue(value); + + auto set_maybe = obj.As()->SetPrivate(context, privateKey.As(), val); + + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value *result) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + + auto getMaybe = obj.As()->GetPrivate(context, privateKey.As()); + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, getMaybe, JSVM_GENERIC_FAILURE); + + v8::Local val = getMaybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DeletePrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + + auto deleteMaybe = obj.As()->DeletePrivate(context, privateKey.As()); + auto success = deleteMaybe.IsJust() && deleteMaybe.FromMaybe(false); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, success, JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + static inline JSVM_Status set_error_code(JSVM_Env env, v8::Local error, JSVM_Value code, @@ -3944,6 +4779,33 @@ JSVM_Status JSVM_CDECL OH_JSVM_CreateReference(JSVM_Env env, return jsvm_clear_last_error(env); } +// ref for data can not be weak, so initialRefcount must be greater than 0 +JSVM_Status JSVM_CDECL OH_JSVM_CreateDataReference(JSVM_Env env, + JSVM_Data data, + uint32_t initialRefcount, + JSVM_Ref* result) { + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, data); + CHECK_ARG(env, result); + RETURN_STATUS_IF_FALSE(env, initialRefcount != 0, JSVM_INVALID_ARG); + + v8::Local v8_value = v8impl::V8LocalDataFromJsData(data); + v8impl::Reference* reference = v8impl::Reference::New( + env, + v8_value, + initialRefcount, + v8impl::Ownership::kUserland, + nullptr, + nullptr, + nullptr, + false); + + *result = reinterpret_cast(reference); + return jsvm_clear_last_error(env); +} + // Deletes a reference. The referenced value is released, and may be GC'd unless // there are other references to it. JSVM_Status JSVM_CDECL OH_JSVM_DeleteReference(JSVM_Env env, JSVM_Ref ref) { @@ -4023,7 +4885,27 @@ JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceValue(JSVM_Env env, CHECK_ARG(env, result); v8impl::Reference* reference = reinterpret_cast(ref); - *result = v8impl::JsValueFromV8LocalValue(reference->Get()); + RETURN_STATUS_IF_FALSE(env, reference->IsValue(), JSVM_INVALID_ARG); + *result = v8impl::JsValueFromV8LocalValue(reference->GetValue()); + + return jsvm_clear_last_error(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value might +// no longer be available, in that case the call is still successful but the +// result is NULL. +JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceData(JSVM_Env env, + JSVM_Ref ref, + JSVM_Data* result) { + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + v8impl::Reference* reference = reinterpret_cast(ref); + RETURN_STATUS_IF_FALSE(env, !reference->IsValue(), JSVM_INVALID_ARG); + *result = v8impl::JsDataFromV8LocalData(reference->GetData()); return jsvm_clear_last_error(env); } @@ -4891,6 +5773,232 @@ OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, return GET_RETURN_STATUS(env); } +JSVM_Status ProcessPropertyHandler(JSVM_Env env, + v8::Local tpl, + JSVM_PropertyHandlerCfg propertyHandlerCfg, + JSVM_Callback callAsFunctionCallback, + v8impl::JSVM_PropertyHandlerCfgStruct** propertyHandlerCfgStruct) { + CHECK_ARG(env, propertyHandlerCfg); + *propertyHandlerCfgStruct = v8impl::CreatePropertyCfg(env, propertyHandlerCfg); + if (*propertyHandlerCfgStruct == nullptr) { + return JSVM_GENERIC_FAILURE; + } + v8::Local cbdata = v8impl::CallbackBundle::New(env, *propertyHandlerCfgStruct); + + // register named property handler + v8::NamedPropertyHandlerConfiguration namedPropertyHandler; + if (propertyHandlerCfg->genericNamedPropertyGetterCallback) { + namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper::NameGetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertySetterCallback) { + namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper::NameSetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) { + namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::NameDeleterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) { + namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::NameEnumeratorInvoke; + } + namedPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(namedPropertyHandler); + + // register indexed property handle + v8::IndexedPropertyHandlerConfiguration indexPropertyHandler; + if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) { + indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper::IndexGetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertySetterCallback) { + indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper::IndexSetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) { + indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::IndexDeleterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) { + indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::IndexEnumeratorInvoke; + } + indexPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(indexPropertyHandler); + + // register call as function + if (callAsFunctionCallback && callAsFunctionCallback->callback) { + v8::Local funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback); + tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata); + } + return JSVM_OK; +} + +class DefineClassOptionsResolver { + public: + void ProcessOptions(size_t length, JSVM_DefineClassOptions options[], JSVM_Env env, + v8::Local tpl) { + for (int32_t i = 0; i < length; i++) { + if (status != JSVM_OK) { + break; + } + switch(options[i].id) { + case JSVM_DEFINE_CLASS_NORMAL: + break; + case JSVM_DEFINE_CLASS_WITH_COUNT: { + auto count = options[i].content.num; + v8::Local instance_templ = tpl->InstanceTemplate(); + instance_templ->SetInternalFieldCount(count); + break; + } + case JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER: { + hasPropertyHandle = true; + auto* propertyHandle = static_cast(options[i].content.ptr); + propertyHandlerCfg = propertyHandle->propertyHandlerCfg; + callAsFunctionCallback = propertyHandle->callAsFunctionCallback; + status = ProcessPropertyHandler(env, tpl, propertyHandlerCfg, callAsFunctionCallback, + &propertyHandlerCfgStruct); + break; + } + default: { + status = JSVM_INVALID_ARG; + } + } + } + } + + JSVM_Status GetStatus() { + return status; + } + + v8impl::JSVM_PropertyHandlerCfgStruct* GetPropertyHandler() { + return propertyHandlerCfgStruct; + } + + bool HasPropertyHandler() { + return hasPropertyHandle; + } + + private: + JSVM_PropertyHandlerCfg propertyHandlerCfg = nullptr; + JSVM_Callback callAsFunctionCallback = nullptr; + bool hasPropertyHandle = false; + JSVM_Status status = JSVM_OK; + v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandlerCfgStruct = nullptr; +}; + +JSVM_Status JSVM_CDECL +OH_JSVM_DefineClassWithOptions(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value parentClass, + size_t option_count, + JSVM_DefineClassOptions options[], + JSVM_Value* result) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, constructor); + CHECK_ARG(env, constructor->callback); + + if (propertyCount > 0) { + CHECK_ARG(env, properties); + } + + v8::Isolate* isolate = env->isolate; + v8::EscapableHandleScope scope(isolate); + v8::Local tpl; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, constructor, &tpl)); + + v8::Local name_string; + CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + tpl->SetClassName(name_string); + + size_t static_property_count = 0; + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + + if ((p->attributes & JSVM_STATIC) != 0) { // attributes + // Static properties are handled separately below. + static_property_count++; + continue; + } + + v8::Local property_name; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in OH_JSVM_DefineProperties(); the + // difference is it applies to a template instead of an object, + // and preferred PropertyAttribute for lack of PropertyDescriptor + // support on ObjectTemplate. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local getter_tpl; + v8::Local setter_tpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->getter, &getter_tpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->setter, &setter_tpl)); + } + + tpl->PrototypeTemplate()->SetAccessorProperty(property_name, + getter_tpl, + setter_tpl, + attributes, + v8::AccessControl::DEFAULT); + } else if (p->method != nullptr) { + v8::Local t; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->method, &t, v8::Signature::New(isolate, tpl))); + + tpl->PrototypeTemplate()->Set(property_name, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(property_name, value, attributes); + } + } + + if (parentClass != nullptr) { + v8::Local parentFunc; + CHECK_TO_FUNCTION(env, parentFunc, parentClass); + if (!tpl->Inherit(parentFunc)) { + return JSVM_INVALID_ARG; + } + } + + DefineClassOptionsResolver optionResolver; + optionResolver.ProcessOptions(option_count, options, env, tpl); + + if (optionResolver.GetStatus() != JSVM_OK) { + return optionResolver.GetStatus(); + } + + v8::Local context = env->context(); + *result = v8impl::JsValueFromV8LocalValue( + scope.Escape(tpl->GetFunction(context).ToLocalChecked())); + + if (optionResolver.HasPropertyHandler()) { + v8impl::Reference::New(env, v8impl::V8LocalValueFromJsValue(*result), 0, v8impl::Ownership::kRuntime, + v8impl::CfgFinalizedCallback, optionResolver.GetPropertyHandler(), nullptr); + } + + if (static_property_count > 0) { + std::vector static_descriptors; + static_descriptors.reserve(static_property_count); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + if ((p->attributes & JSVM_STATIC) != 0) { + static_descriptors.push_back(*p); + } + } + + STATUS_CALL(OH_JSVM_DefineProperties( + env, *result, static_descriptors.size(), static_descriptors.data())); + } + + return GET_RETURN_STATUS(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_IsLocked(JSVM_Env env, bool* isLocked) { CHECK_ENV(env); @@ -5206,7 +6314,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env, JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && !jsvmData->isGlobal, JSVM_INVALID_ARG); @@ -5219,7 +6327,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) { JSVM_Status JSVM_CDECL OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && jsvmData->isGlobal, JSVM_INVALID_ARG); diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h index 7e799c7c2229842c022e0f7b49d7b35e786c77ba..540766b63f87f0535ad80fe473fe167ce3537e95 100644 --- a/src/js_native_api_v8.h +++ b/src/js_native_api_v8.h @@ -58,13 +58,13 @@ class Finalizer; class Agent; } // end of namespace v8impl -struct JSVM_Data__ { +struct JSVM_Script_Data__ { public: using SourcePtr = std::variant, v8::Global>; using DataType = enum { kJsvmScript }; template - JSVM_Data__(T ptr, bool retained, DataType type = kJsvmScript) + JSVM_Script_Data__(T ptr, bool retained, DataType type = kJsvmScript) : taggedPointer(ptr), isGlobal(retained), type(type) {} @@ -193,11 +193,11 @@ struct JSVM_Env__ { } template - JSVM_Data__ *NewJsvmData(T srcPtr, JSVM_Data__::DataType type = JSVM_Data__::kJsvmScript) { + JSVM_Script_Data__ *NewJsvmData(T srcPtr, JSVM_Script_Data__::DataType type = JSVM_Script_Data__::kJsvmScript) { if (dataStack.empty() || open_handle_scopes != dataStack.top().first) { - dataStack.emplace(open_handle_scopes, std::vector()); + dataStack.emplace(open_handle_scopes, std::vector()); } - auto newData = new JSVM_Data__(srcPtr, false, type); + auto newData = new JSVM_Script_Data__(srcPtr, false, type); dataStack.top().second.push_back(newData); return newData; } @@ -234,7 +234,7 @@ struct JSVM_Env__ { int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION; bool in_gc_finalizer = false; v8::Locker* locker = nullptr; - std::stack>> dataStack; + std::stack>> dataStack; private: v8impl::Agent* inspector_agent_; @@ -395,9 +395,19 @@ inline JSVM_Value JsValueFromV8LocalValue(v8::Local local) { return reinterpret_cast(*local); } -inline v8::Local V8LocalValueFromJsValue(JSVM_Value v) { +inline JSVM_Data JsDataFromV8LocalData(v8::Local local) { + return reinterpret_cast(*local); +} + +inline v8::Local V8LocalValueFromJsValue(JSVM_Value value) { v8::Local local; - memcpy(static_cast(&local), &v, sizeof(v)); + memcpy(static_cast(&local), &value, sizeof(JSVM_Value)); + return local; +} + +inline v8::Local V8LocalDataFromJsData(JSVM_Data data) { + v8::Local local; + memcpy(static_cast(&local), &data, sizeof(JSVM_Data)); return local; } @@ -517,16 +527,17 @@ class RefBase : public TrackedFinalizer { class Reference : public RefBase { protected: template - Reference(JSVM_Env env, v8::Local value, Args&&... args); + Reference(JSVM_Env env, v8::Local value, bool isValue, Args&&... args); public: static Reference* New(JSVM_Env env, - v8::Local value, + v8::Local value, uint32_t initialRefcount, Ownership ownership, JSVM_Finalize finalizeCallback = nullptr, void* finalizeData = nullptr, - void* finalizeHint = nullptr); + void* finalizeHint = nullptr, + bool isValue = true); virtual ~Reference(); bool HasDeletedByUser() { @@ -534,8 +545,12 @@ class Reference : public RefBase { } uint32_t Ref(); uint32_t Unref(); - v8::Local Get(); + v8::Local GetValue(); + v8::Local GetData(); void Delete(); + bool IsValue() const { + return is_value; + } protected: void Finalize() override; @@ -545,7 +560,8 @@ class Reference : public RefBase { void SetWeak(); - v8impl::Persistent persistent_; + v8impl::Persistent persistent_; + bool is_value; bool can_be_weak_; bool deleted_by_user; bool wait_callback; diff --git a/src/jsvm.h b/src/jsvm.h index 41a7f81f32df13a16233bcaffd4f2f419929a7ea..ae914f04bce369a7c4860e7513966e0b9fd2cddb 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -98,6 +98,229 @@ EXTERN_C_START +/** + * @brief Check whether the given JSVM_Value is a BigInt Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a BigInt Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBigIntObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Boolean Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Boolean Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBooleanObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a String Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a String Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsStringObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Number Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Number Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsNumberObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Symbol Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Symbol Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsSymbolObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API returns the Symbol.asyncIterator of Well-Known Symbols. + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.asyncIterator of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolAsyncIterator(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.hasInstance of Well-Known Symbols. + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.hasInstance of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolHasInstance(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.isConcatSpreadable of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.isConcatSpreadable of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolIsConcatSpreadable(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.match of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.match of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolMatch(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.replace of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.replace of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolReplace(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.search of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.search of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolSearch(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.split of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.split of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolSplit(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.toPrimitive of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.toPrimitive of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolToPrimitive(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.unscopables of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.unscopables of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolUnscopables(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.toStringTag of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.toStringTag of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolToStringTag(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.iterator of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.iterator of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolIterator(JSVM_Env env, JSVM_Value* result); + /** * @brief Init a JavaScript vm. * @@ -118,6 +341,18 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options); JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result); +/** + * @brief This function controls how Microtasks are invoked of the vm. If the method is not + * called, the default microtask policy of vm is JSVM_MicrotaskPolicy::JSVM_MICROTASK_AUTO. + * + * @param vm The VM instance to set mircrotasks policy. + * @param policy Policy for running microtasks. + * @return Returns JSVM_OK if the API succeeded. + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, + JSVM_MicrotaskPolicy policy); + /** * @brief Destroys VM instance. * @@ -127,6 +362,61 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, */ JSVM_EXTERN JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm); +/** + * @brief This API allocates a default JavaScript Proxy. It is the equivalent of + * doing new Proxy(target, handler) in JavaScript. + * + * @param env The environment that the API is invoked under. + * @param target A JSVM_Value representing the JavaScript Object which you want to proxy. + * @param handler A JSVM_Value representing the JavaScript Object that defines which + * operations will be intercepted and how to redefine intercepted operations. + * @param result A JSVM_Value representing a JavaScript Proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * {@link JSVM_OBJECT_EXPECTED} if target or handler is not Javascript Object. \n + * {@link JSVM_PENDING_EXCEPTION} if an exception occurs. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateProxy(JSVM_Env env, + JSVM_Value target, + JSVM_Value handler, + JSVM_Value* result); + +/** + * @brief This API checks if the value passed in is a Proxy. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param isProxy Whether the given value is Proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsProxy(JSVM_Env env, + JSVM_Value value, + bool* isProxy); + +/** + * @brief This API gets target from proxy. + * + * @param env The environment that the API is invoked under. + * @param value JSVM_Value representing JavaScript Proxy whose target to return. + * @param result Target of the given proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * {@link JSVM_INVALID_TYPE} if value is not a Javascript Proxy. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ProxyGetTarget(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + /** * @brief This API open a new VM scope for the VM instance. * @@ -593,6 +883,23 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateReference(JSVM_Env env, uint32_t initialRefcount, JSVM_Ref* result); +/** + * @brief This API creates a new reference with the specified reference count to the data passed in. + * + * @param env The environment that the API is invoked under. + * @param value The JSVM_Data for which a reference is being created. + * @param initialRefcount Initial reference count for the new reference. + * @param result JSVM_Ref pointing to the new reference. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any parameter is null or the value of initialRefcount is 0.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateDataReference(JSVM_Env env, + JSVM_Data data, + uint32_t initialRefcount, + JSVM_Ref* result); + /** * @brief his API deletes the reference passed in. * @@ -646,6 +953,23 @@ JSVM_EXTERN JSVM_Status OH_JSVM_GetReferenceValue(JSVM_Env env, JSVM_Ref ref, JSVM_Value* result); +/** + * @brief If still valid, this API returns the JSVM_Data representing the + * JavaScript data associated with the JSVM_Ref. Otherwise, result will be NULL. + * + * @param env The environment that the API is invoked under. + * @param ref The JSVM_Ref for which the corresponding value is being requested. + * @param result The JSVM_Data referenced by the JSVM_Ref. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any parameter is null or the ref is not a reference to JSVM_Data.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetReferenceData(JSVM_Env env, + JSVM_Ref ref, + JSVM_Data* result); + /** * @brief This API returns a JSVM-API value corresponding to a JavaScript Array type. * @@ -821,6 +1145,81 @@ JSVM_EXTERN JSVM_Status OH_JSVM_SymbolFor(JSVM_Env env, size_t length, JSVM_Value* result); +/** + * @brief This API creates a JavaScript private key. + * + * @param env The environment that the API is invoked under. + * @param description Optional JSVM_Value which refers to a JavaScript string to be set as the description + * for the private key. + * @param result A JSVM_Data representing a JavaScript private key. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if env or result is NULL.\n + * {@link JSVM_STRING_EXPECTED } if the description is not a string.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreatePrivate(JSVM_Env env, + JSVM_Value description, + JSVM_Data* result); + +/** + * @brief This API set a private property on the Object passed in. + * + * @param env The environment that the API is invoked under. + * @param object The object on which to set the private property. + * @param key The private key of the property. + * @param value The private property value. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to set the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value value); + +/** + * @brief This API gets the requested private property from the Object passed in. + * + * @param env The environment that the API is invoked under. + * @param object The object from which to retrieve the private property. + * @param key The private key of the property. + * @param result The value of the private property. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to get the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value *result); + +/** + * @brief This API attempts to delete the property of the private key from object. + * + * @param env The environment that the API is invoked under. + * @param object The object to query. + * @param key The private key of the property to delete. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to delete the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DeletePrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key); + /** * @brief This API creates a JavaScript TypedArray object over an existing ArrayBuffer. TypedArray * objects provide an array-like view over an underlying data buffer where each element has the @@ -1008,6 +1407,58 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringUtf8(JSVM_Env env, const char* str, size_t length, JSVM_Value* result); +/** + * @brief This API creates an external JavaScript string value from an ISO-8859-1-encoded C + * string. The native string is copied when failed to create external string. + * + * @param env The environment that the API is invoked under. + * @param str Character buffer representing an ISO-8859-1-encoded string. + * @param length The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param finalizeCallback Optional callback to call when the external value is being collected. + * JSVM_Finalize provides more details. + * @param finalizeHint Optional hint to pass to the finalize callback during collection. + * @param result A JSVM_Value representing a JavaScript external string. + * @param copied flag indicate whether the external string is successfully created, + * true for faild to create external ones and fall back to non-external strings, false for success. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if one of env, str and copied is NULL.\n + * @since 16 + */ +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, + char* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied); + +/** + * @brief This API creates an external JavaScript string value from an UTF16-LE-encoded C + * string. The native string is copied when failed to create external string. + * + * @param env The environment that the API is invoked under. + * @param str Character buffer representing an UTF16-LE-encoded string. + * @param length The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param finalizeCallback Optional callback to call when the external value is being collected. + * JSVM_Finalize provides more details. + * @param finalizeHint Optional hint to pass to the finalize callback during collection. + * @param result A JSVM_Value representing a JavaScript external string. + * @param copied flag indicate whether the external string is successfully created, + * true for faild to create external ones and fall back to non-external strings, false for success. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if one of env, str and copied is NULL.\n + * @since 16 + */ + +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, + char16_t* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied); /** * @brief This API returns the length of an array. @@ -1436,6 +1887,91 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value constructor, bool* result); + +/** +* @brief Add VM GC Callback. +* +* @param vm The environment that the API is invoked under. +* @param triggerTime The timing of GC callback trigger. +* @param handler When Trigger gc, the callback function will be called. +* @param gcType The type of gc. +* @param userData The native pointer data. +* @return Returns JSVM funtions result code. +* {@link JSVM_OK } if the function executed successfully.\n +* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL or the handler has been added before.\n +* +* @since 16 +*/ +JSVM_EXTERN JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* userData); + +/** +* @brief Remove VM GC Callback. +* +* @param vm The environment that the API is invoked under. +* @param triggerTime The timing of GC callback trigger. +* @param handler When Trigger gc, the callback function will be called. +* @param userData The native pointer data. +* @return Returns JSVM funtions result code. +* {@link JSVM_OK } if the function executed successfully.\n +* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL, or the handler has been removed, +* or the handler has never been added.\n +* +* @since 16 +*/ +JSVM_EXTERN JSVM_Status OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + void* userData); + +/** + * @brief Set Handler For OOM Error. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for OOM Error. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForOOMError(JSVM_VM vm, + JSVM_HandlerForOOMError handler); + +/** + * @brief Set Handler For Fatal Error. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for Fatal Error. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, + JSVM_HandlerForFatalError handler); + +/** + * @brief Set Handler For Promise Reject. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for Promise Reject. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForPromiseReject(JSVM_VM vm, + JSVM_HandlerForPromiseReject handler); + /** * @brief This API represents invoking the IsArray operation on the object * @@ -2189,6 +2725,28 @@ JSVM_EXTERN JSVM_Status OH_JSVM_IsPromise(JSVM_Env env, JSVM_Value value, bool* isPromise); +/** + * @brief This API register a resolution/rejection handler with a promise. + * @param env The environment that the API is invoked under. + * @param promise The promise to be handled. + * @param onFulfilled The function to be invoked if promise is resolved. + * @param onRejected The function to be invoked if promise is rejected. + * @param result Another promise returned from promise then/catch method. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the arguments are invalid. \n + * {@link JSVM_INVALID_TYPE } if the arguments are invalid Javascript type. \n + * {@link JSVM_PENDING_EXCEPTION} if an exception occurs. \n + * {@link JSVM_GENERIC_FAILURE} if the API failed. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_PromiseRegisterHandler(JSVM_Env env, + JSVM_Value promise, + JSVM_Value onFulfilled, + JSVM_Value onRejected, + JSVM_Value* result); + /** * @brief This API parses a JSON string and returns it as value if successful. * @param env: The environment that the API is invoked under. @@ -2850,6 +3408,74 @@ JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseCache(JSVM_Env env, const uint8_t* cacheData, JSVM_CacheType cacheType); +/** + * @brief Trace start with specified categories for all JSVM VM.(Non-thread-safe) + * + * @param count The count of trace categories. + * @param categories Select internal trace events for tracing by categories. + * @param tag User-defined tag of trace data. + * @param eventsCount Number of trace events. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if categories or count is illegal.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStart(size_t count, const JSVM_TraceCategory* categories, + const char* tag, size_t eventsCount); + +/** + * @brief Trace stop for specified categories for all JSVM VM.(Non-thread-safe) + * + * @param stream The output stream callback for receiving the data. + * @param streamData Data passed to the stream callback. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if stream or streamData is NULL\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStop(JSVM_OutputStream stream, void* streamData); + +/** + * @brief When wrapping a C++ class, the C++ constructor callback passed via constructor + * should be a static method on the class that calls the actual class constructor, then + * wraps the new C++ instance in a JavaScript object according to the different Options + * passed in, and returns the wrapper object. + * + * @param env The environment that the API is invoked under. + * @param utf8name Name of the JavaScript constructor function. For clarity, it is + * recommended to use the C++ class name when wrapping a C++ class. + * @param length The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param constructor Struct include callback function that handles constructing instances of the class. + * When wrapping a C++ class, this method must be a static member with the JSVM_Callback.callback + * signature. A C++ class constructor cannot be used. + * Include Optional data to be passed to the constructor callback as the data + * property of the callback info. JSVM_Callback provides more details. + * @param propertyCount Number of items in the properties array argument. + * @param properties Array of property descriptors describing static and instance data + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param parentClass The parent-class of the currently defined class. + * @param option_count Number of items in an option array argument. + * @param options DefineClass options to be passed. + * @param result A JSVM_Value representing the constructor function for the class. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the function executed successfully. \n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL. \n + * {@link JSVM_GENERIC_FAILURE} if the input utf8name | constructor | properties is invalid. \n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DefineClassWithOptions(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value parentClass, + size_t option_count, + JSVM_DefineClassOptions options[], + JSVM_Value* result); EXTERN_C_END /** @} */ diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 139fec1b6568546217b6b611ca6f6ef18d8d163e..f6ed7267e38de8cdab5547a95df41b143cd4931d 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -107,6 +107,13 @@ typedef struct JSVM_CpuProfiler__* JSVM_CpuProfiler; */ typedef struct JSVM_Value__* JSVM_Value; +/** + * @brief To represent a JavaScript Data type. + * + * @since 16 + */ +typedef struct JSVM_Data__* JSVM_Data; + /** * @brief To represent a JavaScript value references. * @@ -322,6 +329,8 @@ typedef enum { JSVM_NO_EXTERNAL_BUFFERS_ALLOWED, /** cannot run +js status. */ JSVM_CANNOT_RUN_JS, + /** invalid input type status. */ + JSVM_INVALID_TYPE } JSVM_Status; /** @@ -716,6 +725,17 @@ typedef enum { JSVM_CACHE_TYPE_WASM, } JSVM_CacheType; +/** + * @brief Microtask policies of JSVM. +typedef enum { + /** Microtasks are invoked with the OH_JSVM_PerformMicrotaskCheckpoint() method. */ + JSVM_MICROTASK_EXPLICIT = 0, + /** Microtasks are invoked when the script call depth decrements to zero. + * Default mode. + */ + JSVM_MICROTASK_AUTO, +} JSVM_MicrotaskPolicy; + /** * @brief compile profile passed with JSVM_COMPILE_COMPILE_PROFILE * @@ -767,5 +787,219 @@ typedef enum { /** leave uninitialized. */ JSVM_UNINITIALIZED, } JSVM_InitializedFlag; + +/** + * @brief The promise-reject event. + * + * @since 16 + */ +typedef enum { + /** Promise is rejected for other reasons. */ + JSVM_PROMISE_REJECT_OTHER_REASONS = 0, + /** Promise rejected with no handler. */ + JSVM_PROMISE_REJECT_WITH_NO_HANDLER = 1, + /** Add the handler after Promise has been rejected. */ + JSVM_PROMISE_ADD_HANDLER_AFTER_REJECTED = 2, + /** After the Promise has been resolved, try to reject the Promise again. */ + JSVM_PROMISE_REJECT_AFTER_RESOLVED = 3, + /** After the Promise has been resolved, try resolving the Promise again. */ + JSVM_PROMISE_RESOLVE_AFTER_RESOLVED = 4, +} JSVM_PromiseRejectEvent; + +/** + * @brief The level of message error. + * + * @since 16 + */ +typedef enum { + /** Log level message. */ + JSVM_MESSAGE_LOG = (1 << 0), + /** Debug level message. */ + JSVM_MESSAGE_DEBUG = (1 << 1), + /** Info level message. */ + JSVM_MESSAGE_INFO = (1 << 2), + /** Error level message. */ + JSVM_MESSAGE_ERROR = (1 << 3), + /** Warning level message. */ + JSVM_MESSAGE_WARNING = (1 << 4), + /** All level message. */ + JSVM_MESSAGE_ALL = JSVM_MESSAGE_LOG | JSVM_MESSAGE_DEBUG | JSVM_MESSAGE_INFO | JSVM_MESSAGE_ERROR | + JSVM_MESSAGE_WARNING, +} JSVM_MessageErrorLevel; + +/** + * @brief Function pointer type of OOM-Error callback. + * + * @param location The location information of the OOM error. + * @param detail The detail of the OOM error. + * @param isHeapOOM Determine whether the OOM type is Heap OOM. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForOOMError)(const char* location, + const char* detail, + bool isHeapOOM); + +/** + * @brief Function pointer type of Fatal-Error callback. + * + * @param location The location information of the Fatal error. + * @param message The message of the Fatal error. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForFatalError)(const char* location, + const char* message); + +/** + * @brief Function pointer type of Promise-Reject callback. + * + * @param env The environment that the function is invoked under. + * @param rejectEvent The promise-reject event. + * @param rejectInfo An JS-object containing two properties: 'promise' and 'value'. + * The 'promise' represents a reference to the Promise object that was rejected. + * The 'value' represents the rejection reason associated with that promise. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForPromiseReject)(JSVM_Env env, + JSVM_PromiseRejectEvent rejectEvent, + JSVM_Value rejectInfo); +/** + * @brief The timing of GC callback trigger. + * + * @since 16 + */ +typedef enum { + /** Trigger GC callback before GC. */ + JSVM_CB_TRIGGER_BEFORE_GC, + /** Trigger GC callback after GC. */ + JSVM_CB_TRIGGER_AFTER_GC, +} JSVM_CBTriggerTimeForGC; + +/** + * @brief The GC type. + * + * @since 16 + */ +typedef enum { + /** The GC algorithm is Scavenge. */ + JSVM_GC_TYPE_SCAVENGE = 1 << 0, + /** The GC algorithm is Minor-Mark-Compact. */ + JSVM_GC_TYPE_MINOR_MARK_COMPACT = 1 << 1, + /** The GC algorithm is Mark-Sweep-Compact. */ + JSVM_GC_TYPE_MARK_SWEEP_COMPACT = 1 << 2, + /** The GC algorithm is Incremental-Marking. */ + JSVM_GC_TYPE_INCREMENTAL_MARKING = 1 << 3, + /** The GC algorithm is Weak-Callbacks. */ + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS = 1 << 4, + /** All GC algorithm. */ + JSVM_GC_TYPE_ALL = JSVM_GC_TYPE_SCAVENGE | JSVM_GC_TYPE_MINOR_MARK_COMPACT | + JSVM_GC_TYPE_MARK_SWEEP_COMPACT | JSVM_GC_TYPE_INCREMENTAL_MARKING | + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS, +} JSVM_GCType; + +/** + * @brief The GC callback flags. + * + * @since 16 + */ +typedef enum { + /** No GC callback falgs. */ + JSVM_NO_GC_CALLBACK_FLAGS, + /** Reserved object information will be built in the garbage collection callback. */ + JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS, + /** Enforce Garbage Collection Callback. */ + JSVM_GC_CALLBACK_FORCED, + /** Synchronous phantom callback processing. */ + JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING, + /** All available garbage objects are collected during garbage collection. */ + JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE, + /** Garbage collection collects all external memory. */ + JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY, + /** Schedule Garbage Collection at Idle Time. */ + JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION, +} JSVM_GCCallbackFlags; + +/** + * @brief Function pointer type of GC callback. + * + * @param vm The VM instance that the JSVM-API call is invoked under. + * @param gcType The gc type. + * @param flags The GC callback flags. + * @param data The native pointer data. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForGC)(JSVM_VM vm, + JSVM_GCType gcType, + JSVM_GCCallbackFlags flags, + void* data); +/** + * @brief The property-handler used to define class. + * + * @since 16 + */ +typedef struct { + /** The instance object triggers the corresponding callback function. */ + JSVM_PropertyHandlerCfg propertyHandlerCfg; + /** Calling an instance object as a function will trigger this callback. */ + JSVM_Callback callAsFunctionCallback; +} JSVM_PropertyHandler; + +/** + * @brief DefineClass options id. + * + * @since 16 + */ +typedef enum { + /** Defining a class in normal mode. */ + JSVM_DEFINE_CLASS_NORMAL, + /** Defining a class with count. */ + JSVM_DEFINE_CLASS_WITH_COUNT, + /** Defining a class with property handler. */ + JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER, +} JSVM_DefineClassOptionsId; + +/** + * @brief DefineClass options. + * + * @since 16 + */ +typedef struct { + /** DefineClass options id. */ + JSVM_DefineClassOptionsId id; + /** option content. */ + union { + /** for option value with pointer type. */ + void* ptr; + /** for option value with integer type */ + int num; + /** for option value with bool type */ + bool boolean; + } content; +} JSVM_DefineClassOptions; /** @} */ + +/** + * @brief Trace category for jsvm internal trace events. + * + * @since 16 + */ +typedef enum { + /** Tracing main interface invoking of JSVM, such as run scripts. */ + JSVM_TRACE_VM, + /** Tracing interface invoking about compilation, such as CompileCodeBackground. */ + JSVM_TRACE_COMPILE, + /** Tracing interface invoking about execution status, such as Interrupts and Microtasks. */ + JSVM_TRACE_EXECUTE, + /** Tracing external callback */ + JSVM_TRACE_RUNTIME, + /** Tracing stack trace in JSVM. */ + JSVM_TRACE_STACK_TRACE, + /** Tracing main interface invoking of WASM, such as Compile Wasm Module and Instantiate. */ + JSVM_TRACE_WASM, + /** Tracing more detailed interface invoking of WASM, such as background compilation and wrappers. */ + JSVM_TRACE_WASM_DETAILED +} JSVM_TraceCategory; #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */