React-native: [Android] [RN 58.3] Calling substring() on a String that contains an emoji causes "Error: Exception in HostFunction: basic_string::resize"

Created on 14 Feb 2019  Β·  14Comments  Β·  Source: facebook/react-native

πŸ› Bug Report

On Android (I couldn't test on iOS), with react-native 0.58.3, try to call substring(0,1) on a string that begins with an emoji.
You should get this error: [Error: Exception in HostFunction: basic_string::resize]
I guess this is linked to some bad conversion from UTF 16 to UTF 8.

This is not happening on 0.57.3 for instance (which I was using earlier for the same project)

Code Example

const text = 'πŸ™ƒblablabla'
try {
    const newText = text.substring(0, 1)
    Alert.alert(newText)
  } catch(e) {
    console.error(e)
}

Environment

React Native Environment Info:
System:
  OS: macOS High Sierra 10.13.5
  CPU: (4) x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
  Memory: 41.97 MB / 8.00 GB
  Shell: 3.2.57 - /bin/bash
Binaries:
  Node: 10.15.0 - ~/.nvm/versions/node/v10.15.0/bin/node
  Yarn: 1.12.3 - /usr/local/bin/yarn
  npm: 6.4.1 - ~/.nvm/versions/node/v10.15.0/bin/npm
  Watchman: 4.7.0 - /usr/local/bin/watchman
SDKs:
  iOS SDK:
    Platforms: iOS 10.3, macOS 10.12, tvOS 10.2, watchOS 3.2
  Android SDK:
    API Levels: 23, 24, 25, 26, 27, 28
    Build Tools: 23.0.1, 23.0.2, 25.0.0, 25.0.1, 25.0.2, 25.0.3, 26.0.1, 26.0.2, 26.0.3, 27.0.1, 27.0.3, 28.0.0, 28.0.3
    System Images: android-27 | Google APIs Intel x86 Atom, android-28 | Google APIs Intel x86 Atom_64
IDEs:
  Android Studio: 3.1 AI-173.4670197
  Xcode: 8.3.3/8E3004b - /usr/bin/xcodebuild
npmPackages:
  react: 16.6.3 => 16.6.3 
  react-native: 0.58.3 => 0.58.3 
npmGlobalPackages:
  react-native-git-upgrade: 0.2.7
Bug JavaScript Android Locked

Most helpful comment

@OlivierFreyssinet this is a limitation of multi-byte characters in JS strings. While ideally it shouldn't crash/error so badly in React Native it is something that should be accounted for in user-land JS.

'πŸ™ƒ'.length
// 2

As you can see this is a multi-byte character so you'll need to use multi-byte compatible operations if you want to handle the emoji correctly.

As @wakeless pointed out using Array.from() should be safer in most cases however it will give surprising results on emojis that use joining characters like πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©:

'πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©'.length
// 11

Array.from('πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©')
// ["πŸ‘©", "", "❀", "", "", "πŸ’‹", "", "πŸ‘©"]

If you want to handle these cases, a library like https://www.npmjs.com/package/runes might useful.

import runes from "runes";

runes('πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©')
// ["πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©"]

All 14 comments

I'm having a similiar issue on ios. Seems to be something to do with character boundaries on that emoji. Have tested with the following. Both fail on iOS 0.58.4

let emojiString = 'πŸ’•';
<Text>{emojiString.charAt(0)}</Text>
<Text>{emojiString[0]}</Text>

I've been reliably informed that these aren't really supposed to work. The best way to do this for future use is: <Text>Array.from(emojiString)[0]</Text>

@OlivierFreyssinet this is a limitation of multi-byte characters in JS strings. While ideally it shouldn't crash/error so badly in React Native it is something that should be accounted for in user-land JS.

'πŸ™ƒ'.length
// 2

As you can see this is a multi-byte character so you'll need to use multi-byte compatible operations if you want to handle the emoji correctly.

As @wakeless pointed out using Array.from() should be safer in most cases however it will give surprising results on emojis that use joining characters like πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©:

'πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©'.length
// 11

Array.from('πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©')
// ["πŸ‘©", "", "❀", "", "", "πŸ’‹", "", "πŸ‘©"]

If you want to handle these cases, a library like https://www.npmjs.com/package/runes might useful.

import runes from "runes";

runes('πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©')
// ["πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘©"]

@levibuzolic thanks for your reply!
Indeed Array.from() should be safer but the problem here is not that the emojis are not split correctly. It would be perfectly fine for me if it was handled in the same way as on previous RN versions, ie "πŸ™ƒ".substring(0,1) resulting in a "οΏ½" character.

In fact I think that there's a problem in the new JSC introduced in 0.58, because Array.from("πŸ™ƒ") throws the exact same error.

To be perfectly clear, this bug report isn't about an "unexpected result" but about an error that, if it's not caught, will make the app crash.

I'm having a similiar issue on ios. Seems to be something to do with character boundaries on that emoji. Have tested with the following. Both fail on iOS 0.58.4

let emojiString = 'πŸ’•';
<Text>{emojiString.charAt(0)}</Text>
<Text>{emojiString[0]}</Text>

Hi @wakeless ! Is it crashing or just displaying "οΏ½"/something similar?

Thanks everyone for reporting, since it's related to the JSC I'll try to ask around if someone can confirm this.

In the meantime, would anyone of you be kind enough to create a repro?

Actually, could you first test with the 0.59rc and LMK if it still happens?

@kelset thanks for your reply!
I just tried with the 0.59rc and it's still happening. I'm creating a repo right now.

There you go @kelset, it runs on RN 0.59.0-rc.1
https://github.com/OlivierFreyssinet/rn-substring-error

New JSC came in 0.59 not 0.58.
0.58 brought in Android 64-bit support so maybe the issue is just about 64-bit devices, in case your device is one too.

I can confirm that this issue occurs on iOS as well, it appears something else has changed. I am looking in to it.

Error: Exception in HostFunction: basic_string

Encountered this mistake also but when I console.log the split data is ok. why happend this.

My experience with emoji in strings is that the app crash if the emoji is first character in the string, but it works if the string starts with normal character (unicode or not).

On iOS, Array.from works well, but android crash also.

try
(obj.str).substring(0, 1);

Was this page helpful?
0 / 5 - 0 ratings