Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions NativeScript/ffi/objc/hermes/NativeApiJsi.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "Metadata.h"
#include "MetadataReader.h"
#include "ffi.h"
#include "ffi/objc/shared/EnumMemberAliases.h"
#include "NativeApiJsiSignatureDispatch.h"
#ifndef NATIVESCRIPT_REACT_NATIVE
// js_jsr_for_runtime: maps the bridge's unsafe jsi::Runtime back to the
Expand Down
1 change: 1 addition & 0 deletions NativeScript/ffi/objc/jsc/NativeApiJSCRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Metadata.h"
#include "MetadataReader.h"
#include "ffi.h"
#include "ffi/objc/shared/EnumMemberAliases.h"

#include "jsi/jsc/JSCRuntime.h"

Expand Down
103 changes: 19 additions & 84 deletions NativeScript/ffi/objc/napi/Enum.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <unordered_set>
#include <vector>
#include "ObjCBridge.h"
#include "ffi/objc/shared/EnumMemberAliases.h"

namespace nativescript {

Expand All @@ -30,10 +31,6 @@ inline void defineConstantIfMissing(napi_env env, napi_value object, const std::
napi_define_properties(env, object, 1, &prop);
}

inline bool startsWith(const std::string& value, const std::string& prefix) {
return value.size() >= prefix.size() && value.compare(0, prefix.size(), prefix) == 0;
}

inline std::string stripEnumSuffix(const std::string& enumName) {
static const std::vector<std::string> suffixes = {
"Options", "Option", "Enums", "Enum", "Result", "Direction", "Orientation",
Expand All @@ -48,38 +45,24 @@ inline bool startsWith(const std::string& value, const std::string& prefix) {

return enumName;
}

inline bool isNSComparisonResultOrderingName(const std::string& enumName,
const std::string& member) {
if (enumName != "NSComparisonResult") {
return false;
}
return member == "Ascending" || member == "Same" || member == "Descending";
}
} // namespace

void ObjCBridgeState::registerEnumGlobals(napi_env env, napi_value global) {
MDSectionOffset offset = metadata->enumsOffset;
while (offset < metadata->signaturesOffset) {
MDSectionOffset originalOffset = offset;
auto name = metadata->getString(offset);
offset += sizeof(MDSectionOffset);

bool next = true;
while (next) {
auto nameOffset = metadata->getOffset(offset);
next = (nameOffset & mdSectionOffsetNext) != 0;
nameOffset &= ~mdSectionOffsetNext;
auto memberName = metadata->resolveString(nameOffset);
offset += sizeof(MDSectionOffset);
int64_t value = metadata->getEnumValue(offset);
offset += sizeof(int64_t);

napi_value member;
napi_create_int64(env, value, &member);
defineConstantIfMissing(env, global, memberName, member,
(napi_property_attributes)(napi_enumerable | napi_configurable));
}

offset = metadata->forEachEnumMember(
originalOffset, [&](const char*, const std::string& cName, int64_t value) {
if (cName.empty()) {
return;
}
napi_value member;
napi_create_int64(env, value, &member);
defineConstantIfMissing(env, global, cName, member,
(napi_property_attributes)(napi_enumerable | napi_configurable));
});

napi_property_descriptor prop = {
.utf8name = name,
Expand Down Expand Up @@ -112,71 +95,23 @@ inline bool isNSComparisonResultOrderingName(const std::string& enumName,
// enum name
auto enumName = bridgeState->metadata->getString(offset);
std::string enumNameStr = enumName;
offset += sizeof(MDSectionOffset);

std::string strippedPrefix = stripEnumSuffix(enumNameStr);

napi_value global;
napi_get_global(env, &global);

bool next = true;
while (next) {
auto nameOffset = bridgeState->metadata->getOffset(offset);
next = (nameOffset & mdSectionOffsetNext) != 0;
nameOffset &= ~mdSectionOffsetNext;
auto name = bridgeState->metadata->resolveString(nameOffset);
offset += sizeof(MDSectionOffset);
int64_t value = bridgeState->metadata->getEnumValue(offset);
offset += sizeof(int64_t);

bridgeState->metadata->forEachEnumMember(originalOffset, [&](const char* name,
const std::string& cName,
int64_t value) {
napi_value member;
napi_create_int64(env, value, &member);

std::string memberName = name;
std::vector<std::string> aliases;
aliases.push_back(memberName);

if (!strippedPrefix.empty() && startsWith(memberName, strippedPrefix) &&
memberName.size() > strippedPrefix.size()) {
aliases.push_back(memberName.substr(strippedPrefix.size()));
} else if (!strippedPrefix.empty() && !startsWith(memberName, strippedPrefix)) {
aliases.push_back(strippedPrefix + memberName);
}
auto aliases = enum_members::aliasesFor(enumNameStr, strippedPrefix, memberName, cName);

if (startsWith(enumNameStr, "NS") && !startsWith(memberName, "NS")) {
aliases.push_back(std::string("NS") + memberName);
}

if (enumNameStr == "NSStringCompareOptions" && !memberName.ends_with("Search")) {
aliases.push_back(memberName + "Search");
aliases.push_back(std::string("NS") + memberName + "Search");
}

if (!startsWith(memberName, "k")) {
aliases.push_back(std::string("k") + enumNameStr + memberName);
}

if (isNSComparisonResultOrderingName(enumNameStr, memberName)) {
aliases.push_back(std::string("Ordered") + memberName);
aliases.push_back(std::string("NSOrdered") + memberName);
}

std::vector<std::string> uniqueAliases;
uniqueAliases.reserve(aliases.size());
std::unordered_set<std::string> seenAliases;
for (const auto& alias : aliases) {
if (seenAliases.insert(alias).second) {
uniqueAliases.push_back(alias);
}
defineConstantIfMissing(env, result, alias.name, member);
}

for (const auto& alias : uniqueAliases) {
defineConstantIfMissing(env, result, alias, member);
defineConstantIfMissing(env, global, alias, member,
(napi_property_attributes)(napi_enumerable | napi_configurable));
}

std::string reverseCanonical = uniqueAliases.size() > 1 ? uniqueAliases[1] : memberName;
const std::string& reverseCanonical = aliases.size() > 1 ? aliases[1].name : memberName;

// reverse mapping enum[value] -> canonical member name
char valueKey[32];
Expand All @@ -197,7 +132,7 @@ inline bool isNSComparisonResultOrderingName(const std::string& enumName,
};
napi_define_properties(env, result, 1, &reverseProp);
}
}
});

bridgeState->mdValueCache[originalOffset] = make_ref(env, result);

Expand Down
1 change: 1 addition & 0 deletions NativeScript/ffi/objc/quickjs/NativeApiQuickJSRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "Metadata.h"
#include "MetadataReader.h"
#include "ffi.h"
#include "ffi/objc/shared/EnumMemberAliases.h"

#include "jsi/quickjs/QuickJSRuntime.h"

Expand Down
85 changes: 85 additions & 0 deletions NativeScript/ffi/objc/shared/EnumMemberAliases.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#ifndef NATIVESCRIPT_FFI_OBJC_SHARED_ENUM_MEMBER_ALIASES_H
#define NATIVESCRIPT_FFI_OBJC_SHARED_ENUM_MEMBER_ALIASES_H

#include <string>
#include <unordered_set>
#include <vector>

namespace nativescript {
namespace enum_members {

struct Alias {
std::string name;
// Only the C name may become a global; the other aliases are enum-object
// conveniences and can shadow unrelated globals (e.g. Center, Zone).
bool isCName = false;
};

inline bool startsWith(const std::string& value, const std::string& prefix) {
return value.size() >= prefix.size() &&
value.compare(0, prefix.size(), prefix) == 0;
}

inline bool endsWith(const std::string& value, const std::string& suffix) {
return value.size() >= suffix.size() &&
value.compare(value.size() - suffix.size(), suffix.size(), suffix) ==
0;
}

// The first alias is always the stored member name. cName is empty when the
// metadata does not record C names; then no alias is tagged as the C name.
inline std::vector<Alias> aliasesFor(const std::string& enumName,
const std::string& strippedEnumName,
const std::string& storedName,
const std::string& cName) {
std::vector<std::string> names;
names.push_back(storedName);

if (!strippedEnumName.empty() && startsWith(storedName, strippedEnumName) &&
storedName.size() > strippedEnumName.size()) {
names.push_back(storedName.substr(strippedEnumName.size()));
} else if (!strippedEnumName.empty() &&
!startsWith(storedName, strippedEnumName)) {
names.push_back(strippedEnumName + storedName);
}

if (startsWith(enumName, "NS") && !startsWith(storedName, "NS")) {
names.push_back(std::string("NS") + storedName);
}

if (enumName == "NSStringCompareOptions" && !endsWith(storedName, "Search")) {
names.push_back(storedName + "Search");
names.push_back(std::string("NS") + storedName + "Search");
}

if (!startsWith(storedName, "k")) {
names.push_back(std::string("k") + enumName + storedName);
}

if (enumName == "NSComparisonResult" &&
(storedName == "Ascending" || storedName == "Same" ||
storedName == "Descending")) {
names.push_back(std::string("Ordered") + storedName);
names.push_back(std::string("NSOrdered") + storedName);
}

if (!cName.empty()) {
names.push_back(cName);
}

std::vector<Alias> aliases;
aliases.reserve(names.size());
std::unordered_set<std::string> seen;
for (auto& name : names) {
if (!name.empty() && seen.insert(name).second) {
bool isCName = name == cName;
aliases.push_back(Alias{std::move(name), isCName});
}
}
return aliases;
}

} // namespace enum_members
} // namespace nativescript

#endif // NATIVESCRIPT_FFI_OBJC_SHARED_ENUM_MEMBER_ALIASES_H
16 changes: 16 additions & 0 deletions NativeScript/ffi/objc/shared/bridge/HostObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,21 @@ throw JSError(runtime,
return enumToObject(runtime, bridge->metadata(), *symbol);
});
}
if (property == "getEnumGlobalNames") {
auto bridge = bridge_;
return Function::createFromHostFunction(
runtime, PropNameID::forAscii(runtime, "getEnumGlobalNames"), 1,
[bridge](Runtime& runtime, const Value&, const Value* args,
size_t count) -> Value {
std::string enumName = readStringArg(runtime, args, count, 0, "name");
const NativeApiSymbol* symbol = bridge->findEnum(enumName);
if (symbol == nullptr) {
return namesToArray(runtime, {});
}
return namesToArray(runtime,
enumGlobalNames(bridge->metadata(), *symbol));
});
}
if (property == "getProtocol") {
auto bridge = bridge_;
return Function::createFromHostFunction(
Expand Down Expand Up @@ -528,6 +543,7 @@ throw JSError(runtime,
addPropertyName(runtime, names, "getFunction");
addPropertyName(runtime, names, "getConstant");
addPropertyName(runtime, names, "getEnum");
addPropertyName(runtime, names, "getEnumGlobalNames");
addPropertyName(runtime, names, "getProtocol");
addPropertyName(runtime, names, "getStruct");
addPropertyName(runtime, names, "getUnion");
Expand Down
12 changes: 3 additions & 9 deletions NativeScript/ffi/objc/shared/bridge/Install.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1754,16 +1754,10 @@ function resolveNativeApiEnum(enumName) {
});
enumNameList.forEach(function(name) {
defineLazyGlobal(name, resolveNativeApiEnum, false, 'enum');
var enumValue = resolveNativeApiEnum(name);
if (!enumValue || typeof enumValue !== 'object') {
return;
}
Object.keys(enumValue).forEach(function(memberName) {
if (/^-?\d+$/.test(memberName)) {
return;
}
api.getEnumGlobalNames(name).forEach(function(memberName) {
defineLazyGlobal(memberName, function() {
return enumValue[memberName];
var enumValue = resolveNativeApiEnum(name);
return enumValue && enumValue[memberName];
}, false, 'enumMember:' + name);
});
});
Expand Down
Loading
Loading