Skip to content

Commit

Permalink
JSI es6 Symbol impl for JSCRuntime
Browse files Browse the repository at this point in the history
Summary: this is an empirical hack

Reviewed By: fkgozali

Differential Revision: D14216647

fbshipit-source-id: 577ffb555c6e2f5a6456ccea5dff8e6ec757f80f
  • Loading branch information
mhorowitz authored and facebook-github-bot committed Mar 21, 2019
1 parent 7ccec33 commit dcc40a6
Showing 1 changed file with 122 additions and 0 deletions.
122 changes: 122 additions & 0 deletions ReactCommon/jsi/JSCRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ class JSCRuntime : public jsi::Runtime {

protected:
friend class detail::ArgsConverter;
class JSCSymbolValue final : public PointerValue {
#ifndef NDEBUG
JSCSymbolValue(JSGlobalContextRef ctx,
const std::atomic<bool>& ctxInvalid,
JSValueRef sym, std::atomic<intptr_t>& counter);
#else
JSCSymbolValue(JSGlobalContextRef ctx,
const std::atomic<bool>& ctxInvalid,
JSValueRef sym);
#endif
void invalidate() override;

JSGlobalContextRef ctx_;
const std::atomic<bool>& ctxInvalid_;
// There is no C type in the JSC API to represent Symbol, so this stored
// a JSValueRef which contains the Symbol.
JSValueRef sym_;
#ifndef NDEBUG
std::atomic<intptr_t>& counter_;
#endif
protected:
friend class JSCRuntime;
};

class JSCStringValue final : public PointerValue {
#ifndef NDEBUG
JSCStringValue(JSStringRef str, std::atomic<intptr_t>& counter);
Expand Down Expand Up @@ -108,6 +132,7 @@ class JSCRuntime : public jsi::Runtime {
friend class JSCRuntime;
};

PointerValue* cloneSymbol(const Runtime::PointerValue* pv) override;
PointerValue* cloneString(const Runtime::PointerValue* pv) override;
PointerValue* cloneObject(const Runtime::PointerValue* pv) override;
PointerValue* clonePropNameID(const Runtime::PointerValue* pv) override;
Expand All @@ -120,6 +145,8 @@ class JSCRuntime : public jsi::Runtime {
std::string utf8(const jsi::PropNameID&) override;
bool compare(const jsi::PropNameID&, const jsi::PropNameID&) override;

std::string symbolToString(const jsi::Symbol&) override;

jsi::String createStringFromAscii(const char* str, size_t length) override;
jsi::String createStringFromUtf8(const uint8_t* utf8, size_t length) override;
std::string utf8(const jsi::String&) override;
Expand Down Expand Up @@ -176,12 +203,14 @@ class JSCRuntime : public jsi::Runtime {
const jsi::Value* args,
size_t count) override;

bool strictEquals(const jsi::Symbol& a, const jsi::Symbol& b) const override;
bool strictEquals(const jsi::String& a, const jsi::String& b) const override;
bool strictEquals(const jsi::Object& a, const jsi::Object& b) const override;
bool instanceOf(const jsi::Object& o, const jsi::Function& f) override;

private:
// Basically convenience casts
static JSValueRef symbolRef(const jsi::Symbol& str);
static JSStringRef stringRef(const jsi::String& str);
static JSStringRef stringRef(const jsi::PropNameID& sym);
static JSObjectRef objectRef(const jsi::Object& obj);
Expand All @@ -191,11 +220,13 @@ class JSCRuntime : public jsi::Runtime {
#endif

// Factory methods for creating String/Object
jsi::Symbol createSymbol(JSValueRef symbolRef) const;
jsi::String createString(JSStringRef stringRef) const;
jsi::PropNameID createPropNameID(JSStringRef stringRef);
jsi::Object createObject(JSObjectRef objectRef) const;

// Used by factory methods and clone methods
jsi::Runtime::PointerValue* makeSymbolValue(JSValueRef sym) const;
jsi::Runtime::PointerValue* makeStringValue(JSStringRef str) const;
jsi::Runtime::PointerValue* makeObjectValue(JSObjectRef obj) const;

Expand All @@ -209,6 +240,7 @@ class JSCRuntime : public jsi::Runtime {
std::string desc_;
#ifndef NDEBUG
mutable std::atomic<intptr_t> objectCounter_;
mutable std::atomic<intptr_t> symbolCounter_;
mutable std::atomic<intptr_t> stringCounter_;
#endif
};
Expand Down Expand Up @@ -380,6 +412,52 @@ bool JSCRuntime::isInspectable() {
return false;
}

namespace {

bool smellsLikeES6Symbol(JSGlobalContextRef ctx, JSValueRef ref) {
// Empirically, an es6 Symbol is not an object, but its type is
// object. This makes no sense, but we'll run with it.
return (!JSValueIsObject(ctx, ref) &&
JSValueGetType(ctx, ref) == kJSTypeObject);
}

}

JSCRuntime::JSCSymbolValue::JSCSymbolValue(
JSGlobalContextRef ctx,
const std::atomic<bool>& ctxInvalid,
JSValueRef sym
#ifndef NDEBUG
,
std::atomic<intptr_t>& counter
#endif
)
: ctx_(ctx),
ctxInvalid_(ctxInvalid),
sym_(sym)
#ifndef NDEBUG
,
counter_(counter)
#endif
{
assert(smellsLikeES6Symbol(ctx_, sym_));
JSValueProtect(ctx_, sym_);
#ifndef NDEBUG
counter_ += 1;
#endif
}

void JSCRuntime::JSCSymbolValue::invalidate() {
#ifndef NDEBUG
counter_ -= 1;
#endif

if (!ctxInvalid_) {
JSValueUnprotect(ctx_, sym_);
}
delete this;
}

#ifndef NDEBUG
JSCRuntime::JSCStringValue::JSCStringValue(
JSStringRef str,
Expand Down Expand Up @@ -462,6 +540,15 @@ void JSCRuntime::JSCObjectValue::invalidate() {
delete this;
}

jsi::Runtime::PointerValue* JSCRuntime::cloneSymbol(
const jsi::Runtime::PointerValue* pv) {
if (!pv) {
return nullptr;
}
const JSCSymbolValue* symbol = static_cast<const JSCSymbolValue*>(pv);
return makeSymbolValue(symbol->sym_);
}

jsi::Runtime::PointerValue* JSCRuntime::cloneString(
const jsi::Runtime::PointerValue* pv) {
if (!pv) {
Expand Down Expand Up @@ -525,6 +612,12 @@ bool JSCRuntime::compare(const jsi::PropNameID& a, const jsi::PropNameID& b) {
return JSStringIsEqual(stringRef(a), stringRef(b));
}

std::string JSCRuntime::symbolToString(const jsi::Symbol& sym) {
return jsi::Value(*this, sym)
.toString(*this)
.utf8(*this);
}

jsi::String JSCRuntime::createStringFromAscii(const char* str, size_t length) {
// Yes we end up double casting for semantic reasons (UTF8 contains ASCII,
// not the other way around)
Expand Down Expand Up @@ -1122,6 +1215,14 @@ jsi::Value JSCRuntime::callAsConstructor(
return createValue(res);
}

bool JSCRuntime::strictEquals(const jsi::Symbol& a, const jsi::Symbol& b)
const {
JSValueRef exc = nullptr;
bool ret = JSValueIsEqual(ctx_, symbolRef(a), symbolRef(b), &exc);
const_cast<JSCRuntime*>(this)->checkException(exc);
return ret;
}

bool JSCRuntime::strictEquals(const jsi::String& a, const jsi::String& b)
const {
return JSStringIsEqual(stringRef(a), stringRef(b));
Expand All @@ -1140,6 +1241,15 @@ bool JSCRuntime::instanceOf(const jsi::Object& o, const jsi::Function& f) {
return res;
}

jsi::Runtime::PointerValue* JSCRuntime::makeSymbolValue(
JSValueRef symbolRef) const {
#ifndef NDEBUG
return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef, symbolCounter_);
#else
return new JSCSymbolValue(ctx_, ctxInvalid_, symbolRef);
#endif
}

namespace {
JSStringRef getEmptyString() {
static JSStringRef empty = JSStringCreateWithUTF8CString("");
Expand All @@ -1159,6 +1269,10 @@ jsi::Runtime::PointerValue* JSCRuntime::makeStringValue(
#endif
}

jsi::Symbol JSCRuntime::createSymbol(JSValueRef sym) const {
return make<jsi::Symbol>(makeSymbolValue(sym));
}

jsi::String JSCRuntime::createString(JSStringRef str) const {
return make<jsi::String>(makeStringValue(str));
}
Expand Down Expand Up @@ -1200,6 +1314,8 @@ jsi::Value JSCRuntime::createValue(JSValueRef value) const {
} else if (JSValueIsObject(ctx_, value)) {
JSObjectRef objRef = JSValueToObject(ctx_, value, nullptr);
return jsi::Value(createObject(objRef));
} else if (smellsLikeES6Symbol(ctx_, value)) {
return jsi::Value(createSymbol(value));
} else {
// WHAT ARE YOU
abort();
Expand All @@ -1216,6 +1332,8 @@ JSValueRef JSCRuntime::valueRef(const jsi::Value& value) {
return JSValueMakeBoolean(ctx_, value.getBool());
} else if (value.isNumber()) {
return JSValueMakeNumber(ctx_, value.getNumber());
} else if (value.isSymbol()) {
return symbolRef(value.getSymbol(*this));
} else if (value.isString()) {
return JSValueMakeString(ctx_, stringRef(value.getString(*this)));
} else if (value.isObject()) {
Expand All @@ -1226,6 +1344,10 @@ JSValueRef JSCRuntime::valueRef(const jsi::Value& value) {
}
}

JSValueRef JSCRuntime::symbolRef(const jsi::Symbol& sym) {
return static_cast<const JSCSymbolValue*>(getPointerValue(sym))->sym_;
}

JSStringRef JSCRuntime::stringRef(const jsi::String& str) {
return static_cast<const JSCStringValue*>(getPointerValue(str))->str_;
}
Expand Down

0 comments on commit dcc40a6

Please sign in to comment.