Primordials in Node.js

If you are curious programmer who likes reading source code and if you happen to have ever read Node.js's source you would have noticed this term called primordial and would have seen it's usage like:

const {
ObjectCreate,
ObjectDefineProperty,
RegExp,
RegExpPrototypeExec,
SafeArrayIterator,
StringPrototypeToLowerCase,
StringPrototypeToUpperCase,
} = primordials;

You get into your REPL and hit primordials and notice that Uncaught ReferenceError: primordials is not defined

So, what are these primodials?

From the comments in source:

This file subclasses and stores the JS builtins that come from the VM
so that Node.js's builtin modules do not need to later look these up from
the global proxy, which can be mutated by users.

Interesting, so this is here to avoid any problems that might occur in case user overwrites anything in prototype chain or the global proxy.

Example:

Say somewhere in the node internal logger lib we have:

Object.defineproperty(logger, 'enabled', {
get() {
return test();
},
configurable: true,
enumerable: true
});

If a user defined lib overrides or deletes Object.defineproperty the internal logger's defineproperty will fail to execute or behave as per expectations, hence we would need primordials thereby we could rewrite the same code as:

const {
ObjectDefineProperty,
} = primordials;

ObjectDefineProperty(logger, 'enabled', {
get() {
return test();
},
configurable: true,
enumerable: true
});

But there is also a performance concern:

Use of primordials have sometimes a dramatic impact on performance, please
benchmark all changes made in performance-sensitive areas of the codebase.
See: https://github.com/nodejs/node/pull/38248

Digging into the code:

First up we notice aliases for Reflect functions.

const {
defineProperty: ReflectDefineProperty,
getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor,
ownKeys: ReflectOwnKeys,
} = Reflect;

Then we have two interesting methods uncurryThis and applyBind which are basically:

func => Function.prototype.call.bind(func)

func => Function.prototype.apply.bind(func)

It is using bind.bind(apply) or bind.bind(call) to avoid usingFunction.prototype.bindandFunction.prototype.applyorFunction.prototype.call` after users may have mutated it.

But delete Function.prototype.call will still break most of node, also also delete Array.prototype[Symbol.iterator] will break everything most anywhere there's for-of, or spreading an array. [Thanks to @ljharb for this info]

Methods that accept a variable number of arguments, and thus it's useful to
also create ${prefix}${key}Apply, which uses Function.prototype.apply,
instead of Function.prototype.call, and thus doesn't require iterator destructuring.

There are some interesting cases:

createSafeIterator?

const createSafeIterator = (factory, next) => {
class SafeIterator {
constructor(iterable) {
this._iterator = factory(iterable);
}
next() {
return next(this._iterator);
}
[SymbolIterator]() {
return this;
}
}
ObjectSetPrototypeOf(SafeIterator.prototype, null);
ObjectFreeze(SafeIterator.prototype);
ObjectFreeze(SafeIterator);
return SafeIterator;
};

Also, it is worth noting about frozen-intrinsics flag which enables experimental frozen intrinsics like Array and Object. Support is currently only provided for the root context and no guarantees are currently provided that global.Array is indeed the default intrinsic reference. If you were to start your REPL with --forzen-intrinsics flag:

> Object.defineProperty // [Function: defineProperty] Function <Function <[Object: null prototype] {}>>
> delete Object.defineProperty // false
> Object.defineProperty = null // null
> Object.defineProperty // [Function: defineProperty] Function <Function <[Object: null prototype] {}>>

Hope you had fun scratching the surface on Primordials!

P.S: Thanks to Guy Bedford for reviewing this post.

Feel free to share this article. You may as well ping me on Twitter.

Published