Skip to content

Commit 60a719c

Browse files
authored
Implement non-simple String constructors explicitly (#1862)
1 parent 332f8e7 commit 60a719c

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

src/runtime/Types/ClassObject.cs

+45-6
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,63 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
106106

107107
/// <summary>
108108
/// Construct a new .NET String object from Python args
109+
///
110+
/// This manual implementation of all individual relevant constructors
111+
/// is required because System.String can't be allocated uninitialized.
112+
///
113+
/// Additionally, it implements `String(pythonStr)`
109114
/// </summary>
110115
private static NewReference NewString(BorrowedReference args, BorrowedReference tp)
111116
{
112-
if (Runtime.PyTuple_Size(args) == 1)
117+
var argCount = Runtime.PyTuple_Size(args);
118+
119+
string? result = null;
120+
if (argCount == 1)
113121
{
114122
BorrowedReference ob = Runtime.PyTuple_GetItem(args, 0);
115123
if (Runtime.PyString_Check(ob))
116124
{
117125
if (Runtime.GetManagedString(ob) is string val)
118-
return CLRObject.GetReference(val, tp);
126+
result = val;
119127
}
128+
else if (Converter.ToManagedValue(ob, typeof(char[]), out object? arr, false))
129+
{
130+
result = new String((char[])arr!);
131+
}
132+
}
133+
else if (argCount == 2)
134+
{
135+
BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0);
136+
BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1);
120137

121-
// TODO: Initialise using constructors instead
122-
123-
Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments");
124-
return default;
138+
if (
139+
Converter.ToManagedValue(p1, typeof(char), out object? chr, false) &&
140+
Converter.ToManagedValue(p2, typeof(int), out object? count, false)
141+
)
142+
{
143+
result = new String((char)chr!, (int)count!);
144+
}
145+
}
146+
else if (argCount == 3)
147+
{
148+
BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0);
149+
BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1);
150+
BorrowedReference p3 = Runtime.PyTuple_GetItem(args, 2);
151+
152+
if (
153+
Converter.ToManagedValue(p1, typeof(char[]), out object? arr, false) &&
154+
Converter.ToManagedValue(p2, typeof(int), out object? offset, false) &&
155+
Converter.ToManagedValue(p3, typeof(int), out object? length, false)
156+
)
157+
{
158+
result = new String((char[])arr!, (int)offset!, (int)length!);
159+
}
125160
}
126161

162+
if (result != null)
163+
return CLRObject.GetReference(result!, tp);
164+
165+
Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments");
127166
return default;
128167
}
129168

tests/test_constructors.py

+17
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,20 @@ def test_default_constructor_fallback():
6969

7070
with pytest.raises(TypeError):
7171
ob = DefaultConstructorMatching("2")
72+
73+
74+
def test_string_constructor():
75+
from System import String, Char, Array
76+
77+
ob = String('A', 10)
78+
assert ob == 'A' * 10
79+
80+
arr = Array[Char](10)
81+
for i in range(10):
82+
arr[i] = Char(str(i))
83+
84+
ob = String(arr)
85+
assert ob == "0123456789"
86+
87+
ob = String(arr, 5, 4)
88+
assert ob == "5678"

0 commit comments

Comments
 (0)