Open trevnorris opened 9 years ago
I'll start by noting that not using templates leads to very clumsy code, and since V8 makes heavy use of templates, this would require not directly exposing anything from V8. Otherwise the issue of ABI compatibility will reemerge.
Some initial randomish thoughts:
Three things I believe could be done without templates and be ABI compliant:
Local<Value>
with a few methods. Such as Is{Undefined,Null,True,etc.}()
, BooleanValue()
, NumberValue()
, etc. The hurdle to overcome would be casting to other types, and supporting all those APIs.typedef void (*FunctionCallback)(const FunctionCallbackInfo<Value>& info)
could be a normal function call. info[i]
could be an array of values, and since they're all initially Local<Values>
the naive implementation would be supported. The problematic would be all calls that return Local<Object>
.Local<Value>
abstraction, and in fact would be most compatible that way because currently it requires a Local<Object>
and in the future will require a Local<Uint8Array>
. Possibly something like the following (warning, this is very C-ish):struct node_buffer_s {
char* data;
size_t length;
};
typedef void node_buffer_s node_buffer_t;
void FnCallback(const CallbackInfo* info) {
node_buffer_t buf;
Node::Buffer::GetData(info[0], &buf);
}
Would definitely want a code generator to generate the code for all the ensuing virtually identical wrapper functions with different types. It would be nice if one could get at the resulting C++ code after template processing, but before compilation.
Have a look at how Python 3 is doing it. Python is also dynamically typed, and everything is an object, but I have to say that writing addons for it is rather painful.
Also, I think the bar for a useful addon API should be set higher than trivial. If you can only do trivial things, you might as well just set up a socket or pipe of sorts and communicate between JavaScript and the real world through that.
@kkoopa
Also, I think the bar for a useful addon API should be set higher than trivial.
I was just echoing @rvagg's sentiment of:
Let's start simple and unambitious
So to clarify I was simply saying we start small, experiment and see how well it works. If it works at all.
One important thing is that if we want to truly achieve ABI compatibility then the exposed API needs to be written in C. There have been more than a few people smarter than myself that have expressed that C++ doesn't do well at maintaining proper memory structures when compiled with different compilers, or even different versions of the same compiler.
I agree that we have to start simple, implementation-wise. However, that initial implementation needs to scale up to offering a richer feature set.
It does not need to be written in C as such, it's just the external functions (and data) that need to be "C" exported. Without C++, how would the the wrappers interact with v8?
It does not need to be written in C as such, it's just the external functions (and data) that need to be "C" exported.
Agreed. I should have made my following statement more clear:
One important thing is that if we want to truly achieve ABI compatibility then the exposed API needs to be written in C.
So yes. I completely agree. It's just the user facing API that should be C.
Something like this should do. https://github.com/martine/v8c/blob/v8c/src/v8c.cc
On May 14, 2015 10:46:57 PM EEST, Trevor Norris notifications@github.com wrote:
It does not need to be written in C as such, it's just the external functions (and data) that need to be "C" exported.
Agreed. I should have made my following statement more clear:
One important thing is that if we want to truly achieve ABI compatibility then the exposed API needs to be written in C.
So yes. I completely agree. It's just the user facing API that should be C.
Reply to this email directly or view it on GitHub: https://github.com/iojs/nan/issues/349#issuecomment-102147935
@kkoopa good stuff. we'd just have to extend it to support current APIs like Buffer
.
And new V8. Notice that this project has been abandoned for 6 years.
On May 14, 2015 11:00:11 PM EEST, Trevor Norris notifications@github.com wrote:
@kkoopa good stuff. we'd just have to extend it to support current APIs like
Buffer
.
Reply to this email directly or view it on GitHub: https://github.com/iojs/nan/issues/349#issuecomment-102151275
One important thing is that if we want to truly achieve ABI compatibility then the exposed API needs to be written in C. There have been more than a few people smarter than myself that have expressed that C++ doesn't do well at maintaining proper memory structures when compiled with different compilers, or even different versions of the same compiler.
Structures/classes don't have to be a problem when you don't use features such as virtual methods, multiple inheritance, member function pointers, etc.
I suppose name mangling could be an issue for people that mix C++11 code with pre-C++11 code. You can work around it by building everything with -fabi-version=6
but that requires that you use a recent compiler.
So, what we'd essentially want is to wrap v8's C++ exports in C exports to get a stable ABI and then have a (NAN-derived) header library on top of that which would restore the C++ nature of V8's API by mapping to the C exports.
Something like this:
extern "C" {
V8StringUtf8 v8_string_utf8_new(const char* data, int length);
V8Number v8_number_int32_new(int32_t val);
V8Number v8_number_uint32_new(uint32_t val);
}
template<v8::String>
v8::Local<v8::String> NanNew(const char *data, int length = -1) {
return v8_string_utf8_new(data, length);
}
template<v8::Int32>
v8::Local<v8::Int32> NanNew(int32_t val) {
return v8_number_int32_new(val);
}
template<v8::UInt32>
v8::Local<v8::UInt32> NanNew(uint32_t val) {
return v8_number_uint32_new(val);
}
Microsoft appears to have created a V8 compatible C++ API facade for Chakra. While it is a nice piece of engineering, it also has the disadvantage of trying to keep up with a constantly changing V8 API that is not managed by the node project.
I like the javascript engine wrapper C API idea proposed by @trevnorris. Node itself and node native modules could only use this C API. That way the javascript engine (V8, Spidermonkey, Duktape, JavascriptCore or other) could be made to be pluggable at runtime depending on the needs of the user. Native node modules compiled against this hypothetical C API could work against any such runtime-pluggable engine without recompilation due to its more stable ABI.
However, one downside with such a javascript engine C API would be that it would impose a little bit of overhead on every javascript <=> c++ operation as compared to header-only inline C++ classes or C macros. Even so, I still think it is worth the cost for the maintainability and the flexibility it would offer.
@kzc After doing testing in this area I'm fairly confident that we'd be able to do this with very little overhead. At least minimal enough where users wouldn't feel it in their JS code.
Clearly yes. The overhead of context switching from JavaScript to C++ drowns out everything else.
On Friday 15 May 2015 03:06:11 Trevor Norris wrote:
@kzc After doing testing in this area I'm fairly confident that we'd be able to do this with very little overhead. At least minimal enough where users wouldn't feel it in their JS code.
Reply to this email directly or view it on GitHub: https://github.com/iojs/nan/issues/349#issuecomment-102354581
I wouldn't recommend having the proposed C API functions and types mirror v8's C++ API and its concepts, because you would still have the original problem of tightly coupling node against an ever changing v8. Ideally the proposed C API should work equally well with Spidermonkey which cares about registering GC roots and Duktape which uses a Lua-style sandbox approach where you only manipulate copies of the actual data structures. But I recognize that such portability would impose a runtime cost.
On the contrary, I would recommend having it mirror V8's C++ API. Abstracting away the engine in a pluggable way is too much unnecessary work. It is not like io.js core would use these bindings internally, so it still would not become Spidernode. Personally, I have zero interest in supporting other JavaScript engines than V8.
Perhaps I mistook @trevnorris' proposal. I thought both node itself and native node modules would be changed to only use this new C API to isolate them both from the javascript engine's API.
If the goal is to only support v8, then I don't think the proposed C API buys you much. By closely mirroring the C++ v8 API any significant upstream change would still require an equivalent change in the C API.
@kzc The gain for maintaining a public C API is that native modules will not have to be recompiled every time they upgrade their version of node.
@kkoopa Unfortunately using this API internally is exactly what large businesses want. Microsoft has already done a port to Chakra, Oracle and IBM are independently working to get node running on Java. Anyway, just giving you a heads up that this is exactly what these large companies are after.
Oh, really? Why would they want that? What, exactly, are we discussing here? Is it making an ABI-stable API for modules or is it rewriting all of Node to abstract away V8? The former is a lot less work than the latter.
They want the ability to use whatever VM under the hood they deem appropriate. But then also have the user facing API ABI stable so they can ship pre-compiled modules. I'm only relaying this information so you're aware, and I dread to think what would happen to core in an attempt to do this.
The reason I opened this was to discuss the user-facing part. Creating ABI stability for native modules so they don't have to be recompiled with every release.
Good, then we're on the same page. In that case, I think the best to do is the following:
Take (almost) all the functionality already exposed by NAN (diregarding sugar, 96 % of it is used in native modules in the wild). This is the least a usable API should offer to support all the existing native modules.
Create C-exported wrappers for all the necessary V8 functions. Naming should reflect the original API, so let's use some arbitrary, but consistent, sort of mangling system through underscores. The v8 namespace may be considered implicit. We only expose the longest function signature when there are convenience overloads or default argument values.
Local__String__ String_NewFromUtf8(Isolate *, const char *str, int length, ...) {...}
Local__String__ String_NewFromTwoByte(Isolate *, const uint16_t *str, int length, ...) {...}
...
Then, as I mentioned in an earlier post, we could stick a header-only C++ API back on top, e.g. NAN or derivative. This library would circumvent the clumsiness and loss from having C-bindings, giving the best of two worlds. A C++ API with consistent C ABI. The naming scheme for C-exported functions can be arbitrarily clumsy, yet still hidden away.
All of this even seems like it would be possible to automate, i.e. Write a program that parses v8.h, getting all public functions and spits out a bunch of horribly named C function declarations and a C++ header that reconstructs the C++ API via the C functions. This would be cool, but probably not an easy task.
Sounds like a good enough approach to me. @bnoordhuis thoughts?
Would still need to export some functions from Node too, buffer, objectwrap, strings, etc. Then have som representation of exported classes as structs (they are rare,but a couple exist).
On May 18, 2015 8:55:18 PM EEST, Trevor Norris notifications@github.com wrote:
Sounds like a good enough approach to me. @bnoordhuis thoughts?
Reply to this email directly or view it on GitHub: https://github.com/nodejs/nan/issues/349#issuecomment-103148532
Something to consider: V8 uses RAII in a few places to good effect, c.f. HandleScope. A C shim would be quite error prone to use.
Exposing structs in the API might make ABI stability pretty complicated. (If I ever get around to writing a blog post "Libuv, lessons learned", that's going to feature prominently.) I would suggest using opaque pointers, coupled with accessors.
Can you give some concrete examples of what you had in mind, e.g. HandleScope?
On May 18, 2015 9:49:15 PM EEST, Ben Noordhuis notifications@github.com wrote:
Something to consider: V8 uses RAII in a few places to good effect, c.f. HandleScope. A C shim would be quite error prone to use.
Exposing structs in the API might make ABI stability pretty complicated. (If I ever get around to writing a blog post "Libuv, lessons learned", that's going to feature prominently.) I would suggest using opaque pointers, coupled with accessors.
Reply to this email directly or view it on GitHub: https://github.com/nodejs/nan/issues/349#issuecomment-103170593
@trevnorris, I agree that a C API wrapper makes sense if you intend to support javascript engines other than just v8. Just be aware that there are features and concepts in the v8 API that don't easily map to other javascript engines, and vice versa. Mirroring the v8 C++ API in C may create more work for the other engine mappings.
@kzc At this point I'm neither for or against mirroring the V8 API. Right now I'm just trying to facilitate conversation until we can achieve some actionable items.
Can you give some concrete examples of what you had in mind, e.g. HandleScope?
I think that's a question for me? Well, I imagine that C++ code like this:
void f(v8::Isolate* isolate) {
v8::HandleScope handle_scope(isolate);
// ...
if (g()) return;
// ...
}
Would end up looking something like this in C:
void f(v8_isolate *isolate) {
v8_handlescope_enter(isolate);
// ...
if (g()) {
v8_handlescope_exit(isolate);
return;
}
// ...
v8_handlescope_exit(isolate);
}
Manually having to balance enter/exit calls everywhere is a bit of a pain and easy to get wrong.
@bnoordhuis so the best solution would be to still use a C++ interface but stay away from (as you mentioned) virtual methods, multiple inheritance, member function pointers, etc?
I don't know about 'best' but a C++ API done right would certainly be easier and more robust to use.
Note that the idea was to reconstruct the C++ API on top of the C API. But, if it is less work and doable with a C++ API, then I guess that would be better.
+1 on achieving ABI compatibility.
I think that it would be hard to achieve ABI compatibility in C++. Here are two issues that I am aware of: 1) If the class definition changes even just by adding a private field at the end, the size of objects changes and any use of the new operator is broken across module boundaries. I think you could work around this by allowing instantiation only in the main module (node) through factory methods, and disallowing any passing by value in the addon module (because the copy constructor would be affected too). 2) Name mangling. This can break across compiler versions. I am not aware of any silver bullets to address this issue.
Chakra introduces an interesting dynamic here and we may want to engage the Microsoft team on this effort given what they've had to go through to make their shim
cc @geoffkizer, @jianchun, @curtisman, @EdMaurer from the Chakra team.
1) If the class definition changes even just by adding a private field at the end, the size of objects changes and any use of the new operator is broken across module boundaries. I think you could work around this by allowing instantiation only in the main module (node) through factory methods, and disallowing any passing by value in the addon module (because the copy constructor would be affected too).
That's correct. The same issue applies to C structs, however. I don't think C++ is any worse in that respect, it's just different.
2) Name mangling. This can break across compiler versions. I am not aware of any silver bullets to address this issue.
With gcc and clang, you control that through the -fabi-version=
switch. I don't know if VS has a similar option.
The C++ ABI changes very infrequently. Before C++11, the last real update was in 2004, I think.
The same issue applies to C structs, however. I don't think C++ is any worse in that respect, it's just different.
True. I suppose you could design a C++ API that provides ABI compatibility. As you mentioned, it would have to disallow a few features that are specific to C++.
Another advantage of using C is, well, that you can invoke it from pure C code. It’s the lowest common denominator, on top of which you can build bindings for other languages.
So I am +1 for the approach (suggested by @kkoopa) of building the syntactic C++ sugar on top of a flat C layer, which would help streamline things like balancing the enter/exit scope calls.
With gcc and clang, you control that through the -fabi-version= switch. I don't know if VS has a similar option.
I’ll try to find out more about name mangling stability in VC++.
Aside from the C vs C++ issue, I think that in order to achieve ABI compatibility the API needs to be designed and maintained with that goal in mind. As @kzc pointed out in this comment that means that we shouldn’t expose v8 types directly, or we would be back to square one. v8 types can be encapsulated, or passed as opaque tokens (e.g. void *) but modules should not be exposed to their definitions. Correct?
Also, I think it makes a lot of sense to consider the interface between Node itself and the engine as part of this conversation. Please correct me if I am wrong, but an internal abstraction between Node and v8 would also solve the problem of native modules interfacing with Node, while the other way around is not necessarily true. Well, because the former is probably a bigger beast to tackle than the latter. It would also mitigate the pain of Node core keeping up with v8, and possibly allow for pluggable engines. My point is that at the time that we introduce a new API for native addons, and break compatibility, we might want to consider solving the other problems as well, because we don’t want to change this API again in 6 months. API should be designed to last. I guess that’s an argument against starting small and unambitious ;)
On that topic, it should be noted that JxCore has already implemented an abstraction layer that encapsulates the engine interface for both “Node” core and native modules. They have done so while maintaining compatibility with existing native modules written against v8. And they have made all the change in core to interface with this abstracted engine API. I have had the opportunity to talk with the folks who worked on the project, and they claimed to be willing and able to extricate the interface changes from the other features and submit a pull request to Node. Caveats: their changes are currently based on Node v0.10; and I don’t think their API is 100% ABI-ready in that it uses C++ and macros, but fixing that might be a fairly mechanical job.
So I think one possible course of action to address this issue at large could be the following:
Does that sound like a viable option?
I'm personally highly skeptical that any kind of engine abstraction is workable in the long run and not a huge time drain and source of friction.
JXCore basically shims the V8 API, similar to the Chakra port. Updating V8 can already be big source of friction; now factor in the cost of updating all the shims.
What if you want to use V8-specific functionality? You can't unless the other engines have similar functionality.
What if the V8 API changes in a way that you can't reasonably emulate with other engines? Or vice versa? Do we stop upgrading?
Let JXCore and Chakra show that their approach is viable over a multi-year time span first before we start thinking about adopting it.
I've been recently walking through JXCore sources. Important points to remark;
@trevnorris clearly C,C++ etc. wrapper is not a solution alone. I agree with @orangemocha on JXCore's engagement in order to make the process less painful.
What if the V8 API changes in a way that you can't reasonably emulate with other engines? Or vice versa? Do we stop upgrading?
@bnoordhuis I don't know if you are familiar with SpiderMonkey. V8 and SpiderMonkey are already in a place that you can not reasonably emulate.
What if you want to use V8-specific functionality?
@bnoordhuis can you be more specific? Sorry if I'm wrong but Does Node.JS promotes OS specific features ? Besides, other JavaScript engines also have their unique advantages.
One suggestion though. nan and jxcore can collaborate on a common solution for c,c++ node modules. This could be a good starting point and community would benefit from that.
@pittek OS specific != V8 specific. The specifics come when doing things like tooling around the VM, or even things like how we attach C++ class instances to Objects, and the Object to the class instances.
@trevnorris actually OS specific = V8 specific if you are exactly referring to tooling around the VM. How VM is different than OS ? Attaching C++ class instances to Objects?
is the least concern that an abstraction would take care of. However how you manage the life cycle of that Object / Scope and Runtime is another thing. For example, LibUV plays some kind of an abstraction role between the underlying OS and Node.JS.
V8 is a library, not an operating system. They are quite different. V8 runs on many operating systems, so does Node, which depends on V8. Here, libuv abstracts away the operating system, with varying degrees of success.
IMHO, there is no single best pattern that works for each JavaScript engine interface and internals. JXcore encapsulates SpiderMonkey while abstracting V8. Chakra is much more suitable for abstraction but in time the differences between the engines may require encapsulation taking the place.
I believe the real problem is something else.
The problem here is abstracting JavaScript engine capabilities to 3rd party solutions. JXcore has a separate abstraction for the JavaScript engine features. It doesn't have much to do how we abstract or encapsulate the JavaScript engine. It's all about sharing a common interface.
Today we use this interface for iOS and Android applications mostly. For example on Windows, once you have JXcore SM or V8 build as a shared library, feel free to switch it without recompiling the host application.
This interface is usable for the majority of c,c++ module scenarios. We can still allow old style module development but don't encourage it. I don't think a module developer would pick an option that will end up updating again and again.
As far as I know, majority of the c,c++ modules doesn't require a special access to core JavaScript engine.
Any thoughts ?
Hopefully it is not going to be a wrong statement but I consider io.js as the node.js next. So, what will be the next is very important in terms of bringing a rather consistent interface for addons. (c, c++ addons)
I see node.js is engine independent framework in the future. However, node serving on a multiple engine interface is not that easy as a short/mid term goal. I agree with @bnoordhuis at some level that the multiple engine approach should evolve. But this process is not going to evolve alone.
IMHO, it's really nice to have all these (jxcore, chakra) efforts around node.js. I don't see a reason why we shouldn't combine the efforts. We can start with a native addon interface that will be backing the next gen node applications. My humble addition to @rvagg's initial list;
I believe, If we can establish a proper native addon interface, it would open a door to multiple JS engine backend support.
...I consider io.js as the node.js next
Yes, that is sort of how it is.
now
(io.js) v1.6 : v1.7 v1.x
| | : | |
v0.10.x /--------------:-----------------\ Node.js 2.0
____|____/ : \______|_____
\ : /
\--------------:-----------------/
| | : | |
(node.js) v0.12.x : v0.13.x v0.14.x
The full API consists of a Compact + Platform + Extended APIs
That is sort of how it is intended already.
+---------------------------------------------------+
| Module and Application Ecosystem |
+---------------------------------------------------+
| | | |
| | +----------------------+ |
| | | Binary Abstraction | |
| | | Layer | |
| | +----------------------+ |
| | | | |
+----------------+ | | | /
| Node.js Core | | | | /
| Library API | | | | /
+----------------+ | | | /
| js impl | | | | /
+----------------+ | | | /
| | | | /
+--------------------------------------+ |/
| Node.js Application Binary Interface | |
+--------------------------------------| |
| C/C++ impl | |
+--------------------------------------+ |
| |
+---------------------------------------+
| Dependencies: v8, libuv, openssl, etc |
+---------------------------------------+
@kkoopa thanks for sharing the schema. I think I do remember seeing this definition once. Let me remark the differences over the schema then;
Node.JS Core Library API
+ Binary Abstraction Layer
. Kindly consider this as the basics of being a cross platform framework. The 'Platform and Extended' parts of the API I was mentioning applies to the rest.Sorry for the naive question; Is there a working group on this subject or how we/someone propose a working group ?
This seems to be the 'Addon API' working group. That means the Binary Abstraction Layer, and apparently the Node.js Application Binary Interface, because no existing WG would fit it better and they are sort of related.
Abstracting away V8 is sort of out of scope here. While I think everyone agrees that "it would be nice to have a wonderful abstraction layer for the JS engine", that is like having a desire for world peace: nice, but unfeasible. That being said, as there appears to be interest in spidernode or whatever, it seems reasonable to form a WG around that and let those who care for it work on it.
I don't know how new working groups are added. Do they have to go through a TC meeting, or is it just a matter of someone with admin rights creating a repository?
FFI will be brought to io.js, which should make trivial addons doable straight from JavaScript. That fits in the Core Library and API.
I see JXCore's javascript engine abstraction API makes extensive use of macros to achieve native module source code portability between v8 and Spidermonkey with recompilation. Although Chakra doesn't appear to use macros in its Node facade, it also seems to be a compile-time binding. @obastemur, could you comment on the feasibilty and estimated amount of effort to produce a pure C wrapper approach in order to achieve a stable ABI for node modules independent of the JS engine?
could you comment on the feasibilty and estimated amount of effort to produce a pure C wrapper approach in order to achieve a stable ABI for node modules independent of the JS engine?
@kzc JXcore's public API for embedder application is a pure C wrapper (otherwise we wouldn't able to embed it into Objective-C app directly) And it's engine independent. Although our intention wasn't providing something for addons, we can update it to cover this part. In other words, this task has already been done for a compact set of JS engine features. It's not a rocket science what we did and the limited set of API can be extended.
referencing from @plika's comment;
For example on Windows, once you have JXcore SM or V8 build as a shared library, feel free to switch it without recompiling the host application.
@kkoopa
Abstracting away V8 is sort of out of scope here.
Let's say v8 version X is either a starting or end point. Considering the differences let say SpiderMonkey is Y. Preparing a compact API
from X to Y (vice versa) doesn't abstract the V8 away. Instead, it helps to design something less vulnerable to X to Y or Y to X changes. At the JS engine basics level (compact API) it is doable and covers majority of the use cases.
I think everyone agrees that "it would be nice to have a wonderful abstraction layer for the JS engine", that is like having a desire for world peace
It's rather a sampling problem. If we can pick the right sample from the given group we may produce something that makes sense. I wouldn't also go for something like 'for once for all'.. For me, this is not a 'let's replace v8 to something' problem. We already did that. I'm ready to help if you want to do the same and also fine if you don't. My problem is the core of this framework which is subject to be shared among the solutions. Node.JS needs a core, a compact framework, compact public API that is not dependent (at least this much) to the underlying JS engine / openssl version etc...
FFI will be brought to io.js, which should make trivial addons doable straight from JavaScript.
I see why it's needed and I see how it will be hard to maintain it on many platforms. I don't know if I should repeat compact vs rest of the API puzzle.
That being said, as there appears to be interest in spidernode or whatever, it seems reasonable to form a WG around that and let those who care for it work on it.
I would rather go for a Node.JS compact framework workgroup. I don't know if I'm the only person believes that we need something in the core of everything else. Once we have such a core, rest is a customization around it.
:+1: for this process, but I'm trying to (over)-simplify this a bit:
My vote goes to solution that offers both:
a) Compact API, JS engine independent, as a way of calling native compiled libraries. Forget the Local<> stuff and V8 variable types etc. Just plain native types. This excludes a lot functionality, but that's the point. Maintain this compact API in the core.
b) Full native API that we now use directly or with NAN for those who need/want to have full access to and from JS engine.
I think many packages writing their native addons in C/C++ would just need simple "binding" between their native codebase and JS; not the overly fancy V8 facade, data types etc.
If you look at most of the published native addon packages, they have clean native C/C++ code written without any consideration for V8/JS and then a single "node_binding.cc" that is the necessary evil to bind native world with the JS .. and its usually the only thing that breaks with core upgrades.
@imyller couldn't agree more.
nan has done wonders for the community, but there are still missing bits. If these can be addressed there is a good desire to bring this into io.js itself. This thread should serve as a discussion on what are all the missing bits, what we can do about them and how to eventually get this into io.js.
The most prominent issue is lack of ABI compatibility. Needing to recompile with every new release, especially now with more frequent V8 updates, is difficult. @bnoordhuis mentioned that using templates is not a good form of ABI compatibility because they don't play well with the linker. (Ben, if you have any additional thoughts on this please feel free to include anything I've missed).