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

feat(ses): fix undefined AsyncGenerator prototype on Hermes #2220

Closed

Conversation

leotm
Copy link
Contributor

@leotm leotm commented Apr 17, 2024

closes: #XXXX
refs: #1891

Description

Context: Running SES on React Native on Android (Hermes)

The AsyncGenerator prototype const AsyncGeneratorPrototype = AsyncGenerator.prototype is undefined on Hermes
but we could get round this by redefining the AsyncIteratorPrototype instead
we could also keep the original definition, but fallingback to this one

This is throwing an error calling addIntrinsics(getAnonymousIntrinsics())

at const AsyncIteratorPrototype= getPrototypeOf(AsyncGeneratorPrototype);
// TypeError: Cannot convert undefined value to object, js engine: hermes

Fixed in a repro here: leotm/RN07117SES@cb44efd
(ignoring the redefinition of AsyncFunctionInstance for now, which is coming as separate pr part2)

TODO: fix failing test

Alternatives considered

  • redefining the AsyncGeneratorPrototype if possible
  • a separate SES shim for Hermes with AsyncGeneratorPrototype and relevant parts removed

Security Considerations

Scaling Considerations

Documentation Considerations

Testing Considerations

Compatibility Considerations

Upgrade Considerations

  • Includes *BREAKING*: in the commit message with migration instructions for any breaking change.
  • Updates NEWS.md for user-facing changes.

- define async iterable
- define async iterator
- redefine async iterator prototype

Or keep the original and fallback to this if undefined
@leotm leotm force-pushed the ses-hermes-getAnonymousIntrinsics-p1 branch from 4308b5d to 0d2f230 Compare April 17, 2024 17:27
@leotm
Copy link
Contributor Author

leotm commented Apr 17, 2024

TODO: fix failing test

  Uncaught exception in test/test-static-module-record.js

  TypeError: Unexpected intrinsic intrinsics.%AsyncGeneratorPrototype%.__proto__ at %AsyncIteratorPrototype%

  TypeError: Unexpected intrinsic intrinsics.%AsyncGeneratorPrototype%.__proto__ at %AsyncIteratorPrototype%
    at visitPrototype (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:160:11)
    at visitProperties (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:272:5)
    at isAllowedPropertyValue (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:170:7)
    at isAllowedProperty (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:233:14)
    at visitProperties (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:283:26)
    at whitelistIntrinsics (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/permits-intrinsics.js:323:5)
    at repairIntrinsics (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/lockdown.js:348:3)
    at globalThis.lockdown (file:///Users/leo/Documents/GitHub/endo/packages/ses/src/lockdown-shim.js:16:28)
    at file:///Users/leo/Documents/GitHub/endo/packages/static-module-record/test/lockdown.js:3:1
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

  ✘ test/test-static-module-record.js exited with a non-zero exit code: 1

caused by importing lockdown (required otherwise Compartment is undefined)

after our redefinition of the AsyncIteratorPrototype

'%AsyncIteratorPrototype%': AsyncIteratorPrototype,

throwing at visitPrototype

throw TypeError(`Unexpected intrinsic ${path}.__proto__ at ${protoName}`);

unable to clean the new [[prototype]] we've introduced

Unexpected intrinsic intrinsics.%AsyncGeneratorPrototype%.__proto__ at %AsyncIteratorPrototype%

the error is confusing because intrinsics.%AsyncGeneratorPrototype%.__proto__ seems expected, which outputs [object Object] in other engines using
(async function* () {}.constructor).prototype.prototype.__proto__

and we can't simply skip throwing in this case, otherwise subsequent related test breaks
Uncaught exception in test/test-anticipate-async-iterator-helpers-shimmed.js
✘ test/test-anticipate-async-iterator-helpers-shimmed.js

so perhaps redefining AsyncGeneratorPrototype instead may yield a better result if possible
or an alternative redefinition of AsyncIteratorPrototype

or a hermes-flavour shim that excludes AsyncGeneratorPrototype and relevant parts entirely

const AsyncGeneratorPrototype = AsyncGenerator.prototype;
const AsyncIteratorPrototype = getPrototypeOf(AsyncGeneratorPrototype);
const AsyncGeneratorPrototype = AsyncGenerator.prototype; // undefined on Hermes
const AsyncIteratorPrototype = getPrototypeOf(asyncIterator);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be sufficient to do

Suggested change
const AsyncIteratorPrototype = getPrototypeOf(asyncIterator);
const AsyncIteratorPrototype = getPrototypeOf(AsyncGeneratorFunctionInstance());

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's likely. Worth testing if it falls into the category of what Hermes supports, but looks equivalent to what we've tried here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the suggestion works too ^ but still causes the test to fail, i've looked into it here #2220 (comment) if any thoughts

@mhofman
Copy link
Contributor

mhofman commented Apr 24, 2024

I think I'm getting confused by what the prototype chains of iterators/generators are in Hermes, and how they differ from the spec. The permits list really is a representation of the intrinsics graph a standards compliant engine is supposed to setup. Any way to summarize the difference?

@leotm
Copy link
Contributor Author

leotm commented May 3, 2024

I think I'm getting confused by what the prototype chains of iterators/generators are in Hermes, and how they differ from the spec. The permits list really is a representation of the intrinsics graph a standards compliant engine is supposed to setup. Any way to summarize the difference?

makes sense ^ i misread the SES permit TypeError: Unexpected intrinsic intrinsics.%AsyncGeneratorPrototype%.__proto__ at %AsyncIteratorPrototype%
thinking it's a false positive since

// unexpected %AsyncGeneratorPrototype%.__proto__
const AsyncGeneratorFunction = async function* () {}.constructor;
const AsyncGenerator = AsyncGeneratorFunction.prototype;
const AsyncGeneratorPrototype = AsyncGenerator.prototype;
const AsyncGeneratorPrototype__proto__ = AsyncGeneratorPrototype.__proto__
// expected [object Object] in engines supporting async generators

the Hermes async (arrow)fn/generator/iterator is bit confusing w vague/old features list, but w the testsuite confirmed

async functions supported
async arrow functions unsupported transformed by babel
async generators/iterators unsupported (will be added to Static Hermes)
it looks like babel differs from the spec and was hinted at before
so @babel/plugin-transform-async-to-generator may need an update
then bumping in react-native-babel-preset and metro-react-native-babel-preset if backported

some errors in the ses shim are surpressed so raised this here

also Metro errors on RN 71.15 when bundling async functions/generators are unsupported but not in later versions

so think should close this pr and chase up with babel then update here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants