Skip to content

feat: ES modules (ESM) support with conditional esm or commonjs consumption + better error handling #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a2a35d4
fix(catalyst): variable-length arrays
NathanWalker Jul 11, 2025
98d9ad5
feat: support conditional esm, .mjs, consumption
NathanWalker Jul 16, 2025
f42605c
feat: consume esm if .mjs is found + better error handling
NathanWalker Jul 16, 2025
485570c
feat: dynamic import, lazy loaded chunks wip
NathanWalker Jul 16, 2025
b7ac75c
feat: dynamic import support for lazy loading
NathanWalker Jul 16, 2025
f72b565
chore: 9.0.0-esm.1
NathanWalker Jul 16, 2025
b2b65fa
feat: handling external modules
NathanWalker Jul 16, 2025
bd6aee6
feat: provide valid import.meta.url behavior
NathanWalker Jul 17, 2025
109775b
feat: more verbose exception output
NathanWalker Jul 18, 2025
b81746d
feat: workers
NathanWalker Jul 18, 2025
828d022
chore: 9.0.0-esm.2
NathanWalker Jul 18, 2025
5add9e3
feat: worker improvements
NathanWalker Jul 19, 2025
8b78f90
feat: import.meta improvements
NathanWalker Jul 19, 2025
9c482b8
chore: safe global registry handling
NathanWalker Jul 23, 2025
b6d497e
feat: more robust commonjs vs esm conditional handling
NathanWalker Jul 23, 2025
0d0d3b1
chore: 9.0.0-esm.4
NathanWalker Jul 23, 2025
091358d
chore: 9.0.0-esm.5
NathanWalker Jul 24, 2025
2891409
chore: fix tests and add more robustness to module resolution cases
NathanWalker Jul 25, 2025
25a4e90
chore: 9.0.0-esm.6
NathanWalker Jul 25, 2025
37cde9b
fix: allow dynamic import chunks to resolve properly under diverse bu…
NathanWalker Jul 27, 2025
ef9a75a
chore: 9.0.0-esm.7
NathanWalker Jul 27, 2025
5acef93
feat: improved exception handling to not stop execution and show stac…
NathanWalker Jul 29, 2025
db0289d
chore: 9.0.0-esm.8
NathanWalker Jul 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion NativeScript/NativeScript-Prefix.pch
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef NativeScript_Prefix_pch
#define NativeScript_Prefix_pch

#define NATIVESCRIPT_VERSION "8.9.0"
#define NATIVESCRIPT_VERSION "9.0.0-esm.8"

#ifdef DEBUG
#define SIZEOF_OFF_T 8
Expand Down
182 changes: 96 additions & 86 deletions NativeScript/runtime/FFICall.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,117 +2,127 @@
#define FFICall_h

#include <malloc/malloc.h>

#include <map>
#include "robin_hood.h"
#include "Metadata.h"

#include "DataWrapper.h"
#include "Metadata.h"
#include "libffi.h"
#include "robin_hood.h"

namespace tns {

class BaseCall {
public:
BaseCall(uint8_t* buffer, size_t returnOffset = 0)
: buffer_(buffer),
returnOffset_(returnOffset) {
}
public:
BaseCall(uint8_t* buffer, size_t returnOffset = 0)
: buffer_(buffer), returnOffset_(returnOffset) {}

~BaseCall() {
}
~BaseCall() {}

inline void* ResultBuffer() {
return this->buffer_ + this->returnOffset_;
}
inline void* ResultBuffer() { return this->buffer_ + this->returnOffset_; }

template <typename T>
inline T& GetResult() {
return *static_cast<T*>(this->ResultBuffer());
}
protected:
uint8_t* buffer_;
size_t returnOffset_;
template <typename T>
inline T& GetResult() {
return *static_cast<T*>(this->ResultBuffer());
}

protected:
uint8_t* buffer_;
size_t returnOffset_;
};

class ParametrizedCall {
public:
ParametrizedCall(ffi_cif* cif)
: Cif(cif),
ReturnOffset(0),
StackSize(0) {
unsigned int argsCount = cif->nargs;
this->StackSize = 0;

if (argsCount > 0) {
this->StackSize = malloc_good_size(sizeof(void* [argsCount]));
}

this->ReturnOffset = this->StackSize;

this->StackSize += malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg)));

this->ArgValueOffsets.reserve(argsCount);
for (size_t i = 0; i < argsCount; i++) {
this->ArgValueOffsets.push_back(this->StackSize);
ffi_type* argType = cif->arg_types[i];
this->StackSize += malloc_good_size(std::max(argType->size, sizeof(ffi_arg)));
}
public:
ParametrizedCall(ffi_cif* cif) : Cif(cif), ReturnOffset(0), StackSize(0) {
unsigned int argsCount = cif->nargs;
this->StackSize = 0;

if (argsCount > 0) {
// compute total bytes = number of pointers × size of a pointer
size_t needed = sizeof(void*) * static_cast<size_t>(argsCount);
this->StackSize = malloc_good_size(needed);
}

static ParametrizedCall* Get(const TypeEncoding* typeEncoding, const int initialParameterIndex, const int argsCount);
this->ReturnOffset = this->StackSize;

ffi_cif* Cif;
size_t ReturnOffset;
size_t StackSize;
std::vector<size_t> ArgValueOffsets;
private:
static robin_hood::unordered_map<const TypeEncoding*, ParametrizedCall*> callsCache_;
};
this->StackSize +=
malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg)));

class FFICall: public BaseCall {
public:
FFICall(ParametrizedCall* parametrizedCall): BaseCall(nullptr) {
this->returnOffset_ = parametrizedCall->ReturnOffset;
this->useDynamicBuffer_ = parametrizedCall->StackSize > 512;
if(this->useDynamicBuffer_) {
this->buffer_ = reinterpret_cast<uint8_t*>(malloc(parametrizedCall->StackSize));
} else {
this->buffer_ = reinterpret_cast<uint8_t*>(this->staticBuffer);
}

this->argsArray_ = reinterpret_cast<void**>(this->buffer_);
for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) {
this->argsArray_[i] = this->buffer_ + parametrizedCall->ArgValueOffsets[i];
}
this->ArgValueOffsets.reserve(argsCount);
for (size_t i = 0; i < argsCount; i++) {
this->ArgValueOffsets.push_back(this->StackSize);
ffi_type* argType = cif->arg_types[i];
this->StackSize +=
malloc_good_size(std::max(argType->size, sizeof(ffi_arg)));
}
}

~FFICall() {
if(this->useDynamicBuffer_) {
free(this->buffer_);
}
}
static ParametrizedCall* Get(const TypeEncoding* typeEncoding,
const int initialParameterIndex,
const int argsCount);

/**
When calling this, always make another call to DisposeFFIType with the same parameters
*/
static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding, bool isStructMember = false);
static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding);
static StructInfo GetStructInfo(const StructMeta* structMeta, std::string structName = "");
static StructInfo GetStructInfo(size_t fieldsCount, const TypeEncoding* fieldEncoding, const String* fieldNames, std::string structName = "");
ffi_cif* Cif;
size_t ReturnOffset;
size_t StackSize;
std::vector<size_t> ArgValueOffsets;

private:
static robin_hood::unordered_map<const TypeEncoding*, ParametrizedCall*>
callsCache_;
};

class FFICall : public BaseCall {
public:
FFICall(ParametrizedCall* parametrizedCall) : BaseCall(nullptr) {
this->returnOffset_ = parametrizedCall->ReturnOffset;
this->useDynamicBuffer_ = parametrizedCall->StackSize > 512;
if (this->useDynamicBuffer_) {
this->buffer_ =
reinterpret_cast<uint8_t*>(malloc(parametrizedCall->StackSize));
} else {
this->buffer_ = reinterpret_cast<uint8_t*>(this->staticBuffer);
}

inline void* ArgumentBuffer(unsigned index) {
return this->argsArray_[index];
this->argsArray_ = reinterpret_cast<void**>(this->buffer_);
for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) {
this->argsArray_[i] =
this->buffer_ + parametrizedCall->ArgValueOffsets[i];
}
}

inline void** ArgsArray() {
return this->argsArray_;
~FFICall() {
if (this->useDynamicBuffer_) {
free(this->buffer_);
}
private:
static robin_hood::unordered_map<std::string, StructInfo> structInfosCache_;
void** argsArray_;
bool useDynamicBuffer_;
uint8_t staticBuffer[512];
}

/**
When calling this, always make another call to DisposeFFIType with the same
parameters
*/
static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding,
bool isStructMember = false);
static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding);
static StructInfo GetStructInfo(const StructMeta* structMeta,
std::string structName = "");
static StructInfo GetStructInfo(size_t fieldsCount,
const TypeEncoding* fieldEncoding,
const String* fieldNames,
std::string structName = "");

inline void* ArgumentBuffer(unsigned index) {
return this->argsArray_[index];
}

inline void** ArgsArray() { return this->argsArray_; }

private:
static robin_hood::unordered_map<std::string, StructInfo> structInfosCache_;
void** argsArray_;
bool useDynamicBuffer_;
uint8_t staticBuffer[512];
};

}
} // namespace tns

#endif /* FFICall_h */
Loading
Loading