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

A polyfill/ponyfill implementation #40

Open
slavafomin opened this issue Jan 12, 2023 · 12 comments
Open

A polyfill/ponyfill implementation #40

slavafomin opened this issue Jan 12, 2023 · 12 comments

Comments

@slavafomin
Copy link

slavafomin commented Jan 12, 2023

Hello!

Thank you for this proposal and your hard work!

While waiting for the proposal to be generally available, I've implemented a custom JSON parser that is future-compatible with the proposal. It doesn't look feasible to make it a proper polyfill, considering that JS implementation will probably be slower and we can't detect new features usage, but it's a ponyfill that can be used today where needed.

I'm not sure that I've implemented all the features correctly, it's not that easy to follow the standard, but all the serialization examples should work correctly. I will be very grateful if someone could review the implementation.

Thanks!

@ljharb
Copy link
Member

ljharb commented Jan 12, 2023

You should certainly be able to detect this feature, since it’s API and not syntax.

@slavafomin
Copy link
Author

slavafomin commented Jan 14, 2023

You should certainly be able to detect this feature, since it’s API and not syntax.

Could you recommend an approach to do this?

Maybe I don't see something obvious here, but as far as I know JS doesn't have a reflection API that will allow me to examine the number of arguments of the passed callback function. Of course I can turn the function to a string and then parse it's code in order to detect whether it has a third argument, but it looks pretty brittle. And this definitely won't work with the arguments.

In general, the detection could only happen when the user accesses the context.source or context.keys properties of the reviver function argument, but when this happens it's already too late to change the underlying parser implementation.

The only approach I see is that we can use JSON.parse initially and then discard the parsed result and re-parse the content using our custom implementation when we detect access to the context argument. This will lead to parsing the JSON document two times in a worst-case scenario and we will need to count the number of callbacks that was already fired in order to not call reviver multiple times for the same property.

This option is viable, but looks too hacky and not optimal to me.

@slavafomin
Copy link
Author

@gibson042 @mathiasbynens @ljharb could you please take a look at the implementation or at least at the API of the library? There are also some tests for this proposal. I just need to know if I got the proposal correctly. Thanks in advance!

@ljharb
Copy link
Member

ljharb commented Jan 14, 2023

@slavafomin you wouldn't need to do it every time - you just do it once with a known source string, and cache the result forever. For example, from the readme, JSON.stringify({ tooBigForNumber }, bigIntToRawJSON) === '{"tooBigForNumber":9007199254740993}' will be true when the feature is supported.

@slavafomin
Copy link
Author

@slavafomin you wouldn't need to do it every time - you just do it once with a known source string, and cache the result forever. For example, from the readme, JSON.stringify({ tooBigForNumber }, bigIntToRawJSON) === '{"tooBigForNumber":9007199254740993}' will be true when the feature is supported.

I'm not sure I follow, could you elaborate please? What exactly should we cache?

@ljharb
Copy link
Member

ljharb commented Jan 14, 2023

the boolean result of that comparison - either true if it's supported, or false if it's not.

@HolgerJeromin
Copy link

Maybe I don't see something obvious here, but as far as I know JS doesn't have a reflection API that will allow me to examine the number of arguments of the passed callback function. Of course I can turn the function to a string and then parse it's code in order to detect whether it has a third argument, but it looks pretty brittle. And this definitely won't work with the arguments.

Does
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length
solve the problem?

@slavafomin
Copy link
Author

Maybe I don't see something obvious here, but as far as I know JS doesn't have a reflection API that will allow me to examine the number of arguments of the passed callback function. Of course I can turn the function to a string and then parse it's code in order to detect whether it has a third argument, but it looks pretty brittle. And this definitely won't work with the arguments.

Does https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length solve the problem?

Didn't know that, thanks for mentioning! I'm gonna test it out.

@slavafomin slavafomin changed the title A polyfil (ponyfil) implementation A polyfill (ponyfill) implementation Jan 14, 2023
@slavafomin
Copy link
Author

slavafomin commented Jan 14, 2023

Does https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length solve the problem?

By the way it won't work with arguments, but I think it's a pretty rare use case so we can ignore (document) it for now.

@slavafomin slavafomin changed the title A polyfill (ponyfill) implementation A polyfill/ponyfill implementation Jan 15, 2023
@slavafomin
Copy link
Author

Thanks to the @HolgerJeromin suggestion, I've implemented a proper polyfill. Here's the usage details. And here's the implementation.

I've already released a beta-version of the package that can be used for testing: @ton.js/json-parse-polyfill@beta.

Still, will be very happy for implementation review!

@jcubic
Copy link

jcubic commented Dec 23, 2023

You can use this code to detect if the new proposal is implemented:

let parse = function parse(string, callback) {
    callback(1, 2, { source: 3 });
};

function detect() {
  return new Promise(resolve => {
    // use JSON.parse here
    parse('"x"', function(key, value, x) {
      resolve(typeof x !== 'undefined');
    });
  });
}


const supported = await detect();

console.log({supported: await detect()});

parse = function parse(string, callback) {
    callback(1, 2);
};

console.log({supported: await detect()});

the parse function is JSON.parse mock that executes the receiver with an extra argument that is tested.

EDIT:
And since the JSON.parse is not async you can use this version:

function detect() {
  let result;
  JSON.parse('"x"', function(key, value, x) {
      result = typeof x !== 'undefined';
  });
  return result;
}

@WebReflection
Copy link

to whom it might concern, another ponyfill landed in ungap: https://github.com/ungap/raw-json

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

No branches or pull requests

5 participants