mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-22 05:48:37 +00:00
.
This commit is contained in:
parent
0bb7fb87e5
commit
c4de249bee
7 changed files with 285 additions and 74 deletions
|
|
@ -17,7 +17,7 @@ namespace DebugCompiler
|
|||
internal class Program
|
||||
{
|
||||
//public const string CosmosRoot = @"e:\Cosmos";
|
||||
public const string CosmosRoot = @"c:\data\sources\cosmos";
|
||||
public const string CosmosRoot = @"e:\OpenSOurce\Cosmos";
|
||||
//public const string CosmosRoot = @"C:\Users\Huge\Documents\Visual Studio 2010\Projects\IL2CPU";
|
||||
|
||||
private const string KernelFile = CosmosRoot + @"\Users\Kudzu\Breakpoints\bin\Debug\Playground.Kudzu.BreakpointsKernel.dll";
|
||||
|
|
@ -50,7 +50,7 @@ namespace DebugCompiler
|
|||
xTask.OnLogException = (m) =>
|
||||
{
|
||||
Console.WriteLine("Exception: {0}", m.ToString());
|
||||
Console.ReadLine();
|
||||
//Console.ReadLine();
|
||||
};
|
||||
if (xTask.Execute())
|
||||
{
|
||||
|
|
@ -66,7 +66,7 @@ namespace DebugCompiler
|
|||
catch (Exception E)
|
||||
{
|
||||
Console.WriteLine(E.ToString());
|
||||
Console.ReadLine();
|
||||
//Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("Run took {0}", xSW.Elapsed);
|
||||
|
|
|
|||
|
|
@ -375,5 +375,19 @@ namespace Cosmos.IL2CPU {
|
|||
xNextOpCode.InterpretStackTypes(aOpCodes, aStack, ref aSituationChanged, aMaxRecursionDepth - 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsIntegralType(Type type)
|
||||
{
|
||||
return type == typeof(byte)
|
||||
|| type == typeof(sbyte)
|
||||
|| type == typeof(ushort)
|
||||
|| type == typeof(short)
|
||||
|| type == typeof(int)
|
||||
|| type == typeof(uint)
|
||||
|| type == typeof(long)
|
||||
|| type == typeof(ulong)
|
||||
|| type == typeof(char);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,11 +141,7 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (xValue1 == typeof (int) && xValue2 == typeof (int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (xValue1 == typeof(long) && xValue2 == typeof(long))
|
||||
if (IsIntegralType(xValue1) && IsIntegralType(xValue2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -172,6 +168,12 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (xValue1.IsClass &&
|
||||
xValue2.IsClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception(String.Format("Comparing types '{0}' and '{1}' not supported!", xValue1.AssemblyQualifiedName, xValue2.AssemblyQualifiedName));
|
||||
default:
|
||||
throw new NotImplementedException("Checks for opcode " + OpCode + " not implemented!");
|
||||
|
|
@ -194,13 +196,13 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
case Code.Bge_Un:
|
||||
case Code.Beq:
|
||||
case Code.Bne_Un:
|
||||
case Code.Br:
|
||||
base.DoInterpretNextInstructionStackTypes(aOpCodes, new Stack<Type>(aStack), ref aSituationChanged, aMaxRecursionDepth);
|
||||
if (Value > Position)
|
||||
{
|
||||
InterpretInstruction(Value, aOpCodes, aStack, ref aSituationChanged, aMaxRecursionDepth);
|
||||
}
|
||||
break;
|
||||
case Code.Br:
|
||||
case Code.Leave:
|
||||
if (Value > Position)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Cosmos.IL2CPU.X86.IL;
|
||||
using FieldInfo = System.Reflection.FieldInfo;
|
||||
|
||||
namespace Cosmos.IL2CPU.ILOpCodes {
|
||||
public class OpField : ILOpCode {
|
||||
|
|
@ -55,34 +57,13 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
base.DoInitStackAnalysis(aMethod);
|
||||
|
||||
switch (OpCode)
|
||||
{
|
||||
case Code.Stsfld:
|
||||
StackPopTypes[0] = Value.FieldType;
|
||||
if (StackPopTypes[0].IsEnum)
|
||||
{
|
||||
StackPopTypes[0] = StackPopTypes[0].GetEnumUnderlyingType();
|
||||
}
|
||||
|
||||
return;
|
||||
case Code.Ldsfld:
|
||||
{ case Code.Ldsfld:
|
||||
StackPushTypes[0] = Value.FieldType;
|
||||
if (StackPushTypes[0].IsEnum)
|
||||
{
|
||||
StackPushTypes[0] = StackPushTypes[0].GetEnumUnderlyingType();
|
||||
}
|
||||
return;
|
||||
|
||||
case Code.Stfld:
|
||||
//StackPopTypes[0] = Value.FieldType;
|
||||
//if (StackPopTypes[0].IsEnum)
|
||||
//{
|
||||
// StackPopTypes[0] = StackPopTypes[0].GetEnumUnderlyingType();
|
||||
//}
|
||||
//else if (Value.DeclaringType.IsValueType)
|
||||
//{
|
||||
// StackPopTypes[0] = typeof(void*);
|
||||
//}
|
||||
//StackPopTypes[1] = Value.DeclaringType;
|
||||
return;
|
||||
case Code.Ldfld:
|
||||
StackPushTypes[0] = Value.FieldType;
|
||||
|
|
@ -135,6 +116,10 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (IsIntegralType(expectedType) && IsIntegralType(StackPopTypes[0]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (expectedType == typeof (bool))
|
||||
{
|
||||
if (StackPopTypes[0] == typeof (int))
|
||||
|
|
@ -142,7 +127,53 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
return;
|
||||
}
|
||||
}
|
||||
throw new Exception("Wrong Poptype encountered! (Type = " + StackPopTypes[0].FullName + ")");
|
||||
if (StackPopTypes[0] == typeof(NullRef))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (expectedType.IsAssignableFrom(StackPopTypes[0]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw new Exception("Wrong Poptype encountered! (Type = " + StackPopTypes[0].FullName + ", expected = " + expectedType.FullName + ")");
|
||||
case Code.Stsfld:
|
||||
if (StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
expectedType = Value.FieldType;
|
||||
if (expectedType.IsEnum)
|
||||
{
|
||||
expectedType = expectedType.GetEnumUnderlyingType();
|
||||
}
|
||||
else if (Value.DeclaringType.IsValueType)
|
||||
{
|
||||
expectedType = typeof(void*);
|
||||
}
|
||||
if (StackPopTypes[0] == expectedType || StackPopTypes[0] == Value.FieldType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsIntegralType(expectedType) && IsIntegralType(StackPopTypes[0]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (expectedType == typeof(bool))
|
||||
{
|
||||
if (StackPopTypes[0] == typeof(int))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (expectedType.IsAssignableFrom(StackPopTypes[0]))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] == typeof(NullRef))
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw new Exception("Wrong Poptype encountered! (Type = " + StackPopTypes[0].FullName + ", expected = " + expectedType.FullName + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
case Code.Dup:
|
||||
return 1;
|
||||
case Code.Volatile:
|
||||
return 1;
|
||||
return 0;
|
||||
case Code.Endfinally:
|
||||
return 0;
|
||||
default:
|
||||
|
|
@ -264,7 +264,7 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
case Code.Dup:
|
||||
return 2;
|
||||
case Code.Volatile:
|
||||
return 1;
|
||||
return 0;
|
||||
case Code.Endfinally:
|
||||
return 0;
|
||||
default:
|
||||
|
|
@ -283,26 +283,6 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
StackPopTypes[1] = typeof(void*);
|
||||
return;
|
||||
|
||||
case Code.Stind_I1:
|
||||
StackPopTypes[0] = typeof (sbyte);
|
||||
StackPopTypes[1] = typeof(void*);
|
||||
return;
|
||||
|
||||
case Code.Stind_I2:
|
||||
StackPopTypes[0] = typeof (short);
|
||||
StackPopTypes[1] = typeof(void*);
|
||||
return;
|
||||
|
||||
case Code.Stind_I4:
|
||||
StackPopTypes[0] = typeof (int);
|
||||
StackPopTypes[1] = typeof(void*);
|
||||
return;
|
||||
|
||||
case Code.Stind_I8:
|
||||
StackPopTypes[0] = typeof (long);
|
||||
StackPopTypes[1] = typeof(void*);
|
||||
return;
|
||||
|
||||
case Code.Ldind_U1:
|
||||
StackPushTypes[0] = typeof (byte);
|
||||
return;
|
||||
|
|
@ -557,6 +537,13 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if ((StackPopTypes[0] == typeof(int) && StackPopTypes[1] == typeof(IntPtr))
|
||||
|| (StackPopTypes[0] == typeof(IntPtr) && StackPopTypes[1] == typeof(int)))
|
||||
{
|
||||
StackPushTypes[0] = typeof(IntPtr);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if ((StackPopTypes[0] == typeof(int) && StackPopTypes[1] == typeof(uint))
|
||||
|| (StackPopTypes[0] == typeof(uint) && StackPopTypes[1] == typeof(int)))
|
||||
{
|
||||
|
|
@ -585,6 +572,13 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if ((StackPopTypes[0] == typeof(int) && StackPopTypes[1] == typeof(char))
|
||||
|| (StackPopTypes[0] == typeof(char) && StackPopTypes[1] == typeof(int)))
|
||||
{
|
||||
StackPushTypes[0] = typeof(int);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] == typeof(IntPtr) && StackPopTypes[1] == typeof(IntPtr))
|
||||
{
|
||||
StackPushTypes[0] = typeof(uint);
|
||||
|
|
@ -609,6 +603,12 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] == typeof(ulong) && StackPopTypes[1] == typeof(ulong))
|
||||
{
|
||||
StackPushTypes[0] = typeof(ulong);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] == typeof(Double) && StackPopTypes[1] == typeof(Double))
|
||||
{
|
||||
StackPushTypes[0] = typeof(Double);
|
||||
|
|
@ -621,8 +621,23 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(string.Format("Add on types '{0}' and '{1}' not yet implemented!", StackPopTypes[0], StackPopTypes[1]));
|
||||
if (OpCode == Code.Add &&
|
||||
((StackPopTypes[0] == typeof(IntPtr) && (StackPopTypes[1].IsPointer || StackPopTypes[1].IsByRef))
|
||||
|| ((StackPopTypes[0].IsPointer || StackPopTypes[0].IsByRef) && StackPopTypes[1] == typeof(IntPtr))))
|
||||
{
|
||||
if (StackPopTypes[0] == typeof(IntPtr))
|
||||
{
|
||||
StackPushTypes[0] = StackPopTypes[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
StackPushTypes[0] = StackPopTypes[0];
|
||||
}
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(string.Format("{0} on types '{1}' and '{2}' not yet implemented!", OpCode, StackPopTypes[0], StackPopTypes[1]));
|
||||
}
|
||||
break;
|
||||
case Code.Stelem_I2:
|
||||
|
|
@ -680,6 +695,18 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (xTypeValue == typeof(ushort) && xTypeShift == typeof(int))
|
||||
{
|
||||
StackPushTypes[0] = typeof(int);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (xTypeValue == typeof(char) && xTypeShift == typeof(int))
|
||||
{
|
||||
StackPushTypes[0] = typeof(int);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
if (xTypeValue == typeof(uint) && xTypeShift == typeof(int))
|
||||
{
|
||||
StackPushTypes[0] = typeof(int);
|
||||
|
|
@ -741,7 +768,91 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
return;
|
||||
}
|
||||
return;
|
||||
case Code.Stind_I1:
|
||||
if (StackPopTypes[1] == null || StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] != typeof(sbyte) &&
|
||||
StackPopTypes[0] != typeof(byte))
|
||||
{
|
||||
throw new Exception("Wrong value type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
if (!IsPointer(StackPopTypes[1]))
|
||||
{
|
||||
throw new Exception("Wrong Pointer type: " + StackPopTypes[1].FullName);
|
||||
}
|
||||
break;
|
||||
case Code.Stind_I2:
|
||||
if (StackPopTypes[1] == null || StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] != typeof(short) &&
|
||||
StackPopTypes[0] != typeof(ushort) &&
|
||||
StackPopTypes[0] != typeof(int) &&
|
||||
StackPopTypes[0] != typeof(uint) &&
|
||||
StackPopTypes[0] != typeof(char))
|
||||
{
|
||||
throw new Exception("Wrong value type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
if (!IsPointer(StackPopTypes[1]))
|
||||
{
|
||||
throw new Exception("Wrong Pointer type: " + StackPopTypes[1].FullName);
|
||||
}
|
||||
break;
|
||||
case Code.Stind_I4:
|
||||
if (StackPopTypes[1] == null || StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] != typeof(int) &&
|
||||
StackPopTypes[0] != typeof(uint))
|
||||
{
|
||||
throw new Exception("Wrong value type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
if (!IsPointer(StackPopTypes[1]))
|
||||
{
|
||||
throw new Exception("Wrong Pointer type: " + StackPopTypes[1].FullName);
|
||||
}
|
||||
break;
|
||||
case Code.Stind_I8:
|
||||
if (StackPopTypes[1] == null || StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] != typeof(long) &&
|
||||
StackPopTypes[0] != typeof(ulong))
|
||||
{
|
||||
throw new Exception("Wrong value type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
if (!IsPointer(StackPopTypes[1]))
|
||||
{
|
||||
throw new Exception("Wrong Pointer type: " + StackPopTypes[1].FullName);
|
||||
}
|
||||
break;
|
||||
case Code.Ldind_Ref:
|
||||
if (StackPushTypes[0] != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!StackPopTypes[0].IsByRef)
|
||||
{
|
||||
throw new Exception("Invalid ref type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
StackPushTypes[0] = StackPopTypes[0].GetElementType();
|
||||
aSituationChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsPointer(Type aPointer)
|
||||
{
|
||||
return aPointer.IsPointer || aPointer.IsByRef || aPointer == typeof(IntPtr) || aPointer == typeof(UIntPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,41 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
switch (OpCode)
|
||||
{
|
||||
case Code.Switch:
|
||||
StackPopTypes[0] = typeof(uint);
|
||||
//StackPopTypes[0] = typeof(uint);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on updated StackPopTypes, try to update
|
||||
/// </summary>
|
||||
protected override void DoInterpretStackTypes(ref bool aSituationChanged)
|
||||
{
|
||||
base.DoInterpretStackTypes(ref aSituationChanged);
|
||||
// no switch necessary, there's only 1 instruction using this type.
|
||||
|
||||
if (StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (StackPopTypes[0] == typeof(int) ||
|
||||
StackPopTypes[0] == typeof(byte))
|
||||
{
|
||||
return;
|
||||
}
|
||||
throw new Exception("Wrong type: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
|
||||
protected override void DoInterpretNextInstructionStackTypes(IDictionary<int, ILOpCode> aOpCodes, Stack<Type> aStack, ref bool aSituationChanged, int aMaxRecursionDepth)
|
||||
{
|
||||
base.DoInterpretNextInstructionStackTypes(aOpCodes, new Stack<Type>(aStack), ref aSituationChanged, aMaxRecursionDepth);
|
||||
foreach (var xTarget in BranchLocations)
|
||||
{
|
||||
base.InterpretInstruction(xTarget, aOpCodes, new Stack<Type>(aStack), ref aSituationChanged, aMaxRecursionDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,11 +5,13 @@ using System.Reflection;
|
|||
using System.Text;
|
||||
|
||||
namespace Cosmos.IL2CPU.ILOpCodes {
|
||||
public class OpType : ILOpCode {
|
||||
public class OpType: ILOpCode
|
||||
{
|
||||
public readonly Type Value;
|
||||
|
||||
public OpType(Code aOpCode, int aPos, int aNextPos, Type aValue, System.Reflection.ExceptionHandlingClause aCurrentExceptionHandler)
|
||||
: base(aOpCode, aPos, aNextPos, aCurrentExceptionHandler) {
|
||||
: base(aOpCode, aPos, aNextPos, aCurrentExceptionHandler)
|
||||
{
|
||||
Value = aValue;
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +83,9 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
StackPushTypes[0] = typeof(void*);
|
||||
return;
|
||||
case Code.Box:
|
||||
StackPopTypes[0] = Value;
|
||||
if (Value.IsPrimitive)
|
||||
{
|
||||
StackPushTypes[0] = typeof (Box<>).MakeGenericType(Value);
|
||||
StackPushTypes[0] = typeof(Box<>).MakeGenericType(Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -103,21 +104,12 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
StackPushTypes[0] = Value;
|
||||
return;
|
||||
case Code.Isinst:
|
||||
if (Value.IsGenericType && Value.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
StackPopTypes[0] = typeof(Box<>).MakeGenericType(Value.GetGenericArguments()[0]);
|
||||
}
|
||||
else if (Value.IsValueType)
|
||||
{
|
||||
StackPopTypes[0] = typeof(Box<>).MakeGenericType(Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
StackPopTypes[0] = Value;
|
||||
}
|
||||
StackPopTypes[0] = typeof(object);
|
||||
StackPushTypes[0] = typeof(bool);
|
||||
return;
|
||||
case Code.Castclass:
|
||||
if (Value.IsGenericType && Value.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
if (Value.IsGenericType &&
|
||||
Value.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
StackPushTypes[0] = typeof(Box<>).MakeGenericType(Value.GetGenericArguments()[0]);
|
||||
}
|
||||
|
|
@ -134,5 +126,36 @@ namespace Cosmos.IL2CPU.ILOpCodes {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Based on updated StackPopTypes, try to update
|
||||
/// </summary>
|
||||
protected override void DoInterpretStackTypes(ref bool aSituationChanged)
|
||||
{
|
||||
base.DoInterpretStackTypes(ref aSituationChanged);
|
||||
|
||||
switch (OpCode)
|
||||
{
|
||||
case Code.Box:
|
||||
if (StackPushTypes[0] != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (StackPopTypes[0] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsIntegralType(StackPopTypes[0]) &&
|
||||
IsIntegralType(Value))
|
||||
{
|
||||
StackPushTypes[0] = typeof(Box<>).MakeGenericType(Value);
|
||||
aSituationChanged = true;
|
||||
return;
|
||||
}
|
||||
throw new Exception("Wrong poptype: " + StackPopTypes[0].FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue