1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5import json
6import subprocess
7
8cached_functions = {}
9
10
11def getMetricsJson(src_url):
12    print("Fetching source for function extraction: {}".format(src_url))
13    metrics = subprocess.check_output(["./fetch_fn_names.sh", src_url])
14
15    try:
16        return json.loads(metrics)
17    except ValueError:
18        return {"kind": "empty", "name": "anonymous", "spaces": []}
19
20
21def getSpaceFunctionsRecursive(metrics_space):
22    functions = []
23    if (
24        metrics_space["kind"] == "function"
25        and metrics_space["name"]
26        and metrics_space["name"] != "<anonymous>"
27    ):
28        functions.append(
29            {
30                "name": metrics_space["name"],
31                "start_line": int(metrics_space["start_line"]),
32                "end_line": int(metrics_space["end_line"]),
33            }
34        )
35    for space in metrics_space["spaces"]:
36        functions += getSpaceFunctionsRecursive(space)
37    return functions
38
39
40def getSourceFunctions(src_url):
41    if src_url not in cached_functions:
42        metrics_space = getMetricsJson(src_url)
43        cached_functions[src_url] = getSpaceFunctionsRecursive(metrics_space)
44
45    return cached_functions[src_url]
46
47
48def getFunctionName(location):
49    location.replace("annotate", "raw-file")
50    pieces = location.split("#l")
51    src_url = pieces[0]
52    line = int(pieces[1])
53    closest_name = "<Unknown {}>".format(line)
54    closest_start = 0
55    functions = getSourceFunctions(src_url)
56    for fn in functions:
57        if (
58            fn["start_line"] > closest_start
59            and line >= fn["start_line"]
60            and line <= fn["end_line"]
61        ):
62            closest_start = fn["start_line"]
63            closest_name = fn["name"]
64    return closest_name
65