diff --git a/Realm.PCL/Realm.PCL.csproj b/Realm.PCL/Realm.PCL.csproj
index cc8781d1cf..bcbbf2e0cd 100644
--- a/Realm.PCL/Realm.PCL.csproj
+++ b/Realm.PCL/Realm.PCL.csproj
@@ -97,9 +97,6 @@
PreserveAttribute.cs
-
- ICopyValuesFrom.cs
-
Exceptions\RealmInvalidObjectException.cs
diff --git a/Realm.PCL/RealmListPCL.cs b/Realm.PCL/RealmListPCL.cs
index 524302ddeb..3edefc97ec 100755
--- a/Realm.PCL/RealmListPCL.cs
+++ b/Realm.PCL/RealmListPCL.cs
@@ -40,7 +40,7 @@ namespace Realms
///
///
/// Type of the RealmObject which is the target of the relationship.
- public class RealmList : IList, IRealmList, IDynamicMetaObjectProvider, ICopyValuesFrom where T : RealmObject
+ public class RealmList : IList, IRealmList, IDynamicMetaObjectProvider where T : RealmObject
{
///
@@ -227,11 +227,6 @@ private void ManageObjectIfNeeded(T obj)
#endregion
- void ICopyValuesFrom.CopyValuesFrom(IEnumerable values)
- {
- RealmPCLHelpers.ThrowProxyShouldNeverBeUsed();
- }
-
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
{
RealmPCLHelpers.ThrowProxyShouldNeverBeUsed();
diff --git a/Realm.Shared/Attributes.cs b/Realm.Shared/Attributes.cs
index e72e366612..76fffd73c4 100644
--- a/Realm.Shared/Attributes.cs
+++ b/Realm.Shared/Attributes.cs
@@ -60,11 +60,5 @@ public WovenAttribute(Type helperType)
[AttributeUsage(AttributeTargets.Property)]
public class WovenPropertyAttribute : Attribute
{
- internal string BackingFieldName { get; private set; }
-
- public WovenPropertyAttribute(string backingFieldName)
- {
- this.BackingFieldName = backingFieldName;
- }
}
}
diff --git a/Realm.Shared/Dynamic/DynamicRealmObjectHelper.cs b/Realm.Shared/Dynamic/DynamicRealmObjectHelper.cs
index b4d1e9936c..e8fb7f6811 100644
--- a/Realm.Shared/Dynamic/DynamicRealmObjectHelper.cs
+++ b/Realm.Shared/Dynamic/DynamicRealmObjectHelper.cs
@@ -17,6 +17,8 @@
////////////////////////////////////////////////////////////////////////////
using System;
+using System.Collections.Generic;
+using System.Reflection;
using Realms.Weaving;
namespace Realms.Dynamic
@@ -25,6 +27,11 @@ internal class DynamicRealmObjectHelper : IRealmObjectHelper
{
internal static readonly DynamicRealmObjectHelper Instance = new DynamicRealmObjectHelper();
+ public void CopyToRealm(RealmObject instance)
+ {
+ throw new NotSupportedException("DynamicRealmObjectHelper cannot exist in unmanaged state, so CopyToRealm should not be called ever.");
+ }
+
public RealmObject CreateInstance()
{
return new DynamicRealmObject();
diff --git a/Realm.Shared/ICopyValuesFrom.cs b/Realm.Shared/ICopyValuesFrom.cs
deleted file mode 100644
index a14f52a427..0000000000
--- a/Realm.Shared/ICopyValuesFrom.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Collections.Generic;
-namespace Realms
-{
-
- // Interface allows us to cast a generic RealmList to this and invoke CopyValuesFrom
- internal interface ICopyValuesFrom
- {
- void CopyValuesFrom(IEnumerable values);
- }
-}
-
diff --git a/Realm.Shared/Realm.Shared.projitems b/Realm.Shared/Realm.Shared.projitems
index 07a02e8ee8..14a24309ee 100644
--- a/Realm.Shared/Realm.Shared.projitems
+++ b/Realm.Shared/Realm.Shared.projitems
@@ -57,7 +57,6 @@
-
diff --git a/Realm.Shared/RealmList.cs b/Realm.Shared/RealmList.cs
index e58449dc17..77f18c0244 100755
--- a/Realm.Shared/RealmList.cs
+++ b/Realm.Shared/RealmList.cs
@@ -38,7 +38,7 @@ namespace Realms
///
/// Type of the RealmObject which is the target of the relationship.
[Preserve(AllMembers = true)]
- public class RealmList : IList, IRealmList, IDynamicMetaObjectProvider, ICopyValuesFrom where T : RealmObject
+ public class RealmList : IList, IRealmList, IDynamicMetaObjectProvider where T : RealmObject
{
public class Enumerator : IEnumerator
{
@@ -312,14 +312,6 @@ private void ManageObjectIfNeeded(T obj)
#endregion
DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(System.Linq.Expressions.Expression expression) => new Dynamic.MetaRealmList(expression, this);
-
- void ICopyValuesFrom.CopyValuesFrom(IEnumerable values)
- {
- foreach (var item in values.Cast())
- {
- Add(item);
- }
- }
}
[Preserve(AllMembers = true)]
diff --git a/Realm.Shared/RealmObject.cs b/Realm.Shared/RealmObject.cs
index ef02b287b5..0fbb56288f 100644
--- a/Realm.Shared/RealmObject.cs
+++ b/Realm.Shared/RealmObject.cs
@@ -88,28 +88,7 @@ internal class Metadata
internal void _CopyDataFromBackingFieldsToRow()
{
Debug.Assert(this.IsManaged);
-
- foreach (var property in _metadata.Schema)
- {
- var field = property.PropertyInfo.DeclaringType.GetField(
- property.PropertyInfo.GetCustomAttribute().BackingFieldName,
- BindingFlags.Instance | BindingFlags.NonPublic
- );
- var value = field?.GetValue(this);
- if (value != null) {
- var listValue = value as IEnumerable;
- if (listValue != null) // assume it is IList NOT a RealmList so need to wipe afer copy
- {
- // cope with ReplaceListGetter creating a getter which assumes
- // a backing field for a managed IList is already a RealmList, so null it first
- field.SetValue(this, null); // now getter will create a RealmList below
- var realmList = (ICopyValuesFrom)property.PropertyInfo.GetValue(this, null);
- realmList.CopyValuesFrom(listValue);
- } else {
- property.PropertyInfo.SetValue(this, value, null);
- }
- } // only null if blank relationship or string so leave as default
- }
+ _metadata.Helper.CopyToRealm(this);
}
#region Getters
diff --git a/Realm.Shared/weaving/IRealmObjectHelper.cs b/Realm.Shared/weaving/IRealmObjectHelper.cs
index 43e7a1def9..c14950f5cd 100644
--- a/Realm.Shared/weaving/IRealmObjectHelper.cs
+++ b/Realm.Shared/weaving/IRealmObjectHelper.cs
@@ -16,13 +16,12 @@
//
////////////////////////////////////////////////////////////////////////////
-using System;
-
namespace Realms.Weaving
{
public interface IRealmObjectHelper
{
RealmObject CreateInstance();
- }
-}
+ void CopyToRealm(RealmObject instance);
+ }
+}
\ No newline at end of file
diff --git a/RealmWeaver/ModuleWeaver.cs b/RealmWeaver/ModuleWeaver.cs
index bf7536fa8d..33108932d6 100644
--- a/RealmWeaver/ModuleWeaver.cs
+++ b/RealmWeaver/ModuleWeaver.cs
@@ -15,7 +15,7 @@
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
-
+
using System;
using System.Linq;
using Mono.Cecil;
@@ -48,6 +48,9 @@ public class ModuleWeaver
private TypeReference System_String;
private TypeReference System_Type;
private TypeReference System_IList;
+ private TypeReference System_DateTimeOffset;
+ private TypeReference System_Int32;
+ private MethodReference System_DatetimeOffset_Op_Inequality;
private MethodReference _propChangedEventArgsConstructor;
private MethodReference _propChangedEventHandlerInvokeReference;
@@ -167,17 +170,19 @@ public void Execute()
_wovenPropertyAttributeConstructor = ModuleDefinition.ImportReference(wovenPropertyAttributeClass.GetConstructors().First());
_corLib = AssemblyResolver.Resolve((AssemblyNameReference)ModuleDefinition.TypeSystem.CoreLibrary);
- System_Object = ModuleDefinition.TypeSystem.Object;
+ System_Object = ModuleDefinition.TypeSystem.Object;
System_Boolean = ModuleDefinition.TypeSystem.Boolean;
System_String = ModuleDefinition.TypeSystem.String;
- var typeTypeDefinition = _corLib.MainModule.GetType("System.Type");
- if (typeTypeDefinition == null) // For PCL's System.Type is only accessible as an ExportedType for some reason.
- {
- typeTypeDefinition = _corLib.MainModule.ExportedTypes.First(t => t.FullName == "System.Type").Resolve();
- }
- System_Type = ModuleDefinition.ImportReference(typeTypeDefinition);
+ System_Int32 = ModuleDefinition.TypeSystem.Int32;
+
+ var dateTimeOffsetType = GetTypeFromSystemAssembly("System.DateTimeOffset");
+ System_DateTimeOffset = ModuleDefinition.ImportReference(dateTimeOffsetType);
+ System_DatetimeOffset_Op_Inequality = ModuleDefinition.ImportReference(dateTimeOffsetType.GetMethods().Single(m => m.Name == "op_Inequality"));
+
+ System_Type = ModuleDefinition.ImportReference(GetTypeFromSystemAssembly("System.Type"));
var listTypeDefinition = _corLib.MainModule.GetType("System.Collections.Generic.List`1");
+
if (listTypeDefinition == null)
{
System_IList = ModuleDefinition.ImportReference(typeof(System.Collections.Generic.List<>));
@@ -214,7 +219,7 @@ public void Execute()
}
catch (Exception e)
{
- LogError( $"Unexpected error caught weaving type '{type.Name}': {e.Message}.\r\nCallstack:\r\n{e.StackTrace}");
+ LogError($"Unexpected error caught weaving type '{type.Name}': {e.Message}.\r\nCallstack:\r\n{e.StackTrace}");
}
}
@@ -227,7 +232,7 @@ private void WeaveType(TypeDefinition type, Dictionary t.FullName == "System.ComponentModel.INotifyPropertyChanged");
-
+
EventDefinition propChangedEventDefinition = null;
FieldDefinition propChangedFieldDefinition = null;
@@ -237,13 +242,17 @@ private void WeaveType(TypeDefinition type, Dictionary X.FullName.EndsWith("::PropertyChanged"));
}
- var persistedPropertyCount = 0;
- foreach (var prop in type.Properties.Where( x => x.HasThis && !x.CustomAttributes.Any(a => a.AttributeType.Name == "IgnoredAttribute")))
+ var persistedProperties = new Dictionary();
+ foreach (var prop in type.Properties.Where(x => x.HasThis && !x.CustomAttributes.Any(a => a.AttributeType.Name == "IgnoredAttribute")))
{
try
{
- var wasWoven = WeaveProperty(prop, type, methodTable, typeImplementsPropertyChanged, propChangedEventDefinition, propChangedFieldDefinition);
- if (wasWoven) persistedPropertyCount++;
+ FieldReference field;
+ var wasWoven = WeaveProperty(prop, type, methodTable, typeImplementsPropertyChanged, propChangedEventDefinition, propChangedFieldDefinition, out field);
+ if (wasWoven)
+ {
+ persistedProperties[prop] = field;
+ }
}
catch (Exception e)
{
@@ -254,7 +263,7 @@ private void WeaveType(TypeDefinition type, Dictionary> methodTable,
- bool typeImplementsPropertyChanged, EventDefinition propChangedEventDefinition,
- FieldDefinition propChangedFieldDefinition)
+ bool typeImplementsPropertyChanged, EventDefinition propChangedEventDefinition,
+ FieldDefinition propChangedFieldDefinition, out FieldReference backingField)
{
var sequencePoint = prop.GetMethod.Body.Instructions.First().SequencePoint;
var columnName = prop.Name;
var mapToAttribute = prop.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == "MapToAttribute");
if (mapToAttribute != null)
- columnName = ((string) mapToAttribute.ConstructorArguments[0].Value);
+ columnName = ((string)mapToAttribute.ConstructorArguments[0].Value);
- var backingField = GetBackingField(prop);
+ backingField = GetBackingField(prop);
var isIndexed = prop.CustomAttributes.Any(a => a.AttributeType.Name == "IndexedAttribute");
if (isIndexed && (!_indexableTypes.Contains(prop.PropertyType.FullName)))
{
@@ -316,7 +325,7 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
return false;
}
- if (!prop.IsAutomatic())
+ if (!prop.IsAutomatic())
{
if (prop.PropertyType.Resolve().BaseType.IsSameAs(_realmObject))
LogWarningPoint(
@@ -329,7 +338,7 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
// If the property is automatic but doesn't have a setter, we should still ignore it.
if (prop.SetMethod == null)
return false;
-
+
var typeId = prop.PropertyType.FullName + (isPrimaryKey ? " unique" : "");
if (!methodTable.ContainsKey(typeId))
{
@@ -345,7 +354,7 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
// treat IList and RealmList similarly but IList gets a default so is useable as standalone
// IList or RealmList allows people to declare lists only of _realmObject due to the class definition
- else if (prop.PropertyType.Name == "IList`1" && prop.PropertyType.Namespace == "System.Collections.Generic")
+ else if (IsIList(prop))
{
var elementType = ((GenericInstanceType)prop.PropertyType).GenericArguments.Single();
if (!elementType.Resolve().BaseType.IsSameAs(_realmObject))
@@ -377,7 +386,7 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
new GenericInstanceMethod(_genericGetListValueReference) { GenericArguments = { elementType } }, elementType,
ModuleDefinition.ImportReference(concreteListConstructor));
}
- else if (prop.PropertyType.Name == "RealmList`1" && prop.PropertyType.Namespace == "Realms")
+ else if (IsRealmList(prop))
{
var elementType = ((GenericInstanceType)prop.PropertyType).GenericArguments.Single();
if (prop.SetMethod != null)
@@ -402,11 +411,11 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
}
ReplaceGetter(prop, columnName,
- new GenericInstanceMethod(_genericGetObjectValueReference) {GenericArguments = {prop.PropertyType}});
+ new GenericInstanceMethod(_genericGetObjectValueReference) { GenericArguments = { prop.PropertyType } });
ReplaceSetter(prop, backingField, columnName,
- new GenericInstanceMethod(_genericSetObjectValueReference) {GenericArguments = {prop.PropertyType}},
+ new GenericInstanceMethod(_genericSetObjectValueReference) { GenericArguments = { prop.PropertyType } },
typeImplementsPropertyChanged, propChangedEventDefinition, propChangedFieldDefinition);
- // with casting in the _realmObject methods, should just work
+ // with casting in the _realmObject methods, should just work
}
else if (prop.PropertyType.FullName == "System.DateTime")
{
@@ -425,7 +434,6 @@ private bool WeaveProperty(PropertyDefinition prop, TypeDefinition type, Diction
prop.CustomAttributes.Add(preserveAttribute);
var wovenPropertyAttribute = new CustomAttribute(_wovenPropertyAttributeConstructor);
- wovenPropertyAttribute.ConstructorArguments.Add(new CustomAttributeArgument(System_String, backingField.Name));
prop.CustomAttributes.Add(wovenPropertyAttribute);
Debug.WriteLine("");
@@ -634,7 +642,7 @@ void ReplaceSetter(PropertyDefinition prop, FieldReference backingField, string
if (setValueReference == null)
throw new ArgumentNullException(nameof(setValueReference));
-
+
if (!weavePropertyChanged)
{
var start = prop.SetMethod.Body.Instructions.First();
@@ -697,12 +705,12 @@ void ReplaceSetter(PropertyDefinition prop, FieldReference backingField, string
ldloc.1
brfalse.s IL_0017
*/
- il.Append(il.Create(OpCodes.Ldarg_0));
+ il.Append(il.Create(OpCodes.Ldarg_0));
il.Append(il.Create(OpCodes.Call, _realmObjectIsManagedGetter));
- il.Append(il.Create(OpCodes.Ldc_I4_0));
- il.Append(il.Create(OpCodes.Ceq));
- il.Append(il.Create(OpCodes.Stloc_1));
- il.Append(il.Create(OpCodes.Ldloc_1));
+ il.Append(il.Create(OpCodes.Ldc_I4_0));
+ il.Append(il.Create(OpCodes.Ceq));
+ il.Append(il.Create(OpCodes.Stloc_1));
+ il.Append(il.Create(OpCodes.Ldloc_1));
var jumpToLabelA = il.Create(OpCodes.Nop);
il.Append(jumpToLabelA); // Jump to A
@@ -790,7 +798,94 @@ private static FieldReference GetBackingField(PropertyDefinition property)
.SingleOrDefault();
}
- private TypeDefinition WeaveRealmObjectHelper(TypeDefinition realmObjectType, MethodDefinition objectConstructor)
+ private static bool IsIList(PropertyDefinition property)
+ {
+ return property.PropertyType.Name == "IList`1" && property.PropertyType.Namespace == "System.Collections.Generic";
+ }
+
+ private static bool IsRealmList(PropertyDefinition property)
+ {
+ return property.PropertyType.Name == "RealmList`1" && property.PropertyType.Namespace == "Realms";
+ }
+
+ private static bool IsDateTimeOffset(PropertyDefinition property)
+ {
+ return property.PropertyType.Name == "DateTimeOffset" && property.PropertyType.Namespace == "System";
+ }
+
+ private static bool IsNullable(PropertyDefinition property)
+ {
+ return property.PropertyType.Name.Contains("Nullable`1") && property.PropertyType.Namespace == "System";
+ }
+
+ private static bool IsSingle(PropertyDefinition property)
+ {
+ return property.PropertyType.Name == "Single" && property.PropertyType.Namespace == "System";
+ }
+
+ private static bool IsDouble(PropertyDefinition property)
+ {
+ return property.PropertyType.Name == "Double" && property.PropertyType.Namespace == "System";
+ }
+
+ private MethodReference GetIListMethodReference(TypeDefinition interfaceType, string methodName, GenericInstanceType genericInstance)
+ {
+ MethodReference reference = null;
+ var definition = interfaceType.GetMethods().FirstOrDefault(m => m.FullName.Contains($"::{methodName}"));
+ if (definition == null)
+ {
+ foreach (var parent in interfaceType.Interfaces)
+ {
+ reference = GetIListMethodReference(parent.Resolve(), methodName, genericInstance);
+ if (reference != null)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ var generic = definition.MakeHostInstanceGeneric(genericInstance.GenericArguments.ToArray());
+ reference = ModuleDefinition.ImportReference(generic);
+ }
+
+ return reference;
+ }
+
+ private MethodReference MakeMethodGeneric(MethodReference methodReference, GenericInstanceType genericInstance)
+ {
+ var reference = new MethodReference(methodReference.Name, methodReference.ReturnType, genericInstance)
+ {
+ HasThis = methodReference.HasThis,
+ ExplicitThis = methodReference.ExplicitThis,
+ CallingConvention = methodReference.CallingConvention
+ };
+
+ foreach (var parameter in methodReference.Parameters)
+ {
+ reference.Parameters.Add(parameter);
+ }
+
+ foreach (var genericParameter in methodReference.GenericParameters)
+ {
+ reference.GenericParameters.Add(new GenericParameter(genericParameter.Name, reference));
+ }
+
+ return reference;
+ }
+
+ private TypeDefinition GetTypeFromSystemAssembly(string typeName)
+ {
+ var objectTypeDefinition = _corLib.MainModule.GetType(typeName);
+ if (objectTypeDefinition == null) // For PCL's System.XXX is only accessible as an ExportedType for some reason.
+ {
+ objectTypeDefinition = _corLib.MainModule.ExportedTypes.First(t => t.FullName == typeName).Resolve();
+ }
+
+ return objectTypeDefinition;
+ }
+
+ private TypeDefinition WeaveRealmObjectHelper(TypeDefinition realmObjectType, MethodDefinition objectConstructor, IDictionary properties)
{
var helperType = new TypeDefinition(null, "RealmHelper",
TypeAttributes.Class | TypeAttributes.NestedPrivate | TypeAttributes.BeforeFieldInit,
@@ -806,6 +901,168 @@ private TypeDefinition WeaveRealmObjectHelper(TypeDefinition realmObjectType, Me
}
helperType.Methods.Add(createInstance);
+ var copyToRealm = new MethodDefinition("CopyToRealm", MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot, ModuleDefinition.TypeSystem.Void);
+ {
+ // This roughly translates to
+ /*
+ var castInstance = (ObjectType)instance;
+
+ *foreach* non-list woven property in castInstance's schema
+ if (castInstance.field != default(fieldType))
+ {
+ castInstance.Property = castInstance.Field;
+ }
+
+ *foreach* list woven property in castInstance's schema
+ var list = castInstance.field;
+ castInstance.field = null;
+ for (var i = 0; i < list.Count; i++)
+ {
+ castInstance.Property.Add(list[i]);
+ }
+ */
+
+ var instanceParameter = new ParameterDefinition("instance", ParameterAttributes.None, ModuleDefinition.ImportReference(_realmObject));
+ copyToRealm.Parameters.Add(instanceParameter);
+
+ copyToRealm.Body.Variables.Add(new VariableDefinition(ModuleDefinition.ImportReference(realmObjectType)));
+
+ byte currentStloc = 1;
+ if (properties.Any(p => IsDateTimeOffset(p.Key)))
+ {
+ copyToRealm.Body.Variables.Add(new VariableDefinition(System_DateTimeOffset));
+ currentStloc++;
+ }
+
+ foreach (var kvp in properties.Where(kvp => IsIList(kvp.Key) || IsRealmList(kvp.Key)))
+ {
+ copyToRealm.Body.Variables.Add(new VariableDefinition(ModuleDefinition.ImportReference(kvp.Value.FieldType)));
+ copyToRealm.Body.Variables.Add(new VariableDefinition(System_Int32));
+ }
+
+ var il = copyToRealm.Body.GetILProcessor();
+ il.Append(il.Create(OpCodes.Ldarg_1));
+ il.Append(il.Create(OpCodes.Castclass, ModuleDefinition.ImportReference(realmObjectType)));
+ il.Append(il.Create(OpCodes.Stloc_0));
+
+ foreach (var kvp in properties)
+ {
+ var property = kvp.Key;
+ var field = kvp.Value;
+
+ if (property.SetMethod != null)
+ {
+ if (!IsNullable(property))
+ {
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldfld, field));
+
+ if (IsDateTimeOffset(property))
+ {
+ il.Append(il.Create(OpCodes.Ldloca_S, (byte)1));
+ il.Append(il.Create(OpCodes.Initobj, field.FieldType));
+ il.Append(il.Create(OpCodes.Ldloc_1));
+ il.Append(il.Create(OpCodes.Call, System_DatetimeOffset_Op_Inequality));
+ }
+ else if (IsSingle(property))
+ {
+ il.Append(il.Create(OpCodes.Ldc_R4, 0f));
+ }
+ else if (IsDouble(property))
+ {
+ il.Append(il.Create(OpCodes.Ldc_R8, 0.0));
+ }
+ }
+
+ var jumpLabel = il.Create(OpCodes.Nop);
+ il.Append(jumpLabel);
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldfld, field));
+ il.Append(il.Create(OpCodes.Call, ModuleDefinition.ImportReference(property.SetMethod)));
+ var label = il.Create(OpCodes.Nop);
+ il.Append(label);
+
+ if (!IsNullable(property))
+ {
+ if (IsSingle(property) || IsDouble(property))
+ {
+ il.Replace(jumpLabel, il.Create(OpCodes.Beq_S, label));
+ }
+ else
+ {
+ il.Replace(jumpLabel, il.Create(OpCodes.Brfalse_S, label));
+ }
+ }
+ }
+ else if (IsIList(property) || IsRealmList(property))
+ {
+ var propertyTypeDefinition = property.PropertyType.Resolve();
+ var genericType = (GenericInstanceType)property.PropertyType;
+ var iList_Get_ItemMethodReference = GetIListMethodReference(propertyTypeDefinition, "get_Item", genericType);
+ var iList_AddMethodReference = GetIListMethodReference(propertyTypeDefinition, "Add", genericType);
+ var iList_Get_CountMethodReference = GetIListMethodReference(propertyTypeDefinition, "get_Count", genericType);
+
+ var iteratorStLoc = (byte)(currentStloc + 1);
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldfld, field));
+
+ var jumpPlaceholder = il.Create(OpCodes.Nop);
+ il.Append(jumpPlaceholder);
+
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldfld, field));
+ il.Append(il.Create(OpCodes.Stloc_S, currentStloc));
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Ldnull));
+ il.Append(il.Create(OpCodes.Stfld, field));
+ il.Append(il.Create(OpCodes.Ldc_I4_0));
+ il.Append(il.Create(OpCodes.Stloc_S, iteratorStLoc));
+
+ var cyclePlaceholder = il.Create(OpCodes.Nop);
+ il.Append(cyclePlaceholder);
+
+ var cycleStart = il.Create(OpCodes.Nop);
+ il.Append(cycleStart);
+
+ il.Append(il.Create(OpCodes.Ldloc_0));
+ il.Append(il.Create(OpCodes.Callvirt, ModuleDefinition.ImportReference(property.GetMethod)));
+ il.Append(il.Create(OpCodes.Ldloc_S, currentStloc));
+ il.Append(il.Create(OpCodes.Ldloc_S, iteratorStLoc));
+ il.Append(il.Create(OpCodes.Callvirt, iList_Get_ItemMethodReference));
+ il.Append(il.Create(OpCodes.Callvirt, iList_AddMethodReference));
+ il.Append(il.Create(OpCodes.Ldloc_S, iteratorStLoc));
+ il.Append(il.Create(OpCodes.Ldc_I4_1));
+ il.Append(il.Create(OpCodes.Add));
+ il.Append(il.Create(OpCodes.Stloc_S, iteratorStLoc));
+
+ var cycleLabel = il.Create(OpCodes.Nop);
+ il.Append(cycleLabel);
+ il.Replace(cyclePlaceholder, il.Create(OpCodes.Br_S, cycleLabel));
+
+ il.Append(il.Create(OpCodes.Ldloc_S, iteratorStLoc));
+ il.Append(il.Create(OpCodes.Ldloc_S, currentStloc));
+ il.Append(il.Create(OpCodes.Callvirt, iList_Get_CountMethodReference));
+ il.Append(il.Create(OpCodes.Blt_S, cycleStart));
+
+ var jumpLabel = il.Create(OpCodes.Nop);
+ il.Append(jumpLabel);
+ il.Replace(jumpPlaceholder, il.Create(OpCodes.Brfalse_S, jumpLabel));
+
+ currentStloc += 2;
+ }
+ else
+ {
+ var sequencePoint = property.GetMethod.Body.Instructions.First().SequencePoint;
+ LogErrorPoint($"{realmObjectType.Name}.{property.Name} does not have a setter and is not an IList. This is an error in Realm, so please file a bug report.", sequencePoint);
+ }
+ }
+
+ il.Emit(OpCodes.Ret);
+ }
+ copyToRealm.CustomAttributes.Add(new CustomAttribute(_preserveAttributeConstructor));
+ helperType.Methods.Add(copyToRealm);
+
const MethodAttributes ctorAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
var ctor = new MethodDefinition(".ctor", ctorAttributes, ModuleDefinition.TypeSystem.Void);
{
diff --git a/Tests/IntegrationTests.Shared/PerformanceTests.cs b/Tests/IntegrationTests.Shared/PerformanceTests.cs
index 845ca9305d..6fa96dcf71 100644
--- a/Tests/IntegrationTests.Shared/PerformanceTests.cs
+++ b/Tests/IntegrationTests.Shared/PerformanceTests.cs
@@ -15,14 +15,10 @@
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
-
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using NUnit.Framework;
using Realms;
@@ -132,6 +128,92 @@ public void BindingSetValuePerformanceTest(int count)
Console.WriteLine("Time spent: " + sw.Elapsed);
Console.WriteLine("Kilo-iterations per second: {0:0.00}", ((count/1000) / sw.Elapsed.TotalSeconds));
}
+
+ [TestCase(100000), Explicit]
+ public void ManageSmallObjectPerformanceTest(int count)
+ {
+ var objects = new List();
+ for (var i = 0; i < count; i++)
+ {
+ objects.Add(new MiniPerson
+ {
+ Name = "Name" + i,
+ IsInteresting = true
+ });
+ }
+
+ var sw = new Stopwatch();
+ sw.Start();
+
+ _realm.Write(() =>
+ {
+ foreach (var obj in objects)
+ {
+ _realm.Manage(obj);
+ }
+ });
+ sw.Stop();
+ Console.WriteLine($"{count} objects managed for {sw.ElapsedMilliseconds} ms");
+
+ sw.Restart();
+ _realm.Write(() =>
+ {
+ for (var i = 0; i < count; i++)
+ {
+ var newObject = _realm.CreateObject();
+ newObject.Name = objects[i].Name;
+ newObject.IsInteresting = objects[i].IsInteresting;
+ }
+ });
+
+ Console.WriteLine($"{count} objects created for {sw.ElapsedMilliseconds} ms");
+ }
+
+ [TestCase(100000), Explicit]
+ public void ManageLargeObjectPerformanceTest(int count)
+ {
+ var objects = new List();
+ for (var i = 0; i < count; i++)
+ {
+ objects.Add(new Person
+ {
+ FirstName = "Name" + i,
+ IsInteresting = true
+ });
+ }
+
+ var sw = new Stopwatch();
+ sw.Start();
+
+ _realm.Write(() =>
+ {
+ foreach (var obj in objects)
+ {
+ _realm.Manage(obj);
+ }
+ });
+ sw.Stop();
+ Console.WriteLine($"{count} objects managed for {sw.ElapsedMilliseconds} ms");
+
+ sw.Restart();
+ _realm.Write(() =>
+ {
+ for (var i = 0; i < count; i++)
+ {
+ var newObject = _realm.CreateObject();
+ newObject.FirstName = objects[i].FirstName;
+ newObject.IsInteresting = objects[i].IsInteresting;
+ }
+ });
+
+ Console.WriteLine($"{count} objects created for {sw.ElapsedMilliseconds} ms");
+ }
}
-}
+ public class MiniPerson : RealmObject
+ {
+ public string Name { get; set; }
+
+ public bool IsInteresting { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Tests/IntegrationTests.Shared/StandAloneObjectTests.cs b/Tests/IntegrationTests.Shared/StandAloneObjectTests.cs
index d907de9832..0e772141cc 100644
--- a/Tests/IntegrationTests.Shared/StandAloneObjectTests.cs
+++ b/Tests/IntegrationTests.Shared/StandAloneObjectTests.cs
@@ -18,11 +18,9 @@
using NUnit.Framework;
using Realms;
-using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Text;
+using System;
namespace IntegrationTests.Shared
{
@@ -85,5 +83,119 @@ public void RealmObject_WhenStandalone_ShouldHaveDefaultEqualsImplementation()
Assert.DoesNotThrow(() => _person.Equals(otherPerson));
}
+
+ [Test]
+ public void RealmObject_WhenManaged_ShouldNotThrow()
+ {
+ // This is a test to ensure that our weaver is generating valid IL regardless of property configurations
+
+ using (var realm = Realm.GetInstance())
+ {
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new NoListProperties());
+ }), $"{nameof(NoListProperties)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new OnlyListProperties());
+ }), $"{nameof(OnlyListProperties)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new MixedProperties1());
+ }), $"{nameof(MixedProperties1)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new MixedProperties2());
+ }), $"{nameof(MixedProperties2)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new OneNonListProperty());
+ }), $"{nameof(OneNonListProperty)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new OneListProperty());
+ }), $"{nameof(OneListProperty)} manage failed.");
+
+ Assert.DoesNotThrow(() => realm.Write(() =>
+ {
+ realm.Manage(new AllPropsClass());
+ }), $"{nameof(AllPropsClass)} manage failed.");
+ }
+ }
+
+ public class NoListProperties : RealmObject
+ {
+ public string Name { get; set; }
+
+ public int Age { get; set; }
+ }
+
+ public class OnlyListProperties : RealmObject
+ {
+ public IList Friends { get; }
+
+ public IList Enemies { get; }
+ }
+
+ public class MixedProperties1 : RealmObject
+ {
+ public string Name { get; set; }
+
+ public IList Friends { get; }
+
+ public int Age { get; set; }
+
+ public IList Enemies { get; }
+ }
+
+ public class MixedProperties2 : RealmObject
+ {
+ public IList Friends { get; }
+
+ public int Age { get; set; }
+
+ public IList Enemies { get; }
+
+ public string Name { get; set; }
+ }
+
+ public class OneNonListProperty : RealmObject
+ {
+ public string Name { get; set; }
+ }
+
+ public class OneListProperty : RealmObject
+ {
+ public IList People { get; }
+ }
+
+ public class AllPropsClass : RealmObject
+ {
+ public string String { get; set; }
+ public char Char { get; set; }
+ public byte Byte { get; set; }
+ public Int16 Int16 { get; set; }
+ public Int32 Int32 { get; set; }
+ public Int64 Int64 { get; set; }
+ public Single Single { get; set; }
+ public Double Double { get; set; }
+ public DateTimeOffset DateTimeOffset { get; set; }
+ public Boolean Boolean { get; set; }
+ public Byte[] ByteArray { get; set; }
+ public char? NullableChar { get; set; }
+ public byte? NullableByte { get; set; }
+ public Int16? NullableInt16 { get; set; }
+ public Int32? NullableInt32 { get; set; }
+ public Int64? NullableInt64 { get; set; }
+ public Single? NullableSingle { get; set; }
+ public Double? NullableDouble { get; set; }
+ public DateTimeOffset? NullableDateTimeOffset { get; set; }
+ public Boolean? NullableBoolean { get; set; }
+ }
}
}
diff --git a/Tests/WeaverTests/AssemblyToProcess/AssemblyToProcess.csproj b/Tests/WeaverTests/AssemblyToProcess/AssemblyToProcess.csproj
index 4d5e0598d1..cde861561a 100644
--- a/Tests/WeaverTests/AssemblyToProcess/AssemblyToProcess.csproj
+++ b/Tests/WeaverTests/AssemblyToProcess/AssemblyToProcess.csproj
@@ -49,6 +49,7 @@
+
diff --git a/Tests/WeaverTests/AssemblyToProcess/CopyToRealmTestObjects.cs b/Tests/WeaverTests/AssemblyToProcess/CopyToRealmTestObjects.cs
new file mode 100644
index 0000000000..657801aa9f
--- /dev/null
+++ b/Tests/WeaverTests/AssemblyToProcess/CopyToRealmTestObjects.cs
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using Realms;
+using System;
+
+namespace AssemblyToProcess
+{
+ public class NonNullableProperties : RealmObject
+ {
+ public string String { get; set; }
+ public char Char { get; set; }
+ public byte Byte { get; set; }
+ public Int16 Int16 { get; set; }
+ public Int32 Int32 { get; set; }
+ public Int64 Int64 { get; set; }
+ public Single Single { get; set; }
+ public Double Double { get; set; }
+ public DateTimeOffset DateTimeOffset { get; set; }
+ public Boolean Boolean { get; set; }
+ public byte[] ByteArray { get; set; }
+ }
+
+ public class NullableProperties : RealmObject
+ {
+ public char? Char { get; set; }
+ public byte? Byte { get; set; }
+ public Int16? Int16 { get; set; }
+ public Int32? Int32 { get; set; }
+ public Int64? Int64 { get; set; }
+ public Single? Single { get; set; }
+ public Double? Double { get; set; }
+ public DateTimeOffset? DateTimeOffset { get; set; }
+ public Boolean? Boolean { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Tests/WeaverTests/PCLAssemblyToProcess/PCLAssemblyToProcess.csproj b/Tests/WeaverTests/PCLAssemblyToProcess/PCLAssemblyToProcess.csproj
index 42a73220c2..7989820434 100644
--- a/Tests/WeaverTests/PCLAssemblyToProcess/PCLAssemblyToProcess.csproj
+++ b/Tests/WeaverTests/PCLAssemblyToProcess/PCLAssemblyToProcess.csproj
@@ -61,6 +61,9 @@
+
+ CopyToRealmTestObjects.cs
+