Skip to content

Commit

Permalink
Merge pull request #302 from EB-Forks/fix/repair/accessors/no-new-call
Browse files Browse the repository at this point in the history
fix(repair‑accessors): disallow `new` invocation
  • Loading branch information
erights authored Sep 28, 2020
2 parents 0419507 + 1e08029 commit 067bdd9
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 51 deletions.
100 changes: 57 additions & 43 deletions src/repair/accessors.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ export function repairAccessors() {
return;
}

function toObject(obj) {
if (obj === undefined || obj === null) {
throw new TypeError(`can't convert undefined or null to object`);
}
return Object(obj);
}
const { apply } = Reflect;
const uncurryThis = fn => (thisArg, ...args) => apply(fn, thisArg, args);
// %Object.prototype.valueOf% performs:
// 1. Return ? ToObject(this value)
const toObject = uncurryThis(objectPrototype.valueOf);

function asPropertyName(obj) {
if (typeof obj === 'symbol') {
Expand All @@ -61,48 +60,63 @@ export function repairAccessors() {
return obj;
}

defineProperties(objectPrototype, {
__defineGetter__: {
value: function __defineGetter__(prop, func) {
const O = toObject(this);
defineProperty(O, prop, {
get: aFunction(func, 'getter'),
enumerable: true,
configurable: true
});
}
// We use the the concise method syntax to create methods without
// a [[Construct]] internal method (such that the invocation
// "new __method__()" throws "TypeError: __method__ is not a constructor"),
// but which still accepts a 'this' binding.
const {
__defineGetter__,
__defineSetter__,
__lookupGetter__,
__lookupSetter__
} = {
// eslint-disable-next-line no-underscore-dangle
__defineGetter__(prop, func) {
const O = toObject(this);
defineProperty(O, prop, {
get: aFunction(func, 'getter'),
enumerable: true,
configurable: true
});
},
__defineSetter__: {
value: function __defineSetter__(prop, func) {
const O = toObject(this);
defineProperty(O, prop, {
set: aFunction(func, 'setter'),
enumerable: true,
configurable: true
});
}

// eslint-disable-next-line no-underscore-dangle
__defineSetter__(prop, func) {
const O = toObject(this);
defineProperty(O, prop, {
set: aFunction(func, 'setter'),
enumerable: true,
configurable: true
});
},
__lookupGetter__: {
value: function __lookupGetter__(prop) {
let O = toObject(this);
prop = asPropertyName(prop);
let desc;
while (O && !(desc = getOwnPropertyDescriptor(O, prop))) {
O = getPrototypeOf(O);
}
return desc && desc.get;

// eslint-disable-next-line no-underscore-dangle
__lookupGetter__(prop) {
let O = toObject(this);
prop = asPropertyName(prop);
let desc;
while (O !== null && !(desc = getOwnPropertyDescriptor(O, prop))) {
O = getPrototypeOf(O);
}
return desc && desc.get;
},
__lookupSetter__: {
value: function __lookupSetter__(prop) {
let O = toObject(this);
prop = asPropertyName(prop);
let desc;
while (O && !(desc = getOwnPropertyDescriptor(O, prop))) {
O = getPrototypeOf(O);
}
return desc && desc.set;

// eslint-disable-next-line no-underscore-dangle
__lookupSetter__(prop) {
let O = toObject(this);
prop = asPropertyName(prop);
let desc;
while (O !== null && !(desc = getOwnPropertyDescriptor(O, prop))) {
O = getPrototypeOf(O);
}
return desc && desc.set;
}
};

defineProperties(objectPrototype, {
__defineGetter__: { value: __defineGetter__ },
__defineSetter__: { value: __defineSetter__ },
__lookupGetter__: { value: __lookupGetter__ },
__lookupSetter__: { value: __lookupSetter__ }
});
}
40 changes: 32 additions & 8 deletions test/module/accessors.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test('repairAccessors - force', specs => {
} = Object;

specs.test('Object#__defineGetter__', t => {
t.plan(9);
t.plan(10);

t.equal(typeof __defineGetter__, 'function');
t.equal(__defineGetter__.length, 2);
Expand Down Expand Up @@ -64,10 +64,16 @@ test('repairAccessors - force', specs => {
TypeError,
'Throws on undefined as `this`'
);

t.throws(
() => new __defineGetter__(1, () => {}),
TypeError,
'Throws when invoked with `new`'
);
});

specs.test('Object#__defineSetter__', t => {
t.plan(9);
t.plan(10);

t.equal(typeof __defineSetter__, 'function');
t.equal(__defineSetter__.length, 2);
Expand Down Expand Up @@ -107,10 +113,16 @@ test('repairAccessors - force', specs => {
TypeError,
'Throws on undefined as `this`'
);

t.throws(
() => new __defineSetter__(1, () => {}),
TypeError,
'Throws when invoked with `new`'
);
});

specs.test('Object#__lookupGetter__', t => {
t.plan(14);
t.plan(15);

t.equal(typeof __lookupGetter__, 'function');
t.equal(__lookupGetter__.length, 1);
Expand Down Expand Up @@ -146,19 +158,25 @@ test('repairAccessors - force', specs => {
);

t.throws(
() => __lookupGetter__.call(null, 1, () => {}),
() => __lookupGetter__.call(null, 1),
TypeError,
'Throws on null as `this`'
);
t.throws(
() => __lookupGetter__.call(undefined, 1, () => {}),
() => __lookupGetter__.call(undefined, 1),
TypeError,
'Throws on undefined as `this`'
);

t.throws(
() => new __lookupGetter__(1),
TypeError,
'Throws when invoked with `new`'
);
});

specs.test('Object#__lookupSetter__', t => {
t.plan(14);
t.plan(15);

t.equal(typeof __lookupSetter__, 'function');
t.equal(__lookupSetter__.length, 1);
Expand Down Expand Up @@ -194,14 +212,20 @@ test('repairAccessors - force', specs => {
);

t.throws(
() => __lookupSetter__.call(null, 1, () => {}),
() => __lookupSetter__.call(null, 1),
TypeError,
'Throws on null as `this`'
);
t.throws(
() => __lookupSetter__.call(undefined, 1, () => {}),
() => __lookupSetter__.call(undefined, 1),
TypeError,
'Throws on undefined as `this`'
);

t.throws(
() => new __lookupSetter__(1),
TypeError,
'Throws when invoked with `new`'
);
});
});

0 comments on commit 067bdd9

Please sign in to comment.