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