Esempio n. 1
0
    void ProcessMessage(TypeDefinition type)
    {
        var typeName          = type.PackageName();
        var result            = new MessageNode(typeName);
        var defaults          = new Dictionary <string, string>();
        var deserializeWalker = new MethodWalker(type.Methods.First(m =>
                                                                    m.Name == "Deserialize" && m.Parameters.Count == 3));

        deserializeWalker.OnCall = info => {
            if (info.Conditions.Count == 0 && info.Method.Name.StartsWith("set_"))
            {
                var fieldName = info.Method.Name.Substring(4).ToLowerUnder();
                var val       = info.Arguments[1].ToString();
                if (val.EndsWith("String::Empty"))
                {
                    val = "\"\"";
                }
                else if (info.Arguments[1].GetType() == typeof(string))
                {
                    val = "\"" + val.Replace("\"", "\\\"") + "\"";
                }
                if (info.Method.Parameters.First().ParameterType.Name == "Boolean")
                {
                    val = val == "0" ? "false" : "true";
                }
                defaults[fieldName] = val;
            }
        };
        deserializeWalker.Walk();

        var written         = new List <byte>();
        var serializeWalker = new MethodWalker(type.Methods.First(m =>
                                                                  m.Name == "Serialize" && m.Parameters.Count == 2));

        serializeWalker.OnCall = info => {
            if (info.Method.Name == "WriteByte")
            {
                written.Add((byte)(int)info.Arguments[1]);
                return;
            }
            if (info.Arguments.Any(x => x.ToString().Contains("GetSerializedSize()")))
            {
                return;
            }
            if (!info.Method.Name.StartsWith("Write") && info.Method.Name != "Serialize")
            {
                return;
            }

            // !!! packed vs not packed:
            // bnet.protocol.channel_invitation.IncrementChannelCountResponse/reservation_tokens: *not* packed
            // PegasusGame.ChooseEntities/entities: *packed*
            // not packed = {{tag, data}, {tag, data}, ...}
            // packed = {tag, size, data}
            // repeated fixed fields are packed by default.
            //
            // not packed:
            //   call: ProtocolParser.WriteUInt64(arg0, V_0)
            //   conditions: arg1.get_ReservationTokens().get_Count() > 0, &V_1.MoveNext() == true
            //
            // packed:
            //   call: ProtocolParser.WriteUInt32(arg0, V_0) // size
            //   conditions: arg1.get_Entities().get_Count() > 0, &V_2.MoveNext() == false
            //   call: ProtocolParser.WriteUInt64(arg0, V_3) // datum
            //   conditions: arg1.get_Entities().get_Count() > 0, &V_2.MoveNext() == false, &V_4.MoveNext() == true
            var iterConds = info.Conditions.Where(x => x.Lhs.Contains("MoveNext"));
            var listConds = info.Conditions.Where(x => x.Lhs.Contains("().get_Count()"));
            if (listConds.Any() && !iterConds.Any(
                    x => x.Cmp == MethodWalker.Comparison.IsTrue))
            {
                // Skip packed size writes:
                return;
            }
            var packed = iterConds.Any(x => x.Cmp == MethodWalker.Comparison.IsFalse);

            var label = FieldLabel.Invalid;
            if (iterConds.Any())
            {
                label = FieldLabel.Repeated;
            }
            else if (info.Conditions.Any(x => x.Lhs.Contains(".Has")))
            {
                label = FieldLabel.Optional;
            }
            else
            {
                label = FieldLabel.Required;
            }

            // Get name:
            var name = "";
            if (label == FieldLabel.Repeated)
            {
                name = info.Conditions.First(x => x.Lhs.Contains("get_Count()")).Lhs;
                name = name.Substring(name.IndexOf(".get_") + 5);
                name = name.Substring(0, name.Length - 14);
            }
            else
            {
                name = info.Arguments[1].ToString();
                if (name.StartsWith("Encoding.get_UTF8()"))
                {
                    name = name.Substring(31, name.Length - 32);
                }
                name = name.Substring(name.IndexOf(".get_") + 5);
                name = name.Substring(0, name.Length - 2);
            }
            var prop = type.Properties.First(x => x.Name == name);
            name = name.ToLowerUnder();

            // Pop tag:
            var tag = 0;
            var i   = 0;
            while (true)
            {
                var b = written[i];
                tag |= (b & 0x7f) << (7 * i);
                i   += 1;
                if (0 == (b & 0x80))
                {
                    break;
                }
            }
            if (i != written.Count)
            {
                throw new InvalidProgramException(
                          "bad tag bytes, not gonna recover from this state");
            }
            written.Clear();
            tag >>= 3;

            // Parse field type:
            var fieldType = FieldType.Invalid;
            var subType   = default(TypeName);
            if (prop.PropertyType.Resolve().IsEnum)
            {
                fieldType = FieldType.Enum;
                var enumType = prop.PropertyType;
                enumTypes.Add(enumType.Resolve());
                subType   = enumType.PackageName();
                fieldType = FieldType.Enum;
                if (defaults.ContainsKey(name))
                {
                    var intVal = Int32.Parse(defaults[name]);
                    defaults[name] = enumType.Resolve().Fields
                                     .First(x => x.HasConstant && intVal == (int)x.Constant)
                                     .Name;
                }
            }
            else if (info.Method.Name == "Serialize")
            {
                var messageType = info.Method.DeclaringType;
                subType   = messageType.PackageName();
                fieldType = FieldType.Message;
            }
            else if (info.Method.DeclaringType.Name == "ProtocolParser")
            {
                var innerType = prop.PropertyType;
                if (innerType.IsGenericInstance)
                {
                    innerType = (innerType as GenericInstanceType).GenericArguments.First();
                }
                switch (innerType.Name)
                {
                // Int32, Int64,
                // UInt32, UInt64,
                // Bool, String, Bytes
                case "Int32":
                    fieldType = FieldType.Int32;
                    break;

                case "Int64":
                    fieldType = FieldType.Int64;
                    break;

                case "UInt32":
                    fieldType = FieldType.UInt32;
                    break;

                case "UInt64":
                    fieldType = FieldType.UInt64;
                    break;

                case "Boolean":
                    fieldType = FieldType.Bool;
                    break;

                case "String":
                    fieldType = FieldType.String;
                    break;

                case "Byte[]":
                    fieldType = FieldType.Bytes;
                    break;

                default:
                    Console.WriteLine("unresolved type");
                    break;
                }
            }
            else if (info.Method.DeclaringType.Name == "BinaryWriter")
            {
                // Double, Float,
                // Fixed32, Fixed64,
                // SFixed32, SFixed64,
                switch (info.Method.Parameters.First().ParameterType.Name)
                {
                case "Double":
                    fieldType = FieldType.Double;
                    break;

                case "Single":
                    fieldType = FieldType.Float;
                    break;

                case "UInt32":
                    fieldType = FieldType.Fixed32;
                    break;

                case "UInt64":
                    fieldType = FieldType.Fixed64;
                    break;

                default:
                    Console.WriteLine("unresolved type");
                    break;
                }
            }
            if (fieldType == FieldType.Invalid)
            {
                Console.WriteLine("unresolved type");
            }

            var field = new FieldNode(name, label, fieldType, tag);
            field.TypeName = subType;
            field.Packed   = packed;
            if (defaults.ContainsKey(name))
            {
                field.DefaultValue = defaults[name];
            }
            result.Fields.Add(field);
        };
        serializeWalker.Walk();

        AddPackageNode(typeName.Package, result);
    }
