Skip to content

Commit

Permalink
[Breaking] update to match latest spec
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed May 28, 2018
1 parent 826dd0e commit 92a6118
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 82 deletions.
8 changes: 6 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"extends": "@ljharb",

"rules": {
"complexity": [2, 12],
"func-name-matching": 0,
"max-nested-callbacks": [2, 3],
"max-params": [2, 3],
"max-params": [2, 4],
"max-statements-per-line": [2, { "max": 2 }],
"max-statements": [2, 22],
"max-statements": [2, 24],
"new-cap": [2, {
"capIsNewExceptions": [
"IsRegExp",
Expand All @@ -27,8 +28,11 @@
"RegExpExec",
"CreateIterResultObject",
"AdvanceStringIndex",
"GetIntrinsic",
"ObjectCreate",
],
}],
"no-restricted-syntax": [2, "BreakStatement", "ContinueStatement", "DebuggerStatement", "LabeledStatement", "WithStatement"],
"operator-linebreak": [2, "before"],
}
}
41 changes: 23 additions & 18 deletions helpers/MatchAllIterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,34 @@ var flagsGetter = require('regexp.prototype.flags');

var RegExpStringIterator = require('./RegExpStringIterator');
var OrigRegExp = RegExp;
var hasFlags = typeof (/a/).flags === 'string';

