1// Copyright 2022-present 650 Industries. All rights reserved.
2
3#import <ExpoModulesCore/EXJSIConversions.h>
4#import <ExpoModulesCore/EXJavaScriptValue.h>
5#import <ExpoModulesCore/EXJavaScriptRuntime.h>
6
7@implementation EXJavaScriptValue {
8  __weak EXJavaScriptRuntime *_runtime;
9  std::shared_ptr<jsi::Value> _value;
10}
11
12- (nonnull instancetype)initWithRuntime:(nonnull EXJavaScriptRuntime *)runtime
13                                  value:(std::shared_ptr<jsi::Value>)value
14{
15  if (self = [super init]) {
16    _runtime = runtime;
17    _value = value;
18  }
19  return self;
20}
21
22#pragma mark - Type checking
23
24- (BOOL)isUndefined
25{
26  return _value->isUndefined();
27}
28
29- (BOOL)isNull
30{
31  return _value->isNull();
32}
33
34- (BOOL)isBool
35{
36  return _value->isBool();
37}
38
39- (BOOL)isNumber
40{
41  return _value->isNumber();
42}
43
44- (BOOL)isString
45{
46  return _value->isString();
47}
48
49- (BOOL)isSymbol
50{
51  return _value->isSymbol();
52}
53
54- (BOOL)isObject
55{
56  return _value->isObject();
57}
58
59- (BOOL)isFunction
60{
61  if (_value->isObject()) {
62    jsi::Runtime *runtime = [_runtime get];
63    return _value->getObject(*runtime).isFunction(*runtime);
64  }
65  return false;
66}
67
68+ (nonnull NSString *)kindOf:(nonnull EXJavaScriptValue *)value
69{
70  if ([value isUndefined]) {
71    return @"undefined";
72  }
73  if ([value isNull]) {
74    return @"null";
75  }
76  if ([value isBool]) {
77    return @"boolean";
78  }
79  if ([value isNumber]) {
80    return @"number";
81  }
82  if ([value isString]) {
83    return @"string";
84  }
85  if ([value isFunction]) {
86    return @"function";
87  }
88  assert([value isObject] && "Expecting object.");
89  return @"object";
90}
91
92#pragma mark - Type casting
93
94- (nullable id)getRaw
95{
96  return expo::convertJSIValueToObjCObject(*[_runtime get], *_value, [_runtime callInvoker]);
97}
98
99- (BOOL)getBool
100{
101  return _value->getBool();
102}
103
104- (NSInteger)getInt
105{
106  return _value->getNumber();
107}
108
109- (double)getDouble
110{
111  return _value->getNumber();
112}
113
114- (nonnull NSString *)getString
115{
116  jsi::Runtime *runtime = [_runtime get];
117  return expo::convertJSIStringToNSString(*runtime, _value->getString(*runtime));
118}
119
120- (nonnull NSArray<EXJavaScriptValue *> *)getArray
121{
122  jsi::Runtime *runtime = [_runtime get];
123  jsi::Array jsiArray = _value->getObject(*runtime).getArray(*runtime);
124  size_t arraySize = jsiArray.size(*runtime);
125  NSMutableArray *result = [NSMutableArray arrayWithCapacity:arraySize];
126
127  for (size_t i = 0; i < arraySize; i++) {
128    jsi::Value item = jsiArray.getValueAtIndex(*runtime, i);
129
130    if (item.isUndefined() || item.isNull()) {
131      [result addObject:(id)kCFNull];
132    } else {
133      std::shared_ptr<jsi::Value> valuePtr = std::make_shared<jsi::Value>(*runtime, item);
134      [result addObject:[[EXJavaScriptValue alloc] initWithRuntime:_runtime value:valuePtr]];
135    }
136  }
137  return result;
138}
139
140- (nonnull NSDictionary<NSString *, id> *)getDictionary
141{
142  jsi::Runtime *runtime = [_runtime get];
143  return expo::convertJSIObjectToNSDictionary(*runtime, _value->getObject(*runtime), [_runtime callInvoker]);
144}
145
146- (nonnull EXJavaScriptObject *)getObject
147{
148  jsi::Runtime *runtime = [_runtime get];
149  std::shared_ptr<jsi::Object> objectPtr = std::make_shared<jsi::Object>(_value->asObject(*runtime));
150  return [[EXJavaScriptObject alloc] initWith:objectPtr runtime:_runtime];
151}
152
153#pragma mark - Helpers
154
155- (nonnull NSString *)toString
156{
157  jsi::Runtime *runtime = [_runtime get];
158  return expo::convertJSIStringToNSString(*runtime, _value->toString(*runtime));
159}
160
161@end
162