Skip to content

Commit 907a6eb

Browse files
committed
lib: add internal Temporal utils
Signed-off-by: Renegade334 <contact.9a5d6388@renegade334.me.uk>
1 parent 843dc5f commit 907a6eb

5 files changed

Lines changed: 144 additions & 0 deletions

File tree

lib/internal/process/pre_execution.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ function prepareExecution(options) {
138138

139139
require('internal/dns/utils').initializeDns();
140140

141+
require('internal/util/temporal').initialize();
142+
141143
if (isMainThread) {
142144
assert(internalBinding('worker').isMainThread);
143145
// Worker threads will get the manifest in the message handler.

lib/internal/util/temporal.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
'use strict';
2+
3+
// This module is initialized in the pre-execution phase, before user code is
4+
// run. Since the module namespace is not populated at snapshot time, this
5+
// module should be not be imported with a destructuring pattern from within
6+
// snapshotted modules unless imported lazily.
7+
8+
// TODO(Renegade334): Remove typecheck methods once available via V8 API.
9+
// Replace with primordials if/when temporal_rs becomes a mandatory V8
10+
// build dependency and the harmony_temporal flag is removed.
11+
12+
const {
13+
ArrayPrototypeForEach,
14+
FunctionPrototypeCall,
15+
ObjectEntries,
16+
ObjectGetOwnPropertyDescriptors,
17+
SafeMap,
18+
StringPrototypeSubstring,
19+
StringPrototypeToUpperCase,
20+
globalThis,
21+
uncurryThis,
22+
} = primordials;
23+
24+
// The keys of this Map are the Temporal constructor names.
25+
// The values are the names of the most lightweight brand-aware getter within
26+
// each prototype, which can be called speculatively as a brand check.
27+
const constructors = new SafeMap()
28+
.set('Duration', 'nanoseconds')
29+
.set('Instant', 'epochNanoseconds')
30+
.set('PlainDate', 'calendarId')
31+
.set('PlainDateTime', 'calendarId')
32+
.set('PlainMonthDay', 'calendarId')
33+
.set('PlainTime', 'nanosecond')
34+
.set('PlainYearMonth', 'calendarId')
35+
.set('ZonedDateTime', 'calendarId');
36+
37+
exports.initialize = () => {
38+
const { Temporal } = globalThis;
39+
40+
if (Temporal === undefined) {
41+
exports.hasTemporal = false;
42+
43+
for (const name of constructors.keys()) {
44+
exports[`isTemporal${name}`] = () => false;
45+
}
46+
47+
return;
48+
}
49+
50+
exports.hasTemporal = true;
51+
52+
for (const { 0: name, 1: brandedGetter } of constructors.entries()) {
53+
const constructor = Temporal[name];
54+
exports[`Temporal${name}`] = constructor;
55+
56+
ArrayPrototypeForEach(
57+
ObjectEntries(ObjectGetOwnPropertyDescriptors(constructor)),
58+
({ 0: key, 1: desc }) => {
59+
if (typeof key === 'string' && typeof desc.value === 'function') {
60+
exports[`Temporal${name}${capitalizeKey(key)}`] = desc.value;
61+
}
62+
},
63+
);
64+
65+
ArrayPrototypeForEach(
66+
ObjectEntries(ObjectGetOwnPropertyDescriptors(constructor.prototype)),
67+
({ 0: key, 1: desc }) => {
68+
if (typeof key !== 'string') return;
69+
70+
if ('get' in desc) {
71+
exports[`Temporal${name}PrototypeGet${capitalizeKey(key)}`] = uncurryThis(desc.get);
72+
if (key === brandedGetter) {
73+
exports[`isTemporal${name}`] = makeTypeCheckMethod(desc.get);
74+
}
75+
} else {
76+
exports[`Temporal${name}Prototype${capitalizeKey(key)}`] = typeof desc.value === 'function' ?
77+
uncurryThis(desc.value) :
78+
desc.value;
79+
}
80+
},
81+
);
82+
}
83+
};
84+
85+
function makeTypeCheckMethod(getter) {
86+
return (value) => {
87+
try {
88+
FunctionPrototypeCall(getter, value);
89+
return true;
90+
} catch {
91+
return false;
92+
}
93+
};
94+
}
95+
96+
function capitalizeKey(key) {
97+
return `${StringPrototypeToUpperCase(key[0])}${StringPrototypeSubstring(key, 1)}`;
98+
}

test/parallel/test-bootstrap-modules.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ expected.beforePreExec = new Set([
121121

122122
expected.atRunTime = new Set([
123123
'NativeModule internal/process/pre_execution',
124+
'NativeModule internal/util/temporal',
124125
]);
125126

126127
const { isMainThread } = require('worker_threads');
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Flags: --expose-internals --no-harmony-temporal --no-warnings
2+
3+
'use strict';
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
8+
common.allowGlobals(globalThis.Temporal = {
9+
Instant: class Instant {},
10+
});
11+
12+
const instant = new Temporal.Instant();
13+
14+
// Module contents should be independent of global state at require time
15+
const temporalUtils = require('internal/util/temporal');
16+
17+
assert.strictEqual(temporalUtils.hasTemporal, false);
18+
assert.strictEqual(temporalUtils.isTemporalInstant(instant), false);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Flags: --expose-internals --no-warnings
2+
3+
'use strict';
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
8+
if (!common.hasTemporal) {
9+
common.skip('No Temporal support');
10+
}
11+
12+
const instant = Temporal.Now.instant();
13+
14+
delete globalThis.Temporal;
15+
assert.strictEqual(typeof Temporal, 'undefined');
16+
17+
// Module contents should be independent of global state at require time
18+
const temporalUtils = require('internal/util/temporal');
19+
20+
assert.strictEqual(temporalUtils.hasTemporal, true);
21+
assert.strictEqual(instant.constructor, temporalUtils.TemporalInstant);
22+
assert.strictEqual(instant.epochNanoseconds,
23+
temporalUtils.TemporalInstantPrototypeGetEpochNanoseconds(instant));
24+
assert.strictEqual(temporalUtils.isTemporalInstant(instant), true);
25+
assert.strictEqual(temporalUtils.isTemporalZonedDateTime(instant), false);

0 commit comments

Comments
 (0)