Skip to content

Commit a0c3d36

Browse files
authored
Fix function compatibility (#1516)
* Fix function compatibility * Fix test__ssl * Fix comparisons
1 parent 223eede commit a0c3d36

File tree

4 files changed

+60
-37
lines changed

4 files changed

+60
-37
lines changed

Src/IronPython/Runtime/Binding/MetaPythonFunction.cs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,21 @@ private CallSignature Signature {
365365
/// Makes the test for our rule.
366366
/// </summary>
367367
private BindingRestrictions/*!*/ GetRestrictions() {
368-
if (!Signature.HasKeywordArgument()) {
369-
return GetSimpleRestriction();
368+
if (_extractedDefault) {
369+
return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value);
370+
} else if (_needCodeTest) {
371+
return GetSimpleRestriction().Merge(
372+
BindingRestrictions.GetInstanceRestriction(
373+
Expression.Property(
374+
GetFunctionParam(),
375+
"__code__"
376+
),
377+
_func.Value.__code__
378+
)
379+
);
370380
}
371381

372-
return GetComplexRestriction();
382+
return GetSimpleRestriction();
373383
}
374384

375385
/// <summary>
@@ -389,28 +399,6 @@ private CallSignature Signature {
389399
);
390400
}
391401

392-
/// <summary>
393-
/// Makes the test when we have a keyword argument call or splatting.
394-
/// </summary>
395-
/// <returns></returns>
396-
private BindingRestrictions/*!*/ GetComplexRestriction() {
397-
if (_extractedDefault) {
398-
return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value);
399-
} else if (_needCodeTest) {
400-
return GetSimpleRestriction().Merge(
401-
BindingRestrictions.GetInstanceRestriction(
402-
Expression.Property(
403-
GetFunctionParam(),
404-
"__code__"
405-
),
406-
_func.Value.__code__
407-
)
408-
);
409-
}
410-
411-
return GetSimpleRestriction();
412-
}
413-
414402
/// <summary>
415403
/// Gets the array of expressions which correspond to each argument for the function. These
416404
/// correspond with the function as it's defined in Python and must be transformed for our
@@ -524,6 +512,8 @@ private bool FinishArguments(Expression[] exprArgs, List<Expression> paramsArgs,
524512

525513
var keywordOnlyArgumentCount = _func.Value.KeywordOnlyArgumentCount;
526514

515+
if (_func.Value.NeedsCodeTest) _needCodeTest = true;
516+
527517
for (int i = normalArgumentCount; i < normalArgumentCount + keywordOnlyArgumentCount; i++) {
528518
var argName = _func.Value.ArgNames[i];
529519

Src/IronPython/Runtime/PythonFunction.cs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public sealed partial class PythonFunction : PythonTypeSlot, IWeakReferenceable,
3333
public readonly MutableTuple Closure;
3434

3535
private object[]/*!*/ _defaults; // the default parameters of the method
36+
private PythonDictionary _kwdefaults; // the keyword-only arguments defaults for the method
37+
3638
internal PythonDictionary _dict; // a dictionary to story arbitrary members on the function object
3739
private object _module; // the module name
3840

@@ -89,7 +91,7 @@ internal PythonFunction(CodeContext/*!*/ context, FunctionCode funcInfo, object
8991

9092
_context = context;
9193
_defaults = defaults ?? ArrayUtils.EmptyObjects;
92-
__kwdefaults__ = kwdefaults;
94+
_kwdefaults = kwdefaults;
9395
_code = funcInfo;
9496
_doc = funcInfo._initialDoc;
9597
_name = funcInfo.co_name;
@@ -145,8 +147,13 @@ public PythonTuple __defaults__ {
145147
}
146148
}
147149

148-
// TODO: update CalculatedCachedCompat?
149-
public PythonDictionary __kwdefaults__ { get; set; }
150+
public PythonDictionary __kwdefaults__ {
151+
get => _kwdefaults;
152+
set {
153+
_kwdefaults = value;
154+
FunctionCompatibility = CalculatedCachedCompat();
155+
}
156+
}
150157

151158
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
152159
public object __closure__ {
@@ -277,6 +284,14 @@ internal string GetSignatureString() {
277284
/// </summary>
278285
internal int FunctionCompatibility { get; private set; }
279286

287+
internal bool NeedsCodeTest {
288+
get {
289+
return NormalArgumentCount > 0x3ff
290+
|| Defaults.Length > 0x3ff
291+
|| KeywordOnlyArgumentCount > 0x1ff;
292+
}
293+
}
294+
280295
/// <summary>
281296
/// Calculates the _compat value which is used for call-compatibility checks
282297
/// for simple calls. Whenver any of the dependent values are updated this
@@ -289,20 +304,20 @@ internal string GetSignatureString() {
289304
/// expand dict/list - based on nparams and flags, both read-only
290305
///
291306
/// Bits are allocated as:
292-
/// 000000ff - Normal argument count
293-
/// 0000ff00 - Default count
294-
/// 007f0000 - Keyword-only argument count
295-
/// 3f800000 - Keyword-only defaults count
307+
/// 000003ff - Normal argument count
308+
/// 000ffc00 - Default count
309+
/// 1ff00000 - Keyword-only argument count
310+
/// 20000000 - has keyword-only defaults
296311
/// 40000000 - expand list
297312
/// 80000000 - expand dict
298313
///
299314
/// Enforce recursion is added at runtime.
300315
/// </summary>
301316
private int CalculatedCachedCompat() {
302-
return (NormalArgumentCount) |
303-
(Defaults.Length << 8) |
304-
(KeywordOnlyArgumentCount << 16) |
305-
((__kwdefaults__?.Count ?? 0) << 23) |
317+
return NormalArgumentCount |
318+
(Defaults.Length << 10) |
319+
(KeywordOnlyArgumentCount << 20) |
320+
(__kwdefaults__ is not null ? 0x20000000 : 0) |
306321
((ExpandListPosition != -1) ? 0x40000000 : 0) |
307322
((ExpandDictPosition != -1) ? unchecked((int)0x80000000) : 0);
308323
}

Tests/modules/network_related/test__ssl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from iptest import IronPythonTestCase, is_cli, is_netcoreapp, retryOnFailure, run_test, skipUnlessIronPython
1616

1717
SSL_URL = "www.python.org"
18-
SSL_ISSUER = "CN=GlobalSign Atlas R3 DV TLS CA H2 2021, O=GlobalSign nv-sa, C=BE"
18+
SSL_ISSUER = "CN=GlobalSign Atlas R3 DV TLS CA 2022 Q3, O=GlobalSign nv-sa, C=BE"
1919
SSL_SERVER = "www.python.org"
2020
SSL_PORT = 443
2121
SSL_REQUEST = b"GET /en-us HTTP/1.0\r\nHost: www.python.org\r\n\r\n"

Tests/test_regressions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,4 +1683,22 @@ def __radd__(self, other):
16831683
self.assertEqual(seq * m, 23)
16841684
self.assertEqual(seq + m, 64)
16851685

1686+
def test_ipy3_gh1515(self):
1687+
# Because function compatibility for simple calls was defined as:
1688+
# "number of args" + "number of defaults" << 8 + ...
1689+
# we got a conflict between 1 arg with default and 257 args.
1690+
1691+
# 1 arg with default
1692+
def d01(a=1): pass
1693+
1694+
d01(*(1,))
1695+
1696+
# 2.7 fails with 16385 args
1697+
# 3.4.0-beta fails with 257 args
1698+
for i in range(16):
1699+
size = (1<<i) + 1
1700+
d = {}
1701+
exec('def f(' + ','.join('a{0}'.format(i) for i in range(size)) + '): pass', d)
1702+
d["f"](*range(size)) # just make sure this runs successfully
1703+
16861704
run_test(__name__)

0 commit comments

Comments
 (0)