ecma402 icon indicating copy to clipboard operation
ecma402 copied to clipboard

ToDateTimeOptions should avoid making an observable internal-use object

Open gibson042 opened this issue 3 years ago • 1 comments
trafficstars

(created from https://github.com/tc39/ecma402/pull/493#discussion_r465537106 )

ToDateTimeOptions creates an object inheriting from provided options and performs some observable writes upon it, which is unnecessarily weird.

/* Make options a proxy that spies on lots of operations until disabled. */
let spy = {};
const wrapperLabels = new Map();
const options = new Proxy({ignored: true}, {
  ...Object.fromEntries(
    ["getOwnPropertyDescriptor", "defineProperty", "has", "get", "ownKeys"].map(trapName => {
      spy[trapName] = new Map();
      return [trapName, (...args) => {
        const result = Reflect[trapName](...args);
        if (spy) {
          const m = spy[trapName];
          const propKey = args[1];
          const n = (m.get(propKey) || 0) + 1;
          m.set(propKey, n);
          let details = args.slice(1);
          /* Use the get trap to discover system-created objects. */
          if (trapName === "get") {
            const receiver = args[2];
            let receiverLabel;
            if (receiver === options) {
              receiverLabel = "options";
            } else if (Object.getPrototypeOf(receiver) === options) {
              if (!wrapperLabels.has(receiver)) {
                wrapperLabels.set(receiver, "wrapper" + wrapperLabels.size);
              }
              receiverLabel = wrapperLabels.get(receiver);
            } else {
              receiverLabel = "unknown receiver " + String(receiver);
            }
            details = ["@" + receiverLabel, propKey];
          }
          print(n, trapName, ...details, "=>", result);
        }
        return result;
      }];
    })
  )
});
new Intl.DateTimeFormat(undefined, options);
spy = null;
for (const [obj, label] of new Map([[options, "options"], ...wrapperLabels.entries()])) {
  print(`${label} = {\n${
    Reflect.ownKeys(obj).map(k => `\t${String(k)}: ${String(obj[k])},\n`).join("")
  }}`);
}
$ eshost -s demo.js
#### GraalJS, JavaScriptCore, SpiderMonkey, V8
1 get @wrapper0 weekday => undefined
1 get @wrapper0 year => undefined
1 get @wrapper0 month => undefined
1 get @wrapper0 day => undefined
1 get @wrapper0 dayPeriod => undefined
1 get @wrapper0 hour => undefined
1 get @wrapper0 minute => undefined
1 get @wrapper0 second => undefined
1 get @wrapper0 fractionalSecondDigits => undefined
1 get @wrapper0 dateStyle => undefined
1 get @wrapper0 timeStyle => undefined
1 get @wrapper0 localeMatcher => undefined
1 get @wrapper0 calendar => undefined
1 get @wrapper0 numberingSystem => undefined
1 get @wrapper0 hour12 => undefined
1 get @wrapper0 hourCycle => undefined
1 get @wrapper0 timeZone => undefined
2 get @wrapper0 weekday => undefined
1 get @wrapper0 era => undefined
2 get @wrapper0 dayPeriod => undefined
2 get @wrapper0 hour => undefined
2 get @wrapper0 minute => undefined
2 get @wrapper0 second => undefined
2 get @wrapper0 fractionalSecondDigits => undefined
1 get @wrapper0 timeZoneName => undefined
1 get @wrapper0 formatMatcher => undefined
2 get @wrapper0 dateStyle => undefined
2 get @wrapper0 timeStyle => undefined
options = {
	ignored: true,
}
wrapper0 = {
	year: numeric,
	month: numeric,
	day: numeric,
}

gibson042 avatar Aug 24 '22 20:08 gibson042

https://github.com/tc39/ecma402/issues/237#issuecomment-389232483 has a link to patch which avoids creating an intermediate object.

anba avatar Aug 24 '22 21:08 anba

Fixed in #709.

anba avatar Aug 04 '23 08:08 anba