Skip to content

Commit

Permalink
Start adding Node-API to Hermes
Browse files Browse the repository at this point in the history
  • Loading branch information
vmoroz committed Apr 17, 2024
1 parent ce90d24 commit 5d46568
Show file tree
Hide file tree
Showing 12 changed files with 9,270 additions and 2 deletions.
1 change: 1 addition & 0 deletions API/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

add_subdirectory(node-api)
add_subdirectory(hermes)
add_subdirectory(hermes_abi)
add_subdirectory(hermes_sandbox)
6 changes: 4 additions & 2 deletions API/hermes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ add_subdirectory(cdp)
# against the internal functionality they need.
set(api_sources
hermes.cpp
hermes_napi.cpp
MurmurHash.cpp
DebuggerAPI.cpp
AsyncDebuggerAPI.cpp
RuntimeTaskRunner.cpp
Expand Down Expand Up @@ -102,7 +104,7 @@ add_hermes_library(traceInterpreter TraceInterpreter.cpp
LINK_LIBS libhermes hermesInstrumentation synthTrace synthTraceParser)

add_library(libhermes ${api_sources})
target_link_libraries(libhermes PUBLIC jsi PRIVATE hermesVMRuntime ${INSPECTOR_DEPS})
target_link_libraries(libhermes PUBLIC jsi hermesNapi PRIVATE hermesVMRuntime ${INSPECTOR_DEPS})
target_link_options(libhermes PRIVATE ${HERMES_EXTRA_LINKER_FLAGS})

# Export the required header directory
Expand All @@ -113,7 +115,7 @@ set_target_properties(libhermes PROPERTIES OUTPUT_NAME hermes)

# Create a lean version of libhermes in the same way.
add_library(libhermes_lean ${api_sources})
target_link_libraries(libhermes_lean PUBLIC jsi PRIVATE hermesVMRuntimeLean ${INSPECTOR_DEPS})
target_link_libraries(libhermes_lean PUBLIC jsi hermesNapi PRIVATE hermesVMRuntimeLean ${INSPECTOR_DEPS})
target_link_options(libhermes_lean PRIVATE ${HERMES_EXTRA_LINKER_FLAGS})
target_include_directories(libhermes_lean PUBLIC .. ../../public ${HERMES_JSI_DIR})
set_target_properties(libhermes_lean PROPERTIES OUTPUT_NAME hermes_lean)
Expand Down
170 changes: 170 additions & 0 deletions API/hermes/MurmurHash.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// This code was adapted from https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.h
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#include "MurmurHash.h"
#include <stdlib.h>

#if defined(_MSC_VER)

#define FORCE_INLINE __forceinline
#define ROTL64(x, y) _rotl64(x, y)
#define BIG_CONSTANT(x) (x)

#else // defined(_MSC_VER)

#define FORCE_INLINE inline __attribute__((always_inline))
inline uint64_t rotl64(uint64_t x, int8_t r) {
return (x << r) | (x >> (64 - r));
}
#define ROTL64(x, y) rotl64(x, y)
#define BIG_CONSTANT(x) (x##LLU)

#endif // !defined(_MSC_VER)

using namespace std;

FORCE_INLINE uint64_t fmix64(uint64_t k) {
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;

return k;
}

bool isAscii(uint64_t k) {
return (k & 0x8080808080808080) == 0ull;
}

bool MurmurHash3_x64_128(const void *key, const int len, const uint32_t seed, void *out) {
const uint8_t *data = (const uint8_t *)key;
const int nblocks = len / 16;

uint64_t h1 = seed;
uint64_t h2 = seed;

const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);

//----------
// body

const uint64_t *blocks = (const uint64_t *)(data);

bool isAsciiString{true};
for (int i = 0; i < nblocks; i++) {
uint64_t k1 = blocks[i * 2 + 0];
uint64_t k2 = blocks[i * 2 + 1];

isAsciiString &= isAscii(k1) && isAscii(k2);

k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;

h1 = ROTL64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;

k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;

h2 = ROTL64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}

//----------
// tail

const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);

