|
| 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