You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
3.4 KiB

5 years ago
  1. "use strict";
  2. var originalObject = Object;
  3. var originalDefProp = Object.defineProperty;
  4. var originalCreate = Object.create;
  5. function defProp(obj, name, value) {
  6. if (originalDefProp) try {
  7. originalDefProp.call(originalObject, obj, name, { value: value });
  8. } catch (definePropertyIsBrokenInIE8) {
  9. obj[name] = value;
  10. } else {
  11. obj[name] = value;
  12. }
  13. }
  14. // For functions that will be invoked using .call or .apply, we need to
  15. // define those methods on the function objects themselves, rather than
  16. // inheriting them from Function.prototype, so that a malicious or clumsy
  17. // third party cannot interfere with the functionality of this module by
  18. // redefining Function.prototype.call or .apply.
  19. function makeSafeToCall(fun) {
  20. if (fun) {
  21. defProp(fun, "call", fun.call);
  22. defProp(fun, "apply", fun.apply);
  23. }
  24. return fun;
  25. }
  26. makeSafeToCall(originalDefProp);
  27. makeSafeToCall(originalCreate);
  28. var hasOwn = makeSafeToCall(Object.prototype.hasOwnProperty);
  29. var numToStr = makeSafeToCall(Number.prototype.toString);
  30. var strSlice = makeSafeToCall(String.prototype.slice);
  31. var cloner = function(){};
  32. function create(prototype) {
  33. if (originalCreate) {
  34. return originalCreate.call(originalObject, prototype);
  35. }
  36. cloner.prototype = prototype || null;
  37. return new cloner;
  38. }
  39. var rand = Math.random;
  40. var uniqueKeys = create(null);
  41. function makeUniqueKey() {
  42. // Collisions are highly unlikely, but this module is in the business of
  43. // making guarantees rather than safe bets.
  44. do var uniqueKey = internString(strSlice.call(numToStr.call(rand(), 36), 2));
  45. while (hasOwn.call(uniqueKeys, uniqueKey));
  46. return uniqueKeys[uniqueKey] = uniqueKey;
  47. }
  48. function internString(str) {
  49. var obj = {};
  50. obj[str] = true;
  51. return Object.keys(obj)[0];
  52. }
  53. // External users might find this function useful, but it is not necessary
  54. // for the typical use of this module.
  55. exports.makeUniqueKey = makeUniqueKey;
  56. // Object.getOwnPropertyNames is the only way to enumerate non-enumerable
  57. // properties, so if we wrap it to ignore our secret keys, there should be
  58. // no way (except guessing) to access those properties.
  59. var originalGetOPNs = Object.getOwnPropertyNames;
  60. Object.getOwnPropertyNames = function getOwnPropertyNames(object) {
  61. for (var names = originalGetOPNs(object),
  62. src = 0,
  63. dst = 0,
  64. len = names.length;
  65. src < len;
  66. ++src) {
  67. if (!hasOwn.call(uniqueKeys, names[src])) {
  68. if (src > dst) {
  69. names[dst] = names[src];
  70. }
  71. ++dst;
  72. }
  73. }
  74. names.length = dst;
  75. return names;
  76. };
  77. function defaultCreatorFn(object) {
  78. return create(null);
  79. }
  80. function makeAccessor(secretCreatorFn) {
  81. var brand = makeUniqueKey();
  82. var passkey = create(null);
  83. secretCreatorFn = secretCreatorFn || defaultCreatorFn;
  84. function register(object) {
  85. var secret; // Created lazily.
  86. function vault(key, forget) {
  87. // Only code that has access to the passkey can retrieve (or forget)
  88. // the secret object.
  89. if (key === passkey) {
  90. return forget
  91. ? secret = null
  92. : secret || (secret = secretCreatorFn(object));
  93. }
  94. }
  95. defProp(object, brand, vault);
  96. }
  97. function accessor(object) {
  98. if (!hasOwn.call(object, brand))
  99. register(object);
  100. return object[brand](passkey);
  101. }
  102. accessor.forget = function(object) {
  103. if (hasOwn.call(object, brand))
  104. object[brand](passkey, true);
  105. };
  106. return accessor;
  107. }
  108. exports.makeAccessor = makeAccessor;