Skip to content
Merged
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
2 changes: 1 addition & 1 deletion NativeScript/runtime/ArgConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ArgConverter {
// recycled the address and give it a foreign prototype.
static std::shared_ptr<v8::Persistent<v8::Value>> FindCachedInstance(
v8::Isolate* isolate, const std::shared_ptr<Caches>& cache, id target);
static const Meta* GetMeta(std::string name);
static const Meta* GetMeta(const std::string& name);
static const ProtocolMeta* FindProtocolMeta(Protocol* protocol);
static void MethodCallback(ffi_cif* cif, void* retValue, void** argValues,
void* userData);
Expand Down
8 changes: 3 additions & 5 deletions NativeScript/runtime/ArgConverter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,8 @@
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
target = objcWrapper->Data();

std::string className = object_getClassName(target);
auto cache = Caches::Get(isolate);
auto it = cache->ClassPrototypes.find(className);
auto it = cache->ClassPrototypes.find(std::string_view(object_getClassName(target)));
// For extended classes we will call the base method
callSuper = isMethodCallback && it != cache->ClassPrototypes.end();
} else {
Expand Down Expand Up @@ -910,8 +909,7 @@
Class klass = [target class];
const Meta* meta = FindMeta(klass, typeEncoding);
if (meta != nullptr) {
std::string className = object_getClassName(target);
auto it = cache->ClassPrototypes.find(className);
auto it = cache->ClassPrototypes.find(std::string_view(object_getClassName(target)));
if (it != cache->ClassPrototypes.end()) {
// for debugging rlv cell handling:
// NSString* message = [NSString stringWithFormat:@"ArgConverter::CreateJsWrapper FindMeta:
Expand Down Expand Up @@ -1013,7 +1011,7 @@
return nullptr;
}

const Meta* ArgConverter::GetMeta(std::string name) {
const Meta* ArgConverter::GetMeta(const std::string& name) {
bool found;
const Meta* meta = Caches::Metadata->Get(name, found);
if (meta != nullptr || found) {
Expand Down
21 changes: 20 additions & 1 deletion NativeScript/runtime/Caches.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define Caches_h

#include <string>
#include <string_view>
#include <vector>

#include "Common.h"
Expand All @@ -16,6 +17,23 @@ struct ObjectWeakCallbackState;
class PromiseRejectionTracker;
class IsolateTracked;

// Declaring both halves transparent lets robin_hood probe a map keyed by
// std::string with a string_view, so callers holding a const char* from the
// Obj-C runtime do not have to allocate one just to look up.
struct TransparentStringHash {
using is_transparent = void;
size_t operator()(std::string_view key) const {
return robin_hood::hash_bytes(key.data(), key.size());
}
};

struct TransparentStringEqual {
using is_transparent = void;
bool operator()(std::string_view lhs, std::string_view rhs) const {
return lhs == rhs;
}
};

struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const {
Expand Down Expand Up @@ -101,7 +119,8 @@ class Caches {
std::unique_ptr<v8::Persistent<v8::Value>>>
Prototypes;
robin_hood::unordered_map<std::string,
std::unique_ptr<v8::Persistent<v8::Object>>>
std::unique_ptr<v8::Persistent<v8::Object>>,
TransparentStringHash, TransparentStringEqual>
ClassPrototypes;
robin_hood::unordered_map<
const BaseClassMeta*,
Expand Down
60 changes: 30 additions & 30 deletions NativeScript/runtime/ConcurrentMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,36 @@ namespace tns {
template<class TKey, class TValue>
class ConcurrentMap {
public:
inline void Insert(TKey& key, TValue value) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
this->container_[key] = value;
}

inline TValue Get(TKey& key) {
bool found;
return this->Get(key, found);
}

inline TValue Get(TKey& key, bool& found) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
auto it = this->container_.find(key);
found = it != this->container_.end();
if (found) {
return it->second;
}
return nullptr;
}

inline bool ContainsKey(TKey& key) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
auto it = this->container_.find(key);
return it != this->container_.end();
}

inline void Remove(TKey& key) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
this->container_.erase(key);
}
inline void Insert(const TKey& key, TValue value) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
this->container_[key] = value;
}

inline TValue Get(const TKey& key) {
bool found;
return this->Get(key, found);
}

inline TValue Get(const TKey& key, bool& found) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
auto it = this->container_.find(key);
found = it != this->container_.end();
if (found) {
return it->second;
}
return nullptr;
}

inline bool ContainsKey(const TKey& key) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
auto it = this->container_.find(key);
return it != this->container_.end();
}

inline void Remove(const TKey& key) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
this->container_.erase(key);
}

inline void ForEach(const std::function<bool(TKey&, TValue&)>& func) {
std::lock_guard<std::mutex> writerLock(this->containerMutex_);
Expand Down
145 changes: 109 additions & 36 deletions NativeScript/runtime/Helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,59 +41,134 @@ inline v8::Local<v8::String> ToV8String(v8::Isolate* isolate, const char* value,
return v8::String::NewFromUtf8(isolate, value, v8::NewStringType::kNormal, length)
.ToLocalChecked();
}

// Without this overload a bare `const char*` — every string literal, every
// c_str(), every jsName() — picks the std::string one and pays for a temporary
// just to reach V8.
inline v8::Local<v8::String> ToV8String(v8::Isolate* isolate, const char* value) {
return v8::String::NewFromUtf8(isolate, value).ToLocalChecked();
}
#ifdef __OBJC__
// Both sides store text as either 8-bit or UTF-16, never UTF-8, so the buffer is
// handed to V8 in whichever width CFString already holds. Going through
// -UTF8String instead would encode the string twice (once for the bytes, once
// for -lengthOfBytesUsingEncoding:), allocate, and return nil for strings
// containing a lone surrogate — silently turning them into "".
inline v8::Local<v8::String> ToV8String(v8::Isolate* isolate, const NSString* value) {
/*
// TODO: profile if this is faster
// maybe have multiple conversion
if([value fastestEncoding] == NSUTF16StringEncoding) {
uint16_t static_buffer[256];
uint16_t* targetBuffer = static_buffer;
bool isDynamic = false;
auto length = [value
maximumLengthOfBytesUsingEncoding:NSUTF16StringEncoding]; auto numberOfBytes =
length * sizeof(uint16_t); if (length > 256) { targetBuffer =
(uint16_t*)malloc(numberOfBytes); isDynamic = true;
}
NSUInteger usedLength = 0;
NSRange range = NSMakeRange(0, [value length]);
[value getBytes:targetBuffer maxLength:numberOfBytes
usedLength:&usedLength encoding:NSUTF16StringEncoding options:0 range:range
remainingRange:NULL];

auto result = v8::String::NewFromTwoByte(isolate, targetBuffer,
v8::NewStringType::kNormal, (int)[value length]).ToLocalChecked(); if
(isDynamic) { free(targetBuffer);
}
return result;
if (value == nil) {
return v8::String::Empty(isolate);
}
*/
return v8::String::NewFromUtf8(isolate, [value UTF8String], v8::NewStringType::kNormal,
(int)[value lengthOfBytesUsingEncoding:NSUTF8StringEncoding])

CFStringRef str = (__bridge CFStringRef)value;
CFIndex length = CFStringGetLength(str);
if (length == 0) {
return v8::String::Empty(isolate);
}

// An ASCII pointer is handed back only when every code unit is < 0x80, so the
// code unit count doubles as the byte count.
if (const char* ascii = CFStringGetCStringPtr(str, kCFStringEncodingASCII)) {
return v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(ascii),
v8::NewStringType::kNormal, (int)length)
.ToLocalChecked();
}

if (const UniChar* utf16 = CFStringGetCharactersPtr(str)) {
return v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>(utf16),
v8::NewStringType::kNormal, (int)length)
.ToLocalChecked();
}

// Tagged pointers and some bridged strings expose neither buffer, so the
// contents have to be copied out. The narrow attempt writes at most `length`
// bytes, which always fits the UTF-16-sized buffer.
constexpr CFIndex kStackUnits = 256;
uint16_t stackBuffer[kStackUnits];
std::vector<uint16_t> heapBuffer;
uint16_t* buffer = stackBuffer;
if (length > kStackUnits) {
heapBuffer.resize((size_t)length);
buffer = heapBuffer.data();
}

CFRange range = CFRangeMake(0, length);
CFIndex usedLength = 0;
if (CFStringGetBytes(str, range, kCFStringEncodingASCII, 0, false,
reinterpret_cast<UInt8*>(buffer), length, &usedLength) == length) {
return v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(buffer),
v8::NewStringType::kNormal, (int)length)
.ToLocalChecked();
}

CFStringGetCharacters(str, range, reinterpret_cast<UniChar*>(buffer));
return v8::String::NewFromTwoByte(isolate, buffer, v8::NewStringType::kNormal, (int)length)
.ToLocalChecked();
}
#endif
inline std::string ToString(v8::Isolate* isolate, const v8::Local<v8::Value>& value) {
// Unwraps a value to the v8::String the text conversions below read from.
// A throwing toString() is swallowed rather than left pending, which is the
// contract v8::String::Utf8Value offered and callers were written against.
inline bool ToStringLocal(v8::Isolate* isolate, const v8::Local<v8::Value>& value,
v8::Local<v8::String>& out) {
if (value.IsEmpty()) {
return std::string();
return false;
}

if (value->IsString()) {
out = value.As<v8::String>();
return true;
}

if (value->IsStringObject()) {
v8::Local<v8::String> obj = value.As<v8::StringObject>()->ValueOf();
return tns::ToString(isolate, obj);
out = value.As<v8::StringObject>()->ValueOf();
return true;
}

v8::String::Utf8Value result(isolate, value);
v8::TryCatch tc(isolate);
return value->ToString(isolate->GetCurrentContext()).ToLocal(&out);
}

const char* val = *result;
if (val == nullptr) {
inline std::string ToString(v8::Isolate* isolate, const v8::Local<v8::Value>& value) {
v8::Local<v8::String> str;
if (!ToStringLocal(isolate, value, str)) {
return std::string();
}

return std::string(*result, result.length());
{
v8::String::ValueView view(isolate, str);
if (view.is_one_byte()) {
const uint8_t* data = view.data8();
uint32_t length = view.length();
uint32_t i = 0;
while (i < length && data[i] < 0x80) {
i++;
}
// Pure ASCII already is its own UTF-8 encoding, so it can be copied
// straight out. A one-byte string with a high byte is Latin-1 and still
// needs widening, which the encode below handles.
if (i == length) {
return std::string(reinterpret_cast<const char*>(data), length);
}
}
}

size_t length = str->Utf8LengthV2(isolate);
std::string result(length, '\0');
if (length > 0) {
str->WriteUtf8V2(isolate, result.data(), length, v8::String::WriteFlags::kReplaceInvalidUtf8);
}

return result;
}

#ifdef __OBJC__
// Encodes via V8 rather than -UTF8String, which returns nil for a string holding
// a lone surrogate — leaving callers to construct a std::string from nullptr.
// The unpaired half becomes U+FFFD, since it has no UTF-8 spelling.
inline std::string ToString(v8::Isolate* isolate, const NSString* value) {
return tns::ToString(isolate, tns::ToV8String(isolate, value));
}

inline NSString* ToNSString(const std::string& v) {
return [[[NSString alloc] initWithBytes:v.c_str() length:v.length()
encoding:NSUTF8StringEncoding] S_AUTORELEASE];
Expand Down Expand Up @@ -164,8 +239,6 @@ inline bool ToBool(const v8::Local<v8::Value>& value) {

return result;
}
std::vector<uint16_t> ToVector(const std::string& value);

bool Exists(const char* fullPath);
v8::Local<v8::String> ReadModule(v8::Isolate* isolate, const std::string& filePath);
const char* ReadText(const std::string& filePath, long& length, bool& isNew);
Expand Down
14 changes: 0 additions & 14 deletions NativeScript/runtime/Helpers.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <atomic>
#include <codecvt>
#include <fstream>
#include <locale>
#include <sstream>
#include "Caches.h"
#include "ErrorEvents.h"
Expand Down Expand Up @@ -54,18 +52,6 @@
return std::u16string((const char16_t*)result.data16(), result.length());
}

std::vector<uint16_t> tns::ToVector(const std::string& value) {
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
// FIXME: std::codecvt_utf8_utf16 is deprecated
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
std::u16string value16 = convert.from_bytes(value);

const uint16_t* begin = reinterpret_cast<uint16_t const*>(value16.data());
const uint16_t* end = reinterpret_cast<uint16_t const*>(value16.data() + value16.size());
std::vector<uint16_t> vector(begin, end);
return vector;
}

bool tns::Exists(const char* fullPath) {
struct stat statbuf;
mode_t mode = S_IFDIR | S_IFREG;
Expand Down
2 changes: 1 addition & 1 deletion NativeScript/runtime/InlineFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void InlineFunctions::Init(Local<Context> context) {
}
}

bool InlineFunctions::IsGlobalFunction(std::string name) {
bool InlineFunctions::IsGlobalFunction(const std::string& name) {
return name == "CGPointMake" || name == "CGRectMake" ||
name == "CGSizeMake" || name == "UIEdgeInsetsMake" ||
name == "NSMakeRange" || name == "__decorate" || name == "__param" ||
Expand Down
2 changes: 1 addition & 1 deletion NativeScript/runtime/InlineFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace tns {
class InlineFunctions {
public:
static void Init(v8::Local<v8::Context> context);
static bool IsGlobalFunction(std::string name);
static bool IsGlobalFunction(const std::string& name);
};

}
Expand Down
Loading
Loading