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    if src_url.startswith("http"):
13        print("Fetching source for function extraction: {}".format(src_url))
14        metrics = subprocess.check_output(["./fetch_fn_names.sh", src_url])
15    else:
16        print("Skip fetching source: {}".format(src_url))
17        metrics = ""
18
19    try:
20        return json.loads(metrics)
21    except ValueError:
22        return {"kind": "empty", "name": "anonymous", "spaces": []}
23
24
25def getSpaceFunctionsRecursive(metrics_space):
26    functions = []
27    if (
28        metrics_space["kind"] == "function"
29        and metrics_space["name"]
30        and metrics_space["name"] != "<anonymous>"
31    ):
32        functions.append(
33            {
34                "name": metrics_space["name"],
35                "start_line": int(metrics_space["start_line"]),
36                "end_line": int(metrics_space["end_line"]),
37            }
38        )
39    for space in metrics_space["spaces"]:
40        functions += getSpaceFunctionsRecursive(space)
41    return functions
42
43
44def getSourceFunctions(src_url):
45    if src_url not in cached_functions:
46        metrics_space = getMetricsJson(src_url)
47        cached_functions[src_url] = getSpaceFunctionsRecursive(metrics_space)
48
49    return cached_functions[src_url]
50
51
52def getFunctionName(location):
53    location.replace("annotate", "raw-file")
54    pieces = location.split("#l")
55    src_url = pieces[0]
56    line = int(pieces[1])
57    closest_name = "<Unknown {}>".format(line)
58    closest_start = 0
59    functions = getSourceFunctions(src_url)
60    for fn in functions:
61        if (
62            fn["start_line"] > closest_start
63            and line >= fn["start_line"]
64            and line <= fn["end_line"]
65        ):
66            closest_start = fn["start_line"]
67            closest_name = fn["name"]
68    return closest_name
69