Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit 99bd4d8

Browse files
author
Juanjo Alvarez
committed
Fix children duplicating nodes and unchecked nullptr in pyuast.cc
Signed-off-by: Juanjo Alvarez <juanjo@sourced.tech>
1 parent 1031d4a commit 99bd4d8

4 files changed

Lines changed: 38 additions & 37 deletions

File tree

bblfsh/node.py

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,26 @@ def offset(self, v: int) -> None:
4747

4848
class CompatChildren(MutableSequence):
4949
def __init__(self, parent: "Node") -> None:
50-
self._children = parent.get_dict()["@children"]
50+
self._par_dict = parent.get_dict()
51+
self._children = self._sync_children()
52+
53+
def _sync_children(self) -> None:
54+
if "_children" not in self._par_dict:
55+
self._par_dict["_children"] = []
56+
children = self._par_dict["_children"]
57+
for k, v in self._par_dict.items():
58+
if k in ("_children", "@pos", "@role", "@type"):
59+
continue
60+
61+
tv = type(v)
62+
if tv in (Node, dict):
63+
if v not in children:
64+
children.append(v)
65+
elif tv in (list, tuple):
66+
# Get all node|dict types inside the list and add to children
67+
children.extend([i for i in v if type(i) in (Node, dict) and i not in children])
68+
# else ignore it
69+
return children
5170

5271
@staticmethod
5372
def _node2dict(n: Union['Node', dict]) -> dict:
@@ -66,13 +85,16 @@ def __delitem__(self, idx: Union[int, slice]) -> None:
6685
del self._children[idx]
6786

6887
def __setitem__(self, idx: Union[int, slice], val: Union['Node', dict]) -> None:
69-
self._children[idx] = self._node2dict(val)
88+
self._par_dict["_children"].__setitem__(idx, self._node2dict(val))
89+
self._children = self._sync_children()
7090

7191
def insert(self, idx: int, val: Union['Node', dict]) -> None:
72-
self._children.insert(idx, self._node2dict(val))
92+
self._par_dict["_children"].insert(idx, self._node2dict(val))
93+
self._children = self._sync_children()
7394

7495
def append(self, val: Union['Node', dict]) -> None:
75-
self._children.append(self._node2dict(val))
96+
self._par_dict["_children"].append(self._node2dict(val))
97+
self._children = self._sync_children()
7698

7799
def extend(self, items: List[Union['Node', dict]]) -> None:
78100
for i in items:
@@ -112,32 +134,6 @@ def __init__(self, node_ext: NodeExt = None, value: ResultMultiType = None) -> N
112134

113135
self.node_ext = node_ext
114136

115-
if isinstance(self.internal_node, dict):
116-
self._load_children()
117-
118-
# This is for v1 "node.children" compatibility. It will update the children
119-
# property with the dict or Node objects in properties or list/tuple properties
120-
# when .children is accessed (because the user could change the node using get_dict()
121-
# or .properties).
122-
# Also, all these " in children" are O(1) so this will be slow for frequently accessing
123-
# the children property on big nodes.
124-
def _load_children(self) -> None:
125-
"""Get all properties of type node or dict and load them into the list"""
126-
d = self.get_dict()
127-
children = d.get("@children", [])
128-
for k, v in d.items():
129-
if k in ("@children", "@pos", "@role", "@type"):
130-
continue
131-
132-
tv = type(v)
133-
if tv in (Node, dict):
134-
if v not in children:
135-
children.append(v)
136-
elif tv in (list, tuple):
137-
# Get all node|dict types inside the list and add to children
138-
children.extend([i for i in v if type(i) in (Node, dict) and i not in children])
139-
# else ignore it
140-
141137
def __str__(self) -> str:
142138
return str(self.get())
143139

@@ -200,7 +196,6 @@ def _is_dict_list(self, key: str) -> Optional[List]:
200196

201197
@property
202198
def children(self) -> CompatChildren:
203-
self._load_children()
204199
return CompatChildren(self)
205200

206201
@property

bblfsh/pyuast.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ class Node : public uast::Node<Node*> {
551551
if (!keys) return nullptr;
552552

553553
PyObject* key = PyList_GetItem(keys, i); // borrows
554+
if (!key) return nullptr;
554555
const char * k = PyUnicode_AsUTF8(key);
555556

556557
std::string* s = new std::string(k);

bblfsh/test.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ def _parse_fixture(self) -> ResultContext:
3636
self._validate_ctx(ctx)
3737
return ctx
3838

39-
"""
4039
def testVersion(self) -> None:
4140
version = self.client.version()
4241
self.assertTrue(hasattr(version, "version"))
@@ -188,7 +187,6 @@ def testFilterBadType(self) -> None:
188187
def testRoleIdName(self) -> None:
189188
self.assertEqual(role_id(role_name(1)), 1)
190189
self.assertEqual(role_name(role_id("IDENTIFIER")), "IDENTIFIER")
191-
"""
192190

193191
@staticmethod
194192
def _itTestTree() -> dict:
@@ -258,7 +256,6 @@ def testIteratorPostOrder(self) -> None:
258256
self.assertListEqual(expanded, ['son1_1', 'son1_2', 'son1', 'son2_1',
259257
'son2_2', 'son2', 'root'])
260258

261-
"""
262259
def testIteratorLevelOrder(self) -> None:
263260
root = self._itTestTree()
264261
it = iterator(root, TreeOrder.LEVEL_ORDER)
@@ -338,7 +335,6 @@ def testSupportedLanguages(self) -> None:
338335
for key in ('language', 'version', 'status', 'features'):
339336
self.assertTrue(hasattr(l, key))
340337
self.assertIsNotNone(getattr(l, key))
341-
"""
342338

343339

344340
if __name__ == "__main__":

bblfsh/test_compat.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ def testIteratorPositionOrder(self):
226226
'son1_2', 'son2_2', 'son2'])
227227

228228
def testFilterInsideIter(self):
229-
root = self.client.parse(__file__).uast
229+
root = self._parse_fixture().uast
230230
it = iterator(root, TreeOrder.PRE_ORDER)
231231
self.assertIsNotNone(it)
232232
for n in it:
@@ -275,7 +275,6 @@ def testManyParsesAndFilters(self):
275275
xpath_filter(root, "//*[@role='Identifier']")
276276

277277
after = resource.getrusage(resource.RUSAGE_SELF)
278-
279278
self.assertLess(after[2] / before[2], 4.0)
280279

281280
def testSupportedLanguages(self):
@@ -304,6 +303,16 @@ def testChildren(self):
304303
self.assertDictEqual(n.children[3], l[0])
305304
self.assertDictEqual(n.children[4], l[1])
306305

306+
def testChildrenFile(self):
307+
root = self._parse_fixture().uast
308+
self.assertEqual(len(root.children), 10)
309+
n = Node()
310+
n.internal_type = 'child_node'
311+
root.children.append(n)
312+
self.assertEqual(len(root.children), 11)
313+
last = root.children[-1]
314+
self.assertDictEqual(last, n.internal_node)
315+
307316

308317

309318
if __name__ == "__main__":

0 commit comments

Comments
 (0)