Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/internal/process/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ function prepareExecution(options) {

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

require('internal/util/temporal').initialize();

if (isMainThread) {
assert(internalBinding('worker').isMainThread);
// Worker threads will get the manifest in the message handler.
Expand Down
116 changes: 116 additions & 0 deletions lib/internal/util/temporal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
'use strict';

// This module is initialized in the pre-execution phase, before user code is
// run. Since the module namespace is not populated at snapshot time, this
// module should be not be imported with a destructuring pattern in a module
// header or used within top-level module code, as the properties may not yet
// be initialized. Either import this module lazily, or import the namespace
// object without destructuring and access its properties at the point of use.

// This module exposes the following exports:
// * `hasTemporal`, a boolean property indicating whether the Temporal builtin
// is present during initialization.
// * Typecheck methods for Temporal objects: `isTemporalDuration()`,
// `isTemporalInstant()` etc. If `hasTemporal` is false, then these methods
// will always return `false`. Note that these are much less performant than
// the native typecheck methods in internal/util/types, and should not be
// used in performance-critical paths.
// If `hasTemporal` is true, then this module also exports the following, in
// primordial style:
// * Temporal constructors: `TemporalDuration`, `TemporalInstant` etc.
// * Static methods on temporal constructors: `TemporalDurationCompare`,
// `TemporalDurationFrom` etc.
// * Temporal prototype properties, methods and getters:
// `TemporalDurationPrototypeAbs`, `TemporalDurationPrototypeGetBlank` etc.

// TODO(Renegade334): Remove typecheck methods once available via V8 API.
// Replace with primordials if/when temporal_rs becomes a mandatory V8
// build dependency and the harmony_temporal flag is removed.

const {
ArrayPrototypeForEach,
FunctionPrototypeCall,
ObjectEntries,
ObjectGetOwnPropertyDescriptors,
SafeMap,
StringPrototypeSubstring,
StringPrototypeToUpperCase,
globalThis,
uncurryThis,
} = primordials;

// The keys of this Map are the Temporal constructor names.
// The values are the names of the most lightweight brand-aware getter within
// each prototype, which can be called speculatively as a brand check.
const constructors = new SafeMap()
.set('Duration', 'nanoseconds')
.set('Instant', 'epochNanoseconds')
.set('PlainDate', 'calendarId')
.set('PlainDateTime', 'calendarId')
.set('PlainMonthDay', 'calendarId')
.set('PlainTime', 'nanosecond')
.set('PlainYearMonth', 'calendarId')
.set('ZonedDateTime', 'calendarId');

exports.initialize = () => {
const { Temporal } = globalThis;

if (Temporal === undefined) {
exports.hasTemporal = false;

for (const name of constructors.keys()) {
exports[`isTemporal${name}`] = () => false;
}

return;
}

exports.hasTemporal = true;

for (const { 0: name, 1: brandedGetter } of constructors) {
const constructor = Temporal[name];
exports[`Temporal${name}`] = constructor;

ArrayPrototypeForEach(
ObjectEntries(ObjectGetOwnPropertyDescriptors(constructor)),
({ 0: key, 1: desc }) => {
if (typeof key === 'string' && typeof desc.value === 'function') {
exports[`Temporal${name}${capitalizeKey(key)}`] = desc.value;
}
},
);

const descriptors = ObjectGetOwnPropertyDescriptors(constructor.prototype);
ArrayPrototypeForEach(
ObjectEntries(descriptors),
({ 0: key, 1: desc }) => {
if (typeof key !== 'string') return;

if ('get' in desc) {
exports[`Temporal${name}PrototypeGet${capitalizeKey(key)}`] = uncurryThis(desc.get);
} else {
exports[`Temporal${name}Prototype${capitalizeKey(key)}`] = typeof desc.value === 'function' ?
uncurryThis(desc.value) :
desc.value;
}
},
);

exports[`isTemporal${name}`] = makeTypeCheckMethod(descriptors[brandedGetter].get);
}
};

function makeTypeCheckMethod(getter) {
return (value) => {
try {
FunctionPrototypeCall(getter, value);
return true;
} catch {
return false;
}
};
}

function capitalizeKey(key) {
return `${StringPrototypeToUpperCase(key[0])}${StringPrototypeSubstring(key, 1)}`;
}
1 change: 1 addition & 0 deletions test/parallel/test-bootstrap-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ expected.beforePreExec = new Set([

expected.atRunTime = new Set([
'NativeModule internal/process/pre_execution',
'NativeModule internal/util/temporal',
]);

const { isMainThread } = require('worker_threads');
Expand Down
18 changes: 18 additions & 0 deletions test/parallel/test-temporal-utils-no-temporal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Flags: --expose-internals --no-harmony-temporal --no-warnings

'use strict';

const common = require('../common');
const assert = require('assert');

common.allowGlobals(globalThis.Temporal = {
Instant: class Instant {},
});

const instant = new Temporal.Instant();

// Module contents should be independent of global state at require time
const temporalUtils = require('internal/util/temporal');

assert.strictEqual(temporalUtils.hasTemporal, false);
assert.strictEqual(temporalUtils.isTemporalInstant(instant), false);
25 changes: 25 additions & 0 deletions test/parallel/test-temporal-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Flags: --expose-internals --no-warnings

'use strict';

const common = require('../common');
const assert = require('assert');

if (!common.hasTemporal) {
common.skip('No Temporal support');
}

const instant = Temporal.Now.instant();

delete globalThis.Temporal;
assert.strictEqual(typeof Temporal, 'undefined');

// Module contents should be independent of global state at require time
const temporalUtils = require('internal/util/temporal');

assert.strictEqual(temporalUtils.hasTemporal, true);
assert.strictEqual(instant.constructor, temporalUtils.TemporalInstant);
assert.strictEqual(instant.epochNanoseconds,
temporalUtils.TemporalInstantPrototypeGetEpochNanoseconds(instant));
assert.strictEqual(temporalUtils.isTemporalInstant(instant), true);
assert.strictEqual(temporalUtils.isTemporalZonedDateTime(instant), false);
Loading