Skip to content

Commit 7a63b32

Browse files
committed
WIP V8 API usage in Node.js
1 parent d989e20 commit 7a63b32

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

doc/guides/V8-api-for-node.md

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
## V8 API usage for Node.js
2+
3+
v8 docs, not particularly useful:
4+
- https://v8.dev/docs
5+
6+
Some API info at https://v8.dev/docs/embed
7+
8+
- An isolate is a VM instance with its own heap.
9+
Node has one isolate.
10+
Can get from `Local<Object>->GetIsolate()`, `context->GetIsolate()`
11+
12+
- `Local<...>`: A local handle is a pointer to an object. All V8 objects are
13+
accessed using handles, they are necessary because of the way the V8 garbage
14+
collector works. Local handles can only be allocated on the stack, in a
15+
scope, not with new, and will be deleted as stack unwinds, usually with a
16+
scope.
17+
- XXX don't see us create scopes much in node, why?
18+
- XXX locals go into a HandleScope, for auto-dispose? Always, or by default?
19+
- Persistent handles last past C++ functions.
20+
- PersistentBase::SetWeak trigger a callback from the garbage collector when
21+
the only references to an object are from weak persistent handles.
22+
- A UniquePersistent<SomeType> handle relies on C++ constructors and
23+
destructors to manage the lifetime of the underlying object.
24+
- A Persistent<SomeType> can be constructed with its constructor, but must be
25+
explicitly cleared with Persistent::Reset.
26+
- Eternal is a persistent handle for JavaScript objects that are expected to
27+
never be deleted. It is cheaper to use because it relieves the garbage
28+
collector from determining the liveness of that object.
29+
- A handle scope can be thought of as a container for any number of handles.
30+
When you've finished with your handles, instead of deleting each one
31+
individually you can simply delete their scope.
32+
- EscapableHandleScope: Locals can't be returned, their scope will delete them,
33+
so you need an escable scope, and to `return scope.Escape(...the local)`. It
34+
scopes the locals into the enclosing scope and returns a local for that scope.
35+
36+
Local, MaybeLocal, Maybe, Value, oh my...
37+
38+
https://groups.google.com/forum/#!topic/v8-users/gQVpp1HmbqM
39+
40+
- MaybeLocal may be "empty", basically not contain a pointer of its type. See
41+
`class MaybeLocal`, has some useful notes on why, but basically its returned
42+
by anything that might fail to have a value.
43+
- If you know that the MaybeLocal has a value, then call ToLocalChecked() and
44+
node will abort with CHECK() if you are wrong.
45+
- Otherwise, you have to call `bool ToLocal(Local<S>* out)`, and check the
46+
return value to see if there was a value. Or call IsEmpty() to check.
47+
48+
- Maybe is similar, but doesn't hold a Local, just a value of T. "Just" means it
49+
"just has a value", a bizarrely named Haskellism :-(. It has a To() and
50+
ToChecked() similar to MaybeLocal.
51+
- A common node idiom is to make a seemingly side-effect free call to
52+
`.FromJust()` after `->Set()`, which will crash node if the Set failed.
53+
FromJust is also() commonly called after getting a Maybe<> of a concrete data
54+
type from a Local<Value>. It will crash if the conversion fails!
55+
56+
int32_t v = (Local<Value>)->In32Value(env->context()).FromJust()
57+
58+
- As<T> always returns a value, though probably not one that is useful, its
59+
usually best to check the type:
60+
- Boolean becomes 1/0 as int, "true"/"false" as strings, etc.
61+
- Numbers become false as Boolean (for any value), -3 casts to String "-3"
62+
- Functions become numerically zero, and "function () { const hello=0; }" as a
63+
String
64+
- Examples:
65+
CHECK(args[0]->IsInt32()); // Optional, but typical
66+
Local<Int32> l = args[0].As<Int32>(); int32_t v = li->Value();
67+
68+
CHECK(args[0]->IsString()); // Optional, but typical
69+
const node::Utf8Value v(env->isolate(), args[0]); const char* s = *s;
70+
71+
- To<T> will convert values in fairly typical js way:
72+
... never seems to be used by node?
73+
AFAICT, is identical to the As<> route, except for Boolean, which is always
74+
false with As<T>(), but is "expected" with ToT().
75+
76+
- FunctionCallbackInfo
77+
- can GetIsolate()
78+
- can get Environment: Environment* env = Environment::GetCurrent(args);
79+
- can get args using 0-based index
80+
- returns Local<Value>
81+
- has a .Length(), access past length returns a Local<Value> where value is
82+
Undefined
83+
- has a number of Is*() predicates which check exact type of Value, and
84+
(mostly) do NOT consider possible conversions:
85+
- {then: ()=>{}} is not considered a Promise,
86+
- `1` is not considered `true`,
87+
- `null` is not an `Object` (!),
88+
- new String() is a StringObject (not a String),
89+
- 3 is a Int32 and also a Uint32, -3 is only a Int32
90+
- etc.
91+
92+
Conventions on arg checking: two patterns are common:
93+
1. C++ functions directly exposed to the user
94+
2. C++ functions wrapped in a js function, only js is exposed to the user
95+
96+
The first option requires careful checking of argument types.
97+
98+
The second option is becoming more common. In this case the js function can
99+
check all the argument types are as expected (throwing an error if they are
100+
not), and destructure options properties to pass them as positional args (so the
101+
C++ doesn't have to do Object property access and presence/type checks). C++
102+
can use its args fairly directly, aborting if the js layer failed to pass the
103+
expected types:
104+
105+
CHECK(args[0]->IsInt32());
106+
int32_t arg0 = args[0].As<Int32>()->Value();
107+
108+
109+
- A context is an execution environment that allows separate, unrelated,
110+
JavaScript code to run in a single instance of V8. The motivation for using
111+
contexts in V8 was so that each window and iframe in a browser can have its
112+
own fresh JavaScript environment.
113+
XXX Node uses one context, mostly, does vm. create new ones? anything else?
114+
115+
Can get from `isolate->GetCurrentContext()`
116+
117+
118+
XXX Function vs FunctionTemplate ... wth?
119+
120+
121+
- node::Environment contains an Isolate and a Context, various other
122+
information global to Node, and many convenient methods.
123+
124+
It is possible to get an Environment from many v8 objects by calling
125+
Environment::GetCurrent() on a v8::Isolate*, v8::Local<v8::Context>,
126+
v8::FunctionCallbackInfo<v8::Value>, etc...
127+
128+
An Environment can be used to get an Isolate, Context, uv_loop_t, etc.
129+
130+
Commonly used convenience methods:
131+
- ThrowError/TypeError/RangeError/ErrnoException/...
132+
XXX why are some called Error and others called Exception?
133+
- SetMethod/SetMethodNoSideEffect/SetProtoMethod/SetTemplateMethod
134+
XXX difference?
135+
- SetImmediate
136+
- EnvironmentOptions* options(): "some" options...
137+
XXX how to reach PerIsolateOptions, PerProcessOptions
138+
- etc.
139+
140+
Contains many global strings and symbols.
141+
XXX ...
142+
143+
144+
## `NODE_MODULE_CONTEXT_AWARE_INTERNAL`
145+
146+
See: https://nodejs.org/api/addons.html#addons_context_aware_addons
147+
148+
Called with:
149+
- `Local<Object> exports`: where to put exported properties, conventionally
150+
called `target` in node
151+
- `Local<Value> module`: conventionally unused in node
152+
XXX what is this for? addon docs don't mention or use it
153+
- `Local<Context> context`:
154+
- void* priv: not commonly used
155+
XXX where is it ever used? for what?
156+
157+
Initialize is generally used to set methods and contants:
158+
159+
Environment* env = Environment::GetCurrent(context);
160+
env->SetMethod(target, "name", Name);
161+
// ... see Environment for method creation convenience functions
162+
NODE_DEFINE_CONSTANT(target, MACRO_NAME);
163+
// read-only, not deletable, not enumerable:
164+
NODE_DEFINE_HIDDEN_CONSTANT(target, MACRO_NAME);
165+
166+
For functions that wrap a C++ object in a js object, manually do what SetMethod
167+
does, see Hmac::Initialize as an example:
168+
1. Create a FunctionTemplate, used to setup function properties. The various
169+
wrappers all call v8::FunctionTemplate::New() with various arguments, but
170+
env->NewFunctionTemplate() is most commonly used. Signature,
171+
ConstructorBehavior, and SideEffectType can be customized, but aren't
172+
documented, and usually are left as default.
173+
2. Call env->setProtoMethod() to setup instance methods
174+
3. Get a function from the template
175+
4. Set a string in the target to the function
176+
177+
Mysterious boilerplate:
178+
- ToLocalChecked() see MaybeLocal vs Local
179+
- FromJust(): XXX
180+
181+
182+
183+
Local<FunctionTemplate> t = env->NewFunctionTemplate(New);
184+
185+
Fields are used to store pointers to C++ objects:
186+
XXX I think
187+
t->InstanceTemplate()->SetInternalFieldCount(1);
188+
Set methods:
189+
190+
191+
https://v8.dev/docs/embed#more-example-code
192+
- XXX read through, it has examples of calling the API
193+
194+
195+
196+
- <http://izs.me/v8-docs/classv8_1_1Object.html>
197+
- https://code.google.com/p/v8/
198+
- Building: <https://code.google.com/p/v8/wiki/BuildingWithGYP>
199+
- how to compile js to see what it looks like?
200+
- [Breaking V8 Changes](https://docs.google.com/a/strongloop.com/document/d/1g8JFi8T_oAE_7uAri7Njtig7fKaPDfotU6huOa1alds/edit)
201+
- <https://developers.google.com/v8/get_started>
202+
- <https://chromium.googlesource.com/v8/v8/+/master/docs/using_git.md>
203+
204+
- <https://developers.google.com/v8/embed>
205+
206+
Handle is base, from that are Local (go in HandleScope), and Persistent
207+
(manually managed scope). Constructors (String::New) seem to return Locals.
208+
209+
`return Local<Array>();` ... seems to do exactly what you are not supposed
210+
to do... whats up?

0 commit comments

Comments
 (0)