for (auto i = 0; i < len % 16; i++) {
if (tail[i] > 127) {
isAsciiString = false;
break;
}
}

uint64_t k1 = 0;
uint64_t k2 = 0;

switch (len & 15) {
case 15:
k2 ^= ((uint64_t)tail[14]) << 48;
case 14:
k2 ^= ((uint64_t)tail[13]) << 40;
case 13:
k2 ^= ((uint64_t)tail[12]) << 32;
case 12:
k2 ^= ((uint64_t)tail[11]) << 24;
case 11:
k2 ^= ((uint64_t)tail[10]) << 16;
case 10:
k2 ^= ((uint64_t)tail[9]) << 8;
case 9:
k2 ^= ((uint64_t)tail[8]) << 0;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;

case 8:
k1 ^= ((uint64_t)tail[7]) << 56;
case 7:
k1 ^= ((uint64_t)tail[6]) << 48;
case 6:
k1 ^= ((uint64_t)tail[5]) << 40;
case 5:
k1 ^= ((uint64_t)tail[4]) << 32;
case 4:
k1 ^= ((uint64_t)tail[3]) << 24;
case 3:
k1 ^= ((uint64_t)tail[2]) << 16;
case 2:
k1 ^= ((uint64_t)tail[1]) << 8;
case 1:
k1 ^= ((uint64_t)tail[0]) << 0;
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
};

//----------
// finalization

h1 ^= len;
h2 ^= len;

h1 += h2;
h2 += h1;

h1 = fmix64(h1);
h2 = fmix64(h2);

h1 += h2;
h2 += h1;

((uint64_t *)out)[0] = h1;
((uint64_t *)out)[1] = h2;

return isAsciiString;
}

bool murmurhash(const uint8_t *key, size_t length, uint64_t &hash) {
uint64_t hashes[2];

bool isAscii = MurmurHash3_x64_128(key, length, 31, &hashes);

hash = hashes[0];

return isAscii;
}
10 changes: 10 additions & 0 deletions API/hermes/MurmurHash.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#include <cstddef>
#include <cstdint>

// Computes the hash of key using MurmurHash3 algorithm, the value is planced in the "hash" output parameter
// The function returns whether or not key is comprised of only ASCII characters (<=127)
bool murmurhash(const uint8_t *key, size_t length, uint64_t &hash);
79 changes: 79 additions & 0 deletions API/hermes/ScriptStore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once

#include <jsi/jsi.h>
#include <memory>

namespace facebook {
namespace jsi {

// Integer type as it's persist friendly.
using ScriptVersion_t = uint64_t; // It should be std::optional<uint64_t> once we have c++17 available everywhere. Until
// then, 0 implies versioning not available.
using JSRuntimeVersion_t = uint64_t; // 0 implies version can't be computed. We assert whenever that happens.

struct VersionedBuffer {
std::shared_ptr<const facebook::jsi::Buffer> buffer;
ScriptVersion_t version;
};

struct ScriptSignature {
std::string url;
ScriptVersion_t version;
};

struct JSRuntimeSignature {
std::string runtimeName; // e.g. Chakra, V8
JSRuntimeVersion_t version;
};

// Most JSI::Runtime implementation offer some form of prepared JavaScript which offers better performance
// characteristics when loading comparing to plain JavaScript. Embedders can provide an instance of this interface
// (through JSI::Runtime implementation's factory method), to enable persistance of the prepared script and retrieval on
// subsequent evaluation of a script.
struct PreparedScriptStore {
virtual ~PreparedScriptStore() = default;

// Try to retrieve the prepared javascript for a given combination of script & runtime.
// scriptSignature : Javascript url and version
// RuntimeSignature : Javascript engine type and version
// prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while
// experimentation and can be null. It is possible that no prepared script is available for a given script & runtime
// signature. This method should null if so
virtual std::shared_ptr<const facebook::jsi::Buffer> tryGetPreparedScript(
const ScriptSignature &scriptSignature,
const JSRuntimeSignature &runtimeSignature,
const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache.
) noexcept = 0;

// Persist the prepared javascript for a given combination of script & runtime.
// scriptSignature : Javascript url and version
// RuntimeSignature : Javascript engine type and version
// prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while
// experimentation and can be null. It is possible that no prepared script is available for a given script & runtime
// signature. This method should null if so Any failure in persistance should be identified during the subsequent
// retrieval through the integrity mechanism which must be put into the storage.
virtual void persistPreparedScript(
std::shared_ptr<const facebook::jsi::Buffer> preparedScript,
const ScriptSignature &scriptMetadata,
const JSRuntimeSignature &runtimeMetadata,
const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache.
) noexcept = 0;
};

// JSI::Runtime implementation must be provided an instance on this interface to enable version sensitive capabilities
// such as usage of pre-prepared javascript script. Alternatively, this entity can be used to directly provide the
// Javascript buffer and rich metadata to the JSI::Runtime instance.
struct ScriptStore {
virtual ~ScriptStore() = default;

// Return the Javascript buffer and version corresponding to a given url.
virtual VersionedBuffer getVersionedScript(const std::string &url) noexcept = 0;

// Return the version of the Javascript buffer corresponding to a given url.
virtual ScriptVersion_t getScriptVersion(const std::string &url) noexcept = 0;
};

} // namespace jsi
} // namespace facebook
85 changes: 85 additions & 0 deletions API/hermes/hermes_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#ifndef HERMES_HERMES_API_H
#define HERMES_HERMES_API_H

