Skip to content

Commit 01c15a7

Browse files
committed
Add tests for Web and Node builtins in WebNodeBuiltins.js
- Implement tests to verify the presence and functionality of web globals such as fetch, Headers, Request, Response, WebSocket, and ReadableStream. - Include tests for internal web modules and ensure case-insensitivity in Headers. - Add tests for Request and Response body helpers, fetch URL validation, and WebSocket URL scheme validation. - Introduce tests for Node's fs and node:fs modules, covering file reading/writing, binary data handling, directory operations, and support for both callback and promise forms. - Ensure dynamic import functionality for web and fs modules is tested.
1 parent 3368c98 commit 01c15a7

13 files changed

Lines changed: 3499 additions & 25 deletions

File tree

NativeScript/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,13 @@ if(ENABLE_JS_RUNTIME)
151151
runtime/modules/worker/WorkerImpl.mm
152152
runtime/modules/worker/WorkerImpl.mm
153153
runtime/modules/module/ModuleInternal.cpp
154+
runtime/modules/node/Node.cpp
155+
runtime/modules/node/FS.cpp
154156
runtime/modules/performance/Performance.cpp
155157
runtime/Bundle.mm
156158
runtime/modules/timers/Timers.mm
157159
runtime/modules/app/App.mm
160+
runtime/modules/web/Web.mm
158161
runtime/NativeScript.mm
159162
runtime/RuntimeConfig.cpp
160163
runtime/modules/url/ada/ada.cpp

NativeScript/ffi/TypeConv.mm

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,12 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
13751375
resolvedType->type->size > 0) {
13761376
pointeeSize = resolvedType->type->size;
13771377
}
1378-
ref->data = malloc(pointeeSize);
1378+
ref->data = calloc(1, pointeeSize);
1379+
if (ref->data == nullptr) {
1380+
napi_throw_error(env, nullptr, "Out of memory while allocating out parameter");
1381+
*res = nullptr;
1382+
return;
1383+
}
13791384
ref->ownsData = true;
13801385
if (ref->initValue) {
13811386
napi_value initValue = get_ref_value(env, ref->initValue);
@@ -2572,6 +2577,15 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
25722577
return info;
25732578
}
25742579

2580+
inline size_t getStructSize(napi_env env) {
2581+
if (this->type != nullptr && this->type->size > 0) {
2582+
return this->type->size;
2583+
}
2584+
2585+
auto info = getInfo(env);
2586+
return info != nullptr ? info->size : 0;
2587+
}
2588+
25752589
napi_value toJS(napi_env env, void* value, uint32_t flags) override {
25762590
auto info = getInfo(env);
25772591

@@ -2590,6 +2604,12 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
25902604
bool* shouldFreeAny) override {
25912605
NAPI_PREAMBLE
25922606

2607+
const size_t structSize = getStructSize(env);
2608+
if (structSize == 0) {
2609+
napi_throw_type_error(env, "TypeError", "Invalid struct size");
2610+
return;
2611+
}
2612+
25932613
bool isTypedArray = false;
25942614
napi_is_typedarray(env, value, &isTypedArray);
25952615

@@ -2601,8 +2621,10 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
26012621
NAPI_THROW_LAST_ERROR
26022622
return;
26032623
}
2604-
2605-
memcpy(result, data, length * getTypedArrayUnitLength(type));
2624+
const size_t unitLength = getTypedArrayUnitLength(type);
2625+
const size_t byteLength = length * unitLength;
2626+
memset(result, 0, structSize);
2627+
memcpy(result, data, std::min(byteLength, structSize));
26062628

26072629
return;
26082630
}
@@ -2631,7 +2653,9 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
26312653

26322654
auto structObject = StructObject::unwrap(env, value);
26332655
if (structObject != nullptr) {
2634-
memcpy(result, structObject->data, structObject->info->size);
2656+
const size_t copySize = std::min(static_cast<size_t>(structObject->info->size), structSize);
2657+
memset(result, 0, structSize);
2658+
memcpy(result, structObject->data, copySize);
26352659
return;
26362660
}
26372661

@@ -2643,7 +2667,14 @@ void toNative(napi_env env, napi_value value, void* result, bool* shouldFree,
26432667
return;
26442668
}
26452669

2646-
// Serialize directly to previously allocated memory
2670+
if (structSize < info->size) {
2671+
std::vector<uint8_t> storage(info->size, 0);
2672+
StructObject(env, info, value, storage.data());
2673+
memcpy(result, storage.data(), structSize);
2674+
return;
2675+
}
2676+
2677+
// Serialize directly to previously allocated memory.
26472678
StructObject(env, info, value, result);
26482679
}
26492680
};
@@ -2868,8 +2899,7 @@ void copyToInlineArrayStorage(napi_env env, napi_value value, void* result, bool
28682899
std::shared_ptr<TypeConv> elementType;
28692900
MDTypeKind vectorKind;
28702901

2871-
VectorTypeConv(MDTypeKind vectorKind, uint16_t vectorSize,
2872-
std::shared_ptr<TypeConv> elementType)
2902+
VectorTypeConv(MDTypeKind vectorKind, uint16_t vectorSize, std::shared_ptr<TypeConv> elementType)
28732903
: vectorSize(vectorSize), elementType(elementType), vectorKind(vectorKind) {
28742904
auto vectorType = new ffi_type();
28752905
#if defined(FFI_TYPE_EXT_VECTOR)
@@ -2882,10 +2912,12 @@ void copyToInlineArrayStorage(napi_env env, napi_value value, void* result, bool
28822912
size_t abiLanes = lanes == 3 ? 4 : lanes;
28832913
vectorType->elements = (ffi_type**)malloc(sizeof(ffi_type*) * (abiLanes + 1));
28842914

2885-
ffi_type* elementFfiType =
2886-
elementType != nullptr && elementType->type != nullptr ? elementType->type : &ffi_type_float;
2915+
ffi_type* elementFfiType = elementType != nullptr && elementType->type != nullptr
2916+
? elementType->type
2917+
: &ffi_type_float;
28872918
const size_t elementSize = std::max<size_t>(elementFfiType->size, sizeof(float));
2888-
const size_t elementAlignment = std::max<size_t>(elementFfiType->alignment, static_cast<size_t>(1));
2919+
const size_t elementAlignment =
2920+
std::max<size_t>(elementFfiType->alignment, static_cast<size_t>(1));
28892921

28902922
for (size_t i = 0; i < abiLanes; i++) {
28912923
vectorType->elements[i] = elementFfiType;

NativeScript/napi/v8/v8-module-loader.cpp

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <filesystem>
88
#include <fstream>
99
#include <sstream>
10+
#include <unordered_set>
1011

1112
#include "runtime/RuntimeConfig.h"
1213

@@ -57,7 +58,11 @@ std::string ModulePathToURL(const std::string& modulePath) {
5758
}
5859

5960
bool IsNodeBuiltinSpecifier(const std::string& specifier) {
60-
return specifier == "node:url" || specifier == "url";
61+
static const std::unordered_set<std::string> kBuiltins = {
62+
"url", "node:url", "fs", "node:fs",
63+
"fs/promises", "node:fs/promises", "web", "node:web",
64+
"stream/web", "node:stream/web"};
65+
return kBuiltins.contains(specifier);
6166
}
6267

6368
std::string NormalizeNodeBuiltinSpecifier(const std::string& specifier) {
@@ -89,6 +94,109 @@ export default { URL, URLSearchParams, pathToFileURL, fileURLToPath };
8994
)";
9095
}
9196

97+
if (builtinName == "fs") {
98+
return R"(
99+
const __load = (name) => {
100+
if (typeof globalThis.require === "function") {
101+
return globalThis.require(name);
102+
}
103+
if (typeof globalThis.__nativeRequire === "function") {
104+
const dir = typeof globalThis.__approot === "string" ? `${globalThis.__approot}/app` : "";
105+
return globalThis.__nativeRequire(name, dir);
106+
}
107+
throw new Error(`Cannot load builtin module '${name}'`);
108+
};
109+
const __fs = __load("node:fs");
110+
export const readFileSync = __fs.readFileSync;
111+
export const writeFileSync = __fs.writeFileSync;
112+
export const existsSync = __fs.existsSync;
113+
export const mkdirSync = __fs.mkdirSync;
114+
export const readdirSync = __fs.readdirSync;
115+
export const statSync = __fs.statSync;
116+
export const lstatSync = __fs.lstatSync;
117+
export const unlinkSync = __fs.unlinkSync;
118+
export const rmSync = __fs.rmSync;
119+
export const readFile = __fs.readFile;
120+
export const writeFile = __fs.writeFile;
121+
export const constants = __fs.constants;
122+
export const promises = __fs.promises;
123+
export default __fs;
124+
)";
125+
}
126+
127+
if (builtinName == "fs/promises") {
128+
return R"(
129+
const __load = (name) => {
130+
if (typeof globalThis.require === "function") {
131+
return globalThis.require(name);
132+
}
133+
if (typeof globalThis.__nativeRequire === "function") {
134+
const dir = typeof globalThis.__approot === "string" ? `${globalThis.__approot}/app` : "";
135+
return globalThis.__nativeRequire(name, dir);
136+
}
137+
throw new Error(`Cannot load builtin module '${name}'`);
138+
};
139+
const __fsp = __load("node:fs").promises;
140+
export const readFile = __fsp.readFile;
141+
export const writeFile = __fsp.writeFile;
142+
export const mkdir = __fsp.mkdir;
143+
export const readdir = __fsp.readdir;
144+
export const stat = __fsp.stat;
145+
export const lstat = __fsp.lstat;
146+
export const unlink = __fsp.unlink;
147+
export const rm = __fsp.rm;
148+
export default __fsp;
149+
)";
150+
}
151+
152+
if (builtinName == "web") {
153+
return R"(
154+
const __load = (name) => {
155+
if (typeof globalThis.require === "function") {
156+
return globalThis.require(name);
157+
}
158+
if (typeof globalThis.__nativeRequire === "function") {
159+
const dir = typeof globalThis.__approot === "string" ? `${globalThis.__approot}/app` : "";
160+
return globalThis.__nativeRequire(name, dir);
161+
}
162+
throw new Error(`Cannot load builtin module '${name}'`);
163+
};
164+
const __web = __load("web");
165+
export const fetch = __web.fetch;
166+
export const Headers = __web.Headers;
167+
export const Request = __web.Request;
168+
export const Response = __web.Response;
169+
export const WebSocket = __web.WebSocket;
170+
export const ReadableStream = __web.ReadableStream;
171+
export const WritableStream = __web.WritableStream;
172+
export const TransformStream = __web.TransformStream;
173+
export default __web;
174+
)";
175+
}
176+
177+
if (builtinName == "stream/web") {
178+
return R"(
179+
const __load = (name) => {
180+
if (typeof globalThis.require === "function") {
181+
return globalThis.require(name);
182+
}
183+
if (typeof globalThis.__nativeRequire === "function") {
184+
const dir = typeof globalThis.__approot === "string" ? `${globalThis.__approot}/app` : "";
185+
return globalThis.__nativeRequire(name, dir);
186+
}
187+
throw new Error(`Cannot load builtin module '${name}'`);
188+
};
189+
const __streamWeb = __load("stream/web");
190+
export const ReadableStream = __streamWeb.ReadableStream;
191+
export const ReadableStreamDefaultReader = __streamWeb.ReadableStreamDefaultReader;
192+
export const WritableStream = __streamWeb.WritableStream;
193+
export const TransformStream = __streamWeb.TransformStream;
194+
export const ByteLengthQueuingStrategy = __streamWeb.ByteLengthQueuingStrategy;
195+
export const CountQueuingStrategy = __streamWeb.CountQueuingStrategy;
196+
export default __streamWeb;
197+
)";
198+
}
199+
92200
return "";
93201
}
94202

@@ -456,6 +564,7 @@ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback(
456564
isolate, "Failed to resolve module")
457565
.ToLocalChecked()))
458566
.Check();
567+
isolate->PerformMicrotaskCheckpoint();
459568
return scope.Escape(resolver->GetPromise());
460569
}
461570

@@ -469,6 +578,7 @@ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback(
469578
isolate, "Failed to instantiate module")
470579
.ToLocalChecked()))
471580
.Check();
581+
isolate->PerformMicrotaskCheckpoint();
472582
return scope.Escape(resolver->GetPromise());
473583
}
474584
}
@@ -481,11 +591,13 @@ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback(
481591
isolate, "Failed to evaluate module")
482592
.ToLocalChecked()))
483593
.Check();
594+
isolate->PerformMicrotaskCheckpoint();
484595
return scope.Escape(resolver->GetPromise());
485596
}
486597
}
487598

488599
resolver->Resolve(context, module->GetModuleNamespace()).Check();
600+
isolate->PerformMicrotaskCheckpoint();
489601

490602
} catch (const std::exception& e) {
491603
resolver
@@ -494,6 +606,7 @@ v8::MaybeLocal<v8::Promise> ImportModuleDynamicallyCallback(
494606
v8::Exception::Error(
495607
v8::String::NewFromUtf8(isolate, e.what()).ToLocalChecked()))
496608
.Check();
609+
isolate->PerformMicrotaskCheckpoint();
497610
}
498611

499612
return scope.Escape(resolver->GetPromise());

NativeScript/runtime/modules/RuntimeModules.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
#include "console/Console.h"
55
#include "js_native_api_types.h"
6+
#include "node/Node.h"
67
#include "performance/Performance.h"
78
#include "runtime/RuntimeConfig.h"
89
#include "runtime/modules/module/ModuleInternal.h"
910
#include "runtime/modules/worker/Worker.h"
1011
#include "url/URL.h"
1112
#include "url/URLSearchParams.h"
13+
#include "web/Web.h"
1214
#ifdef __APPLE__
1315
#include "app/App.h"
1416
#include "timers/Timers.h"
@@ -28,10 +30,12 @@ class RuntimeModules {
2830

2931
Console::Init(env, global);
3032
Performance::Init(env, global);
33+
Node::Init(env, global);
3134

3235
#ifdef __APPLE__
3336
App::Init(env);
3437
Timers::Init(env, global);
38+
Web::Init(env, global);
3539
#endif // __APPLE__
3640

3741
Worker::Init(env, global);

0 commit comments

Comments
 (0)