What is the best way for creating a TypeReference from a previously defined TypeDefinition?

1,439 views
Skip to first unread message

karl patrik johansson

unread,
Jan 20, 2014, 6:04:04 AM1/20/14
to mono-...@googlegroups.com
Hi!

I must start with to say, thank you for a quick reply when I previously E-Mailed you, JB Evains!

To make it easier, i will paste some of the mail I sent JB Evains earlier.

----

I'm trying to create an application to translate Bethesda's Skyrim Papyrus script into CLR and back. Enabling people to write all scripts using C# or any other .NET language; build it using visual studio and then automatically converts the CIL from the built .dll into Papyrus Assembly code. So far this works for very simple .NET applications. It even builds the output Papyrus Assembly into a compiled script that works in Skyrim.

Now for my main issue. I'm also trying to do it the other way around. And that is to read a Papyrus Assembly Code and generate a .NET library so that the users can reference to any existing scripts from C#.

I'm able to generate a .dll after reading a Papyrus Assembly file but having issues wheb it comes to the  TypeReference object. Since Papyrus is a Object Oriented Script Language it contains stuff as extending a script from another (BaseTypes) using other scripts as variables, parameters and ReturnTypes for methods.

But the issue is that when I'm trying to add a basetype to a defined TypeDefinition using a TypeReference, or when trying to add a TypeReference to a parameter or as a ReturnType of a method, i'm able to write the binary but when i try to reference to the written binary from a new .NET project, all the objects gives out weird information in the intellisense of visual studio.

Looks like:
PapyrusDotNet.Core.Actor+ PapyrusDotNet.Core.ObjectReference/Form

And doesnt show any information about the parameters or the method name.

What would be the best way to define your own TypeReference when you are trying to Reference to a Type that you have created inside the same Module. Basically, best way to get a TypeReference from a TypeDefinition?


Now, for some code, this is how i create my TypeDefinitions (Remember, the original scripts are referencing to eachother but at the same time i'm only adding them one after another, which I know that right now gave me a small headache as it may happen that one of the scripts parsed/ and added as a TypeDefinition to my output .dll are referencing to other objects that not yet have been added. I tried solving this by going through all those references after all objects have been added and updating any references that did not work during the parsing)

public static TypeDefinition TypeDefinitionFromPapyrus(PapyrusAsmObject input)
{
var newType = new TypeDefinition("PapyrusDotNet.Core", input.Name, TypeAttributes.Class);
newType.IsPublic = true;

if (!string.IsNullOrEmpty(input.ExtendsName))
{
newType.BaseType = new TypeReference("PapyrusDotNet.Core", input.ExtendsName, MainModule, MainModule);
newType.DeclaringType = MainModule.Types.FirstOrDefault(t => t.FullName == newType.BaseType.FullName);
newType.Scope = MainModule;
}

foreach (var prop in input.PropertyTable)
{
var typeRef = GetTypeReference(null, prop.Type);
var pro = new PropertyDefinition(prop.Name, PropertyAttributes.HasDefault, typeRef);
newType.Properties.Add(pro);
}

AddEmptyConstructor(newType);

foreach (var state in input.States)
{
TypeReference typeRef = GetTypeReference(newType);
// var typeRef = MainModule.TypeSystem.Void;

var function = new MethodDefinition(state.Name, MethodAttributes.Public, typeRef);
function.IsStatic = state.IsStatic;


foreach (var par in state.Params)
{
TypeReference typeRefp = GetTypeReference(null, par.Type);
// var typeRefp = MainModule.TypeSystem.Object;

var nPar = new ParameterDefinition(par.Name, ParameterAttributes.None, typeRefp);
function.Parameters.Add(nPar);
}
bool skipAdd = false;
foreach (var m in newType.Methods)
{
if (m.Name == function.Name)
{
if (m.Parameters.Count == function.Parameters.Count)
{
skipAdd = true;
for (int pi = 0; pi < m.Parameters.Count; pi++)
{
if (m.Parameters[pi].ParameterType.FullName != function.Parameters[pi].ParameterType.FullName) skipAdd = false;
}
break;
}
}
}
if (!skipAdd)
newType.Methods.Add(function);
}
return newType;
}

public static void AddEmptyConstructor(TypeDefinition type)
{
var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
var method = new MethodDefinition(".ctor", methodAttributes, MainModule.TypeSystem.Void);
/*method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, baseEmptyConstructor));
*/
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
type.Methods.Add(method);
}

private static TypeReference GetTypeReference(TypeDefinition newType, string fallback = null)
{
var typeName = "";
if (!string.IsNullOrEmpty(fallback))
typeName = fallback;
else
typeName = newType.FullName;

var ns = GetTypeNamespace(typeName);
var tn = GetTypeName(typeName);

if (ns == "System")
{
switch (tn.ToLower())
{
case "none":
case "void":
return MainModule.TypeSystem.Void;
case "int":
return MainModule.TypeSystem.Int32;
case "string":
return MainModule.TypeSystem.String;
case "float":
return MainModule.TypeSystem.Double;
case "bool":
case "boolean":
return MainModule.TypeSystem.Boolean;
default:
return MainModule.TypeSystem.Object;
}
}
var typeRef = new TypeReference(ns, tn, MainModule, MainModule);
typeRef.DeclaringType = MainModule.Types.FirstOrDefault(t => t.FullName == typeName);
typeRef.Scope = MainModule;
return typeRef;
}



private static string GetTypeName(string p)
{
if (p.Contains('.')) p = p.Split('.').LastOrDefault();
var pl = p.ToLower();

if (pl == "boolean") 
return "bool";
if (pl == "none")
return "void";
if (pl == "float" || pl == "int" || pl == "bool" || pl == "string") return pl;

return p;
}

private static string GetTypeNamespace(string p)
{
if (p.Contains('.')) p = p.Split('.').LastOrDefault();
var pl = p.ToLower();
/* have not added all possible types yet though.. might be a better way of doing it. */
if (pl == "string" || pl == "int" || pl == "boolean" || pl == "bool" || pl == "none"
  || pl == "void" || pl == "float" || pl == "short" || pl == "char" || pl == "double"
  || pl == "int32" || pl == "integer32" || pl == "long" || pl == "uint")
{
return "System";
}
return "PapyrusDotNet.Core";
}



I'm sorry for the lack of comments in my code...
But as I said before, i'm able to build a .dll containing the classes that I was looking for, except... When I'm opening up the .dll file in, lets say .NET Reflector 


The classes looks like following:

namespace PapyrusDotNet.Core
{
    using System;

    public class Action : Form
    {
        public Form.Action.Form/Action GetState();
        public Form.Action.Form/Action GotoState(string newState);
    }
}

Clicking on 'Form.Action.Form/Action' would give me an error as 'PapyrusDotNet.Core.Form+Action+Form/Action' is not loaded or may be hidden due to your visbility settings.

And when referencing to this dll in visual studio, lets say I have a method called: 

public ObjectReference OnSell(Actor actor)

but in Intellisense i get something as following for it:

(PapyrusDotNet.Core.actor akSeller):PapyrusDotNet.Core.Form+PapyrusDotNet.Core.ObjectReference+PapyrusDotNet.Core.Form/ObjectReference

When i'm expecting to see:

(Actor akSeller):ObjectReference



Once again, I have to apologize for this long post! I hope it made any sense to you guys, in general what i'm trying to do is to create an assembly using mono.cecil, within this assembly i define new Types and those types may be extending some of eachother, so one type that ive recently added to the assembly might be extending another/new type that is being added later in the process. These types are in turn used as parameters, method returnTypes and variables.

Best Regards,
Karl Johansson

karl patrik johansson

unread,
Jan 20, 2014, 6:39:04 AM1/20/14
to mono-...@googlegroups.com
I just wanted to give a quick update to the code, i found a problem when getting the returnType for a method inside the TypeDefinitionFromPapyrus function:

and it should look as following:
(it does atleast give me the correct returnTypes, altough the same problem persists)

public static TypeDefinition TypeDefinitionFromPapyrus(PapyrusAsmObject input)
{
var newType = new TypeDefinition("PapyrusDotNet.Core", input.Name, TypeAttributes.Class);
newType.IsPublic = true;

if (!string.IsNullOrEmpty(input.ExtendsName))
{
newType.BaseType = new TypeReference("PapyrusDotNet.Core", input.ExtendsName, MainModule, MainModule);
newType.DeclaringType = MainModule.Types.FirstOrDefault(t => t.FullName == newType.BaseType.FullName);
newType.Scope = MainModule;
}

foreach (var prop in input.PropertyTable)
{
var typeRef = GetTypeReference(null, prop.Type);
var pro = new PropertyDefinition(prop.Name, PropertyAttributes.HasDefault, typeRef);
newType.Properties.Add(pro);
}

AddEmptyConstructor(newType);

foreach (var state in input.States)
{
TypeReference typeRef = GetTypeReference(null, state.ReturnType);

karl patrik johansson

unread,
Jan 20, 2014, 8:31:04 AM1/20/14
to mono-...@googlegroups.com
Without any further due, I was finally able to resolve this issue. It seem like you get these kind of issues when you set a DeclaringType on a TypeReference, pointing to a TypeDefinition, just leaving the default value as it is worked.

I previously set the DeclaringType because Mono.Cecil threw me an exception on AssemblyDefinition.Write, as long as i'm setting a scope for the TypeReference it will go smoothly. Which I didnt know when I first started.

Jb Evain

unread,
Jan 20, 2014, 8:56:10 AM1/20/14
to mono-...@googlegroups.com
Well seems like you're all set :)

When you create a new TypeDefinition, the best thing to do is to
immediately add it to a Module, this way it gets attached directly.
> --
> --
> --
> mono-cecil
> ---
> You received this message because you are subscribed to the Google Groups
> "mono-cecil" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to mono-cecil+...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
Reply all
Reply to author
Forward
0 new messages