cg-code-graph 0.10.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cg_code_graph-0.10.1.dist-info/METADATA +678 -0
- cg_code_graph-0.10.1.dist-info/RECORD +174 -0
- cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
- cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
- cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
- cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
- codegraph/__init__.py +2 -0
- codegraph/aitools.py +129 -0
- codegraph/apps.py +76 -0
- codegraph/blindspots.py +428 -0
- codegraph/bridges.py +1701 -0
- codegraph/cli.py +725 -0
- codegraph/concepts.py +362 -0
- codegraph/config.py +559 -0
- codegraph/core/__init__.py +0 -0
- codegraph/core/cache.py +375 -0
- codegraph/core/detect.py +80 -0
- codegraph/core/extractors.py +187 -0
- codegraph/core/fsutil.py +61 -0
- codegraph/core/generated.py +575 -0
- codegraph/core/model.py +174 -0
- codegraph/core/paths.py +175 -0
- codegraph/core/plugin.py +160 -0
- codegraph/core/store.py +80 -0
- codegraph/core/syntax_errors.py +132 -0
- codegraph/coverage.py +928 -0
- codegraph/doctor.py +453 -0
- codegraph/external.py +613 -0
- codegraph/indexer.py +336 -0
- codegraph/link.py +434 -0
- codegraph/lint_async.py +524 -0
- codegraph/mcp_server.py +1303 -0
- codegraph/parity.py +473 -0
- codegraph/parity_structure.py +307 -0
- codegraph/payload.py +321 -0
- codegraph/plans.py +1285 -0
- codegraph/platform_scan.py +643 -0
- codegraph/platforms.py +1369 -0
- codegraph/plugins/__init__.py +0 -0
- codegraph/plugins/cfamily/__init__.py +0 -0
- codegraph/plugins/cfamily/plugin.py +930 -0
- codegraph/plugins/cfamily/syntax.py +881 -0
- codegraph/plugins/dart/__init__.py +0 -0
- codegraph/plugins/dart/bridges.py +345 -0
- codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
- codegraph/plugins/dart/extractor/pubspec.lock +149 -0
- codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
- codegraph/plugins/dart/http.py +904 -0
- codegraph/plugins/dart/models.py +308 -0
- codegraph/plugins/dart/plugin.py +625 -0
- codegraph/plugins/dart/program.py +907 -0
- codegraph/plugins/django/__init__.py +0 -0
- codegraph/plugins/django/extras.py +378 -0
- codegraph/plugins/django/models.py +508 -0
- codegraph/plugins/django/plugin.py +728 -0
- codegraph/plugins/django/schemas.py +339 -0
- codegraph/plugins/django/shapes.py +216 -0
- codegraph/plugins/django/urls.py +603 -0
- codegraph/plugins/express/__init__.py +0 -0
- codegraph/plugins/express/plugin.py +428 -0
- codegraph/plugins/flutter/__init__.py +0 -0
- codegraph/plugins/flutter/plugin.py +538 -0
- codegraph/plugins/kotlin/__init__.py +0 -0
- codegraph/plugins/kotlin/exact.py +457 -0
- codegraph/plugins/kotlin/plugin.py +1961 -0
- codegraph/plugins/kotlin/reparse.py +234 -0
- codegraph/plugins/laravel/__init__.py +0 -0
- codegraph/plugins/laravel/broadcast.py +351 -0
- codegraph/plugins/laravel/plugin.py +863 -0
- codegraph/plugins/laravel/tests.py +262 -0
- codegraph/plugins/laravel/values.py +728 -0
- codegraph/plugins/native/__init__.py +0 -0
- codegraph/plugins/native/gates.py +286 -0
- codegraph/plugins/native/runner.py +183 -0
- codegraph/plugins/native/scipread.py +194 -0
- codegraph/plugins/native/ts.py +54 -0
- codegraph/plugins/nest/__init__.py +0 -0
- codegraph/plugins/nest/plugin.py +654 -0
- codegraph/plugins/nextjs/__init__.py +0 -0
- codegraph/plugins/nextjs/plugin.py +336 -0
- codegraph/plugins/nuxt/__init__.py +0 -0
- codegraph/plugins/nuxt/plugin.py +308 -0
- codegraph/plugins/php/__init__.py +0 -0
- codegraph/plugins/php/extractor/composer.json +5 -0
- codegraph/plugins/php/extractor/composer.lock +76 -0
- codegraph/plugins/php/extractor/extract.php +743 -0
- codegraph/plugins/php/gating.py +573 -0
- codegraph/plugins/php/plugin.py +668 -0
- codegraph/plugins/php/strings.py +197 -0
- codegraph/plugins/python/__init__.py +0 -0
- codegraph/plugins/python/aitools.py +664 -0
- codegraph/plugins/python/external.py +245 -0
- codegraph/plugins/python/fields.py +107 -0
- codegraph/plugins/python/plugin.py +1733 -0
- codegraph/plugins/python/refs.py +485 -0
- codegraph/plugins/python/roots.py +412 -0
- codegraph/plugins/python/socketio.py +210 -0
- codegraph/plugins/python/subproc.py +864 -0
- codegraph/plugins/python/tests.py +1040 -0
- codegraph/plugins/python/values.py +179 -0
- codegraph/plugins/pyweb/__init__.py +0 -0
- codegraph/plugins/pyweb/plugin.py +1334 -0
- codegraph/plugins/pyweb/values.py +68 -0
- codegraph/plugins/rust/__init__.py +0 -0
- codegraph/plugins/rust/cargo.py +226 -0
- codegraph/plugins/rust/plugin.py +980 -0
- codegraph/plugins/rust/syntax.py +678 -0
- codegraph/plugins/scip/__init__.py +0 -0
- codegraph/plugins/scip/importer.py +129 -0
- codegraph/plugins/scip/scip.proto +962 -0
- codegraph/plugins/scip/scip_pb2.py +97 -0
- codegraph/plugins/stubs/__init__.py +0 -0
- codegraph/plugins/stubs/plugins.py +38 -0
- codegraph/plugins/swift/__init__.py +0 -0
- codegraph/plugins/swift/baseurl.py +109 -0
- codegraph/plugins/swift/exact.py +415 -0
- codegraph/plugins/swift/indexstore.py +209 -0
- codegraph/plugins/swift/packages.py +174 -0
- codegraph/plugins/swift/plugin.py +2890 -0
- codegraph/plugins/ts/__init__.py +0 -0
- codegraph/plugins/ts/baseurl.py +185 -0
- codegraph/plugins/ts/extractor/extract.mjs +2652 -0
- codegraph/plugins/ts/extractor/fw.mjs +685 -0
- codegraph/plugins/ts/extractor/package-lock.json +205 -0
- codegraph/plugins/ts/extractor/package.json +9 -0
- codegraph/plugins/ts/plugin.py +480 -0
- codegraph/plugins/tsweb/__init__.py +0 -0
- codegraph/plugins/tsweb/common.py +290 -0
- codegraph/plugins/tsweb/data.py +276 -0
- codegraph/presets/__init__.py +146 -0
- codegraph/presets/c_cpp.yaml +9 -0
- codegraph/presets/common.yaml +66 -0
- codegraph/presets/dart.yaml +9 -0
- codegraph/presets/django-ninja.yaml +15 -0
- codegraph/presets/django.yaml +25 -0
- codegraph/presets/djangorestframework.yaml +17 -0
- codegraph/presets/express.yaml +17 -0
- codegraph/presets/kotlin.yaml +11 -0
- codegraph/presets/laravel.yaml +40 -0
- codegraph/presets/nest.yaml +11 -0
- codegraph/presets/nextjs.yaml +15 -0
- codegraph/presets/nuxt.yaml +9 -0
- codegraph/presets/php.yaml +5 -0
- codegraph/presets/python.yaml +10 -0
- codegraph/presets/rust.yaml +5 -0
- codegraph/presets/swift.yaml +10 -0
- codegraph/presets/typescript.yaml +13 -0
- codegraph/process_runs.py +328 -0
- codegraph/protocols/__init__.py +299 -0
- codegraph/protocols/builtin.py +67 -0
- codegraph/protocols/matchers.py +144 -0
- codegraph/protocols/view.py +334 -0
- codegraph/query.py +2089 -0
- codegraph/realtime.py +260 -0
- codegraph/roundtrip.py +346 -0
- codegraph/routes.py +442 -0
- codegraph/starters.py +218 -0
- codegraph/tests_index.py +117 -0
- codegraph/viz/__init__.py +0 -0
- codegraph/viz/graph.py +369 -0
- codegraph/viz/server.py +198 -0
- codegraph/viz/static/app.css +148 -0
- codegraph/viz/static/app.js +1082 -0
- codegraph/viz/static/index.html +81 -0
- codegraph/viz/static/layered.js +237 -0
- codegraph/viz/static/vendor/VERSIONS.txt +4 -0
- codegraph/viz/static/vendor/cose-base.js +3214 -0
- codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
- codegraph/viz/static/vendor/cytoscape.min.js +31 -0
- codegraph/viz/static/vendor/layout-base.js +5230 -0
- codegraph/viz/tools/package-lock.json +303 -0
- codegraph/viz/tools/package.json +7 -0
- codegraph/viz/tools/shoot.mjs +165 -0
- codegraph/xcode.py +251 -0
|
File without changes
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""Flutter platform channels, send side: `MethodChannel('name').invokeMethod('m')` (also invokeMapMethod /
|
|
2
|
+
invokeListMethod, channels held in fields, top-level / static constants and locals) and
|
|
3
|
+
`EventChannel('name').receiveBroadcastStream()`. Each call becomes SENDS_TO endpoint:flutter:<channel>#<method> /
|
|
4
|
+
endpoint:flutter-event:<channel> (codegraph/bridges.py links the Kotlin / Swift handlers).
|
|
5
|
+
|
|
6
|
+
Also the Dart receiving side of native -> Dart calls: `channel.setMethodCallHandler(handler)` and the method names the
|
|
7
|
+
handler tests (RECEIVED_BY, side dart), and Pigeon: the @HostApi / @FlutterApi classes of the definition files, Dart
|
|
8
|
+
calls on host APIs (SENDS_TO endpoint:pigeon:<Api>#<method>) and the Dart classes implementing flutter APIs."""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from ...core.model import EXACT, HEURISTIC, RESOLVED
|
|
14
|
+
from .program import Ctx, DartProgram, DClass, DFunc, DVar, ctor_type
|
|
15
|
+
|
|
16
|
+
METHOD_CHANNELS = {"MethodChannel", "OptionalMethodChannel"}
|
|
17
|
+
EVENT_CHANNELS = {"EventChannel"}
|
|
18
|
+
INVOKE = {"invokeMethod", "invokeMapMethod", "invokeListMethod"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _creation(prog: DartProgram, r, ctx: Ctx, depth: int = 0):
|
|
22
|
+
"""The MethodChannel / EventChannel creation an expression evaluates to: (kind, creation fact, ctx) or None."""
|
|
23
|
+
if not isinstance(r, dict) or depth > 8:
|
|
24
|
+
return None
|
|
25
|
+
ct = ctor_type(r)
|
|
26
|
+
if ct:
|
|
27
|
+
base = ct.split(".")[-1]
|
|
28
|
+
if base in METHOD_CHANNELS:
|
|
29
|
+
return "method", r, ctx
|
|
30
|
+
if base in EVENT_CHANNELS:
|
|
31
|
+
return "event", r, ctx
|
|
32
|
+
return None
|
|
33
|
+
k = r.get("k")
|
|
34
|
+
if k in ("nn", "await", "as") and isinstance(r.get("e"), dict):
|
|
35
|
+
return _creation(prog, r["e"], ctx, depth + 1)
|
|
36
|
+
if k == "cond":
|
|
37
|
+
return _creation(prog, r.get("a"), ctx, depth + 1) or _creation(prog, r.get("b"), ctx, depth + 1)
|
|
38
|
+
if k == "bin" and r.get("op") == "??":
|
|
39
|
+
return _creation(prog, r.get("l"), ctx, depth + 1) or _creation(prog, r.get("r"), ctx, depth + 1)
|
|
40
|
+
var = None
|
|
41
|
+
if k == "id":
|
|
42
|
+
loc = ctx.locals.get(r["v"])
|
|
43
|
+
if loc:
|
|
44
|
+
kind, d = loc
|
|
45
|
+
if kind == "var" and d.get("init") is not None:
|
|
46
|
+
return _creation(prog, d["init"], ctx, depth + 1)
|
|
47
|
+
if kind == "param" and d.get("this") and ctx.cls and r["v"] in ctx.cls.fields:
|
|
48
|
+
var = ctx.cls.fields[r["v"]]
|
|
49
|
+
else:
|
|
50
|
+
return None
|
|
51
|
+
elif ctx.cls:
|
|
52
|
+
m, _ = prog.find_member(ctx.cls, r["v"])
|
|
53
|
+
if isinstance(m, DVar):
|
|
54
|
+
var = m
|
|
55
|
+
elif isinstance(m, DFunc) and m.kind == "getter":
|
|
56
|
+
return _getter(prog, m, depth)
|
|
57
|
+
if var is None:
|
|
58
|
+
v = prog.lookup(ctx.lib, r["v"])
|
|
59
|
+
if isinstance(v, DVar):
|
|
60
|
+
var = v
|
|
61
|
+
elif isinstance(v, DFunc) and v.kind == "getter":
|
|
62
|
+
return _getter(prog, v, depth)
|
|
63
|
+
elif k == "prop":
|
|
64
|
+
t = r.get("t") or {}
|
|
65
|
+
if t.get("k") == "this" and ctx.cls:
|
|
66
|
+
m, _ = prog.find_member(ctx.cls, r["n"])
|
|
67
|
+
var = m if isinstance(m, DVar) else None
|
|
68
|
+
elif t.get("k") == "id" and t["v"][:1].isupper(): # Class.staticField
|
|
69
|
+
c = prog.resolve_class(ctx.lib, t["v"])
|
|
70
|
+
if isinstance(c, DClass):
|
|
71
|
+
m, _ = prog.find_member(c, r["n"])
|
|
72
|
+
var = m if isinstance(m, DVar) else None
|
|
73
|
+
elif t.get("k") == "id" and t["v"] not in ctx.locals and prog.prefix_imports(ctx.lib, t["v"]):
|
|
74
|
+
v = prog.lookup_prefixed(ctx.lib, t["v"], r["n"])
|
|
75
|
+
var = v if isinstance(v, DVar) else None
|
|
76
|
+
if var is not None and var.init is not None:
|
|
77
|
+
return _creation(prog, var.init, Ctx(prog, var.lib, None, var.cls), depth + 1)
|
|
78
|
+
if var is not None and var.cls is not None:
|
|
79
|
+
# `late MethodChannel _channel;` assigned in initState / a constructor: `_channel = MethodChannel('x')`
|
|
80
|
+
for m in list(var.cls.methods.values()) + list(var.cls.ctors.values()):
|
|
81
|
+
for x in m.facts:
|
|
82
|
+
lhs = x.get("lhs") or {}
|
|
83
|
+
if x.get("ft") == "assign" and ((lhs.get("k") == "id" and lhs.get("v") == var.name) or
|
|
84
|
+
(lhs.get("k") == "prop" and lhs.get("n") == var.name
|
|
85
|
+
and (lhs.get("t") or {}).get("k") == "this")):
|
|
86
|
+
cr = _creation(prog, x.get("rhs"), prog.ctx_of(m), depth + 1)
|
|
87
|
+
if cr:
|
|
88
|
+
return cr
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _getter(prog, f: DFunc, depth):
|
|
93
|
+
ctx = prog.ctx_of(f)
|
|
94
|
+
for x in f.facts:
|
|
95
|
+
if x["ft"] == "return":
|
|
96
|
+
return _creation(prog, x.get("v"), ctx, depth + 1)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _channel_name(ev, creation, ctx) -> tuple[str | None, str]:
|
|
101
|
+
a = creation.get("a") or []
|
|
102
|
+
if not a:
|
|
103
|
+
return None, HEURISTIC
|
|
104
|
+
t = ev.eval(a[0], ctx)
|
|
105
|
+
if t.unknown or not t.text or "{" in t.text:
|
|
106
|
+
return None, HEURISTIC
|
|
107
|
+
return t.text, (EXACT if t.conf == EXACT else RESOLVED)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
_CH = {"method": "MethodChannel", "event": "EventChannel"}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _dynamic(builder, fn, f, kind, what) -> None:
|
|
114
|
+
"""A channel call whose channel or method name does not evaluate to a string -> the bridges `unresolved` list
|
|
115
|
+
(`cg bridges --unmatched`), like a dynamic React Native / Capacitor / Cordova name."""
|
|
116
|
+
builder.__dict__.setdefault("bridge_dynamic", []).append(
|
|
117
|
+
{"file": fn.file, "line": f.get("l"), "protocol": "flutter" if kind == "method" else "flutter-event", "what": what})
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def emit(prog: DartProgram, ev, builder) -> dict:
|
|
121
|
+
from ...bridges import protocol_send
|
|
122
|
+
n = {"flutter": 0, "flutter-event": 0, "unresolved_channel": 0}
|
|
123
|
+
texts: dict = {}
|
|
124
|
+
for fn in list(prog.all_funcs()):
|
|
125
|
+
ctx = None
|
|
126
|
+
for f in fn.facts:
|
|
127
|
+
if f.get("ft") == "call" and f.get("n") == "setMethodCallHandler" and f.get("t"):
|
|
128
|
+
ctx = ctx or prog.ctx_of(fn)
|
|
129
|
+
n["dart_handlers"] = n.get("dart_handlers", 0) + _dart_handler(prog, ev, builder, fn, f, ctx, texts)
|
|
130
|
+
continue
|
|
131
|
+
if f.get("ft") != "call" or f.get("n") not in INVOKE | {"receiveBroadcastStream"} or not f.get("t"):
|
|
132
|
+
continue
|
|
133
|
+
ctx = ctx or prog.ctx_of(fn)
|
|
134
|
+
cr = _creation(prog, f["t"], ctx)
|
|
135
|
+
if not cr:
|
|
136
|
+
continue
|
|
137
|
+
kind, creation, cctx = cr
|
|
138
|
+
name, conf = _channel_name(ev, creation, cctx)
|
|
139
|
+
if not name:
|
|
140
|
+
n["unresolved_channel"] += 1
|
|
141
|
+
_dynamic(builder, fn, f, kind, f"{_CH[kind]}(<dynamic>).{f['n']}")
|
|
142
|
+
continue
|
|
143
|
+
if kind == "method" and f["n"] in INVOKE:
|
|
144
|
+
a = f.get("a") or []
|
|
145
|
+
m = ev.eval(a[0], ctx) if a else None
|
|
146
|
+
if m is None or m.unknown or not m.text or "{" in m.text:
|
|
147
|
+
n["unresolved_channel"] += 1
|
|
148
|
+
_dynamic(builder, fn, f, kind, f"{name}.{f['n']}(<dynamic>)")
|
|
149
|
+
continue
|
|
150
|
+
protocol_send(builder, "flutter", name, m.text, fn.id, fn.file, f.get("l"),
|
|
151
|
+
conf if m.conf == EXACT else RESOLVED, via=f["n"])
|
|
152
|
+
n["flutter"] += 1
|
|
153
|
+
elif kind == "event" and f["n"] == "receiveBroadcastStream":
|
|
154
|
+
protocol_send(builder, "flutter-event", name, None, fn.id, fn.file, f.get("l"), conf, via=f["n"])
|
|
155
|
+
n["flutter-event"] += 1
|
|
156
|
+
return n
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ------------------------------------------------------------------ Pigeon (generated BasicMessageChannels)
|
|
160
|
+
PIGEON_ANN = {"HostApi": "host", "FlutterApi": "flutter"}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def pigeon_apis(prog: DartProgram) -> dict:
|
|
164
|
+
"""`@HostApi()` / `@FlutterApi()` abstract classes of the Pigeon definition files (`import
|
|
165
|
+
'package:pigeon/pigeon.dart'`): name -> {kind, methods, file, line, id}. The generated code (`*.g.dart`, `.g.kt`,
|
|
166
|
+
`.g.swift`) is often not checked in; the definitions name the API and its methods either way."""
|
|
167
|
+
out = {}
|
|
168
|
+
for c in prog.classes.values() if isinstance(prog.classes, dict) else prog.classes:
|
|
169
|
+
kinds = [PIGEON_ANN[a] for a in c.ann if a in PIGEON_ANN]
|
|
170
|
+
if not kinds or not prog.imports_ext(c.lib, "package:pigeon/"):
|
|
171
|
+
continue
|
|
172
|
+
meths = sorted(n for n, m in c.methods.items() if m.kind == "method" and not n.startswith("_"))
|
|
173
|
+
out[c.name] = {"kind": kinds[0], "methods": meths, "file": c.file, "line": c.line, "id": c.id}
|
|
174
|
+
return out
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def emit_pigeon(prog: DartProgram, builder, apis: dict) -> dict:
|
|
178
|
+
"""Dart side of Pigeon APIs: a call of a @HostApi method on the generated class (`NativeSyncApi().m()`, a field /
|
|
179
|
+
provider typed `NativeSyncApi`) -> SENDS_TO endpoint:pigeon:<Api>#<method>; a class that extends / implements a
|
|
180
|
+
@FlutterApi (`class BgService extends BackgroundWorkerFlutterApi`) -> RECEIVED_BY from the endpoint to its method
|
|
181
|
+
(the native side calls it)."""
|
|
182
|
+
from ...bridges import protocol_receive, protocol_send
|
|
183
|
+
n = {"sends": 0, "dart_receivers": 0}
|
|
184
|
+
host = {k: v for k, v in apis.items() if v["kind"] == "host"}
|
|
185
|
+
defs = {v["file"] for v in apis.values()}
|
|
186
|
+
if host:
|
|
187
|
+
names = {m for v in host.values() for m in v["methods"]}
|
|
188
|
+
for fn in list(prog.all_funcs()):
|
|
189
|
+
if fn.file in defs:
|
|
190
|
+
continue
|
|
191
|
+
ctx = None
|
|
192
|
+
for f in fn.facts:
|
|
193
|
+
if f.get("ft") != "call" or f.get("n") not in names or not f.get("t"):
|
|
194
|
+
continue
|
|
195
|
+
ctx = ctx or prog.ctx_of(fn)
|
|
196
|
+
try:
|
|
197
|
+
tt = prog.infer(f["t"], ctx)
|
|
198
|
+
except RecursionError:
|
|
199
|
+
continue
|
|
200
|
+
api = None
|
|
201
|
+
if not tt or tt[0] not in ("einst", "inst"):
|
|
202
|
+
pt = _provider_type(prog, f["t"], ctx) # ref.read(nativeSyncApiProvider).m()
|
|
203
|
+
tt = ("einst", pt, []) if pt else tt
|
|
204
|
+
if tt and tt[0] == "einst":
|
|
205
|
+
api = str(tt[1]).split(".")[-1].split("<")[0]
|
|
206
|
+
elif tt and tt[0] == "inst" and tt[1] is not None and tt[1].name in host:
|
|
207
|
+
api = tt[1].name # the definition class, or a checked-in generated class of that name
|
|
208
|
+
if api in host and f["n"] in host[api]["methods"]:
|
|
209
|
+
protocol_send(builder, "pigeon", api, f["n"], fn.id, fn.file, f.get("l"), RESOLVED,
|
|
210
|
+
via="Pigeon HostApi", direction="to_native")
|
|
211
|
+
n["sends"] += 1
|
|
212
|
+
flutter = {k: v for k, v in apis.items() if v["kind"] == "flutter"}
|
|
213
|
+
if flutter:
|
|
214
|
+
for c in (prog.classes.values() if isinstance(prog.classes, dict) else prog.classes):
|
|
215
|
+
if c.file in defs or c.file.startswith(("test/", "integration_test/")) or "/test/" in c.file \
|
|
216
|
+
or c.file.endswith("_test.dart"):
|
|
217
|
+
continue # mocks / fakes of the API in tests
|
|
218
|
+
sup = {split_base(t) for _rel, t in c.supers}
|
|
219
|
+
for api in sup & set(flutter):
|
|
220
|
+
for m in flutter[api]["methods"]:
|
|
221
|
+
f = c.methods.get(m)
|
|
222
|
+
if f is None:
|
|
223
|
+
continue
|
|
224
|
+
protocol_receive(builder, "pigeon", api, m, f.id, f.file, f.line, RESOLVED,
|
|
225
|
+
via="Pigeon FlutterApi implementation", side="dart")
|
|
226
|
+
n["dart_receivers"] += 1
|
|
227
|
+
return n
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def split_base(t: str) -> str:
|
|
231
|
+
return (t or "").split("<")[0].split(".")[-1].strip().rstrip("?")
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
PROVIDER_CTORS = ("Provider", "FutureProvider", "StateProvider", "AutoDisposeProvider", "ChangeNotifierProvider")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _provider_type(prog, r, ctx):
|
|
238
|
+
"""`ref.read(p)` / `ref.watch(p)` / `context.read<T>()` where `p = Provider<T>((ref) => ...)`: T."""
|
|
239
|
+
if not isinstance(r, dict) or r.get("k") != "call" or r.get("n") not in ("read", "watch"):
|
|
240
|
+
return None
|
|
241
|
+
if r.get("ta"):
|
|
242
|
+
return split_base(r["ta"][0])
|
|
243
|
+
a = r.get("a") or []
|
|
244
|
+
if not a or not isinstance(a[0], dict) or a[0].get("k") != "id":
|
|
245
|
+
return None
|
|
246
|
+
v = prog.lookup(ctx.lib, a[0]["v"])
|
|
247
|
+
init = getattr(v, "init", None)
|
|
248
|
+
if not isinstance(init, dict):
|
|
249
|
+
return None
|
|
250
|
+
ct = ctor_type(init) or ""
|
|
251
|
+
if ct.split(".")[-1].split("<")[0] not in PROVIDER_CTORS:
|
|
252
|
+
return None
|
|
253
|
+
ta = init.get("ta") or []
|
|
254
|
+
if ta:
|
|
255
|
+
return split_base(ta[0])
|
|
256
|
+
m = re.search(r"<\s*([\w.]+)", ct)
|
|
257
|
+
return split_base(m.group(1)) if m else None
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# ------------------------------------------------------------------ Dart as the receiving side (native invokeMethod)
|
|
261
|
+
DART_SITE = re.compile(r"\.method\s*==\s*['\"]([^'\"]+)['\"]|['\"]([^'\"]+)['\"]\s*==\s*\w+\.method\b")
|
|
262
|
+
DART_SWITCH = re.compile(r"\bswitch\s*\(\s*\w+\.method\s*\)\s*\{")
|
|
263
|
+
DART_CASE = re.compile(r"\bcase\s+['\"]([^'\"]+)['\"]\s*(?::|when\b|\|\|)|\|\|\s*['\"]([^'\"]+)['\"]")
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _match(text: str, i: int, o: str, c: str) -> int:
|
|
267
|
+
depth = 0
|
|
268
|
+
for j in range(i, len(text)):
|
|
269
|
+
ch = text[j]
|
|
270
|
+
if ch == o:
|
|
271
|
+
depth += 1
|
|
272
|
+
elif ch == c:
|
|
273
|
+
depth -= 1
|
|
274
|
+
if depth == 0:
|
|
275
|
+
return j
|
|
276
|
+
return len(text)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _line_off(text: str, line: int) -> int:
|
|
280
|
+
pos = 0
|
|
281
|
+
for _ in range(max(0, line - 1)):
|
|
282
|
+
pos = text.find("\n", pos) + 1
|
|
283
|
+
if pos == 0:
|
|
284
|
+
return len(text)
|
|
285
|
+
return pos
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _dart_handler(prog, ev, builder, fn, f, ctx, texts) -> int:
|
|
289
|
+
"""`channel.setMethodCallHandler(handler)` on a channel with a known name: the method names the handler tests
|
|
290
|
+
(`call.method == 'x'`, `switch (call.method) { case 'x': }`) are received in Dart (RECEIVED_BY from
|
|
291
|
+
endpoint:flutter:<channel>#<x>; the native side sends them with invokeMethod)."""
|
|
292
|
+
from ...bridges import protocol_receive
|
|
293
|
+
cr = _creation(prog, f["t"], ctx)
|
|
294
|
+
if not cr or cr[0] != "method":
|
|
295
|
+
return 0
|
|
296
|
+
name, conf = _channel_name(ev, cr[1], cr[2])
|
|
297
|
+
if not name:
|
|
298
|
+
return 0
|
|
299
|
+
a = (f.get("a") or [None])[0]
|
|
300
|
+
target, file = fn, fn.file
|
|
301
|
+
if isinstance(a, dict) and a.get("k") in ("id", "prop"):
|
|
302
|
+
nm = a.get("v") if a.get("k") == "id" else a.get("n")
|
|
303
|
+
h = None
|
|
304
|
+
if fn.cls is not None:
|
|
305
|
+
h, _ = prog.find_member(fn.cls, nm)
|
|
306
|
+
if h is None:
|
|
307
|
+
h = prog.lookup(fn.lib, nm)
|
|
308
|
+
if not isinstance(h, DFunc):
|
|
309
|
+
return 0
|
|
310
|
+
target, file = h, h.file
|
|
311
|
+
root = getattr(prog, "root", None)
|
|
312
|
+
if file not in texts:
|
|
313
|
+
try:
|
|
314
|
+
texts[file] = (root / file).read_text(encoding="utf-8", errors="replace") if root else ""
|
|
315
|
+
except OSError:
|
|
316
|
+
texts[file] = ""
|
|
317
|
+
text = texts[file]
|
|
318
|
+
if not text:
|
|
319
|
+
return 0
|
|
320
|
+
if target is fn: # a closure: the call's argument list
|
|
321
|
+
lo = text.find("setMethodCallHandler", _line_off(text, f.get("l") or 1))
|
|
322
|
+
if lo < 0:
|
|
323
|
+
return 0
|
|
324
|
+
lo = text.find("(", lo)
|
|
325
|
+
hi = _match(text, lo, "(", ")")
|
|
326
|
+
else:
|
|
327
|
+
lo = _line_off(text, target.line)
|
|
328
|
+
b0 = text.find("{", lo)
|
|
329
|
+
hi = _match(text, b0, "{", "}") if b0 >= 0 else lo
|
|
330
|
+
region = text[lo:hi]
|
|
331
|
+
meths = []
|
|
332
|
+
for m in DART_SITE.finditer(region):
|
|
333
|
+
meths.append((m.group(1) or m.group(2), lo + m.start()))
|
|
334
|
+
for m in DART_SWITCH.finditer(region):
|
|
335
|
+
end = _match(region, m.end() - 1, "{", "}")
|
|
336
|
+
for cm in DART_CASE.finditer(region, m.end(), end):
|
|
337
|
+
meths.append((cm.group(1) or cm.group(2), lo + cm.start()))
|
|
338
|
+
seen = set()
|
|
339
|
+
for meth, pos in meths:
|
|
340
|
+
if meth in seen:
|
|
341
|
+
continue
|
|
342
|
+
seen.add(meth)
|
|
343
|
+
protocol_receive(builder, "flutter", name, meth, target.id, file, text.count("\n", 0, pos) + 1,
|
|
344
|
+
conf if conf != EXACT else RESOLVED, via="setMethodCallHandler (Dart)", side="dart")
|
|
345
|
+
return len(seen)
|