module.exports = function MatchAllIterator(R, O) {
if (!ES.IsRegExp(R)) {
throw new TypeError('MatchAllIterator requires a regex');
}
var S = ES.ToString(O);
var C = ES.SpeciesConstructor(R, OrigRegExp);
var flags = ES.Get(R, 'flags');

var matcher;
var actualFlags = typeof flags === 'string' ? flags : flagsGetter(R);
if (hasFlags) {
matcher = new C(R, actualFlags); // ES.Construct(C, [R, actualFlags]);
} else if (C === OrigRegExp) {
// workaround for older engines that lack RegExp.prototype.flags
matcher = new C(R.source, actualFlags); // ES.Construct(C, [R.source, actualFlags]);
var matcher, global, fullUnicode, flags;
if (ES.IsRegExp(R)) {
var C = ES.SpeciesConstructor(R, OrigRegExp);
flags = ES.Get(R, 'flags');
if (typeof flags === 'string') {
matcher = new C(R, flags); // ES.Construct(C, [R, flags]);
} else if (C === OrigRegExp) {
// workaround for older engines that lack RegExp.prototype.flags
matcher = new C(R.source, flagsGetter(R)); // ES.Construct(C, [R.source, flagsGetter(R)]);
} else {
matcher = new C(R, flagsGetter(R)); // ES.Construct(C, [R, flagsGetter(R)]);
}
global = ES.ToBoolean(ES.Get(matcher, 'global'));
fullUnicode = ES.ToBoolean(ES.Get(matcher, 'unicode'));
var lastIndex = ES.ToLength(ES.Get(R, 'lastIndex'));
ES.Set(matcher, 'lastIndex', lastIndex, true);
} else {
matcher = new C(R, actualFlags); // ES.Construct(C, [R, actualFlags]);
flags = 'g';
matcher = new OrigRegExp(R, flags);
global = true;
fullUnicode = false;
if (ES.Get(matcher, 'lastIndex') !== 0) {
throw new TypeError('Assertion failed: newly constructed RegExp had a lastIndex !== 0. Please report this!');
}
}
var global = ES.ToBoolean(ES.Get(R, 'global'));
var fullUnicode = ES.ToBoolean(ES.Get(R, 'unicode'));
var lastIndex = ES.ToLength(ES.Get(R, 'lastIndex'));
ES.Set(matcher, 'lastIndex', lastIndex, true);
return new RegExpStringIterator(matcher, S, global, fullUnicode);
};
72 changes: 51 additions & 21 deletions helpers/RegExpStringIterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@

var define = require('define-properties');
var ES = require('es-abstract');
var GetIntrinsic = require('es-abstract/GetIntrinsic');
var hasSymbols = require('has-symbols')();

var hidden = require('./hidden')();
var undefined; // eslint-disable-line no-shadow-restricted-names

/* eslint max-params: 1 */
var RegExpStringIterator = function RegExpStringIterator(R, S, global, fullUnicode) {
if (ES.Type(S) !== 'String') {
throw new TypeError('S must be a string');
}
if (!ES.IsRegExp(R)) {
throw new TypeError('R must be a RegExp');
}
if (ES.Type(global) !== 'Boolean') {
throw new TypeError('global must be a boolean');
}
Expand All @@ -23,34 +21,43 @@ var RegExpStringIterator = function RegExpStringIterator(R, S, global, fullUnico
hidden.set(this, '[[IteratingRegExp]]', R);
hidden.set(this, '[[IteratedString]]', S);
hidden.set(this, '[[Global]]', global);
hidden.set(this, '[[FullUnicode]]', fullUnicode);
hidden.set(this, '[[Unicode]]', fullUnicode);
hidden.set(this, '[[Done]]', false);
};

var IteratorPrototype = GetIntrinsic('%IteratorPrototype%', true);
if (IteratorPrototype) {
RegExpStringIterator.prototype = ES.ObjectCreate(IteratorPrototype);
}

define(RegExpStringIterator.prototype, {
/* eslint complexity: 1, max-statements: 1 */
next: function next() {
var O = this;
if (ES.Type(O) !== 'Object') {
throw new TypeError('receiver must be an object');
}
if (!(this instanceof RegExpStringIterator) || !hidden.has(O, '[[IteratingRegExp]]') || !hidden.has(O, '[[IteratedString]]')) {
if (
!(O instanceof RegExpStringIterator)
|| !hidden.has(O, '[[IteratingRegExp]]')
|| !hidden.has(O, '[[IteratedString]]')
|| !hidden.has(O, '[[Global]]')
|| !hidden.has(O, '[[Unicode]]')
|| !hidden.has(O, '[[Done]]')
) {
throw new TypeError('"this" value must be a RegExpStringIterator instance');
}
if (hidden.get(this, '[[Done]]')) {
return ES.CreateIterResultObject(null, true);
if (hidden.get(O, '[[Done]]')) {
return ES.CreateIterResultObject(undefined, true);
}
var R = hidden.get(this, '[[IteratingRegExp]]');
var S = hidden.get(this, '[[IteratedString]]');
var global = hidden.get(this, '[[Global]]');
var fullUnicode = hidden.get(this, '[[FullUnicode]]');

var R = hidden.get(O, '[[IteratingRegExp]]');
var S = hidden.get(O, '[[IteratedString]]');
var global = hidden.get(O, '[[Global]]');
var fullUnicode = hidden.get(O, '[[Unicode]]');
var match = ES.RegExpExec(R, S);
if (match === null) {
hidden.set(this, '[[Done]]', true);
return ES.CreateIterResultObject(null, true);
hidden.set(O, '[[Done]]', true);
return ES.CreateIterResultObject(undefined, true);
}

if (global) {
var matchStr = ES.ToString(ES.Get(match, '0'));
if (matchStr === '') {
Expand All @@ -60,13 +67,36 @@ define(RegExpStringIterator.prototype, {
}
return ES.CreateIterResultObject(match, false);
}
hidden.set(this, '[[Done]]', true);
hidden.set(O, '[[Done]]', true);
return ES.CreateIterResultObject(match, false);
}
});
if (hasSymbols && Symbol.toStringTag) {
RegExpStringIterator.prototype[Symbol.toStringTag] = 'RegExp String Iterator';
RegExpStringIterator.prototype[Symbol.iterator] = function () { return this; };
if (hasSymbols) {
var defineP = Object.defineProperty;
if (Symbol.toStringTag) {
if (defineP) {
defineP(RegExpStringIterator.prototype, Symbol.toStringTag, {
configurable: true,
enumerable: false,
value: 'RegExp String Iterator',
writable: false
});
} else {
RegExpStringIterator.prototype[Symbol.toStringTag] = 'RegExp String Iterator';
}
}

if (!IteratorPrototype && Symbol.iterator) {
var func = {};
func[Symbol.iterator] = RegExpStringIterator.prototype[Symbol.iterator] || function SymbolIterator() {
return this;
};
var predicate = {};
predicate[Symbol.iterator] = function () {
return RegExpStringIterator.prototype[Symbol.iterator] !== func[Symbol.iterator];
};
define(RegExpStringIterator.prototype, func, predicate);
}
}

module.exports = RegExpStringIterator;
25 changes: 10 additions & 15 deletions implementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@
var ES = require('es-abstract');
var hasSymbols = require('has-symbols')();

var OrigRegExp = RegExp;

var MatchAllIterator = require('./helpers/MatchAllIterator');

module.exports = function matchAll(regexp) {
var O = ES.RequireObjectCoercible(this);
var R;
if (ES.IsRegExp(regexp)) {
R = regexp;
} else {
R = new OrigRegExp(regexp, 'g');
}
var matcher;
if (hasSymbols && typeof Symbol.matchAll === 'symbol') {
matcher = ES.GetMethod(R, Symbol.matchAll);
}
if (typeof matcher !== 'undefined') {
return ES.Call(matcher, R, [O]);

if (typeof regexp !== 'undefined' && regexp !== null) {
var matcher;
if (hasSymbols && typeof Symbol.matchAll === 'symbol') {
matcher = ES.GetMethod(regexp, Symbol.matchAll);
}
if (typeof matcher !== 'undefined') {
return ES.Call(matcher, regexp, [O]);
}
}

return MatchAllIterator(R, O);
return MatchAllIterator(regexp, O);
};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"tests-only": "npm run test:module && npm run test:shim",
"test:module": "node test",
"test:shim": "node test/shimmed",
"prelint": "evalmd *.md",
"prelint": "evalmd *.md",
"lint": "eslint ."
},
"repository": {
Expand All @@ -36,7 +36,7 @@
"homepage": "https://github.com/ljharb/String.prototype.matchAll#readme",
"dependencies": {
"define-properties": "^1.1.2",
"es-abstract": "^1.10.0",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has-symbols": "^1.0.0",
"regexp.prototype.flags": "^1.2.0"
Expand Down
18 changes: 12 additions & 6 deletions regexp-matchall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,22 @@
var ES = require('es-abstract');
var MatchAllIterator = require('./helpers/MatchAllIterator');

var regexMatchAll = function symbolMatchAll(string) {
var R = this; // eslint-disable-line no-invalid-this
if (!ES.IsRegExp(R)) {
throw new TypeError('"this" value must be a RegExp');
var regexMatchAll = function SymbolMatchAll(string) {
var R = this;
if (ES.Type(R) !== 'Object') {
throw new TypeError('"this" value must be an Object');
}
return MatchAllIterator(R, string);
};

if (Object.defineProperty && Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(regexMatchAll, 'name').configurable) {
Object.defineProperty(regexMatchAll, 'name', { value: '[Symbol.matchAll]' });
var defineP = Object.defineProperty;
var gOPD = Object.getOwnPropertyDescriptor;

if (defineP && gOPD) {
var desc = gOPD(regexMatchAll, 'name');
if (desc && desc.configurable) {
defineP(regexMatchAll, 'name', { value: '[Symbol.matchAll]' });
}
}

module.exports = regexMatchAll;
15 changes: 15 additions & 0 deletions shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ var hasSymbols = require('has-symbols')();
var getPolyfill = require('./polyfill');
var regexMatchAll = require('./regexp-matchall');

var defineP = Object.defineProperty;
var gOPD = Object.getOwnPropertyDescriptor;

module.exports = function shimMatchAll() {
var polyfill = getPolyfill();
define(
Expand All @@ -21,6 +24,18 @@ module.exports = function shimMatchAll() {
{ matchAll: function () { return Symbol.matchAll !== symbol; } }
);

if (defineP && gOPD) {
var desc = gOPD(Symbol, symbol);
if (!desc || desc.configurable) {
defineP(Symbol, symbol, {
configurable: false,
enumerable: false,
value: symbol,
writable: false
});
}
}

var func = {};
func[symbol] = RegExp.prototype[symbol] || regexMatchAll;
var predicate = {};
Expand Down
2 changes: 1 addition & 1 deletion test/shimmed.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ test('shimmed', function (t) {
if (functionNamesConfigurable) {
s2t.equal(RegExp.prototype[Symbol.matchAll].name, '[Symbol.matchAll]', 'RegExp.prototype[Symbol.matchAll] has name "[Symbol.matchAll]"');
} else {
s2t.equal(RegExp.prototype[Symbol.matchAll].name, 'symbolMatchAll', 'RegExp.prototype[Symbol.matchAll] has best guess name "symbolMatchAll"');
s2t.equal(RegExp.prototype[Symbol.matchAll].name, 'SymbolMatchAll', 'RegExp.prototype[Symbol.matchAll] has best guess name "SymbolMatchAll"');
}
s2t.end();
});
Expand Down
Loading

0 comments on commit 92a6118

Please sign in to comment.