Eclipse Xbase Model Inferrer

On a previous page we discussed the code generator for a general grammar, on this page we discuss how code is generated for xbase.

Xbase Compiler

Here is a quick overview from the language implementers point of view. The Model Inferrer maps the grammar to the generated java code.

In the example below it is the code on the bottom right.

model inferrer

Compiler

Lets look, in more detail, at how we generate code from our model. In a non-xbase situation the code is generated directly by the user source code. With xbase there is much more indirection and we don't specify the code directly. It can be much more difficult to see whats going on so lets try to investigate.

In our project, in subdirectory 'jvmmodel', we create a class called say: MyJvmModelInferrer which extends AbstractModelInferrer. I have put my full code on github here but for now lets loot at the bit that generates the class:

I have hi-lighted the 'toClass' method in red.

How does this get all the information about the class and put it all together to generate the code?

The code, inside the closure (in the square brackets - which extends JvmGenericType), sets various variables such as:

  • documentation
  • superTypes
  • members

Are there more variables needed to specify options of the class? Where are these options defined? How are they used?

  /**
   * pck = package name (qualified name)
   */
  def void buildClass(IJvmDeclaredTypeAcceptor acceptor,
  	                 EuclidClass ec,String pck){
  	var String qualifiedName = ec.name //ec.fullyQualifiedName.lastSegment
  	if (pck != null){
  	  qualifiedName = pck + "." + ec.name //ec.fullyQualifiedName.lastSegment
  	}
    acceptor.accept(ec.toClass(qualifiedName)).initializeLater [
      documentation = ec.documentation
      var JvmParameterizedTypeReference ext = ec.getExtends()
      if (ext!=null && superTypes!=null) {
        superTypes += ext.cloneWithProxies
      }
      var EList imps = ec.getImplements()
      for (imp:imps){
      	superTypes += imp.cloneWithProxies
      }
      for (methodElement : ec.members) {
        if (methodElement instanceof EuclidFunction) {
          val EuclidFunction me=methodElement as EuclidFunction
          members += buildMethod(me)
        }
        if (methodElement instanceof EuclidConstructor) {
          val EuclidConstructor me=methodElement as EuclidConstructor
          members += buildConstructor(me)
        }
        if (methodElement instanceof EuclidField) {
          val EuclidField fe=methodElement as EuclidField
          members += buildField(fe)
        }
        if (methodElement instanceof EuclidInnerClass) {
          val EuclidInnerClass ic=methodElement as EuclidInnerClass
          members += buildInnerClass(ic,pck)
        }
       }
     ]
    }

In order to attempt to understand these issues lets look at the toClass method below (on the left). We still have not got to the part that actually generates the code. There is yet another indirection to the JvmModelGenerator.xtend method before any code is generated (below right).

The code for 'class' is declared by 'toClass' which is in 'jvmmodel/JvmTypesBuilder' . The code for 'class' is generated by 'generateBody' which is in 'compiler/JvmModelGenerator.xtend' .
	/**
	 * Creates a public class declaration, associated to
 the given sourceElement. It sets the given name,
which might be
	 * fully qualified using the standard Java notation.
	 * 
	 * @param sourceElement
	 *            the sourceElement the resulting element 
is associated with.
	 * @param name
	 *            the qualified name of the resulting class.
	 * @param initializer
	 *            the initializer to apply on the created class
 element. If null, the class won't be initialized.
	 * 
	 * @return a {@link JvmGenericType} representing
 a Java class of the given name, null 
	 *            if sourceElement or name are null.
	 */
	@Nullable 
	public JvmGenericType toClass
       (@Nullable EObject sourceElement,
        @Nullable String name,
        @Nullable Procedure1
                <? super JvmGenericType> initializer) {
		final JvmGenericType result = 
           createJvmGenericType(sourceElement, name);
		if (result == null)
			return null;
		associate(sourceElement, result);
		return initializeSafely(result, initializer);
	}
