Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
This page is work in progress. It will document the internals of groovy, the ideas and the techniques used so other developers may it have more easy to contribute to groovy in the future.

...

Parser

Package Layout

Bytecode Hints

Classloader Structure

One Class One Class Loader

When are two classes the same? If the name is equal and if the class loader is equal. This means you can have multiple versions of the same class if you load the class through different class loaders. Of course this versions don't really have to be the same. This also means if you have code like

Code Block
borderStylesolid
titleDuplicateClass.groovy


h2. Parser

h2. Package Layout

h2. Bytecode Hints

h2. Classloader Structure

h3. One Class One Class Loader

When are two classes the same? If the name is equal and if the class loader is equal. This means you can have multiple versions of the same class if you load the class through different class loaders. Of course this versions don't really have to be the same. This also means if you have code like
{code:title=DuplicateClass.groovy|borderStyle=solid}
Class cls = foo.class
assert cls.name==Foo.class.name
assert cls==Foo.class
{code}
may fail because cls is not Foo. This also means calling a method like

...

Code Block
borderStylesolid
title


{code:title=DuplicateClass.groovy
|borderStyle=solid}
def f(Foo f){
  println "f(Foo) called"
}

def f(Object o){
  println "f(Object) called"
}
{code}
with an Foo does not mean that "f(Foo) called" will be printed! This is no secret, you will find it in the language specification.

...

Class Loading Conventions

There are small conventions about how to do class loading in Java.

  1. always return the same class for the same name
  2. use your cache before asking a parent loader
  3. no parent loader doesn't mean the system loader
  4. ask your parent loader before trying ot load a class by yourself

Most of the loaders in groovy are violating one or more of these rules. I will exlpain why and how in the next sections

RootLoader

First let us start with the RootLoader. This one used used to start the console programs and is something like an advanced java launcher. When using Groovy embedded this loader is usually not used. The RootLoader sees itself as a root in a class loader tree. Against the convention to ask the parent before loading a class this laoder tries to load it by itself first. Only when this fails the parent is asked. They parent is usually the system class loader then. This step was needed because of many problems with multiple versions of libs, causing multiple classes of the same name where they are not expected, or classes loaded from the wrong libs. Because of that reason this loader may also be used in ant or maven to avoid clashes with the classes used by ant.

GroovyClassLoader

...




h3. Class Loading Conventions

There are small conventions about how to do class loading in Java. 
# always return the same class for the same name
# use your cache before asking a parent loader
# no parent loader doesn't mean the system loader
# ask your parent loader before trying ot load a class by yourself

Most of the loaders in groovy are violating one or more of these rules. I will exlpain why and how in the next sections

h3. RootLoader

First let us start with the  RootLoader. This one used used to start the console programs and is something like an advanced java launcher. When using Groovy embedded this loader is usually not used. The RootLoader sees itself as a root in a class loader tree. Against the convention to ask the parent before loading a class this laoder tries to load it by itself first. Only when this fails the parent is asked. They parent is usually the system class loader then. This step was needed because of many problems with multiple versions of libs, causing multiple classes of the same name where they are not expected, or classes loaded from the wrong libs. Because of that reason this loader may also be used in ant or maven to avoid clashes with the classes used by ant. 

h3. GroovyClassLoader

h3. ReflectorLoader

h2. Optional Typing

h3. Assignment 
If you assign a value to a typed variable in Groovy, then this assignment will include an implicit cast. A Declaration counts as assignment in that sense as well.
{code:title=Assignment.groovy|borderStyle=solid}
String x = 8
{groovy}
is in fact
{code:title=AssignmentExpanded.groovy|borderStyle=solid}
String x = (String) 8
{groovy}
Note: there is nothing to be done but boxing, if the cast is to Object, since everything in Groovy is an Object. The cast is realized in Groovy by writing ScriptByteCodeAdapter.castToType(8, String.class), which in turn will call [DefaultTypeTransformation.castToType(Object, Class)|http://git.codehaus.org/gitweb.cgi?p=groovy-git.git;a=blob;f=src/main/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java;h=5ac61d17e08dde0af3fabaf87f528fc202058b3f;hb=refs/heads/master#l183]. 

h3. Casting Rules in General
The following rules apply in the order given.
Casting rules for identity:
* value null stays null
* a cast to Object gives the value itself
* if the value class is equal the casting class, nothing is changed
* if the value class is a subclass of the casting class, nothing is changed

 195         if (ReflectionCache.isArray(type)) {
 196             return asArray(object, type);
 198         }

These cases will cause a new value being created from the old one if the cast type is a Collection:
* a cast to HashSet will create a new HashSet using the HashSet(Collection) constructor
* if the value is an array and the we cast we try to invoke first the parameterless constructor of the cast type, to then add each element of the array using add(Object). Should there be no such constructor or its invocation fail, we fail with a GroovyCastException
* a cast to String will invoke toString() on the value
* a cast to char or Character will invoke the char casting rules
* a cast to Boolean or boolean will invoke the rules for Groovy Truth, see paragraph later
* a cast to Class we will use the toString() representation of value to execute Class.forName with that. The ClassLoader used for this will always be the loader for DefaultTypeTransformation.class. 
* if we cast to a Number or primtive number type we will try to convert value to a number and then use one of the methods to create the kind of value we actually need. Should value not be a number already, but a Character, we use the charValue() for further steps. In case of a GString or String, we use the value of the one element String, if existing. Should it not exist, we fail with a GroovyCastExpression. If value is neither String, GString, Character or a Number, we fail with a GroovyCastExpression. A resulting Double with positive or negative infinity is only allowed, if the original value is already a double value (this may for example fail for the case of a BigDecimal, that does not fit into a Double). If not we fail with a GroovyCastException. BigDecimal and Biginteger are created by the doubleValue() in case we cast from a float or double. In the other cases we use the toString() representation. 
* if we cast a String or GString value to an Enum type, we call Enum#valueOf to get an enum value based on the given string representation of that enum value
* if the value is an Collection, Map or Object[], we try to invoke the cast type constructor. In case of a Object[] or Collect, the elements are used for the arguments. A Map will be given as single argument.
* if we reach this point, the cast fails with a GroovyCastException

h3. Char Casting Rules
* if the value is a Char, nothing is to be done
* if the value is a Number, we use the int value to create a char
* otherwise we execute toString() to get a String and if that String is a one element String, we will use that element to create the char. Is the String longer we fail with a GroovyCastException

h3. Casting Rules for Boolean / Groovy Truth implementation
The implementation of this is determined by calling the asBoolean method of the value. It will realize those cases:
* if value is null: false
* empty Collections, arrays, CharSequence (contains empty String): false
* boolean false: false
* numbers equaling to 0: false
* the char0 : false
* Enumeration that has no more elements: false
* Matcher in which the find method returns false: false
* in the other cases we return true