UNPKG

10.9 kBtext/x-cView Raw
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the LICENSE
5 * file in the root directory of this source tree.
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
15namespace facebook {
16namespace jsi {
17
18namespace {
19
20// This is used for generating short exception strings.
21std::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} // namespace
40
41namespace detail {
42
43void throwJSError(Runtime& rt, const char* msg) {
44 throw JSError(rt, msg);
45}
46
47} // namespace detail
48
49Buffer::~Buffer() = default;
50
51PreparedJavaScript::~PreparedJavaScript() = default;
52
53Value HostObject::get(Runtime&, const PropNameID&) {
54 return Value();
55}
56
57void 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
64HostObject::~HostObject() {}
65
66Runtime::~Runtime() {}
67
68Instrumentation& 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
105Pointer& Pointer::operator=(Pointer&& other) {
106 if (ptr_) {
107 ptr_->invalidate();
108 }
109 ptr_ = other.ptr_;
110 other.ptr_ = nullptr;
111 return *this;
112}
113
114Object 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
127Function 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
142Array 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
152Array 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
162Function 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
172Function 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
182Value::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 // when the other's dtor runs, nothing will happen.
191 other.kind_ = UndefinedKind;
192}
193
194Value::Value(Runtime& runtime, const Value& other) : Value(other.kind_) {
195 // data_ is uninitialized, so use placement new to create non-POD
196 // types in it. Any other kind of initialization will call a dtor
197 // first, which is incorrect.
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
211Value::~Value() {
212 if (kind_ >= PointerKind) {
213 data_.pointer.~Pointer();
214 }
215}
216
217Value 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
227bool 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
255double Value::asNumber() const {
256 if (!isNumber()) {
257 throw JSINativeException(
258 "Value is " + kindToString(*this) + ", expected a number");
259 }
260
261 return getNumber();
262}
263
264Object 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
273Object 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
283Symbol 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
292Symbol 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
301String 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
310String 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
319String Value::toString(Runtime& runtime) const {
320 Function toString = runtime.global().getPropertyAsFunction(runtime, "String");
321 return toString.call(runtime, *this).getString(runtime);
322}
323
324Array 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
335std::vector<PropNameID> HostObject::getPropertyNames(Runtime&) {
336 return {};
337}
338
339Runtime::ScopeState* Runtime::pushScope() {
340 return nullptr;
341}
342
343void Runtime::popScope(ScopeState*) {}
344
345JSError::JSError(Runtime& rt, Value&& value) {
346 setValue(rt, std::move(value));
347}
348
349JSError::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
358JSError::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
370JSError::JSError(std::string what, Runtime& rt, Value&& value)
371 : JSIException(std::move(what)) {
372 setValue(rt, std::move(value));
373}
374
375void 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} // namespace jsi
416} // namespace facebook