NodeJS source code cannot find set_immediate_callback_function definition

I am trying to understand how Node works internally. I am really interested in the check phase but could not make sense of thing how that phase queue is processed. Right now I cannot understand where set_immediate_callback_function is defined. Such method is part of Environment class but I cannot see it.

This methods is called during the setup phase at:

void SetupTimers(const FunctionCallbackInfo<Value>& args) {
  CHECK(args[0]->IsFunction());
  CHECK(args[1]->IsFunction());
  auto env = Environment::GetCurrent(args);

  env->set_immediate_callback_function(args[0].As<Function>());
  env->set_timers_callback_function(args[1].As<Function>());
}

Answer

The set_immediate_callback_function is generated by the preprocessor, by a technique called X-Macros.

Let’s break it down. You are referring to this code:

void SetupTimers(const FunctionCallbackInfo<Value>& args) {
  CHECK(args[0]->IsFunction());
  CHECK(args[1]->IsFunction());
  auto env = Environment::GetCurrent(args);

  env->set_immediate_callback_function(args[0].As<Function>());
  env->set_timers_callback_function(args[1].As<Function>());
}

Grepping for set_immediate_callback_function does not reveal anything. But if you grep for immediate_callback_function (without the set_), you will find this here:

#define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) 
 ...
 V(immediate_callback_function, v8::Function) 

This pattern is often called X-Macros. Following the trace, you can find ENVIRONMENT_STRONG_PERSISTENT_VALUES here:

#define V(PropertyName, TypeName)                                             
  inline v8::Local<TypeName> PropertyName() const;                            
  inline void set_ ## PropertyName(v8::Local<TypeName> value);
  ENVIRONMENT_STRONG_PERSISTENT_VALUES(V)
  ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES(V)
#undef V

Notice this line:

inline void set_ ## PropertyName(v8::Local<TypeName> value);

PropertyName is immediate_callback_function and ## is string concatenation. So, finally that is the origin of set_immediate_callback_function.

For the exact expansion, you would need to run the preprocessor. I find it hard to follow the expansion in my head after this point. It should end up with something like that:

  inline v8::Local<v8::Function> immediate_callback_function() const;                            
  inline void set_immediate_callback_function(v8::Local<v8::Function> value);

Looks like the NodeJs codebase uses the C preprocessor to generate a property-like mechanism. In C and C++, properties are not supported directly, and I assume they wanted to avoid duplicating code. That is why they used code generation through the preprocessor.


(X-Macros are a technique that should not be overused. Yet it is difficult to decide where to draw the line. As you saw, it makes it harder to reason about the code. On the other hands, manual code repetition can be more error-prone and verbose.)