[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
| Re: [m2t-dev] [backend] bugs and thoughts | 
Arno,
Coments below
Arno Haase a écrit :
Laurent,
First of all, let me apologize, this will be a lengthy mail :D.
not at all - quite the contrary, I found it pretty concise and to the 
point.
1) I found the IfExpression you have defined in the backend for the 
control flow. But there isn't any cases such as "an if with no else" 
(I think such use cases could be  handled by creating an "if" with an 
LiteralExpression containing an empty String as the else, but it 
would be better to have the backend perform this transparently) or 
"if - else if - else". What is the expected way to implement this 
last use case? An "if" with another "if" as the statement when false? 
Or simply using a SwitchExpression that seems to present the right 
behavior?
The backend is thoroughly functional - there are no statements but 
only expressions, which by definition always return a value. So an "if 
without else" makes no sense in this paradigm.
The SequenceExpression serves the purpose of "executing" several 
expressions one after another, statement-like. It treats all 
expressions except the last as "statements", i.e. it discards their 
return values and executes them only to have their side effects.
Ok, didn't understand the purpose of the SequenceExpression correctly 
(hence point 3). As for the "if - else if - else" case, I think the 
correct way of implementing it is indeed to create IfExpression with 
another IfExpression as the "else".
2) This might be the most annoying one I have faced since so hard to 
track down : When using a CompositeTypeSystem, getting functions from 
a java file through MiddleEnd#getFunctions() fails in NPE with the 
root being the first line of JavaBeansType#init() :
  - _superTypes = Collections.singleton 
(ts.getRootTypesystem().findType (_javaClass.getSuperclass()));
It so happens that one of the java class as no superClasses, thus 
_javaClass.getSuperclass() returning null and the execution failing 
later on. I have had to completely comment out all of the lines of 
CompositeTypeSystem that references the field _javaBeansTypesystem in 
order to get rid of the error (I do not need the java beans types for 
my use case ... why didn't you create a typeSystem for java beans as 
you did for EMF, one that can be registered if needed and that isn't 
used otherwise).
I do not understand this yet, but it vaguely sounds as if you are 
using things differently from the way I intended them to be used. If 
you send some code or explain some more, I will be happy to look into 
it and advise you.
The reason why the Java type system is built-in is that Java is the 
only language to actually implement "native" functions, and it is used 
internally. This may not be necessary at some time in the future - but 
that should be transparent, and there should be no situation when it 
actually gets in the way - at least that's what I hope and intend ;-)
Sorry, seems my explanation wasn't clear enough. Here is a very small 
unit test case that should throw the exception I mentionned. Note that 
no exception is thrown when we do not register the EMFTypeSystem ... and 
yet it is no longer thrown when the java beans typesystem is commented 
out. As I said earlier, this exception was a hell to track down in order 
to be able to carry on my testing. As a side note, you might need to 
take a look at the patch I attached on bugzilla 
https://bugs.eclipse.org/bugs/show_bug.cgi?id=225565 for this snippet 
not to fail before even executing this far.
public class MiddleendTest extends TestCase {
  public void testFailure() {
      final CompositeTypesystem typeSystem = new CompositeTypesystem();
      typeSystem.register(new EmfTypesystem());
      final MiddleEnd middleEnd = MiddleEndFactory.create(typeSystem, 
null);
      /*
       * We have no middle end registered for "xfdg" files, this isn't 
an issue here since failure happens before we
       * even search for a middle end while registering types.
       */
      final FunctionDefContext functionContext = 
middleEnd.getFunctions("void.xfdg");
  }
}
3) You have defined a SequenceExpression that is described on the 
wiki ( http://wiki.eclipse.org/M2TBackend ) as being "the subsequent 
execution of several expressions"... yet I am puzzled by its 
implementation :
   protected Object evaluateInternal(ExecutionContext ctx) {
           Object result = null;
           for (ExpressionBase e: _inner)
               result = e.evaluate(ctx);
           return result;
   }
The only result we'll get ... is the result of the very last 
statement's evaluation since "result" is overriden by each new 
evaluation. Is that how it is intended? If yes, is there an 
expression that has been defined to allow us to have the backend 
execute a sequence of expressions (let's say two LiteralExpression) 
and get the result of both evaluations? The use case I have is 
similar to (forgive the errors, I've never used xpand :D) :
   «DEFINE function FOR uml::Element-»
       «IF name != null-»
           Element : «name»
           Element type : «eClass»
       «ENDIF»
And I would like to define the backend expressions needed : an 
IfExpression with a condition, but what can I use as the "then part" 
of this IF if not a SequenceExpression?
In the example you included, the then part is about concatenating the 
results of several expressions into a single string. This is done 
using a system library function - look into the Xpand middleend 
implementation which does just this. This is *not* a situation for a 
SequenceExpression.
(woops) indeed, this can be handled with String concatenation, which 
solves my problem. Your answer to 1) highlighted the accurate purpose of 
the SequenceExpression.
4) On to the Type System. You have provided us the way of defining 
new BackendTypeSystem through the CompositeTypeSystem#register() 
method. In the same fashion, you provided us with a way of defining 
new BackendType and to tell that such or such BackendType is 
assignment compatible with another through 
BackendType#isAssignableFrom(). This is perfect ... but what if I 
tell that RandomType is assignment-compatible with String? The 
backend will fail to invoke functions defined with parameter of type 
"RandomType" with a String parameter since it doesn't know how to 
convert from String to Random.
We then need a way to provide our own converters, the same way you 
did with the JavaBuiltinConverterFactory. What I did for now is 
"hack" my way through by adding a method "addConverter" to the 
JavaBuiltinConverterFactory and providing it with as key the Class on 
which the converter applies, and as value the converter itself 
(extending JavaBuiltinConverter). This doesn't seem like a solution 
that could be applied since all types that need conversion are not 
mandatory java.
I think type conversion should be handled by the BackendTypeSystem 
itself when we provide it. Maybe we could discuss this a little 
further? This truly is a blocking issue if we decide to use the 
backend for our project (hence the "hack" I implemented to carry on 
my tests). Except if I deeply misunderstood something (which can be 
the case :p).
You are right, there is currently no way to register implicit type 
conversions. It is a powerful feature, and I somewhat like the idea of 
adding it, but it is extremely complex to integrate in a conceptually 
clean way.
What you want is different from the JavaBuiltin stuff. The JavaBuiltin 
stuff works only at the fringes of the system, i.e. when Java 
functions are called, becasue there are several integer types in Java 
but they are all represented by java.lang.Long in the backend.
Yes, what I did is but a mere hack to allow me to carry on my testing, I 
am totally aware that this cannot be the good solution. As for the 
example you give, I am happy you brang that out since it is involved in 
a bug I was going to send you. I'll elaborate later in this mail.
You on the other hand probably want implicit type conversions for 
function calls inside the backend (with conversions when a Java 
function is called only as a special case), right?
yup, that's it.
Would you view such assignment compatibility as a part of a type 
definition, introducing static dependencies between the types? Or 
would you see it as something introduced by a certain compilation 
unit, giving it a limited scope?
I must admit I don't get your last suggestion quite well. As for the 
first, this seems promising, something like a "convert" method on the 
interface BackendType which would allow us to tell the backend how to 
effectively convert objects of a given type to (and from) ours would 
likely be enough for most conversion needs.
5) Each of the Expressions provided by the backend need a "SourcePos" 
object which seems to hold information on the source template and the 
line number where we are located. Is this intended for debugguing 
purposes? I couldn't find documentation on these.
SourcePos is a placeholder to support debugging, logging etc. - it is 
currently largely transparent to the backend, and we will see what 
information exactly is needed for this purpose.
6) Last but not least, some languages define functions with varargs. 
This can happen in M2T languages (Acceleo comes in mind) as well as 
in programming languages (Java 5 and function defined as "public void 
varargs(Object... args)"). How are these dealed with by the backend? 
Or if they are not, is there even plans to support such feature?
varargs are at the level of the concrete syntax, not of the backend. 
If you want varargs, just add a List parameter in the backend and 
transform accordingly.
5) and 6), thanks for the clarifications.
That's all there is for this mail, hope you are not sleeping or in 
need of an aspirin by now ;) .
Oh, not at all. I am really glad for your feedback - it helps making 
the backend meaningful for several languages, and your different way 
of thinking and requirements are really interesting.
Have fun
- Arno
I'll now elaborate a little more on the bug I mentionned for 4). I have 
faced problems with most of my function since polymorphic resolution 
simply fails because of the type conversion the backend does. Let's say 
I have defined a function "/public Object getElementAt(List, int)/" and, 
perchance, there is an other function going by the same name in the 
context, which signature is "/public Object getElementAt(List, long)/" 
... what is called if I try to invoke something like a "/new 
InvocationOnWhateverExpression("getElementAt", [Collection, Integer], 
true, sourcePos)/"? Well it will be the function defined with a long! In 
fact the very first will never get called since integer are converted to 
long.
Worse yet, if I define a new "/getElementAt/" function with a type that 
is assignment compatible from Integer ... it will be called instead with 
an integer parameter. The underlying purpose of this argument is ... we 
truly **need** the backend to define BackendTypes for each java 
primitive for it to be usable. I can give an even worse example : try 
and invoke the syslib's function "operatorPlus" with two integer ... and 
the backend will end up invoking String concatenation on them, thus "1 + 
1" resulting in "11".
I wound up defining an IntegerType, the syslib's functions for it as 
well as modifying the LongType to allow assignment-compatibility from 
Integer on it so that I could test the backend further... this will 
obviously be a blocking issue for us, is there any plans from your side 
to create the missing primitives (int, float, char, ...)? I think this 
would also maker the JavaBuiltinConverterFactory obsolete.
I was also wondering ... is there a specific bugzilla for the backend? I 
have created https://bugs.eclipse.org/bugs/show_bug.cgi?id=225565 on 
eclipse bugzilla for Modeling / M2T / core. Should I create new bugs on 
the xpand component instead?
Thanks for your time.
Laurent Goubet
Obeo
begin:vcard
fn:Laurent Goubet
n:Goubet;Laurent
org:<a href="http://www.obeo.fr/">Obeo</a>
email;internet:laurent.goubet@xxxxxxx
url:http://www.obeo.fr
version:2.1
end:vcard