hermes icon indicating copy to clipboard operation
hermes copied to clipboard

Hermes object property limit causes problems with real use cases

Open evelant opened this issue 2 years ago • 27 comments

Problem

In development I want to load my source maps into my react-native app so I can parse and pretty print stacks with more useful locations. This works fine on JSC or V8 but cannot be done on hermes due to RangeError: Property storage exceeds 196607 properties.

Is the properly limit an important hard and fast requirement for hermes goals? Could it be lifted higher or lifted only in development mode?

Solution

Raise the hard limit on object properties so that large data structures such as source maps are usable with hermes.

Additional Context

evelant avatar Nov 11 '22 19:11 evelant

@tmikov this seems like it would be in your wheelhouse given your input on https://github.com/facebook/hermes/issues/139

evelant avatar Nov 11 '22 19:11 evelant

As @tmikov has already pointed out in those discussions, that isn't how object properties are intended to be used. Using a Map seems like the best option. I have a couple of follow up questions though.

  • Why are you re-creating this source map in an object in the first place? To me, it seems unlikely that you would need to be using the source map yourself manually. Are you correctly passing in the source map to Hermes? Are you not getting locations resolved automatically in the workflow you're using?
  • How are you creating this source map object? JSON.parse?

fbmal7 avatar Nov 14 '22 19:11 fbmal7

I'm consuming the maps output by metro with https://github.com/mozilla/source-map which I think is pretty much the standard source map consumer (v0.6.1 since later verisons use wasm which is unsupported on react-native).

I'm using it in conjunction with https://github.com/xpl/stacktracey and https://github.com/xpl/ololog to get really nice pretty-printed stacks and location tags for log output. This workflow is much nicer than the standard react-native logging + yellowbox. It works fine on JSC or v8, it's only hermes that fails due to the arbitrary limit.

evelant avatar Nov 14 '22 20:11 evelant

It looks like metro (react-native bundler) actually uses mozilla/source-map v0.5.6. I'm guessing that metro would probably crash if run on hermes due to this issue.

evelant avatar Nov 14 '22 20:11 evelant

@evelant You can recompile Hermes with larger GC segment size, each higher size gives you double the properties. IIRC, 4MB is around 200K props. You could increase it to 32MB (or even more), which should work fine on desktop or 64-bit devices.

tmikov avatar Nov 15 '22 01:11 tmikov

Thanks for the tip @tmikov, unfortunately there doesn't yet seem to be a simple way to build hermes from source as part of a react-native build.

This is a development mode only issue for me so it doesn't really have a huge impact although the dev experience was a lot nicer being able to leverage source maps from inside the app. React-native dev tooling is pretty painful in general (flipper is massively buggy, android crashes a lot, source maps don't work well, metro doesn't support symlinks, dependent upon millisecond clock sync between host and device, debugger usually doesn't work, docs are misleading and outdated, and so on) so anything to ease that a bit is pretty valuable at least to me.

I agree in non-dev situations it probably doesn't make sense to be building objects with > 200k properties, but for development mode in react-native it can be useful as I outlined above.

evelant avatar Nov 15 '22 01:11 evelant

My team is running into this elsewhere now with react-native's built in component hot reload support. I guess react-native maintains some sort of internal map when hot reloading components and it can exceed this property limit and cause a crash.

evelant avatar Nov 29 '22 19:11 evelant

Can you provide a repro with the RN crash?

jpporto avatar Nov 29 '22 20:11 jpporto

@jpporto I can't provide my project where this is happening but I'll see if I can make a reproduction.

evelant avatar Nov 29 '22 20:11 evelant

To add another real world example, where 3rd party code is internally using a plain object to track unique keys even though our own code is only using arrays.

For context our app handles large amounts of data, with multiple record types and some record types having ~300k records for certain customers.

import { uniqBy } from "lodash";

var largeArray = Array.from({ length: 200000 }).map(
  () => Math.random(),
  (item) => item
);

var result = uniqBy(largeArray);

globalThis.console?.log ? console.log(result) : print(result);

(bundled with npx esbuild --bundle in.js --outfile=out.js --target=es5 for testing and ran with the hermes cli)

This works fine in other JS engines but crashes in Hermes. I get that certain tradeoffs were made with this 4MB heap segment size but given React Native is pushing for Hermes to be the default it leaves us in a frustrating position where there's no easy path forward without rewriting large parts of our app, and then having to replace or patch a number of third party dependencies.

Of course one alternative is us maintaining custom Hermes builds with a larger segment size but we'd prefer to keep React Native as standard as possible to avoid future upgrade pain.

Just wanted to highlight that it's not always as simple as 'fix your code'. Sure we can do that and use Map instead of plain objects wherever necessary, but external dependencies are the hidden part of the iceberg here.

mjmasn avatar Dec 16 '22 12:12 mjmasn

I see this every morning when I come back to my simulator that was open all night, so it's only in the dev environment (iOS).

garrettg123 avatar Aug 04 '23 20:08 garrettg123

Update: this issue will be addressed by the next major release of Hermes.

tmikov avatar Aug 04 '23 21:08 tmikov

@tmikov So what is the approximate time window of new release? Is it going to be this year?

Czarczynski avatar Aug 29 '23 11:08 Czarczynski

I just want to add credence to the idea that sometimes there are real-world use cases that are impacted by this:

Our team is running into this issue because of our usage of URQL's graphcache. When we go to write the cache to the disk, it provides us the cache as an object and we often have more than 196k entities in the graphQL cache so we hit this limit.

With JSI being so common now in react-native libraries, using JavaScriptCore sucks because it means we can't run the app in a debugger anymore due to JSI calls. I really don't want to have to switch back to JSC but this deviation from the behavior of every other common JS runtime might force us to.

trcoffman avatar Oct 24 '23 01:10 trcoffman

Update: this issue will be addressed by the next major release of Hermes.

Apologies for the semi-ignorance but which version of react-native are you targeting? My app uses 0.72.5 and runs into this issue on a regular basis. I leave some background tasks running that do REST calls to the backend and usually after a day of leaving them running this issue starts to appear.

ArindamRayMukherjee avatar Oct 24 '23 07:10 ArindamRayMukherjee

@ArindamRayMukherjee Hermes development is independent from RN releases. We are not targeting any specific RN release. RN takes the latest version of Hermes when they cut a new release.

tmikov avatar Oct 25 '23 17:10 tmikov

is there any chance this gets fixed any time soon @tmikov ? bumping for visibility

seavan avatar Jan 17 '24 14:01 seavan

@seavan it is on our radar and will be addressed before the release of Static Hermes, meaning in 2024, but I can't really be more specific than that unfortunately.

tmikov avatar Jan 17 '24 16:01 tmikov

We can't transfer images over the react-native-webview bridge due to this issue unless we stringify them. Really frustrating and performance limiting.

Update: this issue will be addressed by the next major release of Hermes.

@tmikov is there any update on this?

Nantris avatar Mar 18 '24 20:03 Nantris

@Nantris I don't quite follow. Can you please explain the use case?

tmikov avatar Mar 18 '24 20:03 tmikov

@tmikov we need to send a buffer of an image over the bridge but it fails. Right now the only option seems to be to convert it to base64 or some other string, send it across, and then convert it back to a buffer, which is a real pain and performance killer.

Is there any update on what version of Hermes will resolve this issue?

Nantris avatar Mar 19 '24 19:03 Nantris

@Nantris what issue? You haven't explained what the problem is or how it relates to the property limit. I don't see what sending an image over the bridge has to do with the limit on total of number of properties in an object.

tmikov avatar Mar 19 '24 19:03 tmikov

A large enough buffer transferred across the bridge suffers the same error mentioned above (probably because all messages across the bridge undergo JSON.stringify, at least in our case).

Is this still planned to be fixed in Hermes itself? Or is that no longer on the roadmap?

Nantris avatar Mar 19 '24 20:03 Nantris

@Nantris I cannot answer your question before I understand exactly what problem you are having. I don't understand what you mean by "large buffer" or what it has to do with JSON.stringify(). I need a precise description so I can attempt to diagnose the problem.

Let's start here: are you creating an object with more than 200,000 properties and why? Have you considered using an array instead?

tmikov avatar Mar 19 '24 20:03 tmikov

@tmikov you surely meant "have you considered Map" instead! yes we considered! but there are some libraries which rely on default behaviour of JavaScript Object as a hash, with no hard limit for amount of properties. this is important!

seavan avatar Mar 19 '24 23:03 seavan

@seavan the problem that @Nantris seemed to drscribe was sending "an image across the RN bridge".

Without understanding the details of that, I can't really help, but two things stand out:

  • Images probably shouldn't be transmitted as JS objects with thousands of properties.
  • Serializing objects with hundreds of thousands of properties across the RN bridge will cause performance problems.

tmikov avatar Mar 20 '24 04:03 tmikov

For the record, increasing the number of objects properties is definitely still on the roadmap for 2024.

tmikov avatar Mar 20 '24 07:03 tmikov