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

Fix codegen output for object with indexer #35344

Closed
wants to merge 13 commits into from

Conversation

ZihanChen-MSFT
Copy link
Contributor

Summary

The current implementation think {[key:T]:U} and {key:object} are the same type, which is semantically wrong.

This pull request fixes the problem and return {type:'GenericObjectTypeAnnotation'}, so that {[key:T]:U} is Object. The current schema cannot represent dictionary type, Object is the closest one.

The previous incorrect implementation actually bring in code with logic that doesn't make sense (treating indexer as property), those and related unit test are all undone.

Changelog

[General] [Changed] - Fix codegen output for object with indexer

Test Plan

yarn jest react-native-codegen passed

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Microsoft Partner: Microsoft Partner labels Nov 14, 2022
@analysis-bot
Copy link

analysis-bot commented Nov 14, 2022

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 7,776,630 -57
android hermes armeabi-v7a 7,096,414 -57
android hermes x86 8,251,531 -5
android hermes x86_64 8,108,901 -47
android jsc arm64-v8a 8,969,654 +0
android jsc armeabi-v7a 7,702,739 +0
android jsc x86 9,033,808 +0
android jsc x86_64 9,510,746 +0

Base commit: 0f089ea
Branch: main

@christophpurrer
Copy link
Contributor

I fear this will conflict with #35098 and won't pass our internal builds as we rely on parsing indexed properties for (std::)map data structures in C++ TM.

I agree that mixing properties and indexed types is not ideal - but it was a cheap initial solution.

If we want to 'Fix codegen output for object with indexer' I would expect that this change:

  • splits properties and indexes apart
  • correctly parses them and exports them to the output

I don't think the current PR can be landed as it - as it simply removes existing logic.

For the current sample we would then output something as:

                  'typeAnnotation': {
                    'type': 'IndexedObjectTypeAnnotation',
                    'properties': [
                      {
                        'name': 'key',
                        'typeAnnotation': {
                          'type': 'GenericObjectTypeAnnotation'
                        }
                      }
                    ]

@analysis-bot
Copy link

analysis-bot commented Nov 14, 2022

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: a3c47d3
Branch: main

@pull-bot
Copy link

PR build artifact for 32bc4f8 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

Copy link
Contributor

@cipolleschi cipolleschi left a comment

Choose a reason for hiding this comment

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

Thank you for taking the time to solve this semantic issue.

I left a few comments to improve the code maintainability, let me know what do you think.

Functionality-wise, it looks good to me.

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@ZihanChen-MSFT
Copy link
Contributor Author

ZihanChen-MSFT commented Nov 15, 2022

as we rely on parsing indexed properties for (std::)map data structures in C++ TM.

Hi @christophpurrer . As I mentioned in the description, the current behavior parses {[key:T]:U} exactly like {key:object}. I think this is a incorrect behavior, and you can't tell the difference between them, I don't really understand how will it be translated to a map instead of a struct with 'key'.

I think making a IndexedObjectTypeAnnotation would be good, but unfortunately @cipolleschi said it has been rejected by another internal group of people. If I could choose, I would add a new type, it is better.

But @cipolleschi I think here is exactly a useful use case. I am totally fine if we could add a new type here. Otherwise, the only correct way will be treating it like an object, and code using std::map should just be reverted, because it relies on a bug.

@christophpurrer
Copy link
Contributor

as we rely on parsing indexed properties for (std::)map data structures in C++ TM.

Hi @christophpurrer . As I mentioned in the description, the current behavior parses {[key:T]:U} exactly like {key:object}. I think this is a incorrect behavior, and you can't tell the difference between them, I don't really understand how will it be translated to a map instead of a struct with 'key'.

But @cipolleschi I think here is exactly a useful use case. I am totally fine if we could add a new type here. Otherwise, the only correct way will be treating it like an object, and code using std::map should just be reverted, because it relies on a bug.

  • Totally agree we shouldn't treat index and property the same.
  • Ideally it would be great to still parse and emit the indexes to the parsed output - we already have the logic, we just need to stored them under indexes instead of properties > If not doing this here, do you want to send out a follow up PR for that work?
  • If we know the key type and value type of of indexed objects it makes it safer down the road when we convert them to std:map representation

@ZihanChen-MSFT > What is your main motivation for this PR? Are you fixing an actual issue / bug on your side OR you 'just' want to make things semantically correct?

@ZihanChen-MSFT
Copy link
Contributor Author

ZihanChen-MSFT commented Nov 15, 2022

What is your main motivation for this PR?

Hi @christophpurrer

in Microsoft/react-native-windows we have our backend too, the C++ codegen there will print the most detailed types we could find to improve type safety.

By having the codegen treat {[key:T]:U} like {key:U}, it will generate a struct {JsonObject key;}; for a dictionary type, and make real fields missing in native world, it is a blocker for us. We need a way to tell the differences between them, but since adding IndexedObjectTypeAnnotation is not approved (as in offline conversation to @cipolleschi ), so the only correct way is to treat it like Object, which will be translated to a JSON object in native world, and we could read all values by looking into the JSON object, just like a dictionary.

My original idea was to add a IndexedObjectTypeAnnotation. I would be happy to do that.

@pull-bot
Copy link

PR build artifact for 8a559d6 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Copy link
Contributor

@cipolleschi cipolleschi left a comment

Choose a reason for hiding this comment

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

I applied the comments @christophpurrer added internally.

I think that they are valid, so I forwarded them here.

Thank you for taking care of these PRs.

packages/react-native-codegen/src/parsers/parser.js Outdated Show resolved Hide resolved
packages/react-native-codegen/src/parsers/parser.js Outdated Show resolved Hide resolved
).length > 0
) {
// no need to do further checking
return emitObject(nullable);
Copy link
Contributor

Choose a reason for hiding this comment

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

from @christophpurrer:

What if we emit a new type here?
Instead of GenericObjectTypeAnnotation we can use IndexedObjectTypeAnnotation

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is the idea of DictionaryObjectTypeAnnotation but you said you don't want me to add that type am I right?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion of adding or not adding a new type for indexed objects at this point.

If we would add a new type we should keep its name as close as possible to the internal representation in flow-parser (ObjectTypeIndexer) or babel-parser with the TypeScript plugin (TSIndexSignature)

Dictionary is not a term used in either of the 2 parsers.

@pull-bot
Copy link

PR build artifact for eb28c31 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

@cipolleschi
Copy link
Contributor

/rebase

@github-actions
Copy link

github-actions bot commented Nov 29, 2022

Warnings
⚠️

packages/react-native-codegen/src/parsers/tests/parsers-commons-test.js#L31 - packages/react-native-codegen/src/parsers/tests/parsers-commons-test.js line 31 – 'typeScriptParser' is assigned a value but never used. (no-unused-vars)

Generated by 🚫 dangerJS against 49c8252

@pull-bot
Copy link

PR build artifact for aba3568 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

Copy link
Contributor

@cipolleschi cipolleschi left a comment

Choose a reason for hiding this comment

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

I left a couple of comments for some unused return values.
I'll import it to see if something breaks

if (indexSignatures.length > 0) {
// check the property type to prevent developers from using unsupported types
const propertyType = indexSignatures[0].typeAnnotation;
translateTypeAnnotation(
Copy link
Contributor

Choose a reason for hiding this comment

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

We are not using the returned value of translateTypeAnnotation. Is it right?

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 return value is ignored, since we don't really have DictionaryTypeAnnotation, there is no place to return it. But I call this function to check if the type is supported.

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@cipolleschi
Copy link
Contributor

cipolleschi commented Nov 29, 2022

Unfortunately, things are breaking internally.

We have a bunch of
UnsupportedTypeAnnotationParserError: Module NativeFeliosSceneModule: Flow type annotation 'MixedTypeAnnotation' is unsupported in NativeModule specs
I think we should add some logging on which file is being parsed in case of errors 🤔
I cannot understand which module is actually emitting the error because is only see the name of the file that aggregates all the modules specs...

This is the most likely cause of the failure. In a TM we have this export

export type MyType = {[name: string]: (args: mixed) => mixed};

So I think that the Flow mixed is not allowed.

@ZihanChen-MSFT
Copy link
Contributor Author

ZihanChen-MSFT commented Nov 29, 2022

@cipolleschi https://flow.org/en/docs/types/mixed/ Looks like mixed, 'any' and Object all give no information about types but:

  • Object: Flow knows it is an Object (so it could not be undefined, but it could be null though).
  • 'any': Flow would treat it as any type if needed.
  • 'mixed': Flow assume it could be anything, but the type is unknown, no automatic conversion is performed.

So I could generate mixed as Object. mixed is supported in C++ but it reports MixedTypeAnnotation, I don't know what it means, but I will generate GenericObjectTypeAnnotation in other cases for backward compatibility. If you think we should always use MixedTypeAnnotation, I'm fine.

    case 'MixedTypeAnnotation': {
      if (cxxOnly) {
        return emitMixedTypeAnnotation(nullable);
      } else {
        return emitObject(nullable);
      }
    }

@pull-bot
Copy link

PR build artifact for 099766b is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@pull-bot
Copy link

pull-bot commented Dec 1, 2022

PR build artifact for 56f69b8 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

@pull-bot
Copy link

PR build artifact for 49c8252 is ready.
To use, download tarball from "Artifacts" tab in this CircleCI job then run yarn add <path to tarball> in your React Native project.

@facebook-github-bot
Copy link
Contributor

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Dec 13, 2022
@facebook-github-bot
Copy link
Contributor

@cipolleschi merged this pull request in f07490b.

@ZihanChen-MSFT ZihanChen-MSFT deleted the ts-rncodegen15 branch December 13, 2022 19:31
OlimpiaZurek pushed a commit to OlimpiaZurek/react-native that referenced this pull request May 22, 2023
Summary:
The current implementation think `{[key:T]:U}` and `{key:object}` are the same type, which is semantically wrong.

This pull request fixes the problem and return `{type:'GenericObjectTypeAnnotation'}`, so that `{[key:T]:U}` is `Object`. The current schema cannot represent dictionary type, `Object` is the closest one.

The previous incorrect implementation actually bring in code with logic that doesn't make sense (treating indexer as property), those and related unit test are all undone.

## Changelog

[General] [Changed] - Fix codegen output for object with indexer

Pull Request resolved: facebook#35344

Test Plan: `yarn jest react-native-codegen` passed

Reviewed By: rshest

Differential Revision: D41304475

Pulled By: cipolleschi

fbshipit-source-id: caab8e458d83f9850c5c28b67cc561a764738372
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. p: Microsoft Partner: Microsoft Partner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants