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

Use a Map instead of compiled switch-case for keywords #173

Merged
merged 5 commits into from
May 22, 2022
Merged

Use a Map instead of compiled switch-case for keywords #173

merged 5 commits into from
May 22, 2022

Conversation

jsharkey13
Copy link
Contributor

As noted in #141, using Function causes issues for websites using a Content-Security-Policy that blocks unsafe-eval and it also appears that this is no longer the fastest approach.

This PR moves to using a Map, which caniuse data suggests is supported in all major browsers released since 2015. I don't think the overhead of falling back to object attribute lookup is worthwhile unless you specifically support an ancient browser you know to be incompatible. I also altered the benchmark test to use moo.keywords since it was not doing so (although the difference to this benchmark is inside the noise when running in Node for me).

We're going to move to this Map approach in our project that uses moo.js, so we will have some real-world verification all works as expected in a few weeks if you want to wait for that.

Resolves #141.

Otherwise changes to this method were not benchmarked.
…form

This should be more performant in modern browsers and avoids the security
issues of using the Function method with user input. This makes moo.js
compatible with Content-Security-Policy values that block unsafe-eval.

See #141
@tjvr
Copy link
Collaborator

tjvr commented Apr 6, 2022

I would slightly prefer continuing to support ancient browsers just in case someone is still using this on IE10 for some reason 😅 I imagine we can avoid the overhead of this by checking once at startup and swapping out how moo.keywords is defined.

But I do agree it's a bit silly. What do you think @nathan?

@nathan
Copy link
Collaborator

nathan commented Apr 7, 2022

As @tjvr observes, there's essentially no overhead for falling back to Object.create, and it doesn't complicate the implementation much. Dropping support for environments we currently support is arguably semver-major, so I think it makes sense to just keep supporting them.

The overhead of this try-catch applies only once at start-up when
loading the keywords.
I have tested that the fallback works in older browsers, and that there
is no performance impact of the try-catch for modern browsers.
@jsharkey13
Copy link
Contributor Author

Sure. I have added a fallback to using Object.create(null) if necessary, and tested that the method falls back and continues to work in Internet Explorer 10. (I didn't test the whole library, just the keywordsTransform function).

moo.js Outdated
// Use a JavaScript Map to map keywords to their corresponding token type
// unless Map is unsupported, then fall back to using an Object:
var reverseMap, reverseMapGet, reverseMapSet;
try {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not just if (typeof Map !== 'undefined')?

moo.js Outdated
var reverseMap, reverseMapGet, reverseMapSet;
try {
reverseMap = new Map
reverseMapGet = function(k) {
Copy link
Collaborator

@nathan nathan Apr 9, 2022

Choose a reason for hiding this comment

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

The helper functions reverseMapGet and reverseMapSet make your code longer and more complicated than using an if/else on typeof Map in the appropriate places. Maybe call it var isMap = typeof Map !== 'undefined' and ternary on that.

@jsharkey13
Copy link
Contributor Author

I think this is less clear than the helper functions; but I have again tested that it performs the same and still works in IE10, so hopefully this can progress now.

moo.js Outdated
if (typeof keyword !== 'string') {
throw new Error("keyword must be string (in keyword '" + tokenType + "')")
}
reverseMap[keyword] = tokenType
isMap ? reverseMap.set(keyword, tokenType) : reverseMap[keyword] = tokenType
Copy link
Collaborator

@nathan nathan May 7, 2022

Choose a reason for hiding this comment

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

Use an if statement here. You're not doing anything with result of the expression.

moo.js Show resolved Hide resolved
Copy link
Collaborator

@tjvr tjvr left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution! This looks good to me 👍

@tjvr tjvr merged commit 1aac4c2 into no-context:master May 22, 2022
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.

Content Security Policy: avoid need for "unsafe-eval"?
3 participants