Skip to content

Commit

Permalink
[Fix] 2015+: add code to handle IE 8’s problems:
Browse files Browse the repository at this point in the history
 - broken `Object.defineProperty`/`Object.getOwnPropertyDescriptor`
 - missing `Array.prototype.indexOf`
 - `Array.prototype.slice` does not work on strings due to bracket access not working on strings

In:
 - `CreateDataProperty`
 - `CreateListFromArrayLike`
 - `GetSubstitution`
 - `IsExtensible`
 - `IterableToArrayLike`
 - `OrdinaryDefineOwnProperty`
 - `OrdinaryGetOwnProperty`
 - `SetIntegrityLevel`
 - `TestIntegrityLevel`
 - helper: `DefineOwnProperty`

[New] add `getOwnPropertyDescriptor` helper
  • Loading branch information
ljharb committed Jan 20, 2020
1 parent 6f4c07d commit cd75047
Show file tree
Hide file tree
Showing 43 changed files with 269 additions and 101 deletions.
4 changes: 2 additions & 2 deletions 2015/CreateDataProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $TypeError = GetIntrinsic('%TypeError%');

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

var FromPropertyDescriptor = require('./FromPropertyDescriptor');
var OrdinaryGetOwnProperty = require('./OrdinaryGetOwnProperty');
var IsDataDescriptor = require('./IsDataDescriptor');
var IsExtensible = require('./IsExtensible');
var IsPropertyKey = require('./IsPropertyKey');
Expand All @@ -23,7 +23,7 @@ module.exports = function CreateDataProperty(O, P, V) {
if (!IsPropertyKey(P)) {
throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true');
}
var oldDesc = $gOPD(O, P);
var oldDesc = OrdinaryGetOwnProperty(O, P);
var extensible = oldDesc || IsExtensible(O);
var immutable = oldDesc && (!oldDesc.writable || !oldDesc.configurable);
if (immutable || !extensible) {
Expand Down
2 changes: 1 addition & 1 deletion 2015/CreateListFromArrayLike.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var GetIntrinsic = require('../GetIntrinsic');
var callBound = require('../helpers/callBound');

var $TypeError = GetIntrinsic('%TypeError%');
var $indexOf = callBound('Array.prototype.indexOf');
var $indexOf = callBound('Array.prototype.indexOf', true) || callBound('String.prototype.indexOf');
var $push = callBound('Array.prototype.push');

var Get = require('./Get');
Expand Down
24 changes: 15 additions & 9 deletions 2015/GetSubstitution.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

'use strict';

var GetIntrinsic = require('../GetIntrinsic');
Expand All @@ -13,12 +14,19 @@ var every = require('../helpers/every');

var isDigit = regexTester(/^[0-9]$/);

var strSlice = callBound('String.prototype.slice');
var $charAt = callBound('String.prototype.charAt');
var $strSlice = callBound('String.prototype.slice');

var IsArray = require('./IsArray');
var IsInteger = require('./IsInteger');
var Type = require('./Type');

var canDistinguishSparseFromUndefined = 0 in [undefined]; // IE 6 - 8 have a bug where this returns false

var isStringOrHole = function (capture, index, arr) {
return Type(capture) === 'String' || (canDistinguishSparseFromUndefined ? !(index in arr) : Type(capture) === 'Undefined');
};

// https://www.ecma-international.org/ecma-262/6.0/#sec-getsubstitution

// eslint-disable-next-line max-statements, max-params, max-lines-per-function
Expand All @@ -37,8 +45,6 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
throw new $TypeError('Assertion failed: `position` must be a nonnegative integer, and less than or equal to the length of `string`, got ' + inspect(position));
}

var ES = this;
var isStringOrHole = function (capture, index, arr) { return ES.Type(capture) === 'String' || !(index in arr); };
if (!IsArray(captures) || !every(captures, isStringOrHole)) {
throw new $TypeError('Assertion failed: `captures` must be a List of Strings, got ' + inspect(captures));
}
Expand All @@ -53,25 +59,25 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
var result = '';
for (var i = 0; i < replacement.length; i += 1) {
// if this is a $, and it's not the end of the replacement
var current = replacement[i];
var current = $charAt(replacement, i);
var isLast = (i + 1) >= replacement.length;
var nextIsLast = (i + 2) >= replacement.length;
if (current === '$' && !isLast) {
var next = replacement[i + 1];
var next = $charAt(replacement, i + 1);
if (next === '$') {
result += '$';
i += 1;
} else if (next === '&') {
result += matched;
i += 1;
} else if (next === '`') {
result += position === 0 ? '' : strSlice(str, 0, position - 1);
result += position === 0 ? '' : $strSlice(str, 0, position - 1);
i += 1;
} else if (next === "'") {
result += tailPos >= stringLength ? '' : strSlice(str, tailPos);
result += tailPos >= stringLength ? '' : $strSlice(str, tailPos);
i += 1;
} else {
var nextNext = nextIsLast ? null : replacement[i + 2];
var nextNext = nextIsLast ? null : $charAt(replacement, i + 2);
if (isDigit(next) && next !== '0' && (nextIsLast || !isDigit(nextNext))) {
// $1 through $9, and not followed by a digit
var n = $parseInt(next, 10);
Expand All @@ -91,7 +97,7 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
}
} else {
// the final $, or else not a $
result += replacement[i];
result += $charAt(replacement, i);
}
}
return result;
Expand Down
4 changes: 2 additions & 2 deletions 2015/IsExtensible.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ module.exports = $preventExtensions
? function IsExtensible(obj) {
return !isPrimitive(obj) && $isExtensible(obj);
}
: function IsExtensible(obj) { // eslint-disable-line no-unused-vars
return true;
: function IsExtensible(obj) {
return !isPrimitive(obj);
};
25 changes: 24 additions & 1 deletion 2015/OrdinaryDefineOwnProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $SyntaxError = GetIntrinsic('%SyntaxError%');
var $TypeError = GetIntrinsic('%TypeError%');

var isPropertyDescriptor = require('../helpers/isPropertyDescriptor');
Expand All @@ -12,6 +13,7 @@ var IsDataDescriptor = require('./IsDataDescriptor');
var IsExtensible = require('./IsExtensible');
var IsPropertyKey = require('./IsPropertyKey');
var ToPropertyDescriptor = require('./ToPropertyDescriptor');
var SameValue = require('./SameValue');
var Type = require('./Type');
var ValidateAndApplyPropertyDescriptor = require('./ValidateAndApplyPropertyDescriptor');

Expand All @@ -31,6 +33,27 @@ module.exports = function OrdinaryDefineOwnProperty(O, P, Desc) {
}, Desc)) {
throw new $TypeError('Assertion failed: Desc must be a Property Descriptor');
}
if (!$gOPD) {
// ES3/IE 8 fallback
if (IsAccessorDescriptor(Desc)) {
throw new $SyntaxError('This environment does not support accessor property descriptors.');
}
var creatingNormalDataProperty = !(P in O)
&& Desc['[[Writable]]']
&& Desc['[[Enumerable]]']
&& Desc['[[Configurable]]']
&& '[[Value]]' in Desc;
var settingExistingDataProperty = (P in O)
&& (!('[[Configurable]]' in Desc) || Desc['[[Configurable]]'])
&& (!('[[Enumerable]]' in Desc) || Desc['[[Enumerable]]'])
&& (!('[[Writable]]' in Desc) || Desc['[[Writable]]'])
&& '[[Value]]' in Desc;
if (creatingNormalDataProperty || settingExistingDataProperty) {
O[P] = Desc['[[Value]]']; // eslint-disable-line no-param-reassign
return SameValue(O[P], Desc['[[Value]]']);
}
throw new $SyntaxError('This environment does not support defining non-writable, non-enumerable, or non-configurable properties');
}
var desc = $gOPD(O, P);
var current = desc && ToPropertyDescriptor(desc);
var extensible = IsExtensible(O);
Expand Down
4 changes: 2 additions & 2 deletions 2015/OrdinaryGetOwnProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $TypeError = GetIntrinsic('%TypeError%');

var callBound = require('../helpers/callBound');
Expand Down Expand Up @@ -30,7 +30,7 @@ module.exports = function OrdinaryGetOwnProperty(O, P) {
return void 0;
}
if (!$gOPD) {
// ES3 fallback
// ES3 / IE 8 fallback
var arrayLength = IsArray(O) && P === 'length';
var regexLastIndex = IsRegExp(O) && P === 'lastIndex';
return {
Expand Down
2 changes: 1 addition & 1 deletion 2015/SetIntegrityLevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var GetIntrinsic = require('../GetIntrinsic');
var $SyntaxError = GetIntrinsic('%SyntaxError%');
var $TypeError = GetIntrinsic('%TypeError%');
var $preventExtensions = GetIntrinsic('%Object.preventExtensions%');
var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $gOPN = GetIntrinsic('%Object.getOwnPropertyNames%');

var forEach = require('../helpers/forEach');
Expand Down
2 changes: 1 addition & 1 deletion 2015/TestIntegrityLevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $gOPN = GetIntrinsic('%Object.getOwnPropertyNames%');
var $TypeError = GetIntrinsic('%TypeError%');

Expand Down
4 changes: 2 additions & 2 deletions 2016/CreateDataProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $TypeError = GetIntrinsic('%TypeError%');

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

var FromPropertyDescriptor = require('./FromPropertyDescriptor');
var OrdinaryGetOwnProperty = require('./OrdinaryGetOwnProperty');
var IsDataDescriptor = require('./IsDataDescriptor');
var IsExtensible = require('./IsExtensible');
var IsPropertyKey = require('./IsPropertyKey');
Expand All @@ -23,7 +23,7 @@ module.exports = function CreateDataProperty(O, P, V) {
if (!IsPropertyKey(P)) {
throw new $TypeError('Assertion failed: IsPropertyKey(P) is not true');
}
var oldDesc = $gOPD(O, P);
var oldDesc = OrdinaryGetOwnProperty(O, P);
var extensible = oldDesc || IsExtensible(O);
var immutable = oldDesc && (!oldDesc.writable || !oldDesc.configurable);
if (immutable || !extensible) {
Expand Down
2 changes: 1 addition & 1 deletion 2016/CreateListFromArrayLike.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var GetIntrinsic = require('../GetIntrinsic');
var callBound = require('../helpers/callBound');

var $TypeError = GetIntrinsic('%TypeError%');
var $indexOf = callBound('Array.prototype.indexOf');
var $indexOf = callBound('Array.prototype.indexOf', true) || callBound('String.prototype.indexOf');
var $push = callBound('Array.prototype.push');

var Get = require('./Get');
Expand Down
24 changes: 15 additions & 9 deletions 2016/GetSubstitution.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

'use strict';

var GetIntrinsic = require('../GetIntrinsic');
Expand All @@ -13,12 +14,19 @@ var every = require('../helpers/every');

var isDigit = regexTester(/^[0-9]$/);

var strSlice = callBound('String.prototype.slice');
var $charAt = callBound('String.prototype.charAt');
var $strSlice = callBound('String.prototype.slice');

var IsArray = require('./IsArray');
var IsInteger = require('./IsInteger');
var Type = require('./Type');

var canDistinguishSparseFromUndefined = 0 in [undefined]; // IE 6 - 8 have a bug where this returns false

var isStringOrHole = function (capture, index, arr) {
return Type(capture) === 'String' || (canDistinguishSparseFromUndefined ? !(index in arr) : Type(capture) === 'Undefined');
};

// https://www.ecma-international.org/ecma-262/6.0/#sec-getsubstitution

// eslint-disable-next-line max-statements, max-params, max-lines-per-function
Expand All @@ -37,8 +45,6 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
throw new $TypeError('Assertion failed: `position` must be a nonnegative integer, and less than or equal to the length of `string`, got ' + inspect(position));
}

var ES = this;
var isStringOrHole = function (capture, index, arr) { return ES.Type(capture) === 'String' || !(index in arr); };
if (!IsArray(captures) || !every(captures, isStringOrHole)) {
throw new $TypeError('Assertion failed: `captures` must be a List of Strings, got ' + inspect(captures));
}
Expand All @@ -53,25 +59,25 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
var result = '';
for (var i = 0; i < replacement.length; i += 1) {
// if this is a $, and it's not the end of the replacement
var current = replacement[i];
var current = $charAt(replacement, i);
var isLast = (i + 1) >= replacement.length;
var nextIsLast = (i + 2) >= replacement.length;
if (current === '$' && !isLast) {
var next = replacement[i + 1];
var next = $charAt(replacement, i + 1);
if (next === '$') {
result += '$';
i += 1;
} else if (next === '&') {
result += matched;
i += 1;
} else if (next === '`') {
result += position === 0 ? '' : strSlice(str, 0, position - 1);
result += position === 0 ? '' : $strSlice(str, 0, position - 1);
i += 1;
} else if (next === "'") {
result += tailPos >= stringLength ? '' : strSlice(str, tailPos);
result += tailPos >= stringLength ? '' : $strSlice(str, tailPos);
i += 1;
} else {
var nextNext = nextIsLast ? null : replacement[i + 2];
var nextNext = nextIsLast ? null : $charAt(replacement, i + 2);
if (isDigit(next) && next !== '0' && (nextIsLast || !isDigit(nextNext))) {
// $1 through $9, and not followed by a digit
var n = $parseInt(next, 10);
Expand All @@ -91,7 +97,7 @@ module.exports = function GetSubstitution(matched, str, position, captures, repl
}
} else {
// the final $, or else not a $
result += replacement[i];
result += $charAt(replacement, i);
}
}
return result;
Expand Down
4 changes: 2 additions & 2 deletions 2016/IsExtensible.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ module.exports = $preventExtensions
? function IsExtensible(obj) {
return !isPrimitive(obj) && $isExtensible(obj);
}
: function IsExtensible(obj) { // eslint-disable-line no-unused-vars
return true;
: function IsExtensible(obj) {
return !isPrimitive(obj);
};
7 changes: 4 additions & 3 deletions 2016/IterableToArrayLike.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ var $iterator = GetIntrinsic('%Symbol.iterator%', true);

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

var $arrayPush = callBound('Array.prototype.push');
var $arraySlice = callBound('Array.prototype.slice');
var $arrayJoin = callBound('Array.prototype.join');
var $arrayPush = callBound('Array.prototype.push');
var $stringSlice = callBound('String.prototype.slice');
var $stringSplit = callBound('String.prototype.split');

var AdvanceStringIndex = require('./AdvanceStringIndex');
var GetIterator = require('./GetIterator');
Expand Down Expand Up @@ -45,7 +46,7 @@ module.exports = function IterableToArrayLike(items) {
return {
next: function () {
var nextIndex = AdvanceStringIndex(items, i, true);
var value = $arrayJoin($arraySlice(items, i, nextIndex), '');
var value = $arrayJoin($stringSplit($stringSlice(items, i, nextIndex), ''), '');
i = nextIndex;
return {
done: nextIndex > items.length,
Expand Down
25 changes: 24 additions & 1 deletion 2016/OrdinaryDefineOwnProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $SyntaxError = GetIntrinsic('%SyntaxError%');
var $TypeError = GetIntrinsic('%TypeError%');

var isPropertyDescriptor = require('../helpers/isPropertyDescriptor');
Expand All @@ -12,6 +13,7 @@ var IsDataDescriptor = require('./IsDataDescriptor');
var IsExtensible = require('./IsExtensible');
var IsPropertyKey = require('./IsPropertyKey');
var ToPropertyDescriptor = require('./ToPropertyDescriptor');
var SameValue = require('./SameValue');
var Type = require('./Type');
var ValidateAndApplyPropertyDescriptor = require('./ValidateAndApplyPropertyDescriptor');

Expand All @@ -31,6 +33,27 @@ module.exports = function OrdinaryDefineOwnProperty(O, P, Desc) {
}, Desc)) {
throw new $TypeError('Assertion failed: Desc must be a Property Descriptor');
}
if (!$gOPD) {
// ES3/IE 8 fallback
if (!IsDataDescriptor(Desc)) {
throw new $SyntaxError('This environment does not support accessor property descriptors.');
}
var creatingNormalDataProperty = !(P in O)
&& Desc['[[Writable]]']
&& Desc['[[Enumerable]]']
&& Desc['[[Configurable]]']
&& '[[Value]]' in Desc;
var settingExistingDataProperty = (P in O)
&& (!('[[Configurable]]' in Desc) || Desc['[[Configurable]]'])
&& (!('[[Enumerable]]' in Desc) || Desc['[[Enumerable]]'])
&& (!('[[Writable]]' in Desc) || Desc['[[Writable]]'])
&& '[[Value]]' in Desc;
if (creatingNormalDataProperty || settingExistingDataProperty) {
O[P] = Desc['[[Value]]']; // eslint-disable-line no-param-reassign
return SameValue(O[P], Desc['[[Value]]']);
}
throw new $SyntaxError('This environment does not support defining non-writable, non-enumerable, or non-configurable properties');
}
var desc = $gOPD(O, P);
var current = desc && ToPropertyDescriptor(desc);
var extensible = IsExtensible(O);
Expand Down
4 changes: 2 additions & 2 deletions 2016/OrdinaryGetOwnProperty.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var GetIntrinsic = require('../GetIntrinsic');

var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
var $gOPD = require('../helpers/getOwnPropertyDescriptor');
var $TypeError = GetIntrinsic('%TypeError%');

var callBound = require('../helpers/callBound');
Expand Down Expand Up @@ -30,7 +30,7 @@ module.exports = function OrdinaryGetOwnProperty(O, P) {
return void 0;
}
if (!$gOPD) {
// ES3 fallback
// ES3 / IE 8 fallback
var arrayLength = IsArray(O) && P === 'length';
var regexLastIndex = IsRegExp(O) && P === 'lastIndex';
return {
Expand Down

0 comments on commit cd75047

Please sign in to comment.