Skip to content

Commit

Permalink
Add optional enumerable argument to objDefineGet and objDefineAccesso…
Browse files Browse the repository at this point in the history
…rs (#115)
  • Loading branch information
nev21 authored Nov 20, 2022
1 parent a2881f3 commit 02ccba8
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 27 deletions.
1 change: 0 additions & 1 deletion lib/src/helpers/lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import { _GlobalTestHooks, _getGlobalConfig } from "../internal/global";
import { _safeGet } from "../internal/safe_check";
import { objDefineGet, objDefineProp } from "../object/define";

/**
Expand Down
51 changes: 27 additions & 24 deletions lib/src/object/define.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Licensed under the MIT license.
*/

import { ObjClass, VALUE } from "../internal/constants";
import { EMPTY, ObjClass, VALUE } from "../internal/constants";
import { isFunction, isUndefined, objToString } from "../helpers/base";
import { throwUnsupported } from "../helpers/customError";
import { dumpObj } from "../helpers/diagnostics";
Expand All @@ -21,12 +21,19 @@ const _objDefineProperty = ObjClass["defineProperty"];
* @param descriptor - The descriptor for the property being defined or modified.
*/
export function objDefineProp<T>(target: T, key: PropertyKey, descriptor: PropertyDescriptor & ThisType<any>): T {
let error: string = EMPTY;
if (_objDefineProperty) {
_objDefineProperty(target, key, descriptor);
return target;
try {
_objDefineProperty(target, key, descriptor);
return target;
} catch (e) {
// IE8 Defines a defineProperty on Object but it's only supported for DOM elements so it will throw
// We will just ignore this here.
error = " - " + dumpObj(e);
}
}

throwUnsupported("Unable to define property [" + objToString(key) + "] on " + dumpObj(target));
throwUnsupported("Unable to define property [" + objToString(key) + "] on " + dumpObj(target) + error);
}

/**
Expand All @@ -36,11 +43,12 @@ export function objDefineProp<T>(target: T, key: PropertyKey, descriptor: Proper
* @param key - The name of the property to be defined or modified
* @param value - The value or a function that returns the value
* @param configurable - Can the value be changed, defaults to true.
* @param enumerable - Should this get property be enumerable, defaults to true.
* @returns The object that was passed to the function
*/
export function objDefineGet<T, V = any>(target: T, key: PropertyKey, value: (() => V) | V, configurable?: boolean): T {
export function objDefineGet<T, V = any>(target: T, key: PropertyKey, value: (() => V) | V, configurable?: boolean, enumerable?: boolean): T {
const desc: PropertyDescriptor = {
enumerable: true,
enumerable: isUndefined(enumerable) ? true : enumerable,
configurable: isUndefined(configurable) ? true : configurable
}

Expand All @@ -63,28 +71,23 @@ export function objDefineGet<T, V = any>(target: T, key: PropertyKey, value: (()
* @param getProp - The getter function to wire against the getter.
* @param setProp - The setter function to wire against the setter.
* @param configurable - Can the value be changed, defaults to true
* @param enumerable - Should this get property be enumerable, defaults to true.
* @returns The object that was passed to the function
*/
export function objDefineAccessors<T, V = any>(target: T, prop: PropertyKey, getProp?: (() => V) | null, setProp?: ((v: V) => void) | null, configurable?: boolean): T {
export function objDefineAccessors<T, V = any>(target: T, prop: PropertyKey, getProp?: (() => V) | null, setProp?: ((v: V) => void) | null, configurable?: boolean, enumerable?: boolean): T {
if (_objDefineProperty) {
try {
const descriptor: PropertyDescriptor = {
enumerable: true,
configurable: isUndefined(configurable) ? true : configurable
}

if (getProp) {
descriptor.get = getProp;
}
if (setProp) {
descriptor.set = setProp;
}
const descriptor: PropertyDescriptor = {
enumerable: isUndefined(enumerable) ? true : enumerable,
configurable: isUndefined(configurable) ? true : configurable
}

return objDefineProp(target, prop, descriptor);
} catch (e) {
// IE8 Defines a defineProperty on Object but it's only supported for DOM elements so it will throw
// We will just ignore this here.
throwUnsupported("Unable to define accessors for [" + objToString(prop) + "] on " + dumpObj(target));
if (getProp) {
descriptor.get = getProp;
}
if (setProp) {
descriptor.set = setProp;
}

return objDefineProp(target, prop, descriptor);
}
}
4 changes: 3 additions & 1 deletion lib/src/polyfills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { polyStrTrim, polyStrTrimEnd, polyStrTrimStart } from "./polyfills/trim"
import { polyStrPadEnd, polyStrPadStart } from "./string/pad";
import { makePolyFn } from "./internal/poly_helpers";
import { polyObjHasOwn } from "./object/has_own";
import { polyStrSubstr } from "./string/substring";

(function () {

Expand All @@ -33,7 +34,8 @@ import { polyObjHasOwn } from "./object/has_own";
"trimStart": polyStrTrimStart,
"trimLeft": polyStrTrimStart,
"trimEnd": polyStrTrimEnd,
"trimRight": polyStrTrimEnd
"trimRight": polyStrTrimEnd,
"substr": polyStrSubstr
};

// Add Object polyfills
Expand Down
186 changes: 186 additions & 0 deletions lib/test/src/common/object/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { objCreate, polyObjCreate } from "../../../../src/object/create";
import { objHasOwn, polyObjHasOwn } from "../../../../src/object/has_own";
import { isPlainObject } from "../../../../src/object/is_plain_object";
import { getWindow } from "../../../../src/helpers/environment";
import { objGetOwnPropertyDescriptor } from "../../../../src/object/get_own_prop_desc";

describe("object helpers", () => {
it("objKeys", () => {
Expand Down Expand Up @@ -209,10 +210,22 @@ describe("object helpers", () => {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, false, "It should not be writable");

// Redefine
objDefineGet(value, "test", 64);

assert.equal(value.test, 64, "Expected 64");

let desc2 = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc2, "The descriptor must be defined");
assert.equal(desc2?.configurable, true, "It should be configurable");
assert.equal(desc2?.enumerable, true, "It should be enumerable");
assert.equal(desc2?.writable, false, "It should not be writable");
});

it("objDefineGet value - not configurable", () => {
Expand All @@ -227,13 +240,88 @@ describe("object helpers", () => {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, false, "It should not be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, false, "It should not be writable");

// Redefine
try {
objDefineGet(value, "test", 64);
assert.ok(false, "Expected an exception when attempting to reset a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc2 = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc2, "The descriptor must be defined");
assert.equal(desc2?.configurable, false, "It should still not be configurable");
assert.equal(desc2?.enumerable, true, "It should still be enumerable");
assert.equal(desc2?.writable, false, "It should not be writable");
});

it("objDefineGet value - configurable and not enumerable", () => {
let value: any = {};
objDefineGet(value, "test", 42, undefined, false);

assert.equal(value.test, 42, "Expected 42");
try {
value.test = 53;
assert.ok(false, "Expected an exception when attempting to set a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, false, "It should not be enumerable");
assert.equal(desc?.writable, false, "It should not be writable");

// Redefine
objDefineGet(value, "test", 64);

assert.equal(value.test, 64, "Expected 64");

let desc2 = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc2, "The descriptor must be defined");
assert.equal(desc2?.configurable, true, "It should be configurable");
assert.equal(desc2?.enumerable, true, "It should now be enumerable");
assert.equal(desc2?.writable, false, "It should not be writable");
});

it("objDefineGet value - not configurable or enumerable", () => {
let value: any = {};
objDefineGet(value, "test", 42, false, false);

assert.equal(value.test, 42, "Expected 42");
try {
value.test = 53;
assert.ok(false, "Expected an exception when attempting to set a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, false, "It should not be configurable");
assert.equal(desc?.enumerable, false, "It should not be enumerable");
assert.equal(desc?.writable, false, "It should not be writable");

// Redefine
try {
objDefineGet(value, "test", 64);
assert.ok(false, "Expected an exception when attempting to reset a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc2 = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc2, "The descriptor must be defined");
assert.equal(desc2?.configurable, false, "It should still not be configurable");
assert.equal(desc2?.enumerable, false, "It should still not be enumerable");
assert.equal(desc2?.writable, false, "It should not be writable");
});

it("objDefineGet function", () => {
Expand All @@ -257,6 +345,41 @@ describe("object helpers", () => {

result = 64;
assert.equal(value.test, 64, "Expected 64");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineGet function - configurable and not enumerable", () => {
let value: any = {};
let result = 42;

function getFunc() {
return result;
}

objDefineGet(value, "test", getFunc, true, false);

assert.equal(value.test, 42, "Expected 42");

try {
value.test = 53;
assert.ok(false, "Expected an exception when attempting to set a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

result = 64;
assert.equal(value.test, 64, "Expected 64");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, false, "It should not be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineGet function - not configurable", () => {
Expand Down Expand Up @@ -284,6 +407,45 @@ describe("object helpers", () => {
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, false, "It should not be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineGet function - not configurable or enumerable", () => {
let value: any = {};
let result = 42;

function getFunc() {
return result;
}

objDefineGet(value, "test", getFunc, false, false);

assert.equal(value.test, 42, "Expected 42");

try {
value.test = 53;
assert.ok(false, "Expected an exception when attempting to set a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}
// Redefine
try {
objDefineGet(value, "test", getFunc, true);
assert.ok(false, "Expected an exception when attempting to reset a getter");
} catch(e) {
assert.ok(true, "Expected exception - " + dumpObj(e));
}

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, false, "It should not be configurable");
assert.equal(desc?.enumerable, false, "It should not be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineAccessors - getter only", () => {
Expand All @@ -305,6 +467,12 @@ describe("object helpers", () => {

result = 64;
assert.equal(value.test, 64, "Expected 64");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineAccessors - getter only - not configurable", () => {
Expand Down Expand Up @@ -341,6 +509,12 @@ describe("object helpers", () => {
}

assert.equal(value.test, 64, "Expected 64");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, false, "It should not be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineAccessors - setter only", () => {
Expand All @@ -363,6 +537,12 @@ describe("object helpers", () => {
assert.equal(result, 42, "Pre-condition");
value.test = 53;
assert.equal(result, 53, "Expected the value to have been set");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

it("objDefineAccessors - getter and setter", () => {
Expand All @@ -385,6 +565,12 @@ describe("object helpers", () => {

result = 64;
assert.equal(value.test, 64, "Expected a value of ");

let desc = objGetOwnPropertyDescriptor(value, "test");
assert.ok(!!desc, "The descriptor must be defined");
assert.equal(desc?.configurable, true, "It should be configurable");
assert.equal(desc?.enumerable, true, "It should be enumerable");
assert.equal(desc?.writable, undefined, "It should be undefined with get/set functions");
});

describe("setPrototypeOf", () => {
Expand Down
2 changes: 1 addition & 1 deletion lib/test/src/common/timer/idle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as sinon from "sinon";
import { assert } from "chai";
import { hasIdleCallback, scheduleIdleCallback, setDefaultMaxExecutionTime, setDefaultIdleTimeout } from "../../../../src/timer/idle";
import { getGlobal } from "../../../../src/helpers/environment";
import { elapsedTime, getPerformance, perfNow } from "../../../../src/helpers/perf";
import { elapsedTime, perfNow } from "../../../../src/helpers/perf";
import { dumpObj } from "../../../../src/helpers/diagnostics";
import { setBypassLazyCache } from "../../../../src/helpers/lazy";

Expand Down

0 comments on commit 02ccba8

Please sign in to comment.