Summary:
Constructing a `JSError` currently catches all exceptions that occur,
regardless of their type, and embeds the information from them in the
newly created `JSError`. This means that the type of exceptions can be
silently changed.
This results in a bug if Hermes is built with
`HERMESVM_EXCEPTION_ON_OOM` enabled. The OOM exception will get
converted to a `JSError`, which in turn can get converted into a
regular JS exception. The regular JS exception can then be caught by JS
and ignored. This leads to the VM continuing to execute in a bad
state.
I considered three ways to fix this:
1. Add a new type of exception to JSI that is intentionally meant to be
ignored, and the Hermes OOM exception then subclasses it.
2. Propagate all exceptions that happen when constructing `JSError`.
3. Propagate all exceptions except `JSIException`s when constructing
`JSError`.
The first is technically the most surgical, but it adds complexity to
JSI, and will require some extra machinery to implement since we
wouldn't be able to throw it directly from inside the VM. I'm also not
comfortable with the idea of JSI suppressing exceptions that are
completely unrelated to JSI.
The second is the simplest, and seems to match what JS would do if
`throw new Error()` itself threw, but there is some possibility that it
is a change in behaviour for existing code.
So the third approach, which is implemented in this diff, tries to
compromise between the two. Hermes can only ever throw `JSIException`s
from regular JS operations, so there should be no change for existing
code (except code that uses exceptions on OOM, which is what we're
trying to fix). Any other exception gets passed through to the caller.
Changelog:
[Internal][Fixed] - Fixed handling of Hermes OOM exceptions in JSI.
Reviewed By: jpporto
Differential Revision: D41831616
fbshipit-source-id: 42e0dde1c4acc016ab19533941c58fc1797ba1c2
Summary:
X-link: https://github.com/facebook/hermes/pull/793
Add a JSI API for constructing `ArrayBuffer`s from a user provided
`jsi::MutableBuffer`.
Changelog: [General][Added] Added ability to construct ArrayBuffers from existing memory buffers.
Reviewed By: jpporto
Differential Revision: D37744467
fbshipit-source-id: 9d9ece00d1dbde341846c45fa30c935b5fa81e9a
Summary:
This change adds the following methods to jsi::BigInt
* [static] fromInt64(value): creates a jsi::BigInt from a signed 64-bit value
* [static] fromUint64(value): creates a jsi::BigInt from an unsigned 64-bit value
* [static] strictEquals(a, b): return a === b, a and b are BigInts
* asInt64(): truncates the BigInt to a single signed 64-bit integer; throws a JSIException if the truncation is lossy.
* getInt64(): truncates the BigInt to a single signed 64-bit integer
* isInt64(): returns true if the BigInt can be truncated losslessly to an int64_t
* asUint64(): truncates the BigInt to a single unsigned 64-bit integer; throws a JSIException if the truncation is lossy.
* getUint64(): truncates the BigInt to a single unsigned 64-bit integer
* isUint64(): returns true if the BigInt can be truncated losslessly to an uint64_t
* toString(radix): converts the BigInt to a string representing the number in the given radix.
A lossy truncation is one that yields a result from which the BigInt cannot be reconstructed from, i.e.,
* BigInt::fromInt64(b.toInt64()) !== b
* BigInt::fromUint64(b.toUint64()) !== b
Changelog: [Internal]
Reviewed By: kodafb
Differential Revision: D37909139
fbshipit-source-id: 172848024f8367aed73cc602f38cde22f03cac8f
Summary:
Add API for setting/getting native state.
When present, the internal NativeState property of an object always stores a NativeState with a pointer to a heap-allocated shared_ptr + a finalizer that simply `delete`s it.
Changelog:
[Internal][Added] - JSI API for setting/getting native state on a JS object
Reviewed By: jpporto
Differential Revision: D36499239
fbshipit-source-id: a1ff1905811db1aac99ece3f928b81d0abfb342b
Summary:
The current implementation of `throwJSError` places it in jsi.cpp, but
does not actually export it. This means that when JSI is being provided
by a dynamic library, `detail::throwJSError` will not be available.
To fix this, move the definition of `throwJSError` into jsi-inl.h,
similar to all of the other functions in the `detail` namespace. This
uses a roundabout implementation of `throwJSError` in order to avoid
directly using `throw`, which would fail to compile when exceptions are
turned off.
Changelog: [Internal]
Reviewed By: jpporto
Differential Revision: D36873154
fbshipit-source-id: bbea48e0d4d5fd65d67a029ba12e183128b96322
Summary:
The sampling heap profiler will need to be enable-able by the
inspector, and the previous snapshot/profiling APIs are all
defined on `jsi::instrumentation`.
Follow their lead and define the new sampling heap profiler APIs on
`jsi::instrumentation` as well, and implement it for Hermes.
Changelog: [Internal]
Reviewed By: neildhar
Differential Revision: D26835152
fbshipit-source-id: 4625be3214297489e04a4ceea8a22622d14299c4
Summary:
When taking a heap timeline, Hermes wasn't showing any data until the timeline
was written to disk and then reloaded.
Turns out we were missing support for two events:
* `HeapProfiler.lastSeenObjectId`: This event reports the most recently
allocated object ID. Used to know when objects were allocated in the
timeline.
* `HeapProfiler.heapStatsUpdate`: Report how many objects and bytes
exist for a "time fragment", represented by a fragment index. Later updates
to the same index can decrease the amount of live memory, which show up
as grey spikes instead of blue spikes
Previously, we only supported these by writing out to a file, and they didn't work
with a "live" profiling view. To fix this, I changed the periodic sampling thread
to instead be a periodic flush of a sample every few allocations. The performance
impact is tucked away only when profiling is turned on, and it's very non-invasive to
the rest of the GC. The flush calls a callback with the relevant information if the
inspector is on, and the inspector sends a message back to the browser.
Changelog: [Internal] Fix for Hermes heap timeline profiling
Reviewed By: neildhar
Differential Revision: D23993363
fbshipit-source-id: 8e0b571130cbb7e839dfb009b04f584f5179085d
Summary:
Continuing the adding of a "cause" field for logging to GCs.
This allows embedders of Hermes (such as React Native) to specify
the cause of a call to `collectGarbage`.
Notably, this allows Hermes to know when a GC is triggered by a memory warning.
Changelog: [Internal]
Reviewed By: sammy-SC
Differential Revision: D23742099
fbshipit-source-id: 99453e632328c00045b92a72f789d41c898dc518
Summary:
JSError creation can lead to further errors. Make sure these cases
are handled and don't cause weird crashes or other issues.
This solution has a few parts:
* include a ScopedNativeDepthTracker in checkStatus
* If an exception object message or stack property is already a String, don't
call JS String ctor on it
* Verify that a jsi::Value is a String before calling getString on it.
* Add more tests for JSError construction
Changelog: [Internal]
Reviewed By: dulinriley
Differential Revision: D21851645
fbshipit-source-id: 2d10da0e741ad4ede93cd806320f68ad512e5138
Summary:
We consume Hermes through multiple .so's, which means we have multiple (weak) typeinfo definitions of facebook::jsi::JSError. Previously we were using gnustl, which would strcmp typeinfo to decide whether a certain exception handler applies, which meant this didn't cause any major issues. However since this is deprecated, we recently switched to libc++, which does not have this by behaviour (or it does, but behind a flag I'm not sure how to enable). This causes any JS exceptions to fall through from our exception handlers and fatal the app.
This problem is actually documented in the common Android NDK problems page: https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#rtti_exceptions-not-working-across-library-boundaries
The suggested solution is to ensure that any exception types have a key function defined (a non-pure, out-of-line virtual function). The simplest one to add is a virtual destructor. This makes the object file that holds the implementation of the destructor export a non-weak typeinfo definition which will at load time override the other weak versions.
I'm not sure why we're the first to hit this. RN's JSIExecutor doesn't explicitly reference JSError which probably helps (https://github.com/facebook/react-native/blob/master/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp#L256-L258) and they also don't use unguarded callbacks like we do.
Changelog: [Internal]
Reviewed By: mhorowitz
Differential Revision: D21426524
fbshipit-source-id: 474284ada1ca2810045dc4402c420879447f9308
Summary:
If we're using synth traces in production, we probably don't want to accumulate the trace in memory, and write it to a file in one big step when an error happens. Rather, we'd like to write the trace incrementally, to a file: file system space is less scarse than memory, and doing it incrementally means less to do in the error handler.
This diff is the first step towards writing the synth trace incrementally. The most difficult aspect of this is that in the existing code, on Android, the trace is written in the handler of an Intent. Java-level operations on the Intent yielded a temporary filename under the application (so if the application is uninstalled, these temporary files are deleted). We must do something similar, but choose the filename within native code, on construction of the SynthTrace object. We copy what profiling does for this: assume the tmp dir is /data/data/<app>, where <app> can be found from reading /proc/self/cmdLine.
The SynthTrace constructor now takes a new argument: a unique_pointer to a stream. If null, no trace file is written, and the SynthTrace just accumulates records in memory. (This functionality is used during trace replay.) . If non-null, the trace is written to that stream.
The "write{BridgeTraffic}Trace..." methods become "flushAndDisable{BridgeTrafficTrace}" methods.
This diff is the first step towards incremental traces: the trace is still written at the end, but we've arranged that the place to write it is available at construction of the SynthTrace object. Later diffs will move recording of static things (e.g., the RuntimeConfig) into the ctor, then actually make us write the trace to the file incrementally.
For ReactNative:
Changelog: [Internal]
Reviewed By: haozhun, dulinriley
Differential Revision: D19471297
fbshipit-source-id: c1de4d2d9f44a87c7ff6fea38a1ce67de593940c
Summary:
Instead of returning a `bool` which gives no information about the cause of the error,
return `void` and throw when there's some error.
Another alternative is returning `std::error_code`, but that's less flexible than throwing, and
this API already supports throwing.
Changelog: [Internal]
Reviewed By: jbower-fb
Differential Revision: D19170033
fbshipit-source-id: 870cd996a1a53c94524455f31765c1da99f57a1d
Summary:
If building a JSError causes a JSError, this would lead to infinite recursion. This changes the error handling path so the problem is visible to JS. There's also a few related cleanups included, and a new test case.
Changelog: [General] [Fixed] - If Error global is not callable when building an error, jsi will throw a JS exception back to JS code. #158
Reviewed By: tmikov, dulinriley
Differential Revision: D18796269
fbshipit-source-id: 57a45d144fa2ea5e78d18c27d3130611737dda96
Summary:
This parameter used to be useful for a custom format hermes was developing,
but since Hermes now outputs the Chrome format it isn't useful.
Chrome actually disallows prettified JSON, and requires a special version that is faster to parse.
Therefore, `compact` was the only supported mode.
Changelog: [Internal] Remove compact parameter from `createSnapshotToFile`
Reviewed By: willholen
Differential Revision: D17726742
fbshipit-source-id: 6f39af9046dff2f3b4fba822312a9a89c939ed89
Summary: Make it possible for a Runtime implementation to provide its own JSON parsing, by making the method on Value call through Runtime, which now has the default implementation.
Reviewed By: tmikov
Differential Revision: D17637395
fbshipit-source-id: b8997f7d1721a7790326417f3abfa86c875923c9
Summary:
`getHeapInfo` was creating and return a `jsi::Object`. This object was not being traced
when API tracing was turned on.
This resulted in a bug in synth traces, an object was being acted upon without its creation source
being logged.
In order to prevent this, change the API of `getHeapInfo` to no longer return any objects, or modify other heap
contents.
This is preferable to having `TracingRuntime` attempt to mimic the operations of two different implementations.
## Changelog:
[Internal] [Changed] - Changed `jsi::Instrumentation::getHeapInfo` to use a `std::unordered_map`
Reviewed By: mhorowitz
Differential Revision: D17273235
fbshipit-source-id: f69860dcc524c2cf501746a41dbac20b4db8c456
Summary:
Adding an instrumentation endpoint that allows us to write a snapshot out to an
arbitrary output stream. This is in addition to `createSnapshotFromFile`. I
reserve the right to replace the latter with the former in a later diff.
This is necessary to allow snapshots to be requested over the chrome debugger
protocol. The protocol sends the snapshot over the wire in chunks so we need
to be able to use an output stream that can do the chunking.
Because LLVM types are not available at the JSI layer, we accept a
`std::ostream` reference which we wrap with `llvm::raw_os_ostream` within
Hermes. We should not incur the static initializer cost (or other code bloat)
of using ostreams as a result.
Reviewed By: cwdick
Differential Revision: D16016319
fbshipit-source-id: 2d0b4848fd5cbe9ddee371d856cd8eb19dd80396
Summary:
`/*` is the standard throughout open source code. For example, Firefox uses single /*: https://hg.mozilla.org/mozilla-central/file/21d22b2f541258d3d1cf96c7ba5ad73e96e616b5/gfx/ipc/CompositorWidgetVsyncObserver.cpp#l3
In addition, Rust considers `/**` to be a doc comment (similar to Javadoc) and having such a comment at the beginning of the file causes `rustc` to barf.
Note that some JavaScript tooling requires `/**`. This is OK since JavaScript files were not covered by the linter in the first place, but it would be good to have that tooling fixed too.
Reviewed By: zertosh
Differential Revision: D15640366
fbshipit-source-id: b4ed4599071516364d6109720750d6a43304c089
Summary: This change synchronizes the JSI API with its upstream, aiming to make this easier to automate in the future.
Reviewed By: mhorowitz
Differential Revision: D14783311
fbshipit-source-id: c180d0f728afbeb87a3e8e7331a39c06e73b907e
Summary: This will simplify updating the JSI API from upstream in the future.
Reviewed By: mhorowitz
Differential Revision: D14762674
fbshipit-source-id: fa4a86f08425943e301da4ef3df9893ebaa1493e