/// <summary> /// Helper to get the symbols for __*item__ and __*slice__ based upon if we're doing /// a get/set/delete and the minimum number of arguments required for each of those. /// </summary> private static void GetIndexOperators(PythonIndexType op, out string item, out string slice, out int mandatoryArgs) { switch (op) { case PythonIndexType.GetItem: case PythonIndexType.GetSlice: item = "__getitem__"; slice = "__getslice__"; mandatoryArgs = 2; return; case PythonIndexType.SetItem: case PythonIndexType.SetSlice: item = "__setitem__"; slice = "__setslice__"; mandatoryArgs = 3; return; case PythonIndexType.DeleteItem: case PythonIndexType.DeleteSlice: item = "__delitem__"; slice = "__delslice__"; mandatoryArgs = 2; return; } throw new InvalidOperationException(); }
public SlotCallable(PythonContext/*!*/ binder, PythonIndexType op, PythonTypeSlot slot) : base(binder, op) { _slot = slot; }
private static bool IsSlice(PythonIndexType op) { return op >= PythonIndexType.GetSlice; }
/// <summary> /// Creates a new CallableObject. If BuiltinFunction is available we'll create a BuiltinCallable otherwise /// we create a SlotCallable. /// </summary> public static Callable MakeCallable(PythonContext/*!*/ binder, PythonIndexType op, BuiltinFunction itemFunc, PythonTypeSlot itemSlot) { if (itemFunc != null) { // we'll call a builtin function to produce the rule return new BuiltinCallable(binder, op, itemFunc); } else if (itemSlot != null) { // we'll call a PythonTypeSlot to produce the rule return new SlotCallable(binder, op, itemSlot); } return null; }
public BuiltinCallable(PythonContext/*!*/ binder, PythonIndexType op, BuiltinFunction/*!*/ func) : base(binder, op) { Assert.NotNull(func); _bf = func; }
/// <summary> /// Gets the arguments that need to be provided to __*item__ when we need to pass a slice object. /// </summary> private static DynamicMetaObject/*!*/[]/*!*/ GetItemSliceArguments(PythonContext state, PythonIndexType op, DynamicMetaObject/*!*/[]/*!*/ types) { DynamicMetaObject[] args; if (op == PythonIndexType.SetSlice) { args = new DynamicMetaObject[] { types[0].Restrict(types[0].GetLimitType()), GetSetSlice(state, types), types[types.Length- 1].Restrict(types[types.Length - 1].GetLimitType()) }; } else { Debug.Assert(op == PythonIndexType.GetSlice || op == PythonIndexType.DeleteSlice); args = new DynamicMetaObject[] { types[0].Restrict(types[0].GetLimitType()), GetGetOrDeleteSlice(state, types) }; } return args; }
protected Callable(PythonContext/*!*/ binder, PythonIndexType op) { Assert.NotNull(binder); _binder = binder; _op = op; }
public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { if (args.Length >= 3) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + ", " + args[2].LimitType + args.Length); } else { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Fallback Index " + " " + index + " " + args[0].LimitType + ", " + args[1].LimitType + args.Length); } PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, index.ToString()); if (BindingHelpers.NeedsDeferral(args)) { return operation.Defer(args); } ValidationInfo valInfo = BindingHelpers.GetValidationInfo(args[0]); DynamicMetaObject res = BindingHelpers.AddPythonBoxing(MakeIndexerOperation(operation, index, args, errorSuggestion)); return BindingHelpers.AddDynamicTestAndDefer(operation, res, args, valInfo); }
private static DynamicMetaObject MakeUnindexableError(DynamicMetaObjectBinder operation, PythonIndexType op, DynamicMetaObject/*!*/[] types, DynamicMetaObject indexedType, PythonContext state) { DynamicMetaObject[] newTypes = (DynamicMetaObject[])types.Clone(); newTypes[0] = indexedType; PythonTypeSlot dummySlot; if (op != PythonIndexType.GetItem && op != PythonIndexType.GetSlice && DynamicHelpers.GetPythonType(indexedType.Value).TryResolveSlot(state.SharedContext, "__getitem__", out dummySlot)) { // object supports indexing but not setting/deletion if (op == PythonIndexType.SetItem || op == PythonIndexType.SetSlice) { return TypeError(operation, "'{0}' object does not support item assignment", newTypes); } else { return TypeError(operation, "'{0}' object doesn't support item deletion", newTypes); } } return TypeError(operation, "'{0}' object is unsubscriptable", newTypes); }
/// <summary> /// Python has three protocols for slicing: /// Simple Slicing x[i:j] /// Extended slicing x[i,j,k,...] /// Long Slice x[start:stop:step] /// /// The first maps to __*slice__ (get, set, and del). /// This takes indexes - i, j - which specify the range of elements to be /// returned. In the slice variants both i, j must be numeric data types. /// The 2nd and 3rd are both __*item__. /// This receives a single index which is either a Tuple or a Slice object (which /// encapsulates the start, stop, and step values) /// /// This is in addition to a simple indexing x[y]. /// /// For simple slicing and long slicing Python generates Operators.*Slice. For /// the extended slicing and simple indexing Python generates a Operators.*Item /// action. /// /// Extended slicing maps to the normal .NET multi-parameter input. /// /// So our job here is to first determine if we're to call a __*slice__ method or /// a __*item__ method. /// </summary> private static DynamicMetaObject/*!*/ MakeIndexerOperation(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType op, DynamicMetaObject/*!*/[]/*!*/ types, DynamicMetaObject errorSuggestion) { string item, slice; DynamicMetaObject indexedType = types[0].Restrict(types[0].GetLimitType()); PythonContext state = PythonContext.GetPythonContext(operation); BuiltinFunction itemFunc = null; PythonTypeSlot itemSlot = null; bool callSlice = false; int mandatoryArgs; GetIndexOperators(op, out item, out slice, out mandatoryArgs); if (types.Length == mandatoryArgs + 1 && IsSlice(op) && HasOnlyNumericTypes(operation, types, op == PythonIndexType.SetSlice)) { // two slice indexes, all int arguments, need to call __*slice__ if it exists callSlice = BindingHelpers.TryGetStaticFunction(state, slice, indexedType, out itemFunc); if (itemFunc == null || !callSlice) { callSlice = MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.SharedContext, slice, out itemSlot); } } if (!callSlice) { // 1 slice index (simple index) or multiple slice indexes or no __*slice__, call __*item__, if (!BindingHelpers.TryGetStaticFunction(state, item, indexedType, out itemFunc)) { MetaPythonObject.GetPythonType(indexedType).TryResolveSlot(state.SharedContext, item, out itemSlot); } } // make the Callable object which does the actual call to the function or slot Callable callable = Callable.MakeCallable(state, op, itemFunc, itemSlot); if (callable == null) { return errorSuggestion ?? MakeUnindexableError(operation, op, types, indexedType, state); } // prepare the arguments and make the builder which will // call __*slice__ or __*item__ DynamicMetaObject[] args; IndexBuilder builder; if (callSlice) { // we're going to call a __*slice__ method, we pass the args as is. Debug.Assert(IsSlice(op)); builder = new SliceBuilder(types, callable); // slicing is dependent upon the types of the arguments (HasNumericTypes) // so we must restrict them. args = ConvertArgs(types); } else { // we're going to call a __*item__ method. builder = new ItemBuilder(types, callable); if (IsSlice(op)) { // we need to create a new Slice object. args = GetItemSliceArguments(state, op, types); } else { // no need to restrict the arguments. We're not // a slice and so restrictions are not necessary // here because it's not dependent upon our types. args = (DynamicMetaObject[])types.Clone(); // but we do need to restrict based upon the type // of object we're calling on. args[0] = types[0].Restrict(types[0].GetLimitType()); } } return builder.MakeRule(operation, state, args); }
public static DynamicMetaObject/*!*/ Index(DynamicMetaObjectBinder/*!*/ operation, PythonIndexType index, DynamicMetaObject[] args) { return Index(operation, index, args, null); }
/// <summary> /// Helper to get the symbols for __*item__ and __*slice__ based upon if we're doing /// a get/set/delete and the minimum number of arguments required for each of those. /// </summary> private static void GetIndexOperators(PythonIndexType op, out SymbolId item, out SymbolId slice, out int mandatoryArgs) { switch (op) { case PythonIndexType.GetItem: case PythonIndexType.GetSlice: item = Symbols.GetItem; slice = Symbols.GetSlice; mandatoryArgs = 2; return; case PythonIndexType.SetItem: case PythonIndexType.SetSlice: item = Symbols.SetItem; slice = Symbols.SetSlice; mandatoryArgs = 3; return; case PythonIndexType.DeleteItem: case PythonIndexType.DeleteSlice: item = Symbols.DelItem; slice = Symbols.DeleteSlice; mandatoryArgs = 2; return; } throw new InvalidOperationException(); }
public SlotCallable(BinderState/*!*/ binder, PythonIndexType op, PythonTypeSlot slot) : base(binder, op) { _slot = slot; }
protected Callable(BinderState/*!*/ binder, PythonIndexType op) { Assert.NotNull(binder); _binder = binder; _op = op; }