Aspects - get generic type

I have the following line:

var t := Services.FindType("System.Collections.Generic.Dictionary2");`

How do I set the key and valuetype on the dictionary itype?

Tried the whole day I have absolute no clue of how to do this …

Also a few other ones.

  1. In a ForInStatement I have a String value for the For-variable.
    No idea how I can exit (new ExtStatement) this value.

  2. I can get a Field or a local value.
    But I still have no clue of how to get a property value

  3. How do I read a property of a Field value
    Also no clue.

Is there anywhere any documentation available?

Services.CreateGenericInstance(t, key, value);

use new IdentifierValue("forinname");

Same idea really, new IdentifierValue(base, “propname”);

1 Like

That really helped, thank you!

A few other questions:

  1. How do I make a function call?
    Edit: found this one out: new ProcValue(new TypeValue(iType), IMethod, [array of params]);

  2. How do I set the initial value to new? (by example var x: myclass := new MyClass).
    I found the InitialValue property, but I don’t know how to set it.

  3. How do I reference an Indexed property?

Initial value of a field? Just set it to a Value instance like you can do with locals. Like .InitialValue := Value(15);

You want SubArrayValue

Hi Carlo,
Examples with integers are always easy.
I still don’t know how to do it with a reference type; how do I make an instance?

And even that won’t work;
the code:

var TestField2 := aField.Owner.AddField("Key", Services.FindType("System.Int32"), true);
TestField2.InitialValue := Value(15);

decompiles to (C#):

    public static int Key;

No initialization has been done.

Another problem:

new BeginStatement([new LocalVariable(“a”, WeakReferenceType)], stmt.ToArray)

does not write the local.

like .InitialValue := new NewValue(type, args); ?

Hrmm. I think I need to ensure a class constructor really gets generated. something like:
if aType.GetClassConstructor = nil then aType.AddConstructor(true); will work for now; I’ll look into doing this automatically for the future.

did you use ilspy? Most decompilers will kill off unused fields, so it probably works , but if it’s not used you wont’ see it.

I tried to reference it with LocalValue(0), but that did not work.

Reference it by name (IdentifierValue); note that it will only work inside that begin/end, not outside.

You are right.
Decompiled to IL:

        .locals init (
            [0] class [mscorlib]System.WeakReference key,
            [1] string Result

The code:

new AssignmentStatement(new IdentifierValue(“key”), new ProcValue(new TypeValue(aField.Owner), lFindKey, [new ParamValue(0)]))

new IdentifierValue(“key”) is the local variable.

The generated code (IL) from this line is:
IL_0000: ldarg.0
IL_0001: call class [mscorlib]System.WeakReference buildITClasses.testExtension::fTest_GetKeyOnTarget(object)
IL_0006: stloc.0
IL_0007: ldloc.1
IL_0008: ret

In C#:
testExtension.fTest_GetKeyOnTarget(self);

When I use a class variable, it works, but with a local variable, it becomes a function call without assignment

Edit:
The complete code for this:

var lRead := aField.Owner.AddMethod('get_' + aField.Name, aField.Type,  aField.Static);
lRead.Virtual := VirtualMode.None;
lRead.Visibility := Visibility.Protected;

stmt := new System.Collections.Generic.List<Statement>;
stmt.Add(new AssignmentStatement(new IdentifierValue("key"), new ProcValue(new TypeValue(aField.Owner), lFindKey, [new ParamValue(0)])));   

lRead.ReplaceMethodBody(new BeginStatement([new LocalVariable("key", WeakReferenceType)], stmt.ToArray));

Carlo, the complete code with the problems mentioned in it:

namespace builditAspects;

interface

uses 
  System.Linq,
  RemObjects.Elements.Cirrus.*;

type
    [AttributeUsage(AttributeTargets.Field)]
    ExtensionField = public class(Attribute, IFieldInterfaceDecorator)
private
public
    method HandleInterface(Services: IServices; aField: IFieldDefinition);
end;

implementation

method ExtensionField.HandleInterface(Services: IServices; aField: IFieldDefinition);
begin
//Needed in all of the code

    // -> here I am looking for the type where default(Type) is implemented -> this one is incorrect
    var RemObjectsSystemType :=  Services.FindType("RemObjects.Oxygene.System");

    var WeakReferenceType :=  Services.FindType("System.WeakReference");
    var stmt: System.Collections.Generic.List<Statement>;

//Add the private storage dictionary
    var t := Services.FindType("System.Collections.Generic.Dictionary`2");
    var t1 := Services.CreateGenericInstance(t, WeakReferenceType, aField.Type);  //initialize the generic parameters of the dictionary                                        

    var StorageField := aField.Owner.AddField(aField.Name + "_Dict", t1, true);
    StorageField.Visibility := Visibility.Private;

    // -> this part does not work; no assignment is generated (also no error is raised)
    StorageField.InitialValue := new NewValue(t1);
    if aField.Owner.GetClassConstructor = nil then aField.Owner.AddConstructor(true);

