1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | #include <cassert>
|
8 | #include <cmath>
|
9 | #include <cstdlib>
|
10 | #include <stdexcept>
|
11 |
|
12 | #include <jsi/instrumentation.h>
|
13 | #include <jsi/jsi.h>
|
14 |
|
15 | namespace facebook {
|
16 | namespace jsi {
|
17 |
|
18 | namespace {
|
19 |
|
20 |
|
21 | std::string kindToString(const Value& v, Runtime* rt = nullptr) {
|
22 | if (v.isUndefined()) {
|
23 | return "undefined";
|
24 | } else if (v.isNull()) {
|
25 | return "null";
|
26 | } else if (v.isBool()) {
|
27 | return v.getBool() ? "true" : "false";
|
28 | } else if (v.isNumber()) {
|
29 | return "a number";
|
30 | } else if (v.isString()) {
|
31 | return "a string";
|
32 | } else {
|
33 | assert(v.isObject() && "Expecting object.");
|
34 | return rt != nullptr && v.getObject(*rt).isFunction(*rt) ? "a function"
|
35 | : "an object";
|
36 | }
|
37 | }
|
38 |
|
39 | }
|
40 |
|
41 | namespace detail {
|
42 |
|
43 | void throwJSError(Runtime& rt, const char* msg) {
|
44 | throw JSError(rt, msg);
|
45 | }
|
46 |
|
47 | }
|
48 |
|
49 | Buffer::~Buffer() = default;
|
50 |
|
51 | PreparedJavaScript::~PreparedJavaScript() = default;
|
52 |
|
53 | Value HostObject::get(Runtime&, const PropNameID&) {
|
54 | return Value();
|
55 | }
|
56 |
|
57 | void HostObject::set(Runtime& rt, const PropNameID& name, const Value&) {
|
58 | std::string msg("TypeError: Cannot assign to property '");
|
59 | msg += name.utf8(rt);
|
60 | msg += "' on HostObject with default setter";
|
61 | throw JSError(rt, msg);
|
62 | }
|
63 |
|
64 | HostObject::~HostObject() {}
|
65 |
|
66 | Runtime::~Runtime() {}
|
67 |
|
68 | Instrumentation& Runtime::instrumentation() {
|
69 | class NoInstrumentation : public Instrumentation {
|
70 | std::string getRecordedGCStats() override {
|
71 | return "";
|
72 | }
|
73 |
|
74 | Value getHeapInfo(bool) override {
|
75 | return Value::undefined();
|
76 | }
|
77 |
|
78 | void collectGarbage() override {}
|
79 |
|
80 | bool createSnapshotToFile(const std::string&, bool) override {
|
81 | return false;
|
82 | }
|
83 |
|
84 | bool createSnapshotToStream(std::ostream&, bool) override {
|
85 | return false;
|
86 | }
|
87 |
|
88 | void writeBridgeTrafficTraceToFile(const std::string&) const override {
|
89 | std::abort();
|
90 | }
|
91 |
|
92 | void writeBasicBlockProfileTraceToFile(const std::string&) const override {
|
93 | std::abort();
|
94 | }
|
95 |
|
96 | void dumpProfilerSymbolsToFile(const std::string&) const override {
|
97 | std::abort();
|
98 | }
|
99 | };
|
100 |
|
101 | static NoInstrumentation sharedInstance;
|
102 | return sharedInstance;
|
103 | }
|
104 |
|
105 | Pointer& Pointer::operator=(Pointer&& other) {
|
106 | if (ptr_) {
|
107 | ptr_->invalidate();
|
108 | }
|
109 | ptr_ = other.ptr_;
|
110 | other.ptr_ = nullptr;
|
111 | return *this;
|
112 | }
|
113 |
|
114 | Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const {
|
115 | Value v = getProperty(runtime, name);
|
116 |
|
117 | if (!v.isObject()) {
|
118 | throw JSError(
|
119 | runtime,
|
120 | std::string("getPropertyAsObject: property '") + name + "' is " +
|
121 | kindToString(v, &runtime) + ", expected an Object");
|
122 | }
|
123 |
|
124 | return v.getObject(runtime);
|
125 | }
|
126 |
|
127 | Function Object::getPropertyAsFunction(Runtime& runtime, const char* name)
|
128 | const {
|
129 | Object obj = getPropertyAsObject(runtime, name);
|
130 | if (!obj.isFunction(runtime)) {
|
131 | throw JSError(
|
132 | runtime,
|
133 | std::string("getPropertyAsFunction: property '") + name + "' is " +
|
134 | kindToString(std::move(obj), &runtime) + ", expected a Function");
|
135 | };
|
136 |
|
137 | Runtime::PointerValue* value = obj.ptr_;
|
138 | obj.ptr_ = nullptr;
|
139 | return Function(value);
|
140 | }
|
141 |
|
142 | Array Object::asArray(Runtime& runtime) const& {
|
143 | if (!isArray(runtime)) {
|
144 | throw JSError(
|
145 | runtime,
|
146 | "Object is " + kindToString(Value(runtime, *this), &runtime) +
|
147 | ", expected an array");
|
148 | }
|
149 | return getArray(runtime);
|
150 | }
|
151 |
|
152 | Array Object::asArray(Runtime& runtime) && {
|
153 | if (!isArray(runtime)) {
|
154 | throw JSError(
|
155 | runtime,
|
156 | "Object is " + kindToString(Value(runtime, *this), &runtime) +
|
157 | ", expected an array");
|
158 | }
|
159 | return std::move(*this).getArray(runtime);
|
160 | }
|
161 |
|
162 | Function Object::asFunction(Runtime& runtime) const& {
|
163 | if (!isFunction(runtime)) {
|
164 | throw JSError(
|
165 | runtime,
|
166 | "Object is " + kindToString(Value(runtime, *this), &runtime) +
|
167 | ", expected a function");
|
168 | }
|
169 | return getFunction(runtime);
|
170 | }
|
171 |
|
172 | Function Object::asFunction(Runtime& runtime) && {
|
173 | if (!isFunction(runtime)) {
|
174 | throw JSError(
|
175 | runtime,
|
176 | "Object is " + kindToString(Value(runtime, *this), &runtime) +
|
177 | ", expected a function");
|
178 | }
|
179 | return std::move(*this).getFunction(runtime);
|
180 | }
|
181 |
|
182 | Value::Value(Value&& other) : Value(other.kind_) {
|
183 | if (kind_ == BooleanKind) {
|
184 | data_.boolean = other.data_.boolean;
|
185 | } else if (kind_ == NumberKind) {
|
186 | data_.number = other.data_.number;
|
187 | } else if (kind_ >= PointerKind) {
|
188 | new (&data_.pointer) Pointer(std::move(other.data_.pointer));
|
189 | }
|
190 |
|
191 | other.kind_ = UndefinedKind;
|
192 | }
|
193 |
|
194 | Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) {
|
195 |
|
196 |
|
197 |
|
198 | if (kind_ == BooleanKind) {
|
199 | data_.boolean = other.data_.boolean;
|
200 | } else if (kind_ == NumberKind) {
|
201 | data_.number = other.data_.number;
|
202 | } else if (kind_ == SymbolKind) {
|
203 | new (&data_.pointer) Pointer(runtime.cloneSymbol(other.data_.pointer.ptr_));
|
204 | } else if (kind_ == StringKind) {
|
205 | new (&data_.pointer) Pointer(runtime.cloneString(other.data_.pointer.ptr_));
|
206 | } else if (kind_ >= ObjectKind) {
|
207 | new (&data_.pointer) Pointer(runtime.cloneObject(other.data_.pointer.ptr_));
|
208 | }
|
209 | }
|
210 |
|
211 | Value::~Value() {
|
212 | if (kind_ >= PointerKind) {
|
213 | data_.pointer.~Pointer();
|
214 | }
|
215 | }
|
216 |
|
217 | Value Value::createFromJsonUtf8(
|
218 | Runtime& runtime,
|
219 | const uint8_t* json,
|
220 | size_t length) {
|
221 | Function parseJson = runtime.global()
|
222 | .getPropertyAsObject(runtime, "JSON")
|
223 | .getPropertyAsFunction(runtime, "parse");
|
224 | return parseJson.call(runtime, String::createFromUtf8(runtime, json, length));
|
225 | }
|
226 |
|
227 | bool Value::strictEquals(Runtime& runtime, const Value& a, const Value& b) {
|
228 | if (a.kind_ != b.kind_) {
|
229 | return false;
|
230 | }
|
231 | switch (a.kind_) {
|
232 | case UndefinedKind:
|
233 | case NullKind:
|
234 | return true;
|
235 | case BooleanKind:
|
236 | return a.data_.boolean == b.data_.boolean;
|
237 | case NumberKind:
|
238 | return a.data_.number == b.data_.number;
|
239 | case SymbolKind:
|
240 | return runtime.strictEquals(
|
241 | static_cast<const Symbol&>(a.data_.pointer),
|
242 | static_cast<const Symbol&>(b.data_.pointer));
|
243 | case StringKind:
|
244 | return runtime.strictEquals(
|
245 | static_cast<const String&>(a.data_.pointer),
|
246 | static_cast<const String&>(b.data_.pointer));
|
247 | case ObjectKind:
|
248 | return runtime.strictEquals(
|
249 | static_cast<const Object&>(a.data_.pointer),
|
250 | static_cast<const Object&>(b.data_.pointer));
|
251 | }
|
252 | return false;
|
253 | }
|
254 |
|
255 | double Value::asNumber() const {
|
256 | if (!isNumber()) {
|
257 | throw JSINativeException(
|
258 | "Value is " + kindToString(*this) + ", expected a number");
|
259 | }
|
260 |
|
261 | return getNumber();
|
262 | }
|
263 |
|
264 | Object Value::asObject(Runtime& rt) const& {
|
265 | if (!isObject()) {
|
266 | throw JSError(
|
267 | rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");
|
268 | }
|
269 |
|
270 | return getObject(rt);
|
271 | }
|
272 |
|
273 | Object Value::asObject(Runtime& rt) && {
|
274 | if (!isObject()) {
|
275 | throw JSError(
|
276 | rt, "Value is " + kindToString(*this, &rt) + ", expected an Object");
|
277 | }
|
278 | auto ptr = data_.pointer.ptr_;
|
279 | data_.pointer.ptr_ = nullptr;
|
280 | return static_cast<Object>(ptr);
|
281 | }
|
282 |
|
283 | Symbol Value::asSymbol(Runtime& rt) const& {
|
284 | if (!isSymbol()) {
|
285 | throw JSError(
|
286 | rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");
|
287 | }
|
288 |
|
289 | return getSymbol(rt);
|
290 | }
|
291 |
|
292 | Symbol Value::asSymbol(Runtime& rt) && {
|
293 | if (!isSymbol()) {
|
294 | throw JSError(
|
295 | rt, "Value is " + kindToString(*this, &rt) + ", expected a Symbol");
|
296 | }
|
297 |
|
298 | return std::move(*this).getSymbol(rt);
|
299 | }
|
300 |
|
301 | String Value::asString(Runtime& rt) const& {
|
302 | if (!isString()) {
|
303 | throw JSError(
|
304 | rt, "Value is " + kindToString(*this, &rt) + ", expected a String");
|
305 | }
|
306 |
|
307 | return getString(rt);
|
308 | }
|
309 |
|
310 | String Value::asString(Runtime& rt) && {
|
311 | if (!isString()) {
|
312 | throw JSError(
|
313 | rt, "Value is " + kindToString(*this, &rt) + ", expected a String");
|
314 | }
|
315 |
|
316 | return std::move(*this).getString(rt);
|
317 | }
|
318 |
|
319 | String Value::toString(Runtime& runtime) const {
|
320 | Function toString = runtime.global().getPropertyAsFunction(runtime, "String");
|
321 | return toString.call(runtime, *this).getString(runtime);
|
322 | }
|
323 |
|
324 | Array Array::createWithElements(
|
325 | Runtime& rt,
|
326 | std::initializer_list<Value> elements) {
|
327 | Array result(rt, elements.size());
|
328 | size_t index = 0;
|
329 | for (const auto& element : elements) {
|
330 | result.setValueAtIndex(rt, index++, element);
|
331 | }
|
332 | return result;
|
333 | }
|
334 |
|
335 | std::vector<PropNameID> HostObject::getPropertyNames(Runtime&) {
|
336 | return {};
|
337 | }
|
338 |
|
339 | Runtime::ScopeState* Runtime::pushScope() {
|
340 | return nullptr;
|
341 | }
|
342 |
|
343 | void Runtime::popScope(ScopeState*) {}
|
344 |
|
345 | JSError::JSError(Runtime& rt, Value&& value) {
|
346 | setValue(rt, std::move(value));
|
347 | }
|
348 |
|
349 | JSError::JSError(Runtime& rt, std::string msg) : message_(std::move(msg)) {
|
350 | try {
|
351 | setValue(
|
352 | rt, rt.global().getPropertyAsFunction(rt, "Error").call(rt, message_));
|
353 | } catch (...) {
|
354 | setValue(rt, Value());
|
355 | }
|
356 | }
|
357 |
|
358 | JSError::JSError(Runtime& rt, std::string msg, std::string stack)
|
359 | : message_(std::move(msg)), stack_(std::move(stack)) {
|
360 | try {
|
361 | Object e(rt);
|
362 | e.setProperty(rt, "message", String::createFromUtf8(rt, message_));
|
363 | e.setProperty(rt, "stack", String::createFromUtf8(rt, stack_));
|
364 | setValue(rt, std::move(e));
|
365 | } catch (...) {
|
366 | setValue(rt, Value());
|
367 | }
|
368 | }
|
369 |
|
370 | JSError::JSError(std::string what, Runtime& rt, Value&& value)
|
371 | : JSIException(std::move(what)) {
|
372 | setValue(rt, std::move(value));
|
373 | }
|
374 |
|
375 | void JSError::setValue(Runtime& rt, Value&& value) {
|
376 | value_ = std::make_shared<jsi::Value>(std::move(value));
|
377 |
|
378 | try {
|
379 | if ((message_.empty() || stack_.empty()) && value_->isObject()) {
|
380 | auto obj = value_->getObject(rt);
|
381 |
|
382 | if (message_.empty()) {
|
383 | jsi::Value message = obj.getProperty(rt, "message");
|
384 | if (!message.isUndefined()) {
|
385 | message_ = message.toString(rt).utf8(rt);
|
386 | }
|
387 | }
|
388 |
|
389 | if (stack_.empty()) {
|
390 | jsi::Value stack = obj.getProperty(rt, "stack");
|
391 | if (!stack.isUndefined()) {
|
392 | stack_ = stack.toString(rt).utf8(rt);
|
393 | }
|
394 | }
|
395 | }
|
396 |
|
397 | if (message_.empty()) {
|
398 | message_ = value_->toString(rt).utf8(rt);
|
399 | }
|
400 |
|
401 | if (stack_.empty()) {
|
402 | stack_ = "no stack";
|
403 | }
|
404 |
|
405 | if (what_.empty()) {
|
406 | what_ = message_ + "\n\n" + stack_;
|
407 | }
|
408 | } catch (...) {
|
409 | message_ = "[Exception caught creating message string]";
|
410 | stack_ = "[Exception caught creating stack string]";
|
411 | what_ = "[Exception caught getting value fields]";
|
412 | }
|
413 | }
|
414 |
|
415 | }
|
416 | }
|