Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: improve documentation on argument validation #56954

Merged
merged 1 commit into from
Feb 14, 2025
Merged
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
62 changes: 62 additions & 0 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,68 @@ void InitializeHttpParser(Local<Object> target,
}
```

### Argument validation in public APIs vs. internal code

#### Public API argument sanitization

When arguments come directly from user code, Node.js will typically validate them at the
JavaScript layer and throws user-friendly
[errors](https://github.com/nodejs/node/blob/main/doc/contributing/using-internal-errors.md)
(e.g., `ERR_INVALID_*`), if they are invalid. This helps end users
quickly understand and fix mistakes in their own code.

This approach ensures that the error message pinpoints which argument is wrong
and how it should be fixed. Additionally, problems in user code do not cause
mysterious crashes or hard-to-diagnose failures deeper in the engine.

Example from `zlib.js`:

```js
function crc32(data, value = 0) {
if (typeof data !== 'string' && !isArrayBufferView(data)) {
throw new ERR_INVALID_ARG_TYPE('data', ['Buffer', 'TypedArray', 'DataView','string'], data);
}
validateUint32(value, 'value');
return crc32Native(data, value);
}
```

The corresponding C++ assertion code for the above example from it's binding `node_zlib.cc`:

```cpp
CHECK(args[0]->IsArrayBufferView() || args[0]->IsString());
CHECK(args[1]->IsUint32());
```

#### Internal code and C++ binding checks

Inside Node.js’s internal layers, especially the C++ [binding function][]s
typically assume their arguments have already been checked and sanitized
by the upper-level (JavaScript) callers. As a result, internal C++ code
often just uses `CHECK()` or similar assertions to confirm that the
types/values passed in are correct. If that assertion fails, Node.js will
crash or abort with an internal diagnostic message. This is to avoid
re-validating every internal function argument repeatedly which can slow
down the system.

However, in a less common case where the API is implemented completely in
C++, the arguments would be validated directly in C++, with the errors
thrown using `THROW_ERR_INVALID_*` macros from `src/node_errors.h`.

For example in `worker_threads.moveMessagePortToContext`:

```cpp
void MessagePort::MoveToContext(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
if (!args[0]->IsObject() ||
!env->message_port_constructor_template()->HasInstance(args[0])) {
return THROW_ERR_INVALID_ARG_TYPE(env,
"The \"port\" argument must be a MessagePort instance");
}
// ...
}
```

<a id="exception-handling"></a>

### Exception handling
Expand Down
Loading