Created
June 28, 2018 21:52
-
-
Save disruptek/9479b99a5be24ef997270e1d93a8831e to your computer and use it in GitHub Desktop.
An extremely dangerous extension of PyObject
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* A weaponized version of PyObject. | |
* | |
* This code violates multiple tenets of the Geneva Conventions[0] | |
* the Kyoto Protocol, the United Nations Charter, the Ottawa Treaty, | |
* the Roerich Pact, both the first and second London Naval Treaties, | |
* the St. Petersburg Declaration, and the Nuremberg Priciples. It | |
* may be even run afoul of a few loosely-interpreted sections of the | |
* Paris Climate Accord due to ambiguity in the Farsi translation[1]. | |
* | |
* As such, users may be deemed a terrorist threat in (at time of | |
* writing) no less than 233 countries and 4 vassal city-states.[2] | |
* | |
* In fact, merely by reading this source you may have granted yourself | |
* a one-way ticket to Guantánamo -- though, because you're now also on | |
* the TSC No Fly list, you will be travelling by CIA submarine.[3] | |
* | |
* Finally, products containing WeaponizedPyObject have been pulled | |
* from the shelves in both Minot, North Dakota and Pocatello, Idaho | |
* after reporter Morley Safer revealed on CBS's 60 Minutes that | |
* equipment used in the manufacture of this software had been found | |
* to also have processed wheat, dairy, soy, and peanuts. | |
* | |
* You have been warned. | |
* | |
* [0] Both the 1949 agreement as ratified by 196 countries, and the | |
* lesser-known 2005 agreement with respect to the use of logos | |
* and branding on the battlefield. | |
* | |
* [1] Though if the Farsi translation holds dominion over your locale, | |
* this software is probably the least of your concerns. | |
* | |
* [2] Notably, both the Vatican and the Republic of San Marino have | |
* weighed in strongly against the code herein. | |
* | |
* [3] I'm told there are no window seats and a vegetarian meal option | |
* is no longer offered on outgoing legs of the trip. Budgetary | |
* cutbacks at the Pentagon are apparently to blame for limited | |
* services in the brig. | |
*/ | |
using System; | |
using System.Dynamic; | |
using System.Linq.Expressions; | |
namespace Python.Runtime | |
{ | |
/// <summary> | |
/// Represents a generic Python object. The methods of this class are | |
/// generally equivalent to the Python "abstract object API". See | |
/// PY2: https://docs.python.org/2/c-api/object.html | |
/// PY3: https://docs.python.org/3/c-api/object.html | |
/// for details. | |
/// </summary> | |
public class WeaponizedPyObject : PyObject | |
{ | |
//protected new IntPtr obj = IntPtr.Zero; | |
protected bool disposed = false; | |
/// <summary> | |
/// WeaponizedPyObject Constructor | |
/// </summary> | |
/// <remarks> | |
/// Creates a new WeaponizedPyObject from an IntPtr object reference. | |
/// Semantics match those of PyObject. | |
/// </remarks> | |
public WeaponizedPyObject(IntPtr ptr) : base(ptr) | |
{ | |
} | |
/// <summary> | |
/// HasAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns true if the object has an attribute with the given name. | |
/// </remarks> | |
public new virtual bool HasAttr(string name) | |
{ | |
return (Runtime.PyObject_HasAttrString(obj, name) != 0); | |
} | |
/// <summary> | |
/// HasAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns true if the object has an attribute with the given name, | |
/// where name is a PyObject wrapping a string or unicode object. | |
/// </remarks> | |
public new virtual bool HasAttr(PyObject name) | |
{ | |
return (Runtime.PyObject_HasAttr(obj, name.obj) != 0); | |
} | |
/// <summary> | |
/// Perform_GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Attempt to GetAttr the named attribute from Runtime, | |
/// setting a flag to indicate success. | |
/// </remarks> | |
protected virtual IntPtr Perform_GetAttr(PyObject name, out bool found) | |
{ | |
IntPtr op = Runtime.PyObject_GetAttr(obj, name.obj); | |
found = (op != IntPtr.Zero); | |
return op; | |
} | |
/// <summary> | |
/// Perform_GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Attempt to GetAttr the named attribute from Runtime, | |
/// setting a flag to indicate success. | |
/// </remarks> | |
protected virtual IntPtr Perform_GetAttr(string name, out bool found) | |
{ | |
IntPtr op = Runtime.PyObject_GetAttrString(obj, name); | |
found = (op != IntPtr.Zero); | |
return op; | |
} | |
/// <summary> | |
/// GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns the named attribute of the Python object, or raises a | |
/// PythonException if the attribute access fails. | |
/// </remarks> | |
public new virtual PyObject GetAttr(string name) | |
{ | |
bool found; | |
IntPtr op = Perform_GetAttr(name, out found); | |
if (found) | |
{ | |
return new PyObject(op); | |
} | |
throw new PythonException(); | |
} | |
/// <summary> | |
/// GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns the named attribute of the Python object, or the given | |
/// default object if the attribute access fails. | |
/// </remarks> | |
public new virtual PyObject GetAttr(string name, PyObject _default) | |
{ | |
bool found; | |
IntPtr op = Perform_GetAttr(name, out found); | |
if (found) | |
{ | |
return new PyObject(op); | |
} | |
Runtime.PyErr_Clear(); | |
return _default; | |
} | |
/// <summary> | |
/// GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns the named attribute of the Python object or raises a | |
/// PythonException if the attribute access fails. The name argument | |
/// is a PyObject wrapping a Python string or unicode object. | |
/// </remarks> | |
public new virtual PyObject GetAttr(PyObject name) | |
{ | |
bool found; | |
IntPtr op = Perform_GetAttr(name, out found); | |
if (found) | |
{ | |
return new PyObject(op); | |
} | |
throw new PythonException(); | |
} | |
/// <summary> | |
/// GetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Returns the named attribute of the Python object, or the given | |
/// default object if the attribute access fails. The name argument | |
/// is a PyObject wrapping a Python string or unicode object. | |
/// </remarks> | |
public new virtual PyObject GetAttr(PyObject name, PyObject _default) | |
{ | |
bool found; | |
IntPtr op = Perform_GetAttr(name, out found); | |
if (found) | |
{ | |
return new PyObject(op); | |
} | |
Runtime.PyErr_Clear(); | |
return _default; | |
} | |
/// <summary> | |
/// Perform_SetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Set an attribute of the object with the given name and value and | |
/// return the underlying result from Runtime. Flag success or failure. | |
/// </remarks> | |
protected virtual int Perform_SetAttr(string name, PyObject value, out bool success) | |
{ | |
int result = Runtime.PyObject_SetAttrString(obj, name, value.obj); | |
success = !(result < 0); | |
return result; | |
} | |
/// <summary> | |
/// Perform_SetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Set an attribute of the object with the given name and value and | |
/// return the underlying result from Runtime. Flag success or failure. | |
/// </remarks> | |
protected virtual int Perform_SetAttr(PyObject name, PyObject value, out bool success) | |
{ | |
int result = Runtime.PyObject_SetAttr(obj, name.obj, value.obj); | |
success = !(result < 0); | |
return result; | |
} | |
/// <summary> | |
/// SetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Set an attribute of the object with the given name and value. This | |
/// method throws a PythonException if the attribute set fails. | |
/// </remarks> | |
public new virtual void SetAttr(string name, PyObject value) | |
{ | |
bool success; | |
Perform_SetAttr(name, value, out success); | |
if (!success) | |
{ | |
throw new PythonException(); | |
} | |
} | |
/// <summary> | |
/// SetAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Set an attribute of the object with the given name and value, | |
/// where the name is a Python string or unicode object. This method | |
/// throws a PythonException if the attribute set fails. | |
/// </remarks> | |
public new virtual void SetAttr(PyObject name, PyObject value) | |
{ | |
bool success; | |
Perform_SetAttr(name, value, out success); | |
if (!success) | |
{ | |
throw new PythonException(); | |
} | |
} | |
/// <summary> | |
/// Perform_DelAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Delete the named attribute of the Python object. Set a flag | |
/// to indicate success. | |
/// </remarks> | |
protected virtual void Perform_DelAttr(string name, out bool success) | |
{ | |
success = !(Runtime.PyObject_SetAttrString(obj, name, IntPtr.Zero) < 0); | |
} | |
/// <summary> | |
/// Perform_DelAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Delete the named attribute of the Python object. Set a flag | |
/// to indicate success. | |
/// </remarks> | |
protected virtual void Perform_DelAttr(PyObject name, out bool success) | |
{ | |
success = !(Runtime.PyObject_SetAttr(obj, name.obj, IntPtr.Zero) < 0); | |
} | |
/// <summary> | |
/// DelAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Delete the named attribute of the Python object. This method | |
/// throws a PythonException if the attribute set fails. | |
/// </remarks> | |
public new virtual void DelAttr(string name) | |
{ | |
bool success; | |
Perform_DelAttr(name, out success); | |
if (!success) | |
{ | |
throw new PythonException(); | |
} | |
} | |
/// <summary> | |
/// DelAttr Method | |
/// </summary> | |
/// <remarks> | |
/// Delete the named attribute of the Python object, where name is a | |
/// PyObject wrapping a Python string or unicode object. This method | |
/// throws a PythonException if the attribute set fails. | |
/// </remarks> | |
public new virtual void DelAttr(PyObject name) | |
{ | |
bool success; | |
Perform_DelAttr(name, out success); | |
if (!success) | |
{ | |
throw new PythonException(); | |
} | |
} | |
protected virtual void GetArgs(object[] inargs, CallInfo callInfo, out PyTuple args, out PyDict kwargs) | |
{ | |
if (callInfo == null || callInfo.ArgumentNames.Count == 0) | |
{ | |
GetArgs(inargs, out args, out kwargs); | |
return; | |
} | |
// Support for .net named arguments | |
var namedArgumentCount = callInfo.ArgumentNames.Count; | |
var regularArgumentCount = callInfo.ArgumentCount - namedArgumentCount; | |
var argTuple = Runtime.PyTuple_New(regularArgumentCount); | |
for (int i = 0; i < regularArgumentCount; ++i) | |
{ | |
AddArgument(argTuple, i, inargs[i]); | |
} | |
args = new PyTuple(argTuple); | |
var namedArgs = new object[namedArgumentCount * 2]; | |
for (int i = 0; i < namedArgumentCount; ++i) | |
{ | |
namedArgs[i * 2] = callInfo.ArgumentNames[i]; | |
namedArgs[i * 2 + 1] = inargs[regularArgumentCount + i]; | |
} | |
kwargs = Py.kw(namedArgs); | |
} | |
protected virtual void GetArgs(object[] inargs, out PyTuple args, out PyDict kwargs) | |
{ | |
int argCount; | |
for (argCount = 0; argCount < inargs.Length && !(inargs[argCount] is Py.KeywordArguments);) | |
{ | |
++argCount; | |
} | |
IntPtr argtuple = Runtime.PyTuple_New(argCount); | |
for (var i = 0; i < argCount; i++) | |
{ | |
AddArgument(argtuple, i, inargs[i]); | |
} | |
args = new PyTuple(argtuple); | |
kwargs = null; | |
for (int i = argCount; i < inargs.Length; i++) | |
{ | |
if (!(inargs[i] is Py.KeywordArguments)) | |
{ | |
throw new ArgumentException("Keyword arguments must come after normal arguments."); | |
} | |
if (kwargs == null) | |
{ | |
kwargs = (Py.KeywordArguments)inargs[i]; | |
} | |
else | |
{ | |
kwargs.Update((Py.KeywordArguments)inargs[i]); | |
} | |
} | |
} | |
public static void AddArgument(IntPtr argtuple, int i, object target) | |
{ | |
IntPtr ptr = GetPythonObject(target); | |
if (Runtime.PyTuple_SetItem(argtuple, i, ptr) < 0) | |
{ | |
throw new PythonException(); | |
} | |
} | |
public static IntPtr GetPythonObject(object target) | |
{ | |
IntPtr ptr; | |
if (target is PyObject) | |
{ | |
ptr = ((PyObject)target).Handle; | |
Runtime.XIncref(ptr); | |
} | |
else | |
{ | |
ptr = Converter.ToPython(target, target?.GetType()); | |
} | |
return ptr; | |
} | |
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) | |
{ | |
if (HasAttr(binder.Name) && GetAttr(binder.Name).IsCallable()) | |
{ | |
PyTuple pyargs = null; | |
PyDict kwargs = null; | |
try | |
{ | |
GetArgs(args, binder.CallInfo, out pyargs, out kwargs); | |
result = CheckNone(InvokeMethod(binder.Name, pyargs, kwargs)); | |
} | |
finally | |
{ | |
pyargs?.Dispose(); | |
kwargs?.Dispose(); | |
} | |
return true; | |
} | |
else | |
{ | |
return base.TryInvokeMember(binder, args, out result); | |
} | |
} | |
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) | |
{ | |
if (IsCallable()) | |
{ | |
PyTuple pyargs = null; | |
PyDict kwargs = null; | |
try | |
{ | |
GetArgs(args, binder.CallInfo, out pyargs, out kwargs); | |
result = CheckNone(Invoke(pyargs, kwargs)); | |
} | |
finally | |
{ | |
if (null != pyargs) | |
{ | |
pyargs.Dispose(); | |
} | |
if (null != kwargs) | |
{ | |
kwargs.Dispose(); | |
} | |
} | |
return true; | |
} | |
else | |
{ | |
return base.TryInvoke(binder, args, out result); | |
} | |
} | |
public override bool TryConvert(ConvertBinder binder, out object result) | |
{ | |
return Converter.ToManaged(obj, binder.Type, out result, false); | |
} | |
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) | |
{ | |
IntPtr res; | |
if (!(arg is PyObject)) | |
{ | |
arg = arg.ToPython(); | |
} | |
switch (binder.Operation) | |
{ | |
case ExpressionType.Add: | |
res = Runtime.PyNumber_Add(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.AddAssign: | |
res = Runtime.PyNumber_InPlaceAdd(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.Subtract: | |
res = Runtime.PyNumber_Subtract(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.SubtractAssign: | |
res = Runtime.PyNumber_InPlaceSubtract(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.Multiply: | |
res = Runtime.PyNumber_Multiply(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.MultiplyAssign: | |
res = Runtime.PyNumber_InPlaceMultiply(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.Divide: | |
res = Runtime.PyNumber_Divide(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.DivideAssign: | |
res = Runtime.PyNumber_InPlaceDivide(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.And: | |
res = Runtime.PyNumber_And(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.AndAssign: | |
res = Runtime.PyNumber_InPlaceAnd(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.ExclusiveOr: | |
res = Runtime.PyNumber_Xor(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.ExclusiveOrAssign: | |
res = Runtime.PyNumber_InPlaceXor(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.GreaterThan: | |
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) > 0; | |
return true; | |
case ExpressionType.GreaterThanOrEqual: | |
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) >= 0; | |
return true; | |
case ExpressionType.LeftShift: | |
res = Runtime.PyNumber_Lshift(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.LeftShiftAssign: | |
res = Runtime.PyNumber_InPlaceLshift(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.LessThan: | |
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) < 0; | |
return true; | |
case ExpressionType.LessThanOrEqual: | |
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) <= 0; | |
return true; | |
case ExpressionType.Modulo: | |
res = Runtime.PyNumber_Remainder(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.ModuloAssign: | |
res = Runtime.PyNumber_InPlaceRemainder(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.NotEqual: | |
result = Runtime.PyObject_Compare(obj, ((PyObject)arg).obj) != 0; | |
return true; | |
case ExpressionType.Or: | |
res = Runtime.PyNumber_Or(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.OrAssign: | |
res = Runtime.PyNumber_InPlaceOr(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.Power: | |
res = Runtime.PyNumber_Power(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.RightShift: | |
res = Runtime.PyNumber_Rshift(obj, ((PyObject)arg).obj); | |
break; | |
case ExpressionType.RightShiftAssign: | |
res = Runtime.PyNumber_InPlaceRshift(obj, ((PyObject)arg).obj); | |
break; | |
default: | |
result = null; | |
return false; | |
} | |
result = CheckNone(new PyObject(res)); | |
return true; | |
} | |
/// <summary> | |
/// Invoke Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the callable object with the given arguments, passed as a | |
/// PyObject[]. A PythonException is raised if the invokation fails. | |
/// </remarks> | |
public new virtual PyObject Invoke(params PyObject[] args) | |
{ | |
var t = new PyTuple(args); | |
IntPtr r = Runtime.PyObject_Call(obj, t.obj, IntPtr.Zero); | |
t.Dispose(); | |
if (r == IntPtr.Zero) | |
{ | |
throw new PythonException(); | |
} | |
return new PyObject(r); | |
} | |
/// <summary> | |
/// Invoke Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the callable object with the given arguments, passed as a | |
/// Python tuple. A PythonException is raised if the invokation fails. | |
/// </remarks> | |
public new virtual PyObject Invoke(PyTuple args) | |
{ | |
IntPtr r = Runtime.PyObject_Call(obj, args.obj, IntPtr.Zero); | |
if (r == IntPtr.Zero) | |
{ | |
throw new PythonException(); | |
} | |
return new PyObject(r); | |
} | |
/// <summary> | |
/// Invoke Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the callable object with the given positional and keyword | |
/// arguments. A PythonException is raised if the invokation fails. | |
/// </remarks> | |
public new virtual PyObject Invoke(PyObject[] args, PyDict kw) | |
{ | |
var t = new PyTuple(args); | |
IntPtr r = Runtime.PyObject_Call(obj, t.obj, kw != null ? kw.obj : IntPtr.Zero); | |
t.Dispose(); | |
if (r == IntPtr.Zero) | |
{ | |
throw new PythonException(); | |
} | |
return new PyObject(r); | |
} | |
/// <summary> | |
/// Invoke Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the callable object with the given positional and keyword | |
/// arguments. A PythonException is raised if the invokation fails. | |
/// </remarks> | |
public new virtual PyObject Invoke(PyTuple args, PyDict kw) | |
{ | |
IntPtr r = Runtime.PyObject_Call(obj, args.obj, kw?.obj ?? IntPtr.Zero); | |
if (r == IntPtr.Zero) | |
{ | |
throw new PythonException(); | |
} | |
return new PyObject(r); | |
} | |
/// <summary> | |
/// InvokeMethod Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the named method of the object with the given arguments. | |
/// A PythonException is raised if the invokation is unsuccessful. | |
/// </remarks> | |
public new virtual PyObject InvokeMethod(string name, params PyObject[] args) | |
{ | |
PyObject method = GetAttr(name); | |
PyObject result = method.Invoke(args); | |
method.Dispose(); | |
return result; | |
} | |
/// <summary> | |
/// InvokeMethod Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the named method of the object with the given arguments. | |
/// A PythonException is raised if the invokation is unsuccessful. | |
/// </remarks> | |
public new virtual PyObject InvokeMethod(string name, PyTuple args) | |
{ | |
PyObject method = GetAttr(name); | |
PyObject result = method.Invoke(args); | |
method.Dispose(); | |
return result; | |
} | |
/// <summary> | |
/// InvokeMethod Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the named method of the object with the given arguments | |
/// and keyword arguments. Keyword args are passed as a PyDict object. | |
/// A PythonException is raised if the invokation is unsuccessful. | |
/// </remarks> | |
public new virtual PyObject InvokeMethod(string name, PyObject[] args, PyDict kw) | |
{ | |
PyObject method = GetAttr(name); | |
PyObject result = method.Invoke(args, kw); | |
method.Dispose(); | |
return result; | |
} | |
/// <summary> | |
/// InvokeMethod Method | |
/// </summary> | |
/// <remarks> | |
/// Invoke the named method of the object with the given arguments | |
/// and keyword arguments. Keyword args are passed as a PyDict object. | |
/// A PythonException is raised if the invokation is unsuccessful. | |
/// </remarks> | |
public new virtual PyObject InvokeMethod(string name, PyTuple args, PyDict kw) | |
{ | |
PyObject method = GetAttr(name); | |
PyObject result = method.Invoke(args, kw); | |
method.Dispose(); | |
return result; | |
} | |
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509 | |
// See https://github.com/pythonnet/pythonnet/pull/219 | |
public static object CheckNone(PyObject pyObj) | |
{ | |
if (pyObj == null || pyObj.obj == Runtime.PyNone) | |
{ | |
return null; | |
} | |
return pyObj; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment