Skip to content

Commit

Permalink
Get more dynamic APIs working
Browse files Browse the repository at this point in the history
  • Loading branch information
nirinchev committed Sep 9, 2020
1 parent 3ca929b commit 2c912a2
Show file tree
Hide file tree
Showing 19 changed files with 444 additions and 226 deletions.
4 changes: 4 additions & 0 deletions Realm/Realm.Fody/Extensions/PropertyDefinitionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ internal static bool IsIndexable(this PropertyDefinition property, ImportedRefer

public static bool IsValidRealmObjectBaseInheritor(this TypeDefinition type, ImportedReferences references) => type.IsEmbeddedObjectInheritor(references) || type.IsRealmObjectInheritor(references);

public static bool ContainsRealmObject(this PropertyDefinition property, ImportedReferences references) => property.PropertyType.Resolve().IsRealmObjectInheritor(references);

public static bool ContainsEmbeddedObject(this PropertyDefinition property, ImportedReferences references) => property.PropertyType.Resolve().IsEmbeddedObjectInheritor(references);

public static bool IsRealmInteger(this TypeReference type, out bool isNullable, out TypeReference genericArgumentType)
{
var nullableMatch = NullableRegex.Match(type.FullName);
Expand Down
8 changes: 4 additions & 4 deletions Realm/Realm.Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ private WeaveResult WeaveProperty(PropertyDefinition prop, TypeDefinition type,

if (!prop.IsAutomatic())
{
if (prop.PropertyType.Resolve().IsValidRealmObjectBaseInheritor(_references))
if (prop.ContainsRealmObject(_references) || prop.ContainsEmbeddedObject(_references))
{
return WeaveResult.Warning($"{type.Name}.{prop.Name} is not an automatic property but its type is a RealmObject/EmbeddedObject which normally indicates a relationship.");
}
Expand Down Expand Up @@ -491,7 +491,7 @@ private WeaveResult WeaveProperty(PropertyDefinition prop, TypeDefinition type,
new GenericInstanceMethod(_references.RealmObject_GetListValue) { GenericArguments = { elementType } },
concreteListConstructor);
}
else if (prop.PropertyType.Resolve().IsValidRealmObjectBaseInheritor(_references))
else if (prop.ContainsRealmObject(_references) || prop.ContainsEmbeddedObject(_references))
{
// with casting in the _realmObject methods, should just work
ReplaceGetter(prop, columnName,
Expand Down Expand Up @@ -1086,7 +1086,7 @@ private TypeDefinition WeaveRealmObjectHelper(TypeDefinition realmObjectType, Me
// property setting logic. The default check branching instruction is inserted above the *setStartPoint*
// instruction later on.
Instruction skipDefaultsPlaceholder = null;
if (property.PropertyType.Resolve().IsRealmObjectInheritor(_references))
if (property.ContainsRealmObject(_references))
{
il.Append(il.Create(OpCodes.Ldloc_0));
il.Append(il.Create(OpCodes.Ldfld, field));
Expand Down Expand Up @@ -1130,7 +1130,7 @@ private TypeDefinition WeaveRealmObjectHelper(TypeDefinition realmObjectType, Me
var setEndPoint = il.Create(OpCodes.Nop);
il.Append(setEndPoint);

if (property.IsDescendantOf(_references.RealmObjectBase))
if (property.ContainsRealmObject(_references))
{
if (addPlaceholder != null)
{
Expand Down
35 changes: 35 additions & 0 deletions Realm/Realm/Dynamic/DynamicEmbeddedObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
////////////////////////////////////////////////////////////////////////////
//
// 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 System.ComponentModel;
using System.Dynamic;
using System.Linq.Expressions;

namespace Realms.Dynamic
{

[EditorBrowsable(EditorBrowsableState.Never)]
[Ignored]
public class DynamicEmbeddedObject : EmbeddedObject, IDynamicMetaObjectProvider
{
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new MetaRealmObject(parameter, this);
}
}
}
17 changes: 16 additions & 1 deletion Realm/Realm/Dynamic/DynamicRealmObjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ namespace Realms.Dynamic
{
internal class DynamicRealmObjectHelper : IRealmObjectHelper
{
internal static readonly DynamicRealmObjectHelper Instance = new DynamicRealmObjectHelper();
private static readonly DynamicRealmObjectHelper _embeddedInstance = new DynamicRealmObjectHelper(embedded: true);
private static readonly DynamicRealmObjectHelper _objectInstance = new DynamicRealmObjectHelper(embedded: false);

private readonly bool _embedded;

internal static DynamicRealmObjectHelper Instance(bool embedded) => embedded ? _embeddedInstance : _objectInstance;

private DynamicRealmObjectHelper(bool embedded)
{
_embedded = embedded;
}

public void CopyToRealm(RealmObjectBase instance, bool update, bool setPrimaryKey)
{
Expand All @@ -32,6 +42,11 @@ public void CopyToRealm(RealmObjectBase instance, bool update, bool setPrimaryKe

public RealmObjectBase CreateInstance()
{
if (_embedded)
{
return new DynamicEmbeddedObject();
}

return new DynamicRealmObject();
}

Expand Down
1 change: 0 additions & 1 deletion Realm/Realm/Dynamic/MetaRealmList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Realms.Dynamic
{
Expand Down
30 changes: 22 additions & 8 deletions Realm/Realm/Dynamic/MetaRealmObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.Dynamic;
using System.Linq.Expressions;
using System.Reflection;
using Realms.Exceptions;
using Realms.Native;
using Realms.Schema;

Expand All @@ -35,15 +36,17 @@ internal class MetaRealmObject : DynamicMetaObject

private static readonly FieldInfo RealmObjectRealmField = typeof(RealmObjectBase).GetField("_realm", PrivateBindingFlags);
private static readonly FieldInfo RealmObjectObjectHandleField = typeof(RealmObjectBase).GetField("_objectHandle", PrivateBindingFlags);
private static readonly MethodInfo RealmObjectGetBacklinksForHandleMethod = typeof(RealmObjectBase).GetMethod("GetBacklinksForHandle", PrivateBindingFlags)

// V10TODO: this should be restricted by embedded objects if necessary
private static readonly MethodInfo RealmObjectGetBacklinksForHandleMethod = typeof(DynamicRealmObject).GetMethod("GetBacklinksForHandle", PrivateBindingFlags)
.MakeGenericMethod(typeof(DynamicRealmObject));

private static readonly MethodInfo PrimitiveValueGetMethod = typeof(PrimitiveValue).GetMethod(nameof(PrimitiveValue.Get), BindingFlags.Public | BindingFlags.Instance);
private static readonly MethodInfo CreatePrimitiveMethod = typeof(PrimitiveValue).GetMethod(nameof(PrimitiveValue.Create), BindingFlags.Public | BindingFlags.Static);

private static readonly ObjectHandle DummyHandle = new ObjectHandle(null, IntPtr.Zero);

public MetaRealmObject(Expression expression, DynamicRealmObject value)
public MetaRealmObject(Expression expression, RealmObjectBase value)
: base(expression, BindingRestrictions.Empty, value)
{
_realm = value.Realm;
Expand Down Expand Up @@ -133,7 +136,7 @@ public override DynamicMetaObject BindGetMember(GetMemberBinder binder)

break;
case PropertyType.Object:
getter = GetGetMethod(DummyHandle.GetList<DynamicRealmObject>);
getter = IsTargetEmbedded(property) ? GetGetMethod(DummyHandle.GetList<DynamicEmbeddedObject>) : GetGetMethod(DummyHandle.GetList<DynamicRealmObject>);
break;
}
}
Expand Down Expand Up @@ -161,7 +164,8 @@ public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
case PropertyType.Object:
arguments.Insert(0, Expression.Field(GetLimitedSelf(), RealmObjectRealmField));
arguments.Add(Expression.Constant(property.ObjectType));
getter = GetGetMethod(DummyHandle.GetObject<DynamicRealmObject>);

getter = IsTargetEmbedded(property) ? GetGetMethod(DummyHandle.GetObject<DynamicEmbeddedObject>) : GetGetMethod(DummyHandle.GetObject<DynamicRealmObject>);
break;
}
}
Expand All @@ -185,7 +189,7 @@ public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
expression = Expression.Convert(expression, binder.ReturnType);
}

var argumentShouldBeDynamicRealmObject = BindingRestrictions.GetTypeRestriction(Expression, typeof(DynamicRealmObject));
var argumentShouldBeDynamicRealmObject = BindingRestrictions.GetTypeRestriction(Expression, _metadata.Schema.IsEmbedded ? typeof(DynamicEmbeddedObject) : typeof(DynamicRealmObject));
var argumentShouldBeInTheSameRealm = BindingRestrictions.GetInstanceRestriction(Expression.Field(self, RealmObjectRealmField), _realm);
return new DynamicMetaObject(expression, argumentShouldBeDynamicRealmObject.Merge(argumentShouldBeInTheSameRealm));
}
Expand Down Expand Up @@ -246,7 +250,7 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM
break;
case PropertyType.Object:
argumentType = typeof(RealmObjectBase);
arguments.Insert(0, Expression.Constant(GetLimitedSelf()));
arguments.Insert(0, Expression.Field(GetLimitedSelf(), RealmObjectRealmField));
setter = GetSetMethod<RealmObjectBase>(DummyHandle.SetObject);
break;
}
Expand All @@ -260,7 +264,7 @@ public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicM

var expression = Expression.Block(Expression.Call(Expression.Field(GetLimitedSelf(), RealmObjectObjectHandleField), setter, arguments), Expression.Default(binder.ReturnType));

var argumentShouldBeDynamicRealmObject = BindingRestrictions.GetTypeRestriction(Expression, typeof(DynamicRealmObject));
var argumentShouldBeDynamicRealmObject = BindingRestrictions.GetTypeRestriction(Expression, _metadata.Schema.IsEmbedded ? typeof(DynamicEmbeddedObject) : typeof(DynamicRealmObject));
var argumentShouldBeInTheSameRealm = BindingRestrictions.GetInstanceRestriction(Expression.Field(GetLimitedSelf(), RealmObjectRealmField), _realm);
return new DynamicMetaObject(expression, argumentShouldBeDynamicRealmObject.Merge(argumentShouldBeInTheSameRealm));
}
Expand All @@ -281,6 +285,16 @@ private Expression GetLimitedSelf()
return convertedExpression;
}

private bool IsTargetEmbedded(Property property)
{
if (!_realm.Metadata.TryGetValue(property.ObjectType, out var metadata))
{
throw new RealmException($"Couldn't find metadata for type {property.ObjectType}.");
}

return metadata.Schema.IsEmbedded;
}

// GetString(colKey)
// GetByteArray(colKey)
private static MethodInfo GetGetMethod<TResult>(Func<ColumnKey, TResult> @delegate) => @delegate.GetMethodInfo();
Expand All @@ -299,6 +313,6 @@ private Expression GetLimitedSelf()
private static MethodInfo GetSetMethod<TValue>(Action<ColumnKey, TValue> @delegate) => @delegate.GetMethodInfo();

// SetObject(this, colKey)
private static MethodInfo GetSetMethod<TValue>(Action<RealmObjectBase, ColumnKey, TValue> @delegate) => @delegate.GetMethodInfo();
private static MethodInfo GetSetMethod<TValue>(Action<Realm, ColumnKey, TValue> @delegate) => @delegate.GetMethodInfo();
}
}
4 changes: 1 addition & 3 deletions Realm/Realm/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

[assembly: SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "Copied from Xamarin's implementation", Scope = "type", Target = "~T:Realms.PreserveAttribute")]
[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Copied from Xamarin's implementation", Scope = "type", Target = "~T:Realms.PreserveAttribute")]
[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "This is checked in the base class.", Scope = "member", Target = "~M:Realms.Sync.FullSyncConfiguration.#ctor(System.Uri,Realms.Sync.User,System.String)")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicRealmObject")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Shouldn't be used directly.", Scope = "type", Target = "~T:Realms.Dynamic.DynamicEmbeddedObject")]
[assembly: SuppressMessage("Performance", "CA1820:Test for empty strings using string length", Justification = "We're only capturing the method group.", Scope = "type", Target = "~T:Realms.RealmResultsVisitor.Methods.String")]
[assembly: SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "index is the argument name for ElementAt.", Scope = "member", Target = "~M:Realms.RealmResultsVisitor.VisitMethodCall(System.Linq.Expressions.MethodCallExpression)~System.Linq.Expressions.Expression")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Native structs use verbose names.", Scope = "namespaceanddescendants", Target = "Realms.Native")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Native structs use snake_case fields.", Scope = "namespaceanddescendants", Target = "Realms.Native")]
[assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Native structs use verbose names.", Scope = "namespaceanddescendants", Target = "Realms.Server.Native")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Native structs use snake_case fields.", Scope = "namespaceanddescendants", Target = "Realms.Server.Native")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.NativeCommon")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Native methods are snake_case.", Scope = "type", Target = "~T:Realms.NativeCommon")]
[assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Native structs use snake_case fields.", Scope = "type", Target = "~T:Realms.NativeException")]
Expand Down
17 changes: 11 additions & 6 deletions Realm/Realm/Handles/ObjectHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ public T GetObject<T>(Realm realm, ColumnKey columnKey, string objectType)
return null;
}

public void SetObject(RealmObjectBase parent, ColumnKey columnKey, RealmObjectBase @object)
public void SetObject(Realm realm, ColumnKey columnKey, RealmObjectBase @object)
{
if (@object == null)
{
Expand All @@ -329,7 +329,7 @@ public void SetObject(RealmObjectBase parent, ColumnKey columnKey, RealmObjectBa
{
if (!realmObj.IsManaged)
{
parent.Realm.Add(realmObj);
realm.Add(realmObj);
}

SetLink(columnKey, realmObj.ObjectHandle);
Expand All @@ -341,17 +341,22 @@ public void SetObject(RealmObjectBase parent, ColumnKey columnKey, RealmObjectBa
throw new RealmException("Can't link to an embedded object that is already managed.");
}

var objPtr = NativeMethods.create_embedded_link(this, columnKey, out var ex);
ex.ThrowIfNecessary();
var handle = new ObjectHandle(parent.Realm.SharedRealmHandle, objPtr);
parent.Realm.ManageEmbedded(embeddedObj, handle);
var handle = CreateEmbeddedObjectForProperty(columnKey);
realm.ManageEmbedded(embeddedObj, handle);
}
else
{
throw new NotSupportedException($"Tried to add an object of type {@object.GetType().FullName} which does not inherit from RealmObject or EmbeddedObject");
}
}

public ObjectHandle CreateEmbeddedObjectForProperty(ColumnKey columnKey)
{
var objPtr = NativeMethods.create_embedded_link(this, columnKey, out var ex);
ex.ThrowIfNecessary();
return new ObjectHandle(Root, objPtr);
}

public ResultsHandle GetBacklinks(IntPtr propertyIndex)
{
var resultsPtr = NativeMethods.get_backlinks(this, propertyIndex, out var nativeException);
Expand Down
Loading

0 comments on commit 2c912a2

Please sign in to comment.