build class

So how does all this indirection work?

Where do parameters like the following come from?

Need to understand openScope and closeScope.

I think the reason for all this complexity is that we want this to fit into an ecore model of the JVM which is in : org.eclipse.xtext.common.types.

jvmgenerictypejvmgenerictype

So it appears that a 'class' is represented in the model by JvmGenericType which has elements:

  • getExtendedInterfaces
  • getExtendedClass
  • isInstantiateable
  • getDeclaredConstructors
  • interface

It also inherits from JvmDeclaredType which has elements:

  • superTypes
  • members
  • abstract
  • static
  • final
  • packageName

We might also have:

  • declaringType
  • visibility
  • simpleName
  • identifier

inherited from JvmMember.

So now we know where these elements in our 'toClass' closure come from but it is still far from clear to me how all these things are linked up? It all appears very messy to me.

jvmdeclairedtype
  jvmdeclairedtype

 

AbstractModelInferrer

We extend AbstractModelInferrer which has one method: infer

Xbase model

see this page

xbase model xbase model

Type Model

see this page

Below is an attempt to redraw the above common.types model in a more readable format, but it is only a start.

type model

type model part 2

type model part 3

methods of JvmTypesBuilder

Here are some methods of JvmTypesBuilder that generate code.

method returns elements  
toClass JvmGenericType
  • getExtendedInterfaces
  • getExtendedClass
  • isInstantiateable
  • getDeclaredConstructors
  • interface

The following are inherited from JvmDeclaredType

  • superTypes
  • members
  • abstract
  • static
  • final
  • packageName
  • declaringType
  • visibility
  • simpleName
  • identifier

The following are inherited from JvmTypeParameterDeclarator

  • typeParameters
 
toInterface  
toAnnotationType JvmAnnotationType

extends JvmDeclaredType

  • getDeclaredOperations
  • getDeclaredFields
  • findAllFeaturesByName
  • simpleName
  • getAllFeatures
  • superTypes
  • members
  • abstract
  • static
  • final
  • packageName
  • declaringType
  • visibility
  • identifier
 
toEnumerationType JvmEnumerationType
  • literals

plus JvmDeclaredType (see above)

 
toEnumerationLiteral JvmEnumerationLiteral
  • getEnumType

plus JvmField (see below)

 
toField JvmField
  • static
  • final
  • type

Plus JvmFeature

  • isStatic
 
toSetter JvmOperation
  • static
  • final
  • abstract
  • returnType
  • defaultValue
 
toGetter  
toMethod  
toToStringMethod  
toHashCodeMethod  
toEqualsMethod  
toParameter JvmFormalParameter
  • name
  • parameterType
 
toConstructor JvmConstructor

extends JvmExecutable

  • parameters
  • exceptions
  • varArgs
 
toAnnotation JvmAnnotationReference
  • annotations
 
cloneAndAssociate <T extends EObject>T    
cloneWithProxies JvmTypeReference
  • getType
  • getIdentifier
  • documentation
  • getQualifiedName
  • innerClassDelimiter
  • accept
  • parameter
 
newObjectReference  
newTypeRef  
addArrayTypeDimension  

parametrers of JvmTypesBuilder

get and set:

set only:

JvmDeclaredType

set/get parameters:

EList getSuperTypes();

EList getMembers();

boolean isAbstract();

void setAbstract(boolean value);

boolean isStatic();

void setStatic(boolean value);

boolean isFinal();

void setFinal(boolean value);

String getPackageName();

void setPackageName(String value);

Iterable getDeclaredOperations();

Iterable getDeclaredFields();

Iterable findAllFeaturesByName(String simpleName);

Iterable getAllFeatures();

 


metadata block
see also:

 

Correspondence about this page

This site may have errors. Don't use for critical systems.

Copyright (c) 1998-2023 Martin John Baker - All rights reserved - privacy policy.