Esempio n. 2
0
    void ProcessService(TypeDefinition type)
    {
        ServiceNode service           = null;
        var         retType           = default(TypeName);
        var         constructorWalker = new MethodWalker(type.Methods.First(m =>
                                                                            m.IsConstructor && !m.HasParameters));

        constructorWalker.OnCall = info => {
            var methodDef = info.Method.Resolve();
            if (methodDef.IsConstructor)
            {
                var declType = methodDef.DeclaringType;
                if (declType == type.BaseType)
                {
                    // Base class constructor invocation.
                    var fullName    = info.Arguments[1] as string;
                    var i           = fullName.LastIndexOf('.');
                    var serviceType = new TypeName(fullName.Substring(0, i), fullName.Substring(i + 1));
                    service = new ServiceNode(serviceType);
                }
                else if (declType.FullName == "MethodDescriptor/ParseMethod")
                {
                    var funcPtr  = info.Arguments[2] as string;
                    var start    = funcPtr.IndexOf('<') + 1;
                    var end      = funcPtr.IndexOf('>');
                    var fullName = funcPtr.Substring(start, end - start);
                    if (!retType.Equals(default(TypeName)))
                    {
                        Console.WriteLine("Discarding RPC return type: " + retType.Name);
                    }
                    var i = fullName.LastIndexOf('.');
                    retType = new TypeName(fullName.Substring(0, i), fullName.Substring(i + 1));
                }
                else if (declType.FullName == "MethodDescriptor")
                {
                    if (retType.Equals(default(TypeName)))
                    {
                        Console.WriteLine("Missing RPC return type");
                    }
                    else
                    {
                        var fullMethodName = info.Arguments[1] as string;
                        var methodName     = fullMethodName.Substring(fullMethodName.LastIndexOf('.') + 1);
                        var argType        = new TypeName("unknown", "Unknown");
                        var rpc            = new RPCNode(methodName, argType, retType);
                        rpc.Options.Add("(method_id)", info.Arguments[2].ToString());
                        if (service != null)
                        {
                            service.Methods.Add(rpc);
                        }
                        retType = default(TypeName);
                    }
                }
            }
        };
        constructorWalker.Walk();

        // TODO: Extract argument types.
        //       This will require analysis of senders, handlers and/or handler registration.

        if (service == null)
        {
            Console.WriteLine("Failed to extract protobuf name for service class: " + type.FullName);
        }
        else
        {
            AddPackageNode(service.Name.Package, service);
        }
    }