1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.IO; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Exceptions; 13 using Antmicro.Renode.Peripherals; 14 using Antmicro.Renode.Utilities; 15 using System.Collections.Generic; 16 using System.Reflection; 17 using System.Linq; 18 using System.Text; 19 using System.Runtime.Serialization; 20 using Dynamitey; 21 using Microsoft.CSharp.RuntimeBinder; 22 23 //HACK!Type.IsPrimitive/IsEnum is used in some test due to the bug in mono 3.2.0. 24 //It doesn't allow to compare dynamic containing a primitive with null. 25 //This is fixed at least in 3.4.1. Remove this test when 3.2.0 gets obsolete. 26 //Oh, and it crashes for nullables too. Doh. 27 28 namespace Antmicro.Renode.Config.Devices 29 { 30 public class DevicesConfig 31 { 32 private readonly Dictionary<string, List<DeviceInfo>> groups = new Dictionary<string, List<DeviceInfo>>(); 33 private Dictionary<KeyValuePair<string, dynamic>, string> deferred = new Dictionary<KeyValuePair<string, dynamic>, string>(); 34 private List<DeviceInfo> deviceList = new List<DeviceInfo>(); 35 private static string DefaultNamespace = "Antmicro.Renode.Peripherals."; 36 37 public List<DeviceInfo> DeviceList{ get { return deviceList; } } 38 FailDevice(string deviceName, string field = null, Exception e = null)39 private static void FailDevice(string deviceName, string field = null, Exception e = null) 40 { 41 var msg = new StringBuilder(String.Format("Could not create peripheral from node {0}", deviceName)); 42 if(field != null) 43 { 44 msg.Append(String.Format(" in section {0}.", field)); 45 } 46 47 if(e != null) 48 { 49 msg.Append(" Exception message: "); 50 if(!(e is TargetInvocationException)) 51 { 52 msg.Append(String.Format("{0}. ", e.Message)); 53 } 54 if(e.InnerException != null) 55 { 56 msg.Append(String.Format("{0}. ", e.InnerException.Message)); 57 } 58 if(!(e is RecoverableException)) 59 { 60 throw new InvalidOperationException(msg.ToString()); 61 } 62 } 63 throw new RecoverableException(msg.ToString()); 64 } 65 GetDeviceTypeFromName(string typeName)66 private static Type GetDeviceTypeFromName(string typeName) 67 { 68 var extendedTypeName = typeName.StartsWith(DefaultNamespace, StringComparison.Ordinal) ? typeName : DefaultNamespace + typeName; 69 70 var manager = TypeManager.Instance; 71 return manager.TryGetTypeByName(typeName) ?? manager.TryGetTypeByName(extendedTypeName); 72 } 73 InitializeDevice(KeyValuePair<string, dynamic> description, string groupName = null)74 private bool InitializeDevice(KeyValuePair<string, dynamic> description, string groupName = null) 75 { 76 if(description.Value is JsonArray) 77 { 78 if(!groups.ContainsKey(description.Key)) 79 { 80 groups.Add(description.Key, new List<DeviceInfo>()); 81 } 82 var x = groups[description.Key]; 83 84 var any = false; 85 foreach(var element in description.Value) 86 { 87 var dev = InitializeSingleDevice(new KeyValuePair<string, dynamic>(element.Keys[0], element.Values[0]), description.Key); 88 if(dev != null) 89 { 90 deviceList.Add(dev); 91 x.Add(dev); 92 any = true; 93 } 94 } 95 96 return any; 97 } 98 else if(description.Value is JsonObject) 99 { 100 var dev = InitializeSingleDevice(description); 101 if(dev != null) 102 { 103 deviceList.Add(dev); 104 if(groupName != null) 105 { 106 if(!groups.ContainsKey(groupName)) 107 { 108 groups.Add(groupName, new List<DeviceInfo>()); 109 } 110 groups[groupName].Add(dev); 111 } 112 return true; 113 } 114 } 115 else 116 { 117 FailDevice(description.Key); 118 } 119 120 return false; 121 } 122 123 /// Required/possible nodes: 124 /// _type 125 /// _irq/_gpio - optional 126 /// _connection - optional? 127 /// ctorParam 128 /// PropertyWithSetter InitializeSingleDevice(KeyValuePair<string, dynamic> device, string groupName = null)129 private DeviceInfo InitializeSingleDevice(KeyValuePair<string, dynamic> device, string groupName = null) 130 { 131 var info = new DeviceInfo(); 132 info.Name = device.Key; 133 var devContent = device.Value; 134 if(devContent == null) 135 { 136 FailDevice(info.Name); 137 } 138 139 //Type 140 if(!devContent.ContainsKey(TYPE_NODE)) 141 { 142 FailDevice(info.Name, TYPE_NODE); 143 } 144 var typeName = (string)devContent[TYPE_NODE]; 145 146 var devType = GetDeviceTypeFromName(typeName); 147 if(devType == null) 148 { 149 FailDevice(info.Name, TYPE_NODE); 150 } 151 152 object peripheral; 153 //Constructor 154 if(!TryInitializeCtor(devType, devContent, out peripheral)) 155 { 156 FailDevice(info.Name, "constructor_invoke"); 157 } 158 if(peripheral == null) 159 { 160 // special case when construction of the object has been deferred 161 deferred.Add(device, groupName); 162 return null; 163 } 164 devContent.Remove(TYPE_NODE); 165 166 info.Peripheral = (IPeripheral)peripheral; 167 168 //Properties 169 try 170 { 171 InitializeProperties(info.Peripheral, devContent); 172 } 173 catch(InvalidOperationException e) 174 { 175 FailDevice(info.Name, e.Message, e.InnerException); 176 } 177 178 //GPIOs 179 if(devContent.ContainsKey(IRQ_NODE)) 180 { 181 info.AddIrq(IRQ_NODE, devContent[IRQ_NODE]); 182 devContent.Remove(IRQ_NODE); 183 } 184 else if(devContent.ContainsKey(GPIO_NODE)) 185 { 186 info.AddIrq(GPIO_NODE, devContent[GPIO_NODE]); 187 devContent.Remove(GPIO_NODE); 188 } 189 190 //IRQs From 191 if(devContent.ContainsKey(IRQ_FROM_NODE)) 192 { 193 info.AddIrqFrom(IRQ_FROM_NODE, devContent[IRQ_FROM_NODE]); 194 devContent.Remove(IRQ_FROM_NODE); 195 } 196 else if(devContent.ContainsKey(GPIO_FROM_NODE)) 197 { 198 info.AddIrqFrom(GPIO_FROM_NODE, devContent[GPIO_FROM_NODE]); 199 devContent.Remove(GPIO_FROM_NODE); 200 } 201 202 //Connections 203 if(devContent.ContainsKey(CONNECTION_NODE)) 204 { 205 InitializeConnections(info, devContent[CONNECTION_NODE]); 206 devContent.Remove(CONNECTION_NODE); 207 } 208 209 return info; 210 } 211 InitializeGPIOsFrom(DeviceInfo device)212 private void InitializeGPIOsFrom(DeviceInfo device) 213 { 214 foreach(var nodeName in device.IrqsFrom.Keys) 215 { 216 var gpioReceiver = device.Peripheral as IGPIOReceiver; 217 if(gpioReceiver == null) 218 { 219 FailDevice(device.Name, nodeName); 220 } 221 222 var irqs = device.IrqsFrom[nodeName]; 223 if(irqs == null) 224 { 225 FailDevice(device.Name, nodeName); 226 } 227 228 foreach(var source in irqs.Keys) 229 { 230 var sourceIrqs = irqs[source] as List<dynamic>; 231 if(sourceIrqs == null) 232 { 233 FailDevice(device.Name, nodeName + ": " + source); 234 } 235 236 IPeripheral sourcePeripheral; 237 238 var fromList = deviceList.SingleOrDefault(x => x.Name == source); 239 240 if(fromList != null) 241 { 242 sourcePeripheral = (IGPIOReceiver)fromList.Peripheral; 243 } 244 else if(!machine.TryGetByName<IPeripheral>(source, out sourcePeripheral)) 245 { 246 FailDevice(device.Name, nodeName + ": " + source); 247 } 248 if(sourcePeripheral is ILocalGPIOReceiver && source.Length == 2) 249 { 250 sourcePeripheral = ((ILocalGPIOReceiver)sourcePeripheral).GetLocalReceiver(int.Parse(source[1])); 251 } 252 253 var props = sourcePeripheral.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 254 var connectors = props 255 .Where(x => typeof(GPIO).IsAssignableFrom(x.PropertyType)).ToArray(); 256 PropertyInfo defaultConnector = null; 257 if(connectors.Count() == 1) 258 { 259 defaultConnector = connectors.First(); 260 } 261 262 try 263 { 264 if(sourceIrqs.All(x => x is JsonArray)) 265 { 266 foreach(var irqEntry in sourceIrqs) 267 { 268 InitializeGPIO(sourcePeripheral, device.Name, gpioReceiver, irqEntry.ToDynamic(), defaultConnector); 269 } 270 } 271 else 272 { 273 InitializeGPIO(sourcePeripheral, device.Name, gpioReceiver, ((JsonArray)sourceIrqs).ToDynamic(), defaultConnector); 274 } 275 } 276 catch(ArgumentException) 277 { 278 FailDevice(device.Name, nodeName + ": " + source); 279 } 280 } 281 } 282 } 283 InitializeGPIOs(DeviceInfo device)284 private void InitializeGPIOs(DeviceInfo device) 285 { 286 foreach(var nodeName in device.Irqs.Keys) 287 { 288 var irqs = device.Irqs[nodeName]; 289 if(irqs == null) 290 { 291 FailDevice(device.Name, nodeName); 292 } 293 var props = device.Peripheral.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); 294 var connectors = props 295 .Where(x => typeof(GPIO).IsAssignableFrom(x.PropertyType)).ToArray(); 296 PropertyInfo defaultConnector = null; 297 if(connectors.Count() == 1) 298 { 299 defaultConnector = connectors.First(); 300 } 301 302 foreach(var controller in irqs.Keys) 303 { 304 var controllerIrqs = irqs[controller] as List<dynamic>; 305 if(controllerIrqs == null) 306 { 307 FailDevice(device.Name, nodeName + ": " + controller); 308 } 309 310 var controllerElements = controller.Split('#'); 311 if(controllerElements.Length > 2) 312 { 313 FailDevice(device.Name, nodeName + ": " + controller); 314 } 315 316 IGPIOReceiver receiver; 317 318 var fromList = deviceList.SingleOrDefault(x => x.Name == controllerElements[0]); 319 320 if(fromList != null && fromList.Peripheral is IGPIOReceiver) 321 { 322 receiver = (IGPIOReceiver)fromList.Peripheral; 323 } 324 else if(!machine.TryGetByName<IGPIOReceiver>(controllerElements[0], out receiver)) 325 { 326 FailDevice(device.Name, nodeName + ": " + controller); 327 } 328 if(receiver is ILocalGPIOReceiver && controllerElements.Length == 2) 329 { 330 receiver = ((ILocalGPIOReceiver)receiver).GetLocalReceiver(int.Parse(controllerElements[1])); 331 } 332 333 try 334 { 335 if(controllerIrqs.All(x => x is JsonArray)) 336 { 337 foreach(var irqEntry in controllerIrqs) 338 { 339 InitializeGPIO(device.Peripheral, device.Name, receiver, irqEntry.ToDynamic(), defaultConnector); 340 } 341 } 342 else 343 { 344 InitializeGPIO(device.Peripheral, device.Name, receiver, ((JsonArray)controllerIrqs).ToDynamic(), defaultConnector); 345 } 346 } 347 catch(ArgumentException) 348 { 349 FailDevice(device.Name, nodeName + ": " + controller); 350 } 351 } 352 } 353 } 354 355 //[source,dest] or [dest] with non-null defaultConnector InitializeGPIO(IPeripheral device, string deviceName, IGPIOReceiver receiver, IList<int> irqEntry, PropertyInfo defaultConnector)356 void InitializeGPIO(IPeripheral device, string deviceName, IGPIOReceiver receiver, IList<int> irqEntry, PropertyInfo defaultConnector) 357 { 358 var periByNumber = device as INumberedGPIOOutput; 359 if(irqEntry.Count == 2 && periByNumber != null) 360 { 361 periByNumber.Connections[irqEntry[0]].Connect(receiver, irqEntry[1]); 362 } 363 else if(irqEntry.Count == 1 && defaultConnector != null) 364 { 365 var gpioField = defaultConnector.GetValue(device, null) as GPIO; 366 if(gpioField == null) 367 { 368 FailDevice(deviceName); 369 } 370 gpioField.Connect(receiver, irqEntry[0]); 371 } 372 else 373 { 374 throw new ArgumentException(); 375 } 376 } 377 InitializeGPIO(IPeripheral device, string deviceName, IGPIOReceiver receiver, IList<object> irqEntry, PropertyInfo defaultConnector)378 void InitializeGPIO(IPeripheral device, string deviceName, IGPIOReceiver receiver, IList<object> irqEntry, PropertyInfo defaultConnector) 379 { 380 if(!(irqEntry[0] is string && irqEntry[1] is int)) 381 { 382 throw new ArgumentException(); 383 } 384 //May throw AmbiguousMatchException - then use BindingFlags.DeclaredOnly or sth 385 var connector = device.GetType().GetProperty(irqEntry[0] as string); 386 if(connector == null) 387 { 388 throw new ArgumentException(); 389 } 390 var gpio = connector.GetValue(device, null) as GPIO; 391 if(gpio == null) 392 { 393 FailDevice(deviceName); 394 } 395 gpio.Connect(receiver, (int)irqEntry[1]); 396 397 } 398 IsShortNotation(IPeripheral peripheral, PropertyInfo defaultConnector, IList<dynamic> entry)399 private bool IsShortNotation(IPeripheral peripheral, PropertyInfo defaultConnector, IList<dynamic> entry) 400 { 401 return (peripheral is INumberedGPIOOutput && entry.Count == 2 && entry.All(x => x is int)) 402 || (defaultConnector != null && entry.Count == 1 && entry[0] is int) 403 || (entry.Count == 2 && entry[0] is String && entry[1] is int); 404 } 405 InitializeConnections(DeviceInfo device, IDictionary<string, dynamic> connections)406 private static void InitializeConnections(DeviceInfo device, IDictionary<string, dynamic> connections) 407 { 408 if(connections == null) 409 { 410 FailDevice(device.Name, CONNECTION_NODE); 411 } 412 foreach(var container in connections.Keys) 413 { 414 var conDict = connections[container]; 415 device.AddConnection(container, conDict); 416 } 417 } 418 InitializeConnections(DeviceInfo device, string connection)419 private static void InitializeConnections(DeviceInfo device, string connection) 420 { 421 if(string.IsNullOrWhiteSpace(connection)) 422 { 423 FailDevice(device.Name, CONNECTION_NODE); 424 } 425 device.AddConnection(connection); 426 } 427 InitializeProperties(object device, IDictionary<string, dynamic> node)428 private void InitializeProperties(object device, IDictionary<string, dynamic> node) 429 { 430 foreach(var item in node.Keys.Where(x=>Char.IsUpper(x,0))) 431 { 432 var value = node[item]; 433 try 434 { 435 Dynamic.InvokeSet(device, item, value); 436 } 437 catch(Exception e) 438 { 439 throw new RecoverableException(item, e); 440 } 441 } 442 } 443 DevicesConfig(string text, IMachine machine)444 public DevicesConfig(string text, IMachine machine) 445 { 446 try 447 { 448 var devices = SimpleJson.DeserializeObject<dynamic>(text); 449 this.machine = machine; 450 //Every main node is one peripheral/device 451 foreach(var dev in devices) 452 { 453 InitializeDevice(dev); 454 } 455 456 while(deferred.Count > 0) 457 { 458 var lastCount = deferred.Count; 459 foreach(var deferredDevice in deferred.ToList()) 460 { 461 if(InitializeDevice(deferredDevice.Key, deferredDevice.Value)) 462 { 463 deferred.Remove(deferredDevice.Key); 464 } 465 } 466 467 if(lastCount == deferred.Count) 468 { 469 throw new ConstructionException("The provided configuration is not consistent. Some devices could not have been created due to wrong references."); 470 } 471 } 472 473 //Initialize connections 474 while(deviceList.Any(x => !x.IsRegistered)) 475 { 476 var anyChange = false; 477 //setup connections 478 foreach(var periConn in deviceList.Where(x=> !x.IsRegistered && x.HasConnections)) 479 { 480 var parents = new Dictionary<string, IPeripheral>(); 481 foreach(var conn in periConn.Connections.Select(x=>x.Key)) 482 { 483 var fromList = deviceList.SingleOrDefault(x => x.Name == conn); 484 if(fromList != null) 485 { 486 parents.Add(conn, fromList.Peripheral); 487 } 488 else 489 { 490 IPeripheral candidate; 491 if(!machine.TryGetByName(conn, out candidate)) 492 { 493 FailDevice(periConn.Name, "connection to " + conn, null); 494 } 495 parents.Add(conn, candidate); 496 } 497 } 498 499 var canBeRegistered = parents.All(x => machine.IsRegistered(x.Value)); 500 if(canBeRegistered) 501 { 502 RegisterInParents(periConn, parents); 503 periConn.IsRegistered = true; 504 anyChange = true; 505 } 506 } 507 if(!anyChange) 508 { 509 var invalidDevices = deviceList.Where(x => !x.IsRegistered).Select(x => x.Name).Aggregate((x, y) => x + ", " + y); 510 throw new RegistrationException("The " + 511 "provided configuration is not consistent. The following devices could not have been registered: " 512 + invalidDevices 513 ); 514 } 515 } 516 517 foreach(var device in deviceList.Where(x=>x.Irqs.Any())) 518 { 519 InitializeGPIOs(device); 520 } 521 522 foreach(var device in deviceList.Where(x=>x.IrqsFrom.Any())) 523 { 524 InitializeGPIOsFrom(device); 525 } 526 } 527 catch(SerializationException e) 528 { 529 throw new RecoverableException("Invalid JSON string.", e); 530 } 531 catch(RuntimeBinderException e) 532 { 533 throw new RecoverableException("The config file could not be analyzed. You should reset your current emulation.", e); 534 } 535 536 foreach(var group in groups) 537 { 538 machine.PeripheralsGroups.GetOrCreate(group.Key, group.Value.Select(x => x.Peripheral)); 539 } 540 541 foreach(var device in deviceList) 542 { 543 machine.SetLocalName(device.Peripheral, device.Name); 544 } 545 } 546 GetShortInfo(string filename)547 public static IEnumerable<DeviceInfo> GetShortInfo(string filename) 548 { 549 var devices = ((JsonObject)SimpleJson.DeserializeObject<dynamic>(File.ReadAllText(filename))).ToList(); 550 // flattening peripheral groups 551 var arrays = devices.Where(d => d.Value is JsonArray).ToList(); 552 arrays.ForEach(a => devices.Remove(a)); 553 arrays.Select(a => a.Value).Cast<JsonArray>().SelectMany(y => y).Cast<JsonObject>().ToList().ForEach(x => devices.AddRange(x)); 554 555 foreach(var device in devices) 556 { 557 var info = new DeviceInfo(); 558 info.Name = device.Key; 559 dynamic devContent = device.Value; 560 if(devContent == null) 561 { 562 FailDevice(device.Key); 563 } 564 565 if(!devContent.ContainsKey(TYPE_NODE)) 566 { 567 FailDevice(device.Key, TYPE_NODE); 568 } 569 var typeName = (string)devContent[TYPE_NODE]; 570 571 var devType = GetDeviceTypeFromName(typeName); 572 if(devType == null) 573 { 574 FailDevice(device.Key, TYPE_NODE); 575 } 576 info.Type = devType; 577 578 if(devContent.ContainsKey(CONNECTION_NODE)) 579 { 580 InitializeConnections(info, devContent[CONNECTION_NODE]); 581 } 582 yield return info; 583 } 584 } 585 RegisterInParents(DeviceInfo device, IDictionary<string, IPeripheral> parents)586 private void RegisterInParents(DeviceInfo device, IDictionary<string, IPeripheral> parents) 587 { 588 foreach(var parentName in device.Connections.Keys) 589 { 590 //TODO: nongeneric version 591 var parent = parents.Single(x => x.Key == parentName).Value; 592 var connections = device.Connections[parentName]; 593 var ifaces = parent.GetType().GetInterfaces().Where(x => IsSpecializationOfRawGeneric(typeof(IPeripheralRegister<,>), x)).ToList(); 594 var ifaceCandidates = ifaces 595 .Where(x => x.GetGenericArguments()[0].IsAssignableFrom(device.Peripheral.GetType())) 596 .OrderBy(x => typeof(NullRegistrationPoint).IsAssignableFrom(x.GetGenericArguments()[1])) // Use the null registration point last 597 .ToList(); 598 foreach(var connection in connections) 599 { 600 IRegistrationPoint regPoint = null; 601 Type formalType = null; 602 if(connection.ContainsKey(TYPE_NODE)) 603 { 604 var name = (string)connection[TYPE_NODE]; 605 formalType = GetDeviceTypeFromName(name); 606 } 607 608 Type foundIface = null; 609 foreach(var iface in ifaceCandidates) 610 { 611 var iRegPoint = iface.GetGenericArguments()[1]; 612 Type objType; 613 if(formalType != null && iRegPoint.IsAssignableFrom(formalType)) 614 { 615 objType = formalType; 616 } 617 else 618 { 619 objType = iRegPoint; 620 } 621 622 object regPointObject; 623 if(!TryInitializeCtor(objType, connection, out regPointObject)) 624 { 625 if(connection.Keys.Any() || !TryHandleSingleton(objType, out regPointObject)) 626 { 627 continue; 628 } 629 } 630 regPoint = (IRegistrationPoint)regPointObject; 631 foundIface = iface; 632 break; 633 //is a construable type 634 } 635 if(foundIface == null) 636 { 637 // let's try attachment through the AttachTo mechanism 638 FailDevice(device.Name, "connection to " + parentName); 639 } 640 else 641 { 642 Dynamic.InvokeMemberAction(parent, "Register", new object[] { 643 device.Peripheral, 644 regPoint 645 } 646 ); 647 } 648 } 649 } 650 } 651 IsSpecializationOfRawGeneric(Type generic, Type toCheck)652 private static bool IsSpecializationOfRawGeneric(Type generic, Type toCheck) 653 { 654 var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 655 if(generic == cur) 656 { 657 return true; 658 } 659 660 return false; 661 } 662 TryHandleSingleton(Type type, out object instance)663 private static bool TryHandleSingleton(Type type, out object instance) 664 { 665 var properties = type.GetProperties(); 666 var desiredProperty = properties.FirstOrDefault(x => x.Name == "Instance" && x.PropertyType == type); 667 if(desiredProperty == null) 668 { 669 instance = null; 670 return false; 671 } 672 // We have to use reflection-based approach because of a bug in Mono 5.2: 673 // https://bugzilla.xamarin.com/show_bug.cgi?id=58455 674 // 675 //instance = Dynamic.InvokeGet(InvokeContext.CreateStatic(type), desiredProperty.Name); 676 instance = desiredProperty.GetGetMethod().Invoke(null, Type.EmptyTypes); 677 return true; 678 } 679 TryInitializeCtor(Type devType, IDictionary<string,dynamic> node, out object constructedObject)680 private bool TryInitializeCtor(Type devType, IDictionary<string,dynamic> node, out object constructedObject) 681 { 682 //Find best suitable constructor, sort parameter list and create instance. Constructor parameters begin with [a-z] 683 var constructors = FindSuitableConstructors(devType, node.Where(x => Char.IsLower(x.Key, 0)).Select(x => x.Key)); 684 if(constructors.Count != 1) 685 { 686 constructedObject = null; 687 return false; 688 } 689 690 var ctor = constructors[0]; 691 var sortedParams = new Dictionary<string, object>(); 692 foreach(var ctorParam in ctor.GetParameters()) 693 { 694 if(node.ContainsKey(ctorParam.Name)) 695 { 696 if(typeof(IPeripheral).IsAssignableFrom(ctorParam.ParameterType) && !typeof(IMachine).IsAssignableFrom(ctorParam.ParameterType) && !ctorParam.ParameterType.IsArray) 697 { 698 var info = deviceList.SingleOrDefault(di => di.Name == node[ctorParam.Name]); 699 if(info != null) 700 { 701 sortedParams.Add(ctorParam.Name, info.Peripheral); 702 } 703 else 704 { 705 // required peripheral is not yet created, so we need to defer the construction 706 constructedObject = null; 707 return true; 708 } 709 } 710 else 711 { 712 var temp = GenerateObject(node[ctorParam.Name], ctorParam.ParameterType); 713 //HACK: The reason of the following line is described at the top of this class. 714 if(ctorParam.ParameterType.IsPrimitive || ctorParam.ParameterType.IsEnum || Nullable.GetUnderlyingType(ctorParam.ParameterType) != null || temp != null) 715 { 716 sortedParams.Add(ctorParam.Name, temp); 717 } 718 else 719 { 720 // required peripheral is not yet created, so we need to defer the construction 721 constructedObject = null; 722 return true; 723 } 724 } 725 } 726 else if(typeof(IMachine).IsAssignableFrom(ctorParam.ParameterType)) 727 { 728 sortedParams.Add(ctorParam.Name, machine); 729 } 730 else 731 { 732 sortedParams.Add(ctorParam.Name, ctorParam.DefaultValue); 733 } 734 } 735 var paramsArray = sortedParams.Values.ToArray(); 736 try 737 { 738 constructedObject = Dynamic.InvokeConstructor(devType, paramsArray); 739 } 740 catch(ConstructionException) 741 { 742 constructedObject = null; 743 return false; 744 } 745 catch(Exception e) 746 { 747 throw new ConstructionException(String.Format("Could not create object of type {0}.", devType.Name), e); 748 } 749 return true; 750 } 751 GenerateObject(dynamic value, Type type)752 private dynamic GenerateObject(dynamic value, Type type) 753 { 754 var stringValue = value as string; 755 if(type.IsEnum && stringValue != null) 756 { 757 object[] parameters = new object[2]; 758 parameters[0] = stringValue; 759 var parseResult = typeof(Enum).GetMethods().First(x=>x.Name == "TryParse" && x.GetParameters().Length == 2).MakeGenericMethod(type) 760 .Invoke(null, parameters); 761 if((bool)parseResult) 762 { 763 return parameters[1]; 764 } 765 } 766 return Dynamic.InvokeConvert(value, type, true); 767 } 768 GenerateObject(IDictionary<string, dynamic> value, Type type)769 private dynamic GenerateObject(IDictionary<string, dynamic> value, Type type) 770 { 771 object obj; 772 if(!TryInitializeCtor(type, value, out obj)) 773 { 774 throw new ConstructionException("Could not create object " + value); 775 } 776 return obj; 777 } 778 GenerateObject(IList<dynamic> value, Type type)779 private dynamic GenerateObject(IList<dynamic> value, Type type) 780 { 781 Type innerType = typeof(object); 782 var isArray = type.IsArray; 783 if(type.IsGenericType) 784 { 785 innerType = type.GetGenericArguments()[0]; 786 } 787 else if(isArray) 788 { 789 innerType = type.GetElementType(); 790 } 791 792 var list = isArray ? Dynamic.InvokeConstructor(typeof(List<>).MakeGenericType(innerType)) : Dynamic.InvokeConstructor(type); 793 foreach(var item in value) 794 { 795 var obj = GenerateObject(item, innerType); 796 //HACK: The reason of the following line is described at the top of this class. 797 if(!innerType.IsPrimitive && !innerType.IsEnum && Nullable.GetUnderlyingType(innerType) == null && obj == null) 798 { 799 return null; 800 } 801 list.Add(obj); 802 } 803 if(isArray) 804 { 805 return list.ToArray(); 806 } 807 return list; 808 } 809 FindSuitableConstructors(Type type, IEnumerable<string> parameters)810 public IList<ConstructorInfo> FindSuitableConstructors(Type type, IEnumerable<string> parameters) 811 { 812 var goodCtors = new List<ConstructorInfo>(); 813 foreach(var ctor in type.GetConstructors()) 814 { 815 var unusableFound = false; 816 // every parameter in 'parameters' must be present in the constructor 817 var ctorParams = ctor.GetParameters(); 818 if(!parameters.All(x => ctorParams.FirstOrDefault(y => y.Name == x) != null)) 819 { 820 continue; 821 } 822 823 // every argument in ctor must either be present in 'parameters' or set to default 824 foreach(var param in ctorParams) 825 { 826 if(!parameters.Contains(param.Name)) 827 { 828 829 if(!param.IsOptional && !typeof(IMachine).IsAssignableFrom(param.ParameterType)) 830 { 831 unusableFound = true; 832 } 833 } 834 } 835 if(unusableFound) 836 { 837 continue; 838 } 839 goodCtors.Add(ctor); 840 } 841 return goodCtors; 842 } 843 844 private readonly IMachine machine; 845 846 private const string TYPE_NODE = "_type"; 847 private const string IRQ_NODE = "_irq"; 848 private const string GPIO_NODE = "_gpio"; 849 private const string IRQ_FROM_NODE = "_irqFrom"; 850 private const string GPIO_FROM_NODE = "_gpioFrom"; 851 private const string CONNECTION_NODE = "_connection"; 852 } 853 } 854 855