Skip to content

Commit

Permalink
Allow disable of RegExp cache/restore
Browse files Browse the repository at this point in the history
The implementation of cache/restore of RegExp properties
has the potential to cause a number of issues.  A perfect
solution is not possible because it's not possible to
determine the regex that was used to turn the cached input
into the cached match and group matches.  The current
solution simply takes the lastMatch and escapes it,
wrapping any match groups in parens.  One problem with
this approach is that when the match is very long, it
will overrun the maximum length of a RegEx in JavaScript.

In this commit, we add the capability to disable restore
of RegExp properties entirely, via
`IntlPolyfill.__disableRegExpRestore`.
  • Loading branch information
Mike Lewis committed Jul 7, 2016
1 parent f8c5616 commit b5d5bf7
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 1 deletion.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ is automatically added to the runtime and does not need to be provided.
Contents of the `locale-data` directory are a modified form of the Unicode CLDR
data found at http://www.unicode.org/cldr/.

## RegExp cache / restore
`Intl.js` attempts to cache and restore static RegExp properties before executing any
regular expressions in order to comply with ECMA-402. This process is imperfect,
and some situations are not supported. For example, you may experience errors when
attempting to use `Intl.js` methods immediately after executing a regular expression
against a very long input. This behavior is not strictly necessary, and is only
required if the app depends on RegExp static properties not changing (which is highly
unlikely). To disable this functionality, invoke `Intl.__disableRegExpRestore()`.

## Contribute

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"build:dist": "npm run build:dist:dev && npm run build:dist:prod",
"build": "npm run build:data && npm run build:lib && npm run build:dist",
"lint": "eslint .",
"test": "cd tests && node polyfilling.js && node sanity.js && node noderunner.js && node saucelabs.js",
"test": "cd tests && node polyfilling.js && node sanity.js && node noderunner.js && node saucelabs.js && node disableregexprestore.js",
"pretest": "npm run lint",
"preversion": "npm run clean && npm run build && npm run test",
"prepublish": "npm run clean && npm run build"
Expand Down
6 changes: 6 additions & 0 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,10 @@ function addLocaleData (data, tag) {
setDefaultLocale(tag);
}

defineProperty(Intl, '__disableRegExpRestore', {
value: function () {
internals.disableRegExpRestore = true;
}
});

export default Intl;
4 changes: 4 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ List.prototype = objCreate(null);
* Constructs a regular expression to restore tainted RegExp properties
*/
export function createRegExpRestore () {
if (internals.disableRegExpRestore) {
return { exp: /a/, input: 'a' };
}

let esc = /[.?*+^$[\]\\(){}|-]/g,
lm = RegExp.lastMatch || '',
ml = RegExp.multiline ? 'm' : '',
Expand Down
40 changes: 40 additions & 0 deletions tests/disableregexprestore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
var IntlPolyfill = require('../');

function assertEqual(value, expected, message) {
console.log(message);
if (value !== expected) {
console.error(' > ERROR: expected value ' + expected + ' but the actual value is ' + value);
process.exit(1);
} else {
console.log(' > PASSED');
}
}

function assertNotEqual(value, expected, message) {
console.log(message);
if (value === expected) {
console.error(' > ERROR: expected value ' + expected + ' but the actual value is ' + value);
process.exit(1);
} else {
console.log(' > PASSED');
}
}

/foo/.exec('a foo test');
new IntlPolyfill.NumberFormat('en-US', {
style: 'currency',
currency: 'GBP',
minimumFractionDigits: 2,
});
assertEqual(RegExp.input, 'a foo test', 'Normally, RegExp.input should be cached and restored');
assertEqual(RegExp.lastMatch, 'foo', 'Normally, RegExp.lastMatch should be cached and restored');

IntlPolyfill.__disableRegExpRestore();
/foo/.exec('a foo test');
new IntlPolyfill.NumberFormat('en-US', {
style: 'currency',
currency: 'GBP',
minimumFractionDigits: 2,
});
assertNotEqual(RegExp.input, 'a foo test', 'After invoking __disableRegExpRestore, RegExp.input should not be cached and restored');
assertNotEqual(RegExp.lastMatch, 'foo', 'After invoking __disableRegExpRestore, RegExp.lastMatch should not be cached and restored');

0 comments on commit b5d5bf7

Please sign in to comment.