-
-
Notifications
You must be signed in to change notification settings - Fork 234
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
subarray returns Uint8Array which can't handle toString('uff-8') #329
Comments
I'm also experiencing this issue with version 6.0.3 (running in React Native/Hermes). Related: |
Any runtime which displays this behavior doesn't implement the es2016 "subarray calls child class" behavior and is returning a plain Uint8Array. Node v4.0.0 to v8.0.0 displayed this behavior, even with the native node.js Buffer object. So if you are running with an older version of v8, it could be argued that feross/buffer is accurately emulating the native node.js Buffer. This is probably only fixable with an ugly hack. Not sure if people are open to that. Personally, I would just avoid using |
Note: if you are experiencing this in a runtime which does support es2016 (i.e. a modern runtime), please post the runtime/browser/platform here. If there is actually a popular runtime out there that doesn't implement es2016 properly, we may have to support it. |
Hi @chjj 👋 We faced this issue twice, it broke only on this app (React native environment, es2017) : We polyfill Buffer like this : https://github.com/LedgerHQ/ledger-live/blob/develop/apps/ledger-live-mobile/src/polyfill.ts#L33 And it works perfectly fine usually Some infos about our issue... it first occured like this : Our partner Strica found that the issue was related to the subarray method, we fixed the issue by gently asking them to use slice instead of subarray (but slice is deprecated on node) We stumbled on the issue a second time with this PR (that we reverted) : LedgerHQ/ledger-live#6189 Thanks to the analysis of overcat we found some differences between our environments : The second environment is this one (Electron, React, es2020) : Issue occurs here with a subarray method call : https://github.com/stellar/js-xdr/blob/master/src/serialization/xdr-writer.js#L79 According to overcat, returned buffer is a Uint8Array 🤔 We are considering doing the ugly hack you are talking about just below our global.Buffer polyfill Hopefully I gave enough infos to see if there is something to be done on your side or not, and thanks ! |
@hedi-edelbloute is this affecting iOS, Android, or both? Which versions? I can't easily check to see if a Safari web view would cause this right now, but I would be shocked if this is occurring inside an Android web view. edit: The electron one is very strange. Not sure what to make of that. |
In the meantime you could try this monkey patch: if (!(Buffer.alloc(1).subarray(0, 1) instanceof Buffer)) {
Buffer.prototype.subarray = function subarray() {
const result = Uint8Array.prototype.subarray.apply(this, arguments);
Object.setPrototypeOf(result, Buffer.prototype);
return result;
};
} |
@chjj My phone is a Redmi note 10 Pro on Android 11 and I reproduced the issue, I'm trying to gather more informations. I think it also occured on iOS but i'm going to confirm this. Thank you for the patch proposal and your help, it's greatly appreciated. EDIT : We have no issue on Electron environment, sorry if it's confusing, it's only on React native environment |
Is your Android web view somehow using an old version of chrome? Is there any way to check? I'm not an expert on mobile web views, but I'm certain chrome version 60.x.x included v8 version 6.0.286 which should have the correct es2016 subarray behavior. (Chrome 60 was released 2017-07-25) |
Confirmed that issue also happens on iOS, version 16.6.1 on iPhone 12 Same here, not an expert about this subject 😅 I think I found a solution to get the webview version using |
Note that |
@vweevers, agreed. I think ledger might be in an unfortunate situation though. Their ecosystem is comprised of a number submitted apps from many different developers who are probably expecting I'm still not exactly convinced |
Here's a smaller test case you can use for testing the subarray behavior of the web view: 'use strict';
function TestBuffer(...args) {
const buf = new Uint8Array(...args);
Object.setPrototypeOf(buf, TestBuffer.prototype);
return buf;
}
Object.setPrototypeOf(TestBuffer.prototype, Uint8Array.prototype);
Object.setPrototypeOf(TestBuffer, Uint8Array);
console.log(TestBuffer(1).subarray(0, 1) instanceof TestBuffer); Prints |
I am not familiar with this area, but my device ( |
It's Android System Webview
Exactly |
@overcat, okay, I just want to make sure I have this right:
Is that correct? Can you verify for sure that number 2 is also happening -- that is, can you isolate it down to a simple test case rather than the code @hedi-edelbloute posted to be absolutely sure If both of these things are happening at once, there is something very strange going on and I will have to investigate further. |
The following is my test. // main.js
const buffer = require('buffer');
const Buffer = buffer.Buffer
const data = Buffer.alloc(10);
const subarray = data.subarray(0, 5)
const subarray_is_buffer = Buffer.isBuffer(subarray)
document.getElementById("p1").innerHTML = subarray_is_buffer;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p id="p1">Loading...</p>
</body>
<script src="./bundle.js"></script>
</html> The output is Update: I just noticed that browserify is not using the latest version of buffer. |
@overcat, if that's the case then I see only two possibilities for the errors produced by the code mentioned above:
|
Ahh, case solved. That was definitely a rollercoaster. Started questioning reality for a second there. Yeah, make sure you're using the latest buffer module. edit: Wait, do you mean to say your example above is not using the latest buffer or the polyfill.ts is not using the latest buffer? |
@chjj I'm referring to my example, I generated the code by running |
We are using version |
@overcat, hmmm, okay, I guess that makes sense since buffer v5.7 does handle subarray properly, but still doesn't explain our issue then. @hedi-edelbloute, yeah, it would be useful if you would modify the affected code to print something like: function functionThatCallsSubarray(buf) {
console.log(buf instanceof Buffer);
const res = buf.subarray(x, y);
console.log(res instanceof Buffer);
return res;
} If that prints |
I thought of one more idea, but then I need to call it quits for a bit. If the above test fails, please show me the output of: 'use strict';
function TestBuffer(...args) {
console.log(args);
const buf = new Uint8Array(...args);
Object.setPrototypeOf(buf, TestBuffer.prototype);
return buf;
}
Object.setPrototypeOf(TestBuffer.prototype, Uint8Array.prototype);
Object.setPrototypeOf(TestBuffer, Uint8Array);
console.log(TestBuffer(1).subarray(0, 1) instanceof TestBuffer); It should be something like:
|
Calling it quits now, but some final thoughts on this issue:
I suspect this is some kind of dependency issue. There may be multiple versions of |
Hey @chjj don't worry, it's not an urgent matter, I will post results here 😃 Thank you so much already You could be onto something with the dependency issue as we could have multiple versions at play |
I modified the [email protected] package in node_modules, added Here is the output in LLM:
|
[1]
false Why does it now print The above output is consistent with pre-es2016 behavior: the |
@chjj I am using the same device. this test is running on Google Chrome on my phone, and this one is in LLM. LLM is built on React Native, and then I realized that this is because React Native does not use Android System WebView as its default runtime environment? Then I found this link, it uses Hermes. Then I created a brand new project with React Native, which only contains the above code snippet, and it still outputs After I turned off the Hermes engine, it output the following results:
This is a feature supported by Hermes: https://github.com/facebook/hermes/blob/main/doc/Features.md Update: I have created a test project here, you can clone it if you need. https://github.com/overcat/LedgerBufferDebugApp/blob/408f57917a2ca1b01c8c297aae7718083f668404/App.tsx#L67-L95
|
Hi @chjj, based on the above discussion, I would like to ask if you are interested in adding support for the Hermes engine? It is the default engine for React Native projects. |
This is a hermes bug that has nothing to do with Here is a (potential) fix (UPD: non-compliant, don't use, will update): ;(function() {
var TypedArray = Object.getPrototypeOf(Uint8Array)
var { subarray } = TypedArray.prototype
TypedArray.prototype.subarray = function(...args) {
var arr = subarray.apply(this, args)
if (!this.constructor || this.constructor === arr.constructor) return arr
return new this.constructor(arr.buffer, arr.byteOffset, arr.length)
}
})() |
I will post a better fix soonish |
Made a proper fix: https://github.com/ExodusMovement/patch-broken-hermes-typed-arrays It's too large to be a snippet as it fixes several methods + uses runtime detection if it needs fixing and if the fix worked + tests in that repo for node/hermes/jsc https://npmjs.com/@exodus/patch-broken-hermes-typed-arrays The code if single-file, https://github.com/ExodusMovement/patch-broken-hermes-typed-arrays/blob/main/index.js, if anyone is interested in impl details That should cover all Buffer instances and other typed arrays, no matter where does the impl come from (some packages bundle their own copy of Buffer, so monkey-patching Buffer is not a proper solution) Readme explains that |
Filed facebook/hermes#1495 |
The text was updated successfully, but these errors were encountered: