// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using IronPython.Runtime;
using IronPython.Runtime.Exceptions;
using IronPython.Runtime.Operations;
using IronPython.Runtime.Types;
using Microsoft.Scripting;
using Microsoft.Scripting.Actions;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Utils;
[assembly: PythonModule("_weakref", typeof(IronPython.Modules.PythonWeakRef))]
namespace IronPython.Modules {
public static partial class PythonWeakRef {
public const string __doc__ = "Provides support for creating weak references and proxies to objects";
///
/// Wrapper provided for backwards compatibility.
///
internal static IWeakReferenceable ConvertToWeakReferenceable(PythonContext context, object obj) {
return context.ConvertToWeakReferenceable(obj);
}
public static int getweakrefcount(CodeContext context, object @object) {
return @ref.GetWeakRefCount(context.LanguageContext, @object);
}
public static PythonList getweakrefs(CodeContext context, object @object) {
return @ref.GetWeakRefs(context.LanguageContext, @object);
}
public static object proxy(CodeContext context, object @object) {
return proxy(context, @object, null);
}
public static object proxy(CodeContext context, object @object, object callback) {
if (PythonOps.IsCallable(context, @object)) {
return weakcallableproxy.MakeNew(context, @object, callback);
} else {
return weakproxy.MakeNew(context, @object, callback);
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly PythonType CallableProxyType = DynamicHelpers.GetPythonTypeFromType(typeof(weakcallableproxy));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly PythonType ProxyType = DynamicHelpers.GetPythonTypeFromType(typeof(weakproxy));
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
public static readonly PythonType ReferenceType = DynamicHelpers.GetPythonTypeFromType(typeof(@ref));
[PythonType]
public class @ref : IStructuralEquatable
{
private CodeContext _context;
private WeakHandle _target;
private long _targetId;
private int _hashVal;
private bool _fHasHash;
#region Python Constructors
public static object __new__(CodeContext context, PythonType cls, object @object) {
IWeakReferenceable iwr = ConvertToWeakReferenceable(context.LanguageContext, @object);
if (cls == DynamicHelpers.GetPythonTypeFromType(typeof(@ref))) {
WeakRefTracker wrt = iwr.GetWeakRef();
if (wrt != null) {
for (int i = 0; i < wrt.HandlerCount; i++) {
if (wrt.GetHandlerCallback(i) == null && wrt.GetWeakRef(i) is @ref) {
return wrt.GetWeakRef(i);
}
}
}
return new @ref(context, @object);
} else {
return cls.CreateInstance(context, @object);
}
}
public static object __new__(CodeContext context, PythonType cls, object @object, object callback) {
if (callback == null) return __new__(context, cls, @object);
if (cls == DynamicHelpers.GetPythonTypeFromType(typeof(@ref))) {
return new @ref(context, @object, callback);
} else {
return cls.CreateInstance(context, @object, callback);
}
}
public void __init__(CodeContext context, object ob, object callback = null) {
_context = context;
WeakRefTracker wrt = WeakRefHelpers.InitializeWeakRef(_context.LanguageContext, this, ob, callback);
_target = new WeakHandle(ob, false);
_targetId = wrt.TargetId;
}
#endregion
#region Constructors
public @ref(CodeContext context, object @object, object callback = null) {
// the work is actually done on the call to __init__
}
#endregion
#region Finalizer
~@ref() {
IWeakReferenceable iwr;
if (_context.LanguageContext.TryConvertToWeakReferenceable(_target.Target, out iwr))
{
WeakRefTracker wrt = iwr.GetWeakRef();
if (wrt != null) {
// weak reference being finalized before target object,
// we don't want to run the callback when the object is
// finalized.
wrt.RemoveHandler(this);
}
}
_target.Free();
}
#endregion
#region Static helpers
internal static int GetWeakRefCount(PythonContext context, object o) {
IWeakReferenceable iwr;
if (context.TryConvertToWeakReferenceable(o, out iwr)) {
WeakRefTracker wrt = iwr.GetWeakRef();
if (wrt != null) return wrt.HandlerCount;
}
return 0;
}
internal static PythonList GetWeakRefs(PythonContext context, object o) {
PythonList l = new PythonList();
IWeakReferenceable iwr;
if (context.TryConvertToWeakReferenceable(o, out iwr)) {
WeakRefTracker wrt = iwr.GetWeakRef();
if (wrt != null) {
for (int i = 0; i < wrt.HandlerCount; i++) {
l.AddNoLock(wrt.GetWeakRef(i));
}
}
}
return l;
}
#endregion
[SpecialName]
public object Call(CodeContext context) {
object res = _target.Target;
GC.KeepAlive(this);
return res;
}
[return: MaybeNotImplemented]
public static NotImplementedType operator >(@ref self, object other) {
return PythonOps.NotImplemented;
}
[return: MaybeNotImplemented]
public static NotImplementedType operator <(@ref self, object other) {
return PythonOps.NotImplemented;
}
[return: MaybeNotImplemented]
public static NotImplementedType operator <=(@ref self, object other) {
return PythonOps.NotImplemented;
}
[return: MaybeNotImplemented]
public static NotImplementedType operator >=(@ref self, object other) {
return PythonOps.NotImplemented;
}
#region IStructuralEquatable Members
///
/// Special hash function because IStructuralEquatable.GetHashCode is not allowed to throw.
///
public int __hash__(CodeContext/*!*/ context) {
if (!_fHasHash) {
object refObj = _target.Target;
if (refObj == null) throw PythonOps.TypeError("weak object has gone away");
_hashVal = context.LanguageContext.EqualityComparerNonGeneric.GetHashCode(refObj);
_fHasHash = true;
}
GC.KeepAlive(this);
return _hashVal;
}
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
if (!_fHasHash) {
object refObj = _target.Target;
_hashVal = comparer.GetHashCode(refObj);
_fHasHash = true;
}
GC.KeepAlive(this);
return _hashVal;
}
bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) {
return EqualsWorker(other, comparer);
}
private bool EqualsWorker(object other, IEqualityComparer comparer) {
if (object.ReferenceEquals(this, other)) {
return true;
}
bool fResult = false;
@ref wr = other as @ref;
if (wr != null) {
object ourTarget = _target.Target;
object itsTarget = wr._target.Target;
if (ourTarget != null && itsTarget != null) {
fResult = RefEquals(ourTarget, itsTarget, comparer);
} else {
fResult = (_targetId == wr._targetId);
}
}
GC.KeepAlive(this);
return fResult;
}
///
/// Special equals because none of the special cases in Ops.Equals
/// are applicable here, and the reference equality check breaks some tests.
///
private static bool RefEquals(object x, object y, IEqualityComparer comparer) {
CodeContext context;
if (comparer != null && comparer is PythonContext.PythonEqualityComparer) {
context = ((PythonContext.PythonEqualityComparer)comparer).Context.SharedContext;
} else {
context = DefaultContext.Default;
}
object ret;
if (PythonTypeOps.TryInvokeBinaryOperator(context, x, y, "__eq__", out ret) &&
ret != NotImplementedType.Value) {
return (bool)ret;
}
if (PythonTypeOps.TryInvokeBinaryOperator(context, y, x, "__eq__", out ret) &&
ret != NotImplementedType.Value) {
return (bool)ret;
}
if (comparer != null) {
return comparer.Equals(x, y);
}
return x.Equals(y);
}
#endregion
}
[PythonType, DynamicBaseTypeAttribute, PythonHidden]
public sealed partial class weakproxy : IPythonObject, ICodeFormattable, IProxyObject, IPythonMembersList, IStructuralEquatable
{
private readonly WeakHandle _target;
private readonly CodeContext/*!*/ _context;
#region Python Constructors
internal static object MakeNew(CodeContext/*!*/ context, object @object, object callback) {
IWeakReferenceable iwr = ConvertToWeakReferenceable(context.LanguageContext, @object);
if (callback == null) {
WeakRefTracker wrt = iwr.GetWeakRef();
if (wrt != null) {
for (int i = 0; i < wrt.HandlerCount; i++) {
if (wrt.GetHandlerCallback(i) == null && wrt.GetWeakRef(i) is weakproxy) {
return wrt.GetWeakRef(i);
}
}
}
}
return new weakproxy(context, @object, callback);
}
#endregion
#region Constructors
private weakproxy(CodeContext/*!*/ context, object target, object callback) {
WeakRefHelpers.InitializeWeakRef(context.LanguageContext, this, target, callback);
_target = new WeakHandle(target, false);
_context = context;
}
#endregion
#region Finalizer
~weakproxy() {
// remove our self from the chain...
IWeakReferenceable iwr;
if (_context.LanguageContext.TryConvertToWeakReferenceable(_target.Target, out iwr)) {
WeakRefTracker wrt = iwr.GetWeakRef();
wrt.RemoveHandler(this);
}
_target.Free();
}
#endregion
#region private members
///
/// gets the object or throws a reference exception
///
private object GetObject() {
object res;
if (!TryGetObject(out res)) {
throw PythonOps.ReferenceError("weakly referenced object no longer exists");
}
return res;
}
private bool TryGetObject(out object result) {
result = _target.Target;
if (result == null) return false;
GC.KeepAlive(this);
return true;
}
#endregion
#region IPythonObject Members
PythonDictionary IPythonObject.Dict {
get {
IPythonObject sdo = GetObject() as IPythonObject;
if (sdo != null) {
return sdo.Dict;
}
return null;
}
}
PythonDictionary IPythonObject.SetDict(PythonDictionary dict) {
return (GetObject() as IPythonObject).SetDict(dict);
}
bool IPythonObject.ReplaceDict(PythonDictionary dict) {
return (GetObject() as IPythonObject).ReplaceDict(dict);
}
void IPythonObject.SetPythonType(PythonType newType) {
(GetObject() as IPythonObject).SetPythonType(newType);
}
PythonType IPythonObject.PythonType {
get {
return DynamicHelpers.GetPythonTypeFromType(typeof(weakproxy));
}
}
object[] IPythonObject.GetSlots() { return null; }
object[] IPythonObject.GetSlotsCreate() { return null; }
#endregion
#region object overloads
public override string ToString() {
return PythonOps.ToString(GetObject());
}
#endregion
#region ICodeFormattable Members
public string/*!*/ __repr__(CodeContext/*!*/ context) {
object obj = _target.Target;
GC.KeepAlive(this);
return String.Format("",
IdDispenser.GetId(this),
PythonOps.GetPythonTypeName(obj),
IdDispenser.GetId(obj));
}
#endregion
#region Custom member access
[SpecialName]
public object GetCustomMember(CodeContext/*!*/ context, string name) {
object value, o = GetObject();
if (PythonOps.TryGetBoundAttr(context, o, name, out value)) {
return value;
}
return OperationFailed.Value;
}
[SpecialName]
public void SetMember(CodeContext/*!*/ context, string name, object value) {
object o = GetObject();
PythonOps.SetAttr(context, o, name, value);
}
[SpecialName]
public void DeleteMember(CodeContext/*!*/ context, string name) {
object o = GetObject();
PythonOps.DeleteAttr(context, o, name);
}
IList IMembersList.GetMemberNames() {
return PythonOps.GetStringMemberList(this);
}
IList