Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypedArrays are not spec-compliant and break Buffer with many ecosystem packages #1495

Open
2 tasks done
ChALkeR opened this issue Aug 27, 2024 · 8 comments
Open
2 tasks done
Labels
bug Something isn't working

Comments

@ChALkeR
Copy link

ChALkeR commented Aug 27, 2024

Bug Description

See explainer, test and a monkey-patch fix at https://github.com/ExodusMovement/patch-broken-hermes-typed-arrays

> Buffer.alloc(10).subarray(0).toString('hex')
'0,0,0,0,0,0,0,0,0,0'
// What?

Sections of specification broken in Hermes:

See https://github.com/ExodusMovement/patch-broken-hermes-typed-arrays/blob/main/index.js for links to specific spec steps

See ecosystem fallout in feross/buffer#329

  • I have run gradle clean and confirmed this bug does not occur with JSC
  • The issue is reproducible with the latest version of React Native. Hermes

Hermes git revision (if applicable): 0410fb4 (latest main)
React Native version: 🤷🏻
OS:
Platform (most likely one of arm64-v8a, armeabi-v7a, x86, x86_64):

Steps To Reproduce

See full explainer, test and a monkey-patch fix at https://github.com/ExodusMovement/patch-broken-hermes-typed-arrays

chalker@Nikitas-Air hermes_workingdir % cat bad-inheritance.js 
var TestArray = function (...args) {
  print('called TestArray constructor')
  var buf = new Uint8Array(...args)
  Object.setPrototypeOf(buf, TestArray.prototype)
  return buf
}

Object.setPrototypeOf(TestArray.prototype, Uint8Array.prototype)
Object.setPrototypeOf(TestArray, Uint8Array)

var arr = new TestArray(10)
var mapped = arr.map((_, i) => i * 10)
print(mapped.constructor.name)
chalker@Nikitas-Air hermes_workingdir % jsc bad-inheritance.js                       
called TestArray constructor
called TestArray constructor
TestArray
chalker@Nikitas-Air hermes_workingdir % ./build_release/bin/hermes bad-inheritance.js
called TestArray constructor
Uint8Array

The Expected Behavior

Implementation of TypedArray per spec, perhaps without Symbol.species but with working inheritance

See spec refs above

@tmikov
Copy link
Contributor

tmikov commented Aug 27, 2024

Thank you for the detailed and well-researched bug report. We appreciate the effort you put into diagnosing the issue and linking to the relevant sections of the ECMAScript specification.

To provide some context, the behavior you're observing is actually by design in Hermes. As documented in our Excluded from Support section, we deliberately do not support Symbol.species or the use of .constructor property when creating new arrays in Array.prototype methods (the same reasoning applies to TypedArray.

Supporting this would require introducing a slow path in every case where a new object is returned that doesn't match the expected type. That would add a ton of complexity for a use case that didn't seem very important.

Frankly, we didn't realize the importance of this for implementing something Buffer on top of TypedArray. Additionally, it looks like in this case the returned object is of the same type.

I wonder whether it would be sufficient, as an intermediate step, to only support the case where .constructor is required to return an instance of the type in question.

@ChALkeR
Copy link
Author

ChALkeR commented Aug 27, 2024

@tmikov this is not about Symbol.species

image

TypedArraySpeciesCreate falls back to defaultConstructor on step 1 -- that is what this report is about
Missing Symbol.Species is not the issue here

Unsupported Symbol.Species should have been replaced with default constructor of the object, not with nothing

The testcase doesn't use Symbol.species

@ChALkeR
Copy link
Author

ChALkeR commented Aug 27, 2024

use of constructor property when creating new Arrays in Array.prototype methods

Ah, haven't noticed that! This breaks a lot of things though in a subtle unexpected way

E.g.: https://github.com/bitcoinjs/bitcoinjs-lib/blob/8d9775c20da03ab40ccbb1971251e71d117bcd8b/ts_src/psbt.ts#L1727-L1734

I wonder whether it would be sufficient, as an intermediate step, to only support the case where .constructor is required to return an instance of the type in question.

Unsure
Subtle spec differences may be breaking

A previous Buffer version relied on Symbol.species even

@tmikov
Copy link
Contributor

tmikov commented Aug 27, 2024

@ChALkeR so, it appears the intermediate step we proposed is the actual spec-compliant behavior of TypedArray. That is fortunate. We will implement at least the partial support through constructor and will consider adding incremental Symbol.species support.

@ChALkeR
Copy link
Author

ChALkeR commented Aug 27, 2024

Symbol.species is... at danger i think?
cc @ljharb perhaps?

@ljharb
Copy link

ljharb commented Aug 27, 2024

Symbol.species usage is being evaluated, and it's unclear which parts of it, if any, would be removed.

@ChALkeR
Copy link
Author

ChALkeR commented Aug 27, 2024

Hm: https://github.com/tc39/proposal-rm-builtin-subclassing?tab=readme-ov-file#prototype-methods-3

In that taxonomy:

  • That stage-1 proposal is about removing Type 2-4 and keeping only Type 1
  • This issue is about Type 2 not working correctly (which Buffer and ecosystem relies upon)
  • Symbol.species is Type 3 in that taxonomy

I'm not sure if it's worth to add Symbol.species but I don't think that Type 2 can realistically be removed

@ljharb
Copy link

ljharb commented Aug 27, 2024

I agree that Type 2 is unlikely to be removable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants