jso0 / runsharp

Automatically exported from code.google.com/p/runsharp
MIT License
2 stars 0 forks source link

Exp.New(ValueT) doesn't work if ValueT is a value type #16

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Calling Exp.New(value type) causes the exception "Cannot find constructor" 
because Type.GetConstructors doesn't return the default constructor of value 
types. I gather that the constructor does not actually exist, instead one is 
supposed to use the initobj opcode to default-initialize a value type.

I am not sure how to fix the code. I was thinking of doing "new NewObject(null, 
new Operand[0])" in this case, and then changing NewObject to use 
Opcodes.Initobj in this case, but Initobj requires the address of an object to 
be on the evaluation stack and I don't know what to take the address of.

Version: runsharp-0.1.2-pre-alpha-src.zip

Original issue reported on code.google.com by qwertie...@gmail.com on 16 Aug 2010 at 10:45

GoogleCodeExporter commented 9 years ago
Workaround: create a local variable and initialize it with CodeGen.InitObj(), 
e.g.

if (T.IsValueType) {
    @object = g.Local(T);
    g.InitObj(@object);
} else {
    @object = g.Local(Exp.New(T));
}

Original comment by qwertie...@gmail.com on 16 Aug 2010 at 11:04

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Hi,

It's more or less exactly as you say - there is no 'default' constructor for 
value types, because initializing them this way would be inefficient. You are 
also right regarding the 'workaround', except I don't think it's a workaround, 
but rather that it's the way to do it correctly.

The reason for this is probably the fact, that while it is meaningful to 
initialize a value type in a separate space on the stack and then copy it into 
the storage variable (the newobj case) - to avoid partially initialized value 
types, there is no such point in creating a zero-filled area on the stack and 
then copying this to the variable, which is why initobj operates on the storage 
area directly.

If you look at the code generated by the C# compiler, it works pretty much like 
this, except that under some circumstances it even avoids the newobj in favor 
of a call of the constructor directly on the storage variable (a third option 
for value type initialization).

In short - there is no way to create a zero-initialized value type on stack, 
except by providing a temporary local variable and throwing initobj at it. I'm 
also not really convinced that it's usefull to have Exp.New emulate this 
behavior the way C# does, but if you have a valid use case, please let me know 
and maybe I'll change my mind :) After all, this all comes down to the lack of 
documentation (Issue 1).

Best Regards,
Stefan

Original comment by StefanSi...@gmail.com on 17 Aug 2010 at 6:55

GoogleCodeExporter commented 9 years ago
Well, it seemed to me that Run#'s API was trying hard to look like C#, with 
"Public.Static.Class", For/ForEach loops and so on. An this is nice, it means I 
can do metaprogramming without knowing CIL intimately. C# (and VB too?) nicely 
harmonizes structs and classes so that you can always use "new T(...)" whether 
T is a struct or class; I think having this feature in Run# would be a good 
thing. In any case, I agree, documentation would help.

By the way, this is unrelated but I have repeatedly made the mistake of writing 
code like

obj.Invoke("Func", args);

instead of 

g.Invoke(obj, "Func", args);

The first form is required only for a subexpression, and the second only for a 
standalone statement. Unfortunately, the first form compiles fine but doesn't 
work. 

It makes the API a bit confusing that two forms are required. I would prefer to 
use only the first form and then pass it to g to execute, e.g.

g.Do(obj.Invoke("Func", args));

But I don't see a method in g for doing this. Did I miss something?

Original comment by qwertie...@gmail.com on 17 Aug 2010 at 2:39

GoogleCodeExporter commented 9 years ago
Hi,

The only problem with the New() is that C# avoids creating a temporary local 
variable and copying it's contents, which is not all that easy to do with 
RunSharp - though it quite likely doesn't affect performance in any meaningful 
way (after all, there are worse things in the generated code than that ;)

As for the Invoke, this is a very interesting idea and I'll definitely look 
into it.

Regards,
Stefan

Original comment by StefanSi...@gmail.com on 17 Aug 2010 at 3:13

GoogleCodeExporter commented 9 years ago
Even if New() has to create a temporary local variable, I think that would be 
better than throwing an exception.

Original comment by qwertie...@gmail.com on 18 Aug 2010 at 8:34

GoogleCodeExporter commented 9 years ago
Sure, will fix it.

Original comment by StefanSi...@gmail.com on 20 Aug 2010 at 8:52