//find key method (works 100%)
    var lFindKey := aField.Owner.AddMethod(aField.Name + "_GetKeyOnTarget", WeakReferenceType, true);
    lFindKey.AddParameter("self", ParameterModifier.In, Services.FindType("System.Object"));
    lFindKey.Virtual := VirtualMode.None;
    lFindKey.Visibility := Visibility.Private;

    var body:= new IfStatement(
                    new BinaryValue(new IdentifierValue(new IdentifierValue("k"), "Target"), new ParamValue(0), 
                                    BinaryOperator.Equal), 
                         new ExitStatement(new IdentifierValue("k"))) ;
    stmt := new System.Collections.Generic.List<Statement>;
    stmt.Add(new ForInStatement( "k",                                                           //aName: String;      -> the var for the foreach
                                 WeakReferenceType,                                             //aType: IType;       -> type of the previous one
                                 false,                                                         //aMatching: Boolean; -> use matchin
                                 new IdentifierValue(new FieldValue(nil, StorageField), "Keys"),//aSource: Value;     -> the enummerable
                                 body,                                                          //aBody: Statement;   -> statement in the for
                                 nil,                                                           //aIndexName: String; -> indexname
                                 nil,                                                           //aIndexType: IType;  -> type of the previous, always int32
                                 false                                                          //aParallel: Boolean  -> use a parallel for
                                )
            );                   
    lFindKey.ReplaceMethodBody(new BeginStatement(stmt.ToArray));
       
//private write method for field replacing property 
    var lWrite := aField.Owner.AddMethod('set_' + aField.Name, nil,  aField.Static);
    lWrite.AddParameter('val', ParameterModifier.In, aField.Type);
    lWrite.Virtual := VirtualMode.None;
    lWrite.Visibility := Visibility.Protected;

    // -> not implemented yet as it will get the same problems as the read method
    lWrite.ReplaceMethodBody(new BeginStatement([new LocalVariable("key", WeakReferenceType)],[new ExitStatement]));

//private read method for field replacing property
    var lRead := aField.Owner.AddMethod('get_' + aField.Name, aField.Type,  aField.Static);
    lRead.Virtual := VirtualMode.None;
    lRead.Visibility := Visibility.Protected;

    stmt := new System.Collections.Generic.List<Statement>;

    // -> this part does not work; no assignment is generated on a local variable (also no error is raised)
    stmt.Add(new AssignmentStatement(new IdentifierValue("key"), new ProcValue(new TypeValue(aField.Owner), lFindKey, [new ParamValue(0)])));   

    stmt.Add(new IfStatement(
                    new BinaryValue(new IdentifierValue("key"), new NilValue,    
                                   BinaryOperator.Equal), 
                         // -> cant get the next line to compile because of the indexd property Item[Key]
                         new ExitStatement(new IdentifierValue(new SubArrayValue(new FieldValue(nil, StorageField), "Item"), new IdentifierValue("key"))),
                         // -> problem with the classtype, I can not find the type for Default(Type)
                         new ExitStatement(new ProcValue(new TypeValue(RemObjectsSystemType), "Default", [aField.Type])))) ; 

    lRead.ReplaceMethodBody(new BeginStatement([new LocalVariable("key", WeakReferenceType)], stmt.ToArray));


//the field replacing property - visibility the same as the original field (works 100%)
    var lProp := aField.Owner.AddProperty(aField.Name, aField.Type, aField.Static);
    lProp.ReadMethod := lRead;
    lProp.WriteMethod := lWrite;
    lProp.Visibility := aField.Visibility;

//remove the original field (works 100%)
    aField.Owner.RemoveField(aField);
end;

end.

How do I use this? (Can you send the full project, that’s a lot easier)

Hi Carlo

Hereby the project: AspectTest.zip (198.6 KB)

The aspect is defined in project buildIT Aspects, file ExtensionFields.pas.

The aspect is used in project buildITClasses, file buildIT Strings.pas.

In the project AspectTest, file Main you can see that the new storage is used (does not work yet of course).

Oke so a few things:
new ExitStatement(new IdentifierValue(new SubArrayValue(new FieldValue(nil, StorageField), "Item"), new IdentifierValue("key"))),

should be:
new ExitStatement(new SubArrayValue(new IdentifierValue(new FieldValue(nil, StorageField), “Item”), new IdentifierValue(“key”))),

For this:
new ExitStatement(new ProcValue(new TypeValue(RemObjectsSystemType), "Default", [aField.Type])))) ;

you want
new ExitStatement(new DefaultValue(aField.Type))

Two other things:

  • the property get/set needs to be public, not protected (normally the compiler does it for you, but cirrus is fairly low level)
  • the property getter/setters need an extra parameter:
    lWrite.AddParameter('self', ParameterModifier.In, aField.Type).Prefix := '$mapped';

Then it seems to work:

{
	WeakReference key = Test_GetKeyOnTarget(self);
	if (key == null)
	{
		return Test_Dict[key];
	}
	return null;
}

1 Like

Thanks Carlo,
It all works now!

2 Likes