#include "js_runtime_api.h"

EXTERN_C_START

typedef struct hermes_local_connection_s *hermes_local_connection;
typedef struct hermes_remote_connection_s *hermes_remote_connection;

//=============================================================================
// jsr_runtime
//=============================================================================

JSR_API hermes_dump_crash_data(jsr_runtime runtime, int32_t fd);
JSR_API hermes_sampling_profiler_enable();
JSR_API hermes_sampling_profiler_disable();
JSR_API hermes_sampling_profiler_add(jsr_runtime runtime);
JSR_API hermes_sampling_profiler_remove(jsr_runtime runtime);
JSR_API hermes_sampling_profiler_dump_to_file(const char *filename);

//=============================================================================
// jsr_config
//=============================================================================

JSR_API hermes_config_enable_default_crash_handler(
jsr_config config,
bool value);

//=============================================================================
// Setting inspector singleton
//=============================================================================

typedef int32_t(NAPI_CDECL *hermes_inspector_add_page_cb)(
const char *title,
const char *vm,
void *connectFunc);

typedef void(NAPI_CDECL *hermes_inspector_remove_page_cb)(int32_t page_id);

JSR_API hermes_set_inspector(
hermes_inspector_add_page_cb add_page_cb,
hermes_inspector_remove_page_cb remove_page_cb);

//=============================================================================
// Local and remote inspector connections.
// Local is defined in Hermes VM, Remote is defined by inspector outside of VM.
//=============================================================================

typedef void(NAPI_CDECL *hermes_remote_connection_send_message_cb)(
hermes_remote_connection remote_connection,
const char *message);

typedef void(NAPI_CDECL *hermes_remote_connection_disconnect_cb)(
hermes_remote_connection remote_connection);

JSR_API hermes_create_local_connection(
void *connect_func,
hermes_remote_connection remote_connection,
hermes_remote_connection_send_message_cb on_send_message_cb,
hermes_remote_connection_disconnect_cb on_disconnect_cb,
jsr_data_delete_cb on_delete_cb,
void *deleter_data,
hermes_local_connection *local_connection);

JSR_API hermes_delete_local_connection(
hermes_local_connection local_connection);

JSR_API hermes_local_connection_send_message(
hermes_local_connection local_connection,
const char *message);

JSR_API hermes_local_connection_disconnect(
hermes_local_connection local_connection);

EXTERN_C_END

#endif // !HERMES_HERMES_API_H
Loading

0 comments on commit 5d46568

Please sign in to comment.