defineLazyObjectProperty.js 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. /**
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @format
  8. * @flow
  9. */
  10. 'use strict';
  11. /**
  12. * Defines a lazily evaluated property on the supplied `object`.
  13. */
  14. function defineLazyObjectProperty<T>(
  15. object: Object,
  16. name: string,
  17. descriptor: {
  18. get: () => T,
  19. enumerable?: boolean,
  20. writable?: boolean,
  21. ...
  22. },
  23. ): void {
  24. const {get} = descriptor;
  25. const enumerable = descriptor.enumerable !== false;
  26. const writable = descriptor.writable !== false;
  27. let value;
  28. let valueSet = false;
  29. function getValue(): T {
  30. // WORKAROUND: A weird infinite loop occurs where calling `getValue` calls
  31. // `setValue` which calls `Object.defineProperty` which somehow triggers
  32. // `getValue` again. Adding `valueSet` breaks this loop.
  33. if (!valueSet) {
  34. // Calling `get()` here can trigger an infinite loop if it fails to
  35. // remove the getter on the property, which can happen when executing
  36. // JS in a V8 context. `valueSet = true` will break this loop, and
  37. // sets the value of the property to undefined, until the code in `get()`
  38. // finishes, at which point the property is set to the correct value.
  39. valueSet = true;
  40. setValue(get());
  41. }
  42. return value;
  43. }
  44. function setValue(newValue: T): void {
  45. value = newValue;
  46. valueSet = true;
  47. Object.defineProperty(object, name, {
  48. value: newValue,
  49. configurable: true,
  50. enumerable,
  51. writable,
  52. });
  53. }
  54. Object.defineProperty(object, name, {
  55. get: getValue,
  56. set: setValue,
  57. configurable: true,
  58. enumerable,
  59. });
  60. }
  61. module.exports = defineLazyObjectProperty;