function initClass in generated source file: type-constructors.cpp

33 views
Skip to first unread message

PatlaDJ

unread,
Sep 30, 2012, 4:36:13 PM9/30/12
to av...@googlegroups.com
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.
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.

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)?
2. Does the operator = have different meaning here, and if so, what it is ?
3. I couldn't seem to find any function defined having the name 'classRuntimeDataIndex',.and/or.. 'classVmFlags'....etc. Are these 'a function" at all?
4. What is the difference between 't->m->processor->makeClass' and simply 'makeClass', these are calls to (seemingly functions) in machine.cpp ?

void
initClass(Thread* t, object o, uint16_t flags, uint16_t vmFlags, uint16_t fixedSize, uint8_t arrayElementSize, uint8_t arrayDimensions, uint32_t runtimeDataIndex, object objectMask, object name, object sourceFile, object super, object interfaceTable, object virtualTable, object fieldTable, object methodTable, object addendum, object staticTable, object loader, object source, uintptr_t length)
{
  setObjectClass(t, o, arrayBody(t, t->m->types, Machine::ClassType));
  classFlags(t, o) = flags;
  classVmFlags(t, o) = vmFlags;
  classFixedSize(t, o) = fixedSize;
  classArrayElementSize(t, o) = arrayElementSize;
  classArrayDimensions(t, o) = arrayDimensions;
  classRuntimeDataIndex(t, o) = runtimeDataIndex;
  classObjectMask(t, o) = objectMask;
  className(t, o) = name;
  classSourceFile(t, o) = sourceFile;
  classSuper(t, o) = super;
  classInterfaceTable(t, o) = interfaceTable;
  classVirtualTable(t, o) = virtualTable;
  classFieldTable(t, o) = fieldTable;
  classMethodTable(t, o) = methodTable;
  classAddendum(t, o) = addendum;
  classStaticTable(t, o) = staticTable;
  classLoader(t, o) = loader;
  classSource(t, o) = source;
  classLength(t, o) = length;
}

Joel Dice

unread,
Oct 1, 2012, 12:00:19 PM10/1/12
to av...@googlegroups.com
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.

PatlaDJ

unread,
Oct 4, 2012, 6:26:14 AM10/4/12
to av...@googlegroups.com
Thank you for you answer Joel. It help me understand a little bit better.
Btw, I managed to run debugging in VS2010. It runs perfect now :)
I just had to make some additional setups on the project, pointing the generated .pdb file. Yet I still build it thru Cygwin and your original makefile, which is not a problem for the debugging process to work flawless.

1. Btw, one more question. You answered on my previous question in another topic, that we can build with pre-compiled classes, so the app starts faster, Is this possible with an embedded build?
2. Also is it possible for such pre-compiled build with OpenJDK, which I find particularly useful as there the count of the classes loaded at app start is huge.


Joel Dice

unread,
Oct 6, 2012, 5:42:09 PM10/6/12
to av...@googlegroups.com
On Thu, 4 Oct 2012, PatlaDJ wrote:

> Thank you for you answer Joel. It help me understand a little bit
> better.Btw, I managed to run debugging in VS2010. It runs perfect now :)
> I just had to make some additional setups on the project, pointing the
> generated .pdb file. Yet I still build it thru Cygwin and your original
> makefile, which is not a problem for the debugging process to work flawless.
>
> 1. Btw, one more question. You answered on my previous question in another
> topic, that we can build with pre-compiled classes, so the app starts
> faster, Is this possible with an embedded build?

Yes, and in fact that's really the only time it makes sense.

> 2. Also is it possible for such pre-compiled build with OpenJDK, which I
> find particularly useful as there the count of the classes loaded at app
> start is huge.

Yes, and I was about to point you to avian-swt-examples to see how it's
done, but then I tried it myself and found it didn't work. There were a
few recent Avian regressions which broke the bootimage build, and
avian-swt-examples also needed some work to support it. Anyway, it's
working for me now. Please check out the latest and let me know if you
have any trouble:

https://github.com/ReadyTalk/avian
https://github.com/ReadyTalk/avian-swt-examples
Reply all
Reply to author
Forward
0 new messages