@descryy/adapter-kotlin 0.4.0 → 0.5.0

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.
@@ -0,0 +1,226 @@
1
+ /**
2
+ * The dependency boundary, Kotlin side.
3
+ *
4
+ * `client.get(url)` on an `io.ktor.client.HttpClient`, or `Json.encodeToString(x)`
5
+ * on `kotlinx.serialization.json.Json`, leaves the repository. Today that becomes
6
+ * a ledger row saying there is no declaration for the name in the analysed set,
7
+ * and the call graph stops. This module answers the one question that turns the
8
+ * boundary into a fact the graph can hold: **which package, and which symbol in
9
+ * it.**
10
+ *
11
+ * ## The rule everything here rests on: attribute only from a binding the
12
+ * developer wrote
13
+ *
14
+ * In Go (`adapter-go/src/external.ts`, the model for all of these) that binding
15
+ * is the file's own `import` block. Kotlin's is the same statement — and Kotlin
16
+ * makes it stricter than Java does, which is why this file is shorter than
17
+ * `adapter-java/src/external.ts`:
18
+ *
19
+ * - **A Kotlin import names a symbol, not only a type.** `import
20
+ * io.ktor.server.request.receive` imports an extension function; `import
21
+ * okhttp3.Request` imports a class; `import kotlin.test.assertEquals`
22
+ * imports a top-level function. All three are the same statement and all
23
+ * three bind one local name.
24
+ * - **There is no inline fully-qualified fallback here.** Java attributes
25
+ * `java.util.List` written out at the reference, because Java code writes
26
+ * FQNs inline often enough to be worth the naming-convention rule it takes
27
+ * to read one. Kotlin's `parse.ts` reports a receiver as a written *name*
28
+ * and a dotted path is a value traversal far more often than a package, so
29
+ * the same rule would buy little and cost precision. Only an import is read.
30
+ *
31
+ * Nothing here reads a Gradle configuration, a resolved classpath, `~/.m2` or a
32
+ * jar. Two reasons, the same two Go gives: most checkouts have no dependencies
33
+ * fetched, and an identity taken from a resolved classpath would *move* the
34
+ * first time somebody ran a build.
35
+ *
36
+ * ## Where the package ends and the symbol begins
37
+ *
38
+ * Java's `external.ts` cuts a fully-qualified name at its first capitalised
39
+ * segment, which works because a Java import always ends at a type. **A Kotlin
40
+ * import does not**: `io.ktor.server.request.receive` has no capitalised segment
41
+ * at all. So the cut here is the import statement's own structure first — the
42
+ * last segment is the imported symbol, the rest is where it lives — and the
43
+ * capitalisation rule only refines it, to keep a nested symbol
44
+ * (`okhttp3.Headers.Companion.of`) attached to the type that owns it rather than
45
+ * turned into three package segments.
46
+ *
47
+ * ## The refusals, and what each costs
48
+ *
49
+ * - **A wildcard import binds no name.** `import io.ktor.util.*` then
50
+ * `escapeHTML()` could come from that package or any other wildcard in the
51
+ * file. Python's `from x import *` refusal, for the same reason.
52
+ * - **Kotlin's default imports are never written.** `List`, `println`,
53
+ * `String`, `require` arrive with no import statement anywhere, because the
54
+ * compiler imports `kotlin.*`, `kotlin.collections.*`, `kotlin.io.*` and
55
+ * `java.lang.*` into every file. A language rule filling a silence is not a
56
+ * binding a developer wrote, and this is the single largest recall cost here.
57
+ * - **A symbol under one of the repository's own packages is ours**, analysed
58
+ * or not. Unanalysed code of ours is still ours.
59
+ * - **The reflection packages are declined** — see `DYNAMIC_DISPATCH_PACKAGES`.
60
+ *
61
+ * **Everything this module does not attribute is a deliberate refusal.** Rule 2
62
+ * prices a wrong edge far above a missing one.
63
+ */
64
+ /**
65
+ * Provenance of the platform-package list, carried on every attributed node. A
66
+ * property of the language and its JDK, frozen into this source — never read
67
+ * from whatever toolchain a developer happens to have installed.
68
+ *
69
+ * `kotlinx` is deliberately **not** here: `kotlinx.coroutines` and
70
+ * `kotlinx.serialization` are separately released libraries, not the standard
71
+ * library, and calling them platform would be a wrong disclosure on thousands
72
+ * of nodes. `javax` is split for the same reason `adapter-java` splits it.
73
+ */
74
+ export const PLATFORM_BASIS = "kotlin-2 stdlib (kotlin.) plus jdk-21 java./jdk./sun./com.sun. roots and the JDK's own javax subpackages";
75
+ const PLATFORM_ROOTS = ["kotlin", "java", "jdk", "sun", "com.sun"];
76
+ const PLATFORM_JAVAX = new Set([
77
+ "javax.accessibility", "javax.annotation", "javax.crypto", "javax.imageio",
78
+ "javax.lang", "javax.management", "javax.naming", "javax.net", "javax.print",
79
+ "javax.rmi", "javax.script", "javax.security", "javax.smartcardio", "javax.sound",
80
+ "javax.sql", "javax.swing", "javax.tools", "javax.transaction", "javax.xml",
81
+ ]);
82
+ /**
83
+ * The packages whose whole purpose is to make a call target dynamic.
84
+ *
85
+ * Golden pattern 12 — the corpus's most important fixture — forbids a `CALLS`
86
+ * edge out of a method that dispatches reflectively, *to any target at any
87
+ * resolution*. An edge into `Method.invoke` or a `KFunction.call` would be
88
+ * true and would still be the one edge that pattern exists to forbid: it makes
89
+ * a traversal look as though it continued through a site where the graph cannot
90
+ * see the next hop. `adapter-java` declines the same two JDK packages for the
91
+ * same reason. Filed as
92
+ * `DEC-NEXT-external-nodes-through-the-reflection-packages`.
93
+ */
94
+ const DYNAMIC_DISPATCH_PACKAGES = [
95
+ "java.lang.reflect",
96
+ "java.lang.invoke",
97
+ "kotlin.reflect",
98
+ ];
99
+ /** Is `name` the package `prefix`, or inside it? Whole segments only — a raw
100
+ * `startsWith` would put `io.ktor.serverx` inside `io.ktor.server`, which is
101
+ * the vendor-namespace hazard rule 2 prices above a missing edge. */
102
+ function isUnder(name, prefix) {
103
+ return name === prefix || name.startsWith(`${prefix}.`);
104
+ }
105
+ function isPlatform(pkg) {
106
+ for (const root of PLATFORM_ROOTS) {
107
+ if (isUnder(pkg, root))
108
+ return true;
109
+ }
110
+ return PLATFORM_JAVAX.has(pkg.split(".").slice(0, 2).join("."));
111
+ }
112
+ /** A package segment, by the JVM's own naming convention: packages are
113
+ * lowercase and types are capitalised. */
114
+ function isPackageSegment(segment) {
115
+ return /^[a-z_$]/.test(segment);
116
+ }
117
+ /**
118
+ * An import path split into the package that holds it and the symbol chain
119
+ * inside that package.
120
+ *
121
+ * The import statement settles the coarse split by itself — the last segment is
122
+ * the symbol — and the capitalisation rule refines it so a nested symbol stays
123
+ * attached to the type that owns it:
124
+ *
125
+ * `okhttp3.Request` -> `okhttp3` + `Request`
126
+ * `io.ktor.server.request.receive` -> `io.ktor.server.request` + `receive`
127
+ * `okhttp3.Headers.Companion.of` -> `okhttp3` + `Headers.Companion.of`
128
+ *
129
+ * `null` when the path names no package at all (a single segment), which an
130
+ * import of a dependency's symbol never is.
131
+ */
132
+ export function splitImportPath(path) {
133
+ const parts = path.split(".");
134
+ if (parts.length < 2 || parts.some((part) => part === ""))
135
+ return null;
136
+ let cut = 0;
137
+ while (cut < parts.length && isPackageSegment(parts[cut]))
138
+ cut += 1;
139
+ // No capitalised segment anywhere: a top-level function or property, and the
140
+ // statement's own structure is the only thing that says where it lives.
141
+ if (cut === parts.length)
142
+ cut = parts.length - 1;
143
+ if (cut === 0)
144
+ return null;
145
+ return { pkg: parts.slice(0, cut).join("."), symbols: parts.slice(cut) };
146
+ }
147
+ function isOurs(path, context) {
148
+ for (const own of context.ownPackages) {
149
+ if (own !== "" && isUnder(path, own))
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+ /**
155
+ * The package and symbol behind a name that left the repository, or `null` when
156
+ * no import the developer wrote binds it.
157
+ *
158
+ * `written` is the local name at the reference — a type name, a receiver, or a
159
+ * bare callee. `extra` is whatever was named on it: `["get"]` for a method call
160
+ * on an imported type, empty for a type reference.
161
+ */
162
+ export function attributeExternal(written, extra, context) {
163
+ const name = written.trim();
164
+ if (name === "")
165
+ return null;
166
+ // A dotted name is a value traversal far more often than a package here, and
167
+ // reading one would need the type of its head — a question no import table
168
+ // answers. Java's inline-FQN branch is deliberately absent; see the header.
169
+ if (name.includes("."))
170
+ return null;
171
+ if (extra.some((part) => part === ""))
172
+ return null;
173
+ const imported = context.imports.get(name);
174
+ if (imported === undefined || imported.path === "")
175
+ return null;
176
+ if (isOurs(imported.path, context))
177
+ return null;
178
+ const split = splitImportPath(imported.path);
179
+ if (split === null)
180
+ return null;
181
+ for (const dynamic of DYNAMIC_DISPATCH_PACKAGES) {
182
+ if (isUnder(split.pkg, dynamic))
183
+ return null;
184
+ }
185
+ return {
186
+ moduleOrNamespace: split.pkg,
187
+ // The symbol the DEPENDENCY exports, never the local alias: `import a.b.C as
188
+ // D` is identified by `C`, because `D` is a fact about this file and not
189
+ // about the dependency.
190
+ symbolPath: [...split.symbols, ...extra],
191
+ binding: imported.aliased ? "import-alias" : "import",
192
+ thirdParty: !isPlatform(split.pkg),
193
+ };
194
+ }
195
+ /**
196
+ * This file's non-wildcard imports, as `attributeExternal` reads them.
197
+ *
198
+ * A wildcard is dropped: it names a package and binds no local name, so there
199
+ * is no written binding to read. A name bound twice by two different statements
200
+ * is dropped outright — which import wins is a question the source did not
201
+ * answer here, and Python's conditional-import refusal is the same call.
202
+ */
203
+ export function externalImportTable(imports) {
204
+ const table = new Map();
205
+ const conflicted = new Set();
206
+ for (const each of imports) {
207
+ if (each.isWildcard)
208
+ continue;
209
+ if (each.path === "")
210
+ continue;
211
+ const local = each.alias ?? (each.path.split(".").pop() ?? "");
212
+ if (local === "")
213
+ continue;
214
+ const existing = table.get(local);
215
+ if (existing !== undefined && existing.path !== each.path) {
216
+ conflicted.add(local);
217
+ table.delete(local);
218
+ continue;
219
+ }
220
+ if (conflicted.has(local))
221
+ continue;
222
+ table.set(local, { path: each.path, aliased: each.alias !== undefined });
223
+ }
224
+ return table;
225
+ }
226
+ //# sourceMappingURL=external.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external.js","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"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"}
package/dist/extract.d.ts CHANGED
@@ -24,6 +24,7 @@
24
24
  */
25
25
  import { type IRBatch, type ResolutionLevel } from "@descryy/ir";
26
26
  import type { KotlinFile } from "./parse.ts";
27
+ import type { ShapeReach } from "./shape-reach.ts";
27
28
  export interface ExtractInput {
28
29
  readonly files: readonly KotlinFile[];
29
30
  readonly producedBy: string;
@@ -37,6 +38,12 @@ export interface ExtractInput {
37
38
  * conformance harness flags as `RESOLUTION_EXCEEDS_BATCH`.
38
39
  */
39
40
  readonly reached: ResolutionLevel;
41
+ /**
42
+ * The JPA entity map, read once by `adapter.ts` (it decides `reached`) and
43
+ * handed on unchanged so the batch is never read twice. `undefined` when
44
+ * nothing parsed.
45
+ */
46
+ readonly reach?: ShapeReach | undefined;
40
47
  }
41
48
  export declare function extract(input: ExtractInput): Pick<IRBatch, "nodes" | "edges" | "unresolved">;
42
49
  //# sourceMappingURL=extract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAKL,KAAK,OAAO,EAGZ,KAAK,eAAe,EAErB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAC;AAqB9D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACnF;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AASD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC,CA+tB5F"}
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAML,KAAK,OAAO,EAGZ,KAAK,eAAe,EAErB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA+CnD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACnF;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACzC;AASD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,YAAY,CAAC,CA+/B5F"}