- 37개 파일 IP → zioinfo.co.kr 치환 (소스/매뉴얼/설정/하네스) - Manager DrConsole/NetworkConsole/CsapConsole 빌드 + /var/www/manager/ 배포 - 테스트: Manager HTTP 200, ITSM 신규 API 7개 전체 200 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
264 lines
11 KiB
Plaintext
264 lines
11 KiB
Plaintext
// Copyright 2018-present 650 Industries. All rights reserved.
|
|
|
|
#import <jsi/jsi.h>
|
|
|
|
#if __has_include(<reacthermes/HermesExecutorFactory.h>)
|
|
#import <reacthermes/HermesExecutorFactory.h>
|
|
#elif __has_include(<React-jsc/JSCRuntime.h>)
|
|
// react-native@>=0.71 has a specific React-jsc pod
|
|
#import <React-jsc/JSCRuntime.h>
|
|
// use_frameworks! transforms the dash to underscore
|
|
#elif __has_include(<React_jsc/JSCRuntime.h>)
|
|
#import <React_jsc/JSCRuntime.h>
|
|
#else
|
|
#import <jsi/JSCRuntime.h>
|
|
#endif
|
|
|
|
#import <ExpoModulesCore/EXJavaScriptRuntime.h>
|
|
#import <ExpoModulesCore/ExpoModulesHostObject.h>
|
|
#import <ExpoModulesCore/EXJSIUtils.h>
|
|
#import <ExpoModulesCore/EXJSIConversions.h>
|
|
#import <ExpoModulesCore/SharedObject.h>
|
|
#import <ExpoModulesCore/Swift.h>
|
|
#import <ExpoModulesCore/TestingSyncJSCallInvoker.h>
|
|
|
|
@implementation EXJavaScriptRuntime {
|
|
std::shared_ptr<jsi::Runtime> _runtime;
|
|
std::shared_ptr<react::CallInvoker> _jsCallInvoker;
|
|
}
|
|
|
|
/**
|
|
Initializes a runtime that is independent from React Native and its runtime initialization.
|
|
This flow is mostly intended for tests.
|
|
*/
|
|
- (nonnull instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
#if __has_include(<reacthermes/HermesExecutorFactory.h>)
|
|
_runtime = facebook::hermes::makeHermesRuntime();
|
|
|
|
// This version of the Hermes uses a Promise implementation that is provided by the RN.
|
|
// The `setImmediate` function isn't defined, but is required by the Promise implementation.
|
|
// That's why we inject it here.
|
|
auto setImmediatePropName = jsi::PropNameID::forUtf8(*_runtime, "setImmediate");
|
|
_runtime->global().setProperty(
|
|
*_runtime, setImmediatePropName, jsi::Function::createFromHostFunction(*_runtime, setImmediatePropName, 1,
|
|
[](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) {
|
|
args[0].asObject(rt).asFunction(rt).call(rt);
|
|
return jsi::Value::undefined();
|
|
})
|
|
);
|
|
#else
|
|
_runtime = jsc::makeJSCRuntime();
|
|
#endif
|
|
_jsCallInvoker = std::make_shared<expo::TestingSyncJSCallInvoker>(_runtime);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (nonnull instancetype)initWithRuntime:(nonnull jsi::Runtime *)runtime
|
|
callInvoker:(std::shared_ptr<react::CallInvoker>)callInvoker
|
|
{
|
|
if (self = [super init]) {
|
|
// Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it.
|
|
// In this code flow, the runtime should be owned by something else like the RCTBridge.
|
|
// See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
|
|
_runtime = std::shared_ptr<jsi::Runtime>(std::shared_ptr<jsi::Runtime>(), runtime);
|
|
_jsCallInvoker = callInvoker;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (nonnull jsi::Runtime *)get
|
|
{
|
|
return _runtime.get();
|
|
}
|
|
|
|
- (std::shared_ptr<react::CallInvoker>)callInvoker
|
|
{
|
|
return _jsCallInvoker;
|
|
}
|
|
|
|
- (nonnull EXJavaScriptObject *)createObject
|
|
{
|
|
auto jsObjectPtr = std::make_shared<jsi::Object>(*_runtime);
|
|
return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self];
|
|
}
|
|
|
|
- (nonnull EXJavaScriptObject *)createHostObject:(std::shared_ptr<jsi::HostObject>)jsiHostObjectPtr
|
|
{
|
|
auto jsObjectPtr = std::make_shared<jsi::Object>(jsi::Object::createFromHostObject(*_runtime, jsiHostObjectPtr));
|
|
return [[EXJavaScriptObject alloc] initWith:jsObjectPtr runtime:self];
|
|
}
|
|
|
|
- (nonnull EXJavaScriptObject *)global
|
|
{
|
|
auto jsGlobalPtr = std::make_shared<jsi::Object>(_runtime->global());
|
|
return [[EXJavaScriptObject alloc] initWith:jsGlobalPtr runtime:self];
|
|
}
|
|
|
|
- (nonnull EXJavaScriptObject *)createSyncFunction:(nonnull NSString *)name
|
|
argsCount:(NSInteger)argsCount
|
|
block:(nonnull JSSyncFunctionBlock)block
|
|
{
|
|
JSHostFunctionBlock hostFunctionBlock = ^jsi::Value(
|
|
jsi::Runtime &runtime,
|
|
std::shared_ptr<react::CallInvoker> callInvoker,
|
|
EXJavaScriptValue * _Nonnull thisValue,
|
|
NSArray<EXJavaScriptValue *> * _Nonnull arguments) {
|
|
NSError *error;
|
|
id result = block(thisValue, arguments, &error);
|
|
|
|
if (error == nil) {
|
|
return expo::convertObjCObjectToJSIValue(runtime, result);
|
|
} else {
|
|
// `expo::makeCodedError` doesn't work during unit tests, so we construct Error and add a code,
|
|
// instead of using the CodedError subclass.
|
|
jsi::String jsCode = expo::convertNSStringToJSIString(runtime, error.userInfo[@"code"]);
|
|
jsi::String jsMessage = expo::convertNSStringToJSIString(runtime, error.userInfo[@"message"]);
|
|
jsi::Value error = runtime
|
|
.global()
|
|
.getProperty(runtime, "Error")
|
|
.asObject(runtime)
|
|
.asFunction(runtime)
|
|
.callAsConstructor(runtime, {
|
|
jsi::Value(runtime, jsMessage)
|
|
});
|
|
error.asObject(runtime).setProperty(runtime, "code", jsi::Value(runtime, jsCode));
|
|
throw jsi::JSError(runtime, jsi::Value(runtime, error));
|
|
}
|
|
};
|
|
return [self createHostFunction:name argsCount:argsCount block:hostFunctionBlock];
|
|
}
|
|
|
|
- (nonnull EXJavaScriptObject *)createAsyncFunction:(nonnull NSString *)name
|
|
argsCount:(NSInteger)argsCount
|
|
block:(nonnull JSAsyncFunctionBlock)block
|
|
{
|
|
JSHostFunctionBlock hostFunctionBlock = ^jsi::Value(
|
|
jsi::Runtime &runtime,
|
|
std::shared_ptr<react::CallInvoker> callInvoker,
|
|
EXJavaScriptValue * _Nonnull thisValue,
|
|
NSArray<EXJavaScriptValue *> * _Nonnull arguments) {
|
|
if (!callInvoker) {
|
|
// In mocked environment the call invoker may be null so it's not supported to call async functions.
|
|
// Testing async functions is a bit more complicated anyway. See `init` description for more.
|
|
throw jsi::JSError(runtime, "Calling async functions is not supported when the call invoker is unavailable");
|
|
}
|
|
// The function that is invoked as a setup of the EXJavaScript `Promise`.
|
|
auto promiseSetup = [callInvoker, block, thisValue, arguments](jsi::Runtime &runtime, std::shared_ptr<Promise> promise) {
|
|
expo::callPromiseSetupWithBlock(runtime, callInvoker, promise, ^(RCTPromiseResolveBlock resolver, RCTPromiseRejectBlock rejecter) {
|
|
block(thisValue, arguments, resolver, rejecter);
|
|
});
|
|
};
|
|
return createPromiseAsJSIValue(runtime, promiseSetup);
|
|
};
|
|
return [self createHostFunction:name argsCount:argsCount block:hostFunctionBlock];
|
|
}
|
|
|
|
#pragma mark - Classes
|
|
|
|
- (nonnull EXJavaScriptObject *)createClass:(nonnull NSString *)name
|
|
constructor:(nonnull ClassConstructorBlock)constructor
|
|
{
|
|
expo::common::ClassConstructor jsConstructor = [self, constructor](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
std::shared_ptr<jsi::Object> thisPtr = std::make_shared<jsi::Object>(thisValue.asObject(runtime));
|
|
EXJavaScriptObject *caller = [[EXJavaScriptObject alloc] initWith:thisPtr runtime:self];
|
|
NSArray<EXJavaScriptValue *> *arguments = expo::convertJSIValuesToNSArray(self, args, count);
|
|
|
|
// Returning something else than `this` is not supported in native constructors.
|
|
constructor(caller, arguments);
|
|
|
|
return jsi::Value(runtime, thisValue);
|
|
};
|
|
std::shared_ptr<jsi::Function> klass = std::make_shared<jsi::Function>(expo::common::createClass(*_runtime, [name UTF8String], jsConstructor));
|
|
return [[EXJavaScriptObject alloc] initWith:klass runtime:self];
|
|
}
|
|
|
|
- (nullable EXJavaScriptObject *)createObjectWithPrototype:(nonnull EXJavaScriptObject *)prototype
|
|
{
|
|
std::shared_ptr<jsi::Object> object = std::make_shared<jsi::Object>(expo::common::createObjectWithPrototype(*_runtime, [prototype getShared].get()));
|
|
return object ? [[EXJavaScriptObject alloc] initWith:object runtime:self] : nil;
|
|
}
|
|
|
|
#pragma mark - Shared objects
|
|
|
|
- (nonnull EXJavaScriptObject *)createSharedObjectClass:(nonnull NSString *)name
|
|
constructor:(nonnull ClassConstructorBlock)constructor
|
|
{
|
|
expo::common::ClassConstructor jsConstructor = [self, constructor](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *args, size_t count) {
|
|
std::shared_ptr<jsi::Object> thisPtr = std::make_shared<jsi::Object>(thisValue.asObject(runtime));
|
|
EXJavaScriptObject *caller = [[EXJavaScriptObject alloc] initWith:thisPtr runtime:self];
|
|
NSArray<EXJavaScriptValue *> *arguments = expo::convertJSIValuesToNSArray(self, args, count);
|
|
|
|
constructor(caller, arguments);
|
|
return jsi::Value(runtime, thisValue);
|
|
};
|
|
std::shared_ptr<jsi::Function> klass = std::make_shared<jsi::Function>(expo::SharedObject::createClass(*_runtime, [name UTF8String], jsConstructor));
|
|
return [[EXJavaScriptObject alloc] initWith:klass runtime:self];
|
|
}
|
|
|
|
#pragma mark - Script evaluation
|
|
|
|
- (nonnull EXJavaScriptValue *)evaluateScript:(nonnull NSString *)scriptSource
|
|
{
|
|
std::shared_ptr<jsi::StringBuffer> scriptBuffer = std::make_shared<jsi::StringBuffer>([scriptSource UTF8String]);
|
|
std::shared_ptr<jsi::Value> result;
|
|
|
|
try {
|
|
result = std::make_shared<jsi::Value>(_runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>"));
|
|
} catch (jsi::JSError &error) {
|
|
NSString *reason = [NSString stringWithUTF8String:error.getMessage().c_str()];
|
|
NSString *stack = [NSString stringWithUTF8String:error.getStack().c_str()];
|
|
|
|
@throw [NSException exceptionWithName:@"ScriptEvaluationException" reason:reason userInfo:@{
|
|
@"message": reason,
|
|
@"stack": stack,
|
|
}];
|
|
} catch (jsi::JSIException &error) {
|
|
NSString *reason = [NSString stringWithUTF8String:error.what()];
|
|
|
|
@throw [NSException exceptionWithName:@"ScriptEvaluationException" reason:reason userInfo:@{
|
|
@"message": reason
|
|
}];
|
|
}
|
|
return [[EXJavaScriptValue alloc] initWithRuntime:self value:result];
|
|
}
|
|
|
|
#pragma mark - Runtime execution
|
|
|
|
- (void)schedule:(nonnull JSRuntimeExecutionBlock)block priority:(int)priority
|
|
{
|
|
#if REACT_NATIVE_TARGET_VERSION >= 75
|
|
_jsCallInvoker->invokeAsync(SchedulerPriority(priority), [block = std::move(block)](jsi::Runtime&) {
|
|
block();
|
|
});
|
|
#else
|
|
_jsCallInvoker->invokeAsync(SchedulerPriority(priority), block);
|
|
#endif
|
|
}
|
|
|
|
#pragma mark - Private
|
|
|
|
- (nonnull EXJavaScriptObject *)createHostFunction:(nonnull NSString *)name
|
|
argsCount:(NSInteger)argsCount
|
|
block:(nonnull JSHostFunctionBlock)block
|
|
{
|
|
jsi::PropNameID propNameId = jsi::PropNameID::forAscii(*_runtime, [name UTF8String], [name length]);
|
|
std::weak_ptr<react::CallInvoker> weakCallInvoker = _jsCallInvoker;
|
|
jsi::HostFunctionType function = [weakCallInvoker, block, self](jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *args, size_t count) -> jsi::Value {
|
|
// Theoretically should check here whether the call invoker isn't null, but in mocked environment
|
|
// there is no need to care about that for synchronous calls, so it's ensured in `createAsyncFunction` instead.
|
|
auto callInvoker = weakCallInvoker.lock();
|
|
NSArray<EXJavaScriptValue *> *arguments = expo::convertJSIValuesToNSArray(self, args, count);
|
|
std::shared_ptr<jsi::Value> thisValPtr = std::make_shared<jsi::Value>(runtime, std::move(thisVal));
|
|
EXJavaScriptValue *thisValue = [[EXJavaScriptValue alloc] initWithRuntime:self value:thisValPtr];
|
|
|
|
return block(runtime, callInvoker, thisValue, arguments);
|
|
};
|
|
std::shared_ptr<jsi::Object> fnPtr = std::make_shared<jsi::Object>(jsi::Function::createFromHostFunction(*_runtime, propNameId, (unsigned int)argsCount, function));
|
|
return [[EXJavaScriptObject alloc] initWith:fnPtr runtime:self];
|
|
}
|
|
|
|
@end
|