Skip to content

Commit 5d46568

Browse files
committed
Start adding Node-API to Hermes
1 parent ce90d24 commit 5d46568

12 files changed

+9270
-2
lines changed

API/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# This source code is licensed under the MIT license found in the
44
# LICENSE file in the root directory of this source tree.
55

6+
add_subdirectory(node-api)
67
add_subdirectory(hermes)
78
add_subdirectory(hermes_abi)
89
add_subdirectory(hermes_sandbox)

API/hermes/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ add_subdirectory(cdp)
7373
# against the internal functionality they need.
7474
set(api_sources
7575
hermes.cpp
76+
hermes_napi.cpp
77+
MurmurHash.cpp
7678
DebuggerAPI.cpp
7779
AsyncDebuggerAPI.cpp
7880
RuntimeTaskRunner.cpp
@@ -102,7 +104,7 @@ add_hermes_library(traceInterpreter TraceInterpreter.cpp
102104
LINK_LIBS libhermes hermesInstrumentation synthTrace synthTraceParser)
103105

104106
add_library(libhermes ${api_sources})
105-
target_link_libraries(libhermes PUBLIC jsi PRIVATE hermesVMRuntime ${INSPECTOR_DEPS})
107+
target_link_libraries(libhermes PUBLIC jsi hermesNapi PRIVATE hermesVMRuntime ${INSPECTOR_DEPS})
106108
target_link_options(libhermes PRIVATE ${HERMES_EXTRA_LINKER_FLAGS})
107109

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

114116
# Create a lean version of libhermes in the same way.
115117
add_library(libhermes_lean ${api_sources})
116-
target_link_libraries(libhermes_lean PUBLIC jsi PRIVATE hermesVMRuntimeLean ${INSPECTOR_DEPS})
118+
target_link_libraries(libhermes_lean PUBLIC jsi hermesNapi PRIVATE hermesVMRuntimeLean ${INSPECTOR_DEPS})
117119
target_link_options(libhermes_lean PRIVATE ${HERMES_EXTRA_LINKER_FLAGS})
118120
target_include_directories(libhermes_lean PUBLIC .. ../../public ${HERMES_JSI_DIR})
119121
set_target_properties(libhermes_lean PROPERTIES OUTPUT_NAME hermes_lean)

API/hermes/MurmurHash.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
// This code was adapted from https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.h
4+
//-----------------------------------------------------------------------------
5+
// MurmurHash3 was written by Austin Appleby, and is placed in the public
6+
// domain. The author hereby disclaims copyright to this source code.
7+
#include "MurmurHash.h"
8+
#include <stdlib.h>
9+
10+
#if defined(_MSC_VER)
11+
12+
#define FORCE_INLINE __forceinline
13+
#define ROTL64(x, y) _rotl64(x, y)
14+
#define BIG_CONSTANT(x) (x)
15+
16+
#else // defined(_MSC_VER)
17+
18+
#define FORCE_INLINE inline __attribute__((always_inline))
19+
inline uint64_t rotl64(uint64_t x, int8_t r) {
20+
return (x << r) | (x >> (64 - r));
21+
}
22+
#define ROTL64(x, y) rotl64(x, y)
23+
#define BIG_CONSTANT(x) (x##LLU)
24+
25+
#endif // !defined(_MSC_VER)
26+
27+
using namespace std;
28+
29+
FORCE_INLINE uint64_t fmix64(uint64_t k) {
30+
k ^= k >> 33;
31+
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
32+
k ^= k >> 33;
33+
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
34+
k ^= k >> 33;
35+
36+
return k;
37+
}
38+
39+
bool isAscii(uint64_t k) {
40+
return (k & 0x8080808080808080) == 0ull;
41+
}
42+
43+
bool MurmurHash3_x64_128(const void *key, const int len, const uint32_t seed, void *out) {
44+
const uint8_t *data = (const uint8_t *)key;
45+
const int nblocks = len / 16;
46+
47+
uint64_t h1 = seed;
48+
uint64_t h2 = seed;
49+
50+
const uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
51+
const uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
52+
53+
//----------
54+
// body
55+
56+
const uint64_t *blocks = (const uint64_t *)(data);
57+
58+
bool isAsciiString{true};
59+
for (int i = 0; i < nblocks; i++) {
60+
uint64_t k1 = blocks[i * 2 + 0];
61+
uint64_t k2 = blocks[i * 2 + 1];
62+
63+
isAsciiString &= isAscii(k1) && isAscii(k2);
64+
65+
k1 *= c1;
66+
k1 = ROTL64(k1, 31);
67+
k1 *= c2;
68+
h1 ^= k1;
69+
70+
h1 = ROTL64(h1, 27);
71+
h1 += h2;
72+
h1 = h1 * 5 + 0x52dce729;
73+
74+
k2 *= c2;
75+
k2 = ROTL64(k2, 33);
76+
k2 *= c1;
77+
h2 ^= k2;
78+
79+
h2 = ROTL64(h2, 31);
80+
h2 += h1;
81+
h2 = h2 * 5 + 0x38495ab5;
82+
}
83+
84+
//----------
85+
// tail
86+
87+
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
88+
89+
for (auto i = 0; i < len % 16; i++) {
90+
if (tail[i] > 127) {
91+
isAsciiString = false;
92+
break;
93+
}
94+
}
95+
96+
uint64_t k1 = 0;
97+
uint64_t k2 = 0;
98+
99+
switch (len & 15) {
100+
case 15:
101+
k2 ^= ((uint64_t)tail[14]) << 48;
102+
case 14:
103+
k2 ^= ((uint64_t)tail[13]) << 40;
104+
case 13:
105+
k2 ^= ((uint64_t)tail[12]) << 32;
106+
case 12:
107+
k2 ^= ((uint64_t)tail[11]) << 24;
108+
case 11:
109+
k2 ^= ((uint64_t)tail[10]) << 16;
110+
case 10:
111+
k2 ^= ((uint64_t)tail[9]) << 8;
112+
case 9:
113+
k2 ^= ((uint64_t)tail[8]) << 0;
114+
k2 *= c2;
115+
k2 = ROTL64(k2, 33);
116+
k2 *= c1;
117+
h2 ^= k2;
118+
119+
case 8:
120+
k1 ^= ((uint64_t)tail[7]) << 56;
121+
case 7:
122+
k1 ^= ((uint64_t)tail[6]) << 48;
123+
case 6:
124+
k1 ^= ((uint64_t)tail[5]) << 40;
125+
case 5:
126+
k1 ^= ((uint64_t)tail[4]) << 32;
127+
case 4:
128+
k1 ^= ((uint64_t)tail[3]) << 24;
129+
case 3:
130+
k1 ^= ((uint64_t)tail[2]) << 16;
131+
case 2:
132+
k1 ^= ((uint64_t)tail[1]) << 8;
133+
case 1:
134+
k1 ^= ((uint64_t)tail[0]) << 0;
135+
k1 *= c1;
136+
k1 = ROTL64(k1, 31);
137+
k1 *= c2;
138+
h1 ^= k1;
139+
};
140+
141+
//----------
142+
// finalization
143+
144+
h1 ^= len;
145+
h2 ^= len;
146+
147+
h1 += h2;
148+
h2 += h1;
149+
150+
h1 = fmix64(h1);
151+
h2 = fmix64(h2);
152+
153+
h1 += h2;
154+
h2 += h1;
155+
156+
((uint64_t *)out)[0] = h1;
157+
((uint64_t *)out)[1] = h2;
158+
159+
return isAsciiString;
160+
}
161+
162+
bool murmurhash(const uint8_t *key, size_t length, uint64_t &hash) {
163+
uint64_t hashes[2];
164+
165+
bool isAscii = MurmurHash3_x64_128(key, length, 31, &hashes);
166+
167+
hash = hashes[0];
168+
169+
return isAscii;
170+
}

API/hermes/MurmurHash.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
#pragma once
4+
5+
#include <cstddef>
6+
#include <cstdint>
7+
8+
// Computes the hash of key using MurmurHash3 algorithm, the value is planced in the "hash" output parameter
9+
// The function returns whether or not key is comprised of only ASCII characters (<=127)
10+
bool murmurhash(const uint8_t *key, size_t length, uint64_t &hash);

API/hermes/ScriptStore.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
#pragma once
4+
5+
#include <jsi/jsi.h>
6+
#include <memory>
7+
8+
namespace facebook {
9+
namespace jsi {
10+
11+
// Integer type as it's persist friendly.
12+
using ScriptVersion_t = uint64_t; // It should be std::optional<uint64_t> once we have c++17 available everywhere. Until
13+
// then, 0 implies versioning not available.
14+
using JSRuntimeVersion_t = uint64_t; // 0 implies version can't be computed. We assert whenever that happens.
15+
16+
struct VersionedBuffer {
17+
std::shared_ptr<const facebook::jsi::Buffer> buffer;
18+
ScriptVersion_t version;
19+
};
20+
21+
struct ScriptSignature {
22+
std::string url;
23+
ScriptVersion_t version;
24+
};
25+
26+
struct JSRuntimeSignature {
27+
std::string runtimeName; // e.g. Chakra, V8
28+
JSRuntimeVersion_t version;
29+
};
30+
31+
// Most JSI::Runtime implementation offer some form of prepared JavaScript which offers better performance
32+
// characteristics when loading comparing to plain JavaScript. Embedders can provide an instance of this interface
33+
// (through JSI::Runtime implementation's factory method), to enable persistance of the prepared script and retrieval on
34+
// subsequent evaluation of a script.
35+
struct PreparedScriptStore {
36+
virtual ~PreparedScriptStore() = default;
37+
38+
// Try to retrieve the prepared javascript for a given combination of script & runtime.
39+
// scriptSignature : Javascript url and version
40+
// RuntimeSignature : Javascript engine type and version
41+
// prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while
42+
// experimentation and can be null. It is possible that no prepared script is available for a given script & runtime
43+
// signature. This method should null if so
44+
virtual std::shared_ptr<const facebook::jsi::Buffer> tryGetPreparedScript(
45+
const ScriptSignature &scriptSignature,
46+
const JSRuntimeSignature &runtimeSignature,
47+
const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache.
48+
) noexcept = 0;
49+
50+
// Persist the prepared javascript for a given combination of script & runtime.
51+
// scriptSignature : Javascript url and version
52+
// RuntimeSignature : Javascript engine type and version
53+
// prepareTag : Custom tag to uniquely identify JS engine specific preparation schemes. It is usually useful while
54+
// experimentation and can be null. It is possible that no prepared script is available for a given script & runtime
55+
// signature. This method should null if so Any failure in persistance should be identified during the subsequent
56+
// retrieval through the integrity mechanism which must be put into the storage.
57+
virtual void persistPreparedScript(
58+
std::shared_ptr<const facebook::jsi::Buffer> preparedScript,
59+
const ScriptSignature &scriptMetadata,
60+
const JSRuntimeSignature &runtimeMetadata,
61+
const char *prepareTag // Optional tag. For e.g. eagerly evaluated vs lazy cache.
62+
) noexcept = 0;
63+
};
64+
65+
// JSI::Runtime implementation must be provided an instance on this interface to enable version sensitive capabilities
66+
// such as usage of pre-prepared javascript script. Alternatively, this entity can be used to directly provide the
67+
// Javascript buffer and rich metadata to the JSI::Runtime instance.
68+
struct ScriptStore {
69+
virtual ~ScriptStore() = default;
70+
71+
// Return the Javascript buffer and version corresponding to a given url.
72+
virtual VersionedBuffer getVersionedScript(const std::string &url) noexcept = 0;
73+
74+
// Return the version of the Javascript buffer corresponding to a given url.
75+
virtual ScriptVersion_t getScriptVersion(const std::string &url) noexcept = 0;
76+
};
77+
78+
} // namespace jsi
79+
} // namespace facebook

API/hermes/hermes_api.h

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#ifndef HERMES_HERMES_API_H
9+
#define HERMES_HERMES_API_H
10+
11+
#include "js_runtime_api.h"
12+
13+
EXTERN_C_START
14+
15+
typedef struct hermes_local_connection_s *hermes_local_connection;
16+
typedef struct hermes_remote_connection_s *hermes_remote_connection;
17+
18+
//=============================================================================
19+
// jsr_runtime
20+
//=============================================================================
21+
22+
JSR_API hermes_dump_crash_data(jsr_runtime runtime, int32_t fd);
23+
JSR_API hermes_sampling_profiler_enable();
24+
JSR_API hermes_sampling_profiler_disable();
25+
JSR_API hermes_sampling_profiler_add(jsr_runtime runtime);
26+
JSR_API hermes_sampling_profiler_remove(jsr_runtime runtime);
27+
JSR_API hermes_sampling_profiler_dump_to_file(const char *filename);
28+
29+
//=============================================================================
30+
// jsr_config
31+
//=============================================================================
32+
33+
JSR_API hermes_config_enable_default_crash_handler(
34+
jsr_config config,
35+
bool value);
36+
37+
//=============================================================================
38+
// Setting inspector singleton
39+
//=============================================================================
40+
41+
typedef int32_t(NAPI_CDECL *hermes_inspector_add_page_cb)(
42+
const char *title,
43+
const char *vm,
44+
void *connectFunc);
45+
46+
typedef void(NAPI_CDECL *hermes_inspector_remove_page_cb)(int32_t page_id);
47+
48+
JSR_API hermes_set_inspector(
49+
hermes_inspector_add_page_cb add_page_cb,
50+
hermes_inspector_remove_page_cb remove_page_cb);
51+
52+
//=============================================================================
53+
// Local and remote inspector connections.
54+
// Local is defined in Hermes VM, Remote is defined by inspector outside of VM.
55+
//=============================================================================
56+
57+
typedef void(NAPI_CDECL *hermes_remote_connection_send_message_cb)(
58+
hermes_remote_connection remote_connection,
59+
const char *message);
60+
61+
typedef void(NAPI_CDECL *hermes_remote_connection_disconnect_cb)(
62+
hermes_remote_connection remote_connection);
63+
64+
JSR_API hermes_create_local_connection(
65+
void *connect_func,
66+
hermes_remote_connection remote_connection,
67+
hermes_remote_connection_send_message_cb on_send_message_cb,
68+
hermes_remote_connection_disconnect_cb on_disconnect_cb,
69+
jsr_data_delete_cb on_delete_cb,
70+
void *deleter_data,
71+
hermes_local_connection *local_connection);
72+
73+
JSR_API hermes_delete_local_connection(
74+
hermes_local_connection local_connection);
75+
76+
JSR_API hermes_local_connection_send_message(
77+
hermes_local_connection local_connection,
78+
const char *message);
79+
80+
JSR_API hermes_local_connection_disconnect(
81+
hermes_local_connection local_connection);
82+
83+
EXTERN_C_END
84+
85+
#endif // !HERMES_HERMES_API_H

0 commit comments

Comments
 (0)