Skip to content

Commit 9e3f4d7

Browse files
committed
Port to emscripten
1 parent a482974 commit 9e3f4d7

25 files changed

+395
-28
lines changed

C/tests/CMakeLists.txt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ elseif(APPLE)
3737
include("${CMAKE_CURRENT_LIST_DIR}/cmake/platform_apple.cmake")
3838
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
3939
include("${CMAKE_CURRENT_LIST_DIR}/cmake/platform_linux.cmake")
40+
elseif(EMSCRIPTEN)
41+
include("${CMAKE_CURRENT_LIST_DIR}/cmake/platform_emscripten.cmake")
4042
else()
4143
message(FATAL_ERROR "Unsupported platform ${CMAKE_SYSTEM_NAME}")
4244
endif()
@@ -56,15 +58,10 @@ add_executable(
5658
# EE tests:
5759
c4DatabaseEncryptionTest.cc
5860
c4CertificateTest.cc
59-
61+
6062
${TOP}LiteCore/tests/main.cpp
6163
${TOP}Crypto/SecureRandomize.cc
62-
${TOP}LiteCore/Support/FilePath.cc
63-
${TOP}LiteCore/Support/LogDecoder.cc
64-
${TOP}LiteCore/Support/Logging_Stub.cc
65-
${TOP}LiteCore/Support/StringUtil.cc
6664
${TOP}LiteCore/Support/TestsCommon.cc
67-
${TOP}LiteCore/Support/Error.cc
6865
${TOP}vendor/fleece/ObjC/slice+CoreFoundation.cc
6966
${TOP}vendor/fleece/vendor/libb64/cencode.c
7067
${TOP}vendor/fleece/vendor/libb64/cdecode.c
@@ -79,7 +76,7 @@ target_compile_definitions(
7976

8077
target_link_libraries(
8178
C4Tests PRIVATE
82-
LiteCore
79+
LiteCoreStatic
8380
BLIPStatic
8481
FleeceBase
8582
)
@@ -91,6 +88,7 @@ target_include_directories(
9188
${TOP}vendor/fleece/API
9289
${TOP}vendor/fleece/Fleece/Support
9390
${TOP}C
91+
${TOP}C/include
9492
${TOP}Crypto
9593
${TOP}Replicator
9694
${TOP}Replicator/tests

C/tests/c4DatabaseTest.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ N_WAY_TEST_CASE_METHOD(C4DatabaseTest, "Database ErrorMessages", "[Database][Err
7575
assertMessage(SQLiteDomain, SQLITE_IOERR_ACCESS, "SQLite error 3338", "disk I/O error (3338)");
7676
assertMessage(SQLiteDomain, SQLITE_IOERR, "SQLite error 10", "disk I/O error");
7777
assertMessage(LiteCoreDomain, 15, "LiteCore CorruptData", "data is corrupted");
78+
#ifdef __EMSCRIPTEN__
79+
assertMessage(POSIXDomain, ENOENT, "POSIX error 44", "No such file or directory");
80+
#else
7881
assertMessage(POSIXDomain, ENOENT, "POSIX error 2", "No such file or directory");
82+
#endif
7983
assertMessage(LiteCoreDomain, kC4ErrorTransactionNotClosed, "LiteCore TransactionNotClosed", "transaction not closed");
8084
assertMessage(SQLiteDomain, -1234, "SQLite error -1234", "unknown error (-1234)");
8185
assertMessage((C4ErrorDomain)666, -1234, "INVALID_DOMAIN error -1234", "invalid C4Error (unknown domain)");
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function(setup_build)
2+
target_sources(
3+
C4Tests PRIVATE
4+
${TOP}Crypto/mbedUtils.cc
5+
${TOP}LiteCore/Unix/strlcat.c
6+
)
7+
8+
target_link_libraries(
9+
C4Tests PRIVATE
10+
mbedcrypto
11+
mbedx509
12+
)
13+
14+
target_link_options(
15+
C4Tests PRIVATE
16+
"-pthread"
17+
"-fwasm-exceptions"
18+
"-lembind"
19+
"SHELL:-s EXIT_RUNTIME=1"
20+
"SHELL:-s PTHREAD_POOL_SIZE=24"
21+
"SHELL:-s ALLOW_MEMORY_GROWTH=1"
22+
"SHELL:-s DEMANGLE_SUPPORT=1"
23+
"SHELL:-s WASM_BIGINT=1"
24+
"-lnodefs.js"
25+
"-lnoderawfs.js"
26+
)
27+
28+
target_include_directories(
29+
C4Tests PRIVATE
30+
${TOP}LiteCore/Unix
31+
)
32+
endfunction()

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ elseif(ANDROID)
113113
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
114114
option(LITECORE_DYNAMIC_ICU "If enabled, search for ICU at runtime so as not to depend on a specific version" OFF)
115115
include("${PROJECT_SOURCE_DIR}/cmake/platform_linux_desktop.cmake")
116+
elseif(EMSCRIPTEN)
117+
# Emscripten does not actually support shared libraries and instead
118+
# just builds a static library for compatibility with existing
119+
# build setups. We just set this property to suppress a CMake warning.
120+
set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
121+
122+
include("${PROJECT_SOURCE_DIR}/cmake/platform_emscripten.cmake")
116123
else()
117124
message(FATAL_ERROR "Unable to determine a supported platform from ${CMAKE_SYSTEM_NAME}")
118125
endif(MSVC)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//
2+
// UnicodeCollator_JS.cc
3+
//
4+
// Copyright 2017-Present Couchbase, Inc.
5+
//
6+
// Use of this software is governed by the Business Source License included
7+
// in the file licenses/BSL-Couchbase.txt. As of the Change Date specified
8+
// in that file, in accordance with the Business Source License, use of this
9+
// software will be governed by the Apache License, Version 2.0, included in
10+
// the file licenses/APL2.txt.
11+
//
12+
13+
// This is an UnicodeCollaction implementation based on the JS Intl.Collator API.
14+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator
15+
16+
#include "UnicodeCollator.hh"
17+
#include "Error.hh"
18+
#include "emscripten/val.h"
19+
#include "SQLiteCpp/Exception.h"
20+
#include <sqlite3.h>
21+
22+
namespace litecore {
23+
24+
using namespace std;
25+
using namespace emscripten;
26+
27+
class JSCollationContext : public CollationContext {
28+
public:
29+
val collator = val::undefined();
30+
31+
JSCollationContext(const Collation &collation)
32+
: CollationContext(collation) {
33+
auto locale = val::undefined();
34+
auto options = val::object();
35+
36+
if (collation.localeName) {
37+
locale = val(collation.localeName.asString());
38+
}
39+
40+
if (collation.diacriticSensitive) {
41+
if (collation.caseSensitive) {
42+
options.set("sensitivity", "variant");
43+
} else {
44+
options.set("sensitivity", "accent");
45+
}
46+
}
47+
else {
48+
if (collation.caseSensitive) {
49+
options.set("sensitivity", "case");
50+
} else {
51+
options.set("sensitivity", "base");
52+
}
53+
}
54+
55+
collator = val::global("Intl")["Collator"].new_(locale, options);
56+
}
57+
};
58+
59+
unique_ptr<CollationContext> CollationContext::create(const Collation &coll) {
60+
return make_unique<JSCollationContext>(coll);
61+
}
62+
63+
static inline int compareStringsUnicode(int len1, const void *chars1,
64+
int len2, const void *chars2,
65+
const JSCollationContext &ctx) {
66+
return ctx.collator.call<int>("compare", string((const char *)chars1, len1),
67+
string((const char *)chars2, len2));
68+
}
69+
70+
static int collateUnicodeCallback(void *context,
71+
int len1, const void *chars1,
72+
int len2, const void *chars2) {
73+
auto &coll = *(JSCollationContext *)context;
74+
if (coll.canCompareASCII) {
75+
int result = CompareASCII(len1, (const uint8_t *)chars1,
76+
len2, (const uint8_t *)chars2, coll.caseSensitive);
77+
if (result != kCompareASCIIGaveUp)
78+
return result;
79+
}
80+
return compareStringsUnicode(len1, chars1, len2, chars2, coll);
81+
}
82+
83+
int CompareUTF8(slice str1, slice str2, const Collation &coll) {
84+
return CompareUTF8(str1, str2, JSCollationContext(coll));
85+
}
86+
87+
int CompareUTF8(slice str1, slice str2, const CollationContext &ctx) {
88+
return collateUnicodeCallback((void *)&ctx, (int)str1.size, str1.buf,
89+
(int)str2.size, str2.buf);
90+
}
91+
92+
int LikeUTF8(slice str1, slice str2, const Collation &coll) {
93+
return LikeUTF8(str1, str2, JSCollationContext(coll));
94+
}
95+
96+
bool ContainsUTF8(slice str, slice substr, const CollationContext &ctx) {
97+
return ContainsUTF8_Slow(str, substr, ctx);
98+
}
99+
100+
unique_ptr<CollationContext> RegisterSQLiteUnicodeCollation(sqlite3 *dbHandle,
101+
const Collation &coll) {
102+
unique_ptr<CollationContext> context(new JSCollationContext(coll));
103+
int rc = sqlite3_create_collation(dbHandle,
104+
coll.sqliteName().c_str(),
105+
SQLITE_UTF8,
106+
(void *)context.get(),
107+
collateUnicodeCallback);
108+
if (rc != SQLITE_OK)
109+
throw SQLite::Exception(dbHandle, rc);
110+
return context;
111+
}
112+
113+
}

LiteCore/Support/Error.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#include <android/log.h>
3636
#endif
3737

38-
#if defined(__clang__) && !defined(__ANDROID__) // For logBacktrace:
38+
#if defined(__clang__) && !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) // For logBacktrace:
3939
#include <execinfo.h> // Not available in Windows?
4040
#include <cxxabi.h>
4141
#endif

LiteCore/Support/FilePath.cc

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
using namespace std;
5050
using namespace fleece;
5151

52-
#ifdef __linux__
52+
#if defined(__linux__) || defined(__EMSCRIPTEN__)
5353
static int copyfile(const char* from, const char* to)
5454
{
5555
int read_fd, write_fd;
@@ -75,13 +75,38 @@ static int copyfile(const char* from, const char* to)
7575
return write_fd;
7676
}
7777

78+
#ifdef __EMSCRIPTEN__
79+
static const size_t kBufSize = 1024;
80+
uint8_t buf[kBufSize];
81+
while (offset < stat_buf.st_size) {
82+
auto bytes_left_to_read = (size_t)(stat_buf.st_size - offset);
83+
auto max_bytes_to_read = std::min(bytes_left_to_read, kBufSize);
84+
ssize_t bytes_read;
85+
if ((bytes_read = read(read_fd, &buf, max_bytes_to_read)) < 0) {
86+
int e = errno;
87+
close(read_fd);
88+
close(write_fd);
89+
errno = e;
90+
return -1;
91+
}
92+
offset = offset + bytes_read;
93+
if (write(write_fd, &buf, bytes_read) < 0) {
94+
int e = errno;
95+
close(read_fd);
96+
close(write_fd);
97+
errno = e;
98+
return -1;
99+
}
100+
}
101+
#else
78102
if(sendfile(write_fd, read_fd, &offset, stat_buf.st_size) < 0) {
79103
int e = errno;
80104
close(read_fd);
81105
close(write_fd);
82106
errno = e;
83107
return -1;
84108
}
109+
#endif
85110

86111
if(close(read_fd) < 0) {
87112
int e = errno;

LiteCore/Support/LogDecoder.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ namespace litecore {
4747

4848
auto now = time_point_cast<microseconds>(system_clock::now());
4949
auto count = now.time_since_epoch().count();
50-
time_t secs = (time_t)count / 1000000;
50+
time_t secs = (time_t)(count / 1000000);
5151
unsigned microsecs = count % 1000000;
5252
return {secs, microsecs};
5353
}

LiteCore/Support/Logging.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#include <android/log.h>
3131
#endif
3232

33-
#if (defined(__linux__) || defined(__APPLE__)) && !defined(__ANDROID__)
33+
#if (defined(__linux__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)) && !defined(__ANDROID__)
3434
#include <cxxabi.h>
3535
#endif
3636

@@ -604,7 +604,7 @@ namespace litecore {
604604

605605
static std::string classNameOf(const Logging *obj) {
606606
const char *name = typeid(*obj).name();
607-
#if (defined(__linux__) || defined(__APPLE__)) && !defined(__ANDROID__)
607+
#if (defined(__linux__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)) && !defined(__ANDROID__)
608608
// Get the name of my class, unmangle it, and remove namespaces:
609609
size_t unmangledLen;
610610
int status;

LiteCore/Support/MultiLogDecoder.hh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "LogDecoder.hh"
1515
#include <algorithm>
1616
#include <climits>
17+
#include <limits>
1718
#include <fstream>
1819
#include <queue>
1920
#include <string>
@@ -27,9 +28,10 @@ namespace litecore {
2728
class MultiLogDecoder : public LogIterator {
2829
public:
2930
MultiLogDecoder() {
30-
_startTime = {UINT_MAX, 0};
31+
auto max_time_t = std::numeric_limits<time_t>::max();
32+
_startTime = {max_time_t, 0};
3133
for (unsigned i = 0; i <= kMaxLevel; i++)
32-
_startTimeByLevel[i] = {UINT_MAX, 0};
34+
_startTimeByLevel[i] = {max_time_t, 0};
3335
}
3436

3537
/// Adds a log iterator. Must be called before calling \ref next().

0 commit comments

Comments
 (0)