-
-
Notifications
You must be signed in to change notification settings - Fork 35.5k
inspect: add Temporal support #63154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -218,6 +218,47 @@ const builtInObjects = new SafeSet( | |||||
| // https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot | ||||||
| const isUndetectableObject = (v) => typeof v === 'undefined' && v !== undefined; | ||||||
|
|
||||||
| const temporalConstructors = [ | ||||||
| { name: 'Duration', fullName: 'Temporal.Duration' }, | ||||||
| { name: 'Instant', fullName: 'Temporal.Instant' }, | ||||||
| { name: 'PlainDate', fullName: 'Temporal.PlainDate' }, | ||||||
| { name: 'PlainDateTime', fullName: 'Temporal.PlainDateTime' }, | ||||||
| { name: 'PlainMonthDay', fullName: 'Temporal.PlainMonthDay' }, | ||||||
| { name: 'PlainTime', fullName: 'Temporal.PlainTime' }, | ||||||
| { name: 'PlainYearMonth', fullName: 'Temporal.PlainYearMonth' }, | ||||||
| { name: 'ZonedDateTime', fullName: 'Temporal.ZonedDateTime' }, | ||||||
| ]; | ||||||
|
|
||||||
| // Best-effort, using instanceof. Polyfills will likely pass these checks. | ||||||
| // Returns void if either the type checks fail or the attempt at calling | ||||||
| // .toString() fails, so that format() can fall back to standard object | ||||||
| // formatting in either case. | ||||||
| // TODO: Hopefully the V8 API will expose typechecks at some point. | ||||||
| function getTemporalInfo(value) { | ||||||
| try { | ||||||
| const { Temporal } = globalThis; | ||||||
| if (Temporal === undefined) { | ||||||
| return; | ||||||
| } | ||||||
| for (let i = 0; i < temporalConstructors.length; i++) { | ||||||
| const entry = temporalConstructors[i]; | ||||||
| const constructor = Temporal[entry.name]; | ||||||
| if (typeof constructor !== 'function') { | ||||||
| return; | ||||||
| } | ||||||
| if (!FunctionPrototypeSymbolHasInstance(constructor, value)) { | ||||||
| continue; | ||||||
| } | ||||||
| const { prototype } = constructor; | ||||||
| if (!ObjectPrototypeHasOwnProperty(prototype, 'toString')) { | ||||||
| return; | ||||||
| } | ||||||
| const result = FunctionPrototypeCall(prototype.toString, value); | ||||||
| return { ...entry, result }; | ||||||
| } | ||||||
| } catch { /* void */ } | ||||||
| } | ||||||
|
|
||||||
| // These options must stay in sync with `getUserOptions`. So if any option will | ||||||
| // be added or removed, `getUserOptions` must also be updated accordingly. | ||||||
| const inspectDefaultOptions = ObjectSeal({ | ||||||
|
|
@@ -1321,6 +1362,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { | |||||
| if (noIterator) { | ||||||
| keys = getKeys(value, ctx.showHidden); | ||||||
| braces = ['{', '}']; | ||||||
| let temporalInfo; | ||||||
| if (typeof value === 'function') { | ||||||
| base = getFunctionBase(ctx, value, constructor, tag); | ||||||
| if (keys.length === 0 && protoProps === undefined) | ||||||
|
|
@@ -1404,6 +1446,14 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { | |||||
| if (keys.length === 0 && protoProps === undefined) { | ||||||
| return base; | ||||||
| } | ||||||
| } else if ((temporalInfo = getTemporalInfo(value))) { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we want to avoid calling
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be better called something like |
||||||
| const prefix = constructor === temporalInfo.name && tag === temporalInfo.fullName ? | ||||||
| (temporalInfo.fullName + ' ') : | ||||||
| getPrefix(constructor, tag, temporalInfo.fullName); | ||||||
| base = `${prefix}${temporalInfo.result}`; | ||||||
| if (keys.length === 0 && protoProps === undefined) { | ||||||
| return ctx.stylize(base, 'date'); | ||||||
| } | ||||||
| } else { | ||||||
| if (keys.length === 0 && protoProps === undefined) { | ||||||
| if (isExternal(value)) { | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporal itself exposes type checks via internal slots and builtin methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but we can't guarantee a non-mutable path to get our hands on those methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
of course we can, via primordials
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not while Temporal remains an optional feature, unfortunately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not? node 26 ships it unconditionally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's enabled by default, but remains behind both runtime (cf.
--no-harmony-temporal) and build flags, so this is unlikely to change any time soon.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh gotcha. surely we can still capture the builtins at startup, though, even if they're not real primordials yet?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's too early. Attempting to capture the global with something like
const { Temporal } = globalThisin the header of inspect.js always hits thin air (I guess it's snapshotted?). We have to do it lazily frominspect(), at which point anything that intends to modify the Temporal global (polyfills etc.) will almost certainly already have run.We could go full ham to hack around this, but it's not really a huge issue in the context of a function which only uses those methods to decide what flavour of human-readable output gets generated.