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

Make big.js work when the Object prototype is frozen #202

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions big.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,16 @@

// Retain a reference to this Big constructor.
// Shadow Big.prototype.constructor which points to Object.
x.constructor = Big;
if (typeof Object.defineProperty === 'function') {
// If the Object prototype is frozen, the "constructor" property is non-writable. This means that any objects which inherit this
// property cannot have the property changed using an assignment. If using strict mode, attempting that will cause an error. If not
// using strict mode, attempting that will be silently ignored.
// However, we can still explicitly shadow the prototype's non-writable property by defining a new property on this object.
Object.defineProperty(x, 'constructor', { value: Big });
} else {
// If Object.defineProperty() doesn't exist, attempt to shadow this property using the assignment operator.
x.constructor = Big;
}
Comment on lines -114 to +123
Copy link
Author

@jportner jportner Mar 29, 2023

Choose a reason for hiding this comment

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

I noticed that this package is designed to only use ECMAScript 3, so it works on all browsers.

I looked in the ECMAScript 3 specification and did not find mention of Object.defineProperty() or Object.freeze().

I'm not sure exactly when these global functions were introduced in the ECMAScript specification, but here is are the MDN compatibility pages for both of them:

So, Object.defineProperty() was supported before Object.freeze(). This means we can assume that if Object.defineProperty() does not exist, then the global Object prototype has not been frozen either.

I think the safest thing to do here is to check if Object.defineProperty() exists and use it, otherwise fall back to shadowing these properties using the assignment operator.

I don't think this defensive code is necessary for the ES module though, because ES modules were introduced in ES2015, well after Object.defineProperty() and Object.freeze().

}

Big.prototype = P;
Expand Down Expand Up @@ -960,7 +969,7 @@
* Big.PE, or a negative exponent equal to or less than Big.NE.
* Omit the sign for negative zero.
*/
P.toJSON = P.toString = function () {
function toString() {
var x = this,
Big = x.constructor;
return stringify(x, x.e <= Big.NE || x.e >= Big.PE, !!x.c[0]);
Expand Down Expand Up @@ -1011,7 +1020,7 @@
* Big.PE, or a negative exponent equal to or less than Big.NE.
* Include the sign for negative zero.
*/
P.valueOf = function () {
function valueOf() {
var x = this,
Big = x.constructor;
if (Big.strict === true) {
Expand All @@ -1021,6 +1030,21 @@
};


P.toJSON = toString;
if (typeof Object.defineProperty === 'function') {
// If the Object prototype is frozen, the "toString" and "valueOf" properties are non-writable. This means that any objects which
// inherit these property cannot have the property changed using an assignment. If using strict mode, attempting that will cause an
// error. If not using strict mode, attempting that will be silently ignored.
// However, we can still explicitly shadow the prototype's non-writable properties by defining new properties on this object.
Object.defineProperty(P, 'toString', { value: toString });
Object.defineProperty(P, 'valueOf', { value: valueOf });
} else {
// If Object.defineProperty() doesn't exist, attempt to shadow this property using the assignment operator.
P.toString = toString;
P.valueOf = valueOf;
}


// Export


Expand Down
19 changes: 16 additions & 3 deletions big.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ function _Big_() {

// Retain a reference to this Big constructor.
// Shadow Big.prototype.constructor which points to Object.
x.constructor = Big;
// If the Object prototype is frozen, the "constructor" property is non-writable. This means that any objects which inherit this
// property cannot have the property changed using an assignment. If using strict mode, attempting that will cause an error. If not
// using strict mode, attempting that will be silently ignored.
// However, we can still explicitly shadow the prototype's non-writable property by defining a new property on this object.
Object.defineProperty(x, 'constructor', { value: Big });
}

Big.prototype = P;
Expand Down Expand Up @@ -957,7 +961,7 @@ P.toFixed = function (dp, rm) {
* Big.PE, or a negative exponent equal to or less than Big.NE.
* Omit the sign for negative zero.
*/
P[Symbol.for('nodejs.util.inspect.custom')] = P.toJSON = P.toString = function () {
var toString = P[Symbol.for('nodejs.util.inspect.custom')] = function () {
var x = this,
Big = x.constructor;
return stringify(x, x.e <= Big.NE || x.e >= Big.PE, !!x.c[0]);
Expand Down Expand Up @@ -1008,7 +1012,7 @@ P.toPrecision = function (sd, rm) {
* Big.PE, or a negative exponent equal to or less than Big.NE.
* Include the sign for negative zero.
*/
P.valueOf = function () {
function valueOf() {
var x = this,
Big = x.constructor;
if (Big.strict === true) {
Expand All @@ -1018,6 +1022,15 @@ P.valueOf = function () {
};


P.toJSON = toString;
// If the Object prototype is frozen, the "toString" and "valueOf" properties are non-writable. This means that any objects which
// inherit these property cannot have the property changed using an assignment. If using strict mode, attempting that will cause an
// error. If not using strict mode, attempting that will be silently ignored.
// However, we can still explicitly shadow the prototype's non-writable properties by defining new properties on this object.
Object.defineProperty(P, 'toString', { value: toString });
Object.defineProperty(P, 'valueOf', { value: valueOf });


// Export


Expand Down