On Sun, 30 Sep 2012, PatlaDJ wrote:
> I've made a fork in Avian 0.5 and ever since I am trying to understand the
> inner workings of the project, even though I am simultaneously learning the
> C syntax.
Keep in mind that the VM is written in C++, not C. They are two different
languages. Even an expert in C would have a hard time understanding C++
when encountering it for the first time.
If you really want to learn C++, I highly recommend "The C++ Programming
Language" by Bjarne Stroustrup (although you might want to wait until
next March when the 4th edition is published, since it will cover C++11).
> There are very few comments especially on seemingly "hot action" source
> files, like machine.cpp and compile.cpp. Also there is very few information
> on how exactly the generation process of those type-....cpp files is
> happening, or maybe it is something very common in cpp projects, and I am
> simply unfamiliar with it? What to read for?
>
> Please provide some insights on types.def purpose and its formatting.
> Please, a type of insight - oriented to a less experienced C developer.
The program in type-generator.cpp takes types.def and a set of bootstrap
classes (e.g. Object.class, String.class, Class.class, etc.) as input and
produces the various type-*.cpp files as output. The syntax of types.def
is a set of S-expressions (
http://en.wikipedia.org/wiki/S-expression)
which define structures for use in the VM.
There are a few reasons those structures are defined in types.def instead
of as C++ classes. The first is that the C++ language does not specify
the offsets of fields in structs or classes, which makes it impossible to
walk the fields programatically during garbage collection. Even if it
did, we'd still need to generate meta-data to tell the collector how to
determine the type and layout of each object; C++'s RTTI does not
currently provide such information.
Second is that we use the type generation code to parse Java classes and
thereby determine the type meta-data discussed above, and it would be a
maintenance nightmare to maintain a separate C++ representation of each
class which needs to be kept in sync with its Java counterpart.
Third is that we generate field accessors which do runtime type checks
when the VM is built in debug mode every time the fields are read or
written to protect against programmer error.
Finally, we can do some things in types.def that can't be done portably in
C++, such as allow variable-length fields.
Anyway, the syntax of a type definition is
(type <name> [<java class>]
[(extends <type>)]
[([<field qualifier>] <field type> <field name> [<field alias>]) ...]),
where the things in <>'s are replaced with actual names, and the things in
[]'s are optional. If a Java class is specified, the type generator will
look for it in its classpath, parse it, and add any non-static fields it
finds to the type. If any fields are specified directly, they are also
added to the type in the order they are listed. There are several
possible field qualifiers:
* array: indicates that the field is a variable length array, and that
there is an implicit field representing the length of that array. For
example, (type foo (array uint8_t baz)) means type foo has a bazLength
field and an array of uint8_t called baz.
* nogc: indicates that even though the type of the field is object
(which normally tells the VM that it should be visited during GC), this
field should *not* be visited during GC
* noassert: means that the debug build should not check the type of the
object passed to a field accessor since it may be used during VM bootstrap
before the correct type information is available
* alias: means that the Java class may already have the field but under
a different name, in which case they should be considered the same; if it
doesn't, the field should be added under the specified name
* require: means that the Java class may already have the field under
the same name; if it doesn't, the field should be added
Finally, the (extends <type>) syntax just means that the type should
inherit from the specified supertype.
> A list of more specific questions goes here:
> 1. Down there is a function: Where and how the actual definition and
> declaration of this function, is taking place (I mean... excluding the
> generated code)?
See writeInitializers in type-generator.cpp. In this example, initClass
is called by makeClass to construct a new instance of the "class" type.
> 2. Does the operator = have different meaning here, and if so, what it is ?
e.g. classFlags returns a C++ reference
(
http://en.wikipedia.org/wiki/Reference_(C%2B%2B)), which is an L-value
(
http://en.wikipedia.org/wiki/Value_(computer_science)) and thus may be
assigned to. There's nothing special about the use of the "=" operator in
this case -- it's just assignment to a memory location as usual.
> 3. I couldn't seem to find any function defined having the name
> 'classRuntimeDataIndex',.and/or..�'classVmFlags'....etc. Are these 'a
> function" at all?
Yes, they're functions, and they're generated by type-generator.cpp as
part of type-declarations.cpp.
> 4. What is the difference between 't->m->processor->makeClass' and simply
> 'makeClass', these are calls to (seemingly functions) in machine.cpp ?
makeClass is a generated function which may be found in
type-constructors.cpp. Processor::makeClass is a virtual function which
is defined either in interpret.cpp or compile.cpp, depending on how the VM
is built. The process=interpret build doesn't require that each class has
an inline virtual lookup table (AKA vtable), so it just calls the
low-level makeClass function with a vtable length of zero. In contrast,
the process=compile build does make use of an inline vtable, so it passes
a potentially non-zero length to the low-level makeClass function.