Skip to content

Commit 8973763

Browse files
committed
Add more testing in node port in order to catch the callback bug.
1 parent 09f3a4b commit 8973763

File tree

4 files changed

+91
-14
lines changed

4 files changed

+91
-14
lines changed

source/loaders/py_loader/source/py_loader_impl.c

+17-8
Original file line numberDiff line numberDiff line change
@@ -801,21 +801,30 @@ PyObject * py_loader_impl_function_type_invoke(PyObject * self, PyObject * args)
801801

802802
loader_impl_py_function_type_invoke_state invoke_state = (loader_impl_py_function_type_invoke_state)PyModule_GetState(self);
803803

804-
signature s = function_signature(invoke_state->callback);
804+
signature s;
805805

806-
const size_t args_size = signature_count(s);
806+
size_t args_size, args_count, min_args_size;
807807

808-
size_t args_count;
808+
Py_ssize_t callee_args_size;
809809

810-
/* type ret_type = signature_get_return(s); */
810+
void ** value_args;
811811

812-
Py_ssize_t callee_args_size = PyTuple_Size(args);
812+
value ret;
813813

814-
const size_t min_args_size = args_size < (size_t)callee_args_size ? args_size : (size_t)callee_args_size;
814+
if (invoke_state == NULL)
815+
{
816+
log_write("metacall", LOG_LEVEL_ERROR, "Fatal error when invoking a function, state cannot be recovered, avoiding the function call");
815817

816-
void ** value_args;
818+
return Py_None;
819+
}
817820

818-
value ret;
821+
s = function_signature(invoke_state->callback);
822+
823+
args_size = signature_count(s);
824+
825+
callee_args_size = PyTuple_Size(args);
826+
827+
min_args_size = args_size < (size_t)callee_args_size ? args_size : (size_t)callee_args_size;
819828

820829
if (args_size != (size_t)callee_args_size)
821830
{

source/ports/node_port/test/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ describe('metacall', () => {
133133
// Receiving undefined from a function that returns nothing in Python
134134
assert.strictEqual(f.function_pass(), undefined);
135135

136+
// Double recursion
137+
/*
138+
const sum = (value, f) => value <= 0 ? 0 : value + f(value - 1, sum);
139+
assert.strictEqual(sum(5, f.function_sum), 15);
140+
assert.strictEqual(f.function_sum(5, sum), 15);
141+
*/
142+
136143
// Factorial composition (@trgwii)
137144
/*
138145
const fact = f.function_factorial(c => v => v);

source/scripts/python/function/source/function.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,20 @@ def function_ret_lambda(y):
2020
return lambda x: function_print_and_return(x) * y
2121

2222
def function_currying(y):
23-
return lambda x: lambda z: x * z * y
23+
return lambda x: lambda z: x * z * y;
2424

2525
def function_currying_more(y):
26-
return lambda x: lambda z: lambda w: lambda n: x * z * w * n * y
26+
return lambda x: lambda z: lambda w: lambda n: x * z * w * n * y;
27+
28+
def function_pass():
29+
pass;
30+
31+
def function_sum(value, f):
32+
return 0 if value <= 0 else value + f(value - 1, function_sum);
2733

2834
def function_chain(x):
29-
return lambda n: x(x)(n)
35+
return lambda n: x(x)(n);
3036

3137
def function_factorial(x):
32-
return lambda n: 1 if n == 0 else n * x(x)(n - 1)
38+
return lambda n: 1 if n == 0 else n * x(x)(n - 1);
3339

34-
def function_pass():
35-
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#! /usr/bin/awk -f
2+
# A script to extract the actual suppression info from the output of (for example) valgrind --leak-check=full --show-reachable=yes --error-limit=no --gen-suppressions=all ./minimal
3+
# The desired bits are between ^{ and ^} (including the braces themselves).
4+
# The combined output should either be appended to /usr/lib/valgrind/default.supp, or placed in a .supp of its own
5+
# If the latter, either tell valgrind about it each time with --suppressions=<filename>, or add that line to ~/.valgrindrc
6+
7+
# NB This script uses the |& operator, which I believe is gawk-specific. In case of failure, check that you're using gawk rather than some other awk
8+
9+
# The script looks for suppressions. When it finds one it stores it temporarily in an array,
10+
# and also feeds it line by line to the external app 'md5sum' which generates a unique checksum for it.
11+
# The checksum is used as an index in a different array. If an item with that index already exists the suppression must be a duplicate and is discarded.
12+
13+
BEGIN { suppression=0; md5sum = "md5sum" }
14+
# If the line begins with '{', it's the start of a supression; so set the var and initialise things
15+
/^{/ {
16+
suppression=1; i=0; next
17+
}
18+
# If the line begins with '}' its the end of a suppression
19+
/^}/ {
20+
if (suppression)
21+
{ suppression=0;
22+
close(md5sum, "to") # We've finished sending data to md5sum, so close that part of the pipe
23+
ProcessInput() # Do the slightly-complicated stuff in functions
24+
delete supparray # We don't want subsequent suppressions to append to it!
25+
}
26+
}
27+
# Otherwise, it's a normal line. If we're inside a supression, store it, and pipe it to md5sum. Otherwise it's cruft, so ignore it
28+
{ if (suppression)
29+
{
30+
supparray[++i] = $0
31+
print |& md5sum
32+
}
33+
}
34+
35+
36+
function ProcessInput()
37+
{
38+
# Pipe the result from md5sum, then close it
39+
md5sum |& getline result
40+
close(md5sum)
41+
# gawk can't cope with enormous ints like $result would be, so stringify it first by prefixing a definite string
42+
resultstring = "prefix"result
43+
44+
if (! (resultstring in chksum_array) )
45+
{ chksum_array[resultstring] = 0; # This checksum hasn't been seen before, so add it to the array
46+
OutputSuppression() # and output the contents of the suppression
47+
}
48+
}
49+
50+
function OutputSuppression()
51+
{
52+
# A suppression is surrounded by '{' and '}'. Its data was stored line by line in the array
53+
print "{"
54+
for (n=1; n <= i; ++n)
55+
{ print supparray[n] }
56+
print "}"
57+
}

0 commit comments

Comments
 (0)