Skip to content

Commit 8151440

Browse files
committed
lib: add internal Temporal utils
Signed-off-by: Renegade334 <contact.9a5d6388@renegade334.me.uk>
1 parent 2d6cbea commit 8151440

5 files changed

Lines changed: 146 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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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 in a module
6+
// header or used within top-level module code, as the properties may not yet
7+
// be initialized. Either import this module lazily, or import the namespace
8+
// object without destructuring and access its properties at the point of use.
9+
10+
// TODO(Renegade334): Remove typecheck methods once available via V8 API.
11+
// Replace with primordials if/when temporal_rs becomes a mandatory V8
12+
// build dependency and the harmony_temporal flag is removed.
13+
14+
const {
15+
ArrayPrototypeForEach,
16+
FunctionPrototypeCall,
17+
ObjectEntries,
18+
ObjectGetOwnPropertyDescriptors,
19+
SafeMap,
20+
StringPrototypeSubstring,
21+
StringPrototypeToUpperCase,
22+
globalThis,
23+
uncurryThis,
24+
} = primordials;
25+
26+
// The keys of this Map are the Temporal constructor names.
27+
// The values are the names of the most lightweight brand-aware getter within
28+
// each prototype, which can be called speculatively as a brand check.
29+
const constructors = new SafeMap()
30+
.set('Duration', 'nanoseconds')
31+
.set('Instant', 'epochNanoseconds')
32+
.set('PlainDate', 'calendarId')
33+
.set('PlainDateTime', 'calendarId')
34+
.set('PlainMonthDay', 'calendarId')
35+
.set('PlainTime', 'nanosecond')
36+
.set('PlainYearMonth', 'calendarId')
37+
.set('ZonedDateTime', 'calendarId');
38+
39+
exports.initialize = () => {
40+
const { Temporal } = globalThis;
41+
42+
if (Temporal === undefined) {
43+
exports.hasTemporal = false;
44+
45+
for (const name of constructors.keys()) {
46+
exports[`isTemporal${name}`] = () => false;
47+
}
48+
49+
return;
50+
}
51+
52+
exports.hasTemporal = true;
53+
54+
for (const { 0: name, 1: brandedGetter } of constructors) {
55+
const constructor = Temporal[name];
56+
exports[`Temporal${name}`] = constructor;
57+
58+
ArrayPrototypeForEach(
59+
ObjectEntries(ObjectGetOwnPropertyDescriptors(constructor)),
60+
({ 0: key, 1: desc }) => {
61+
if (typeof key === 'string' && typeof desc.value === 'function') {
62+
exports[`Temporal${name}${capitalizeKey(key)}`] = desc.value;
63+
}
64+
},
65+
);
66+
67+
const descriptors = ObjectGetOwnPropertyDescriptors(constructor.prototype);
68+
ArrayPrototypeForEach(
69+
ObjectEntries(descriptors),
70+
({ 0: key, 1: desc }) => {
71+
if (typeof key !== 'string') return;
72+
73+
if ('get' in desc) {
74+
exports[`Temporal${name}PrototypeGet${capitalizeKey(key)}`] = uncurryThis(desc.get);
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+
exports[`isTemporal${name}`] = makeTypeCheckMethod(descriptors[brandedGetter].get);
84+
}
85+
};
86+
87+
function makeTypeCheckMethod(getter) {
88+
return (value) => {
89+
try {
90+
FunctionPrototypeCall(getter, value);
91+
return true;
92+
} catch {
93+
return false;
94+
}
95+
};
96+
}
97+
98+
function capitalizeKey(key) {
99+
return `${StringPrototypeToUpperCase(key[0])}${StringPrototypeSubstring(key, 1)}`;
100+
}

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)