From 808fb0a27213c9be56677ef429910dde1dcac333 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 11 Jul 2022 16:16:32 +0200 Subject: [PATCH] Implement non-simple String constructors explicitly --- src/runtime/Types/ClassObject.cs | 51 ++++++++++++++++++++++++++++---- tests/test_constructors.py | 17 +++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 70ec53b18..cc42039e8 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -106,24 +106,63 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo /// /// Construct a new .NET String object from Python args + /// + /// This manual implementation of all individual relevant constructors + /// is required because System.String can't be allocated uninitialized. + /// + /// Additionally, it implements `String(pythonStr)` /// private static NewReference NewString(BorrowedReference args, BorrowedReference tp) { - if (Runtime.PyTuple_Size(args) == 1) + var argCount = Runtime.PyTuple_Size(args); + + string? result = null; + if (argCount == 1) { BorrowedReference ob = Runtime.PyTuple_GetItem(args, 0); if (Runtime.PyString_Check(ob)) { if (Runtime.GetManagedString(ob) is string val) - return CLRObject.GetReference(val, tp); + result = val; } + else if (Converter.ToManagedValue(ob, typeof(char[]), out object? arr, false)) + { + result = new String((char[])arr!); + } + } + else if (argCount == 2) + { + BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0); + BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1); - // TODO: Initialise using constructors instead - - Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); - return default; + if ( + Converter.ToManagedValue(p1, typeof(char), out object? chr, false) && + Converter.ToManagedValue(p2, typeof(int), out object? count, false) + ) + { + result = new String((char)chr!, (int)count!); + } + } + else if (argCount == 3) + { + BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0); + BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1); + BorrowedReference p3 = Runtime.PyTuple_GetItem(args, 2); + + if ( + Converter.ToManagedValue(p1, typeof(char[]), out object? arr, false) && + Converter.ToManagedValue(p2, typeof(int), out object? offset, false) && + Converter.ToManagedValue(p3, typeof(int), out object? length, false) + ) + { + result = new String((char[])arr!, (int)offset!, (int)length!); + } } + if (result != null) + return CLRObject.GetReference(result!, tp); + + Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); return default; } diff --git a/tests/test_constructors.py b/tests/test_constructors.py index 8e7ef2794..6d8f00c12 100644 --- a/tests/test_constructors.py +++ b/tests/test_constructors.py @@ -69,3 +69,20 @@ def test_default_constructor_fallback(): with pytest.raises(TypeError): ob = DefaultConstructorMatching("2") + + +def test_string_constructor(): + from System import String, Char, Array + + ob = String('A', 10) + assert ob == 'A' * 10 + + arr = Array[Char](10) + for i in range(10): + arr[i] = Char(str(i)) + + ob = String(arr) + assert ob == "0123456789" + + ob = String(arr, 5, 4) + assert ob == "5678"